diff --git a/borsh-derive/src/internals/schema/enums/mod.rs b/borsh-derive/src/internals/schema/enums/mod.rs index 80825a3c2..17a84241a 100644 --- a/borsh-derive/src/internals/schema/enums/mod.rs +++ b/borsh-derive/src/internals/schema/enums/mod.rs @@ -53,8 +53,10 @@ pub fn process(input: &ItemEnum, cratename: Path) -> syn::Result { fn add_definitions_recursively(definitions: &mut #cratename::__private::maybestd::collections::BTreeMap<#cratename::schema::Declaration, #cratename::schema::Definition>) { #inner_defs #add_recursive_defs - let variants = #cratename::__private::maybestd::vec![#(#variants_defs),*]; - let definition = #cratename::schema::Definition::Enum{variants}; + let definition = #cratename::schema::Definition::Enum { + tag_width: 1, + variants: #cratename::__private::maybestd::vec![#(#variants_defs),*], + }; #cratename::schema::add_definition(Self::declaration(), definition, definitions); } }; diff --git a/borsh-derive/src/internals/schema/enums/snapshots/complex_enum.snap b/borsh-derive/src/internals/schema/enums/snapshots/complex_enum.snap index 032c5e413..027619667 100644 --- a/borsh-derive/src/internals/schema/enums/snapshots/complex_enum.snap +++ b/borsh-derive/src/internals/schema/enums/snapshots/complex_enum.snap @@ -35,13 +35,13 @@ impl borsh::BorshSchema for A { ::add_definitions_recursively(definitions); ::add_definitions_recursively(definitions); ::add_definitions_recursively(definitions); - let variants = borsh::__private::maybestd::vec![ - ("Bacon".to_string(), < ABacon > ::declaration()), ("Eggs".to_string(), < - AEggs > ::declaration()), ("Salad".to_string(), < ASalad > ::declaration()), - ("Sausage".to_string(), < ASausage > ::declaration()) - ]; let definition = borsh::schema::Definition::Enum { - variants, + tag_width: 1, + variants: borsh::__private::maybestd::vec![ + ("Bacon".to_string(), < ABacon > ::declaration()), ("Eggs".to_string(), < + AEggs > ::declaration()), ("Salad".to_string(), < ASalad > + ::declaration()), ("Sausage".to_string(), < ASausage > ::declaration()) + ], }; borsh::schema::add_definition(Self::declaration(), definition, definitions); } diff --git a/borsh-derive/src/internals/schema/enums/snapshots/complex_enum_generics.snap b/borsh-derive/src/internals/schema/enums/snapshots/complex_enum_generics.snap index 798e52f36..bc4c91503 100644 --- a/borsh-derive/src/internals/schema/enums/snapshots/complex_enum_generics.snap +++ b/borsh-derive/src/internals/schema/enums/snapshots/complex_enum_generics.snap @@ -42,13 +42,14 @@ where ::add_definitions_recursively(definitions); as borsh::BorshSchema>::add_definitions_recursively(definitions); as borsh::BorshSchema>::add_definitions_recursively(definitions); - let variants = borsh::__private::maybestd::vec![ - ("Bacon".to_string(), < ABacon > ::declaration()), ("Eggs".to_string(), < - AEggs > ::declaration()), ("Salad".to_string(), < ASalad < C > > - ::declaration()), ("Sausage".to_string(), < ASausage < W > > ::declaration()) - ]; let definition = borsh::schema::Definition::Enum { - variants, + tag_width: 1, + variants: borsh::__private::maybestd::vec![ + ("Bacon".to_string(), < ABacon > ::declaration()), ("Eggs".to_string(), < + AEggs > ::declaration()), ("Salad".to_string(), < ASalad < C > > + ::declaration()), ("Sausage".to_string(), < ASausage < W > > + ::declaration()) + ], }; borsh::schema::add_definition(Self::declaration(), definition, definitions); } diff --git a/borsh-derive/src/internals/schema/enums/snapshots/complex_enum_generics_borsh_skip_named_field.snap b/borsh-derive/src/internals/schema/enums/snapshots/complex_enum_generics_borsh_skip_named_field.snap index a7b557b6f..99f616396 100644 --- a/borsh-derive/src/internals/schema/enums/snapshots/complex_enum_generics_borsh_skip_named_field.snap +++ b/borsh-derive/src/internals/schema/enums/snapshots/complex_enum_generics_borsh_skip_named_field.snap @@ -44,14 +44,14 @@ where ::add_definitions_recursively(definitions); as borsh::BorshSchema>::add_definitions_recursively(definitions); as borsh::BorshSchema>::add_definitions_recursively(definitions); - let variants = borsh::__private::maybestd::vec![ - ("Bacon".to_string(), < ABacon > ::declaration()), ("Eggs".to_string(), < - AEggs > ::declaration()), ("Salad".to_string(), < ASalad < C > > - ::declaration()), ("Sausage".to_string(), < ASausage < W, U > > - ::declaration()) - ]; let definition = borsh::schema::Definition::Enum { - variants, + tag_width: 1, + variants: borsh::__private::maybestd::vec![ + ("Bacon".to_string(), < ABacon > ::declaration()), ("Eggs".to_string(), < + AEggs > ::declaration()), ("Salad".to_string(), < ASalad < C > > + ::declaration()), ("Sausage".to_string(), < ASausage < W, U > > + ::declaration()) + ], }; borsh::schema::add_definition(Self::declaration(), definition, definitions); } diff --git a/borsh-derive/src/internals/schema/enums/snapshots/complex_enum_generics_borsh_skip_tuple_field.snap b/borsh-derive/src/internals/schema/enums/snapshots/complex_enum_generics_borsh_skip_tuple_field.snap index 0fbddbf5c..b9ee7f0c5 100644 --- a/borsh-derive/src/internals/schema/enums/snapshots/complex_enum_generics_borsh_skip_tuple_field.snap +++ b/borsh-derive/src/internals/schema/enums/snapshots/complex_enum_generics_borsh_skip_tuple_field.snap @@ -43,13 +43,14 @@ where ::add_definitions_recursively(definitions); as borsh::BorshSchema>::add_definitions_recursively(definitions); as borsh::BorshSchema>::add_definitions_recursively(definitions); - let variants = borsh::__private::maybestd::vec![ - ("Bacon".to_string(), < ABacon > ::declaration()), ("Eggs".to_string(), < - AEggs > ::declaration()), ("Salad".to_string(), < ASalad < C > > - ::declaration()), ("Sausage".to_string(), < ASausage < W > > ::declaration()) - ]; let definition = borsh::schema::Definition::Enum { - variants, + tag_width: 1, + variants: borsh::__private::maybestd::vec![ + ("Bacon".to_string(), < ABacon > ::declaration()), ("Eggs".to_string(), < + AEggs > ::declaration()), ("Salad".to_string(), < ASalad < C > > + ::declaration()), ("Sausage".to_string(), < ASausage < W > > + ::declaration()) + ], }; borsh::schema::add_definition(Self::declaration(), definition, definitions); } diff --git a/borsh-derive/src/internals/schema/enums/snapshots/filter_foreign_attrs.snap b/borsh-derive/src/internals/schema/enums/snapshots/filter_foreign_attrs.snap index 96f79ce65..273b550ee 100644 --- a/borsh-derive/src/internals/schema/enums/snapshots/filter_foreign_attrs.snap +++ b/borsh-derive/src/internals/schema/enums/snapshots/filter_foreign_attrs.snap @@ -29,12 +29,12 @@ impl borsh::BorshSchema for A { } ::add_definitions_recursively(definitions); ::add_definitions_recursively(definitions); - let variants = borsh::__private::maybestd::vec![ - ("B".to_string(), < AB > ::declaration()), ("Negative".to_string(), < - ANegative > ::declaration()) - ]; let definition = borsh::schema::Definition::Enum { - variants, + tag_width: 1, + variants: borsh::__private::maybestd::vec![ + ("B".to_string(), < AB > ::declaration()), ("Negative".to_string(), < + ANegative > ::declaration()) + ], }; borsh::schema::add_definition(Self::declaration(), definition, definitions); } diff --git a/borsh-derive/src/internals/schema/enums/snapshots/generic_associated_type.snap b/borsh-derive/src/internals/schema/enums/snapshots/generic_associated_type.snap index ab5048dd5..0db595cc6 100644 --- a/borsh-derive/src/internals/schema/enums/snapshots/generic_associated_type.snap +++ b/borsh-derive/src/internals/schema/enums/snapshots/generic_associated_type.snap @@ -55,12 +55,12 @@ where as borsh::BorshSchema>::add_definitions_recursively(definitions); - let variants = borsh::__private::maybestd::vec![ - ("B".to_string(), < EnumParametrizedB < K, V > > ::declaration()), ("C" - .to_string(), < EnumParametrizedC < T > > ::declaration()) - ]; let definition = borsh::schema::Definition::Enum { - variants, + tag_width: 1, + variants: borsh::__private::maybestd::vec![ + ("B".to_string(), < EnumParametrizedB < K, V > > ::declaration()), ("C" + .to_string(), < EnumParametrizedC < T > > ::declaration()) + ], }; borsh::schema::add_definition(Self::declaration(), definition, definitions); } diff --git a/borsh-derive/src/internals/schema/enums/snapshots/generic_associated_type_param_override.snap b/borsh-derive/src/internals/schema/enums/snapshots/generic_associated_type_param_override.snap index 405b62f59..1b56f94f5 100644 --- a/borsh-derive/src/internals/schema/enums/snapshots/generic_associated_type_param_override.snap +++ b/borsh-derive/src/internals/schema/enums/snapshots/generic_associated_type_param_override.snap @@ -56,12 +56,12 @@ where as borsh::BorshSchema>::add_definitions_recursively(definitions); - let variants = borsh::__private::maybestd::vec![ - ("B".to_string(), < EnumParametrizedB < K, V > > ::declaration()), ("C" - .to_string(), < EnumParametrizedC < T > > ::declaration()) - ]; let definition = borsh::schema::Definition::Enum { - variants, + tag_width: 1, + variants: borsh::__private::maybestd::vec![ + ("B".to_string(), < EnumParametrizedB < K, V > > ::declaration()), ("C" + .to_string(), < EnumParametrizedC < T > > ::declaration()) + ], }; borsh::schema::add_definition(Self::declaration(), definition, definitions); } diff --git a/borsh-derive/src/internals/schema/enums/snapshots/recursive_enum.snap b/borsh-derive/src/internals/schema/enums/snapshots/recursive_enum.snap index d895d6625..a1b906dfb 100644 --- a/borsh-derive/src/internals/schema/enums/snapshots/recursive_enum.snap +++ b/borsh-derive/src/internals/schema/enums/snapshots/recursive_enum.snap @@ -36,12 +36,12 @@ where struct AC(K, Vec); as borsh::BorshSchema>::add_definitions_recursively(definitions); as borsh::BorshSchema>::add_definitions_recursively(definitions); - let variants = borsh::__private::maybestd::vec![ - ("B".to_string(), < AB < K, V > > ::declaration()), ("C".to_string(), < AC < - K > > ::declaration()) - ]; let definition = borsh::schema::Definition::Enum { - variants, + tag_width: 1, + variants: borsh::__private::maybestd::vec![ + ("B".to_string(), < AB < K, V > > ::declaration()), ("C".to_string(), < + AC < K > > ::declaration()) + ], }; borsh::schema::add_definition(Self::declaration(), definition, definitions); } diff --git a/borsh-derive/src/internals/schema/enums/snapshots/simple_enum.snap b/borsh-derive/src/internals/schema/enums/snapshots/simple_enum.snap index 320d884b9..603c0f776 100644 --- a/borsh-derive/src/internals/schema/enums/snapshots/simple_enum.snap +++ b/borsh-derive/src/internals/schema/enums/snapshots/simple_enum.snap @@ -22,12 +22,12 @@ impl borsh::BorshSchema for A { struct AEggs; ::add_definitions_recursively(definitions); ::add_definitions_recursively(definitions); - let variants = borsh::__private::maybestd::vec![ - ("Bacon".to_string(), < ABacon > ::declaration()), ("Eggs".to_string(), < - AEggs > ::declaration()) - ]; let definition = borsh::schema::Definition::Enum { - variants, + tag_width: 1, + variants: borsh::__private::maybestd::vec![ + ("Bacon".to_string(), < ABacon > ::declaration()), ("Eggs".to_string(), < + AEggs > ::declaration()) + ], }; borsh::schema::add_definition(Self::declaration(), definition, definitions); } diff --git a/borsh-derive/src/internals/schema/enums/snapshots/simple_enum_with_custom_crate.snap b/borsh-derive/src/internals/schema/enums/snapshots/simple_enum_with_custom_crate.snap index c78d0848e..538e501a1 100644 --- a/borsh-derive/src/internals/schema/enums/snapshots/simple_enum_with_custom_crate.snap +++ b/borsh-derive/src/internals/schema/enums/snapshots/simple_enum_with_custom_crate.snap @@ -26,12 +26,12 @@ impl reexporter::borsh::BorshSchema for A { ::add_definitions_recursively( definitions, ); - let variants = reexporter::borsh::__private::maybestd::vec![ - ("Bacon".to_string(), < ABacon > ::declaration()), ("Eggs".to_string(), < - AEggs > ::declaration()) - ]; let definition = reexporter::borsh::schema::Definition::Enum { - variants, + tag_width: 1, + variants: reexporter::borsh::__private::maybestd::vec![ + ("Bacon".to_string(), < ABacon > ::declaration()), ("Eggs".to_string(), < + AEggs > ::declaration()) + ], }; reexporter::borsh::schema::add_definition( Self::declaration(), diff --git a/borsh-derive/src/internals/schema/enums/snapshots/single_field_enum.snap b/borsh-derive/src/internals/schema/enums/snapshots/single_field_enum.snap index e903f8fc1..c3bf310dd 100644 --- a/borsh-derive/src/internals/schema/enums/snapshots/single_field_enum.snap +++ b/borsh-derive/src/internals/schema/enums/snapshots/single_field_enum.snap @@ -17,11 +17,11 @@ impl borsh::BorshSchema for A { #[borsh(crate = "borsh")] struct ABacon; ::add_definitions_recursively(definitions); - let variants = borsh::__private::maybestd::vec![ - ("Bacon".to_string(), < ABacon > ::declaration()) - ]; let definition = borsh::schema::Definition::Enum { - variants, + tag_width: 1, + variants: borsh::__private::maybestd::vec![ + ("Bacon".to_string(), < ABacon > ::declaration()) + ], }; borsh::schema::add_definition(Self::declaration(), definition, definitions); } diff --git a/borsh-derive/src/internals/schema/enums/snapshots/trailing_comma_generics.snap b/borsh-derive/src/internals/schema/enums/snapshots/trailing_comma_generics.snap index 387c8141e..151473bd3 100644 --- a/borsh-derive/src/internals/schema/enums/snapshots/trailing_comma_generics.snap +++ b/borsh-derive/src/internals/schema/enums/snapshots/trailing_comma_generics.snap @@ -39,12 +39,12 @@ where B: Display + Debug; as borsh::BorshSchema>::add_definitions_recursively(definitions); as borsh::BorshSchema>::add_definitions_recursively(definitions); - let variants = borsh::__private::maybestd::vec![ - ("Left".to_string(), < SideLeft < A > > ::declaration()), ("Right" - .to_string(), < SideRight < B > > ::declaration()) - ]; let definition = borsh::schema::Definition::Enum { - variants, + tag_width: 1, + variants: borsh::__private::maybestd::vec![ + ("Left".to_string(), < SideLeft < A > > ::declaration()), ("Right" + .to_string(), < SideRight < B > > ::declaration()) + ], }; borsh::schema::add_definition(Self::declaration(), definition, definitions); } diff --git a/borsh-derive/src/internals/schema/enums/snapshots/with_funcs_attr.snap b/borsh-derive/src/internals/schema/enums/snapshots/with_funcs_attr.snap index 9a08a5e5b..766464e7f 100644 --- a/borsh-derive/src/internals/schema/enums/snapshots/with_funcs_attr.snap +++ b/borsh-derive/src/internals/schema/enums/snapshots/with_funcs_attr.snap @@ -40,12 +40,12 @@ where ); ::add_definitions_recursively(definitions); as borsh::BorshSchema>::add_definitions_recursively(definitions); - let variants = borsh::__private::maybestd::vec![ - ("C3".to_string(), < CC3 > ::declaration()), ("C4".to_string(), < CC4 < K, V - > > ::declaration()) - ]; let definition = borsh::schema::Definition::Enum { - variants, + tag_width: 1, + variants: borsh::__private::maybestd::vec![ + ("C3".to_string(), < CC3 > ::declaration()), ("C4".to_string(), < CC4 < + K, V > > ::declaration()) + ], }; borsh::schema::add_definition(Self::declaration(), definition, definitions); } diff --git a/borsh/src/lib.rs b/borsh/src/lib.rs index 81b28f58e..8f09da872 100644 --- a/borsh/src/lib.rs +++ b/borsh/src/lib.rs @@ -62,7 +62,6 @@ */ -#[cfg(not(feature = "std"))] extern crate alloc; /// Derive macro available if borsh is built with `features = ["derive", "schema"]`. diff --git a/borsh/src/schema.rs b/borsh/src/schema.rs index b7a02aadd..b295cbaee 100644 --- a/borsh/src/schema.rs +++ b/borsh/src/schema.rs @@ -35,6 +35,16 @@ pub type VariantName = String; pub type FieldName = String; /// The type that we use to represent the definition of the Borsh type. +/// Description of data encoding on the wire. +/// +/// Note: Since at the end of the day users can define arbitrary serialisation, +/// it’s not always possible to express using definitions how a type is encoded. +/// For example, let’s say programmer uses [varint] encoding for their data. +/// Such type cannot be fully expressed using `BorshSchema` (or at least not +/// easily). As a consequence, a tool which validates whether binary data +/// matches a schema wouldn’t be able to validate data including such types. +/// +/// [varint]: https://en.wikipedia.org/wiki/Variable-length_quantity#Variants #[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize, BorshSchemaMacro)] pub enum Definition { /// A fixed-size array with the length known at the compile time and the same-type elements. @@ -44,10 +54,31 @@ pub enum Definition { /// A fixed-size tuple with the length known at the compile time and the elements of different /// types. Tuple { elements: Vec }, - /// A tagged union, a.k.a enum. Tagged-unions have variants with associated structures. + + /// A possibly tagged union, a.k.a enum. + /// + /// Tagged unions are prefixed by a tag identifying encoded variant followed + /// by encoding of that variant. + /// + /// Untagged unions don’t have a separate tag which means that knowledge of + /// the type is necessary to fully analyse the binary. Variants may still + /// be used to list possible values or determine the longest possible + /// encoding. Enum { + /// Width in bytes of the discriminant tag. + /// + /// Zero indicates this is an untagged union. In standard borsh + /// encoding this is one however custom encoding formats may use larger + /// width if they need to encode more than 256 variants. + /// + /// Note: This definition must not be used if the tag is not encoded + /// using little-endian format. + tag_width: u8, + + /// Possible variants of the enumeration. variants: Vec<(VariantName, Declaration)>, }, + /// A structure, structurally similar to a tuple. Struct { fields: Fields }, } @@ -281,6 +312,7 @@ where { fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Enum { + tag_width: 1, variants: vec![ ("None".to_string(), <()>::declaration()), ("Some".to_string(), T::declaration()), @@ -302,6 +334,7 @@ where { fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Enum { + tag_width: 1, variants: vec![ ("Ok".to_string(), T::declaration()), ("Err".to_string(), E::declaration()), @@ -521,11 +554,14 @@ mod tests { Option::::add_definitions_recursively(&mut actual_defs); assert_eq!("Option", actual_name); assert_eq!( - map! {"Option" => - Definition::Enum{ variants: vec![ - ("None".to_string(), "nil".to_string()), - ("Some".to_string(), "u64".to_string()), - ]} + map! { + "Option" => Definition::Enum { + tag_width: 1, + variants: vec![ + ("None".to_string(), "nil".to_string()), + ("Some".to_string(), "u64".to_string()), + ] + } }, actual_defs ); @@ -539,16 +575,20 @@ mod tests { assert_eq!("Option>", actual_name); assert_eq!( map! { - "Option" => - Definition::Enum {variants: vec![ - ("None".to_string(), "nil".to_string()), - ("Some".to_string(), "u64".to_string()), - ]}, - "Option>" => - Definition::Enum {variants: vec![ - ("None".to_string(), "nil".to_string()), - ("Some".to_string(), "Option".to_string()), - ]} + "Option" => Definition::Enum { + tag_width: 1, + variants: vec![ + ("None".to_string(), "nil".to_string()), + ("Some".to_string(), "u64".to_string()), + ] + }, + "Option>" => Definition::Enum { + tag_width: 1, + variants: vec![ + ("None".to_string(), "nil".to_string()), + ("Some".to_string(), "Option".to_string()), + ] + } }, actual_defs ); diff --git a/borsh/src/schema_helpers.rs b/borsh/src/schema_helpers.rs index 3ae83f1e2..bb8a7f95f 100644 --- a/borsh/src/schema_helpers.rs +++ b/borsh/src/schema_helpers.rs @@ -113,6 +113,12 @@ fn is_zero_size(declaration: &str, schema: &BorshSchemaContainer) -> bool { Ok(Definition::Tuple { elements }) => elements .iter() .all(|element| is_zero_size(element.as_str(), schema)), + Ok(Definition::Enum { + tag_width: 0, + variants, + }) => variants + .iter() + .all(|variant| is_zero_size(&variant.1, schema)), Ok(Definition::Enum { .. }) => false, Ok(Definition::Struct { fields }) => match fields { Fields::NamedFields(fields) => fields @@ -189,14 +195,17 @@ fn max_serialized_size_impl<'a>( mul(count, add(sz, 4)?) } - Ok(Definition::Enum { variants }) => { - // Size of an enum is the largest variant plus one for tag. + Ok(Definition::Enum { + tag_width, + variants, + }) => { let mut max = 0; for (_, variant) in variants { let sz = max_serialized_size_impl(1, variant, schema, stack)?; max = max.max(sz); } - max.checked_add(1).ok_or(MaxSizeError::Overflow) + max.checked_add(usize::from(*tag_width)) + .ok_or(MaxSizeError::Overflow) } // Tuples and structs sum sizes of all the members. @@ -227,80 +236,131 @@ fn max_serialized_size_impl<'a>( Ok(res) } -#[test] -fn test_max_serialized_size() { - #[cfg(not(feature = "std"))] +#[cfg(test)] +mod tests { + use super::*; + use alloc::{ boxed::Box, + collections::BTreeMap, string::{String, ToString}, + vec, }; #[track_caller] fn test_ok(want: usize) { - let schema = borsh::schema::BorshSchemaContainer::for_type::(); + let schema = BorshSchemaContainer::for_type::(); assert_eq!(Ok(want), max_serialized_size(&schema)); + assert_eq!( + want == 0, + is_zero_size(schema.declaration().as_str(), &schema) + ); } #[track_caller] fn test_err(err: MaxSizeError) { - let schema = borsh::schema::BorshSchemaContainer::for_type::(); + let schema = BorshSchemaContainer::for_type::(); assert_eq!(Err(err), max_serialized_size(&schema)); } const MAX_LEN: usize = u32::MAX as usize; - test_ok::(2); - test_ok::(8); + #[test] + fn max_serialized_size_built_in_types() { + test_ok::(2); + test_ok::(8); - test_ok::>(1); - test_ok::>(2); - test_ok::>(9); + test_ok::>(1); + test_ok::>(2); + test_ok::>(9); - test_ok::<()>(0); - test_ok::<(u8,)>(1); - test_ok::<(u8, u32)>(5); + test_ok::<()>(0); + test_ok::<(u8,)>(1); + test_ok::<(u8, u32)>(5); - test_ok::<[u8; 0]>(0); - test_ok::<[u8; 16]>(16); - test_ok::<[[u8; 4]; 4]>(16); + test_ok::<[u8; 0]>(0); + test_ok::<[u8; 16]>(16); + test_ok::<[[u8; 4]; 4]>(16); - test_ok::>(4 + MAX_LEN); - test_ok::(4 + MAX_LEN); + test_ok::>(4 + MAX_LEN); + test_ok::(4 + MAX_LEN); - test_err::>>(MaxSizeError::Overflow); - test_ok::>>(4 + MAX_LEN * 4); - test_ok::<[[[(); MAX_LEN]; MAX_LEN]; MAX_LEN]>(0); + test_err::>>(MaxSizeError::Overflow); + test_ok::>>(4 + MAX_LEN * 4); + test_ok::<[[[(); MAX_LEN]; MAX_LEN]; MAX_LEN]>(0); + } - use crate as borsh; + #[test] + fn max_serialized_size_derived_types() { + use crate as borsh; - #[derive(::borsh_derive::BorshSchema)] - pub struct Empty; + #[derive(::borsh_derive::BorshSchema)] + pub struct Empty; - #[derive(::borsh_derive::BorshSchema)] - pub struct Named { - _foo: usize, - _bar: [u8; 15], - } + #[derive(::borsh_derive::BorshSchema)] + pub struct Named { + _foo: usize, + _bar: [u8; 15], + } + + #[derive(::borsh_derive::BorshSchema)] + pub struct Unnamed(usize, [u8; 15]); + + #[derive(::borsh_derive::BorshSchema)] + struct Multiple { + _usz0: usize, + _usz1: usize, + _usz2: usize, + _vec0: Vec, + _vec1: Vec, + } - #[derive(::borsh_derive::BorshSchema)] - pub struct Unnamed(usize, [u8; 15]); + #[derive(::borsh_derive::BorshSchema)] + struct Recursive(Option>); - #[derive(::borsh_derive::BorshSchema)] - struct Multiple { - _usz0: usize, - _usz1: usize, - _usz2: usize, - _vec0: Vec, - _vec1: Vec, + test_ok::(0); + test_ok::(23); + test_ok::(23); + test_ok::(3 * 8 + 2 * (4 + MAX_LEN * 8)); + test_err::(MaxSizeError::Overflow); + test_err::(MaxSizeError::Recursive); } - #[derive(::borsh_derive::BorshSchema)] - struct Recursive(Option>); + #[test] + fn max_serialized_size_custom_enum() { + #[allow(dead_code)] + enum Maybe { + Just(T), + Nothing, + } + + impl BorshSchema for Maybe { + fn declaration() -> Declaration { + "Maybe".into() + } + fn add_definitions_recursively(definitions: &mut BTreeMap) { + let definition = Definition::Enum { + tag_width: N as u8, + variants: vec![ + ("Just".into(), T::declaration()), + ("Nothing".into(), "nil".into()), + ], + }; + crate::schema::add_definition(Self::declaration(), definition, definitions); + T::add_definitions_recursively(definitions); + } + } + + test_ok::>(0); + test_ok::>(2); + test_ok::>(8); - test_ok::(0); - test_ok::(23); - test_ok::(23); - test_ok::(3 * 8 + 2 * (4 + MAX_LEN * 8)); - test_err::(MaxSizeError::Overflow); - test_err::(MaxSizeError::Recursive); + test_ok::>(1); + test_ok::>(3); + test_ok::>(9); + + test_ok::>(4); + test_ok::>(6); + test_ok::>(12); + } } diff --git a/borsh/tests/snapshots/test_schema_enums__complex_enum_with_schema.snap b/borsh/tests/snapshots/test_schema_enums__complex_enum_with_schema.snap index 5acee471b..a9c7f34d7 100644 --- a/borsh/tests/snapshots/test_schema_enums__complex_enum_with_schema.snap +++ b/borsh/tests/snapshots/test_schema_enums__complex_enum_with_schema.snap @@ -18,6 +18,7 @@ expression: data 0, 65, 3, + 1, 4, 0, 0, diff --git a/borsh/tests/test_schema_enums.rs b/borsh/tests/test_schema_enums.rs index 4e902bcc7..30338b91f 100644 --- a/borsh/tests/test_schema_enums.rs +++ b/borsh/tests/test_schema_enums.rs @@ -46,7 +46,10 @@ pub fn simple_enum() { map! { "ABacon" => Definition::Struct{ fields: Fields::Empty }, "AEggs" => Definition::Struct{ fields: Fields::Empty }, - "A" => Definition::Enum { variants: vec![("Bacon".to_string(), "ABacon".to_string()), ("Eggs".to_string(), "AEggs".to_string())]} + "A" => Definition::Enum { + tag_width: 1, + variants: vec![("Bacon".to_string(), "ABacon".to_string()), ("Eggs".to_string(), "AEggs".to_string())] + } }, defs ); @@ -63,8 +66,11 @@ pub fn single_field_enum() { A::add_definitions_recursively(&mut defs); assert_eq!( map! { - "ABacon" => Definition::Struct {fields: Fields::Empty}, - "A" => Definition::Enum { variants: vec![("Bacon".to_string(), "ABacon".to_string())]} + "ABacon" => Definition::Struct {fields: Fields::Empty}, + "A" => Definition::Enum { + tag_width: 1, + variants: vec![("Bacon".to_string(), "ABacon".to_string())] + } }, defs ); @@ -176,11 +182,15 @@ pub fn complex_enum_with_schema() { "ASalad" => Definition::Struct{ fields: Fields::UnnamedFields(vec!["Tomatoes".to_string(), "Cucumber".to_string(), "Oil".to_string()])}, "ABacon" => Definition::Struct {fields: Fields::Empty}, "Oil" => Definition::Struct {fields: Fields::Empty}, - "A" => Definition::Enum{ variants: vec![ - ("Bacon".to_string(), "ABacon".to_string()), - ("Eggs".to_string(), "AEggs".to_string()), - ("Salad".to_string(), "ASalad".to_string()), - ("Sausage".to_string(), "ASausage".to_string())]}, + "A" => Definition::Enum { + tag_width: 1, + variants: vec![ + ("Bacon".to_string(), "ABacon".to_string()), + ("Eggs".to_string(), "AEggs".to_string()), + ("Salad".to_string(), "ASalad".to_string()), + ("Sausage".to_string(), "ASausage".to_string()) + ] + }, "Wrapper" => Definition::Struct {fields: Fields::Empty}, "Tomatoes" => Definition::Struct {fields: Fields::Empty}, "ASausage" => Definition::Struct { fields: Fields::NamedFields(vec![ @@ -234,12 +244,13 @@ pub fn complex_enum_generics() { }, "ABacon" => Definition::Struct {fields: Fields::Empty}, "Oil" => Definition::Struct {fields: Fields::Empty}, - "A" => Definition::Enum{ + "A" => Definition::Enum { + tag_width: 1, variants: vec![ - ("Bacon".to_string(), "ABacon".to_string()), - ("Eggs".to_string(), "AEggs".to_string()), - ("Salad".to_string(), "ASalad".to_string()), - ("Sausage".to_string(), "ASausage".to_string()) + ("Bacon".to_string(), "ABacon".to_string()), + ("Eggs".to_string(), "AEggs".to_string()), + ("Salad".to_string(), "ASalad".to_string()), + ("Sausage".to_string(), "ASausage".to_string()) ] }, "Wrapper" => Definition::Struct {fields: Fields::Empty}, @@ -259,10 +270,13 @@ pub fn complex_enum_generics() { fn common_map() -> BTreeMap { map! { - "EnumParametrized" => Definition::Enum{ variants: vec![ + "EnumParametrized" => Definition::Enum { + tag_width: 1, + variants: vec![ ("B".to_string(), "EnumParametrizedB".to_string()), ("C".to_string(), "EnumParametrizedC".to_string()) - ]}, + ] + }, "EnumParametrizedB" => Definition::Struct { fields: Fields::NamedFields(vec![ ("x".to_string(), "BTreeMap".to_string()), ("y".to_string(), "string".to_string()), diff --git a/borsh/tests/test_schema_nested.rs b/borsh/tests/test_schema_nested.rs index 99c40d16f..550ed68f8 100644 --- a/borsh/tests/test_schema_nested.rs +++ b/borsh/tests/test_schema_nested.rs @@ -68,17 +68,24 @@ pub fn duplicated_instantiations() { >>::add_definitions_recursively(&mut defs); assert_eq!( map! { - "A>" => Definition::Enum {variants: vec![ - ("Bacon".to_string(), "ABacon".to_string()), - ("Eggs".to_string(), "AEggs".to_string()), - ("Salad".to_string(), "ASalad".to_string()), - ("Sausage".to_string(), "ASausage>".to_string()) - ]}, - "A" => Definition::Enum {variants: vec![ - ("Bacon".to_string(), "ABacon".to_string()), - ("Eggs".to_string(), "AEggs".to_string()), - ("Salad".to_string(), "ASalad".to_string()), - ("Sausage".to_string(), "ASausage".to_string())]}, + "A>" => Definition::Enum { + tag_width: 1, + variants: vec![ + ("Bacon".to_string(), "ABacon".to_string()), + ("Eggs".to_string(), "AEggs".to_string()), + ("Salad".to_string(), "ASalad".to_string()), + ("Sausage".to_string(), "ASausage>".to_string()) + ] + }, + "A" => Definition::Enum { + tag_width: 1, + variants: vec![ + ("Bacon".to_string(), "ABacon".to_string()), + ("Eggs".to_string(), "AEggs".to_string()), + ("Salad".to_string(), "ASalad".to_string()), + ("Sausage".to_string(), "ASausage".to_string()) + ] + }, "ABacon" => Definition::Struct {fields: Fields::Empty}, "AEggs" => Definition::Struct {fields: Fields::Empty}, "ASalad" => Definition::Struct {fields: Fields::UnnamedFields(vec!["Tomatoes".to_string(), "Cucumber".to_string(), "Oil".to_string()])}, @@ -89,8 +96,20 @@ pub fn duplicated_instantiations() { "Filling" => Definition::Struct {fields: Fields::Empty}, "HashMap" => Definition::Sequence { elements: "Tuple".to_string()}, "Oil" => Definition::Struct { fields: Fields::NamedFields(vec![("seeds".to_string(), "HashMap".to_string()), ("liquid".to_string(), "Option".to_string())])}, - "Option" => Definition::Enum {variants: vec![("None".to_string(), "nil".to_string()), ("Some".to_string(), "string".to_string())]}, - "Option" => Definition::Enum { variants: vec![("None".to_string(), "nil".to_string()), ("Some".to_string(), "u64".to_string())]}, + "Option" => Definition::Enum { + tag_width: 1, + variants: vec![ + ("None".to_string(), "nil".to_string()), + ("Some".to_string(), "string".to_string()) + ] + }, + "Option" => Definition::Enum { + tag_width: 1, + variants: vec![ + ("None".to_string(), "nil".to_string()), + ("Some".to_string(), "u64".to_string()) + ] + }, "Tomatoes" => Definition::Struct {fields: Fields::Empty}, "Tuple" => Definition::Tuple {elements: vec!["u64".to_string(), "string".to_string()]}, "Wrapper" => Definition::Struct{ fields: Fields::NamedFields(vec![("foo".to_string(), "Option".to_string()), ("bar".to_string(), "A".to_string())])} diff --git a/borsh/tests/test_schema_recursive.rs b/borsh/tests/test_schema_recursive.rs index 482b14afe..ee4a1f3ea 100644 --- a/borsh/tests/test_schema_recursive.rs +++ b/borsh/tests/test_schema_recursive.rs @@ -76,39 +76,24 @@ pub fn recursive_enum_schema() { assert_eq!( map! { "ERecD" => Definition::Enum { + tag_width: 1, variants: vec![ - - ( - "B".to_string(), - "ERecDB".to_string(), - ), - ( - "C".to_string(), - "ERecDC".to_string(), - ), + ("B".to_string(), "ERecDB".to_string()), + ("C".to_string(), "ERecDC".to_string()), ] }, "ERecDB" => Definition::Struct { - fields: Fields::NamedFields ( vec![ - ( - "x".to_string(), - "string".to_string(), - ), - ( - "y".to_string(), - "i32".to_string(), - ), + ("x".to_string(), "string".to_string()), + ("y".to_string(), "i32".to_string()), ] - ) }, "ERecDC" => Definition::Struct { fields: Fields::UnnamedFields( vec![ "u8".to_string(), "Vec".to_string(), - ]) }, "Vec" => Definition::Sequence { diff --git a/borsh/tests/test_schema_with.rs b/borsh/tests/test_schema_with.rs index 11550c9de..5274f8b56 100644 --- a/borsh/tests/test_schema_with.rs +++ b/borsh/tests/test_schema_with.rs @@ -130,10 +130,13 @@ pub fn enum_overriden() { >::add_definitions_recursively(&mut defs); assert_eq!( map! { - "C" => Definition::Enum { variants: vec![ - ("C3".to_string(), "CC3".to_string()), - ("C4".to_string(), "CC4".to_string()) - ] }, + "C" => Definition::Enum { + tag_width: 1, + variants: vec![ + ("C3".to_string(), "CC3".to_string()), + ("C4".to_string(), "CC4".to_string()) + ] + }, "CC3" => Definition::Struct { fields: Fields::UnnamedFields(vec!["u64".to_string(), "u64".to_string()]) }, "CC4" => Definition::Struct { fields: Fields::UnnamedFields(vec![ "u64".to_string(), "ThirdParty".to_string()