Skip to content

Commit

Permalink
Add default_initializer_fn optimisation
Browse files Browse the repository at this point in the history
This adds a optional function for initialising a sequence. This can then
be used if a codec can know that the sequence is entirely empty and all
of the fields from the sequence are optional and default, this brings a
dramatic speed up decoding of empty sequences.

fixes #224
  • Loading branch information
XAMPPRocky committed Feb 18, 2024
1 parent 33d22a6 commit 6733000
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 10 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ use rasn::{prelude::*, types::{Integer, Utf8String}};
impl Decode for Person {
fn decode_with_tag_and_constraints<D: Decoder>(decoder: &mut D, tag: Tag, constraints: Constraints) -> Result<Self, D::Error> {
// Accepts a closure that decodes the contents of the sequence.
decoder.decode_sequence(tag, |decoder| {
decoder.decode_sequence(tag, None::<fn () -> Self>, |decoder| {
let age = Integer::decode(decoder)?;
let name = Utf8String::decode(decoder)?;
Ok(Self { age, name })
Expand Down
32 changes: 31 additions & 1 deletion macros/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,14 @@ pub fn derive_struct_impl(
)
}
} else {
let mut all_fields_optional_or_default = true;
for (i, field) in container.fields.iter().enumerate() {
let field_config = FieldConfig::new(field, config);

if !field_config.is_option_or_default_type() {
all_fields_optional_or_default = false;
}

list.push(field_config.decode_field_def(&name, i));
}

Expand All @@ -179,8 +184,33 @@ pub fn derive_struct_impl(
Fields::Unit => quote!(),
};

let initializer_fn = if all_fields_optional_or_default {
let fields = match container.fields {
Fields::Named(_) => {
let init_fields = container
.fields
.iter()
.map(|field| field.ident.clone())
.map(|name| quote!(#name : <_>::default()));
quote!({ #(#init_fields),* })
}
Fields::Unnamed(_) => {
let init_fields = container.fields.iter().map(|_| quote!(<_>::default()));
quote!(( #(#init_fields),* ))
}
Fields::Unit => quote!(),
};
quote! {
Some(|| {
Self #fields
})
}
} else {
quote!(None::<fn() -> Self>)
};

quote! {
decoder.decode_sequence(tag, |decoder| {
decoder.decode_sequence(tag, #initializer_fn, |decoder| {
Ok(Self #fields)
})
}
Expand Down
34 changes: 31 additions & 3 deletions src/ber/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,8 +599,16 @@ impl<'input> crate::Decoder for Decoder<'input> {
self.parse_constructed_contents(tag, true, |decoder| {
let mut items = Vec::new();

if decoder.input.is_empty() {
return Ok(items);
}

while let Ok(item) = D::decode(decoder) {
items.push(item);

if decoder.input.is_empty() {
return Ok(items);
}
}

Ok(items)
Expand All @@ -623,12 +631,32 @@ impl<'input> crate::Decoder for Decoder<'input> {
})
}

fn decode_sequence<D, F: FnOnce(&mut Self) -> Result<D>>(
fn decode_sequence<
D: crate::types::Constructed,
DF: FnOnce() -> D,
F: FnOnce(&mut Self) -> Result<D>,
>(
&mut self,
tag: Tag,
default_initializer_fn: Option<DF>,
decode_fn: F,
) -> Result<D> {
self.parse_constructed_contents(tag, true, decode_fn)
self.parse_constructed_contents(tag, true, |decoder| {
// If there are no fields, or the input is empty and we know that
// all fields are optional or default fields, we call the default
// initializer and skip calling the decode function at all.
if D::FIELDS.is_empty()
|| (D::FIELDS.len() == D::FIELDS.number_of_optional_and_default_fields()
&& decoder.input.is_empty())
{
if let Some(default_initializer_fn) = default_initializer_fn {
return Ok((default_initializer_fn)());
} else {
return Err(BerDecodeErrorKind::UnexpectedEmptyInput.into());
}
}
(decode_fn)(decoder)
})
}

fn decode_explicit_prefix<D: Decode>(&mut self, tag: Tag) -> Result<D> {
Expand Down Expand Up @@ -949,7 +977,7 @@ mod tests {
tag: Tag,
_: Constraints,
) -> Result<Self, D::Error> {
decoder.decode_sequence(tag, |sequence| {
decoder.decode_sequence(tag, None::<fn() -> Self>, |sequence| {
let name: Ia5String = Ia5String::decode(sequence)?;
let ok: bool = bool::decode(sequence)?;
Ok(Self { name, ok })
Expand Down
8 changes: 7 additions & 1 deletion src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,15 @@ pub trait Decoder: Sized {
) -> Result<types::ObjectIdentifier, Self::Error>;
/// Decode a `SEQUENCE` identified by `tag` from the available input. Returning
/// a new `Decoder` containing the sequence's contents to be decoded.
fn decode_sequence<D, F>(&mut self, tag: Tag, decode_fn: F) -> Result<D, Self::Error>
fn decode_sequence<D, DF, F>(
&mut self,
tag: Tag,
default_initializer_fn: Option<DF>,
decode_fn: F,
) -> Result<D, Self::Error>
where
D: crate::types::Constructed,
DF: FnOnce() -> D,
F: FnOnce(&mut Self) -> Result<D, Self::Error>;
/// Decode a `SEQUENCE OF D` where `D: Decode` identified by `tag` from the available input.
fn decode_sequence_of<D: Decode>(
Expand Down
2 changes: 2 additions & 0 deletions src/error/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,8 @@ pub enum BerDecodeErrorKind {
/// The actual tag.
actual: Tag,
},
#[snafu(display("SEQUENCE has at least one required field, but no input provided"))]
UnexpectedEmptyInput,
}

impl BerDecodeErrorKind {
Expand Down
7 changes: 6 additions & 1 deletion src/jer/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,12 @@ impl crate::Decoder for Decoder {
decode_jer_value!(Self::object_identifier_from_value, self.stack)
}

fn decode_sequence<D, F>(&mut self, _: crate::Tag, decode_fn: F) -> Result<D, Self::Error>
fn decode_sequence<D, DF, F>(
&mut self,
_: crate::Tag,
_: Option<DF>,
decode_fn: F,
) -> Result<D, Self::Error>
where
D: Constructed,
F: FnOnce(&mut Self) -> Result<D, Self::Error>,
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![doc = include_str!("../README.md")]
#![cfg_attr(not(test), no_std)]
// #![cfg_attr(not(test), no_std)]
extern crate alloc;

#[cfg(test)]
Expand Down
8 changes: 7 additions & 1 deletion src/per/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -815,9 +815,15 @@ impl<'input> crate::Decoder for Decoder<'input> {
.map(|seq| seq.into_iter().collect())
}

fn decode_sequence<D, F>(&mut self, _: Tag, decode_fn: F) -> Result<D, Self::Error>
fn decode_sequence<D, DF, F>(
&mut self,
_: Tag,
_: Option<DF>,
decode_fn: F,
) -> Result<D, Self::Error>
where
D: crate::types::Constructed,
DF: FnOnce() -> D,
F: FnOnce(&mut Self) -> Result<D, Self::Error>,
{
let is_extensible = D::EXTENDED_FIELDS
Expand Down
2 changes: 1 addition & 1 deletion src/types/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl<T: crate::Decode> crate::Decode for InstanceOf<T> {
tag: Tag,
_: Constraints,
) -> Result<Self, D::Error> {
decoder.decode_sequence(tag, |sequence| {
decoder.decode_sequence(tag, None::<fn() -> Self>, |sequence| {
let type_id = ObjectIdentifier::decode(sequence)?;
let value = sequence.decode_explicit_prefix(Tag::new(Class::Context, 0))?;

Expand Down

0 comments on commit 6733000

Please sign in to comment.