[][src]Module binread::attribute

A documentation-only module for attributes

List of attributes

AttributeSupportsDescription
bigallSet the endianness to big endian
littleallSet the endianness to little endian
magictop-levelAt the start of parsing read a value and make sure it is equivalent to a constant value
asserttop-levelAfter parsing, check if a condition is true and, optionally, return a custom error if false. Allows multiple.
importtop-levelDefine the arguments for parsing the given type
argsfieldsPass a set of arguments.
defaultfieldsSet a field to the default value for the type
ignorefieldsAn alias for default
postprocess_nowfieldsImmediately run after_parse after reading
deref_nowfieldsAlias for postprocess_now
restore_positionfieldsRestore the reader position after reading the field
tryfieldsAttempt to parse a value and store None if parsing fails.
mapfieldsRead a type from the reader and then apply a function to map it to the type to store in the struct
parse_withfieldsUse a custom parser function for reading from a file
calcfieldsCompute an expression to store. Can use previously read values.
countfieldsSet the length for a vector
is_littlefieldsConditionally set the endian to little
is_bigfieldsConditionally set the endian to big
offsetfieldsChange the offset a FilePtr is relative to
iffieldsUsed on an Option<T> to read a value of type T only if the condition is met
pad_beforefieldsSkip a constant number of bytes forward before reading
pad_afterfieldsSkip a constant number of bytes forward after reading
align_beforefieldsSkip to the next Nth byte before reading
align_afterfieldsSkip to the next Nth byte after reading
seek_beforefieldsPasses the given SeekFrom to Seek::seek
pad_size_tofieldsEnsures the cursor is at least N bytes after the starting position for this field
return_all_errorsenum-levelUse an error handling type in which enum failures return a Vec with an error for every variant

Byteorder

You can use big or little at either the struct-level or the field-level in order to override the byte order of values.

#[derive(BinRead)]
#[br(little)]
struct MyType (
    #[br(big)] u32, // will be big endian
    u32, // will be little endian
);

The order of precedence is: (from highed to lowest)

  1. Field-level
  2. Variant-level (for enums)
  3. Top-level
  4. Configured (i.e. what endianess was passed in)
  5. Native endianess

binread also offers the ability to conditionally set endianness for when the endianess is described within the data itself using is_big or is_little:

#[derive(BinRead, Debug, PartialEq)]
#[br(big)]
struct MyType {
    val: u8,
    #[br(is_little = (val == 3))]
    other_val: u16
}
 

Note: is_big and is_little supports using previous fields

Magic

Magic, or magic values, are constants used for sanity/integrity checking or simply for making file identification easier. Since these are such a common use case binread provides an attribute for handling this for you to save code/memory/time/etc.

The format is magic = [lit] where [lit] is any literal supported by Rust. This is allowed at the following levels: struct, enum, variant, and field.

Examples:

#[derive(BinRead, Debug)]
#[br(magic = b"TEST")]
struct Test {
    val: u32
}
 
#[derive(BinRead, Debug)]
#[br(magic = 1.2f32)]
struct Version(u16);
 
#[derive(BinRead)]
enum Command {
    #[br(magic = 0u8)] Nop,
    #[br(magic = 1u8)] Jump { loc: u32 },
    #[br(magic = 2u8)] Begin { var_count: u16, local_count: u16 }
}

Example error:

Error::BadMagic { pos: 0x30 }

See binread::Error for more info.

Assert

assert is the core of error handling in BinRead. It returns either an AssertFail or, optionally, a custom user-generated error, allowing you to attach context from before parsing failed.

Custom Error Handling Example:

#[derive(Debug, PartialEq)]
struct NotSmallerError(u32, u32);

#[derive(BinRead, Debug)]
#[br(assert(some_val > some_smaller_val, NotSmallerError(some_val, some_smaller_val)))]
struct Test {
    some_val: u32,
    some_smaller_val: u32
}

let error = Cursor::new(b"\0\0\0\x01\0\0\0\xFF").read_be::<Test>();
assert!(error.is_err());
let error = error.unwrap_err();
assert_eq!(error.custom_err(), Some(&NotSmallerError(0x1, 0xFF)));

Note: supports using previous fields

Arguments

One feature of binread is allowing arguments to be passed to the type in order to tell the type any info it needs to parse the data. To accept arguments when using the derive macro, you can use the import attribute and to pass arguments you can use the args attribute.

Example:

#[derive(BinRead)]
#[br(import(val1: u32, val2: &'static str))]
struct ImportTest {
    // ...
}
 
#[derive(BinRead)]
struct ArgsTets {
    val: u32,
    #[br(args(val + 3, "test"))]
    test: ImportTest
}

Note: supports using previous fields

Default

Set the field to the default value for the type.

#[derive(BinRead, Debug, PartialEq)]
struct Test {
    #[br(default)]
    path: Option<std::path::PathBuf>,
}
 
