Skip to main content

Module attributes

Module attributes 

Source
Expand description

A documentation-only module for #[deku] attributes

§Scopes

To understand the Scope column of the table below:

#[deku(/* top-level */)]
struct DekuStruct {
    #[deku( /* field */)]
    field: u8,
}

#[deku(/* top-level */)]
enum DekuEnum {
    #[deku(/* variant */)]
    VariantA,

    #[deku(/* variant */)]
    VariantB( #[deku(/* field */)] u8 ),

    #[deku(/* variant */)]
    VariantC {
        #[deku( /* field */)]
        field: u8,
    }
}

§List of attributes

AttributeScopeDescription
endiantop-level, fieldSet the endianness
bit_ordertop-level, fieldSet the bit-order when reading bits
magictop-level, fieldA magic value that must be present at the start of this struct/enum/field
seek_from_currenttop-level, fieldSets the offset of reader and writer to the current position plus the specified number of bytes
seek_from_endtop-level, fieldSets the offset to the size of reader and writer plus the specified number of bytes
seek_from_starttop-level, fieldSets the offset of reader and writer to provided number of bytes
seek_rewindtop-level, fieldRewind the reader and writer to the beginning
assertfieldAssert a condition
assert_eqfieldAssert equals on the field
bitsfieldSet the bit-size of the field
bytesfieldSet the byte-size of the field
countfieldSet the field representing the element count of a container
bits_readfieldSet the field representing the number of bits to read into a container
bytes_readfieldSet the field representing the number of bytes to read into a container
untilfieldSet a predicate returning when to stop reading elements into a container
read_allfieldRead until reader.end() returns true
updatefieldApply code over the field when .update() is called
tempfieldRead the field but exclude it from the struct/enum
temp_valuefieldWrite the field but exclude it from the struct/enum
skipfieldSkip the reading/writing of a field
pad_bytes_beforefieldSkip bytes before reading, pad before writing
pad_bits_beforefieldSkip bits before reading, pad before writing
pad_bytes_afterfieldSkip bytes after reading, pad after writing
pad_bits_afterfieldSkip bits after reading, pad after writing
condfieldConditional expression for the field
defaultfieldProvide default value. Used with skip or cond
mapfieldSpecify a function or lambda to apply to the result of the read
readervariant, fieldCustom reader code
writervariant, fieldCustom writer code
ctxtop-level, fieldContext list for context sensitive parsing
ctx_defaulttop-level, fieldDefault context values
enum: idtop-level, variantenum or variant id value
enum: id_endiantop-levelEndianness of just the enum id
enum: id_patvariantvariant id match pattern
enum: id_typetop-levelSet the type of the variant id
enum: bitstop-levelSet the bit-size of the variant id
enum: bytestop-levelSet the byte-size of the variant id

§endian

Set to read/write bytes in a specific byte order.

Values: big, little or an expression which returns a Endian

Precedence: field > top-level > system endianness (default)

Example:

#[deku(endian = "little")] // top-level, defaults to system endianness
struct DekuTest {
    #[deku(endian = "big")] // field-level override
    field_be: u16,
    field_default: u16, // defaults to top-level
}

let data: &[u8] = &[0xAB, 0xCD, 0xAB, 0xCD];
let mut cursor = Cursor::new(data);

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
       field_be: 0xABCD,
       field_default: 0xCDAB,
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, &*value);

Note: The endian, as well as attributes bit_order, bits, bytes, count, is passed as a context argument to sub-types when specified. Any child struct, if present, must use ctx attribute to receive the argument. Otherwise, you may see an error like this on DekuRead and DekuWrite trait in the derive macro:

mismatched types
expected `()`, found `Endian`

Example:

#[deku(endian = "endian", ctx = "endian: deku::ctx::Endian")] // context passed from `DekuTest` top-level endian
struct Child {
    field_a: u16
}

// The `ctx` attribute here is **always** necessary to receive the context argument passed from the parent struct
// as long as `endian` attribute is specified in the parent struct either at top-level or field position,
// even when we do not care about the endianness of the parent struct,
// in this case, since we know that the struct is always big-endian, we use `_` to ignore the passed value.
#[deku(endian = "big", ctx = "_: deku::ctx::Endian")]
struct NetworkBuffer {
    type_: u8,
    #[deku(update = "self.payload.len()")]
    length: u16,
    #[deku(count = "length")]
    payload: Vec<u8>
}

