spacetimedb_sats/
algebraic_value.rs

1pub mod de;
2pub mod ser;
3
4use crate::{AlgebraicType, ArrayValue, ProductValue, SumValue};
5use core::mem;
6use core::ops::{Bound, RangeBounds};
7use derive_more::From;
8use enum_as_inner::EnumAsInner;
9
10pub use ethnum::{i256, u256};
11
12/// Totally ordered [`f32`] allowing all IEEE-754 floating point values.
13pub type F32 = decorum::Total<f32>;
14
15/// Totally ordered [`f64`] allowing all IEEE-754 floating point values.
16pub type F64 = decorum::Total<f64>;
17
18/// A value in SATS typed at some [`AlgebraicType`].
19///
20/// Values are type erased, so they do not store their type.
21/// This is important mainly for space efficiency,
22/// including network latency and bandwidth.
23///
24/// These are only values and not expressions.
25/// That is, they are canonical and cannot be simplified further by some evaluation.
26/// So forms like `42 + 24` are not represented in an `AlgebraicValue`.
27#[derive(EnumAsInner, Debug, Clone, Eq, PartialEq, Ord, PartialOrd, From)]
28pub enum AlgebraicValue {
29    /// The minimum value in the total ordering.
30    /// Cannot be serialized and only exists to facilitate range index scans.
31    /// This variant must always be first.
32    Min,
33
34    /// A structural sum value.
35    ///
36    /// Given a sum type `{ N_0(T_0), N_1(T_1), ..., N_n(T_n) }`
37    /// where `N_i` denotes a variant name
38    /// and where `T_i` denotes the type the variant stores,
39    /// a sum value makes a specific choice as to the variant.
40    /// So for example, we might chose `N_1(T_1)`
41    /// and represent this choice with `(1, v)` where `v` is a value of type `T_1`.
42    Sum(SumValue),
43    /// A structural product value.
44    ///
45    /// Given a product type `{ N_0: T_0, N_1: T_1, ..., N_n: T_n }`
46    /// where `N_i` denotes a field / element name
47    /// and where `T_i` denotes the type the field stores,
48    /// a product value stores a value `v_i` of type `T_i` for each field `N_i`.
49    Product(ProductValue),
50    /// A homogeneous array of `AlgebraicValue`s.
51    /// The array has the type [`AlgebraicType::Array(elem_ty)`].
52    ///
53    /// The contained values are stored packed in a representation appropriate for their type.
54    /// See [`ArrayValue`] for details on the representation.
55    Array(ArrayValue),
56    /// A [`bool`] value of type [`AlgebraicType::Bool`].
57    Bool(bool),
58    /// An [`i8`] value of type [`AlgebraicType::I8`].
59    I8(i8),
60    /// A [`u8`] value of type [`AlgebraicType::U8`].
61    U8(u8),
62    /// An [`i16`] value of type [`AlgebraicType::I16`].
63    I16(i16),
64    /// A [`u16`] value of type [`AlgebraicType::U16`].
65    U16(u16),
66    /// An [`i32`] value of type [`AlgebraicType::I32`].
67    I32(i32),
68    /// A [`u32`] value of type [`AlgebraicType::U32`].
69    U32(u32),
70    /// An [`i64`] value of type [`AlgebraicType::I64`].
71    I64(i64),
72    /// A [`u64`] value of type [`AlgebraicType::U64`].
73    U64(u64),
74    /// An [`i128`] value of type [`AlgebraicType::I128`].
75    ///
76    /// We pack these to shrink `AlgebraicValue`.
77    I128(Packed<i128>),
78    /// A [`u128`] value of type [`AlgebraicType::U128`].
79    ///
80    /// We pack these to to shrink `AlgebraicValue`.
81    U128(Packed<u128>),
82    /// An [`i256`] value of type [`AlgebraicType::I256`].
83    ///
84    /// We box these up to shrink `AlgebraicValue`.
85    I256(Box<i256>),
86    /// A [`u256`] value of type [`AlgebraicType::U256`].
87    ///
88    /// We pack these to shrink `AlgebraicValue`.
89    U256(Box<u256>),
90    /// A totally ordered [`F32`] value of type [`AlgebraicType::F32`].
91    ///
92    /// All floating point values defined in IEEE-754 are supported.
93    /// However, unlike the primitive [`f32`], a [total order] is established.
94    ///
95    /// [total order]: https://docs.rs/decorum/0.3.1/decorum/#total-ordering
96    F32(F32),
97    /// A totally ordered [`F64`] value of type [`AlgebraicType::F64`].
98    ///
99    /// All floating point values defined in IEEE-754 are supported.
100    /// However, unlike the primitive [`f64`], a [total order] is established.
101    ///
102    /// [total order]: https://docs.rs/decorum/0.3.1/decorum/#total-ordering
103    F64(F64),
104    /// A UTF-8 string value of type [`AlgebraicType::String`].
105    ///
106    /// Uses Rust's standard representation of strings.
107    String(Box<str>),
108
109    /// The maximum value in the total ordering.
110    /// Cannot be serialized and only exists to facilitate range index scans.
111    /// This variant must always be last.
112    Max,
113}
114
115/// Wraps `T` making the outer type packed with alignment 1.
116#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
117#[repr(Rust, packed)]
118pub struct Packed<T>(pub T);
119
120impl<T> From<T> for Packed<T> {
121    fn from(value: T) -> Self {
122        Self(value)
123    }
124}
125
126#[allow(non_snake_case)]
127impl AlgebraicValue {
128    /// Extract the value and replace it with a dummy one that is cheap to make.
129    pub fn take(&mut self) -> Self {
130        mem::replace(self, Self::U8(0))
131    }
132
133    /// Interpret the value as a byte slice or `None` if it isn't a byte slice.
134    #[inline]
135    pub fn as_bytes(&self) -> Option<&[u8]> {
136        match self {
137            Self::Array(ArrayValue::U8(a)) => Some(a),
138            _ => None,
139        }
140    }
141
142    /// The canonical unit value defined as the nullary product value `()`.
143    ///
144    /// The type of `UNIT` is `()`.
145    pub fn unit() -> Self {
146        Self::product([])
147    }
148
149    /// Returns an [`AlgebraicValue`] representing `v: Box<[u8]>`.
150    #[inline]
151    pub const fn Bytes(v: Box<[u8]>) -> Self {
152        Self::Array(ArrayValue::U8(v))
153    }
154
155    /// Converts `self` into a byte string, if applicable.
156    pub fn into_bytes(self) -> Result<Box<[u8]>, Self> {
157        match self {
158            Self::Array(ArrayValue::U8(v)) => Ok(v),
159            _ => Err(self),
160        }
161    }
162
163    /// Converts `self` into an `Option<AlgebraicValue>`, if applicable.
164    pub fn into_option(self) -> Result<Option<Self>, Self> {
165        match self {
166            AlgebraicValue::Sum(sum_value) => match sum_value.tag {
167                0 => Ok(Some(*sum_value.value)),
168                1 => Ok(None),
169                _ => Err(AlgebraicValue::Sum(sum_value)),
170            },
171            _ => Err(self),
172        }
173    }
174
175    /// Returns an [`AlgebraicValue`] for `some: v`.
176    ///
177    /// The `some` variant is assigned the tag `0`.
178    #[inline]
179    pub fn OptionSome(v: Self) -> Self {
180        Self::sum(0, v)
181    }
182
183    /// Returns an [`AlgebraicValue`] for `none`.
184    ///
185    /// The `none` variant is assigned the tag `1`.
186    #[inline]
187    pub fn OptionNone() -> Self {
188        Self::sum(1, Self::unit())
189    }
190
191    /// Returns an [`AlgebraicValue`] representing a sum value with `tag` and `value`.
192    pub fn sum(tag: u8, value: Self) -> Self {
193        Self::Sum(SumValue::new(tag, value))
194    }
195
196    /// Returns an [`AlgebraicValue`] representing a sum value with `tag` and empty [AlgebraicValue::product], that is
197    /// valid for simple enums without payload.
198    pub fn enum_simple(tag: u8) -> Self {
199        Self::Sum(SumValue::new_simple(tag))
200    }
201
202    /// Returns an [`AlgebraicValue`] representing a product value with the given `elements`.
203    pub fn product(elements: impl Into<ProductValue>) -> Self {
204        Self::Product(elements.into())
205    }
206
207    /// Returns the [`AlgebraicType`] of the product value `x`.
208    pub(crate) fn type_of_product(x: &ProductValue) -> Option<AlgebraicType> {
209        let mut elems = Vec::with_capacity(x.elements.len());
210        for elem in &*x.elements {
211            elems.push(elem.type_of()?.into());
212        }
213        Some(AlgebraicType::product(elems.into_boxed_slice()))
214    }
215
216    /// Infer the [`AlgebraicType`] of an [`AlgebraicValue`].
217    ///
218    /// This function is partial
219    /// as type inference is not possible for `AlgebraicValue` in the case of sums.
220    /// Thus the method only answers for the decidable subset.
221    ///
222    /// # A note on sums
223    ///
224    /// The type of a sum value must be a sum type and *not* a product type.
225    /// Suppose `x.tag` is for the variant `VarName(VarType)`.
226    /// Then `VarType` is *not* the same type as `{ VarName(VarType) | r }`
227    /// where `r` represents a polymorphic variants component.
228    ///
229    /// To assign this a correct type we either have to store the type with the value
230    /// r alternatively, we must have polymorphic variants (see row polymorphism)
231    /// *and* derive the correct variant name.
232    pub fn type_of(&self) -> Option<AlgebraicType> {
233        match self {
234            Self::Sum(_) => None,
235            Self::Product(x) => Self::type_of_product(x),
236            Self::Array(x) => x.type_of().map(Into::into),
237            Self::Bool(_) => Some(AlgebraicType::Bool),
238            Self::I8(_) => Some(AlgebraicType::I8),
239            Self::U8(_) => Some(AlgebraicType::U8),
240            Self::I16(_) => Some(AlgebraicType::I16),
241            Self::U16(_) => Some(AlgebraicType::U16),
242            Self::I32(_) => Some(AlgebraicType::I32),
243            Self::U32(_) => Some(AlgebraicType::U32),
244            Self::I64(_) => Some(AlgebraicType::I64),
245            Self::U64(_) => Some(AlgebraicType::U64),
246            Self::I128(_) => Some(AlgebraicType::I128),
247            Self::U128(_) => Some(AlgebraicType::U128),
248            Self::I256(_) => Some(AlgebraicType::I256),
249            Self::U256(_) => Some(AlgebraicType::U256),
250            Self::F32(_) => Some(AlgebraicType::F32),
251            Self::F64(_) => Some(AlgebraicType::F64),
252            Self::String(_) => Some(AlgebraicType::String),
253            AlgebraicValue::Min | AlgebraicValue::Max => None,
254        }
255    }
256
257    /// Returns whether this value represents a numeric zero.
258    ///
259    /// Can only be true where the type is numeric.
260    pub fn is_numeric_zero(&self) -> bool {
261        match *self {
262            Self::I8(x) => x == 0,
263            Self::U8(x) => x == 0,
264            Self::I16(x) => x == 0,
265            Self::U16(x) => x == 0,
266            Self::I32(x) => x == 0,
267            Self::U32(x) => x == 0,
268            Self::I64(x) => x == 0,
269            Self::U64(x) => x == 0,
270            Self::I128(x) => x.0 == 0,
271            Self::U128(x) => x.0 == 0,
272            Self::I256(ref x) => **x == i256::ZERO,
273            Self::U256(ref x) => **x == u256::ZERO,
274            Self::F32(x) => x == 0.0,
275            Self::F64(x) => x == 0.0,
276            _ => false,
277        }
278    }
279}
280
281impl<T: Into<AlgebraicValue>> From<Option<T>> for AlgebraicValue {
282    fn from(value: Option<T>) -> Self {
283        match value {
284            None => AlgebraicValue::OptionNone(),
285            Some(x) => AlgebraicValue::OptionSome(x.into()),
286        }
287    }
288}
289
290/// An AlgebraicValue can be interpreted as a range containing a only the value itself.
291/// This is useful for BTrees where single key scans are still viewed range scans.
292impl RangeBounds<AlgebraicValue> for &AlgebraicValue {
293    fn start_bound(&self) -> Bound<&AlgebraicValue> {
294        Bound::Included(self)
295    }
296    fn end_bound(&self) -> Bound<&AlgebraicValue> {
297        Bound::Included(self)
298    }
299}
300
301impl RangeBounds<AlgebraicValue> for AlgebraicValue {
302    fn start_bound(&self) -> Bound<&AlgebraicValue> {
303        Bound::Included(self)
304    }
305    fn end_bound(&self) -> Bound<&AlgebraicValue> {
306        Bound::Included(self)
307    }
308}
309
310#[cfg(test)]
311mod tests {
312    use crate::satn::Satn;
313    use crate::{AlgebraicType, AlgebraicValue, ArrayValue, Typespace, ValueWithType, WithTypespace};
314
315    fn in_space<'a, T: crate::Value>(ts: &'a Typespace, ty: &'a T::Type, val: &'a T) -> ValueWithType<'a, T> {
316        WithTypespace::new(ts, ty).with_value(val)
317    }
318
319    #[test]
320    fn unit() {
321        let val = AlgebraicValue::unit();
322        let unit = AlgebraicType::unit();
323        let typespace = Typespace::new(vec![]);
324        assert_eq!(in_space(&typespace, &unit, &val).to_satn(), "()");
325    }
326
327    #[test]
328    fn product_value() {
329        let product_type = AlgebraicType::product([("foo", AlgebraicType::I32)]);
330        let typespace = Typespace::new(vec![]);
331        let product_value = AlgebraicValue::product([AlgebraicValue::I32(42)]);
332        assert_eq!(
333            "(foo = 42)",
334            in_space(&typespace, &product_type, &product_value).to_satn(),
335        );
336    }
337
338    #[test]
339    fn option_some() {
340        let option = AlgebraicType::option(AlgebraicType::never());
341        let sum_value = AlgebraicValue::OptionNone();
342        let typespace = Typespace::new(vec![]);
343        assert_eq!("(none = ())", in_space(&typespace, &option, &sum_value).to_satn(),);
344    }
345
346    #[test]
347    fn primitive() {
348        let u8 = AlgebraicType::U8;
349        let value = AlgebraicValue::U8(255);
350        let typespace = Typespace::new(vec![]);
351        assert_eq!(in_space(&typespace, &u8, &value).to_satn(), "255");
352    }
353
354    #[test]
355    fn array() {
356        let array = AlgebraicType::array(AlgebraicType::U8);
357        let value = AlgebraicValue::Array(ArrayValue::Sum([].into()));
358        let typespace = Typespace::new(vec![]);
359        assert_eq!(in_space(&typespace, &array, &value).to_satn(), "[]");
360    }
361
362    #[test]
363    fn array_of_values() {
364        let array = AlgebraicType::array(AlgebraicType::U8);
365        let value = AlgebraicValue::Array([3u8].into());
366        let typespace = Typespace::new(vec![]);
367        assert_eq!(in_space(&typespace, &array, &value).to_satn(), "0x03");
368    }
369}