assert_eq!(
    Test::read(&mut Cursor::new(vec![])).unwrap(),
    Test { path: None }
);

Postprocessing

In binread postprocessing refers to the act of running after_parse on a field. It is used in order to allow a field to take control of the reader temporarily in order to parse any values not stored inline.

Postprocessing can be fast-tracked using either deref_now or postprocess_now (these are simply aliases for each other to allow). deref_now is recommended for FilePtr's, post_process is recommended for anything else.

#[derive(BinRead, Debug)]
#[br(big, magic = b"TEST")]
struct TestFile {
    #[br(deref_now)]
    ptr: FilePtr32<NullString>,

    value: i32,
 
    // Notice how `ptr` can be used as it has already been postprocessed
    #[br(calc = ptr.len())]
    ptr_len: usize,
}

Restore Position

binread supports restoring the reader position after reading the field using the restore_position attribute.

Example:

#[derive(BinRead, Debug, PartialEq)]
struct MyType {
    #[br(restore_position)]
    test: u32,
    test_bytes: [u8; 4]
}

Try

When you want to optionally allow parsing to fail, wrap the type with Option and use the try attribute. binread makes no guarantees about the position of the reader after a try in which parsing failed, so use with caution.

#[derive(BinRead)]
struct MyType {
    #[br(try)]
    maybe_u32: Option<u32>
}
 
assert_eq!(Cursor::new(b"").read_be::<MyType>().unwrap().maybe_u32, None);

Map

Sometimes the form you read isn't the form you want to store. For that, you can use the map attribute in order to apply a mapping function to map it to the type of the field.

#[derive(BinRead)]
struct MyType {
    #[br(map = |x: u8| x.to_string())]
    int_str: String
}
 

Note: supports using previous fields (if you use a closure)

Custom Parsers

In some cases, you need more advanced logic than deriving BinRead provides. For that, binread provides the parse_with attribute to allow specifying custom parser functions.

fn custom_parser<R: Read + Seek>(reader: &mut R, ro: &ReadOptions, _: ())
    -> BinResult<HashMap<u16, u16>>
{
    let mut map = HashMap::new();
    map.insert(
        reader.read_be().unwrap(),
        reader.read_be().unwrap()
    );
    Ok(map)
}
 
#[derive(BinRead)]
struct MyType {
    #[br(parse_with = custom_parser)]
    offsets: HashMap<u16, u16>
}

This can also be used with FilePtr::parse in order to read and immediately dereference a FilePtr to an owned value.

#[derive(BinRead)]
struct MyType {
    #[br(parse_with = FilePtr32::parse)]
    some_string: NullString,
}
 

Calculations

Sometimes you don't want to read a value from a file, but you want to set a field equal to an expression. Or you just want to initialize a value for a type that doesn't have a Default implementation.

#[derive(BinRead)]
struct MyType {
    var: u32,
    #[br(calc = 3 + var)]
    var_plus_3: u32,
}

Note: supports using previous fields

Count

The count attribute allows you to set the number of values to read for a Vec. If you wish to use count with a custom parser or a type's BinRead implementation you can access it using the count field on the ReadOptions type.

#[derive(BinRead)]
struct MyType {
    size: u32,
    #[br(count = size)]
    data: Vec<u8>,
}

Note: supports using previous fields

Offset

Sometimes, when you use a FilePtr you want it to represent a location that is relative to a certain location in the reader other than the start of the reader. For that you want to use one of two attributes: offset and offset_after.

Example:

#[derive(BinRead, Debug, PartialEq)]
struct OffsetTest {
    #[br(little, offset = 4)]
    test: FilePtr<u8, u16>
}

The only time you need to use offset_after is if, in your offset calculation, you use fields that are defined after your FilePtr. Otherwise use offset as offset_after doesn't support some features of binread due to order of execution.

Note: supports using previous fields

Conditional Values

binread also provides the ability to conditionally parse an Option<T> field using the if attribute. If the condition is true it will parse the value and store it as Some(T), otherwise it will store None.

#[derive(BinRead)]
struct MyType {
    var: u32,

    #[br(if(var == 1))]
    original_byte: Option<u8>,

    #[br(if(var != 1))]
    other_byte: Option<u8>,
}

Note: supports using previous fields

Padding and Alignment

  • pad_before/pad_after - skip a fixed number of bytes
  • align_before/align_after - skip bytes until aligned
  • seek_before - attribute form of calling Seek::seek
  • pad_size_to - skips to a certain number past the start of this field if that point hasn't already been passed
#[derive(BinRead)]
struct MyType {
    #[br(align_before = 4, pad_after = 1, align_after = 4)]
    str: NullString,
 
    #[br(pad_size_to = 0x10)]
    test: u64,
     
    #[br(seek_before = SeekFrom::End(-4))]
    end: u32,
}

Note: supports using previous fields