#[deku(endian = "little")] // top-level, defaults to system endianness
struct DekuTest {
    #[deku(endian = "big")] // field-level override
    field_be: u16,
    field_default: u16, // defaults to top-level

    // because a top-level endian is specified,
    // it is passed as a context
    field_child: Child,

    buf: NetworkBuffer
}

let data: &[u8] = &[0xAB, 0xCD, 0xAB, 0xCD, 0xEF, 0xBE, 0x00, 0x00, 0x04, 0xDE, 0xAD, 0xBE, 0xEF];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
       field_be: 0xABCD,
       field_default: 0xCDAB,
       field_child: Child { field_a: 0xBEEF },
       buf: NetworkBuffer {
            type_: 0,
            length: 4,
            payload: 0xDEADBEEFu32.to_be_bytes().to_vec()
       }
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(&*data, value);

§bit_order

Specify the field or containers bit order. By default all bits are read in Msb0 (Most significant bit) order.

§Top-Level Example

#[deku(bit_order = "lsb")]
pub struct SquashfsV3 {
    #[deku(bits = "4")]
    inode_type: u8,
    #[deku(bits = "12")]
    mode: u16,
    uid: u8,
    guid: u8,
    mtime: u32,
    inode_number: u32,
}

let data: &[u8] = &[
//       inode_type
//     ╭-----------
//     |
//     |    mode
//    ╭+--------   ...and so on
//    ||    ||
//    vv    vv
    0x31, 0x12, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
];
let header = SquashfsV3::try_from(data).unwrap();
assert_eq!(
    SquashfsV3 {
        inode_type: 0x01,
        mode: 0x123,
        uid: 0x4,
        guid: 0x5,
        mtime: 0x6,
        inode_number: 0x7
    },
    header,
);

With endian-ness:

#[deku(endian = "big", bit_order = "lsb")]
pub struct BigEndian {
    #[deku(bits = "13")]
    offset: u16,
    #[deku(bits = "3")]
    t: u8,
    child: Child
}

// Similar to `endian`, any child struct also must use `ctx` attribute to receive the `bit_order` attribute from the parent struct.
#[deku(endian = "big", bit_order = "msb", ctx = "_: deku::ctx::Endian, _: deku::ctx::Order")]
pub struct Child {
    field_a: u8,
    #[deku(bits = "1")]
    flag1: bool,
    #[deku(bits = "1")]
    flag2: bool,
    #[deku(bits = "1")]
    flag3: bool,
    #[deku(bits = "5")]
    field_b: u8,
}

let data = vec![0x10, 0x81, 0xAB, 0b10010110];
let big_endian = BigEndian::try_from(data.as_ref()).unwrap();
assert_eq!(
    big_endian,
    BigEndian {
        offset: 0x1001,
        t: 0b100,
        child: Child {
            field_a: 0xAB,
            flag1: true,
            flag2: false,
            flag3: false,
            field_b: 0b10110,
        }
    }
);
let bytes = big_endian.to_bytes().unwrap();
assert_eq!(bytes, data);

§Field Example

pub struct LsbField {
    #[deku(bit_order = "lsb", bits = "13")]
    offset: u16,
    #[deku(bit_order = "lsb", bits = "3")]
    t: u8,
}

let data = vec![0x40, 0x40];
let more_first = LsbField::try_from(data.as_ref()).unwrap();
assert_eq!(more_first, LsbField { offset: 0x40, t: 2 });
let bytes = more_first.to_bytes().unwrap();
assert_eq!(bytes, data);

§magic

Sets a “magic” value that must be present in the data at the start of a struct/enum or field when reading, and that is written out of the start of that type’s data when writing.

Example (top-level):

#[deku(magic = b"deku")]
struct DekuTest {
    data: u8
}

let data: &[u8] = &[b'd', b'e', b'k', b'u', 50];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest { data: 50 },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);

Example (field):

struct DekuTest {
    #[deku(magic = b"deku")]
    data: u8
}

let data: &[u8] = &[b'd', b'e', b'k', b'u', 50];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest { data: 50 },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);

§seek_from_current

Using the internal reader, seek to current position plus offset before reading field.

Field Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
struct DekuTest {
    // how many following bytes to skip
    skip_u8: u8,
    #[deku(seek_from_current = "*skip_u8")]
    byte: u8,
}

