Skip to content

Instantly share code, notes, and snippets.

@Enet4
Last active December 11, 2018 18:00
Show Gist options
  • Save Enet4/ce9fac871bf688e1bf5c7b37e1a4dfaf to your computer and use it in GitHub Desktop.
Save Enet4/ce9fac871bf688e1bf5c7b37e1a4dfaf to your computer and use it in GitHub Desktop.
byteordered::with_order macro rendered docs
macro_rules! with_order {
( ($($src: expr ),*), $endianness: expr, |$($bo: ident ),*| $e: expr ) => { ... }
($src: expr, $endianness: expr, |$bo: ident| $e: expr ) => { ... }
}

Creates a 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 sure to be triggered only once, at the beginning of the scope. The given expression $e is then monomorphized for both cases.

Examples

Pass something that implements Read or Write, and something which evaluates to a byte order descriptor (typically Endianness). What follows is a pseudo-closure declaration exposing the same value with the expected byte order awareness.

#[macro_use] extern crate byteordered;
use byteordered::Endianness;
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.

#[macro_use] extern crate byteordered;
use byteordered::Endianness;
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 improves performance, since a runtime-bound ByteOrdered with a sequence of reads/writes would expand into one check for each method call:

use byteordered::{ByteOrdered, Endianness};
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 can be seen as yet another way to create and manage data sources/destinations with byte order awareness.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment