#proc-macro #data #transmute #safely #data-structures #assist #transmutable

macro transmute-tools

Proc macros and traits to assist with safely creating transmutable data structures

1 unstable release

0.1.0 Apr 22, 2021

#1913 in Procedural macros

MIT license

18KB
319 lines

transmute-tools

Proc macros to assist with safely creating transmutable data structures. To be clear, this does not make it safe to use transmute. This is just a collection of tools that might help you do that. The specific, initial use case was to help faithfully reproduce structures from the NVMe spec.

Usage

Location Assertions

transmute_tools::test_structure provides an attribute which allows specifying the location within a struct that a field must be placed:

use structural_assert::test_structure;

#[test_structure(size = 20)]
#[repr(C, packed)]
pub struct Foo {
    #[loc(0:0)]
    pub a: u8,
    #[loc(1:1)]
    pub b: u8,
    #[loc(2:3)]
    pub c: u16,
    #[loc(4:19)]
    pub d: u128,
}

Endianness

transmute_tools::endianness provides an attribute which allows specifying specific endianness for a struct's fields. A struct like this:

#[macro_use]
extern crate transmute_tools;

#[endianness(be)]
#[repr(C, packed)]
pub struct Foo {
    pub a: u8,
    pub b: u8,
    pub c: u16,
    #[le]
    pub d: u128,
    pub e: Box<()>,
}

Will generate this:

#[repr(C, packed)]
pub struct Convert {
    // Note the missing visbilities.
    a: u8,
    b: u8,
    c: u16,
    d: u128,
    pub e: Box<()>,
}
impl Convert {
    #[inline]
    pub fn a(&self) -> u8 {
        u8::from_be(self.a)
    }
    #[inline]
    pub fn set_a(&mut self, value: u8) {
        self.a = value.to_be();
    }
    #[inline]
    pub fn b(&self) -> u8 {
        u8::from_be(self.b)
    }
    #[inline]
    pub fn set_b(&mut self, value: u8) {
        self.b = value.to_be();
    }
    #[inline]
    pub fn c(&self) -> u16 {
        u16::from_be(self.c)
    }
    #[inline]
    pub fn set_c(&mut self, value: u16) {
        self.c = value.to_be();
    }
    #[inline]
    pub fn d(&self) -> u128 {
        u128::from_le(self.d)
    }
    #[inline]
    pub fn set_d(&mut self, value: u128) {
        self.d = value.to_le();
    }
}

NOTE: This must generate functions rather than use something like simple_endianness because access to fields of packed structs when done by value rather than reference.

Dependencies

~1.5MB
~35K SLoC