let data: &[u8] = &[0x01, 0x00, 0x02];
let mut cursor = Cursor::new(data);

let (_amt_read, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest { skip_u8: 0x01, byte: 0x02 },
    value
);

let bytes = value.to_bytes().unwrap();
assert_eq!(bytes, data);

Top-Level Example (with ctx usage):

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
#[deku(seek_from_current = "skip", ctx = "skip: usize")]
struct DekuTest {
    byte: u8,
}

let data: &[u8] = &[0x00, 0x02];
let mut cursor = Cursor::new(data);
let mut reader = Reader::new(&mut cursor);

let value = DekuTest::from_reader_with_ctx(&mut reader, 1).unwrap();

assert_eq!(
    DekuTest { byte: 0x02 },
    value
);

let mut buf = vec![];
let mut cursor = Cursor::new(&mut buf);
let mut writer = Writer::new(&mut cursor);
let bytes = value.to_writer(&mut writer, 1).unwrap();
assert_eq!(buf, data);

§seek_from_end

Using the internal reader, seek to size of reader plus offset before reading field.

Field Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
struct DekuTest {
    #[deku(seek_from_end = "-2")]
    byte: u8,
}

let data: &[u8] = &[0x01, 0xff, 0x02];
let mut cursor = Cursor::new(data);

let (_amt_read, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest { byte: 0xff },
    value
);

// NOTE: to_bytes() doesn't work, because we need `seek_from_end` to already
// have a correct allocated buffer length!
let mut buf = vec![0x01, 0x00, 0x02];
let mut cursor = Cursor::new(&mut buf);
let mut writer = Writer::new(&mut cursor);
let _ = value.to_writer(&mut writer, ()).unwrap();
assert_eq!(buf, data);

Top-Level Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
#[deku(seek_from_end = "-2")]
struct DekuTest {
    byte: u8,
}

let data: &[u8] = &[0x01, 0xff, 0x02];
let mut cursor = Cursor::new(data);

let (_amt_read, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest { byte: 0xff },
    value
);

// NOTE: to_bytes() doesn't work, because we need `seek_from_end` to already
// have a correct allocated buffer length!
let mut buf = vec![0x01, 0x00, 0x02];
let mut cursor = Cursor::new(&mut buf);
let mut writer = Writer::new(&mut cursor);
let _ = value.to_writer(&mut writer, ()).unwrap();
assert_eq!(buf, data);

§seek_from_start

Using the internal reader, seek from reader start plus offset before reading field.

Field Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
struct DekuTest {
    #[deku(seek_from_start = "2")]
    byte: u8,
}

let data: &[u8] = &[0x01, 0xff, 0x02];
let mut cursor = Cursor::new(data);

let (_amt_read, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest { byte: 0x02 },
    value
);

// NOTE: to_bytes() doesn't work, because we need `seek_from_start` to already
// have a correct allocated buffer length!
let mut buf = vec![0x01, 0xff, 0x00];
let mut cursor = Cursor::new(&mut buf);
let mut writer = Writer::new(&mut cursor);
let _ = value.to_writer(&mut writer, ()).unwrap();
assert_eq!(buf, data);

Top-Level Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
#[deku(seek_from_start = "2")]
struct DekuTest {
    byte: u8,
}

let data: &[u8] = &[0x01, 0xff, 0x02];
let mut cursor = Cursor::new(data);

let (_amt_read, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest { byte: 0x02 },
    value
);

// NOTE: to_bytes() doesn't work, because we need `seek_from_start` to already
// have a correct allocated buffer length!
let mut buf = vec![0x01, 0xff, 0x00];
let mut cursor = Cursor::new(&mut buf);
let mut writer = Writer::new(&mut cursor);
let _ = value.to_writer(&mut writer, ()).unwrap();
assert_eq!(buf, data);

§seek_rewind

Rewind the internal reader to starting position.

Field Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
struct DekuTest {
    byte_01: u8,
    #[deku(seek_rewind)]
    byte_02: u8,
}

let data: &[u8] = &[0xff];
let mut cursor = Cursor::new(data);

let (_amt_read, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest { byte_01: 0xff, byte_02: 0xff },
    value
);
let bytes = value.to_bytes().unwrap();
assert_eq!(bytes, data);

Top-Level Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
#[deku(seek_rewind)]
struct DekuTest {
    byte: u8,
}

