[][src]Macro byteordered::with_order

macro_rules! with_order {
    ($byteordered: expr, |$bo: ident| $e: expr) => { ... };
    ( ($($src: expr ),*), $endianness: expr, |$($bo: ident ),*| $e: expr ) => { ... };
    ($src: expr, $endianness: expr, |$bo: ident| $e: expr ) => { ... };
}

Creates a monomorphized scope for reading or writing with run-time byte order awareness.

The condition of whether to read or write data in big endian or little endian is evaluated only once, at the beginning of the scope. The given expression $e is then monomorphized for both cases.

The last argument is not a closure. It is only depicted as one to convey the familiar aspect of being provided a local variable. As such, the data source and other captured values are moved by default.

Examples

Pass a ByteOrdered object, or a pair of data (source or destination) and endianness (typically Endianness). What follows is a pseudo-closure declaration exposing the same value with the expected byte order awareness.

let e: Endianness = get_endianness();
let mut sink = Vec::new(); 
with_order!(&mut sink, e, |dest| {
    // dset is a `ByteOrdered<_, StaticEndianness<_>>`
    dest.write_u32(8)?;
    dest.write_u32(1024)?;
    dest.write_u32(0xF0FF_F0FF)?;
});
assert_eq!(sink.len(), 12);

Moreover, you can pass multiple readers or writers to be augmented with the same implicit byte order. Note that the macro requires a literal tuple expression.

let e: Endianness = get_endianness();
let (mut sink1, mut sink2) = (Vec::new(), Vec::new());
with_order!((&mut sink1, &mut sink2), e, |dest1, dest2| {
    dest1.write_u32(0x0000_EEFF)?;
    dest2.write_u32(0xFFEE_0000)?;
});
assert_eq!(&sink1, &[0xFF, 0xEE, 0x00, 0x00]);
assert_eq!(&sink2, &[0x00, 0x00, 0xEE, 0xFF]);

One might think that this always improves performance, since a runtime-bound ByteOrdered with a sequence of reads/writes would expand into one check for each method call:

let mut dst = ByteOrdered::runtime(Vec::new(), get_endianness());
// dynamic dispatch each time (or is it?)
dst.write_u32(8)?;
dst.write_u32(1024)?;
dst.write_u32(0xF0FF_F0FF)?;

However, because the compiler is known to optimize these checks away in the same context, making a scope for that purpose is not always necessary. On the other hand, this will ensure that deeper function calls are monomorphized to a static endianness without making unnecessary run-time checks, specifically when function calls are not inlined. It can also be seen as yet another way to create and manage data sources/destinations with byte order awareness.