alchemy/src/endian.rs
Jason Travis Smith 010abeff7c Fixed the tests to be correct.
There is now a random test for all the primitive number types.

Also, I fixed my errors with the tests that should panic. I kept thinking
that they should run fine and it was throwing off how I was writing
the code.
2016-01-03 03:22:21 -05:00

257 lines
5.5 KiB
Rust

use std::mem;
use std::ptr::copy_nonoverlapping;
use super::transmutable::Transmutable;
/// Handles serialization where the most
/// significant byte is stored at the lowest address.
pub struct BigEndian;
/// Handles serialization where the most
/// significant byte is stored at the lowest address.
pub struct LittleEndian;
/// Defines the current platforms endianess.
/// This is can only be big endian or little endian.
/// This library does not support a mixed endian setting.
#[cfg(target_endian="big")]
pub type PlatformEndian = BigEndian;
/// Defines the current platforms endianess.
/// This is can only be BigEndian or LittleEndian.
/// This library does not support a mixed endian setting.
///
/// Defaults to LittleEndian.
//#[cfg(target_endian="little")]
#[cfg(not(target_endian="big"))]
pub type PlatformEndian = LittleEndian;
/// Handles reading bytes from a given buffer
/// and turning them into the requested type.
macro_rules! read_bytes
{
($buffer: expr, $returnType: ident, $convertFunc: ident) =>
({
use std::$returnType;
// Make sure that there is enough space to read
// a value from the buffer.
assert!($buffer.len() == $returnType::BYTES);
unsafe
{
(*($buffer.as_ptr() as *const $returnType)).$convertFunc()
}
})
}
/// Handles turning a given number into bytes
/// and writing them to a buffer.
macro_rules! write_bytes
{
($buffer: expr, $valueType: ident, $num: expr, $convertFunc: ident) =>
({
use std::$valueType;
assert!($buffer.len() >= $valueType::BYTES,
"Not enough room in the buffer to write to.");
unsafe
{
let size: usize;
let bytes: [u8; $valueType::BYTES];
size = $valueType::BYTES as usize;
bytes =
mem::transmute::<_,[u8; $valueType::BYTES]>($num.$convertFunc());
copy_nonoverlapping::<u8>((&bytes).as_ptr(),
$buffer.as_mut_ptr(),
size);
}
})
}
impl Transmutable for BigEndian
{
fn bytes_to_u16(buffer: &[u8]) -> u16
{
read_bytes!(buffer, u16, to_be)
}
fn bytes_to_u32(buffer: &[u8]) -> u32
{
read_bytes!(buffer, u32, to_be)
}
fn bytes_to_u64(buffer: &[u8]) -> u64
{
read_bytes!(buffer, u64, to_be)
}
fn bytes_to_usize(buffer: &[u8]) -> usize
{
let mut out: [u8; 8];
let ptr_out: *mut u8;
assert!(buffer.len() >= 1 && buffer.len() <= 8);
out = [0u8; 8];
ptr_out = out.as_mut_ptr();
unsafe
{
copy_nonoverlapping::<u8>(buffer.as_ptr(),
ptr_out.offset((8 - buffer.len()) as isize),
buffer.len());
(*(ptr_out as *const u64)).to_be() as usize
}
}
fn u16_to_bytes(buffer: &mut [u8], num: u16)
{
write_bytes!(buffer, u16, num, to_be);
}
fn u32_to_bytes(buffer: &mut [u8], num: u32)
{
write_bytes!(buffer, u32, num, to_be);
}
fn u64_to_bytes(buffer: &mut [u8], num: u64)
{
write_bytes!(buffer, u64, num, to_be);
}
fn usize_to_bytes(buffer: &mut [u8], num: usize)
{
let bytes: [u8; 8];
let num_bytes: u8;
num_bytes = buffer.len() as u8;
assert!(determine_size(num as u64) <= num_bytes && num_bytes <= 8);
assert!(buffer.len() >= 1 && buffer.len() <= 8);
unsafe
{
bytes = mem::transmute::<usize, [u8; 8]>(num.to_be());
copy_nonoverlapping::<u8>(
bytes.as_ptr().offset((8 - num_bytes) as isize),
buffer.as_mut_ptr(), num_bytes as usize);
}
}
}
impl Transmutable for LittleEndian
{
fn bytes_to_u16(buffer: &[u8]) -> u16
{
read_bytes!(buffer, u16, to_le)
}
fn bytes_to_u32(buffer: &[u8]) -> u32
{
read_bytes!(buffer, u32, to_le)
}
fn bytes_to_u64(buffer: &[u8]) -> u64
{
read_bytes!(buffer, u64, to_le)
}
fn bytes_to_usize(buffer: &[u8]) -> usize
{
let mut out: [u8; 8];
let ptr_out: *mut u8;
assert!(buffer.len() >= 1 && buffer.len() <= 8);
out = [0u8; 8];
ptr_out = out.as_mut_ptr();
unsafe
{
copy_nonoverlapping::<u8>(buffer.as_ptr(), ptr_out, buffer.len());
(*(ptr_out as *const u64)).to_le() as usize
}
}
fn u16_to_bytes(buffer: &mut [u8], num: u16)
{
write_bytes!(buffer, u16, num, to_le);
}
fn u32_to_bytes(buffer: &mut [u8], num: u32)
{
write_bytes!(buffer, u32, num, to_le);
}
fn u64_to_bytes(buffer: &mut [u8], num: u64)
{
write_bytes!(buffer, u64, num, to_le);
}
fn usize_to_bytes(buffer: &mut [u8], num: usize)
{
let bytes: [u8; 8];
let num_bytes: u8;
num_bytes = buffer.len() as u8;
assert!(determine_size(num as u64) <= num_bytes && num_bytes <= 8);
assert!(buffer.len() >= 1 && buffer.len() <= 8);
unsafe
{
bytes = mem::transmute::<usize, [u8; 8]>(num.to_le());
copy_nonoverlapping::<u8>(bytes.as_ptr(), buffer.as_mut_ptr(),
num_bytes as usize);
}
}
}
/// Determine the amount of bytes required to
/// represent the given number.
fn determine_size(num: u64) -> u8
{
if num < (1 << 8)
{
1
}
else if num < (1 << 16)
{
2
}
else if num < (1 << 24)
{
3
}
else if num < (1 << 32)
{
4
}
else if num < (1 << 40)
{
5
}
else if num < (1 << 48)
{
6
}
else if num < (1 << 56)
{
7
}
else
{
8
}
}