let data: &[u8] = &[0xff];
let mut cursor = Cursor::new(data);

let (_amt_read, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest { byte: 0xff},
    value
);
let bytes = value.to_bytes().unwrap();
assert_eq!(bytes, data);

§assert

Assert a condition after reading and before writing a field

Example:

struct DekuTest {
    #[deku(assert = "*data >= 8")]
    data: u8
}

let data: &[u8] = &[0x00, 0x01, 0x02];

let value = DekuTest::try_from(data);

#[cfg(feature = "descriptive-errors")]
assert_eq!(
    Err(DekuError::Assertion("Field failed assertion: DekuTest.data: * data >= 8".into())),
    value
);
#[cfg(not(feature = "descriptive-errors"))]
assert_eq!(
    Err(DekuError::Assertion("Field failed assertion".into())),
    value
);

§assert_eq

Assert equals after reading and before writing a field

Example:

struct DekuTest {
    #[deku(assert_eq = "0x01")]
    data: u8,
}

let data: &[u8] = &[0x01];

let mut value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest { data: 0x01 },
    value
);

value.data = 0x02;

let value: Result<Vec<u8>, DekuError> = value.try_into();

assert_eq!(
    Err(DekuError::Assertion("Field failed assertion: DekuTest.data: data == 0x01".into())),
    value
);

§bits

Set the bit-size of the field

Note: Cannot be used in combination with bytes

Example:

struct DekuTest {
    #[deku(bits = 2)]
    field_a: u8,
    #[deku(bits = 6)]
    field_b: u8,
    field_c: u8, // defaults to size_of<u8>*8
}

let data: &[u8] = &[0b11_101010, 0xFF];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
       field_a: 0b11,
       field_b: 0b101010,
       field_c: 0xFF,
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(&*data, value);

This attribute can also be set from a previous read:

Example:

struct DekuTest {
    field_a_len: u8,
    #[deku(bits = "*field_a_len as usize")]
    field_a: u8,
    #[deku(bits = 6)]
    field_b: u8,
}

let data: &[u8] = &[0x02, 0b11_101010];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
       field_a_len: 2,
       field_a: 0b11,
       field_b: 0b101010,
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(&*data, value);

§bytes

Set the byte-size of the field

Note: Cannot be used in combination with bits

Example:

struct DekuTest {
    #[deku(bytes = 2)]
    field_a: u32,
    field_b: u8, // defaults to size_of<u8>
}

let data: &[u8] = &[0xAB, 0xCD, 0xFF];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
       field_a: 0xCDAB,
       field_b: 0xFF,
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);

This attribute can also be set from a previous read:

Example:

struct DekuTest {
    field_a_size: u8,
    #[deku(bytes = "*field_a_size as usize")]
    field_a: u32,
}

let data: &[u8] = &[0x03, 0x01, 0x02, 0x03];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
       field_a_size: 0x03,
       field_a: 0x030201,
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);

§count

Specify the field representing the length of the container, i.e. a Vec

Example:

struct DekuTest {
    #[deku(update = "self.items.len()")]
    count: u8,
    #[deku(count = "count")]
    items: Vec<u8>,
}

let data: &[u8] = &[0x02, 0xAB, 0xCD];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
       count: 0x02,
       items: vec![0xAB, 0xCD],
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);

Note: See update for more information on the attribute!

§Specializations

  • Vec<u8>: count used with a byte vector will result in one invocation to read_bytes, thus improving performance.

§bytes_read

Specify the field representing the total number of bytes to read into a container

See the following example, where InnerDekuTest is 2 bytes, so setting bytes_read to 4 will read 2 items into the container:

struct InnerDekuTest {
    field_a: u8,
    field_b: u8
}

struct DekuTest {
    #[deku(update = "(self.items.len() / 2)")]
    bytes: u8,

    #[deku(bytes_read = "bytes")]
    items: Vec<InnerDekuTest>,
}

let data: &[u8] = &[0x04, 0xAB, 0xBC, 0xDE, 0xEF];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
       bytes: 0x04,
       items: vec![
           InnerDekuTest{field_a: 0xAB, field_b: 0xBC},
           InnerDekuTest{field_a: 0xDE, field_b: 0xEF}],
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(&*data, value);

Note: See update for more information on the attribute!

§bits_read

This is equivalent to bytes_read, however specifies the bit limit instead of a byte limit

§until

Specifies a predicate which sets when to stop reading values into the container.

