Skip to main content

cu_bincode/
value_decode.rs

1//! Static recipes for reading native Encode bytes without the native Rust type.
2//! Recipes allocate nothing and are evaluated only by offline readers. Enable
3//! `self-describing` to generate companion implementations alongside Encode, or
4//! use `#[bincode(describe)]` on individual Encode types for unconditional support.
5
6use core::any::{TypeId, type_name};
7pub use cu29_value_types::{QuantityMetadata, TimeStorageUnit, ValueMetadata};
8
9/// Describes the encoded representation, independently of reflection and memory layout.
10/// A handwritten encoder can delegate to the type it actually writes:
11/// ```
12/// use cu_bincode::{ValueDecode, ValueDecodeSpec};
13/// struct Orientation([f32; 4]);
14/// impl ValueDecode for Orientation {
15///     const DECODE: &'static ValueDecodeSpec = <[f32; 4] as ValueDecode>::DECODE;
16/// }
17/// ```
18pub trait ValueDecode: 'static + Sized {
19    /// Complete wire recipe for this encoding.
20    const DECODE: &'static ValueDecodeSpec;
21
22    /// Static logical metadata retained alongside the wire recipe.
23    ///
24    /// Metadata is borrowed from the image and never changes payload encoding.
25    /// Copper owns the typed metadata vocabulary.
26    const METADATA: &'static [ValueMetadata] = &[];
27
28    /// Typed lazy reference for generated field bindings.
29    #[doc(hidden)]
30    const __DECODE_REF: ValueDecodeRef = ValueDecodeRef {
31        describe: describe::<Self>,
32    };
33}
34
35/// Lazy typed reference, allowing recursive descriptions without recursive constants.
36#[derive(Clone, Copy, Debug)]
37pub struct ValueDecodeRef {
38    /// Obtain the original type identity and its recipe during packaging.
39    pub describe: fn() -> ValueDecodeType,
40}
41
42impl ValueDecodeRef {
43    /// Bind a recipe to its original Rust type, including delegated representations.
44    pub const fn of<T: ValueDecode>() -> Self {
45        Self {
46            describe: describe::<T>,
47        }
48    }
49}
50
51fn describe<T: ValueDecode>() -> ValueDecodeType {
52    ValueDecodeType {
53        type_id: TypeId::of::<T>(),
54        type_name: type_name::<T>(),
55        spec: T::DECODE,
56        metadata: T::METADATA,
57    }
58}
59
60/// Type identity is retained even when two types share an identical recipe.
61#[derive(Clone, Copy, Debug)]
62pub struct ValueDecodeType {
63    /// Native identity used to check reflection bindings at packaging time.
64    pub type_id: TypeId,
65    /// Original Rust type name.
66    pub type_name: &'static str,
67    /// Encoded representation.
68    pub spec: &'static ValueDecodeSpec,
69    /// Borrowed logical metadata emitted with this type's description.
70    pub metadata: &'static [ValueMetadata],
71}
72
73pub use cu29_value_types::Scalar;
74
75/// Identity-based selector for a reflected field, preserving the declaration index for tuples.
76#[derive(Clone, Copy, Debug)]
77pub enum FieldSelector {
78    /// Named field identity.
79    Named(&'static str),
80    /// Tuple field identity. The full arity detects reflection that omits positions.
81    Index {
82        /// Original declaration index.
83        index: usize,
84        /// Number of declared fields before wire skips.
85        declared_fields: usize,
86    },
87}
88
89/// One field in wire order; skipped fields are absent.
90#[derive(Clone, Copy, Debug)]
91pub struct ValueDecodeField {
92    /// Logical field selector.
93    pub selector: FieldSelector,
94    /// Original declaration index, including fields omitted from encoding.
95    pub declaration_index: usize,
96    /// Typed child recipe.
97    pub value: ValueDecodeRef,
98}
99
100pub use cu29_value_types::RecordShape;
101
102/// One encoded enum branch. Tags are declaration indices, regardless of Rust discriminants.
103#[derive(Clone, Copy, Debug)]
104pub struct ValueDecodeVariant {
105    /// Wire tag.
106    pub tag: u32,
107    /// Logical variant name.
108    pub name: &'static str,
109    /// Shape of the variant's fields.
110    pub shape: RecordShape,
111    /// Fields in wire order.
112    pub fields: &'static [ValueDecodeField],
113}
114
115/// Wire operations supported by the first self-description API.
116#[derive(Clone, Copy, Debug)]
117pub enum ValueDecodeSpec {
118    /// No bytes.
119    Unit,
120    /// One native scalar.
121    Scalar(Scalar),
122    /// UTF-8 bytes prefixed by a codec-encoded u64 length.
123    String,
124    /// Raw bytes prefixed by a codec-encoded u64 length.
125    Bytes,
126    /// Fields concatenated in wire order.
127    Record {
128        /// Logical shape.
129        shape: RecordShape,
130        /// Encoded fields in wire order.
131        fields: &'static [ValueDecodeField],
132    },
133    /// Fixed repeat, without a count prefix.
134    Array {
135        /// Child representation.
136        element: ValueDecodeRef,
137        /// Fixed count.
138        len: usize,
139    },
140    /// Count-prefixed repeat; capacity is checked by the offline decoder.
141    Sequence {
142        /// Child representation.
143        element: ValueDecodeRef,
144        /// Count encoding.
145        count: Scalar,
146        /// Native capacity, when bounded.
147        capacity: Option<usize>,
148    },
149    /// Count-prefixed key/value pairs.
150    Map {
151        /// Key representation.
152        key: ValueDecodeRef,
153        /// Value representation.
154        value: ValueDecodeRef,
155    },
156    /// One-byte presence tag followed by an optional child.
157    Option(ValueDecodeRef),
158    /// Transparent indirection retaining the child's original type binding.
159    Delegate(ValueDecodeRef),
160    /// Integer tag followed by the selected branch.
161    Enum {
162        /// Tag encoding.
163        tag: Scalar,
164        /// Supported branches.
165        variants: &'static [ValueDecodeVariant],
166    },
167}
168
169macro_rules! scalars {
170    ($($ty:ty => $scalar:ident),* $(,)?) => {$(
171        impl ValueDecode for $ty { const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Scalar(Scalar::$scalar); }
172    )*};
173}
174scalars!(bool=>Bool, u8=>U8, u16=>U16, u32=>U32, u64=>U64, u128=>U128,
175    i8=>I8, i16=>I16, i32=>I32, i64=>I64, i128=>I128, f32=>F32, f64=>F64, char=>Char,
176    usize=>U64, isize=>I64);
177impl ValueDecode for () {
178    const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Unit;
179}
180impl<T: 'static> ValueDecode for core::marker::PhantomData<T> {
181    const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Unit;
182}
183impl<T: ValueDecode, const N: usize> ValueDecode for [T; N] {
184    const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Array {
185        element: ValueDecodeRef::of::<T>(),
186        len: N,
187    };
188}
189impl<T: ValueDecode> ValueDecode for Option<T> {
190    const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Option(ValueDecodeRef::of::<T>());
191}
192impl<T: ValueDecode> ValueDecode for &'static T {
193    const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Delegate(ValueDecodeRef::of::<T>());
194}
195impl ValueDecode for &'static str {
196    const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::String;
197}
198impl<T: ValueDecode> ValueDecode for &'static [T] {
199    const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Sequence {
200        element: ValueDecodeRef::of::<T>(),
201        count: Scalar::U64,
202        capacity: None,
203    };
204}
205
206#[cfg(feature = "alloc")]
207mod allocated {
208    use super::*;
209    use alloc::{
210        boxed::Box,
211        collections::{BTreeMap, BTreeSet, VecDeque},
212        rc::Rc,
213        string::String,
214        sync::Arc,
215        vec::Vec,
216    };
217    impl ValueDecode for String {
218        const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::String;
219    }
220    macro_rules! sequence { ($($container:ident),*) => {$(
221        impl<T: ValueDecode> ValueDecode for $container<T> {
222            const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Sequence { element: ValueDecodeRef::of::<T>(), count: Scalar::U64, capacity: None };
223        }
224    )*}; }
225    sequence!(Vec, VecDeque, BTreeSet);
226    impl<K: ValueDecode, V: ValueDecode> ValueDecode for BTreeMap<K, V> {
227        const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Map {
228            key: ValueDecodeRef::of::<K>(),
229            value: ValueDecodeRef::of::<V>(),
230        };
231    }
232    macro_rules! delegate { ($($container:ident),*) => {$(
233        impl<T: ValueDecode> ValueDecode for $container<T> { const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Delegate(ValueDecodeRef::of::<T>()); }
234    )*}; }
235    delegate!(Box, Rc, Arc);
236}
237
238macro_rules! tuple_len { ($($ty:ident),+) => { [$(stringify!($ty)),+].len() }; }
239
240macro_rules! tuples {
241    ($($ty:ident:$index:tt),+) => {
242        impl<$($ty: ValueDecode),+> ValueDecode for ($($ty,)+) {
243            const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Record { shape: RecordShape::Tuple, fields: { const LEN: usize = tuple_len!($($ty),+); &[$(
244                ValueDecodeField { selector: FieldSelector::Index { index: $index, declared_fields: LEN }, declaration_index: $index, value: ValueDecodeRef::of::<$ty>() }
245            ),+] } };
246        }
247    };
248}
249tuples!(A:0);
250tuples!(A:0,B:1);
251tuples!(A:0,B:1,C:2);
252tuples!(A:0,B:1,C:2,D:3);
253tuples!(A:0,B:1,C:2,D:3,E:4);
254tuples!(A:0,B:1,C:2,D:3,E:4,F:5);
255tuples!(A:0,B:1,C:2,D:3,E:4,F:5,G:6);
256tuples!(A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7);
257tuples!(A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8);
258tuples!(A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8,J:9);
259tuples!(A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8,J:9,K:10);
260tuples!(A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8,J:9,K:10,L:11);
261
262#[cfg(feature = "std")]
263mod standard {
264    use super::*;
265    use std::collections::{HashMap, HashSet};
266    impl<K: ValueDecode, V: ValueDecode, S: 'static> ValueDecode for HashMap<K, V, S> {
267        const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Map {
268            key: ValueDecodeRef::of::<K>(),
269            value: ValueDecodeRef::of::<V>(),
270        };
271    }
272    impl<T: ValueDecode, S: 'static> ValueDecode for HashSet<T, S> {
273        const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Sequence {
274            element: ValueDecodeRef::of::<T>(),
275            count: Scalar::U64,
276            capacity: None,
277        };
278    }
279}
280
281// The codec owns serialization for the shared description vocabulary.
282macro_rules! encode_description_kind {
283    ($ty:ident, {$($variant:ident = $id:literal,)+}) => {
284        impl crate::Encode for $ty {
285            fn encode<E: crate::enc::Encoder>(&self, encoder: &mut E) -> Result<(), crate::error::EncodeError> {
286                (*self as u32).encode(encoder)
287            }
288        }
289        impl<Context> crate::Decode<Context> for $ty {
290            fn decode<D: crate::de::Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, crate::error::DecodeError> {
291                match <u32 as crate::Decode<Context>>::decode(decoder)? {
292                    $($id => Ok(Self::$variant),)+
293                    found => Err(crate::error::DecodeError::UnexpectedVariant {
294                        type_name: core::any::type_name::<Self>(),
295                        allowed: &crate::error::AllowedEnumVariants::Allowed(&[$($id,)+]),
296                        found,
297                    }),
298                }
299            }
300        }
301        crate::impl_borrow_decode!($ty);
302        impl ValueDecode for $ty {
303            const DECODE: &'static ValueDecodeSpec = &ValueDecodeSpec::Enum {
304                tag: Scalar::U32,
305                variants: &[$(ValueDecodeVariant {
306                    tag: $id,
307                    name: stringify!($variant),
308                    shape: RecordShape::Unit,
309                    fields: &[],
310                },)+],
311            };
312        }
313    };
314}
315encode_description_kind!(Scalar, {
316    Bool = 0,
317    U8 = 1,
318    U16 = 2,
319    U32 = 3,
320    U64 = 4,
321    U128 = 5,
322    I8 = 6,
323    I16 = 7,
324    I32 = 8,
325    I64 = 9,
326    I128 = 10,
327    F32 = 11,
328    F64 = 12,
329    Char = 13,
330});
331encode_description_kind!(RecordShape, {
332    Unit = 0,
333    Tuple = 1,
334    Newtype = 2,
335    Struct = 3,
336});
337
338impl crate::Encode for ValueMetadata {
339    fn encode<E: crate::enc::Encoder>(
340        &self,
341        encoder: &mut E,
342    ) -> Result<(), crate::error::EncodeError> {
343        self.kind_id().encode(encoder)?;
344        match *self {
345            ValueMetadata::Quantity(quantity) => {
346                let mut bytes = [0; 8];
347                bytes[..4].copy_from_slice(&quantity.quantity().id().to_le_bytes());
348                bytes[4..].copy_from_slice(&quantity.storage_unit().id().to_le_bytes());
349                bytes.as_slice().encode(encoder)
350            }
351        }
352    }
353}