diff --git a/src/generation.rs b/src/generation.rs index d28d31b..111f555 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -6892,22 +6892,30 @@ fn generate_enum( // We avoid checking ALL variants if we can figure it out by instead checking the type. // This only works when the variants don't have first types in common. let mut non_overlapping_types_match = { - let mut first_types = BTreeSet::new(); - let mut duplicates = false; + let mut all_first_types = BTreeSet::new(); + let mut duplicates_or_unknown = false; for variant in variants.iter() { - for first_type in variant.cbor_types(types) { - // to_byte(0) is used since cbor_event::Type doesn't implement - // Ord or Hash so we can't put it in a set. Since we fix the lenth - // to always 0 this still remains a 1-to-1 mapping to Type. - if !first_types.insert(first_type.to_byte(0)) { - duplicates = true; + match variant.cbor_types_inner(types, rep) { + Some(first_types) => { + for first_type in first_types.iter() { + // to_byte(0) is used since cbor_event::Type doesn't implement + // Ord or Hash so we can't put it in a set. Since we fix the lenth + // to always 0 this still remains a 1-to-1 mapping to Type. + if !all_first_types.insert(first_type.to_byte(0)) { + duplicates_or_unknown = true; + } + } + } + None => { + duplicates_or_unknown = true; + break; } } } - if duplicates { + if duplicates_or_unknown { None } else { - let deser_covers_all_types = first_types.len() == 8; + let deser_covers_all_types = all_first_types.len() == 8; Some((Block::new("match raw.cbor_type()?"), deser_covers_all_types)) } }; @@ -6971,7 +6979,7 @@ fn generate_enum( .iter() .any(|field| field.rust_type.config.bounds.is_some()); // bounds checking should be handled by the called constructor here - let mut ctor = format!("{}::new(", variant.name); + let mut ctor = format!("{}::new(", ty.conceptual_type.for_variant()); for field in ctor_fields { if output_comma { ctor.push_str(", "); @@ -7209,12 +7217,14 @@ fn generate_enum( } else { variant.name_as_var() }; - let (before, after) = - if cli.preserve_encodings || !variant.rust_type().is_fixed_value() { - (Cow::from(format!("let {var_names_str} = ")), ";") - } else { - (Cow::from(""), "") - }; + let (before, after) = if cli.preserve_encodings + || !variant.rust_type().is_fixed_value() + || rep.is_some() + { + (Cow::from(format!("let {var_names_str} = ")), ";") + } else { + (Cow::from(""), "") + }; let mut variant_deser_code = gen_scope.generate_deserialize( types, (variant.rust_type()).into(), @@ -7222,33 +7232,57 @@ fn generate_enum( DeserializeConfig::new(&variant.name_as_var()), cli, ); - let names_without_outer = enum_gen_info.names_without_outer(); - // we can avoid this ugly block and directly do it as a line possibly - if variant_deser_code.content.as_single_line().is_some() - && names_without_outer.len() == 1 - { - variant_deser_code = gen_scope.generate_deserialize( - types, - (variant.rust_type()).into(), - DeserializeBeforeAfter::new( - &format!("Ok({}::{}(", name, variant.name), - "))", - false, - ), - DeserializeConfig::new(&variant.name_as_var()), - cli, - ); - } else if names_without_outer.is_empty() { - variant_deser_code - .content - .line(&format!("Ok({}::{})", name, variant.name)); + if let Some(r) = rep { + let len_info = match ty.conceptual_type.resolve_alias_shallow() { + ConceptualRustType::Rust(ident) if types.is_plain_group(ident) => { + types.rust_struct(ident).unwrap().cbor_len_info(types) + } + _ => RustStructCBORLen::Fixed(1), + }; + // this will never be 1 line so don't bother with the below cases + variant_deser_code = + surround_in_len_checks(variant_deser_code, len_info, r, cli); + if enum_gen_info.outer_vars == 0 { + variant_deser_code.content.line(&format!( + "Ok({}::{}({}))", + name, variant.name, var_names_str + )); + } else { + enum_gen_info.generate_constructor( + &mut variant_deser_code.content, + "Ok(", + ")", + None, + ); + } } else { - enum_gen_info.generate_constructor( - &mut variant_deser_code.content, - "Ok(", - ")", - None, - ); + // we can avoid this ugly block and directly do it as a line possibly + if variant_deser_code.content.as_single_line().is_some() + && enum_gen_info.names.len() == 1 + { + variant_deser_code = gen_scope.generate_deserialize( + types, + (variant.rust_type()).into(), + DeserializeBeforeAfter::new( + &format!("Ok({}::{}(", name, variant.name), + "))", + false, + ), + DeserializeConfig::new(&variant.name_as_var()), + cli, + ); + } else if enum_gen_info.names.is_empty() { + variant_deser_code + .content + .line(&format!("Ok({}::{})", name, variant.name)); + } else { + enum_gen_info.generate_constructor( + &mut variant_deser_code.content, + "Ok(", + ")", + None, + ); + } } variant_deser_code } @@ -7263,7 +7297,8 @@ fn generate_enum( ), }; let cbor_types_str = variant - .cbor_types(types) + .cbor_types_inner(types, rep) + .expect("Already checked above") .into_iter() .map(cbor_type_code_str) .collect::>() diff --git a/src/intermediate.rs b/src/intermediate.rs index 4229af9..a9590f3 100644 --- a/src/intermediate.rs +++ b/src/intermediate.rs @@ -1502,6 +1502,7 @@ impl ConceptualRustType { } pub fn directly_wasm_exposable(&self, types: &IntermediateTypes) -> bool { + println!("{self:?}.directly_wasm_exposable()"); match self { Self::Fixed(_) => false, Self::Primitive(_) => true, @@ -2104,13 +2105,60 @@ impl EnumVariant { } } - pub fn cbor_types(&self, types: &IntermediateTypes) -> Vec { + /// Gets the next CBOR type after the passed in rep (array/map) tag + /// Returns None if this is not possible and brute-force deserialization + /// trying every variant should be used instead + pub fn cbor_types_inner( + &self, + types: &IntermediateTypes, + outer_rep: Option, + ) -> Option> { match &self.data { - EnumVariantData::RustType(ty) => ty.cbor_types(types), - EnumVariantData::Inlined(record) => match record.rep { - Representation::Array => vec![CBORType::Array], - Representation::Map => vec![CBORType::Map], - }, + EnumVariantData::RustType(ty) => { + if ty.encodings.is_empty() && outer_rep.is_some() { + if let ConceptualRustType::Rust(ident) = + ty.conceptual_type.resolve_alias_shallow() + { + match types.rust_struct(ident).unwrap().variant() { + // we can't know this unless there's a way to provide this info + RustStructType::Extern => None, + RustStructType::Record(record) => { + let mut ret = vec![]; + for field in record.fields.iter() { + ret.extend(field.rust_type.cbor_types(types)); + if !field.optional { + break; + } + } + Some(ret) + } + RustStructType::GroupChoice { .. } => None, + _ => Some(ty.cbor_types(types)), + } + } else { + Some(ty.cbor_types(types)) + } + } else { + Some(ty.cbor_types(types)) + } + } + EnumVariantData::Inlined(record) => { + if outer_rep.is_some() { + let mut ret = vec![]; + for field in record.fields.iter() { + ret.extend(field.rust_type.cbor_types(types)); + if !field.optional { + break; + } + } + Some(ret) + } else { + Some(match record.rep { + Representation::Array => vec![CBORType::Array], + Representation::Map => vec![CBORType::Map], + }) + } + } } } diff --git a/src/parsing.rs b/src/parsing.rs index 8751737..814ee8d 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -5,9 +5,9 @@ use std::collections::BTreeMap; use crate::comment_ast::{merge_metadata, metadata_from_comments, RuleMetadata}; use crate::intermediate::{ - AliasInfo, CDDLIdent, ConceptualRustType, EnumVariant, FixedValue, GenericDef, GenericInstance, - IntermediateTypes, ModuleScope, Primitive, Representation, RustField, RustIdent, RustRecord, - RustStruct, RustStructType, RustType, VariantIdent, + AliasInfo, CBOREncodingOperation, CDDLIdent, ConceptualRustType, EnumVariant, FixedValue, + GenericDef, GenericInstance, IntermediateTypes, ModuleScope, Primitive, Representation, + RustField, RustIdent, RustRecord, RustStruct, RustStructType, RustType, VariantIdent, }; use crate::utils::{ append_number_if_duplicate, convert_to_camel_case, convert_to_snake_case, @@ -1534,7 +1534,12 @@ pub fn parse_group( if let ConceptualRustType::Rust(ident) = &ty.conceptual_type { // we might need to generate it if not used elsewhere types.set_rep_if_plain_group(parent_visitor, ident, rep, cli); + // manual match in case we expand operaitons later types.is_plain_group(ident) + && !ty.encodings.iter().any(|enc| match enc { + CBOREncodingOperation::Tagged(_) => true, + CBOREncodingOperation::CBORBytes => true, + }) } else { false }; diff --git a/tests/core/input.cddl b/tests/core/input.cddl index f53521f..0197907 100644 --- a/tests/core/input.cddl +++ b/tests/core/input.cddl @@ -40,6 +40,61 @@ non_overlapping_type_choice_all = uint / nint / text / bytes / #6.30("hello worl non_overlapping_type_choice_some = uint / nint / text +overlap_basic_embed = [ + ; @name identity + tag: 0 // + ; @name x + tag: 1, hash: bytes .size 32 +] + +non_overlap_basic_embed = [ + ; @name first + x: uint, tag: 0 // + ; @name second + y: text, tag: 1 +] + +non_overlap_basic_embed_multi_fields = [ + ; @name first + x: uint, z: uint // + ; @name second + y: text, z: uint +] + +non_overlap_basic_embed_mixed = [ + ; @name first + x: uint, tag: 0 // + ; @name second + y: text, z: uint +] + +bytes_uint = (bytes, uint) + +non_overlap_basic_embed_mixed_explicit = [ + ; @name first + x: uint, tag: 0 // + ; @name second + y: text, z: uint // + ; @name third + bytes_uint +] + +basic = (uint, text) + +basic_arr = [basic] + +; not overlap since double array for second +non_overlap_basic_not_basic = [ + ; @name group + basic // + ; @name group_arr + basic_arr // + ; @name group_tagged + #6.11(basic) // + ; @name group_bytes + bytes .cbor basic +] + enums = [ c_enum, type_choice, diff --git a/tests/core/tests.rs b/tests/core/tests.rs index 740dd85..7bd41a6 100644 --- a/tests/core/tests.rs +++ b/tests/core/tests.rs @@ -282,6 +282,45 @@ mod tests { deser_test(&NonOverlappingTypeChoiceSome::N64(10000)); deser_test(&NonOverlappingTypeChoiceSome::Text("Hello, World!".into())); } + + #[test] + fn overlap_basic_embed() { + deser_test(&OverlapBasicEmbed::new_identity()); + deser_test(&OverlapBasicEmbed::new_x(vec![85; 32]).unwrap()); + } + + #[test] + fn non_overlap_basic_embed() { + deser_test(&NonOverlapBasicEmbed::new_first(100)); + deser_test(&NonOverlapBasicEmbed::new_second("cddl".to_owned())); + } + + #[test] + fn non_overlap_basic_embed_multi_fields() { + deser_test(&NonOverlapBasicEmbedMultiFields::new_first(100, 1_000_000)); + deser_test(&NonOverlapBasicEmbedMultiFields::new_second("cddl".to_owned(), 0)); + } + + #[test] + fn non_overlap_basic_embed_mixed() { + deser_test(&NonOverlapBasicEmbedMixed::new_first(100)); + deser_test(&NonOverlapBasicEmbedMixed::new_second("cddl".to_owned(), 0)); + } + + #[test] + fn non_overlap_basic_embed_mixed_explicit() { + deser_test(&NonOverlapBasicEmbedMixedExplicit::new_first(100)); + deser_test(&NonOverlapBasicEmbedMixedExplicit::new_second("cddl".to_owned(), 0)); + deser_test(&NonOverlapBasicEmbedMixedExplicit::new_third(vec![0xBA, 0xAD, 0xF0, 0x0D], 4)); + } + + #[test] + fn non_overlap_basic_not_basic() { + deser_test(&NonOverlapBasicNotBasic::new_group(4, "basic".to_owned())); + deser_test(&NonOverlapBasicNotBasic::new_group_arr(Basic::new(4, "".to_owned()))); + deser_test(&NonOverlapBasicNotBasic::new_group_tagged(0, " T A G G E D ".to_owned())); + deser_test(&NonOverlapBasicNotBasic::new_group_bytes(u64::MAX, "bytes .cbor basic".to_owned())); + } #[test] fn array_opt_fields() { diff --git a/tests/preserve-encodings/input.cddl b/tests/preserve-encodings/input.cddl index 3038478..a06b760 100644 --- a/tests/preserve-encodings/input.cddl +++ b/tests/preserve-encodings/input.cddl @@ -34,6 +34,61 @@ non_overlapping_type_choice_all = uint / nint / text / bytes / #6.13("hello worl non_overlapping_type_choice_some = uint / nint / text ; @used_as_key +overlap_basic_embed = [ + ; @name identity + tag: 0 // + ; @name x + tag: 1, hash: bytes .size 32 +] + +non_overlap_basic_embed = [ + ; @name first + x: uint, tag: 0 // + ; @name second + y: text, tag: 1 +] + +non_overlap_basic_embed_multi_fields = [ + ; @name first + x: uint, z: uint // + ; @name second + y: text, z: uint +] + +non_overlap_basic_embed_mixed = [ + ; @name first + x: uint, tag: 0 // + ; @name second + y: text, z: uint +] + +bytes_uint = (bytes, uint) + +non_overlap_basic_embed_mixed_explicit = [ + ; @name first + x: uint, tag: 0 // + ; @name second + y: text, z: uint // + ; @name third + bytes_uint +] + +basic = (uint, text) + +basic_arr = [basic] + +; not overlap since double array for second +non_overlap_basic_not_basic = [ + ; @name group + basic // + ; @name group_arr + basic_arr // + ; @name group_tagged + #6.11(basic) // + ; @name group_bytes + bytes .cbor basic_arr +] + c_enum = 3 / 1 / 4 enums = [ diff --git a/tests/preserve-encodings/tests.rs b/tests/preserve-encodings/tests.rs index fb44943..6b8d168 100644 --- a/tests/preserve-encodings/tests.rs +++ b/tests/preserve-encodings/tests.rs @@ -482,6 +482,196 @@ mod tests { } } + #[test] + fn overlap_basic_embed() { + let def_encodings = vec![Sz::Inline, Sz::One, Sz::Two, Sz::Four, Sz::Eight]; + let str_32_encodings = vec![ + StringLenSz::Len(Sz::One), + StringLenSz::Indefinite(vec![(16, Sz::Two), (16, Sz::One)]), + StringLenSz::Indefinite(vec![(10, Sz::Inline), (0, Sz::Inline), (22, Sz::Four)]), + ]; + for str_enc in &str_32_encodings { + for def_enc in &def_encodings { + let irregular_bytes_identity = vec![ + arr_sz(1, *def_enc), + cbor_int(0, *def_enc), + ].into_iter().flatten().clone().collect::>(); + let irregular_bytes_x = vec![ + arr_sz(2, *def_enc), + cbor_int(1, *def_enc), + cbor_bytes_sz(vec![170; 32], str_enc.clone()), + ].into_iter().flatten().clone().collect::>(); + let irregular_identity = OverlapBasicEmbed::from_cbor_bytes(&irregular_bytes_identity).unwrap(); + assert_eq!(irregular_bytes_identity, irregular_identity.to_cbor_bytes()); + let irregular_x = OverlapBasicEmbed::from_cbor_bytes(&irregular_bytes_x).unwrap(); + assert_eq!(irregular_bytes_x, irregular_x.to_cbor_bytes()); + } + } + } + + #[test] + fn non_overlap_basic_embed() { + let def_encodings = vec![Sz::Inline, Sz::One, Sz::Two, Sz::Four, Sz::Eight]; + let str_5_encodings = vec![ + StringLenSz::Len(Sz::One), + StringLenSz::Indefinite(vec![(3, Sz::Two), (2, Sz::One)]), + StringLenSz::Indefinite(vec![(0, Sz::Eight), (1, Sz::Inline), (0, Sz::Inline), (4, Sz::Four), (0, Sz::Inline)]), + ]; + for str_enc in &str_5_encodings { + for def_enc in &def_encodings { + let irregular_bytes_first = vec![ + arr_sz(2, *def_enc), + cbor_int(10, *def_enc), + cbor_int(0, *def_enc), + ].into_iter().flatten().clone().collect::>(); + let irregular_bytes_second = vec![ + arr_sz(2, *def_enc), + cbor_str_sz("world", str_enc.clone()), + cbor_int(1, *def_enc), + ].into_iter().flatten().clone().collect::>(); + let irregular_first = NonOverlapBasicEmbed::from_cbor_bytes(&irregular_bytes_first).unwrap(); + assert_eq!(irregular_bytes_first, irregular_first.to_cbor_bytes()); + let irregular_second = NonOverlapBasicEmbed::from_cbor_bytes(&irregular_bytes_second).unwrap(); + assert_eq!(irregular_bytes_second, irregular_second.to_cbor_bytes()); + } + } + } + + #[test] + fn non_overlap_basic_embed_multi_fields() { + let def_encodings = vec![Sz::Inline, Sz::One, Sz::Two, Sz::Four, Sz::Eight]; + let str_5_encodings = vec![ + StringLenSz::Len(Sz::One), + StringLenSz::Indefinite(vec![(3, Sz::Two), (2, Sz::One)]), + StringLenSz::Indefinite(vec![(0, Sz::Eight), (1, Sz::Inline), (0, Sz::Inline), (4, Sz::Four), (0, Sz::Inline)]), + ]; + for str_enc in &str_5_encodings { + for def_enc in &def_encodings { + let irregular_bytes_first = vec![ + arr_sz(2, *def_enc), + cbor_int(10, *def_enc), + cbor_int(11, *def_enc), + ].into_iter().flatten().clone().collect::>(); + let irregular_bytes_second = vec![ + arr_sz(2, *def_enc), + cbor_str_sz("HELLO", str_enc.clone()), + cbor_int(0, *def_enc), + ].into_iter().flatten().clone().collect::>(); + let irregular_first = NonOverlapBasicEmbedMultiFields::from_cbor_bytes(&irregular_bytes_first).unwrap(); + assert_eq!(irregular_bytes_first, irregular_first.to_cbor_bytes()); + let irregular_second = NonOverlapBasicEmbedMultiFields::from_cbor_bytes(&irregular_bytes_second).unwrap(); + assert_eq!(irregular_bytes_second, irregular_second.to_cbor_bytes()); + } + } + } + + #[test] + fn non_overlap_basic_embed_mixed() { + let def_encodings = vec![Sz::Inline, Sz::One, Sz::Two, Sz::Four, Sz::Eight]; + let str_5_encodings = vec![ + StringLenSz::Len(Sz::One), + StringLenSz::Indefinite(vec![(3, Sz::Two), (2, Sz::One)]), + StringLenSz::Indefinite(vec![(0, Sz::Eight), (1, Sz::Inline), (0, Sz::Inline), (4, Sz::Four), (0, Sz::Inline)]), + ]; + for str_enc in &str_5_encodings { + for def_enc in &def_encodings { + let irregular_bytes_first = vec![ + arr_sz(2, *def_enc), + cbor_int(10, *def_enc), + cbor_int(0, *def_enc), + ].into_iter().flatten().clone().collect::>(); + let irregular_bytes_second = vec![ + arr_sz(2, *def_enc), + cbor_str_sz("world", str_enc.clone()), + cbor_int(1, *def_enc), + ].into_iter().flatten().clone().collect::>(); + let irregular_first = NonOverlapBasicEmbedMixed::from_cbor_bytes(&irregular_bytes_first).unwrap(); + assert_eq!(irregular_bytes_first, irregular_first.to_cbor_bytes()); + let irregular_second = NonOverlapBasicEmbedMixed::from_cbor_bytes(&irregular_bytes_second).unwrap(); + assert_eq!(irregular_bytes_second, irregular_second.to_cbor_bytes()); + } + } + } + + #[test] + fn non_overlap_basic_embed_mixed_explicit() { + let def_encodings = vec![Sz::Inline, Sz::One, Sz::Two, Sz::Four, Sz::Eight]; + let str_5_encodings = vec![ + StringLenSz::Len(Sz::One), + StringLenSz::Indefinite(vec![(3, Sz::Two), (2, Sz::One)]), + StringLenSz::Indefinite(vec![(0, Sz::Eight), (1, Sz::Inline), (0, Sz::Inline), (4, Sz::Four), (0, Sz::Inline)]), + ]; + for str_enc in &str_5_encodings { + for def_enc in &def_encodings { + let irregular_bytes_first = vec![ + arr_sz(2, *def_enc), + cbor_int(10, *def_enc), + cbor_int(0, *def_enc), + ].into_iter().flatten().clone().collect::>(); + let irregular_bytes_second = vec![ + arr_sz(2, *def_enc), + cbor_str_sz("MiXeD", str_enc.clone()), + cbor_int(1, *def_enc), + ].into_iter().flatten().clone().collect::>(); + let irregular_bytes_third = vec![ + arr_sz(2, *def_enc), + cbor_bytes_sz(vec![0x00, 0x01, 0x02, 0x03, 0x04], str_enc.clone()), + cbor_int(1, *def_enc), + ].into_iter().flatten().clone().collect::>(); + let irregular_first = NonOverlapBasicEmbedMixedExplicit::from_cbor_bytes(&irregular_bytes_first).unwrap(); + assert_eq!(irregular_bytes_first, irregular_first.to_cbor_bytes()); + let irregular_second = NonOverlapBasicEmbedMixedExplicit::from_cbor_bytes(&irregular_bytes_second).unwrap(); + assert_eq!(irregular_bytes_second, irregular_second.to_cbor_bytes()); + let irregular_third = NonOverlapBasicEmbedMixedExplicit::from_cbor_bytes(&irregular_bytes_third).unwrap(); + assert_eq!(irregular_bytes_third, irregular_third.to_cbor_bytes()); + } + } + } + + #[test] + fn non_overlap_basic_not_basic() { + let def_encodings = vec![Sz::Inline, Sz::One, Sz::Two, Sz::Four, Sz::Eight]; + let str_5_encodings = vec![ + StringLenSz::Len(Sz::One), + StringLenSz::Indefinite(vec![(3, Sz::Two), (2, Sz::One)]), + StringLenSz::Indefinite(vec![(0, Sz::Eight), (1, Sz::Inline), (0, Sz::Inline), (4, Sz::Four), (0, Sz::Inline)]), + ]; + for str_enc in &str_5_encodings { + for def_enc in &def_encodings { + let irregular_bytes_group = vec![ + arr_sz(2, *def_enc), + cbor_int(0, *def_enc), + cbor_str_sz("hello", str_enc.clone()), + ].into_iter().flatten().clone().collect::>(); + let irregular_bytes_group_arr = vec![ + vec![ARR_INDEF], + arr_sz(2, *def_enc), + cbor_int(1, *def_enc), + cbor_str_sz("world", str_enc.clone()), + vec![BREAK], + ].into_iter().flatten().clone().collect::>(); + let irregular_bytes_group_tagged = vec![ + arr_sz(2, *def_enc), + cbor_tag_sz(11, *def_enc), + cbor_int(3, *def_enc), + cbor_str_sz(" test", str_enc.clone()), + ].into_iter().flatten().clone().collect::>(); + let irregular_bytes_group_bytes = vec![ + arr_sz(1, *def_enc), + cbor_bytes_sz(irregular_bytes_group.clone(), StringLenSz::Len(Sz::Two)), + ].into_iter().flatten().clone().collect::>(); + let irregular_group = NonOverlapBasicNotBasic::from_cbor_bytes(&irregular_bytes_group).unwrap(); + assert_eq!(irregular_bytes_group, irregular_group.to_cbor_bytes()); + let irregular_group_arr = NonOverlapBasicNotBasic::from_cbor_bytes(&irregular_bytes_group_arr).unwrap(); + assert_eq!(irregular_bytes_group_arr, irregular_group_arr.to_cbor_bytes()); + let irregular_group_tagged = NonOverlapBasicNotBasic::from_cbor_bytes(&irregular_bytes_group_tagged).unwrap(); + assert_eq!(irregular_bytes_group_tagged, irregular_group_tagged.to_cbor_bytes()); + let irregular_group_bytes = NonOverlapBasicNotBasic::from_cbor_bytes(&irregular_bytes_group_bytes).unwrap(); + assert_eq!(irregular_bytes_group_bytes, irregular_group_bytes.to_cbor_bytes()); + } + } + } + #[test] fn enums() { let def_encodings = vec![Sz::Inline, Sz::One, Sz::Two, Sz::Four, Sz::Eight];