Note: The last value which matches the predicate is read

The predicate is given a borrow to each item as it is read, and must return a boolean as to whether this should be the last item or not. If it returns true, then reading stops.

A good example of this is to read a null-terminated string:

struct DekuTest {
    #[deku(until = "|v: &u8| *v == 0")]
    string: Vec<u8>
}

let data: &[u8] = &[b'H', b'e', b'l', b'l', b'o', 0];
let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
        string: CString::new(b"Hello".to_vec()).unwrap().into_bytes_with_nul()
    },
    value
);

§read_all

Read values into the container until reader.end() returns true.

Example:

struct InnerDekuTest {
    field_a: u8,
    field_b: u8
}

struct DekuTest {
    #[deku(read_all)]
    items: Vec<InnerDekuTest>,
}

let data: &[u8] = &[0xAB, 0xBC, 0xDE, 0xEF];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
       items: vec![
           InnerDekuTest{field_a: 0xAB, field_b: 0xBC},
           InnerDekuTest{field_a: 0xDE, field_b: 0xEF}],
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(&*data, value);

§update

Specify custom code to run on the field when .update() is called on the struct/enum

Example:

use core::convert::{TryInto, TryFrom};
use deku::prelude::*;
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
struct DekuTest {
    #[deku(update = "self.items.len()")]
    count: u8,
    #[deku(count = "count")]
    items: Vec<u8>,
}

let data: &[u8] = &[0x02, 0xAB, 0xCD];

// `mut` so it can be updated
let mut value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest { count: 0x02, items: vec![0xAB, 0xCD] },
    value
);

// push a new item to the vec
value.items.push(0xFF);

// update it, this will update the `count` field
value.update().unwrap();

assert_eq!(
    DekuTest { count: 0x03, items: vec![0xAB, 0xCD, 0xFF] },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0x03, 0xAB, 0xCD, 0xFF], value);

§temp

A temporary field

Included in the reading of the struct/enum but not stored

Note: Struct/enum must be derived with #[deku_derive(...)] to derive DekuRead and/or DekuWrite, not with #[derive(...)]. This is because the struct/enum needs to be modified at compile time.

Example:

#[deku_derive(DekuRead, DekuWrite)]
#[derive(Debug, PartialEq)]
struct DekuTest {
    #[deku(temp)]
    num_items: u8,

    #[deku(count = "num_items", endian = "big")]
    items: Vec<u16>,
}

let data: &[u8] = &[0x01, 0xBE, 0xEF];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
       items: vec![0xBEEF]
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0xBE, 0xEF], value);

§temp_value

Value for temporary field

Will be written on corresponding offset of the struct/enum

Note: Struct/enum must be derived with #[deku_derive(...)] to derive DekuRead and/or DekuWrite, not with #[derive(...)]. This is because the struct/enum needs to be modified at compile time.

Example:

#[deku_derive(DekuRead, DekuWrite)]
#[derive(Debug, PartialEq)]
struct DekuTest {
    #[deku(temp, temp_value = "items.len() as u8")]
    num_items: u8,

    #[deku(count = "num_items", endian = "big")]
    items: Vec<u16>,
}

let value = DekuTest {
    items: vec![0xDEAD, 0xBEEF]
};
let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0x02, 0xDE, 0xAD, 0xBE, 0xEF], value);

§skip

Skip the reading/writing of a field.

Defaults value to default

Note: Can be paired with cond to have conditional skipping

Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
struct DekuTest {
    field_a: u8,
    #[deku(skip)]
    field_b: Option<u8>,
    field_c: u8,
}

let data: &[u8] = &[0x01, 0x02];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest { field_a: 0x01, field_b: None, field_c: 0x02 },
    value
);

§pad_bytes_before

Skip a number of bytes before reading, pad with 0x00s before writing

Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
pub struct DekuTest {
    pub field_a: u8,
    #[deku(pad_bytes_before = "2")]
    pub field_b: u8,
}

let data: &[u8] = &[0xAA, 0xBB, 0xCC, 0xDD];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
        field_a: 0xAA,
        field_b: 0xDD,
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0xAA, 0x00, 0x00, 0xDD], value);

§pad_bits_before

Skip a number of bytes before reading, pad with 0s before writing

Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
struct DekuTest {
    #[deku(bits = 2)]
    field_a: u8,
    #[deku(pad_bits_before = "2", bits = 4)]
    field_b: u8,
}

