blob: 8351604ef16b0db3fe30828e25b20a3051179aa6 [file] [log] [blame]
/*!
This crate provides convenience methods for encoding and decoding numbers
in either big-endian or little-endian order.
*/
#![crate_name = "byteorder"]
#![doc(html_root_url = "http://burntsushi.net/rustdoc/byteorder")]
#![deny(missing_docs)]
#![allow(unused_features)] // for `rand` while testing
#![feature(core, io, rand, test)]
use std::old_io::IoResult;
// A trivial logging macro. No reason to pull in `log`, which has become
// difficult to use in tests.
macro_rules! lg {
($($arg:tt)*) => ({
let _ = ::std::old_io::stderr().write_str(&*format!($($arg)*));
let _ = ::std::old_io::stderr().write_str("\n");
});
}
/// ByteOrder describes types that can serialize integers as bytes.
///
/// Note that `Self` does not appear anywhere in this trait's definition!
/// Therefore, in order to use it, you'll need to use syntax like
/// `<T as ByteOrder>::read_u16(&[0, 1])` where `T` implements `ByteOrder`.
///
/// This crate provides two types that implement `ByteOrder`: `BigEndian`
/// and `LittleEndian`.
///
/// # Examples
///
/// Write and read `u32` numbers in little endian order:
///
/// ```rust
/// use byteorder::{ByteOrder, LittleEndian};
///
/// let buf = &mut [0; 4];
/// <LittleEndian as ByteOrder>::write_u32(buf, 1_000_000);
/// assert_eq!(1_000_000, <LittleEndian as ByteOrder>::read_u32(buf));
/// ```
///
/// Write and read `i16` numbers in big endian order:
///
/// ```rust
/// use byteorder::{ByteOrder, BigEndian};
///
/// let buf = &mut [0; 2];
/// <BigEndian as ByteOrder>::write_i16(buf, -50_000);
/// assert_eq!(-50_000, <BigEndian as ByteOrder>::read_i16(buf));
/// ```
pub trait ByteOrder {
/// Reads an unsigned 16 bit integer from `buf`.
///
/// Task failure occurs when `buf.len() < 2`.
fn read_u16(buf: &[u8]) -> u16;
/// Reads an unsigned 32 bit integer from `buf`.
///
/// Task failure occurs when `buf.len() < 4`.
fn read_u32(buf: &[u8]) -> u32;
/// Reads an unsigned 64 bit integer from `buf`.
///
/// Task failure occurs when `buf.len() < 8`.
fn read_u64(buf: &[u8]) -> u64;
/// Writes an unsigned 16 bit integer `n` to `buf`.
///
/// Task failure occurs when `buf.len() < 2`.
fn write_u16(buf: &mut [u8], n: u16);
/// Writes an unsigned 32 bit integer `n` to `buf`.
///
/// Task failure occurs when `buf.len() < 4`.
fn write_u32(buf: &mut [u8], n: u32);
/// Writes an unsigned 64 bit integer `n` to `buf`.
///
/// Task failure occurs when `buf.len() < 8`.
fn write_u64(buf: &mut [u8], n: u64);
/// Reads a signed 16 bit integer from `buf`.
///
/// Task failure occurs when `buf.len() < 2`.
fn read_i16(buf: &[u8]) -> i16 {
<Self as ByteOrder>::read_u16(buf) as i16
}
/// Reads a signed 32 bit integer from `buf`.
///
/// Task failure occurs when `buf.len() < 4`.
fn read_i32(buf: &[u8]) -> i32 {
<Self as ByteOrder>::read_u32(buf) as i32
}
/// Reads a signed 64 bit integer from `buf`.
///
/// Task failure occurs when `buf.len() < 8`.
fn read_i64(buf: &[u8]) -> i64 {
<Self as ByteOrder>::read_u64(buf) as i64
}
/// Writes a signed 16 bit integer `n` to `buf`.
///
/// Task failure occurs when `buf.len() < 2`.
fn write_i16(buf: &mut [u8], n: i16) {
<Self as ByteOrder>::write_u16(buf, n as u16)
}
/// Writes a signed 32 bit integer `n` to `buf`.
///
/// Task failure occurs when `buf.len() < 4`.
fn write_i32(buf: &mut [u8], n: i32) {
<Self as ByteOrder>::write_u32(buf, n as u32)
}
/// Writes a signed 64 bit integer `n` to `buf`.
///
/// Task failure occurs when `buf.len() < 8`.
fn write_i64(buf: &mut [u8], n: i64) {
<Self as ByteOrder>::write_u64(buf, n as u64)
}
}
/// Extends `Reader` with methods for reading numbers.
///
/// Most of the methods defined here have an unconstrained type parameter that
/// must be explicitly instantiated. Typically, it is instantiated with either
/// the `BigEndian` or `LittleEndian` types defined in this crate.
///
/// # Examples
///
/// Read unsigned 16 bit big-endian integers from a `Reader`:
///
/// ```rust
/// use std::old_io::MemReader;
/// use byteorder::{BigEndian, ReaderBytesExt};
///
/// let mut rdr = MemReader::new(vec![2, 5, 3, 0]);
/// assert_eq!(517, rdr.read_u16::<BigEndian>().unwrap());
/// assert_eq!(768, rdr.read_u16::<BigEndian>().unwrap());
/// ```
pub trait ReaderBytesExt: Reader + Sized {
/// Reads an unsigned 8 bit integer from the underlying reader.
///
/// Note that since this reads a single byte, no byte order conversions
/// are used. It is included for completeness.
fn read_u8(&mut self) -> IoResult<u8> {
let mut buf = &mut [0; 1];
try!(read_full(self, buf));
Ok(buf[0])
}
/// Reads a signed 8 bit integer from the underlying reader.
///
/// Note that since this reads a single byte, no byte order conversions
/// are used. It is included for completeness.
fn read_i8(&mut self) -> IoResult<i8> {
let mut buf = &mut [0; 1];
try!(read_full(self, buf));
Ok(buf[0] as i8)
}
/// Reads an unsigned 16 bit integer from the underlying reader.
fn read_u16<T: ByteOrder>(&mut self) -> IoResult<u16> {
let mut buf = &mut [0; 2];
try!(read_full(self, buf));
Ok(<T as ByteOrder>::read_u16(buf))
}
/// Reads a signed 16 bit integer from the underlying reader.
fn read_i16<T: ByteOrder>(&mut self) -> IoResult<i16> {
let mut buf = &mut [0; 2];
try!(read_full(self, buf));
Ok(<T as ByteOrder>::read_i16(buf))
}
/// Reads an unsigned 32 bit integer from the underlying reader.
fn read_u32<T: ByteOrder>(&mut self) -> IoResult<u32> {
let mut buf = &mut [0; 4];
try!(read_full(self, buf));
Ok(<T as ByteOrder>::read_u32(buf))
}
/// Reads a signed 32 bit integer from the underlying reader.
fn read_i32<T: ByteOrder>(&mut self) -> IoResult<i32> {
let mut buf = &mut [0; 4];
try!(read_full(self, buf));
Ok(<T as ByteOrder>::read_i32(buf))
}
/// Reads an unsigned 64 bit integer from the underlying reader.
fn read_u64<T: ByteOrder>(&mut self) -> IoResult<u64> {
let mut buf = &mut [0; 8];
try!(read_full(self, buf));
Ok(<T as ByteOrder>::read_u64(buf))
}
/// Reads a signed 64 bit integer from the underlying reader.
fn read_i64<T: ByteOrder>(&mut self) -> IoResult<i64> {
let mut buf = &mut [0; 8];
try!(read_full(self, buf));
Ok(<T as ByteOrder>::read_i64(buf))
}
}
/// All types that implement `Reader` get methods defined in `ReaderBytesExt`
/// for free.
impl<R: Reader> ReaderBytesExt for R {}
fn read_full<R: Reader>(rdr: &mut R, buf: &mut [u8]) -> IoResult<()> {
let mut n = 0us;
while n < buf.len() {
n += try!(rdr.read(&mut buf[n..]));
}
Ok(())
}
/// Extends `Writer` with methods for writing numbers.
///
/// Most of the methods defined here have an unconstrained type parameter that
/// must be explicitly instantiated. Typically, it is instantiated with either
/// the `BigEndian` or `LittleEndian` types defined in this crate.
///
/// # Examples
///
/// Write unsigned 16 bit big-endian integers to a `Writer`:
///
/// ```rust
/// use byteorder::{BigEndian, WriterBytesExt};
///
/// let mut wtr = vec![];
/// wtr.write_u16::<BigEndian>(517).unwrap();
/// wtr.write_u16::<BigEndian>(768).unwrap();
/// assert_eq!(wtr, vec![2, 5, 3, 0]);
/// ```
pub trait WriterBytesExt: Writer + Sized {
/// Writes an unsigned 8 bit integer to the underlying writer.
///
/// Note that since this writes a single byte, no byte order conversions
/// are used. It is included for completeness.
fn write_u8(&mut self, n: u8) -> IoResult<()> {
self.write_all(&[n])
}
/// Writes a signed 8 bit integer to the underlying writer.
///
/// Note that since this writes a single byte, no byte order conversions
/// are used. It is included for completeness.
fn write_i8(&mut self, n: i8) -> IoResult<()> {
self.write_all(&[n as u8])
}
/// Writes an unsigned 16 bit integer to the underlying writer.
fn write_u16<T: ByteOrder>(&mut self, n: u16) -> IoResult<()> {
let mut buf = &mut [0; 2];
<T as ByteOrder>::write_u16(buf, n);
self.write_all(buf)
}
/// Writes a signed 16 bit integer to the underlying writer.
fn write_i16<T: ByteOrder>(&mut self, n: i16) -> IoResult<()> {
let mut buf = &mut [0; 2];
<T as ByteOrder>::write_i16(buf, n);
self.write_all(buf)
}
/// Writes an unsigned 32 bit integer to the underlying writer.
fn write_u32<T: ByteOrder>(&mut self, n: u32) -> IoResult<()> {
let mut buf = &mut [0; 4];
<T as ByteOrder>::write_u32(buf, n);
self.write_all(buf)
}
/// Writes a signed 32 bit integer to the underlying writer.
fn write_i32<T: ByteOrder>(&mut self, n: i32) -> IoResult<()> {
let mut buf = &mut [0; 4];
<T as ByteOrder>::write_i32(buf, n);
self.write_all(buf)
}
/// Writes an unsigned 64 bit integer to the underlying writer.
fn write_u64<T: ByteOrder>(&mut self, n: u64) -> IoResult<()> {
let mut buf = &mut [0; 8];
<T as ByteOrder>::write_u64(buf, n);
self.write_all(buf)
}
/// Writes a signed 64 bit integer to the underlying writer.
fn write_i64<T: ByteOrder>(&mut self, n: i64) -> IoResult<()> {
let mut buf = &mut [0; 8];
<T as ByteOrder>::write_i64(buf, n);
self.write_all(buf)
}
}
/// All types that implement `Writer` get methods defined in `WriterBytesExt`
/// for free.
impl<W: Writer> WriterBytesExt for W {}
/// Defines big-endian serialization.
///
/// Note that this type has no value constructor. It is used purely at the
/// type level.
#[allow(missing_copy_implementations)] pub struct BigEndian;
/// Defines little-endian serialization.
///
/// Note that this type has no value constructor. It is used purely at the
/// type level.
#[allow(missing_copy_implementations)] pub struct LittleEndian;
macro_rules! read_num_bytes {
($ty:ty, $size:expr, $src:expr, $which:ident) => ({
use std::num::Int;
use std::ptr::copy_nonoverlapping_memory;
let mut out = [0u8; $size];
let ptr_out = out.as_mut_ptr();
unsafe {
copy_nonoverlapping_memory(ptr_out, $src.as_ptr(), $size);
(*(ptr_out as *const $ty)).$which()
}
});
}
macro_rules! write_num_bytes {
($ty:ty, $size:expr, $n:expr, $dst:expr, $which:ident) => ({
use std::mem::transmute;
use std::num::Int;
use std::ptr::copy_nonoverlapping_memory;
unsafe {
let bytes = (&transmute::<_, [u8; $size]>($n.$which())).as_ptr();
copy_nonoverlapping_memory($dst.as_mut_ptr(), bytes, $size);
}
});
}
impl ByteOrder for BigEndian {
fn read_u16(buf: &[u8]) -> u16 {
read_num_bytes!(u16, 2, buf, to_be)
}
fn read_u32(buf: &[u8]) -> u32 {
read_num_bytes!(u32, 4, buf, to_be)
}
fn read_u64(buf: &[u8]) -> u64 {
read_num_bytes!(u64, 8, buf, to_be)
}
fn write_u16(buf: &mut [u8], n: u16) {
write_num_bytes!(u16, 2, n, buf, to_be);
}
fn write_u32(buf: &mut [u8], n: u32) {
write_num_bytes!(u32, 4, n, buf, to_be);
}
fn write_u64(buf: &mut [u8], n: u64) {
write_num_bytes!(u64, 8, n, buf, to_be);
}
}
impl ByteOrder for LittleEndian {
fn read_u16(buf: &[u8]) -> u16 {
read_num_bytes!(u16, 2, buf, to_le)
}
fn read_u32(buf: &[u8]) -> u32 {
read_num_bytes!(u32, 4, buf, to_le)
}
fn read_u64(buf: &[u8]) -> u64 {
read_num_bytes!(u64, 8, buf, to_le)
}
fn write_u16(buf: &mut [u8], n: u16) {
write_num_bytes!(u16, 2, n, buf, to_le);
}
fn write_u32(buf: &mut [u8], n: u32) {
write_num_bytes!(u32, 4, n, buf, to_le);
}
fn write_u64(buf: &mut [u8], n: u64) {
write_num_bytes!(u64, 8, n, buf, to_le);
}
}
#[cfg(test)]
mod test {
extern crate quickcheck;
use std::rand::thread_rng;
use test::quickcheck::{QuickCheck, StdGen, Testable};
fn qc_sized<A: Testable>(f: A, size: u64) {
QuickCheck::new()
.gen(StdGen::new(thread_rng(), size as usize))
.tests(1_00)
.max_tests(10_000)
.quickcheck(f);
}
macro_rules! qc_byte_order {
($name:ident, $ty_int:ident, $read:ident, $write:ident) => (
mod $name {
use std::$ty_int;
use {BigEndian, ByteOrder, LittleEndian};
use super::qc_sized;
#[test]
fn big_endian() {
fn prop(n: $ty_int) -> bool {
let buf = &mut [0; 8];
<BigEndian as ByteOrder>::$write(buf, n);
n == <BigEndian as ByteOrder>::$read(buf)
}
qc_sized(prop as fn($ty_int) -> bool, $ty_int::MAX as u64);
}
#[test]
fn little_endian() {
fn prop(n: $ty_int) -> bool {
let buf = &mut [0; 8];
<LittleEndian as ByteOrder>::$write(buf, n);
n == <LittleEndian as ByteOrder>::$read(buf)
}
qc_sized(prop as fn($ty_int) -> bool, $ty_int::MAX as u64);
}
}
);
}
qc_byte_order!(prop_u16, u16, read_u16, write_u16);
qc_byte_order!(prop_i16, i16, read_i16, write_i16);
qc_byte_order!(prop_u32, u32, read_u32, write_u32);
qc_byte_order!(prop_i32, i32, read_i32, write_i32);
qc_byte_order!(prop_u64, u64, read_u64, write_u64);
qc_byte_order!(prop_i64, i64, read_i64, write_i64);
macro_rules! qc_bytes_ext {
($name:ident, $ty_int:ident, $read:ident, $write:ident) => (
mod $name {
use std::old_io::MemReader;
use std::$ty_int;
use {ReaderBytesExt, WriterBytesExt, BigEndian, LittleEndian};
use super::qc_sized;
#[test]
fn big_endian() {
fn prop(n: $ty_int) -> bool {
let mut wtr = vec![];
wtr.$write::<BigEndian>(n).unwrap();
let mut rdr = MemReader::new(wtr);
n == rdr.$read::<BigEndian>().unwrap()
}
qc_sized(prop as fn($ty_int) -> bool, $ty_int::MAX as u64);
}
#[test]
fn little_endian() {
fn prop(n: $ty_int) -> bool {
let mut wtr = vec![];
wtr.$write::<LittleEndian>(n).unwrap();
let mut rdr = MemReader::new(wtr);
n == rdr.$read::<LittleEndian>().unwrap()
}
qc_sized(prop as fn($ty_int) -> bool, $ty_int::MAX as u64);
}
}
);
}
qc_bytes_ext!(prop_ext_u16, u16, read_u16, write_u16);
qc_bytes_ext!(prop_ext_i16, i16, read_i16, write_i16);
qc_bytes_ext!(prop_ext_u32, u32, read_u32, write_u32);
qc_bytes_ext!(prop_ext_i32, i32, read_i32, write_i32);
qc_bytes_ext!(prop_ext_u64, u64, read_u64, write_u64);
qc_bytes_ext!(prop_ext_i64, i64, read_i64, write_i64);
}
#[cfg(test)]
mod bench {
extern crate test;
macro_rules! bench_num {
($ty:ident, $read:ident, $write:ident, $size:expr, $data:expr) => (
mod $ty {
use std::$ty;
use {ByteOrder, BigEndian, LittleEndian};
use super::test::Bencher;
use super::test::black_box as bb;
const NITER: usize = 100_000;
#[bench]
fn read_big_endian(b: &mut Bencher) {
let buf = $data;
b.iter(|| {
for _ in 0..NITER {
bb(<BigEndian as ByteOrder>::$read(&buf));
}
});
}
#[bench]
fn read_little_endian(b: &mut Bencher) {
let buf = $data;
b.iter(|| {
for _ in 0..NITER {
bb(<LittleEndian as ByteOrder>::$read(&buf));
}
});
}
#[bench]
fn write_big_endian(b: &mut Bencher) {
let mut buf = $data;
let n = $ty::MAX;
b.iter(|| {
for _ in 0..NITER {
bb(<BigEndian as ByteOrder>::$write(&mut buf,
n));
}
});
}
#[bench]
fn write_little_endian(b: &mut Bencher) {
let mut buf = $data;
let n = $ty::MAX;
b.iter(|| {
for _ in 0..NITER {
bb(<LittleEndian as ByteOrder>::$write(&mut buf,
n));
}
});
}
}
);
}
bench_num!(u16, read_u16, write_u16, 2, [1, 2]);
bench_num!(i16, read_i16, write_i16, 2, [1, 2]);
bench_num!(u32, read_u32, write_u32, 4, [1, 2, 3, 4]);
bench_num!(i32, read_i32, write_i32, 4, [1, 2, 3, 4]);
bench_num!(u64, read_u64, write_u64, 8, [1, 2, 3, 4, 5, 6, 7, 8]);
bench_num!(i64, read_i64, write_i64, 8, [1, 2, 3, 4, 5, 6, 7, 8]);
}