alchemy/src/endian.rs

401 lines
9.0 KiB
Rust
Raw Normal View History

use std::mem;
use std::ptr::copy_nonoverlapping;
use ::byte_sized::ByteSized;
use ::byte_sized::{U16_BYTES, U32_BYTES, U64_BYTES};
use ::converter::Converter;
/// 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 serialization where the most
/// significant byte is stored at the lowest address.
pub enum BigEndian
{
}
/// Handles serialization where the most
/// significant byte is stored at the lowest address.
pub enum LittleEndian
{
}
/// Create an enumeration of the different
/// available endianesses.
#[derive(Clone, Copy)]
pub enum Endianess
{
/// Referes to BigEndian.
BIG,
/// Referes to LittleEndian.
LITTLE,
/// Referes to PlatformEndian. This can be anyone
/// of the other available endians depending on
/// the platform you are on.
PLATFORM
}
/// Handles reading bytes from a given buffer
/// and turning them into the requested type.
macro_rules! read_bytes
{
($buffer: expr, $returnType: ident, $convertFunc: ident) =>
({
// 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, $numBytes: expr,
$num: expr, $convertFunc: ident) =>
({
assert!($buffer.len() >= $valueType::BYTES,
"Not enough room in the buffer to write to.");
unsafe
{
let size: usize;
let bytes: [u8; $numBytes];
size = $valueType::BYTES;
bytes =
mem::transmute::<_, [u8; $numBytes]>($num.$convertFunc());
copy_nonoverlapping::<u8>((&bytes).as_ptr(),
$buffer.as_mut_ptr(),
size);
}
})
}
impl Converter 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, U16_BYTES, num, to_be);
}
fn u32_to_bytes(buffer: &mut [u8], num: u32)
{
write_bytes!(buffer, u32, U32_BYTES, num, to_be);
}
fn u64_to_bytes(buffer: &mut [u8], num: u64)
{
write_bytes!(buffer, u64, U64_BYTES, 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);
}
}
fn bytes_to_string(buffer: &[u8]) -> String
{
let byte_count: u64;
let new_string: String;
// A string array should have atleast a u64 size byte count.
assert!(buffer.len() >= U64_BYTES);
// Strings start with the size of bytes to read as
// a u64. So read that in and then we know how many
// bytes make up the string.
byte_count = BigEndian::bytes_to_u64(buffer);
if byte_count > 0
{
match String::from_utf8(buffer[U64_BYTES..(buffer.len()-1)].to_vec())
{
Ok(string) =>
{
new_string = string;
}
Err(error) =>
{
error!("{}", error);
}
}
}
else
{
new_string = String::new();
}
new_string
}
fn string_to_bytes(buffer: &mut [u8], string: String)
{
let bytes: &[u8];
let byte_count: u64;
// Turn the string into a byte array.
bytes = string.as_bytes();
// Determine how many bytes will be written
// for this string.
byte_count = bytes.len() as u64;
// Make sure the buffer has enough space for this string.
assert!(buffer.len() as u64 >= byte_count + U64_BYTES as u64);
// Add the count to the buffer.
BigEndian::u64_to_bytes(&mut buffer[0..U64_BYTES], byte_count);
// Add each byte of the string to the buffer.
for (i, byte) in bytes.iter().enumerate()
{
buffer[U64_BYTES + i] = byte.clone();
}
}
}
impl Converter 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, U16_BYTES, num, to_le);
}
fn u32_to_bytes(buffer: &mut [u8], num: u32)
{
write_bytes!(buffer, u32, U32_BYTES, num, to_le);
}
fn u64_to_bytes(buffer: &mut [u8], num: u64)
{
write_bytes!(buffer, u64, U64_BYTES, 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);
}
}
fn bytes_to_string(buffer: &[u8]) -> String
{
let byte_count: u64;
let new_string: String;
// A string array should have atleast a u64 size byte count.
assert!(buffer.len() >= U64_BYTES);
// Strings start with the size of bytes to read as
// a u64. So read that in and then we know how many
// bytes make up the string.
byte_count = BigEndian::bytes_to_u64(buffer);
if byte_count > 0
{
match String::from_utf8(buffer[U64_BYTES..(buffer.len()-1)].to_vec())
{
Ok(string) =>
{
new_string = string;
}
Err(error) =>
{
error!("{}", error);
}
}
}
else
{
new_string = String::new();
}
new_string
}
fn string_to_bytes(buffer: &mut [u8], string: String)
{
let bytes: &[u8];
let byte_count: u64;
// Turn the string into a byte array.
bytes = string.as_bytes();
// Determine how many bytes will be written
// for this string.
byte_count = bytes.len() as u64 + U64_BYTES as u64;
// Make sure the buffer has enough space for this string.
assert!(buffer.len() as u64 >= byte_count);
// Add the count to the buffer.
LittleEndian::u64_to_bytes(&mut buffer[0..U64_BYTES], byte_count);
// Add each byte of the string to the buffer.
for (i, byte) in bytes.iter().enumerate()
{
buffer[U64_BYTES + i] = byte.clone();
}
}
}
/// 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
}
}