let data: &[u8] = &[0b10_01_1001];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
        field_a: 0b10,
        field_b: 0b1001,
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0b10_00_1001], value);

§pad_bytes_after

Skip a number of bytes after reading, pad with 0x00s after writing

Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
pub struct DekuTest {
    #[deku(pad_bytes_after = "2")]
    pub field_a: u8,
    pub field_b: u8,
}

let data: &[u8] = &[0xAA, 0xBB, 0xCC, 0xDD];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
        field_a: 0xAA,
        field_b: 0xDD,
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0xAA, 0x00, 0x00, 0xDD], value);

§pad_bits_after

Skip a number of bytes after reading, pad with 0s after writing

Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
struct DekuTest {
    #[deku(bits = 2, pad_bits_after = "2")]
    field_a: u8,
    #[deku(bits = 4)]
    field_b: u8,
}

let data: &[u8] = &[0b10_01_1001];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
        field_a: 0b10,
        field_b: 0b1001,
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0b10_00_1001], value);

§cond

Specify a condition to parse or skip a field

Note: Can be paired with default

Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
struct DekuTest {
    field_a: u8,
    #[deku(cond = "*field_a == 0x01")]
    field_b: Option<u8>,
    #[deku(cond = "*field_b == Some(0xFF)", default = "Some(0x05)")]
    field_c: Option<u8>,
    #[deku(skip, cond = "*field_a == 0x01", default = "Some(0x06)")]
    field_d: Option<u8>,
}

let data: &[u8] = &[0x01, 0x02];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest { field_a: 0x01, field_b: Some(0x02), field_c: Some(0x05), field_d: Some(0x06)},
    value
);

assert_eq!(
    vec![0x01, 0x02, 0x05],
    value.to_bytes().unwrap(),
)

§default

Default code tokens used with skip or cond

Defaults to Default::default()

Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
struct DekuTest {
    field_a: u8,
    #[deku(skip, default = "Some(*field_a)")]
    field_b: Option<u8>,
    field_c: u8,
}

let data: &[u8] = &[0x01, 0x02];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest { field_a: 0x01, field_b: Some(0x01), field_c: 0x02 },
    value
);

§map

Specify a function or lambda to apply to the result of the read

Example:

Read a u8 and apply a function to convert it to a String.

#[derive(PartialEq, Debug, DekuRead)]
struct DekuTest {
    #[deku(map = "|field: u8| -> Result<_, DekuError> { Ok(field.to_string()) }")]
    field_a: String,
    #[deku(map = "DekuTest::map_field_b")]
    field_b: String,
}

impl DekuTest {
    fn map_field_b(field_b: u8) -> Result<String, DekuError> {
        Ok(field_b.to_string())
    }
}

let data: &[u8] = &[0x01, 0x02];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest { field_a: "1".to_string(), field_b: "2".to_string() },
    value
);

§reader/writer

Specify custom reader or writer tokens for reading a field or variant

Example:

use core::convert::{TryInto, TryFrom};
use deku::bitvec::{BitSlice, BitVec, Msb0};
use deku::prelude::*;

struct DekuTest {
    #[deku(
        reader = "DekuTest::read(deku::reader)",
        writer = "DekuTest::write(deku::writer, &self.field_a)"
    )]
    field_a: String,
}

impl DekuTest {
    /// Read and convert to String
    fn read<R: std::io::Read + std::io::Seek>(
        reader: &mut deku::reader::Reader<R>,
    ) -> Result<String, DekuError> {
        let value = u8::from_reader_with_ctx(reader, ())?;
        Ok(value.to_string())
    }

    /// Parse from String to u8 and write
    fn write<W: std::io::Write + std::io::Seek>(writer: &mut Writer<W>, field_a: &str) -> Result<(), DekuError> {
        let value = field_a.parse::<u8>().unwrap();
        value.to_writer(writer, ())
    }
}

let data: &[u8] = &[0x01];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest { field_a: "1".to_string() },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, &*value);

§ctx

This attribute allows sending and receiving context (variables/values) to sub-parsers/writers

Note: endian, bit_order, bytes, bits, count attributes use ctx internally. Any child struct whose parent struct specifies these attributes MUST receive them in ctx. See examples below, and those in endian and bit_order

top-level: The value of a ctx attribute is a function argument list, for example #[deku(ctx = "a: u8, b: String")]

