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