Skip to content

Commit

Permalink
feat: unroll container type en-/decoding
Browse files Browse the repository at this point in the history
Instead of relying on generic implementations for the Rust generated
code that en- or decodes the data, these are defined as closures.

The main benefit is that it allows optimized calls that would not be
possible through the trait system (for example a fast encoding of
`Vec<u8>` instead of the regular `Vec<T>` encoding that would write the
bytes one-by-one).
  • Loading branch information
dnaka91 committed Oct 14, 2023
1 parent 39c6ccb commit 3d64436
Show file tree
Hide file tree
Showing 21 changed files with 628 additions and 935 deletions.
34 changes: 24 additions & 10 deletions crates/stef-build/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,23 @@ fn compile_data_type(ty: &DataType<'_>) -> TokenStream {
DataType::F64 => quote! { ::stef::buf::decode_f64(r) },
DataType::String | DataType::StringRef => quote! { ::stef::buf::decode_string(r) },
DataType::Bytes | DataType::BytesRef => quote! { ::stef::buf::decode_bytes(r) },
DataType::Vec(_ty) => quote! { ::stef::buf::decode_vec(r) },
DataType::HashMap(_kv) => quote! { ::stef::buf::decode_hash_map(r) },
DataType::HashSet(_ty) => quote! { ::stef::buf::decode_hash_set(r) },
DataType::Option(_ty) => quote! { ::stef::buf::decode_option(r) },
DataType::Vec(ty) => {
let ty = compile_data_type(ty);
quote! { ::stef::buf::decode_vec(r, |r| { #ty }) }
}
DataType::HashMap(kv) => {
let ty_k = compile_data_type(&kv.0);
let ty_v = compile_data_type(&kv.1);
quote! { ::stef::buf::decode_hash_map(r, |r| { #ty_k }, |r| { #ty_v }) }
}
DataType::HashSet(ty) => {
let ty = compile_data_type(ty);
quote! { ::stef::buf::decode_hash_set(r, |r| { #ty }) }
}
DataType::Option(ty) => {
let ty = compile_data_type(ty);
quote! { ::stef::buf::decode_option(r, |r| { #ty }) }
}
DataType::NonZero(ty) => match **ty {
DataType::U8 => quote! { NonZeroU8::decode(r) },
DataType::U16 => quote! { NonZeroU16::decode(r) },
Expand All @@ -266,21 +279,22 @@ fn compile_data_type(ty: &DataType<'_>) -> TokenStream {
DataType::I32 => quote! { NonZeroI32::decode(r) },
DataType::I64 => quote! { NonZeroI64::decode(r) },
DataType::I128 => quote! { NonZeroI128::decode(r) },
_ => todo!(),
_ => quote! { todo!() },
},
DataType::BoxString => quote! { Box<str>::decode(r) },
DataType::BoxBytes => quote! { Box<[u8]>::decode(r) },
DataType::Tuple(types) => match types.len() {
size @ 2..=12 => {
let fn_name = Ident::new(&format!("decode_tuple{size}"), Span::call_site());
quote! { ::stef::buf::#fn_name(r) }
2..=12 => {
let types = types.iter().map(|ty| compile_data_type(ty));
quote! { { Ok::<_, ::stef::buf::Error>((#(#types?,)*)) } }
}
0 => panic!("tuple with zero elements"),
1 => panic!("tuple with single element"),
_ => panic!("tuple with more than 12 elements"),
},
DataType::Array(_ty, _size) => {
quote! { ::stef::buf::decode_array(r) }
DataType::Array(ty, _size) => {
let ty = compile_data_type(ty);
quote! { ::stef::buf::decode_array(r, |r| { #ty }) }
}
DataType::External(ty) => {
let ty = Ident::new(ty.name, Span::call_site());
Expand Down
60 changes: 54 additions & 6 deletions crates/stef-build/src/definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,17 +406,35 @@ mod tests {
}
#[automatically_derived]
impl ::stef::Encode for Sample {
#[allow(clippy::needless_borrow)]
fn encode(&self, w: &mut impl ::stef::BufMut) {
::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, self.field1) });
::stef::buf::encode_field(
w,
1,
|w| {
::stef::buf::encode_u32(w, self.field1);
},
);
::stef::buf::encode_field(
w,
2,
|w| { ::stef::buf::encode_bytes(w, &self.field2) },
|w| {
::stef::buf::encode_bytes(w, &self.field2);
},
);
::stef::buf::encode_field(
w,
3,
|w| { ::stef::buf::encode_tuple2(w, &self.field3) },
|w| {
::stef::buf::encode_bool(w, self.field3.0);
::stef::buf::encode_array(
w,
&&(self.field3.1),
|w, v| {
::stef::buf::encode_i16(w, *v);
},
);
},
);
::stef::buf::encode_u32(w, ::stef::buf::END_MARKER);
}
Expand All @@ -432,7 +450,22 @@ mod tests {
::stef::buf::END_MARKER => break,
1 => field1 = Some(::stef::buf::decode_u32(r)?),
2 => field2 = Some(::stef::buf::decode_bytes(r)?),
3 => field3 = Some(::stef::buf::decode_tuple2(r)?),
3 => {
field3 = Some(
{
Ok::<
_,
::stef::buf::Error,
>((
::stef::buf::decode_bool(r)?,
::stef::buf::decode_array(
r,
|r| { ::stef::buf::decode_i16(r) },
)?,
))
}?,
);
}
_ => continue,
}
}
Expand Down Expand Up @@ -505,7 +538,15 @@ mod tests {
::stef::buf::encode_field(
w,
2,
|w| { ::stef::buf::encode_vec(w, &*field2) },
|w| {
::stef::buf::encode_vec(
w,
&*field2,
|w, v| {
::stef::buf::encode_bool(w, *v);
},
)
},
);
::stef::buf::encode_u32(w, ::stef::buf::END_MARKER);
}
Expand Down Expand Up @@ -550,7 +591,14 @@ mod tests {
match ::stef::buf::decode_id(r)? {
::stef::buf::END_MARKER => break,
1 => field1 = Some(::stef::buf::decode_string(r)?),
2 => field2 = Some(::stef::buf::decode_vec(r)?),
2 => {
field2 = Some(
::stef::buf::decode_vec(
r,
|r| { ::stef::buf::decode_bool(r) },
)?,
);
}
_ => continue,
}
}
Expand Down
117 changes: 104 additions & 13 deletions crates/stef-build/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub fn compile_struct(
quote! {
#[automatically_derived]
impl #generics ::stef::Encode for #name #generics #generics_where {
#[allow(clippy::needless_borrow)]
fn encode(&self, w: &mut impl ::stef::BufMut) {
#fields
}
Expand All @@ -38,11 +39,16 @@ fn compile_struct_fields(fields: &Fields<'_>) -> TokenStream {
let id = proc_macro2::Literal::u32_unsuffixed(id.0);
let name = proc_macro2::Ident::new(name, Span::call_site());

if matches!(ty, DataType::Option(_)) {
quote! { ::stef::buf::encode_field_option(w, #id, &self.#name); }
if let DataType::Option(ty) = ty {
let ty = compile_data_type(ty, if is_copy(ty) {
quote! { *v }
} else {
quote! { v }
});
quote! { ::stef::buf::encode_field_option(w, #id, &self.#name, |w, v| { #ty; }); }
} else {
let ty = compile_data_type(ty, quote! { self.#name });
quote! { ::stef::buf::encode_field(w, #id, |w| { #ty }); }
quote! { ::stef::buf::encode_field(w, #id, |w| { #ty; }); }
}
},
);
Expand Down Expand Up @@ -208,7 +214,26 @@ fn compile_generics(Generics(types): &Generics<'_>) -> (TokenStream, TokenStream
.unwrap_or_default()
}

#[allow(clippy::needless_pass_by_value)]
fn is_copy(ty: &DataType<'_>) -> bool {
matches!(
ty,
DataType::Bool
| DataType::U8
| DataType::U16
| DataType::U32
| DataType::U64
| DataType::U128
| DataType::I8
| DataType::I16
| DataType::I32
| DataType::I64
| DataType::I128
| DataType::F32
| DataType::F64
)
}

#[allow(clippy::needless_pass_by_value, clippy::too_many_lines)]
fn compile_data_type(ty: &DataType<'_>, name: TokenStream) -> TokenStream {
match ty {
DataType::Bool => quote! { ::stef::buf::encode_bool(w, #name) },
Expand All @@ -226,23 +251,89 @@ fn compile_data_type(ty: &DataType<'_>, name: TokenStream) -> TokenStream {
DataType::F64 => quote! { ::stef::buf::encode_f64(w, #name) },
DataType::String | DataType::StringRef => quote! { ::stef::buf::encode_string(w, &#name) },
DataType::Bytes | DataType::BytesRef => quote! { ::stef::buf::encode_bytes(w, &#name) },
DataType::Vec(_ty) => quote! { ::stef::buf::encode_vec(w, &#name) },
DataType::HashMap(_kv) => quote! { ::stef::buf::encode_hash_map(w, #name) },
DataType::HashSet(_ty) => quote! { ::stef::buf::encode_hash_set(w, #name) },
DataType::Option(_ty) => quote! { ::stef::buf::encode_option(w, &#name) },
DataType::Vec(ty) => {
let ty = compile_data_type(
ty,
if is_copy(ty) {
quote! { *v }
} else {
quote! { v }
},
);
quote! { ::stef::buf::encode_vec(w, &#name, |w, v| { #ty; }) }
}
DataType::HashMap(kv) => {
let ty_k = compile_data_type(
&kv.0,
if is_copy(&kv.0) {
quote! { *k }
} else {
quote! { k }
},
);
let ty_v = compile_data_type(
&kv.1,
if is_copy(&kv.1) {
quote! { *v }
} else {
quote! { v }
},
);
quote! { ::stef::buf::encode_hash_map(w, #name, |w, k| { #ty_k; }, |w, v| { #ty_v; }) }
}
DataType::HashSet(ty) => {
let ty = compile_data_type(
ty,
if is_copy(ty) {
quote! { *v }
} else {
quote! { v }
},
);
quote! { ::stef::buf::encode_hash_set(w, #name, |w, v| { #ty; }) }
}
DataType::Option(ty) => {
let ty = compile_data_type(
ty,
if is_copy(ty) {
quote! { *v }
} else {
quote! { v }
},
);
quote! { ::stef::buf::encode_option(w, &#name, |w, v| { #ty; }) }
}
DataType::BoxString => quote! { ::stef::buf::encode_string(w, &*#name) },
DataType::BoxBytes => quote! { ::stef::buf::encode_bytes(w, &*#name) },
DataType::Tuple(types) => match types.len() {
size @ 2..=12 => {
let fn_name = Ident::new(&format!("encode_tuple{size}"), Span::call_site());
quote! { ::stef::buf::#fn_name(w, &#name) }
2..=12 => {
let types = types.iter().enumerate().map(|(idx, ty)| {
let idx = proc_macro2::Literal::usize_unsuffixed(idx);
compile_data_type(
ty,
if is_copy(ty) {
quote! { #name.#idx }
} else {
quote! { &(#name.#idx) }
},
)
});
quote! { #(#types;)* }
}
0 => panic!("tuple with zero elements"),
1 => panic!("tuple with single element"),
_ => panic!("tuple with more than 12 elements"),
},
DataType::Array(_ty, _size) => {
quote! { ::stef::buf::encode_array(w, &#name) }
DataType::Array(ty, _size) => {
let ty = compile_data_type(
ty,
if is_copy(ty) {
quote! { *v }
} else {
quote! { v }
},
);
quote! { ::stef::buf::encode_array(w, &#name, |w, v| { #ty; }) }
}
DataType::NonZero(_) | DataType::External(_) => {
quote! { (#name).encode(w) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ input_file: crates/stef-parser/tests/inputs/attribute-multi.stef
pub struct Sample;
#[automatically_derived]
impl ::stef::Encode for Sample {
#[allow(clippy::needless_borrow)]
fn encode(&self, w: &mut impl ::stef::BufMut) {}
}
#[automatically_derived]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ input_file: crates/stef-parser/tests/inputs/attribute-single.stef
pub struct Sample;
#[automatically_derived]
impl ::stef::Encode for Sample {
#[allow(clippy::needless_borrow)]
fn encode(&self, w: &mut impl ::stef::BufMut) {}
}
#[automatically_derived]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ input_file: crates/stef-parser/tests/inputs/attribute-unit.stef
pub struct Sample;
#[automatically_derived]
impl ::stef::Encode for Sample {
#[allow(clippy::needless_borrow)]
fn encode(&self, w: &mut impl ::stef::BufMut) {}
}
#[automatically_derived]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ input_file: crates/stef-parser/tests/inputs/attributes-min-ws.stef
pub struct Sample;
#[automatically_derived]
impl ::stef::Encode for Sample {
#[allow(clippy::needless_borrow)]
fn encode(&self, w: &mut impl ::stef::BufMut) {}
}
#[automatically_derived]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ input_file: crates/stef-parser/tests/inputs/attributes.stef
pub struct Sample;
#[automatically_derived]
impl ::stef::Encode for Sample {
#[allow(clippy::needless_borrow)]
fn encode(&self, w: &mut impl ::stef::BufMut) {}
}
#[automatically_derived]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,14 @@ pub mod a {
}
#[automatically_derived]
impl ::stef::Encode for Sample {
#[allow(clippy::needless_borrow)]
fn encode(&self, w: &mut impl ::stef::BufMut) {
::stef::buf::encode_field(
w,
1,
|w| { ::stef::buf::encode_u32(w, self.value) },
|w| {
::stef::buf::encode_u32(w, self.value);
},
);
::stef::buf::encode_u32(w, ::stef::buf::END_MARKER);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,22 @@ pub struct Sample {
}
#[automatically_derived]
impl ::stef::Encode for Sample {
#[allow(clippy::needless_borrow)]
fn encode(&self, w: &mut impl ::stef::BufMut) {
::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u32(w, self.a) });
::stef::buf::encode_field(w, 2, |w| { ::stef::buf::encode_bool(w, self.b) });
::stef::buf::encode_field(
w,
1,
|w| {
::stef::buf::encode_u32(w, self.a);
},
);
::stef::buf::encode_field(
w,
2,
|w| {
::stef::buf::encode_bool(w, self.b);
},
);
::stef::buf::encode_u32(w, ::stef::buf::END_MARKER);
}
}
Expand Down
Loading

0 comments on commit 3d64436

Please sign in to comment.