field-level: The value of the ctx attribute is a list of expressions, for example #[deku("a, b")]

Visibility: The following can be accessed:

  1. All former fields which have been parsed (given as a reference).
  2. endian, bytes, bits attributes declared on the top-level
    • These are prepended to the list of ctx variables

Note: The enum or struct that uses ctx will not implement DekuContainerRead or DekuContainerWrite unless ctx_default is also used.

Example

#[derive(DekuRead, DekuWrite)]
#[deku(ctx = "a: u8")]
struct Subtype {
    #[deku(map = "|b: u8| -> Result<_, DekuError> { Ok(b + a) }")]
    b: u8
}

#[derive(DekuRead, DekuWrite)]
struct Test {
    a: u8,
    #[deku(ctx = "*a")] // pass `a` to `SubType`, `a` is a reference
    sub: Subtype
}

let data: &[u8] = &[0x01, 0x02];
let mut cursor = Cursor::new(data);

let (amt_read, value) = Test::from_reader((&mut cursor, 0)).unwrap();
assert_eq!(value.a, 0x01);
assert_eq!(value.sub.b, 0x01 + 0x02)

Note: In addition, endian, bytes and bits use the ctx concept internally, examples below are equivalent:

Example:

struct Type1 {
    #[deku(endian = "big", bits = 1)]
    field: u8,
}

// is equivalent to

struct Type1 {
    #[deku(ctx = "Endian::Big, BitSize(1)")]
    field: u8,
}

Example: Adding context

#[deku(endian = "big")]
struct Type1 {
    field_a: u16,
    #[deku(bits = 5, ctx = "*field_a")]
    field_b: SubType,
}

// is equivalent to

struct Type1 {
    #[deku(ctx = "Endian::Big")]
    field_a: u16,
    #[deku(ctx = "Endian::Big, BitSize(5), *field_a")] // endian is prepended
    field_b: SubType,
}

§ctx_default

When paired with the ctx attribute, ctx_default provides default values for the context

Example:

#[derive(DekuRead, DekuWrite)]
#[deku(ctx = "a: u8", ctx_default = "1")] // Defaults `a` to 1
struct Subtype {
    #[deku(map = "|b: u8| -> Result<_, DekuError> { Ok(b + a) }")]
    b: u8
}

#[derive(DekuRead, DekuWrite)]
struct Test {
    a: u8,
    #[deku(ctx = "*a")] // pass `a` to `SubType`, `a` is a reference
    sub: Subtype
}

let data: &[u8] = &[0x01, 0x02];
let mut cursor = Cursor::new(data);

// Use with context from `Test`
let (amt_read, value) = Test::from_reader((&mut cursor, 0)).unwrap();
assert_eq!(value.a, 0x01);
assert_eq!(value.sub.b, 0x01 + 0x02);

// Use as a stand-alone container, using defaults
// Note: `from_reader` is now available on `SubType`
let data: &[u8] = &[0x02];
let mut cursor = Cursor::new(data);

let (amt_read, value) = Subtype::from_reader((&mut cursor, 0)).unwrap();
assert_eq!(value.b, 0x01 + 0x02)

§id

§id (top-level)

Specify the enum id

This is useful in cases when the enum id is already consumed or is given externally

Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
struct DekuTest {
    my_id: u8,
    data: u8,
    #[deku(ctx = "*my_id")]
    enum_from_id: MyEnum,
}

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
#[deku(ctx = "my_id: u8", id = "my_id")]
enum MyEnum {
    #[deku(id = 1)]
    VariantA(u8),
    #[deku(id = 2)]
    VariantB,
}

let data: &[u8] = &[0x01_u8, 0xff, 0xab];
let ret_read = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest {
        my_id: 0x01,
        data: 0xff,
        enum_from_id: MyEnum::VariantA(0xab),
    },
    ret_read
);

let ret_write: Vec<u8> = ret_read.try_into().unwrap();
assert_eq!(&*ret_write, data)

§id (variant)

Specify the identifier of the enum variant, must be paired with id_type or id (top-level)

Note: - If no id is specified, it is defaulted to the discriminant value. - The discriminant value is retrieved using the as keyword.

Example:

#[deku(id_type = "u8")]
enum DekuTest {
    #[deku(id = 0x01)]
    VariantA(u8),
    #[deku(id = 0x02)]
    VariantB(u8, u16),
}

