scale_value/scale_impls/
decode.rs

1// Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the scale-value crate.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//         http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use core::marker::PhantomData;
17
18use crate::prelude::*;
19use crate::value_type::{Composite, Primitive, Value, ValueDef, Variant};
20use scale_decode::{FieldIter, TypeResolver};
21
22// This is emitted if something goes wrong decoding into a Value.
23pub use scale_decode::visitor::DecodeError;
24
25/// A visitor that can be used to decode types into [`Value`]s.
26pub type ValueVisitor<Resolver> = DecodeValueVisitor<Resolver, TypeIdContext>;
27
28/// Attempt to decode some SCALE encoded bytes into a value, by providing a pointer
29/// to the bytes (which will be moved forwards as bytes are used in the decoding),
30/// a type ID, and a type registry from which we'll look up the relevant type information.
31pub fn decode_value_as_type<R>(
32    data: &mut &[u8],
33    ty_id: R::TypeId,
34    types: &R,
35) -> Result<Value<R::TypeId>, DecodeError>
36where
37    R: TypeResolver,
38    R::TypeId: Clone,
39{
40    scale_decode::visitor::decode_with_visitor(
41        data,
42        ty_id,
43        types,
44        DecodeValueVisitor::<R, TypeIdContext>::new(),
45    )
46}
47
48/// Decode data into a [`Composite`] according to a series of fields.
49/// Each field contains a type id and optionally a field name.
50pub fn decode_composite_as_fields<'resolver, R>(
51    input: &mut &[u8],
52    fields: &mut dyn FieldIter<'resolver, R::TypeId>,
53    types: &'resolver R,
54) -> Result<Composite<R::TypeId>, DecodeError>
55where
56    R: TypeResolver,
57    R::TypeId: Clone,
58{
59    // Build a Composite type to pass to a one-off visitor:
60    let mut composite = scale_decode::visitor::types::Composite::new(
61        core::iter::empty(),
62        input,
63        fields,
64        types,
65        false,
66    );
67    // Decode into a Composite value from this:
68    let val = visit_composite::<R, TypeIdContext>(&mut composite)?;
69    // Consume remaining bytes and update input cursor:
70    composite.skip_decoding()?;
71    *input = composite.bytes_from_undecoded();
72    Ok(val)
73}
74
75// Sequences, Tuples and Arrays all have the same methods, so decode them in the same way:
76macro_rules! to_unnamed_composite {
77    ($value:ident, $type_id:ident) => {{
78        let mut vals = Vec::with_capacity($value.remaining());
79        while let Some(val) = $value.decode_item(DecodeValueVisitor::<R, F>::new()) {
80            let val = val?;
81            vals.push(val);
82        }
83        Ok(Value {
84            value: ValueDef::Composite(Composite::Unnamed(vals)),
85            context: F::context_from_type_id(&$type_id),
86        })
87    }};
88}
89
90impl scale_decode::DecodeAsFields for Composite<()> {
91    fn decode_as_fields<'resolver, R: TypeResolver>(
92        input: &mut &[u8],
93        fields: &mut dyn FieldIter<'resolver, R::TypeId>,
94        types: &'resolver R,
95    ) -> Result<Self, scale_decode::Error> {
96        // Build a Composite type to pass to a one-off visitor:
97        let mut composite = scale_decode::visitor::types::Composite::new(
98            core::iter::empty(),
99            input,
100            fields,
101            types,
102            false,
103        );
104        // Decode into a Composite value from this:
105        let val = visit_composite::<R, EmptyContext>(&mut composite);
106        // Consume remaining bytes and update input cursor:
107        composite.skip_decoding()?;
108        *input = composite.bytes_from_undecoded();
109        val.map_err(From::from).map(|v| v.map_context(|_| {}))
110    }
111}
112
113impl scale_decode::DecodeAsFields for Value<()> {
114    fn decode_as_fields<'resolver, R: TypeResolver>(
115        input: &mut &[u8],
116        fields: &mut dyn FieldIter<'resolver, R::TypeId>,
117        types: &'resolver R,
118    ) -> Result<Self, scale_decode::Error> {
119        let composite = Composite::<()>::decode_as_fields(input, fields, types)?;
120        Ok(Value { value: ValueDef::Composite(composite), context: () })
121    }
122}
123
124impl scale_decode::IntoVisitor for Value<()> {
125    // Note: the DefaultMapper just removes all type ids here.
126    type AnyVisitor<R: scale_decode::TypeResolver> =
127        scale_decode::visitor::VisitorWithCrateError<DecodeValueVisitor<R, EmptyContext>>;
128
129    fn into_visitor<R: scale_decode::TypeResolver>() -> Self::AnyVisitor<R> {
130        scale_decode::visitor::VisitorWithCrateError(DecodeValueVisitor::new())
131    }
132}
133
134/// We can use [`DecodeValueVisitor`] to decode values, but have two cases to handle:
135///
136/// - We need to be able to decode into [`Value<()>`] for our [`scale_decode::IntoVisitor`]
137///   implementation above (because that trait is agnostic over which visitor is used, and thus
138///   can't have any knowledge on the TypeId type).
139/// - We need to be able to decode into [`Value<TypeId>`] via the [`decode_value_as_type`] fn
140///   above.
141///
142/// This trait basically allows us to handle each case by having a function that is given a
143/// `TypeId` and decides whether to hand back `()` or the `TypeId`.
144pub trait ContextFromTypeId<TypeId> {
145    type Output;
146    fn context_from_type_id(type_id: &TypeId) -> Self::Output;
147}
148
149/// Return () for our value context.
150pub struct EmptyContext;
151impl<TypeId> ContextFromTypeId<TypeId> for EmptyContext {
152    type Output = ();
153    fn context_from_type_id(_type_id: &TypeId) {}
154}
155
156/// Return the type ID for our value context.
157pub struct TypeIdContext;
158impl<TypeId: Clone> ContextFromTypeId<TypeId> for TypeIdContext {
159    type Output = TypeId;
160    fn context_from_type_id(type_id: &TypeId) -> TypeId {
161        type_id.clone()
162    }
163}
164
165/// A [`scale_decode::Visitor`] implementation for decoding into [`Value`]s.
166pub struct DecodeValueVisitor<R: TypeResolver, F> {
167    resolver: PhantomData<(R, F)>,
168}
169impl<R: TypeResolver, F> Default for DecodeValueVisitor<R, F> {
170    fn default() -> Self {
171        Self::new()
172    }
173}
174
175impl<R: TypeResolver, F> DecodeValueVisitor<R, F> {
176    pub fn new() -> Self {
177        DecodeValueVisitor { resolver: PhantomData }
178    }
179}
180
181impl<R, F> scale_decode::visitor::Visitor for DecodeValueVisitor<R, F>
182where
183    R: TypeResolver,
184    F: ContextFromTypeId<R::TypeId>,
185{
186    type Value<'scale, 'info> = Value<F::Output>;
187    type Error = DecodeError;
188    type TypeResolver = R;
189
190    fn visit_bool<'scale, 'info>(
191        self,
192        value: bool,
193        type_id: R::TypeId,
194    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
195        Ok(Value::bool(value).map_context(|_| F::context_from_type_id(&type_id)))
196    }
197    fn visit_char<'scale, 'info>(
198        self,
199        value: char,
200        type_id: R::TypeId,
201    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
202        Ok(Value::char(value).map_context(|_| F::context_from_type_id(&type_id)))
203    }
204    fn visit_u8<'scale, 'info>(
205        self,
206        value: u8,
207        type_id: R::TypeId,
208    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
209        self.visit_u128(value as u128, type_id)
210    }
211    fn visit_u16<'scale, 'info>(
212        self,
213        value: u16,
214        type_id: R::TypeId,
215    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
216        self.visit_u128(value as u128, type_id)
217    }
218    fn visit_u32<'scale, 'info>(
219        self,
220        value: u32,
221        type_id: R::TypeId,
222    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
223        self.visit_u128(value as u128, type_id)
224    }
225    fn visit_u64<'scale, 'info>(
226        self,
227        value: u64,
228        type_id: R::TypeId,
229    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
230        self.visit_u128(value as u128, type_id)
231    }
232    fn visit_u128<'scale, 'info>(
233        self,
234        value: u128,
235        type_id: R::TypeId,
236    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
237        Ok(Value::u128(value).map_context(|_| F::context_from_type_id(&type_id)))
238    }
239    fn visit_u256<'info>(
240        self,
241        value: &[u8; 32],
242        type_id: R::TypeId,
243    ) -> Result<Self::Value<'_, 'info>, Self::Error> {
244        Ok(Value {
245            value: ValueDef::Primitive(Primitive::U256(*value)),
246            context: F::context_from_type_id(&type_id),
247        })
248    }
249    fn visit_i8<'scale, 'info>(
250        self,
251        value: i8,
252        type_id: R::TypeId,
253    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
254        self.visit_i128(value as i128, type_id)
255    }
256    fn visit_i16<'scale, 'info>(
257        self,
258        value: i16,
259        type_id: R::TypeId,
260    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
261        self.visit_i128(value as i128, type_id)
262    }
263    fn visit_i32<'scale, 'info>(
264        self,
265        value: i32,
266        type_id: R::TypeId,
267    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
268        self.visit_i128(value as i128, type_id)
269    }
270    fn visit_i64<'scale, 'info>(
271        self,
272        value: i64,
273        type_id: R::TypeId,
274    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
275        self.visit_i128(value as i128, type_id)
276    }
277    fn visit_i128<'scale, 'info>(
278        self,
279        value: i128,
280        type_id: R::TypeId,
281    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
282        Ok(Value::i128(value).map_context(|_| F::context_from_type_id(&type_id)))
283    }
284    fn visit_i256<'info>(
285        self,
286        value: &[u8; 32],
287        type_id: R::TypeId,
288    ) -> Result<Self::Value<'_, 'info>, Self::Error> {
289        Ok(Value {
290            value: ValueDef::Primitive(Primitive::U256(*value)),
291            context: F::context_from_type_id(&type_id),
292        })
293    }
294    fn visit_sequence<'scale, 'info>(
295        self,
296        value: &mut scale_decode::visitor::types::Sequence<'scale, 'info, R>,
297        type_id: R::TypeId,
298    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
299        to_unnamed_composite!(value, type_id)
300    }
301    fn visit_tuple<'scale, 'info>(
302        self,
303        value: &mut scale_decode::visitor::types::Tuple<'scale, 'info, R>,
304        type_id: R::TypeId,
305    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
306        to_unnamed_composite!(value, type_id)
307    }
308    fn visit_array<'scale, 'info>(
309        self,
310        value: &mut scale_decode::visitor::types::Array<'scale, 'info, R>,
311        type_id: R::TypeId,
312    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
313        to_unnamed_composite!(value, type_id)
314    }
315    fn visit_bitsequence<'scale, 'info>(
316        self,
317        value: &mut scale_decode::visitor::types::BitSequence<'scale>,
318        type_id: R::TypeId,
319    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
320        let bits: Result<_, _> = value.decode()?.collect();
321        Ok(Value {
322            value: ValueDef::BitSequence(bits?),
323            context: F::context_from_type_id(&type_id),
324        })
325    }
326    fn visit_str<'scale, 'info>(
327        self,
328        value: &mut scale_decode::visitor::types::Str<'scale>,
329        type_id: R::TypeId,
330    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
331        Ok(Value::string(value.as_str()?).map_context(|_| F::context_from_type_id(&type_id)))
332    }
333    fn visit_variant<'scale, 'info>(
334        self,
335        value: &mut scale_decode::visitor::types::Variant<'scale, 'info, R>,
336        type_id: R::TypeId,
337    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
338        let values = visit_composite::<R, F>(value.fields())?;
339        Ok(Value {
340            value: ValueDef::Variant(Variant { name: value.name().to_owned(), values }),
341            context: F::context_from_type_id(&type_id),
342        })
343    }
344    fn visit_composite<'scale, 'info>(
345        self,
346        value: &mut scale_decode::visitor::types::Composite<'scale, 'info, R>,
347        type_id: R::TypeId,
348    ) -> Result<Self::Value<'scale, 'info>, Self::Error> {
349        Ok(Value {
350            value: ValueDef::Composite(visit_composite::<R, F>(value)?),
351            context: F::context_from_type_id(&type_id),
352        })
353    }
354}
355
356/// Extract a named/unnamed Composite type out of scale_decode's Composite.
357fn visit_composite<R, F>(
358    value: &mut scale_decode::visitor::types::Composite<'_, '_, R>,
359) -> Result<Composite<F::Output>, DecodeError>
360where
361    R: TypeResolver,
362    F: ContextFromTypeId<R::TypeId>,
363{
364    let len = value.remaining();
365    // if no fields, we'll always assume unnamed.
366    let named = len > 0 && !value.has_unnamed_fields();
367
368    if named {
369        let mut vals = Vec::with_capacity(len);
370        let mut name = value.peek_name();
371        while let Some(v) = value.decode_item(DecodeValueVisitor::<R, F>::new()) {
372            let v = v?;
373            vals.push((name.expect("all fields should be named; we have checked").to_owned(), v));
374            // get the next field name now we've decoded one.
375            name = value.peek_name();
376        }
377        Ok(Composite::Named(vals))
378    } else {
379        let mut vals = Vec::with_capacity(len);
380        while let Some(v) = value.decode_item(DecodeValueVisitor::<R, F>::new()) {
381            let v = v?;
382            vals.push(v);
383        }
384        Ok(Composite::Unnamed(vals))
385    }
386}
387
388#[cfg(test)]
389mod test {
390    use super::*;
391    use crate::value;
392    use codec::{Compact, Encode};
393    use scale_info::PortableRegistry;
394
395    /// Given a type definition, return the PortableType and PortableRegistry
396    /// that our decode functions expect.
397    fn make_type<T: scale_info::TypeInfo + 'static>() -> (u32, PortableRegistry) {
398        let m = scale_info::MetaType::new::<T>();
399        let mut types = scale_info::Registry::new();
400        let id = types.register_type(&m);
401        let portable_registry: PortableRegistry = types.into();
402
403        (id.id, portable_registry)
404    }
405
406    /// Given a value to encode, and a representation of the decoded value, check that our decode functions
407    /// successfully decodes the type to the expected value, based on the implicit SCALE type info that the type
408    /// carries
409    fn encode_decode_check<T: Encode + scale_info::TypeInfo + 'static>(val: T, exp: Value<()>) {
410        encode_decode_check_explicit_info::<T, _>(val, exp)
411    }
412
413    /// Given a value to encode, a type to decode it back into, and a representation of
414    /// the decoded value, check that our decode functions successfully decodes as expected.
415    fn encode_decode_check_explicit_info<Ty: scale_info::TypeInfo + 'static, T: Encode>(
416        val: T,
417        ex: Value<()>,
418    ) {
419        let encoded = val.encode();
420        let encoded = &mut &*encoded;
421
422        let (id, portable_registry) = make_type::<Ty>();
423
424        // Can we decode?
425        let val = decode_value_as_type(encoded, id, &portable_registry).expect("decoding failed");
426        // Is the decoded value what we expected?
427        assert_eq!(val.remove_context(), ex, "decoded value does not look like what we expected");
428        // Did decoding consume all of the encoded bytes, as expected?
429        assert_eq!(encoded.len(), 0, "decoding did not consume all of the encoded bytes");
430    }
431
432    #[test]
433    fn decode_primitives() {
434        encode_decode_check(true, Value::bool(true));
435        encode_decode_check(false, Value::bool(false));
436        encode_decode_check_explicit_info::<char, _>('a' as u32, Value::char('a'));
437        encode_decode_check("hello", Value::string("hello"));
438        encode_decode_check(
439            "hello".to_string(), // String or &str (above) decode OK
440            Value::string("hello"),
441        );
442        encode_decode_check(123u8, Value::u128(123));
443        encode_decode_check(123u16, Value::u128(123));
444        encode_decode_check(123u32, Value::u128(123));
445        encode_decode_check(123u64, Value::u128(123));
446        encode_decode_check(123u128, Value::u128(123));
447        //// Todo [jsdw]: Can we test this if we need a TypeInfo param?:
448        // encode_decode_check_explicit_info(
449        //     [123u8; 32], // Anything 32 bytes long will do here
450        //     Value::u256([123u8; 32]),
451        // );
452        encode_decode_check(123i8, Value::i128(123));
453        encode_decode_check(123i16, Value::i128(123));
454        encode_decode_check(123i32, Value::i128(123));
455        encode_decode_check(123i64, Value::i128(123));
456        encode_decode_check(123i128, Value::i128(123));
457        //// Todo [jsdw]: Can we test this if we need a TypeInfo param?:
458        // encode_decode_check_explicit_info(
459        //     [123u8; 32], // Anything 32 bytes long will do here
460        //     Value::i256([123u8; 32]),
461        // );
462    }
463
464    #[test]
465    fn decode_compact_primitives() {
466        encode_decode_check(Compact(123u8), Value::u128(123));
467        encode_decode_check(Compact(123u16), Value::u128(123));
468        encode_decode_check(Compact(123u32), Value::u128(123));
469        encode_decode_check(Compact(123u64), Value::u128(123));
470        encode_decode_check(Compact(123u128), Value::u128(123));
471    }
472
473    #[test]
474    fn decode_compact_named_wrapper_struct() {
475        // A struct that can be compact encoded:
476        #[derive(Encode, scale_info::TypeInfo)]
477        struct MyWrapper {
478            inner: u32,
479        }
480        impl From<Compact<MyWrapper>> for MyWrapper {
481            fn from(val: Compact<MyWrapper>) -> MyWrapper {
482                val.0
483            }
484        }
485        impl codec::CompactAs for MyWrapper {
486            type As = u32;
487
488            fn encode_as(&self) -> &Self::As {
489                &self.inner
490            }
491            fn decode_from(inner: Self::As) -> Result<Self, codec::Error> {
492                Ok(MyWrapper { inner })
493            }
494        }
495
496        encode_decode_check(
497            Compact(MyWrapper { inner: 123 }),
498            Value::named_composite(vec![("inner", Value::u128(123))]),
499        );
500    }
501
502    #[test]
503    fn decode_compact_unnamed_wrapper_struct() {
504        // A struct that can be compact encoded:
505        #[derive(Encode, scale_info::TypeInfo)]
506        struct MyWrapper(u32);
507        impl From<Compact<MyWrapper>> for MyWrapper {
508            fn from(val: Compact<MyWrapper>) -> MyWrapper {
509                val.0
510            }
511        }
512        impl codec::CompactAs for MyWrapper {
513            type As = u32;
514
515            // Node the requirement to return something with a lifetime tied
516            // to self here. This means that we can't implement this for things
517            // more complex than wrapper structs (eg `Foo(u32,u32,u32,u32)`) without
518            // shenanigans, meaning that (hopefully) supporting wrapper struct
519            // decoding and nothing fancier is sufficient.
520            fn encode_as(&self) -> &Self::As {
521                &self.0
522            }
523            fn decode_from(inner: Self::As) -> Result<Self, codec::Error> {
524                Ok(MyWrapper(inner))
525            }
526        }
527
528        encode_decode_check(
529            Compact(MyWrapper(123)),
530            Value::unnamed_composite(vec![Value::u128(123)]),
531        );
532    }
533
534    #[test]
535    fn decode_sequence_array_tuple_types() {
536        encode_decode_check(vec![1i32, 2, 3], value!((1, 2, 3)));
537        encode_decode_check(
538            [1i32, 2, 3], // compile-time length known
539            Value::unnamed_composite(vec![Value::i128(1), Value::i128(2), Value::i128(3)]),
540        );
541        encode_decode_check(
542            (1i32, true, 123456u128),
543            Value::unnamed_composite(vec![Value::i128(1), Value::bool(true), Value::u128(123456)]),
544        );
545    }
546
547    #[test]
548    fn decode_variant_types() {
549        #[derive(Encode, scale_info::TypeInfo)]
550        enum MyEnum {
551            Foo(bool),
552            Bar { hi: String, other: u128 },
553        }
554
555        encode_decode_check(
556            MyEnum::Foo(true),
557            Value::unnamed_variant("Foo", vec![Value::bool(true)]),
558        );
559        encode_decode_check(
560            MyEnum::Bar { hi: "hello".to_string(), other: 123 },
561            value!(Bar { hi: "hello", other: 123u32 }),
562        );
563    }
564
565    #[test]
566    fn decode_composite_types() {
567        #[derive(Encode, scale_info::TypeInfo)]
568        struct Unnamed(bool, String, Vec<u8>);
569
570        #[derive(Encode, scale_info::TypeInfo)]
571        struct Named {
572            is_valid: bool,
573            name: String,
574            bytes: Vec<u8>,
575        }
576
577        encode_decode_check(
578            Unnamed(true, "James".into(), vec![1, 2, 3]),
579            value!((true, "James", (1u8, 2u8, 3u8))),
580        );
581        encode_decode_check(
582            Named { is_valid: true, name: "James".into(), bytes: vec![1, 2, 3] },
583            value!({is_valid: true, name: "James", bytes: (1u8, 2u8, 3u8)}),
584        );
585    }
586
587    #[test]
588    fn decoding_zero_length_composites_always_unnamed() {
589        // The scale-info repr is just a composite, so we don't really track
590        // whether the thing was named or not. either Value will convert back ok anyway.
591        #[derive(Encode, scale_info::TypeInfo)]
592        struct Named {}
593        #[derive(Encode, scale_info::TypeInfo)]
594        struct Unnamed();
595
596        encode_decode_check(Unnamed(), Value::unnamed_composite(vec![]));
597        encode_decode_check(Named {}, Value::unnamed_composite(vec![]));
598    }
599
600    #[test]
601    fn decode_bit_sequence() {
602        use scale_bits::bits;
603
604        // scale-decode already tests this more thoroughly:
605        encode_decode_check(bits![0, 1, 1, 0, 1, 0], Value::bit_sequence(bits![0, 1, 1, 0, 1, 0]));
606    }
607
608    #[test]
609    fn decode_composite_fields() {
610        use codec::Encode;
611        use scale_decode::DecodeAsFields;
612
613        #[derive(Encode, scale_decode::DecodeAsType, scale_info::TypeInfo)]
614        struct Foo {
615            a: String,
616            b: bool,
617            c: u16,
618        }
619
620        // Get the fields we want to decode:
621        let (id, types) = make_type::<Foo>();
622        let scale_info::TypeDef::Composite(c) = &types.resolve(id).unwrap().type_def else {
623            panic!("Couldn't get fields");
624        };
625        let mut fields = c.fields.iter().map(|f| scale_decode::Field::new(f.ty.id, f.name));
626
627        // get some bytes to decode from:
628        let foo = Foo { a: "Hello".to_owned(), b: true, c: 123 };
629        let foo_bytes = foo.encode();
630        let foo_bytes_cursor = &mut &*foo_bytes;
631
632        // Decode and check that things line up:
633        let out = Composite::decode_as_fields(foo_bytes_cursor, &mut fields, &types)
634            .expect("can decode as fields")
635            .map_context(|_| ());
636        assert_eq!(
637            out,
638            Composite::named([
639                ("a", Value::string("Hello")),
640                ("b", Value::bool(true)),
641                ("c", Value::u128(123))
642            ])
643        );
644        assert_eq!(foo_bytes_cursor.len(), 0, "all bytes should have been consumed");
645    }
646}