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(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 /// Returns an [`AlgebraicValue`] for `some: v`.
164 ///
165 /// The `some` variant is assigned the tag `0`.
166 #[inline]
167 pub fn OptionSome(v: Self) -> Self {
168 Self::sum(0, v)
169 }
170
171 /// Returns an [`AlgebraicValue`] for `none`.
172 ///
173 /// The `none` variant is assigned the tag `1`.
174 #[inline]
175 pub fn OptionNone() -> Self {
176 Self::sum(1, Self::unit())
177 }
178
179 /// Returns an [`AlgebraicValue`] representing a sum value with `tag` and `value`.
180 pub fn sum(tag: u8, value: Self) -> Self {
181 let value = Box::new(value);
182 Self::Sum(SumValue { tag, value })
183 }
184
185 /// Returns an [`AlgebraicValue`] representing a sum value with `tag` and empty [AlgebraicValue::product], that is
186 /// valid for simple enums without payload.
187 pub fn enum_simple(tag: u8) -> Self {
188 let value = Box::new(AlgebraicValue::product(vec![]));
189 Self::Sum(SumValue { tag, value })
190 }
191
192 /// Returns an [`AlgebraicValue`] representing a product value with the given `elements`.
193 pub fn product(elements: impl Into<ProductValue>) -> Self {
194 Self::Product(elements.into())
195 }
196
197 /// Returns the [`AlgebraicType`] of the product value `x`.
198 pub(crate) fn type_of_product(x: &ProductValue) -> Option<AlgebraicType> {
199 let mut elems = Vec::with_capacity(x.elements.len());
200 for elem in &*x.elements {
201 elems.push(elem.type_of()?.into());
202 }
203 Some(AlgebraicType::product(elems.into_boxed_slice()))
204 }
205
206 /// Infer the [`AlgebraicType`] of an [`AlgebraicValue`].
207 ///
208 /// This function is partial
209 /// as type inference is not possible for `AlgebraicValue` in the case of sums.
210 /// Thus the method only answers for the decidable subset.
211 ///
212 /// # A note on sums
213 ///
214 /// The type of a sum value must be a sum type and *not* a product type.
215 /// Suppose `x.tag` is for the variant `VarName(VarType)`.
216 /// Then `VarType` is *not* the same type as `{ VarName(VarType) | r }`
217 /// where `r` represents a polymorphic variants component.
218 ///
219 /// To assign this a correct type we either have to store the type with the value
220 /// r alternatively, we must have polymorphic variants (see row polymorphism)
221 /// *and* derive the correct variant name.
222 pub fn type_of(&self) -> Option<AlgebraicType> {
223 match self {
224 Self::Sum(_) => None,
225 Self::Product(x) => Self::type_of_product(x),
226 Self::Array(x) => x.type_of().map(Into::into),
227 Self::Bool(_) => Some(AlgebraicType::Bool),
228 Self::I8(_) => Some(AlgebraicType::I8),
229 Self::U8(_) => Some(AlgebraicType::U8),
230 Self::I16(_) => Some(AlgebraicType::I16),
231 Self::U16(_) => Some(AlgebraicType::U16),
232 Self::I32(_) => Some(AlgebraicType::I32),
233 Self::U32(_) => Some(AlgebraicType::U32),
234 Self::I64(_) => Some(AlgebraicType::I64),
235 Self::U64(_) => Some(AlgebraicType::U64),
236 Self::I128(_) => Some(AlgebraicType::I128),
237 Self::U128(_) => Some(AlgebraicType::U128),
238 Self::I256(_) => Some(AlgebraicType::I256),
239 Self::U256(_) => Some(AlgebraicType::U256),
240 Self::F32(_) => Some(AlgebraicType::F32),
241 Self::F64(_) => Some(AlgebraicType::F64),
242 Self::String(_) => Some(AlgebraicType::String),
243 AlgebraicValue::Min | AlgebraicValue::Max => None,
244 }
245 }
246
247 /// Returns whether this value represents a numeric zero.
248 ///
249 /// Can only be true where the type is numeric.
250 pub fn is_numeric_zero(&self) -> bool {
251 match *self {
252 Self::I8(x) => x == 0,
253 Self::U8(x) => x == 0,
254 Self::I16(x) => x == 0,
255 Self::U16(x) => x == 0,
256 Self::I32(x) => x == 0,
257 Self::U32(x) => x == 0,
258 Self::I64(x) => x == 0,
259 Self::U64(x) => x == 0,
260 Self::I128(x) => x.0 == 0,
261 Self::U128(x) => x.0 == 0,
262 Self::I256(ref x) => **x == i256::ZERO,
263 Self::U256(ref x) => **x == u256::ZERO,
264 Self::F32(x) => x == 0.0,
265 Self::F64(x) => x == 0.0,
266 _ => false,
267 }
268 }
269}
270
271impl<T: Into<AlgebraicValue>> From<Option<T>> for AlgebraicValue {
272 fn from(value: Option<T>) -> Self {
273 match value {
274 None => AlgebraicValue::OptionNone(),
275 Some(x) => AlgebraicValue::OptionSome(x.into()),
276 }
277 }
278}
279
280/// An AlgebraicValue can be interpreted as a range containing a only the value itself.
281/// This is useful for BTrees where single key scans are still viewed range scans.
282impl RangeBounds<AlgebraicValue> for &AlgebraicValue {
283 fn start_bound(&self) -> Bound<&AlgebraicValue> {
284 Bound::Included(self)
285 }
286 fn end_bound(&self) -> Bound<&AlgebraicValue> {
287 Bound::Included(self)
288 }
289}
290
291impl RangeBounds<AlgebraicValue> for AlgebraicValue {
292 fn start_bound(&self) -> Bound<&AlgebraicValue> {
293 Bound::Included(self)
294 }
295 fn end_bound(&self) -> Bound<&AlgebraicValue> {
296 Bound::Included(self)
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 use crate::satn::Satn;
303 use crate::{AlgebraicType, AlgebraicValue, ArrayValue, Typespace, ValueWithType, WithTypespace};
304
305 fn in_space<'a, T: crate::Value>(ts: &'a Typespace, ty: &'a T::Type, val: &'a T) -> ValueWithType<'a, T> {
306 WithTypespace::new(ts, ty).with_value(val)
307 }
308
309 #[test]
310 fn unit() {
311 let val = AlgebraicValue::unit();
312 let unit = AlgebraicType::unit();
313 let typespace = Typespace::new(vec![]);
314 assert_eq!(in_space(&typespace, &unit, &val).to_satn(), "()");
315 }
316
317 #[test]
318 fn product_value() {
319 let product_type = AlgebraicType::product([("foo", AlgebraicType::I32)]);
320 let typespace = Typespace::new(vec![]);
321 let product_value = AlgebraicValue::product([AlgebraicValue::I32(42)]);
322 assert_eq!(
323 "(foo = 42)",
324 in_space(&typespace, &product_type, &product_value).to_satn(),
325 );
326 }
327
328 #[test]
329 fn option_some() {
330 let option = AlgebraicType::option(AlgebraicType::never());
331 let sum_value = AlgebraicValue::OptionNone();
332 let typespace = Typespace::new(vec![]);
333 assert_eq!("(none = ())", in_space(&typespace, &option, &sum_value).to_satn(),);
334 }
335
336 #[test]
337 fn primitive() {
338 let u8 = AlgebraicType::U8;
339 let value = AlgebraicValue::U8(255);
340 let typespace = Typespace::new(vec![]);
341 assert_eq!(in_space(&typespace, &u8, &value).to_satn(), "255");
342 }
343
344 #[test]
345 fn array() {
346 let array = AlgebraicType::array(AlgebraicType::U8);
347 let value = AlgebraicValue::Array(ArrayValue::Sum([].into()));
348 let typespace = Typespace::new(vec![]);
349 assert_eq!(in_space(&typespace, &array, &value).to_satn(), "[]");
350 }
351
352 #[test]
353 fn array_of_values() {
354 let array = AlgebraicType::array(AlgebraicType::U8);
355 let value = AlgebraicValue::Array([3u8].into());
356 let typespace = Typespace::new(vec![]);
357 assert_eq!(in_space(&typespace, &array, &value).to_satn(), "0x03");
358 }
359}