let data: &[u8] = &[0x01, 0xFF, 0x02, 0xAB, 0xEF, 0xBE];
let mut cursor = Cursor::new(data);

let (amt_read, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest::VariantA(0xFF),
    value
);

let variant_bytes: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0x01, 0xFF], variant_bytes);

let (amt_read, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest::VariantB(0xAB, 0xBEEF),
    value
);

let variant_bytes: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0x02, 0xAB, 0xEF, 0xBE], variant_bytes);

Example discriminant

#[repr(u8)]
#[deku(id_type = "u8")]
enum DekuTest {
    VariantA = 0x01,
    VariantB,
}

let data: &[u8] = &[0x01, 0x02];
let mut cursor = Cursor::new(data);

let (amt_read, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest::VariantA,
    value
);

let variant_bytes: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0x01], variant_bytes);

let (rest, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest::VariantB,
    value
);

let variant_bytes: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0x02], variant_bytes);

§id_endian

Specify the endianness of the variant id, without mandating the same endianness for the fields.

Example:

#[deku(id_type = "u16", id_endian = "big", endian = "little")]
enum DekuTest {
    // Takes its endianness from the enum spec
    #[deku(id = "0x01")]
    VariantLittle(u16),
    // Force the endianness on the field
    #[deku(id = "0x02")]
    VariantBig {
        #[deku(endian = "big")]
        x: u16,
    },
}

let data: Vec<u8> = vec![0x00, 0x01, 0x01, 0x00];

let (_, value) = DekuTest::from_bytes((data.as_ref(), 0)).unwrap();

assert_eq!(
    DekuTest::VariantLittle(1),
    value
);

// ID changes, data bytes the same
let data: Vec<u8> = vec![0x00, 0x02, 0x01, 0x00];

let (_, value) = DekuTest::from_bytes((data.as_ref(), 0)).unwrap();

assert_eq!(
    DekuTest::VariantBig { x: 256 },
    value
);

§id_pat

Specify the identifier in the form of a match pattern for the enum variant.

The first field of the variant may be used for storage, and must be the same type as id_type and no attributes.

If no storage for the id is provided, the enum discriminent (if provided) will be used to write as the id for that variant

The writing of the field will use the same options as the reading.

Example:

#[deku(id_type = "u8")]
enum DekuTest {
    #[deku(id = 0x01)]
    VariantA(u8),
    #[deku(id_pat = "0x02..=0x06")]
    VariantB {
        id: u8
    },
    #[deku(id_pat = "_")]
    VariantC(u8),
}

let data: &[u8] = &[0x03, 0xFF];
let mut cursor = Cursor::new(data);

let (amt_read, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest::VariantB { id: 0x03 },
    value
);

let variant_bytes: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0x03], variant_bytes);

let (rest, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest::VariantC(0xFF),
    value
);

let variant_bytes: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0xFF], variant_bytes);

§id_type

Specify the type of the enum variant id to consume, see example

§bits

Set the bit size of the enum variant id

Note: Cannot be used in combination with bytes

Example:

#[deku(id_type = "u8", bits = 4)]
enum DekuTest {
    #[deku(id = 0b1001)]
    VariantA( #[deku(bits = 4)] u8, u8),
}

let data: &[u8] = &[0b1001_0110, 0xFF];
let mut cursor = Cursor::new(data);

let (amt_read, value) = DekuTest::from_reader((&mut cursor, 0)).unwrap();

assert_eq!(
    DekuTest::VariantA(0b0110, 0xFF),
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);

When using id_type with non-unit variants, Deku requires primitive representation. Deku uses this for calculating the discriminant when reading and writing.

#[derive(Copy, Clone, Debug, DekuRead, DekuWrite)]
#[deku(endian = "endian", ctx = "endian: deku::ctx::Endian", id_type = "u8")]
#[repr(u8)]
enum OpKind {
    Id = 0,
    Opcode = 255,
}

§bytes

Set the byte size of the enum variant id

Note: Cannot be used in combination with bits

Example:

#[deku(id_type = "u32", bytes = 2)]
enum DekuTest {
    #[deku(id = 0xBEEF)]
    VariantA(u8),
}

let data: &[u8] = &[0xEF, 0xBE, 0xFF];

let value = DekuTest::try_from(data).unwrap();

assert_eq!(
    DekuTest::VariantA(0xFF),
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);