From e9ab0bdece39d67daa49e139286d164b0bddb27f Mon Sep 17 00:00:00 2001 From: Jason Travis Smith Date: Wed, 13 Apr 2016 23:57:40 -0400 Subject: [PATCH] Strings can now be transmuted or converted into binary data. This also added the scribe library so that this library can use the logging system. --- Cargo.lock | 6 +++ Cargo.toml | 9 ++++ src/byte_sized.rs | 17 ++++++ src/converter.rs | 20 +++++++ src/endian.rs | 125 +++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 7 +++ src/transmutable.rs | 42 +++++++++++++++ 7 files changed, 225 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 53e7705..e91a898 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,6 +3,7 @@ name = "alchemy" version = "0.1.0" dependencies = [ "rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "scribe 0.1.0 (git+https://gitlab.com/CyberMages/scribe.git)", "sigils 0.1.0 (git+https://gitlab.com/CyberMages/sigils.git)", ] @@ -30,6 +31,11 @@ dependencies = [ "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "scribe" +version = "0.1.0" +source = "git+https://gitlab.com/CyberMages/scribe.git#e52418d3bfc28cd1f03cc7f31af06fce2e03f844" + [[package]] name = "sigils" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index ffa054f..8abf649 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,15 @@ name = "alchemy" version = "0.1.0" authors = ["Jason Travis Smith "] +description = "Handles converting values to and from a binary format." +license = "" +repository = "https://gitlab.com/CyberMages/alchemy.git" +documentation = "" +keywords = ["converter", "binary"] + + +[dependencies.scribe] +git = "https://gitlab.com/CyberMages/scribe.git" [dependencies.sigils] git = "https://gitlab.com/CyberMages/sigils.git" diff --git a/src/byte_sized.rs b/src/byte_sized.rs index f53bbd5..063b890 100644 --- a/src/byte_sized.rs +++ b/src/byte_sized.rs @@ -115,3 +115,20 @@ byte_sized_impl!((i8, I8_BYTES) (i16, I16_BYTES)); byte_sized_impl!((i32, I32_BYTES) (i64, U64_BYTES)); byte_sized_impl!((f32, F32_BYTES) (f64, F64_BYTES)); byte_sized_impl!((usize, USIZE_BYTES) (isize, ISIZE_BYTES)); + + +/// This returns the amount of bytes it takes to store a string +/// in a binary format. This includes the u64 size we prepend to +/// store the amount of bytes of data in the String. +pub fn get_byte_size_of_string(string: &String) -> usize +{ + let bytes: &[u8]; + + // Turn the string into a byte array. + bytes = string.as_bytes(); + + // Determine how many bytes will be written + // for this string and add the amount of + // bytes needed for the u64 size. + bytes.len() + U64_BYTES +} diff --git a/src/converter.rs b/src/converter.rs index 9f3b658..fb37712 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -231,6 +231,26 @@ pub trait Converter /// 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); + + + /// Converts a String 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 String. + fn bytes_to_string(buffer: &[u8]) -> String; + + /// Converts an array of bytes to a String. + /// + /// # Panics + /// This will panic if the buffer does not have + /// enough information to convert. + fn string_to_bytes(buffer: &mut [u8], string: String); } diff --git a/src/endian.rs b/src/endian.rs index 9827c52..ee2064b 100644 --- a/src/endian.rs +++ b/src/endian.rs @@ -74,7 +74,8 @@ macro_rules! read_bytes /// and writing them to a buffer. macro_rules! write_bytes { - ($buffer: expr, $valueType: ident, $numBytes: expr, $num: expr, $convertFunc: ident) => + ($buffer: expr, $valueType: ident, $numBytes: expr, + $num: expr, $convertFunc: ident) => ({ assert!($buffer.len() >= $valueType::BYTES, "Not enough room in the buffer to write to."); @@ -166,6 +167,67 @@ impl Converter for BigEndian 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 @@ -234,6 +296,67 @@ impl Converter for LittleEndian 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(); + } + } } diff --git a/src/lib.rs b/src/lib.rs index fbe872c..5a16bcf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,15 @@ //! The Alchemy library is a data type to byte converter library. //! Alchemy handles converting numbers to and from bytes //! in either big or little endian format. +#![doc(html_logo_url="http://cybermagesllc.com/wp-content/uploads/2012/06/logo-300x300.png", + html_favicon_url="http://cybermagesllc.com/favicon.ico", + html_root_url="http://cybermagesllc.com")] #![feature(associated_consts)] #![warn(missing_docs)] +#[macro_use] +extern crate scribe; + extern crate sigils; @@ -19,6 +25,7 @@ pub use ::byte_sized::ByteSized; pub use ::byte_sized::{U8_BYTES, U16_BYTES, U32_BYTES, U64_BYTES, USIZE_BYTES}; pub use ::byte_sized::{I8_BYTES, I16_BYTES, I32_BYTES, I64_BYTES, ISIZE_BYTES}; pub use ::byte_sized::{F32_BYTES, F64_BYTES}; +pub use ::byte_sized::get_byte_size_of_string; pub use ::converter::Converter; pub use ::endian::{BigEndian, LittleEndian, PlatformEndian, Endianess}; pub use ::transmutable::Transmutable; diff --git a/src/transmutable.rs b/src/transmutable.rs index 340fa13..14f5f59 100644 --- a/src/transmutable.rs +++ b/src/transmutable.rs @@ -366,6 +366,48 @@ impl Transmutable for f64 } } +impl Transmutable for String +{ + fn to_bytes(&self, buffer: &mut [u8], endianess: Endianess) + { + let temp: String; + let mut mut_buffer; + + // Create a mutable handle to the buffer. + mut_buffer = buffer; + + // Clone the string so that we can use its data. + // We have to do this because Strings don't implement Copy. + temp = self.clone(); + + // Convert this to bytes and add it to the buffer. + // We are not using the macro because *String is a str. + match endianess + { + Endianess::BIG => + { + BigEndian::string_to_bytes(&mut mut_buffer, temp); + } + + Endianess::LITTLE => + { + LittleEndian::string_to_bytes(&mut mut_buffer, temp); + } + + Endianess::PLATFORM => + { + PlatformEndian::string_to_bytes(&mut mut_buffer, temp); + } + } + } + + fn from_bytes(buffer: &[u8], endianess: Endianess) -> String + { + // Convert the given bytes to this type and return it. + handle_endianess_from_bytes!(buffer, endianess, bytes_to_string) + } +} + impl Transmutable for Vector2 where T: Number + ByteSized + Transmutable { fn to_bytes(&self, buffer: &mut [u8], endianess: Endianess)