byteordered/
lib.rs

1//! This crate provides an alternative API for reading and writing data in an
2//! endianness that might only be known at run-time. It encapsulates the
3//! existing capabilities of the [`byteorder`] crate with an interface that
4//! assumes an implicitly acknowledged byte order.
5//!
6//! The benefits of this API is two-fold. This crate supports use cases where
7//! the data's endianness is only known during program execution, which may
8//! happen in some formats and protocols. The same API can be used to reduce
9//! redundancy by indicating the intended byte order once for the entire
10//! routine, instead of once for each method call.
11//!
12//! The main contribution in this crate is the [`ByteOrdered`] wrapper type,
13//! which infuses byte order information to a data source or destination (it
14//! works for both readers and writers). Moreover, the [`Endian`] trait
15//! contains multiple primitive data reading and writing methods, and the
16//! [`Endianness`] type provides a basic enumerate for endianness information
17//! only known at run-time.
18//!
19//! # Examples
20//!
21//! Use one of [`ByteOrdered`]'s constructors to create a wrapper with byte
22//! order awareness.
23//!
24//! ```no_run
25//! use byteordered::{ByteOrdered, Endianness};
26//! # use std::error::Error;
27//! # use std::io::Read;
28//!
29//! # fn get_data_source() -> Result<Box<Read>, Box<Error>> {
30//! #     unimplemented!()
31//! # }
32//! # fn run() -> Result<(), Box<Error>> {
33//! let mut rd = ByteOrdered::le(get_data_source()?); // little endian
34//! // read a u16
35//! let w = rd.read_u16()?;
36//! // choose to read the following data in Little Endian if it's
37//! // smaller than 256, otherwise read in Big Endian
38//! let mut rd = rd.into_endianness(Endianness::le_iff(w < 256));
39//! let value: u32 = rd.read_u32()?;
40//! # Ok(())
41//! # }
42//! # fn main() {
43//! # run().unwrap();
44//! # }
45//! ```
46//!
47//! Both `byteordered` and [`byteorder`] work well side by side. You can use
48//! [`byteorder`] in one part of
49//! the routine, and wrap the reader or writer when deemed useful.
50//!
51//! ```
52//! # extern crate byteorder;
53//! # extern crate byteordered;
54//! use byteorder::ReadBytesExt;
55//! use byteordered::{ByteOrdered, Endianness};
56//! # use std::error::Error;
57//! # use std::io::Read;
58//!
59//! # fn get_data_source() -> Result<Box<Read>, Box<Error>> { unimplemented!() }
60//! # fn run() -> Result<(), Box<Error>> {
61//! let b = 5;
62//! // choose to read the following data in Little Endian if it's 0,
63//! // otherwise read in Big Endian (what happens in this case)
64//! let mut wt = ByteOrdered::runtime(
65//!     Vec::new(),
66//!     if b == 0 { Endianness::Little } else { Endianness::Big }
67//! );
68//! // write in this byte order
69//! wt.write_u16(0xC000)?;
70//! wt.write_u32(0)?;
71//! // then invert the byte order
72//! let mut wt = wt.into_opposite();
73//! wt.write_u16(0xEEFF)?;
74//! assert_eq!(&*wt.into_inner(), &[0xC0, 0, 0, 0, 0, 0, 0xFF, 0xEE]);
75//! # Ok(())
76//! # }
77//! # fn main() {
78//! # run().unwrap();
79//! # }
80//! ```
81//!
82//! As an additional construct, the [`with_order!`] macro is another API for
83//! reading and writing data, with the perk of providing explicit
84//! monomorphization with respect to the given endianness.
85//!
86//! ```no_run
87//! # #[macro_use] extern crate byteordered;
88//! # use byteordered::Endianness;
89//! # use std::error::Error;
90//! # use std::io::Read;
91//! # fn get_data_source() -> Result<Box<Read>, Box<Error>> {
92//! #     unimplemented!()
93//! # }
94//! # fn run() -> Result<(), Box<Error>> {
95//! with_order!(get_data_source()?, Endianness::Little, |rd| {
96//!     let value: u32 = rd.read_u32()?;
97//!     println!("-> {}", value);
98//! });
99//! # Ok(())
100//! # }
101//! # fn main() {
102//! # run().unwrap();
103//! # }
104//! ```
105//!
106//! # Features
107//!
108//! This library requires the standard library (`no_std` is currently not
109//! supported).
110//!
111//! [`byteorder`]: https://docs.rs/byteorder
112//! [`Endian`]: trait.Endian.html
113//! [`Endianness`]: enum.Endianness.html
114//! [`ByteOrdered`]: struct.ByteOrdered.html
115//! [`with_order!`]: macro.with_order.html
116#![warn(missing_docs)]
117
118pub extern crate byteorder;
119
120mod base;
121mod wrap;
122
123pub use base::{Endian, Endianness, StaticEndianness};
124pub use wrap::ByteOrdered;
125
126/// Creates a monomorphized scope for reading or writing with run-time byte
127/// order awareness.
128///
129/// The condition of whether to read or write data in big endian or little
130/// endian is evaluated only once, at the beginning of the scope. The given
131/// expression `$e` is then monomorphized for both cases.
132///
133/// The last argument is not a closure. It is only depicted as one to convey
134/// the familiar aspect of being provided a local variable. As such, the data
135/// source and other captured values are moved by default.
136///
137/// # Examples
138///
139/// Pass a [`ByteOrdered`] object, or a pair of data (source or destination)
140/// and endianness (typically [`Endianness`]). What follows is a pseudo-closure
141/// declaration exposing the same value with the expected byte order awareness.
142///  
143/// ```
144/// # #[macro_use] extern crate byteordered;
145/// # use byteordered::Endianness;
146/// # fn get_endianness() -> Endianness { Endianness::Little }
147/// # fn run() -> Result<(), ::std::io::Error> {
148/// let e: Endianness = get_endianness();
149/// let mut sink = Vec::new();
150/// with_order!(&mut sink, e, |dest| {
151///     // dset is a `ByteOrdered<_, StaticEndianness<_>>`
152///     dest.write_u32(8)?;
153///     dest.write_u32(1024)?;
154///     dest.write_u32(0xF0FF_F0FF)?;
155/// });
156/// assert_eq!(sink.len(), 12);
157/// # Ok(())
158/// # }
159/// # fn main() {
160/// #   run().unwrap();
161/// # }
162/// ```
163///
164/// Moreover, you can pass multiple readers or writers to be augmented with
165/// the same implicit byte order. Note that the macro requires a literal tuple
166/// expression.
167///
168/// ```
169/// # #[macro_use] extern crate byteordered;
170/// # use byteordered::Endianness;
171/// # fn get_endianness() -> Endianness { Endianness::Little }
172/// # fn run() -> Result<(), ::std::io::Error> {
173/// let e: Endianness = get_endianness();
174/// let (mut sink1, mut sink2) = (Vec::new(), Vec::new());
175/// with_order!((&mut sink1, &mut sink2), e, |dest1, dest2| {
176///     dest1.write_u32(0x0000_EEFF)?;
177///     dest2.write_u32(0xFFEE_0000)?;
178/// });
179/// assert_eq!(&sink1, &[0xFF, 0xEE, 0x00, 0x00]);
180/// assert_eq!(&sink2, &[0x00, 0x00, 0xEE, 0xFF]);
181/// # Ok(())
182/// # }
183/// # fn main() {
184/// #   run().unwrap();
185/// # }
186/// ```
187///
188/// One might think that this always improves performance, since a
189/// runtime-bound `ByteOrdered` with a sequence of reads/writes would expand
190/// into one check for each method call:
191///
192/// ```
193/// # use byteordered::{ByteOrdered, Endianness};
194/// # fn get_endianness() -> Endianness { Endianness::Little }
195/// # fn run() -> Result<(), ::std::io::Error> {
196/// let mut dst = ByteOrdered::runtime(Vec::new(), get_endianness());
197/// // dynamic dispatch each time (or is it?)
198/// dst.write_u32(8)?;
199/// dst.write_u32(1024)?;
200/// dst.write_u32(0xF0FF_F0FF)?;
201/// # Ok(())
202/// # }
203/// # run().unwrap();
204/// ```
205///
206/// However, because the compiler is known to optimize these checks away in
207/// the same context, making a scope for that purpose is not always necessary.
208/// On the other hand, this will ensure that deeper function calls are
209/// monomorphized to a static endianness without making unnecessary run-time
210/// checks, specifically when function calls are not inlined. It can also be
211/// seen as yet another way to create and manage data sources/destinations with
212/// byte order awareness.
213///
214/// [`ByteOrdered`]: struct.ByteOrdered.html
215/// [`Endianness`]: enum.Endianness.html
216#[macro_export]
217macro_rules! with_order {
218    ($byteordered: expr, |$bo: ident| $e: expr) => {
219        {
220            let b = $byteordered;
221            let e = b.endianness();
222            with_order!(b.into_inner(), e, |$bo| $e)
223        }
224    };
225    ( ($($src: expr ),*), $endianness: expr, |$($bo: ident ),*| $e: expr ) => {
226        match $endianness {
227            Endianness::Big => {
228                $(
229                let mut $bo = ::byteordered::ByteOrdered::new(
230                    $src,
231                    ::byteordered::StaticEndianness::<::byteordered::byteorder::BigEndian>::default());
232                )*
233                $e
234            }
235            Endianness::Little => {
236                $(
237                let mut $bo = ::byteordered::ByteOrdered::new(
238                    $src,
239                    ::byteordered::StaticEndianness::<::byteordered::byteorder::LittleEndian>::default());
240                )*
241                $e
242            }
243        }
244    };
245    ($src: expr, $endianness: expr, |$bo: ident| $e: expr ) => {
246        match $endianness {
247            Endianness::Big => {
248                let mut $bo = ::byteordered::ByteOrdered::new(
249                    $src,
250                    ::byteordered::StaticEndianness::<::byteordered::byteorder::BigEndian>::default());
251                $e
252            }
253            Endianness::Little => {
254                let mut $bo = ::byteordered::ByteOrdered::new(
255                    $src,
256                    ::byteordered::StaticEndianness::<::byteordered::byteorder::LittleEndian>::default());
257                $e
258            }
259        }
260    };
261}