diff --git a/book/src/ideas.md b/book/src/ideas.md index 1f8d4fc..1c609c0 100644 --- a/book/src/ideas.md +++ b/book/src/ideas.md @@ -21,3 +21,30 @@ Something that is missing from crates.io is to allow for private registries that The schema allows for certain changes that are backwards compatible and do not create a break in the wire format. That means older generated code is still able to read the data despite being created by a newer version of the schema. But the rules are difficult to always keep in mind and mistakes can easily be made. To avoid accidental breaking changes, it would be nice to have an auto-detection mechanism that compares the current version to a previous one whenever changes are made. The _old_ version could be the current Git HEAD compared to the local changes. + +## Schema evolution + +When decoding a value, it may contain new fields and enum variants that are not known to the decoder yet. This can happen if the schema changed and but was only updated in one of two parties. For example a server might have introduced new fields to a response, but not all clients have updated to the new schema yet. + +The same can happen the other way around. For example, if the data was saved in some form of storage and the schema evolved in the meantime, the decoder might encounter old data that lacks the newer content. + +In both cases, the schema must be able to handle missing or unknown fields. Several rules must be upheld when updating a schema, to ensure it is both forward and backward compatible. + +### Skip fields without knowing the exact type + +This section explains how a decoder is able to process payloads that contain newer or unknown fields, given these were introduced in a backward compatible way. + +Without the new schema it's not possible to make decisions about the data that follows after a field identifier. To work around this, reduced information can be encoded into the identifier. + +Only a few details are important for the decoder to proceed, not needing full type information: + +- Is the value a variable integer? + - Skip over individual bytes until the end marker is found +- Is the value length delimited? + - Parse the delimiter, which is always a _varint_, and skip over the length. +- Is the value a nested struct or enum? + - Step into the nested type and skip over all its fields. +- Is the value of fixed length? + - Skip over the fixed length of 1 (`bool`, `u8` and `i8`), 4 (`f32`) or 8 (`f64`) bytes. + +Furthermore, this information is only needed for direct elements of a struct or enum variant, as this allows to skip over the whole field. Types nested into another, like a `vec` for example, don't need to provide this information for each element again. diff --git a/crates/stef-build/src/decode.rs b/crates/stef-build/src/decode.rs index 278d5b1..043bc9c 100644 --- a/crates/stef-build/src/decode.rs +++ b/crates/stef-build/src/decode.rs @@ -29,10 +29,11 @@ pub(super) fn compile_struct( #field_vars loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, #field_matches - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } @@ -70,7 +71,7 @@ pub(super) fn compile_enum( impl #generics ::stef::Decode for #name #generics #generics_where { #[allow(clippy::too_many_lines)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { - match ::stef::buf::decode_id(r)? { + match ::stef::buf::decode_variant_id(r)?.value { #(#variants,)* id => Err(::stef::buf::Error::UnknownVariant(id)), } @@ -103,10 +104,11 @@ fn compile_variant( #field_vars loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, #field_matches - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } @@ -162,6 +164,7 @@ fn compile_field_matches(opts: &Opts, fields: &Fields<'_>) -> TokenStream { } else { ty }, + true, ); quote! { #id => #name = Some(#ty?) } @@ -184,6 +187,7 @@ fn compile_field_matches(opts: &Opts, fields: &Fields<'_>) -> TokenStream { } else { ty }, + true, ); quote! { #id => #name = Some(#ty?) } @@ -253,7 +257,7 @@ fn compile_generics(Generics(types): &Generics<'_>) -> (TokenStream, TokenStream } #[allow(clippy::needless_pass_by_value, clippy::too_many_lines)] -fn compile_data_type(opts: &Opts, ty: &Type<'_>) -> TokenStream { +fn compile_data_type(opts: &Opts, ty: &Type<'_>, root: bool) -> TokenStream { match &ty.value { DataType::Bool => quote! { ::stef::buf::decode_bool(r) }, DataType::U8 => quote! { ::stef::buf::decode_u8(r) }, @@ -274,20 +278,20 @@ fn compile_data_type(opts: &Opts, ty: &Type<'_>) -> TokenStream { BytesType::Bytes => quote! { ::stef::buf::decode_bytes_bytes(r) }, }, DataType::Vec(ty) => { - let ty = compile_data_type(opts, ty); + let ty = compile_data_type(opts, ty, false); quote! { ::stef::buf::decode_vec(r, |r| { #ty }) } } DataType::HashMap(kv) => { - let ty_k = compile_data_type(opts, &kv.0); - let ty_v = compile_data_type(opts, &kv.1); + let ty_k = compile_data_type(opts, &kv.0, false); + let ty_v = compile_data_type(opts, &kv.1, false); quote! { ::stef::buf::decode_hash_map(r, |r| { #ty_k }, |r| { #ty_v }) } } DataType::HashSet(ty) => { - let ty = compile_data_type(opts, ty); + let ty = compile_data_type(opts, ty, false); quote! { ::stef::buf::decode_hash_set(r, |r| { #ty }) } } DataType::Option(ty) => { - let ty = compile_data_type(opts, ty); + let ty = compile_data_type(opts, ty, false); quote! { ::stef::buf::decode_option(r, |r| { #ty }) } } DataType::NonZero(ty) => match &ty.value { @@ -313,16 +317,16 @@ fn compile_data_type(opts: &Opts, ty: &Type<'_>) -> TokenStream { } }, DataType::Vec(ty) => { - let ty = compile_data_type(opts, ty); + let ty = compile_data_type(opts, ty, false); quote! { ::stef::buf::decode_non_zero_vec(r, |r| { #ty }) } } DataType::HashMap(kv) => { - let ty_k = compile_data_type(opts, &kv.0); - let ty_v = compile_data_type(opts, &kv.1); + let ty_k = compile_data_type(opts, &kv.0, false); + let ty_v = compile_data_type(opts, &kv.1, false); quote! { ::stef::buf::decode_non_zero_hash_map(r, |r| { #ty_k }, |r| { #ty_v }) } } DataType::HashSet(ty) => { - let ty = compile_data_type(opts, ty); + let ty = compile_data_type(opts, ty, false); quote! { ::stef::buf::decode_non_zero_hash_set(r, |r| { #ty }) } } ty => todo!("compiler should catch invalid {ty:?} type"), @@ -331,13 +335,17 @@ fn compile_data_type(opts: &Opts, ty: &Type<'_>) -> TokenStream { DataType::BoxBytes => quote! { Box::<[u8]>::decode(r) }, DataType::Tuple(types) => match types.len() { 2..=12 => { - let types = types.iter().map(|ty| compile_data_type(opts, ty)); - quote! { { Ok::<_, ::stef::buf::Error>((#(#types?,)*)) } } + let types = types.iter().map(|ty| compile_data_type(opts, ty, false)); + let length = root.then_some(quote! { ::stef::buf::decode_u64(r)?; }); + quote! { { + #length + Ok::<_, ::stef::buf::Error>((#(#types?,)*)) + } } } n => todo!("compiler should catch invalid tuple with {n} elements"), }, DataType::Array(ty, _size) => { - let ty = compile_data_type(opts, ty); + let ty = compile_data_type(opts, ty, false); quote! { ::stef::buf::decode_array(r, |r| { #ty }) } } DataType::External(ExternalType { diff --git a/crates/stef-build/src/encode.rs b/crates/stef-build/src/encode.rs index 70a29ca..6fd7663 100644 --- a/crates/stef-build/src/encode.rs +++ b/crates/stef-build/src/encode.rs @@ -51,14 +51,16 @@ fn compile_struct_fields(opts: &Opts, fields: &Fields<'_>) -> TokenStream { let name = proc_macro2::Ident::new(name.get(), Span::call_site()); if let DataType::Option(ty) = &ty.value { - let ty = compile_data_type(opts, ty, if is_copy(&ty.value) { + let (enc, ty) = compile_data_type(opts, ty, if is_copy(&ty.value) { quote! { *v } } else { quote! { v } - }); + }, true); + let id = quote! { ::stef::FieldId::new(#id, #enc) }; quote! { ::stef::buf::encode_field_option(w, #id, &self.#name, |w, v| { #ty; }); } } else { - let ty = compile_data_type(opts, ty, quote! { self.#name }); + let (enc, ty) = compile_data_type(opts, ty, quote! { self.#name }, true); + let id = quote! { ::stef::FieldId::new(#id, #enc) }; quote! { ::stef::buf::encode_field(w, #id, |w| { #ty; }); } } }, @@ -78,14 +80,16 @@ fn compile_struct_fields(opts: &Opts, fields: &Fields<'_>) -> TokenStream { let idx = proc_macro2::Literal::usize_unsuffixed(idx); if let DataType::Option(ty) = &ty.value { - let ty = compile_data_type(opts, ty, if is_copy(&ty.value) { + let (enc, ty) = compile_data_type(opts, ty, if is_copy(&ty.value) { quote! { *v } } else { quote! { v } - }); + }, true); + let id = quote !{ ::stef::FieldId::new(#id, #enc) }; quote! { ::stef::buf::encode_field_option(w, #id, &self.#idx, |w, v| { #ty; }); } }else{ - let ty = compile_data_type(opts, ty, quote! { self.#idx }); + let (enc, ty) = compile_data_type(opts, ty, quote! { self.#idx }, true); + let id = quote !{ ::stef::FieldId::new(#id, #enc) }; quote! { ::stef::buf::encode_field(w, #id, |w| { #ty; }); } } }); @@ -141,6 +145,7 @@ fn compile_variant( }: &Variant<'_>, ) -> TokenStream { let id = proc_macro2::Literal::u32_unsuffixed(id.get()); + let id = quote! { ::stef::VariantId::new(#id) }; let name = Ident::new(name.get(), Span::call_site()); let fields_body = compile_variant_fields(opts, fields); @@ -152,7 +157,7 @@ fn compile_variant( quote! { Self::#name{ #(#field_names,)* } => { - ::stef::buf::encode_id(w, #id); + ::stef::buf::encode_variant_id(w, #id); #fields_body } } @@ -165,14 +170,14 @@ fn compile_variant( quote! { Self::#name(#(#field_names,)*) => { - ::stef::buf::encode_id(w, #id); + ::stef::buf::encode_variant_id(w, #id); #fields_body } } } Fields::Unit => quote! { Self::#name => { - ::stef::buf::encode_id(w, #id); + ::stef::buf::encode_variant_id(w, #id); #fields_body } }, @@ -196,7 +201,8 @@ fn compile_variant_fields(opts: &Opts, fields: &Fields<'_>) -> TokenStream { if matches!(ty.value, DataType::Option(_)) { quote! { ::stef::buf::encode_field_option(w, #id, &#name); } } else { - let ty = compile_data_type(opts, ty, quote! { *#name }); + let (enc, ty) = compile_data_type(opts, ty, quote! { *#name }, true); + let id = quote! { ::stef::FieldId::new(#id, #enc) }; quote! { ::stef::buf::encode_field(w, #id, |w| { #ty }); } } }, @@ -214,7 +220,8 @@ fn compile_variant_fields(opts: &Opts, fields: &Fields<'_>) -> TokenStream { .map(|(idx, UnnamedField { ty, id, .. })| { let id = proc_macro2::Literal::u32_unsuffixed(id.get()); let name = Ident::new(&format!("n{idx}"), Span::call_site()); - let ty = compile_data_type(opts, ty, quote! { *#name }); + let (enc, ty) = compile_data_type(opts, ty, quote! { *#name }, true); + let id = quote! { ::stef::FieldId::new(#id, #enc) }; quote! { ::stef::buf::encode_field(w, #id, |w| { #ty }); } }); @@ -238,7 +245,7 @@ fn compile_generics(Generics(types): &Generics<'_>) -> (TokenStream, TokenStream ( quote! { <#(#types,)*> }, - quote! { where #(#types2: ::stef::buf::Encode,)* }, + quote! { where #(#types2: ::stef::buf::Encode + ::stef::buf::Size,)* }, ) }) .unwrap_or_default() @@ -264,30 +271,81 @@ fn is_copy(ty: &DataType<'_>) -> bool { } #[allow(clippy::needless_pass_by_value, clippy::too_many_lines)] -fn compile_data_type(opts: &Opts, ty: &Type<'_>, name: TokenStream) -> TokenStream { +fn compile_data_type( + opts: &Opts, + ty: &Type<'_>, + name: TokenStream, + root: bool, +) -> (TokenStream, TokenStream) { match &ty.value { - DataType::Bool => quote! { ::stef::buf::encode_bool(w, #name) }, - DataType::U8 => quote! { ::stef::buf::encode_u8(w, #name) }, - DataType::U16 => quote! { ::stef::buf::encode_u16(w, #name) }, - DataType::U32 => quote! { ::stef::buf::encode_u32(w, #name) }, - DataType::U64 => quote! { ::stef::buf::encode_u64(w, #name) }, - DataType::U128 => quote! { ::stef::buf::encode_u128(w, #name) }, - DataType::I8 => quote! { ::stef::buf::encode_i8(w, #name) }, - DataType::I16 => quote! { ::stef::buf::encode_i16(w, #name) }, - DataType::I32 => quote! { ::stef::buf::encode_i32(w, #name) }, - DataType::I64 => quote! { ::stef::buf::encode_i64(w, #name) }, - DataType::I128 => quote! { ::stef::buf::encode_i128(w, #name) }, - DataType::F32 => quote! { ::stef::buf::encode_f32(w, #name) }, - DataType::F64 => quote! { ::stef::buf::encode_f64(w, #name) }, - DataType::String | DataType::StringRef | DataType::BoxString => { - quote! { ::stef::buf::encode_string(w, &#name) } - } + DataType::Bool => ( + quote! { ::stef::FieldEncoding::Fixed1 }, + quote! { ::stef::buf::encode_bool(w, #name) }, + ), + DataType::U8 => ( + quote! { ::stef::FieldEncoding::Fixed1 }, + quote! { ::stef::buf::encode_u8(w, #name) }, + ), + DataType::U16 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_u16(w, #name) }, + ), + DataType::U32 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_u32(w, #name) }, + ), + DataType::U64 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_u64(w, #name) }, + ), + DataType::U128 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_u128(w, #name) }, + ), + DataType::I8 => ( + quote! { ::stef::FieldEncoding::Fixed1 }, + quote! { ::stef::buf::encode_i8(w, #name) }, + ), + DataType::I16 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_i16(w, #name) }, + ), + DataType::I32 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_i32(w, #name) }, + ), + DataType::I64 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_i64(w, #name) }, + ), + DataType::I128 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_i128(w, #name) }, + ), + DataType::F32 => ( + quote! { ::stef::FieldEncoding::Fixed4 }, + quote! { ::stef::buf::encode_f32(w, #name) }, + ), + DataType::F64 => ( + quote! { ::stef::FieldEncoding::Fixed8 }, + quote! { ::stef::buf::encode_f64(w, #name) }, + ), + DataType::String | DataType::StringRef | DataType::BoxString => ( + quote! { ::stef::FieldEncoding::LengthPrefixed }, + quote! { ::stef::buf::encode_string(w, &#name) }, + ), DataType::Bytes | DataType::BytesRef | DataType::BoxBytes => match opts.bytes_type { - BytesType::VecU8 => quote! { ::stef::buf::encode_bytes_std(w, &#name) }, - BytesType::Bytes => quote! { ::stef::buf::encode_bytes_bytes(w, &#name) }, + BytesType::VecU8 => ( + quote! { ::stef::FieldEncoding::LengthPrefixed }, + quote! { ::stef::buf::encode_bytes_std(w, &#name) }, + ), + BytesType::Bytes => ( + quote! { ::stef::FieldEncoding::LengthPrefixed }, + quote! { ::stef::buf::encode_bytes_bytes(w, &#name) }, + ), }, DataType::Vec(ty) => { - let ty = compile_data_type( + let size = super::size::compile_data_type( opts, ty, if is_copy(&ty.value) { @@ -296,10 +354,40 @@ fn compile_data_type(opts: &Opts, ty: &Type<'_>, name: TokenStream) -> TokenStre quote! { v } }, ); - quote! { ::stef::buf::encode_vec(w, &#name, |w, v| { #ty; }) } + let (_, encode) = compile_data_type( + opts, + ty, + if is_copy(&ty.value) { + quote! { *v } + } else { + quote! { v } + }, + false, + ); + (quote! { ::stef::FieldEncoding::LengthPrefixed }, { + quote! { ::stef::buf::encode_vec(w, &#name, |v| { #size }, |w, v| { #encode; }) } + }) } DataType::HashMap(kv) => { - let ty_k = compile_data_type( + let size_k = super::size::compile_data_type( + opts, + &kv.0, + if is_copy(&kv.0.value) { + quote! { *k } + } else { + quote! { k } + }, + ); + let size_v = super::size::compile_data_type( + opts, + &kv.1, + if is_copy(&kv.1.value) { + quote! { *v } + } else { + quote! { v } + }, + ); + let (_, encode_k) = compile_data_type( opts, &kv.0, if is_copy(&kv.0.value) { @@ -307,8 +395,9 @@ fn compile_data_type(opts: &Opts, ty: &Type<'_>, name: TokenStream) -> TokenStre } else { quote! { k } }, + false, ); - let ty_v = compile_data_type( + let (_, encode_v) = compile_data_type( opts, &kv.1, if is_copy(&kv.1.value) { @@ -316,11 +405,24 @@ fn compile_data_type(opts: &Opts, ty: &Type<'_>, name: TokenStream) -> TokenStre } else { quote! { v } }, + false, ); - quote! { ::stef::buf::encode_hash_map(w, &#name, |w, k| { #ty_k; }, |w, v| { #ty_v; }) } + ( + quote! { ::stef::FieldEncoding::LengthPrefixed }, + quote! { + ::stef::buf::encode_hash_map( + w, + &#name, + |k| { #size_k }, + |v| { #size_v }, + |w, k| { #encode_k; }, + |w, v| { #encode_v; }, + ) + }, + ) } DataType::HashSet(ty) => { - let ty = compile_data_type( + let size = super::size::compile_data_type( opts, ty, if is_copy(&ty.value) { @@ -329,10 +431,23 @@ fn compile_data_type(opts: &Opts, ty: &Type<'_>, name: TokenStream) -> TokenStre quote! { v } }, ); - quote! { ::stef::buf::encode_hash_set(w, &#name, |w, v| { #ty; }) } + let (_, encode) = compile_data_type( + opts, + ty, + if is_copy(&ty.value) { + quote! { *v } + } else { + quote! { v } + }, + false, + ); + ( + quote! { ::stef::FieldEncoding::LengthPrefixed }, + quote! { ::stef::buf::encode_hash_set(w, &#name, |v| { #size }, |w, v| { #encode; }) }, + ) } DataType::Option(ty) => { - let ty = compile_data_type( + let (_, encode) = compile_data_type( opts, ty, if is_copy(&ty.value) { @@ -340,33 +455,68 @@ fn compile_data_type(opts: &Opts, ty: &Type<'_>, name: TokenStream) -> TokenStre } else { quote! { v } }, + false, ); - quote! { ::stef::buf::encode_option(w, &#name, |w, v| { #ty; }) } + ( + quote! { ::stef::FieldEncoding::LengthPrefixed }, + quote! { ::stef::buf::encode_option(w, &#name, |w, v| { #encode; }) }, + ) } DataType::NonZero(ty) => match &ty.value { - DataType::U8 => quote! { ::stef::buf::encode_u8(w, #name.get()) }, - DataType::U16 => quote! { ::stef::buf::encode_u16(w, #name.get()) }, - DataType::U32 => quote! { ::stef::buf::encode_u32(w, #name.get()) }, - DataType::U64 => quote! { ::stef::buf::encode_u64(w, #name.get()) }, - DataType::U128 => quote! { ::stef::buf::encode_u128(w, #name.get()) }, - DataType::I8 => quote! { ::stef::buf::encode_i8(w, #name.get()) }, - DataType::I16 => quote! { ::stef::buf::encode_i16(w, #name.get()) }, - DataType::I32 => quote! { ::stef::buf::encode_i32(w, #name.get()) }, - DataType::I64 => quote! { ::stef::buf::encode_i64(w, #name.get()) }, - DataType::I128 => quote! { ::stef::buf::encode_i128(w, #name.get()) }, + DataType::U8 => ( + quote! { ::stef::FieldEncoding::Fixed1 }, + quote! { ::stef::buf::encode_u8(w, #name.get()) }, + ), + DataType::U16 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_u16(w, #name.get()) }, + ), + DataType::U32 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_u32(w, #name.get()) }, + ), + DataType::U64 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_u64(w, #name.get()) }, + ), + DataType::U128 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_u128(w, #name.get()) }, + ), + DataType::I8 => ( + quote! { ::stef::FieldEncoding::Fixed1 }, + quote! { ::stef::buf::encode_i8(w, #name.get()) }, + ), + DataType::I16 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_i16(w, #name.get()) }, + ), + DataType::I32 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_i32(w, #name.get()) }, + ), + DataType::I64 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_i64(w, #name.get()) }, + ), + DataType::I128 => ( + quote! { ::stef::FieldEncoding::Varint }, + quote! { ::stef::buf::encode_i128(w, #name.get()) }, + ), DataType::String | DataType::StringRef | DataType::Bytes | DataType::BytesRef | DataType::Vec(_) | DataType::HashMap(_) - | DataType::HashSet(_) => compile_data_type(opts, ty, quote! { #name.get() }), + | DataType::HashSet(_) => compile_data_type(opts, ty, quote! { #name.get() }, false), ty => todo!("compiler should catch invalid {ty:?} type"), }, DataType::Tuple(types) => match types.len() { 2..=12 => { - let types = types.iter().enumerate().map(|(idx, ty)| { + let encode = types.iter().enumerate().map(|(idx, ty)| { let idx = proc_macro2::Literal::usize_unsuffixed(idx); + compile_data_type( opts, ty, @@ -375,14 +525,37 @@ fn compile_data_type(opts: &Opts, ty: &Type<'_>, name: TokenStream) -> TokenStre } else { quote! { &(#name.#idx) } }, + false, ) + .1 }); - quote! { #(#types;)* } + ( + quote! { ::stef::FieldEncoding::LengthPrefixed }, + if root { + let size = types.iter().enumerate().map(|(idx, ty)| { + let idx = proc_macro2::Literal::usize_unsuffixed(idx); + + super::size::compile_data_type( + opts, + ty, + if is_copy(&ty.value) { + quote! { #name.#idx } + } else { + quote! { &(#name.#idx) } + }, + ) + }); + + quote! { ::stef::buf::encode_tuple(w, || { #(#size)+* }, |w| { #(#encode;)* }) } + } else { + quote! { #(#encode;)* } + }, + ) } n => todo!("compiler should catch invalid tuple with {n} elements"), }, DataType::Array(ty, _size) => { - let ty = compile_data_type( + let size = super::size::compile_data_type( opts, ty, if is_copy(&ty.value) { @@ -391,10 +564,24 @@ fn compile_data_type(opts: &Opts, ty: &Type<'_>, name: TokenStream) -> TokenStre quote! { v } }, ); - quote! { ::stef::buf::encode_array(w, &#name, |w, v| { #ty; }) } - } - DataType::External(_) => { - quote! { (#name).encode(w) } + let (_, encode) = compile_data_type( + opts, + ty, + if is_copy(&ty.value) { + quote! { *v } + } else { + quote! { v } + }, + false, + ); + ( + quote! { ::stef::FieldEncoding::LengthPrefixed }, + quote! { ::stef::buf::encode_array(w, &#name, |v| { #size }, |w, v| { #encode; }) }, + ) } + DataType::External(_) => ( + quote! { ::stef::FieldEncoding::LengthPrefixed }, + quote! { (#name).encode(w) }, + ), } } diff --git a/crates/stef-build/src/size.rs b/crates/stef-build/src/size.rs index e96765c..4dd2d25 100644 --- a/crates/stef-build/src/size.rs +++ b/crates/stef-build/src/size.rs @@ -279,7 +279,7 @@ fn is_copy(ty: &DataType<'_>) -> bool { } #[allow(clippy::needless_pass_by_value, clippy::too_many_lines)] -fn compile_data_type(opts: &Opts, ty: &Type<'_>, name: TokenStream) -> TokenStream { +pub(crate) fn compile_data_type(opts: &Opts, ty: &Type<'_>, name: TokenStream) -> TokenStream { match &ty.value { DataType::Bool => quote! { ::stef::buf::size_bool(#name) }, DataType::U8 => quote! { ::stef::buf::size_u8(#name) }, diff --git a/crates/stef-build/tests/snapshots/compiler__compile@enum_basic.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@enum_basic.stef.snap index bb2b34b..7dd6cf9 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@enum_basic.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@enum_basic.stef.snap @@ -28,24 +28,32 @@ impl ::stef::Encode for Sample { fn encode(&self, w: &mut impl ::stef::BufMut) { match self { Self::One => { - ::stef::buf::encode_id(w, 1); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(1)); } Self::Two(n0, n1) => { - ::stef::buf::encode_id(w, 2); - ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, *n0) }); - ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_u64(w, *n1) }); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(2)); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), + |w| { ::stef::buf::encode_u32(w, *n0) }, + ); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Varint), + |w| { ::stef::buf::encode_u64(w, *n1) }, + ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); } Self::Three { field1, field2 } => { - ::stef::buf::encode_id(w, 3); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(3)); ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, *field1) }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_bool(w, *field2) }, ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); @@ -57,17 +65,18 @@ impl ::stef::Encode for Sample { impl ::stef::Decode for Sample { #[allow(clippy::too_many_lines)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { - match ::stef::buf::decode_id(r)? { + match ::stef::buf::decode_variant_id(r)?.value { 1 => Ok(Self::One), 2 => { let mut n0: Option = None; let mut n1: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => n0 = Some(::stef::buf::decode_u32(r)?), 2 => n1 = Some(::stef::buf::decode_u64(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok( @@ -89,11 +98,12 @@ impl ::stef::Decode for Sample { let mut field1: Option = None; let mut field2: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => field1 = Some(::stef::buf::decode_u32(r)?), 2 => field2 = Some(::stef::buf::decode_bool(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self::Three { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@enum_generics.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@enum_generics.stef.snap index c9404ac..f9bab3d 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@enum_generics.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@enum_generics.stef.snap @@ -16,10 +16,10 @@ pub enum Sample { #[automatically_derived] impl ::stef::Encode for Sample where - A: ::stef::buf::Encode, - B: ::stef::buf::Encode, - C: ::stef::buf::Encode, - D: ::stef::buf::Encode, + A: ::stef::buf::Encode + ::stef::buf::Size, + B: ::stef::buf::Encode + ::stef::buf::Size, + C: ::stef::buf::Encode + ::stef::buf::Size, + D: ::stef::buf::Encode + ::stef::buf::Size, { #[allow( clippy::borrow_deref_ref, @@ -29,18 +29,34 @@ where fn encode(&self, w: &mut impl ::stef::BufMut) { match self { Self::One => { - ::stef::buf::encode_id(w, 1); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(1)); } Self::Two(n0, n1) => { - ::stef::buf::encode_id(w, 2); - ::stef::buf::encode_field(w, 1, |w| { (*n0).encode(w) }); - ::stef::buf::encode_field(w, 2, |w| { (*n1).encode(w) }); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(2)); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(1, ::stef::FieldEncoding::LengthPrefixed), + |w| { (*n0).encode(w) }, + ); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), + |w| { (*n1).encode(w) }, + ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); } Self::Three { field1, field2 } => { - ::stef::buf::encode_id(w, 3); - ::stef::buf::encode_field(w, 1, |w| { (*field1).encode(w) }); - ::stef::buf::encode_field(w, 2, |w| { (*field2).encode(w) }); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(3)); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(1, ::stef::FieldEncoding::LengthPrefixed), + |w| { (*field1).encode(w) }, + ); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), + |w| { (*field2).encode(w) }, + ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); } } @@ -56,17 +72,18 @@ where { #[allow(clippy::too_many_lines)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { - match ::stef::buf::decode_id(r)? { + match ::stef::buf::decode_variant_id(r)?.value { 1 => Ok(Self::One), 2 => { let mut n0: Option = None; let mut n1: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => n0 = Some(A::decode(r)?), 2 => n1 = Some(B::decode(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok( @@ -88,11 +105,12 @@ where let mut field1: Option = None; let mut field2: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => field1 = Some(C::decode(r)?), 2 => field2 = Some(D::decode(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self::Three { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@enum_many_ws.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@enum_many_ws.stef.snap index b479511..c5fea89 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@enum_many_ws.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@enum_many_ws.stef.snap @@ -23,24 +23,32 @@ impl ::stef::Encode for Sample { fn encode(&self, w: &mut impl ::stef::BufMut) { match self { Self::One => { - ::stef::buf::encode_id(w, 1); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(1)); } Self::Two(n0, n1) => { - ::stef::buf::encode_id(w, 2); - ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, *n0) }); - ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_u64(w, *n1) }); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(2)); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), + |w| { ::stef::buf::encode_u32(w, *n0) }, + ); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Varint), + |w| { ::stef::buf::encode_u64(w, *n1) }, + ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); } Self::Three { field1, field2 } => { - ::stef::buf::encode_id(w, 3); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(3)); ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, *field1) }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_bool(w, *field2) }, ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); @@ -52,17 +60,18 @@ impl ::stef::Encode for Sample { impl ::stef::Decode for Sample { #[allow(clippy::too_many_lines)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { - match ::stef::buf::decode_id(r)? { + match ::stef::buf::decode_variant_id(r)?.value { 1 => Ok(Self::One), 2 => { let mut n0: Option = None; let mut n1: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => n0 = Some(::stef::buf::decode_u32(r)?), 2 => n1 = Some(::stef::buf::decode_u64(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok( @@ -84,11 +93,12 @@ impl ::stef::Decode for Sample { let mut field1: Option = None; let mut field2: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => field1 = Some(::stef::buf::decode_u32(r)?), 2 => field2 = Some(::stef::buf::decode_bool(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self::Three { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@enum_min_ws.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@enum_min_ws.stef.snap index 1470aaa..3200202 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@enum_min_ws.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@enum_min_ws.stef.snap @@ -15,7 +15,7 @@ pub enum Sample { #[automatically_derived] impl ::stef::Encode for Sample where - T: ::stef::buf::Encode, + T: ::stef::buf::Encode + ::stef::buf::Size, { #[allow( clippy::borrow_deref_ref, @@ -25,28 +25,44 @@ where fn encode(&self, w: &mut impl ::stef::BufMut) { match self { Self::One => { - ::stef::buf::encode_id(w, 1); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(1)); } Self::Two(n0, n1, n2) => { - ::stef::buf::encode_id(w, 2); - ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, *n0) }); - ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_u64(w, *n1) }); - ::stef::buf::encode_field(w, 3, |w| { (*n2).encode(w) }); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(2)); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), + |w| { ::stef::buf::encode_u32(w, *n0) }, + ); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Varint), + |w| { ::stef::buf::encode_u64(w, *n1) }, + ); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(3, ::stef::FieldEncoding::LengthPrefixed), + |w| { (*n2).encode(w) }, + ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); } Self::Three { field1, field2, field3 } => { - ::stef::buf::encode_id(w, 3); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(3)); ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, *field1) }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_bool(w, *field2) }, ); - ::stef::buf::encode_field(w, 3, |w| { (*field3).encode(w) }); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(3, ::stef::FieldEncoding::LengthPrefixed), + |w| { (*field3).encode(w) }, + ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); } } @@ -59,19 +75,20 @@ where { #[allow(clippy::too_many_lines)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { - match ::stef::buf::decode_id(r)? { + match ::stef::buf::decode_variant_id(r)?.value { 1 => Ok(Self::One), 2 => { let mut n0: Option = None; let mut n1: Option = None; let mut n2: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => n0 = Some(::stef::buf::decode_u32(r)?), 2 => n1 = Some(::stef::buf::decode_u64(r)?), 3 => n2 = Some(T::decode(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok( @@ -99,12 +116,13 @@ where let mut field2: Option = None; let mut field3: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => field1 = Some(::stef::buf::decode_u32(r)?), 2 => field2 = Some(::stef::buf::decode_bool(r)?), 3 => field3 = Some(T::decode(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self::Three { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@mixed.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@mixed.stef.snap index 5cc3a8b..6e01b34 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@mixed.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@mixed.stef.snap @@ -29,14 +29,14 @@ impl ::stef::Encode for User { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::LengthPrefixed), |w| { (self.name).encode(w); }, ); ::stef::buf::encode_field_option( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), &self.address, |w, v| { (v).encode(w); @@ -44,14 +44,14 @@ impl ::stef::Encode for User { ); ::stef::buf::encode_field( w, - 3, + ::stef::FieldId::new(3, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_u8(w, self.age); }, ); ::stef::buf::encode_field( w, - 4, + ::stef::FieldId::new(4, ::stef::FieldEncoding::LengthPrefixed), |w| { (self.birthday).encode(w); }, @@ -68,13 +68,14 @@ impl ::stef::Decode for User { let mut age: Option = None; let mut birthday: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => name = Some(FullName::decode(r)?), 2 => address = Some(Address::decode(r)?), 3 => age = Some(::stef::buf::decode_u8(r)?), 4 => birthday = Some(birthday::DayOfBirth::decode(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { @@ -135,14 +136,14 @@ impl ::stef::Encode for FullName { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_string(w, &self.first); }, ); ::stef::buf::encode_field_option( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), &self.middle, |w, v| { ::stef::buf::encode_string(w, &v); @@ -150,7 +151,7 @@ impl ::stef::Encode for FullName { ); ::stef::buf::encode_field( w, - 3, + ::stef::FieldId::new(3, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_string(w, &self.last); }, @@ -166,12 +167,13 @@ impl ::stef::Decode for FullName { let mut middle: Option = None; let mut last: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => first = Some(::stef::buf::decode_string(r)?), 2 => middle = Some(::stef::buf::decode_string(r)?), 3 => last = Some(::stef::buf::decode_string(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { @@ -233,21 +235,21 @@ impl ::stef::Encode for Address { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_string(w, &self.street); }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), |w| { (self.house_no).encode(w); }, ); ::stef::buf::encode_field( w, - 3, + ::stef::FieldId::new(3, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_string(w, &self.city); }, @@ -263,12 +265,13 @@ impl ::stef::Decode for Address { let mut house_no: Option = None; let mut city: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => street = Some(::stef::buf::decode_string(r)?), 2 => house_no = Some(HouseNumber::decode(r)?), 3 => city = Some(::stef::buf::decode_string(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { @@ -326,15 +329,19 @@ impl ::stef::Encode for HouseNumber { fn encode(&self, w: &mut impl ::stef::BufMut) { match self { Self::Digit(n0) => { - ::stef::buf::encode_id(w, 1); - ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u16(w, *n0) }); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(1)); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), + |w| { ::stef::buf::encode_u16(w, *n0) }, + ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); } Self::Text(n0) => { - ::stef::buf::encode_id(w, 2); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(2)); ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_string(w, &*n0) }, ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); @@ -346,14 +353,15 @@ impl ::stef::Encode for HouseNumber { impl ::stef::Decode for HouseNumber { #[allow(clippy::too_many_lines)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { - match ::stef::buf::decode_id(r)? { + match ::stef::buf::decode_variant_id(r)?.value { 1 => { let mut n0: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => n0 = Some(::stef::buf::decode_u16(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok( @@ -369,10 +377,11 @@ impl ::stef::Decode for HouseNumber { 2 => { let mut n0: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => n0 = Some(::stef::buf::decode_string(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok( @@ -442,27 +451,31 @@ pub mod birthday { fn encode(&self, w: &mut impl ::stef::BufMut) { match self { Self::Specific { year, month, day } => { - ::stef::buf::encode_id(w, 1); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(1)); ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u16(w, *year) }, ); - ::stef::buf::encode_field(w, 2, |w| { (*month).encode(w) }); ::stef::buf::encode_field( w, - 3, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), + |w| { (*month).encode(w) }, + ); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(3, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_u8(w, *day) }, ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); } Self::Secret { reason } => { - ::stef::buf::encode_id(w, 2); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(2)); ::stef::buf::encode_field_option(w, 1, &reason); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); } Self::Unknown => { - ::stef::buf::encode_id(w, 3); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(3)); } } } @@ -471,18 +484,19 @@ pub mod birthday { impl ::stef::Decode for DayOfBirth { #[allow(clippy::too_many_lines)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { - match ::stef::buf::decode_id(r)? { + match ::stef::buf::decode_variant_id(r)?.value { 1 => { let mut year: Option = None; let mut month: Option = None; let mut day: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => year = Some(::stef::buf::decode_u16(r)?), 2 => month = Some(Month::decode(r)?), 3 => day = Some(::stef::buf::decode_u8(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self::Specific { @@ -506,10 +520,11 @@ pub mod birthday { 2 => { let mut reason: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => reason = Some(::stef::buf::decode_string(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self::Secret { reason }) @@ -578,40 +593,40 @@ pub mod birthday { fn encode(&self, w: &mut impl ::stef::BufMut) { match self { Self::January => { - ::stef::buf::encode_id(w, 1); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(1)); } Self::February => { - ::stef::buf::encode_id(w, 2); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(2)); } Self::March => { - ::stef::buf::encode_id(w, 3); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(3)); } Self::April => { - ::stef::buf::encode_id(w, 4); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(4)); } Self::May => { - ::stef::buf::encode_id(w, 5); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(5)); } Self::June => { - ::stef::buf::encode_id(w, 6); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(6)); } Self::July => { - ::stef::buf::encode_id(w, 7); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(7)); } Self::August => { - ::stef::buf::encode_id(w, 8); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(8)); } Self::September => { - ::stef::buf::encode_id(w, 9); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(9)); } Self::October => { - ::stef::buf::encode_id(w, 10); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(10)); } Self::November => { - ::stef::buf::encode_id(w, 11); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(11)); } Self::December => { - ::stef::buf::encode_id(w, 12); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(12)); } } } @@ -620,7 +635,7 @@ pub mod birthday { impl ::stef::Decode for Month { #[allow(clippy::too_many_lines)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { - match ::stef::buf::decode_id(r)? { + match ::stef::buf::decode_variant_id(r)?.value { 1 => Ok(Self::January), 2 => Ok(Self::February), 3 => Ok(Self::March), diff --git a/crates/stef-build/tests/snapshots/compiler__compile@module_basic.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@module_basic.stef.snap index 0648ae3..1d0f857 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@module_basic.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@module_basic.stef.snap @@ -27,7 +27,7 @@ pub mod a { fn encode(&self, w: &mut impl ::stef::BufMut) { match self { Self::One => { - ::stef::buf::encode_id(w, 1); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(1)); } } } @@ -36,7 +36,7 @@ pub mod a { impl ::stef::Decode for Sample { #[allow(clippy::too_many_lines)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { - match ::stef::buf::decode_id(r)? { + match ::stef::buf::decode_variant_id(r)?.value { 1 => Ok(Self::One), id => Err(::stef::buf::Error::UnknownVariant(id)), } @@ -73,14 +73,14 @@ pub mod a { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, self.value); }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), |w| { (self.inner).encode(w); }, @@ -95,11 +95,12 @@ pub mod a { let mut value: Option = None; let mut inner: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => value = Some(::stef::buf::decode_u32(r)?), 2 => inner = Some(b::Sample::decode(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@schema_basic.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@schema_basic.stef.snap index 786304d..41c6aae 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@schema_basic.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@schema_basic.stef.snap @@ -23,14 +23,14 @@ impl ::stef::Encode for SampleStruct { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, self.a); }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_bool(w, self.b); }, @@ -45,11 +45,12 @@ impl ::stef::Decode for SampleStruct { let mut a: Option = None; let mut b: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => a = Some(::stef::buf::decode_u32(r)?), 2 => b = Some(::stef::buf::decode_bool(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { @@ -98,24 +99,32 @@ impl ::stef::Encode for SampleEnum { fn encode(&self, w: &mut impl ::stef::BufMut) { match self { Self::One => { - ::stef::buf::encode_id(w, 1); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(1)); } Self::Two(n0, n1) => { - ::stef::buf::encode_id(w, 2); - ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, *n0) }); - ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_u64(w, *n1) }); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(2)); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), + |w| { ::stef::buf::encode_u32(w, *n0) }, + ); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Varint), + |w| { ::stef::buf::encode_u64(w, *n1) }, + ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); } Self::Three { field1, field2 } => { - ::stef::buf::encode_id(w, 3); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(3)); ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, *field1) }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_bool(w, *field2) }, ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); @@ -127,17 +136,18 @@ impl ::stef::Encode for SampleEnum { impl ::stef::Decode for SampleEnum { #[allow(clippy::too_many_lines)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { - match ::stef::buf::decode_id(r)? { + match ::stef::buf::decode_variant_id(r)?.value { 1 => Ok(Self::One), 2 => { let mut n0: Option = None; let mut n1: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => n0 = Some(::stef::buf::decode_u32(r)?), 2 => n1 = Some(::stef::buf::decode_u64(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok( @@ -159,11 +169,12 @@ impl ::stef::Decode for SampleEnum { let mut field1: Option = None; let mut field2: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => field1 = Some(::stef::buf::decode_u32(r)?), 2 => field2 = Some(::stef::buf::decode_bool(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self::Three { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@struct_basic.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@struct_basic.stef.snap index 9505a4f..16d26e6 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@struct_basic.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@struct_basic.stef.snap @@ -24,14 +24,14 @@ impl ::stef::Encode for Sample { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, self.a); }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_bool(w, self.b); }, @@ -46,11 +46,12 @@ impl ::stef::Decode for Sample { let mut a: Option = None; let mut b: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => a = Some(::stef::buf::decode_u32(r)?), 2 => b = Some(::stef::buf::decode_bool(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@struct_generics.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@struct_generics.stef.snap index 87cdc0b..8f79390 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@struct_generics.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@struct_generics.stef.snap @@ -15,8 +15,8 @@ pub struct KeyValue { #[automatically_derived] impl ::stef::Encode for KeyValue where - K: ::stef::buf::Encode, - V: ::stef::buf::Encode, + K: ::stef::buf::Encode + ::stef::buf::Size, + V: ::stef::buf::Encode + ::stef::buf::Size, { #[allow( clippy::borrow_deref_ref, @@ -27,14 +27,14 @@ where fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::LengthPrefixed), |w| { (self.key).encode(w); }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), |w| { (self.value).encode(w); }, @@ -53,11 +53,12 @@ where let mut key: Option = None; let mut value: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => key = Some(K::decode(r)?), 2 => value = Some(V::decode(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@struct_many_ws.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@struct_many_ws.stef.snap index 460f1bc..b1df045 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@struct_many_ws.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@struct_many_ws.stef.snap @@ -16,7 +16,7 @@ pub struct Sample { #[automatically_derived] impl ::stef::Encode for Sample where - T: ::stef::buf::Encode, + T: ::stef::buf::Encode + ::stef::buf::Size, { #[allow( clippy::borrow_deref_ref, @@ -27,21 +27,21 @@ where fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, self.a); }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_bool(w, self.b); }, ); ::stef::buf::encode_field( w, - 3, + ::stef::FieldId::new(3, ::stef::FieldEncoding::LengthPrefixed), |w| { (self.c).encode(w); }, @@ -60,12 +60,13 @@ where let mut b: Option = None; let mut c: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => a = Some(::stef::buf::decode_u32(r)?), 2 => b = Some(::stef::buf::decode_bool(r)?), 3 => c = Some(T::decode(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@struct_min_ws.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@struct_min_ws.stef.snap index 1a7014e..a041faf 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@struct_min_ws.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@struct_min_ws.stef.snap @@ -15,7 +15,7 @@ pub struct Sample { #[automatically_derived] impl ::stef::Encode for Sample where - T: ::stef::buf::Encode, + T: ::stef::buf::Encode + ::stef::buf::Size, { #[allow( clippy::borrow_deref_ref, @@ -26,21 +26,21 @@ where fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, self.a); }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_bool(w, self.b); }, ); ::stef::buf::encode_field( w, - 3, + ::stef::FieldId::new(3, ::stef::FieldEncoding::LengthPrefixed), |w| { (self.c).encode(w); }, @@ -59,12 +59,13 @@ where let mut b: Option = None; let mut c: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => a = Some(::stef::buf::decode_u32(r)?), 2 => b = Some(::stef::buf::decode_bool(r)?), 3 => c = Some(T::decode(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@struct_tuple.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@struct_tuple.stef.snap index ddeb50d..d8789a1 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@struct_tuple.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@struct_tuple.stef.snap @@ -20,14 +20,14 @@ impl ::stef::Encode for Sample { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, self.0); }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_bool(w, self.1); }, @@ -42,11 +42,12 @@ impl ::stef::Decode for Sample { let mut n0: Option = None; let mut n1: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => n0 = Some(::stef::buf::decode_u32(r)?), 2 => n1 = Some(::stef::buf::decode_bool(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok( diff --git a/crates/stef-build/tests/snapshots/compiler__compile@types_basic.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@types_basic.stef.snap index d03920c..f6020e8 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@types_basic.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@types_basic.stef.snap @@ -41,153 +41,164 @@ impl ::stef::Encode for Sample { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_bool(w, self.f01); }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_u8(w, self.f02); }, ); ::stef::buf::encode_field( w, - 3, + ::stef::FieldId::new(3, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u16(w, self.f03); }, ); ::stef::buf::encode_field( w, - 4, + ::stef::FieldId::new(4, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, self.f04); }, ); ::stef::buf::encode_field( w, - 5, + ::stef::FieldId::new(5, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u64(w, self.f05); }, ); ::stef::buf::encode_field( w, - 6, + ::stef::FieldId::new(6, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u128(w, self.f06); }, ); ::stef::buf::encode_field( w, - 7, + ::stef::FieldId::new(7, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_i8(w, self.f07); }, ); ::stef::buf::encode_field( w, - 8, + ::stef::FieldId::new(8, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_i16(w, self.f08); }, ); ::stef::buf::encode_field( w, - 9, + ::stef::FieldId::new(9, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_i32(w, self.f09); }, ); ::stef::buf::encode_field( w, - 10, + ::stef::FieldId::new(10, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_i64(w, self.f10); }, ); ::stef::buf::encode_field( w, - 11, + ::stef::FieldId::new(11, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_i128(w, self.f11); }, ); ::stef::buf::encode_field( w, - 12, + ::stef::FieldId::new(12, ::stef::FieldEncoding::Fixed4), |w| { ::stef::buf::encode_f32(w, self.f12); }, ); ::stef::buf::encode_field( w, - 13, + ::stef::FieldId::new(13, ::stef::FieldEncoding::Fixed8), |w| { ::stef::buf::encode_f64(w, self.f13); }, ); ::stef::buf::encode_field( w, - 14, + ::stef::FieldId::new(14, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_string(w, &self.f14); }, ); ::stef::buf::encode_field( w, - 15, + ::stef::FieldId::new(15, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_string(w, &self.f15); }, ); ::stef::buf::encode_field( w, - 16, + ::stef::FieldId::new(16, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_bytes_std(w, &self.f16); }, ); ::stef::buf::encode_field( w, - 17, + ::stef::FieldId::new(17, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_bytes_std(w, &self.f17); }, ); ::stef::buf::encode_field( w, - 18, + ::stef::FieldId::new(18, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_string(w, &self.f18); }, ); ::stef::buf::encode_field( w, - 19, + ::stef::FieldId::new(19, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_bytes_std(w, &self.f19); }, ); ::stef::buf::encode_field( w, - 20, + ::stef::FieldId::new(20, ::stef::FieldEncoding::LengthPrefixed), |w| { - ::stef::buf::encode_u32(w, self.f20.0); - ::stef::buf::encode_u32(w, self.f20.1); - ::stef::buf::encode_u32(w, self.f20.2); + ::stef::buf::encode_tuple( + w, + || { + ::stef::buf::size_u32(self.f20.0) + + ::stef::buf::size_u32(self.f20.1) + + ::stef::buf::size_u32(self.f20.2) + }, + |w| { + ::stef::buf::encode_u32(w, self.f20.0); + ::stef::buf::encode_u32(w, self.f20.1); + ::stef::buf::encode_u32(w, self.f20.2); + }, + ); }, ); ::stef::buf::encode_field( w, - 21, + ::stef::FieldId::new(21, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_array( w, &self.f21, + |v| { ::stef::buf::size_u32(*v) }, |w, v| { ::stef::buf::encode_u32(w, *v); }, @@ -223,7 +234,8 @@ impl ::stef::Decode for Sample { let mut f20: Option<(u32, u32, u32)> = None; let mut f21: Option<[u32; 12]> = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => f01 = Some(::stef::buf::decode_bool(r)?), 2 => f02 = Some(::stef::buf::decode_u8(r)?), @@ -247,6 +259,7 @@ impl ::stef::Decode for Sample { 20 => { f20 = Some( { + ::stef::buf::decode_u64(r)?; Ok::< _, ::stef::buf::Error, @@ -263,7 +276,7 @@ impl ::stef::Decode for Sample { ::stef::buf::decode_array(r, |r| { ::stef::buf::decode_u32(r) })?, ); } - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@types_generic.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@types_generic.stef.snap index 40f20d8..0729505 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@types_generic.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@types_generic.stef.snap @@ -25,11 +25,12 @@ impl ::stef::Encode for Sample { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_vec( w, &self.f1, + |v| { ::stef::buf::size_u32(*v) }, |w, v| { ::stef::buf::encode_u32(w, *v); }, @@ -38,11 +39,13 @@ impl ::stef::Encode for Sample { ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_hash_map( w, &self.f2, + |k| { ::stef::buf::size_u32(*k) }, + |v| { ::stef::buf::size_string(&v) }, |w, k| { ::stef::buf::encode_u32(w, *k); }, @@ -54,11 +57,12 @@ impl ::stef::Encode for Sample { ); ::stef::buf::encode_field( w, - 3, + ::stef::FieldId::new(3, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_hash_set( w, &self.f3, + |v| { ::stef::buf::size_u32(*v) }, |w, v| { ::stef::buf::encode_u32(w, *v); }, @@ -67,7 +71,7 @@ impl ::stef::Encode for Sample { ); ::stef::buf::encode_field_option( w, - 4, + ::stef::FieldId::new(4, ::stef::FieldEncoding::Varint), &self.f4, |w, v| { ::stef::buf::encode_u32(w, *v); @@ -75,7 +79,7 @@ impl ::stef::Encode for Sample { ); ::stef::buf::encode_field( w, - 5, + ::stef::FieldId::new(5, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, self.f5.get()); }, @@ -93,7 +97,8 @@ impl ::stef::Decode for Sample { let mut f4: Option = None; let mut f5: Option<::std::num::NonZeroU32> = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => { f1 = Some( @@ -119,7 +124,7 @@ impl ::stef::Decode for Sample { } 4 => f4 = Some(::stef::buf::decode_u32(r)?), 5 => f5 = Some(::stef::buf::decode_non_zero_u32(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { @@ -207,11 +212,12 @@ impl ::stef::Encode for SampleUnnamed { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_vec( w, &self.0, + |v| { ::stef::buf::size_u32(*v) }, |w, v| { ::stef::buf::encode_u32(w, *v); }, @@ -220,11 +226,13 @@ impl ::stef::Encode for SampleUnnamed { ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_hash_map( w, &self.1, + |k| { ::stef::buf::size_u32(*k) }, + |v| { ::stef::buf::size_string(&v) }, |w, k| { ::stef::buf::encode_u32(w, *k); }, @@ -236,11 +244,12 @@ impl ::stef::Encode for SampleUnnamed { ); ::stef::buf::encode_field( w, - 3, + ::stef::FieldId::new(3, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_hash_set( w, &self.2, + |v| { ::stef::buf::size_u32(*v) }, |w, v| { ::stef::buf::encode_u32(w, *v); }, @@ -249,7 +258,7 @@ impl ::stef::Encode for SampleUnnamed { ); ::stef::buf::encode_field_option( w, - 4, + ::stef::FieldId::new(4, ::stef::FieldEncoding::Varint), &self.3, |w, v| { ::stef::buf::encode_u32(w, *v); @@ -257,7 +266,7 @@ impl ::stef::Encode for SampleUnnamed { ); ::stef::buf::encode_field( w, - 5, + ::stef::FieldId::new(5, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, self.4.get()); }, @@ -275,7 +284,8 @@ impl ::stef::Decode for SampleUnnamed { let mut n3: Option = None; let mut n4: Option<::std::num::NonZeroU32> = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => { n0 = Some( @@ -301,7 +311,7 @@ impl ::stef::Decode for SampleUnnamed { } 4 => n3 = Some(::stef::buf::decode_u32(r)?), 5 => n4 = Some(::stef::buf::decode_non_zero_u32(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok( diff --git a/crates/stef-build/tests/snapshots/compiler__compile@types_nested.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@types_nested.stef.snap index cd23749..0fac5dd 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@types_nested.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@types_nested.stef.snap @@ -21,11 +21,23 @@ impl ::stef::Encode for Sample { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_vec( w, &self.value, + |v| { + ::stef::buf::size_option( + v.as_ref(), + |v| { + ::stef::buf::size_hash_map( + &v.get(), + |k| { ::stef::buf::size_i64(*k) }, + |v| { ::stef::buf::size_string(&v) }, + ) + }, + ) + }, |w, v| { ::stef::buf::encode_option( w, @@ -34,6 +46,8 @@ impl ::stef::Encode for Sample { ::stef::buf::encode_hash_map( w, &v.get(), + |k| { ::stef::buf::size_i64(*k) }, + |v| { ::stef::buf::size_string(&v) }, |w, k| { ::stef::buf::encode_i64(w, *k); }, @@ -56,7 +70,8 @@ impl ::stef::Decode for Sample { fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { let mut value: Option>>>> = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => { value = Some( @@ -77,7 +92,7 @@ impl ::stef::Decode for Sample { )?, ); } - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@types_non_zero.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@types_non_zero.stef.snap index 1de9d21..215e161 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@types_non_zero.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@types_non_zero.stef.snap @@ -35,95 +35,96 @@ impl ::stef::Encode for Sample { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_u8(w, self.f01.get()); }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u16(w, self.f02.get()); }, ); ::stef::buf::encode_field( w, - 3, + ::stef::FieldId::new(3, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, self.f03.get()); }, ); ::stef::buf::encode_field( w, - 4, + ::stef::FieldId::new(4, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u64(w, self.f04.get()); }, ); ::stef::buf::encode_field( w, - 5, + ::stef::FieldId::new(5, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u128(w, self.f05.get()); }, ); ::stef::buf::encode_field( w, - 6, + ::stef::FieldId::new(6, ::stef::FieldEncoding::Fixed1), |w| { ::stef::buf::encode_i8(w, self.f06.get()); }, ); ::stef::buf::encode_field( w, - 7, + ::stef::FieldId::new(7, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_i16(w, self.f07.get()); }, ); ::stef::buf::encode_field( w, - 8, + ::stef::FieldId::new(8, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_i32(w, self.f08.get()); }, ); ::stef::buf::encode_field( w, - 9, + ::stef::FieldId::new(9, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_i64(w, self.f09.get()); }, ); ::stef::buf::encode_field( w, - 10, + ::stef::FieldId::new(10, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_i128(w, self.f10.get()); }, ); ::stef::buf::encode_field( w, - 11, + ::stef::FieldId::new(11, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_string(w, &self.f11.get()); }, ); ::stef::buf::encode_field( w, - 12, + ::stef::FieldId::new(12, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_bytes_std(w, &self.f12.get()); }, ); ::stef::buf::encode_field( w, - 13, + ::stef::FieldId::new(13, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_vec( w, &self.f13.get(), + |v| { ::stef::buf::size_string(&v) }, |w, v| { ::stef::buf::encode_string(w, &v); }, @@ -132,11 +133,13 @@ impl ::stef::Encode for Sample { ); ::stef::buf::encode_field( w, - 14, + ::stef::FieldId::new(14, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_hash_map( w, &self.f14.get(), + |k| { ::stef::buf::size_string(&k) }, + |v| { ::stef::buf::size_bytes_std(&v) }, |w, k| { ::stef::buf::encode_string(w, &k); }, @@ -148,11 +151,12 @@ impl ::stef::Encode for Sample { ); ::stef::buf::encode_field( w, - 15, + ::stef::FieldId::new(15, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_hash_set( w, &self.f15.get(), + |v| { ::stef::buf::size_string(&v) }, |w, v| { ::stef::buf::encode_string(w, &v); }, @@ -182,7 +186,8 @@ impl ::stef::Decode for Sample { let mut f14: Option<::stef::NonZeroHashMap>> = None; let mut f15: Option<::stef::NonZeroHashSet> = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => f01 = Some(::stef::buf::decode_non_zero_u8(r)?), 2 => f02 = Some(::stef::buf::decode_non_zero_u16(r)?), @@ -221,7 +226,7 @@ impl ::stef::Decode for Sample { )?, ); } - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@types_ref.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@types_ref.stef.snap index ba15fc1..63e7865 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile@types_ref.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile@types_ref.stef.snap @@ -22,14 +22,14 @@ impl ::stef::Encode for Sample { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::LengthPrefixed), |w| { (self.basic).encode(w); }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), |w| { (self.with_generics).encode(w); }, @@ -44,11 +44,12 @@ impl ::stef::Decode for Sample { let mut basic: Option = None; let mut with_generics: Option> = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => basic = Some(Test123::decode(r)?), 2 => with_generics = Some(KeyValue::::decode(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { @@ -94,7 +95,7 @@ impl ::stef::Encode for Test123 { fn encode(&self, w: &mut impl ::stef::BufMut) { match self { Self::Value => { - ::stef::buf::encode_id(w, 1); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(1)); } } } @@ -103,7 +104,7 @@ impl ::stef::Encode for Test123 { impl ::stef::Decode for Test123 { #[allow(clippy::too_many_lines)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { - match ::stef::buf::decode_id(r)? { + match ::stef::buf::decode_variant_id(r)?.value { 1 => Ok(Self::Value), id => Err(::stef::buf::Error::UnknownVariant(id)), } @@ -131,8 +132,8 @@ pub struct KeyValue { #[automatically_derived] impl ::stef::Encode for KeyValue where - K: ::stef::buf::Encode, - V: ::stef::buf::Encode, + K: ::stef::buf::Encode + ::stef::buf::Size, + V: ::stef::buf::Encode + ::stef::buf::Size, { #[allow( clippy::borrow_deref_ref, @@ -143,14 +144,14 @@ where fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::LengthPrefixed), |w| { (self.key).encode(w); }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), |w| { (self.value).encode(w); }, @@ -169,11 +170,12 @@ where let mut key: Option = None; let mut value: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => key = Some(K::decode(r)?), 2 => value = Some(V::decode(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { diff --git a/crates/stef-build/tests/snapshots/compiler__compile_extra@enum.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile_extra@enum.stef.snap index ee855e5..1d69532 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile_extra@enum.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile_extra@enum.stef.snap @@ -23,28 +23,37 @@ impl ::stef::Encode for Sample { fn encode(&self, w: &mut impl ::stef::BufMut) { match self { Self::Variant1 => { - ::stef::buf::encode_id(w, 1); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(1)); } Self::Variant2(n0, n1) => { - ::stef::buf::encode_id(w, 2); - ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, *n0) }); - ::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_u8(w, *n1) }); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(2)); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), + |w| { ::stef::buf::encode_u32(w, *n0) }, + ); + ::stef::buf::encode_field( + w, + ::stef::FieldId::new(2, ::stef::FieldEncoding::Fixed1), + |w| { ::stef::buf::encode_u8(w, *n1) }, + ); ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); } Self::Variant3 { field1, field2 } => { - ::stef::buf::encode_id(w, 3); + ::stef::buf::encode_variant_id(w, ::stef::VariantId::new(3)); ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_string(w, &*field1) }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_vec( w, &*field2, + |v| { ::stef::buf::size_bool(*v) }, |w, v| { ::stef::buf::encode_bool(w, *v); }, @@ -60,17 +69,18 @@ impl ::stef::Encode for Sample { impl ::stef::Decode for Sample { #[allow(clippy::too_many_lines)] fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { - match ::stef::buf::decode_id(r)? { + match ::stef::buf::decode_variant_id(r)?.value { 1 => Ok(Self::Variant1), 2 => { let mut n0: Option = None; let mut n1: Option = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => n0 = Some(::stef::buf::decode_u32(r)?), 2 => n1 = Some(::stef::buf::decode_u8(r)?), - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok( @@ -92,7 +102,8 @@ impl ::stef::Decode for Sample { let mut field1: Option = None; let mut field2: Option> = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => field1 = Some(::stef::buf::decode_string(r)?), 2 => { @@ -103,7 +114,7 @@ impl ::stef::Decode for Sample { )?, ); } - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self::Variant3 { diff --git a/crates/stef-build/tests/snapshots/compiler__compile_extra@struct.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile_extra@struct.stef.snap index 84feb20..1acc02d 100644 --- a/crates/stef-build/tests/snapshots/compiler__compile_extra@struct.stef.snap +++ b/crates/stef-build/tests/snapshots/compiler__compile_extra@struct.stef.snap @@ -24,28 +24,41 @@ impl ::stef::Encode for Sample { fn encode(&self, w: &mut impl ::stef::BufMut) { ::stef::buf::encode_field( w, - 1, + ::stef::FieldId::new(1, ::stef::FieldEncoding::Varint), |w| { ::stef::buf::encode_u32(w, self.field1); }, ); ::stef::buf::encode_field( w, - 2, + ::stef::FieldId::new(2, ::stef::FieldEncoding::LengthPrefixed), |w| { ::stef::buf::encode_bytes_std(w, &self.field2); }, ); ::stef::buf::encode_field( w, - 3, + ::stef::FieldId::new(3, ::stef::FieldEncoding::LengthPrefixed), |w| { - ::stef::buf::encode_bool(w, self.field3.0); - ::stef::buf::encode_array( + ::stef::buf::encode_tuple( w, - &&(self.field3.1), - |w, v| { - ::stef::buf::encode_i16(w, *v); + || { + ::stef::buf::size_bool(self.field3.0) + + ::stef::buf::size_array( + &&(self.field3.1), + |v| { ::stef::buf::size_i16(*v) }, + ) + }, + |w| { + ::stef::buf::encode_bool(w, self.field3.0); + ::stef::buf::encode_array( + w, + &&(self.field3.1), + |v| { ::stef::buf::size_i16(*v) }, + |w, v| { + ::stef::buf::encode_i16(w, *v); + }, + ); }, ); }, @@ -61,13 +74,15 @@ impl ::stef::Decode for Sample { let mut field2: Option> = None; let mut field3: Option<(bool, [i16; 4])> = None; loop { - match ::stef::buf::decode_id(r)? { + let id = ::stef::buf::decode_id(r)?; + match id.value { ::stef::buf::END_MARKER => break, 1 => field1 = Some(::stef::buf::decode_u32(r)?), 2 => field2 = Some(::stef::buf::decode_bytes_std(r)?), 3 => { field3 = Some( { + ::stef::buf::decode_u64(r)?; Ok::< _, ::stef::buf::Error, @@ -81,7 +96,7 @@ impl ::stef::Decode for Sample { }?, ); } - _ => continue, + _ => ::stef::buf::decode_skip(r, id.encoding)?, } } Ok(Self { diff --git a/crates/stef-playground/build.rs b/crates/stef-playground/build.rs index ce76c7a..1fb9c40 100644 --- a/crates/stef-playground/build.rs +++ b/crates/stef-playground/build.rs @@ -2,7 +2,7 @@ fn main() -> stef_build::Result<()> { let compiler = stef_build::Compiler::default(); - compiler.compile(&["src/sample.stef"])?; + compiler.compile(&["src/evolution.stef", "src/sample.stef"])?; compiler.compile(&["schemas/*.stef", "src/other.stef", "src/second.stef"])?; Ok(()) } diff --git a/crates/stef-playground/src/evolution.stef b/crates/stef-playground/src/evolution.stef new file mode 100644 index 0000000..8950fd3 --- /dev/null +++ b/crates/stef-playground/src/evolution.stef @@ -0,0 +1,8 @@ +struct Version1 { + field1: u32 @1, +} + +struct Version2 { + field1: u32 @1, + field2: string @2, +} diff --git a/crates/stef-playground/src/lib.rs b/crates/stef-playground/src/lib.rs index 1e2c503..fce9502 100644 --- a/crates/stef-playground/src/lib.rs +++ b/crates/stef-playground/src/lib.rs @@ -1,5 +1,9 @@ #![allow(missing_docs, clippy::missing_errors_doc)] +mod evolution { + stef::include!("evolution"); +} + mod sample { stef::include!("sample"); } @@ -120,7 +124,7 @@ mod tests { use stef::{Decode, Encode}; - use super::sample; + use super::{evolution, sample}; fn roundtrip(value: &T) { let mut buf = Vec::new(); @@ -131,6 +135,21 @@ mod tests { assert_eq!(*value, value2); } + #[test] + fn evolution() { + let mut buf = Vec::new(); + evolution::Version2 { + field1: 5, + field2: "Test".to_owned(), + } + .encode(&mut buf); + + println!("{}: {buf:?}", std::any::type_name::()); + + let value = evolution::Version1::decode(&mut &*buf).unwrap(); + assert_eq!(5, value.field1); + } + #[test] fn sample() { roundtrip(&sample::Sample { diff --git a/crates/stef/src/buf/decode.rs b/crates/stef/src/buf/decode.rs index 2b66f16..ef5388b 100644 --- a/crates/stef/src/buf/decode.rs +++ b/crates/stef/src/buf/decode.rs @@ -8,7 +8,7 @@ use std::{ pub use bytes::{Buf, Bytes}; -use crate::{varint, NonZero}; +use crate::{varint, FieldEncoding, FieldId, NonZero, VariantId}; pub type Result = std::result::Result; @@ -19,6 +19,7 @@ pub enum Error { NonUtf8(std::string::FromUtf8Error), MissingField { id: u32, name: Option<&'static str> }, UnknownVariant(u32), + UnknownEncoding(u32), Zero, } @@ -108,10 +109,19 @@ pub fn decode_bytes_bytes(r: &mut impl Buf) -> Result { pub fn decode_vec(r: &mut R, decode: D) -> Result> where R: Buf, - D: Fn(&mut R) -> Result, + D: Fn(&mut bytes::buf::Take<&mut R>) -> Result, { let len = decode_u64(r)?; - (0..len).map(|_| decode(r)).collect() + ensure_size!(r, len as usize); + + let mut vec = Vec::new(); + let mut r = r.take(len as usize); + + while r.has_remaining() { + vec.push(decode(&mut r)?); + } + + Ok(vec) } pub fn decode_hash_map( @@ -122,23 +132,39 @@ pub fn decode_hash_map( where R: Buf, K: Hash + Eq, - DK: Fn(&mut R) -> Result, - DV: Fn(&mut R) -> Result, + DK: Fn(&mut bytes::buf::Take<&mut R>) -> Result, + DV: Fn(&mut bytes::buf::Take<&mut R>) -> Result, { let len = decode_u64(r)?; - (0..len) - .map(|_| Ok((decode_key(r)?, decode_value(r)?))) - .collect() + ensure_size!(r, len as usize); + + let mut map = HashMap::new(); + let mut r = r.take(len as usize); + + while r.has_remaining() { + map.insert(decode_key(&mut r)?, decode_value(&mut r)?); + } + + Ok(map) } pub fn decode_hash_set(r: &mut R, decode: D) -> Result> where R: Buf, T: Hash + Eq, - D: Fn(&mut R) -> Result, + D: Fn(&mut bytes::buf::Take<&mut R>) -> Result, { let len = decode_u64(r)?; - (0..len).map(|_| decode(r)).collect() + ensure_size!(r, len as usize); + + let mut set = HashSet::new(); + let mut r = r.take(len as usize); + + while r.has_remaining() { + set.insert(decode(&mut r)?); + } + + Ok(set) } pub fn decode_option(r: &mut R, decode: D) -> Result> @@ -158,24 +184,24 @@ pub fn decode_array(r: &mut R, decode: D) -> Result<[T; where R: Buf, T: Debug, - D: Fn(&mut R) -> Result, + D: Fn(&mut bytes::buf::Take<&mut R>) -> Result, { let len = decode_u64(r)?; - if (len as usize) < N { - return Err(Error::InsufficientData); - } + ensure_size!(r, len as usize); - let buf = (0..N).map(|_| decode(r)).collect::>>()?; + let mut vec = Vec::new(); + let mut r = r.take(len as usize); - // read any remaining values, in case the old array definition was larger. - // still have to decode the values to advance the buffer accordingly. - for _ in N..len as usize { - decode(r)?; + while r.has_remaining() && vec.len() < N { + vec.push(decode(&mut r)?); } + // skip any remaining values, in case the old array definition was larger. + r.advance(r.remaining()); + // SAFETY: we can unwrap here, because we ensured the Vec exactly matches // with the length of the array. - Ok(buf.try_into().unwrap()) + Ok(vec.try_into().unwrap()) } macro_rules! ensure_not_empty { @@ -230,15 +256,20 @@ pub fn decode_non_zero_bytes_bytes(r: &mut impl Buf) -> Result> { pub fn decode_non_zero_vec(r: &mut R, decode: D) -> Result>> where R: Buf, - D: Fn(&mut R) -> Result, + D: Fn(&mut bytes::buf::Take<&mut R>) -> Result, { let len = decode_u64(r)?; ensure_not_empty!(len); + ensure_size!(r, len as usize); - (0..len) - .map(|_| decode(r)) - .collect::>() - .map(|v| NonZero::>::new(v).unwrap()) + let mut vec = Vec::new(); + let mut r = r.take(len as usize); + + while r.has_remaining() { + vec.push(decode(&mut r)?); + } + + Ok(NonZero::>::new(vec).unwrap()) } pub fn decode_non_zero_hash_map( @@ -249,36 +280,83 @@ pub fn decode_non_zero_hash_map( where R: Buf, K: Hash + Eq, - DK: Fn(&mut R) -> Result, - DV: Fn(&mut R) -> Result, + DK: Fn(&mut bytes::buf::Take<&mut R>) -> Result, + DV: Fn(&mut bytes::buf::Take<&mut R>) -> Result, { let len = decode_u64(r)?; ensure_not_empty!(len); + ensure_size!(r, len as usize); + + let mut map = HashMap::new(); + let mut r = r.take(len as usize); - (0..len) - .map(|_| Ok((decode_key(r)?, decode_value(r)?))) - .collect::>() - .map(|v| NonZero::>::new(v).unwrap()) + while r.has_remaining() { + map.insert(decode_key(&mut r)?, decode_value(&mut r)?); + } + + Ok(NonZero::>::new(map).unwrap()) } pub fn decode_non_zero_hash_set(r: &mut R, decode: D) -> Result>> where R: Buf, T: Hash + Eq, - D: Fn(&mut R) -> Result, + D: Fn(&mut bytes::buf::Take<&mut R>) -> Result, { let len = decode_u64(r)?; ensure_not_empty!(len); + ensure_size!(r, len as usize); + + let mut set = HashSet::new(); + let mut r = r.take(len as usize); + + while r.has_remaining() { + set.insert(decode(&mut r)?); + } + + Ok(NonZero::>::new(set).unwrap()) +} - (0..len) - .map(|_| decode(r)) - .collect::>() - .map(|v| NonZero::>::new(v).unwrap()) +#[inline] +pub fn decode_id(r: &mut impl Buf) -> Result { + decode_u32(r).and_then(|id| FieldId::from_u32(id).ok_or(Error::UnknownEncoding(id))) } -#[inline(always)] -pub fn decode_id(r: &mut impl Buf) -> Result { - decode_u32(r) +#[inline] +pub fn decode_variant_id(r: &mut impl Buf) -> Result { + decode_u32(r).map(VariantId::new) +} + +pub fn decode_skip(r: &mut impl Buf, encoding: FieldEncoding) -> Result<()> { + match encoding { + FieldEncoding::Varint => loop { + ensure_size!(r, 1); + if r.get_u8() & 0x80 == 0 { + return Ok(()); + } + }, + FieldEncoding::LengthPrefixed => { + let len = decode_u64(r)? as usize; + ensure_size!(r, len); + r.advance(len); + Ok(()) + } + FieldEncoding::Fixed1 => { + ensure_size!(r, 1); + r.advance(1); + Ok(()) + } + FieldEncoding::Fixed4 => { + ensure_size!(r, 4); + r.advance(4); + Ok(()) + } + FieldEncoding::Fixed8 => { + ensure_size!(r, 8); + r.advance(8); + Ok(()) + } + } } pub trait Decode: Sized { @@ -333,7 +411,7 @@ where { #[inline(always)] fn decode(r: &mut impl Buf) -> Result { - decode_vec(r, T::decode) + decode_vec(r, |r| T::decode(r)) } } @@ -344,7 +422,7 @@ where { #[inline(always)] fn decode(r: &mut impl Buf) -> Result { - decode_hash_map(r, K::decode, V::decode) + decode_hash_map(r, |r| K::decode(r), |r| V::decode(r)) } } @@ -354,7 +432,7 @@ where { #[inline(always)] fn decode(r: &mut impl Buf) -> Result { - decode_hash_set(r, T::decode) + decode_hash_set(r, |r| T::decode(r)) } } @@ -374,7 +452,7 @@ where { #[inline(always)] fn decode(r: &mut impl Buf) -> Result { - decode_array(r, T::decode) + decode_array(r, |r| T::decode(r)) } } diff --git a/crates/stef/src/buf/encode.rs b/crates/stef/src/buf/encode.rs index 88be4cc..ffa036b 100644 --- a/crates/stef/src/buf/encode.rs +++ b/crates/stef/src/buf/encode.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; pub use bytes::{BufMut, Bytes}; -use crate::{varint, NonZero}; +use crate::{varint, FieldId, NonZero, VariantId}; pub fn encode_bool(w: &mut impl BufMut, value: bool) { w.put_u8(value.into()); @@ -54,29 +54,39 @@ pub fn encode_bytes_bytes(w: &mut impl BufMut, value: &Bytes) { encode_bytes_std(w, value); } -pub fn encode_vec(w: &mut W, vec: &[T], encode: E) +pub fn encode_vec(w: &mut W, vec: &[T], size: S, encode: E) where W: BufMut, + S: Fn(&T) -> usize, E: Fn(&mut W, &T), { - encode_u64(w, vec.len() as u64); + encode_u64(w, vec.iter().map(size).sum::() as u64); for value in vec { encode(w, value); } } -pub fn encode_hash_map( +pub fn encode_hash_map( w: &mut W, map: &HashMap, + size_key: SK, + size_value: SV, encode_key: EK, encode_value: EV, ) where W: BufMut, + SK: Fn(&K) -> usize, + SV: Fn(&V) -> usize, EK: Fn(&mut W, &K), EV: Fn(&mut W, &V), { - encode_u64(w, map.len() as u64); + encode_u64( + w, + map.iter() + .map(|(k, v)| size_key(k) + size_value(v)) + .sum::() as u64, + ); for (key, value) in map { encode_key(w, key); @@ -84,12 +94,13 @@ pub fn encode_hash_map( } } -pub fn encode_hash_set(w: &mut W, set: &HashSet, encode: E) +pub fn encode_hash_set(w: &mut W, set: &HashSet, size: S, encode: E) where W: BufMut, + S: Fn(&T) -> usize, E: Fn(&mut W, &T), { - encode_u64(w, set.len() as u64); + encode_u64(w, set.iter().map(size).sum::() as u64); for value in set { encode(w, value); @@ -109,12 +120,13 @@ where } } -pub fn encode_array(w: &mut W, array: &[T; N], encode: E) +pub fn encode_array(w: &mut W, array: &[T; N], size: S, encode: E) where W: BufMut, + S: Fn(&T) -> usize, E: Fn(&mut W, &T), { - encode_u64(w, N as u64); + encode_u64(w, array.iter().map(size).sum::() as u64); for value in array { encode(w, value); @@ -122,12 +134,28 @@ where } #[inline(always)] -pub fn encode_id(w: &mut impl BufMut, id: u32) { - encode_u32(w, id); +pub fn encode_tuple(w: &mut W, size: S, encode: E) +where + W: BufMut, + S: Fn() -> usize, + E: Fn(&mut W), +{ + encode_u64(w, size() as u64); + encode(w); +} + +#[inline(always)] +pub fn encode_id(w: &mut impl BufMut, id: FieldId) { + encode_u32(w, id.into_u32()); +} + +#[inline(always)] +pub fn encode_variant_id(w: &mut impl BufMut, id: VariantId) { + encode_u32(w, id.value); } #[inline(always)] -pub fn encode_field(w: &mut W, id: u32, encode: E) +pub fn encode_field(w: &mut W, id: FieldId, encode: E) where W: BufMut, E: Fn(&mut W), @@ -137,7 +165,7 @@ where } #[inline(always)] -pub fn encode_field_option(w: &mut W, id: u32, option: &Option, encode: E) +pub fn encode_field_option(w: &mut W, id: FieldId, option: &Option, encode: E) where W: BufMut, E: Fn(&mut W, &T), @@ -148,7 +176,7 @@ where } } -pub trait Encode { +pub trait Encode: super::Size { fn encode(&self, w: &mut impl BufMut); } @@ -200,7 +228,7 @@ where { #[inline(always)] fn encode(&self, w: &mut impl BufMut) { - encode_vec(w, self, |w, v| v.encode(w)); + encode_vec(w, self, T::size, |w, v| v.encode(w)); } } @@ -210,7 +238,7 @@ where { #[inline(always)] fn encode(&self, w: &mut impl BufMut) { - encode_vec(w, self, |w, v| v.encode(w)); + encode_vec(w, self, T::size, |w, v| v.encode(w)); } } @@ -221,7 +249,14 @@ where { #[inline(always)] fn encode(&self, w: &mut impl BufMut) { - encode_hash_map(w, self, |w, k| k.encode(w), |w, v| v.encode(w)); + encode_hash_map( + w, + self, + K::size, + V::size, + |w, k| k.encode(w), + |w, v| v.encode(w), + ); } } @@ -231,7 +266,7 @@ where { #[inline(always)] fn encode(&self, w: &mut impl BufMut) { - encode_hash_set(w, self, |w, v| v.encode(w)); + encode_hash_set(w, self, T::size, |w, v| v.encode(w)); } } @@ -251,7 +286,7 @@ where { #[inline(always)] fn encode(&self, w: &mut impl BufMut) { - encode_array(w, self, |w, v| v.encode(w)); + encode_array(w, self, T::size, |w, v| v.encode(w)); } } diff --git a/crates/stef/src/buf/mod.rs b/crates/stef/src/buf/mod.rs index 9e707c5..eea23f1 100644 --- a/crates/stef/src/buf/mod.rs +++ b/crates/stef/src/buf/mod.rs @@ -68,16 +68,21 @@ mod tests { #[test] fn non_zero_vec_valid() { let mut buf = Vec::new(); - encode_vec(&mut buf, &[1, 2, 3], |w, v| encode_u32(w, *v)); - assert!(decode_non_zero_vec(&mut &*buf, decode_u32).is_ok()); + encode_vec( + &mut buf, + &[1, 2, 3], + |v| size_u32(*v), + |w, v| encode_u32(w, *v), + ); + assert!(decode_non_zero_vec(&mut &*buf, |r| decode_u32(r)).is_ok()); } #[test] fn non_zero_vec_invalid() { let mut buf = Vec::new(); - encode_vec(&mut buf, &[], |w, v| encode_u32(w, *v)); + encode_vec(&mut buf, &[], |v| size_u32(*v), |w, v| encode_u32(w, *v)); assert!(matches!( - decode_non_zero_vec(&mut &*buf, decode_u32), + decode_non_zero_vec(&mut &*buf, |r| decode_u32(r)), Err(Error::Zero), )); } @@ -88,10 +93,14 @@ mod tests { encode_hash_map( &mut buf, &HashMap::from_iter([(1, true), (2, false)]), + |k| size_u32(*k), + |v| size_bool(*v), |w, k| encode_u32(w, *k), |w, v| encode_bool(w, *v), ); - assert!(decode_non_zero_hash_map(&mut &*buf, decode_u32, decode_bool).is_ok()); + assert!( + decode_non_zero_hash_map(&mut &*buf, |r| decode_u32(r), |r| decode_bool(r)).is_ok() + ); } #[test] @@ -100,11 +109,13 @@ mod tests { encode_hash_map( &mut buf, &HashMap::new(), + |k| size_u32(*k), + |v| size_bool(*v), |w, k| encode_u32(w, *k), |w, v| encode_bool(w, *v), ); assert!(matches!( - decode_non_zero_hash_map(&mut &*buf, decode_u32, decode_bool), + decode_non_zero_hash_map(&mut &*buf, |r| decode_u32(r), |r| decode_bool(r)), Err(Error::Zero), )); } @@ -112,18 +123,28 @@ mod tests { #[test] fn non_zero_hash_set_valid() { let mut buf = Vec::new(); - encode_hash_set(&mut buf, &HashSet::from_iter([1, 2, 3]), |w, v| { - encode_u32(w, *v); - }); - assert!(decode_non_zero_hash_set(&mut &*buf, decode_u32).is_ok()); + encode_hash_set( + &mut buf, + &HashSet::from_iter([1, 2, 3]), + |v| size_u32(*v), + |w, v| { + encode_u32(w, *v); + }, + ); + assert!(decode_non_zero_hash_set(&mut &*buf, |r| decode_u32(r)).is_ok()); } #[test] fn non_zero_hash_set_invalid() { let mut buf = Vec::new(); - encode_hash_set(&mut buf, &HashSet::new(), |w, v| encode_u32(w, *v)); + encode_hash_set( + &mut buf, + &HashSet::new(), + |v| size_u32(*v), + |w, v| encode_u32(w, *v), + ); assert!(matches!( - decode_non_zero_hash_set(&mut &*buf, decode_u32), + decode_non_zero_hash_set(&mut &*buf, |r| decode_u32(r)), Err(Error::Zero), )); } diff --git a/crates/stef/src/lib.rs b/crates/stef/src/lib.rs index 3e3bb6e..538c638 100644 --- a/crates/stef/src/lib.rs +++ b/crates/stef/src/lib.rs @@ -18,6 +18,78 @@ pub use buf::{Buf, BufMut, Bytes, Decode, Encode}; pub mod buf; pub mod varint; +#[derive(Clone, Copy)] +pub struct FieldId { + pub value: u32, + pub encoding: FieldEncoding, +} + +impl FieldId { + #[inline] + #[must_use] + pub const fn new(value: u32, encoding: FieldEncoding) -> Self { + Self { value, encoding } + } + + #[must_use] + pub const fn from_u32(value: u32) -> Option { + let Some(encoding) = FieldEncoding::from_u32(value) else { + return None; + }; + let value = value >> 3; + + Some(Self { value, encoding }) + } + + #[inline] + #[must_use] + pub const fn into_u32(self) -> u32 { + self.value << 3 | self.encoding as u32 + } +} + +#[derive(Clone, Copy, Eq, PartialEq)] +#[repr(u32)] +pub enum FieldEncoding { + /// Variable-length integer. + Varint = 0, + /// Arbitrary content prefixed with its byte length. + LengthPrefixed = 1, + /// 1-byte fixed width data. + Fixed1 = 2, + /// 4-byte fixed width data. + Fixed4 = 3, + /// 8-byte fixed width data. + Fixed8 = 4, +} + +impl FieldEncoding { + #[must_use] + pub const fn from_u32(value: u32) -> Option { + Some(match value & 0b111 { + 0 => Self::Varint, + 1 => Self::LengthPrefixed, + 2 => Self::Fixed1, + 3 => Self::Fixed4, + 4 => Self::Fixed8, + _ => return None, + }) + } +} + +#[derive(Clone, Copy)] +pub struct VariantId { + pub value: u32, +} + +impl VariantId { + #[inline] + #[must_use] + pub fn new(value: u32) -> Self { + Self { value } + } +} + #[macro_export] macro_rules! include { ($name:literal) => {