pigeonhands
pigeonhands

Reputation: 3414

Parsing binary structures into a struct in a single iteration of the data

I am writing a library in rust to deserialize LTV (length type value) data into a struct in rust. An binary ltv object like

L  T  V  L  T    V
02 01 0A 03 02 00 0B

is currently being implemented something like this:

struct LTVObject{
    pub field_1: u8,
    pub field_2: u16,
}
impl LTVObject {
    pub fn parse(r: &LtvReader) -> LTVResult<Self>{
        Ok(LTVObject {
            field_1: r.read_field(1)?,
            field_2: r.read_field(2)?,
        })
    }
}

The problem with this implementation is every call to read_field is looping through the LTV structure to find the field. For large objects, this is very slow. What I want to do is make it so that ltv data only needs to be iterated once. My thinking is to change the parsing to:

pub fn parse(r: &LtvReader) -> LTVResult<Self>{
    let mut inst = Self{ ... };
    for field in r.get_fields(){
        match field.id {
            1 => inst.field_1 = field.get_value()?,
            2 => inst.field_2 = field.get_value()?,
       }
    }
    Ok(inst)
}

However there are a few design problems im unsure how to resolve

  1. Returning an error if one of the fields does not exist in the LTV
  2. Creating the inst variable without the Default trait

In the actual library, the parse function is generated from a macro so I would like to avoid having the constraint that Default needs to be implemented on the field types.

Upvotes: 0

Views: 465

Answers (1)

Peter Hall
Peter Hall

Reputation: 58735

Parse the values into Options and then check that you have them all. Something like this:

pub fn parse(r: &LtvReader) -> LTVResult<Self> {
    let mut field_1 = None;
    let mut field_2 = None;

    for field in r.get_fields(){
        match field.id {
            1 => field_1 = Some(field.get_value()?),
            2 => field_2 = Some(field.get_value()?),
       }
    }
    Ok(LTVObject {
        field_1: field_1.ok_or(Error::MissingField)?,
        field_2: field_2.ok_or(Error::MissingField)?,
    })
}

Upvotes: 1

Related Questions