From 039c093b92096269c3a6538f743309b8d61b5de5 Mon Sep 17 00:00:00 2001 From: Jason Travis Smith Date: Wed, 30 Dec 2015 17:06:48 -0500 Subject: [PATCH] The library can now convert numbers to and from bytes. Numbers can be converted to and from bytes in both BigEndian and LittleEndian styles. This does not yet take care of the other mathematical structures provided by Sigil. Also, no tests of these capabilities have been written yet. --- src/endian.rs | 245 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 15 ++- src/transmutable.rs | 260 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 517 insertions(+), 3 deletions(-) create mode 100644 src/endian.rs create mode 100644 src/transmutable.rs diff --git a/src/endian.rs b/src/endian.rs new file mode 100644 index 0000000..fbeae5d --- /dev/null +++ b/src/endian.rs @@ -0,0 +1,245 @@ +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, $return_type: ident, $convert_func: ident) => + ({ + use std::$return_type; + + assert!($return_type::BYTES <= $buffer.len()); + unsafe + { + (*($buffer.as_ptr() as *const $return_type)).$convert_func() + } + }) +} + +/// Handles turning a given number into bytes +/// and writing them to a buffer. +macro_rules! write_bytes +{ + ($buffer: expr, $value_type: ident, $num: expr, $convert_func: ident) => + ({ + use std::$value_type; + + assert!($value_type::BYTES <= $buffer.len()); + unsafe + { + let size: usize; + let bytes: [u8; $value_type::BYTES]; + + size = $value_type::BYTES as usize; + + bytes = + mem::transmute::<_,[u8; $value_type::BYTES]>($num.$convert_func()); + copy_nonoverlapping((&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], num_bytes: u8) -> usize + { + let mut out: [u8; 8]; + let ptr_out: *mut u8; + + assert!(1 <= num_bytes && num_bytes <= 8); + assert!(num_bytes as usize <= buffer.len()); + + out = [0u8; 8]; + ptr_out = out.as_mut_ptr(); + unsafe + { + copy_nonoverlapping(buffer.as_ptr(), + ptr_out.offset((8 - num_bytes) as isize), + num_bytes as usize); + + (*(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, num_bytes: u8) + { + let bytes: [u8; 8]; + + assert!(determine_size(num as u64) <= num_bytes && num_bytes <= 8); + assert!(num_bytes as usize <= buffer.len()); + unsafe + { + + bytes = mem::transmute::(num.to_be()); + copy_nonoverlapping(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], num_bytes: u8) -> usize + { + let mut out: [u8; 8]; + let ptr_out: *mut u8; + + assert!(1 <= num_bytes && num_bytes <= 8); + assert!(num_bytes as usize <= buffer.len()); + + out = [0u8; 8]; + ptr_out = out.as_mut_ptr(); + unsafe + { + copy_nonoverlapping(buffer.as_ptr(), ptr_out, num_bytes as usize); + (*(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, num_bytes: u8) + { + let bytes: [u8; 8]; + + assert!(determine_size(num as u64) <= num_bytes && num_bytes <= 8); + assert!(num_bytes as usize <= buffer.len()); + unsafe + { + + bytes = mem::transmute::(num.to_le()); + copy_nonoverlapping(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 + } +} diff --git a/src/lib.rs b/src/lib.rs index a93251b..f2ef538 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,12 @@ -#[test] -fn it_works() { -} +#![feature(num_bits_bytes)] + +extern crate sigils; + + + +mod transmutable; +mod endian; + + +pub use ::transmutable::Transmutable; +pub use ::endian::{BigEndian, LittleEndian, PlatformEndian}; diff --git a/src/transmutable.rs b/src/transmutable.rs new file mode 100644 index 0000000..e3ac467 --- /dev/null +++ b/src/transmutable.rs @@ -0,0 +1,260 @@ +use std::mem; + + +/// Describes types that can transmute Numbers into bytes, +/// or bytes into Numbers. This means that they can also +/// transmute higher order mathematic types like Vectors, +/// Quaternions, Matrices, and such. +pub trait Transmutable +{ + /// Converts an array of bytes to a signed 16-bit integer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough information to convert. + fn bytes_to_i16(buffer: &[u8]) -> i16 + { + Self::bytes_to_u16(buffer) as i16 + } + + /// Converts an array of bytes to a signed 32-bit integer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough information to convert. + fn bytes_to_i32(buffer: &[u8]) -> i32 + { + Self::bytes_to_u32(buffer) as i32 + } + + /// Converts an array of bytes to a signed 64-bit integer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough information to convert. + fn bytes_to_i64(buffer: &[u8]) -> i64 + { + Self::bytes_to_u64(buffer) as i64 + } + + /// Converts an array of bytes to a signed integer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough information to convert. + /// + /// This will panic if the number of bytes + /// passed in is less than one or more than eight. + fn bytes_to_isize(buffer: &[u8], num_bytes: u8) -> isize + { + let temp_num: u64; + + assert!(num_bytes > 1 && num_bytes < 8); + assert!(buffer.len() < (num_bytes as usize)); + + temp_num = Self::bytes_to_usize(buffer, num_bytes) as u64; + add_sign(temp_num, num_bytes) as isize + } + + /// Converts an array of bytes to a 32-bit floating point number. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough information to convert. + fn bytes_to_f32(buffer: &[u8]) -> f32 + { + unsafe + { + mem::transmute::(Self::bytes_to_u32(buffer)) + } + } + + /// Converts an array of bytes to a 64-bit floating point number. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough information to convert. + fn bytes_to_f64(buffer: &[u8]) -> f64 + { + unsafe + { + mem::transmute::(Self::bytes_to_u64(buffer)) + } + } + + + /// Converts a signed 16-bit integer to bytes + /// and places them into the given buffer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough space to store the converted value. + fn i16_to_bytes(buffer: &mut [u8], num: i16) + { + Self::u16_to_bytes(buffer, num as u16) + } + + /// Converts a signed 32-bit integer to bytes + /// and places them into the given buffer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough space to store the converted value. + fn i32_to_bytes(buffer: &mut [u8], num: i32) + { + Self::u32_to_bytes(buffer, num as u32) + } + + /// Converts a signed 64-bit integer to bytes + /// and places them into the given buffer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough space to store the converted value. + fn i64_to_bytes(buffer: &mut [u8], num: i64) + { + Self::u64_to_bytes(buffer, num as u64) + } + + /// Converts a signed integer to bytes + /// and places them into the given buffer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough space to store the converted value. + /// + /// This will panic if the number of bytes + /// passed in is less than the byte size of the given number + /// or more than eight. + fn isize_to_bytes(buffer: &mut [u8], num: isize, num_bytes: u8) + { + let temp_num: usize; + + assert!(num_bytes > 1 && num_bytes < 8); + assert!(buffer.len() < (num_bytes as usize)); + + temp_num = remove_sign(num as i64, num_bytes) as usize; + Self::usize_to_bytes(buffer, temp_num, num_bytes) + } + + /// Converts a 32-bit floating point number to bytes + /// and places them into the given buffer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough space to store the converted value. + fn f32_to_bytes(buffer: &mut [u8], num: f32) + { + unsafe + { + Self::u32_to_bytes(buffer, mem::transmute::(num)); + } + } + + /// Converts a 64-bit floating point number to bytes + /// and places them into the given buffer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough space to store the converted value. + fn f64_to_bytes(buffer: &mut [u8], num: f64) + { + unsafe + { + Self::u64_to_bytes(buffer, mem::transmute::(num)); + } + } + + + + /// Converts an array of bytes to an unsigned 16-bit integer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough information to convert. + fn bytes_to_u16(buffer: &[u8]) -> u16; + + /// Converts an array of bytes to an unsigned 32-bit integer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough information to convert. + fn bytes_to_u32(buffer: &[u8]) -> u32; + + /// Converts an array of bytes to an unsigned 64-bit integer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough information to convert. + fn bytes_to_u64(buffer: &[u8]) -> u64; + + /// Converts an array of bytes to an unsigned integer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough information to convert. + /// + /// This will panic if the number of bytes + /// passed in is less than one or more than eight. + fn bytes_to_usize(buffer: &[u8], num_bytes: u8) -> usize; + + + /// Converts an unsigned 16-bit integer to bytes + /// and places them into the given buffer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough space to store the converted value. + fn u16_to_bytes(buffer: &mut [u8], num: u16); + + /// Converts an unsigned 32-bit integer to bytes + /// and places them into the given buffer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough space to store the converted value. + fn u32_to_bytes(buffer: &mut [u8], num: u32); + + /// Converts an unsigned 64-bit integer to bytes + /// and places them into the given buffer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough space to store the converted value. + fn u64_to_bytes(buffer: &mut [u8], num: u64); + + /// Converts an unsigned integer to bytes + /// and places them into the given buffer. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough space to store the converted value. + /// + /// This will panic if the number of bytes + /// passed in is less than the byte size of the given number + /// or more than eight. + fn usize_to_bytes(buffer: &mut [u8], num: usize, num_bytes: u8); +} + + +/// Switches an unsigned value into its signed equivalent. +/// +/// NOTE: This seems messy for larger unsigned values. +fn add_sign(val: u64, num_bytes: u8) -> i64 +{ + let shift: u8; + + shift = (8 - num_bytes) * 8; + (val << shift) as i64 >> shift +} + +/// Switches a signed value into its unsigned equivalent. +/// +/// NOTE: This seems messy for values under zero. +fn remove_sign(val: i64, num_bytes: u8) -> u64 +{ + let shift: u8; + + shift = (8 - num_bytes) * 8; + (val << shift) as u64 >> shift +}