From 12b85f9a7f1f59fe835cef2d22a5015f3df9fd18 Mon Sep 17 00:00:00 2001 From: Artyom Pavlov Date: Tue, 6 Jun 2017 14:24:48 +0300 Subject: [PATCH] Byte tools update --- byte-tools/Cargo.toml | 2 +- byte-tools/src/lib.rs | 312 +-------------------------------- byte-tools/src/read_single.rs | 38 ++++ byte-tools/src/read_slice.rs | 44 +++++ byte-tools/src/tests.rs | 50 ------ byte-tools/src/write_single.rs | 39 +++++ byte-tools/src/write_slice.rs | 44 +++++ 7 files changed, 175 insertions(+), 354 deletions(-) create mode 100644 byte-tools/src/read_single.rs create mode 100644 byte-tools/src/read_slice.rs delete mode 100644 byte-tools/src/tests.rs create mode 100644 byte-tools/src/write_single.rs create mode 100644 byte-tools/src/write_slice.rs diff --git a/byte-tools/Cargo.toml b/byte-tools/Cargo.toml index bd851c38..ce797501 100644 --- a/byte-tools/Cargo.toml +++ b/byte-tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "byte-tools" -version = "0.1.3" +version = "0.2.0" authors = ["The Rust-Crypto Project Developers"] license = "MIT/Apache-2.0" description = "Utility functions for working with bytes" diff --git a/byte-tools/src/lib.rs b/byte-tools/src/lib.rs index 65212a45..b21a12a1 100644 --- a/byte-tools/src/lib.rs +++ b/byte-tools/src/lib.rs @@ -1,243 +1,15 @@ #![no_std] -use core::{mem, ptr}; +use core::ptr; -/// Write a u64 into a vector, which must be 8 bytes long. The value is written -/// in big-endian format. -pub fn write_u64_be(dst: &mut [u8], mut input: u64) { - assert!(dst.len() == 8); - input = input.to_be(); - unsafe { - let tmp = &input as *const _ as *const u8; - ptr::copy_nonoverlapping(tmp, dst.get_unchecked_mut(0), 8); - } -} - -/// Write a u64 into a vector, which must be 8 bytes long. The value is written -/// in little-endian format. -pub fn write_u64_le(dst: &mut [u8], mut input: u64) { - assert!(dst.len() == 8); - input = input.to_le(); - unsafe { - let tmp = &input as *const _ as *const u8; - ptr::copy_nonoverlapping(tmp, dst.get_unchecked_mut(0), 8); - } -} - -/// Write a vector of u64s into a vector of bytes. The values are written in -/// little-endian format. -pub fn write_u64v_be(dst: &mut [u8], input: &[u64]) { - assert!(dst.len() == 8 * input.len()); - unsafe { - let mut x: *mut u8 = dst.get_unchecked_mut(0); - let mut y: *const u64 = input.get_unchecked(0); - for _ in 0..input.len() { - let tmp = (*y).to_be(); - ptr::copy_nonoverlapping(&tmp as *const _ as *const u8, x, 8); - x = x.offset(8); - y = y.offset(1); - } - } -} - -/// Write a vector of u64s into a vector of bytes. The values are written in -/// little-endian format. -pub fn write_u64v_le(dst: &mut [u8], input: &[u64]) { - assert!(dst.len() == 8 * input.len()); - unsafe { - let mut x: *mut u8 = dst.get_unchecked_mut(0); - let mut y: *const u64 = input.get_unchecked(0); - for _ in 0..input.len() { - let tmp = (*y).to_le(); - ptr::copy_nonoverlapping(&tmp as *const _ as *const u8, x, 8); - x = x.offset(8); - y = y.offset(1); - } - } -} +mod read_single; +mod write_single; +mod read_slice; +mod write_slice; -/// Write a u32 into a vector, which must be 4 bytes long. The value is written -/// in big-endian format. -pub fn write_u32_be(dst: &mut [u8], mut input: u32) { - assert!(dst.len() == 4); - input = input.to_be(); - unsafe { - let tmp = &input as *const _ as *const u8; - ptr::copy_nonoverlapping(tmp, dst.get_unchecked_mut(0), 4); - } -} - -/// Write a u32 into a vector, which must be 4 bytes long. The value is written -/// in little-endian format. -pub fn write_u32_le(dst: &mut [u8], mut input: u32) { - assert!(dst.len() == 4); - input = input.to_le(); - unsafe { - let tmp = &input as *const _ as *const u8; - ptr::copy_nonoverlapping(tmp, dst.get_unchecked_mut(0), 4); - } -} - -/// Write a vector of u32s into a vector of bytes. The values are written in -/// little-endian format. -pub fn write_u32v_le(dst: &mut [u8], input: &[u32]) { - assert!(dst.len() == 4 * input.len()); - unsafe { - let mut x: *mut u8 = dst.get_unchecked_mut(0); - let mut y: *const u32 = input.get_unchecked(0); - for _ in 0..input.len() { - let tmp = (*y).to_le(); - ptr::copy_nonoverlapping(&tmp as *const _ as *const u8, x, 4); - x = x.offset(4); - y = y.offset(1); - } - } -} - -/// Write a vector of u32s into a vector of bytes. The values are written in -/// big-endian format. -pub fn write_u32v_be(dst: &mut [u8], input: &[u32]) { - assert!(dst.len() == 4 * input.len()); - unsafe { - let mut x: *mut u8 = dst.get_unchecked_mut(0); - let mut y: *const u32 = input.get_unchecked(0); - for _ in 0..input.len() { - let tmp = (*y).to_be(); - ptr::copy_nonoverlapping(&tmp as *const _ as *const u8, x, 4); - x = x.offset(4); - y = y.offset(1); - } - } -} - -/// Read a vector of bytes into a vector of u64s. The values are read in -/// big-endian format. -pub fn read_u64v_be(dst: &mut [u64], input: &[u8]) { - assert!(dst.len() * 8 == input.len()); - unsafe { - let mut x: *mut u64 = dst.get_unchecked_mut(0); - let mut y: *const u8 = input.get_unchecked(0); - for _ in 0..dst.len() { - let mut tmp: u64 = mem::uninitialized(); - ptr::copy_nonoverlapping(y, &mut tmp as *mut _ as *mut u8, 8); - *x = u64::from_be(tmp); - x = x.offset(1); - y = y.offset(8); - } - } -} - -/// Read a vector of bytes into a vector of u64s. The values are read in -/// little-endian format. -pub fn read_u64v_le(dst: &mut [u64], input: &[u8]) { - assert!(dst.len() * 8 == input.len()); - unsafe { - let mut x: *mut u64 = dst.get_unchecked_mut(0); - let mut y: *const u8 = input.get_unchecked(0); - for _ in 0..dst.len() { - let mut tmp: u64 = mem::uninitialized(); - ptr::copy_nonoverlapping(y, &mut tmp as *mut _ as *mut u8, 8); - *x = u64::from_le(tmp); - x = x.offset(1); - y = y.offset(8); - } - } -} - -/// Read a vector of bytes into a vector of u32s. The values are read in -/// big-endian format. -pub fn read_u32v_be(dst: &mut [u32], input: &[u8]) { - assert!(dst.len() * 4 == input.len()); - unsafe { - let mut x: *mut u32 = dst.get_unchecked_mut(0); - let mut y: *const u8 = input.get_unchecked(0); - for _ in 0..dst.len() { - let mut tmp: u32 = mem::uninitialized(); - ptr::copy_nonoverlapping(y, &mut tmp as *mut _ as *mut u8, 4); - *x = u32::from_be(tmp); - x = x.offset(1); - y = y.offset(4); - } - } -} - -/// Read a vector of bytes into a vector of u32s. The values are read in -/// little-endian format. -pub fn read_u32v_le(dst: &mut [u32], input: &[u8]) { - assert!(dst.len() * 4 == input.len()); - unsafe { - let mut x: *mut u32 = dst.get_unchecked_mut(0); - let mut y: *const u8 = input.get_unchecked(0); - for _ in 0..dst.len() { - let mut tmp: u32 = mem::uninitialized(); - ptr::copy_nonoverlapping(y, &mut tmp as *mut _ as *mut u8, 4); - *x = u32::from_le(tmp); - x = x.offset(1); - y = y.offset(4); - } - } -} - -/// Read the value of a vector of bytes as a u32 value in little-endian format. -pub fn read_u32_le(input: &[u8]) -> u32 { - assert!(input.len() == 4); - unsafe { - let mut tmp: u32 = mem::uninitialized(); - ptr::copy_nonoverlapping(input.get_unchecked(0), - &mut tmp as *mut _ as *mut u8, - 4); - u32::from_le(tmp) - } -} - -/// Read the value of a vector of bytes as a u64 value in little-endian format. -pub fn read_u64_le(input: &[u8]) -> u64 { - assert!(input.len() == 8); - unsafe { - let mut tmp: u64 = mem::uninitialized(); - ptr::copy_nonoverlapping(input.get_unchecked(0), - &mut tmp as *mut _ as *mut u8, - 8); - u64::from_le(tmp) - } -} - -/// Read the value of a vector of bytes as a u32 value in big-endian format. -pub fn read_u32_be(input: &[u8]) -> u32 { - assert!(input.len() == 4); - unsafe { - let mut tmp: u32 = mem::uninitialized(); - ptr::copy_nonoverlapping(input.get_unchecked(0), - &mut tmp as *mut _ as *mut u8, - 4); - u32::from_be(tmp) - } -} - -/// Read the value of a vector of bytes as a u64 value in big-endian format. -pub fn read_u64_be(input: &[u8]) -> u64 { - assert!(input.len() == 8); - unsafe { - let mut tmp: u64 = mem::uninitialized(); - ptr::copy_nonoverlapping(input.get_unchecked(0), - &mut tmp as *mut _ as *mut u8, - 8); - u64::from_be(tmp) - } -} - -/// XOR plaintext and keystream, storing the result in dst. -pub fn xor_keystream(dst: &mut [u8], plaintext: &[u8], keystream: &[u8]) { - assert!(dst.len() == plaintext.len()); - assert!(plaintext.len() <= keystream.len()); - - // Do one byte at a time, using unsafe to skip bounds checking. - let p = plaintext.as_ptr(); - let k = keystream.as_ptr(); - let d = dst.as_mut_ptr(); - for i in 0isize..plaintext.len() as isize { - unsafe { *d.offset(i) = *p.offset(i) ^ *k.offset(i) }; - } -} +pub use read_single::*; +pub use write_single::*; +pub use read_slice::*; +pub use write_slice::*; /// Copy bytes from src to dest #[inline] @@ -257,69 +29,3 @@ pub fn zero(dst: &mut [u8]) { ptr::write_bytes(dst.as_mut_ptr(), 0, dst.len()); } } - -/// Convert the value in bytes to the number of bits, a tuple where the 1st -/// item is the high-order value and the 2nd item is the low order value. -fn to_bits(x: u64) -> (u64, u64) { (x >> 61, x << 3) } - -/// Adds the specified number of bytes to the bit count. panic!() if this -/// would cause numeric overflow. -pub fn add_bytes_to_bits(bits: u64, bytes: u64) -> u64 { - let (new_high_bits, new_low_bits) = to_bits(bytes); - - if new_high_bits > 0 { - panic!("Numeric overflow occured.") - } - - bits.checked_add(new_low_bits).expect("Numeric overflow occured.") -} - -/// Adds the specified number of bytes to the bit count, which is a tuple where -/// the first element is the high order value. panic!() if this would cause -/// numeric overflow. -pub fn add_bytes_to_bits_tuple(bits: (u64, u64), bytes: u64) -> (u64, u64) { - let (new_high_bits, new_low_bits) = to_bits(bytes); - let (hi, low) = bits; - - // Add the low order value - if there is no overflow, then add the high - // order values. If the addition of the low order values causes overflow, - // add one to the high order values before adding them. - match low.checked_add(new_low_bits) { - Some(x) => { - if new_high_bits == 0 { - // This is the fast path - every other alternative will rarely - // occur in practice considering how large an input would need - // to be for those paths to be used. - (hi, x) - } else { - match hi.checked_add(new_high_bits) { - Some(y) => (y, x), - None => panic!("Numeric overflow occured."), - } - } - }, - None => { - let z = match new_high_bits.checked_add(1) { - Some(w) => w, - None => panic!("Numeric overflow occured."), - }; - match hi.checked_add(z) { - // This re-executes the addition that was already performed - // earlier when overflow occured, this time allowing the - // overflow to happen. Technically, this could be avoided by - // using the checked add intrinsic directly, but that involves - // using unsafe code and is not really worthwhile considering - // how infrequently code will run in practice. This is the - // reason that this function requires that the type T be - // UnsignedInt - overflow is not defined for Signed types. - // This function could be implemented for signed types as well - // if that were needed. - Some(y) => (y, low.wrapping_add(new_low_bits)), - None => panic!("Numeric overflow occured."), - } - }, - } -} - -#[cfg(test)] -pub mod tests; diff --git a/byte-tools/src/read_single.rs b/byte-tools/src/read_single.rs new file mode 100644 index 00000000..76e7feb9 --- /dev/null +++ b/byte-tools/src/read_single.rs @@ -0,0 +1,38 @@ +use core::{mem, ptr}; + +macro_rules! read_single { + ($src:expr, $size:expr, $ty:ty, $which:ident) => ({ + assert!($size == mem::size_of::<$ty>()); + assert!($size == $src.len()); + unsafe { + let mut tmp: $ty = mem::uninitialized(); + let p = &mut tmp as *mut _ as *mut u8; + ptr::copy_nonoverlapping($src.as_ptr(), p, $size); + tmp.$which() + } + }); +} + +/// Read the value of a vector of bytes as a u32 value in little-endian format. +#[inline] +pub fn read_u32_le(src: &[u8]) -> u32 { + read_single!(src, 4, u32, to_le) +} + +/// Read the value of a vector of bytes as a u32 value in big-endian format. +#[inline] +pub fn read_u32_be(src: &[u8]) -> u32 { + read_single!(src, 4, u32, to_be) +} + +/// Read the value of a vector of bytes as a u64 value in little-endian format. +#[inline] +pub fn read_u64_le(src: &[u8]) -> u64 { + read_single!(src, 8, u64, to_le) +} + +/// Read the value of a vector of bytes as a u64 value in big-endian format. +#[inline] +pub fn read_u64_be(src: &[u8]) -> u64 { + read_single!(src, 8, u64, to_be) +} diff --git a/byte-tools/src/read_slice.rs b/byte-tools/src/read_slice.rs new file mode 100644 index 00000000..858e34b8 --- /dev/null +++ b/byte-tools/src/read_slice.rs @@ -0,0 +1,44 @@ +use core::ptr; + +macro_rules! read_slice { + ($src:expr, $dst:expr, $size:expr, $which:ident) => ({ + assert_eq!($size*$dst.len(), $src.len()); + unsafe { + ptr::copy_nonoverlapping( + $src.as_ptr(), + $dst.as_mut_ptr() as *mut u8, + $src.len()); + } + for v in $dst.iter_mut() { + *v = v.$which(); + } + }); +} + +/// Read a vector of bytes into a vector of u32s. The values are read in +/// little-endian format. +#[inline] +pub fn read_u32v_le(dst: &mut [u32], src: &[u8]) { + read_slice!(src, dst, 4, to_le); +} + +/// Read a vector of bytes into a vector of u32s. The values are read in +/// big-endian format. +#[inline] +pub fn read_u32v_be(dst: &mut [u32], src: &[u8]) { + read_slice!(src, dst, 4, to_be); +} + +/// Read a vector of bytes into a vector of u64s. The values are read in +/// little-endian format. +#[inline] +pub fn read_u64v_le(dst: &mut [u64], src: &[u8]) { + read_slice!(src, dst, 8, to_le); +} + +/// Read a vector of bytes into a vector of u64s. The values are read in +/// big-endian format. +#[inline] +pub fn read_u64v_be(dst: &mut [u64], src: &[u8]) { + read_slice!(src, dst, 8, to_be); +} diff --git a/byte-tools/src/tests.rs b/byte-tools/src/tests.rs deleted file mode 100644 index d1133025..00000000 --- a/byte-tools/src/tests.rs +++ /dev/null @@ -1,50 +0,0 @@ -use core; - -use super::{add_bytes_to_bits, add_bytes_to_bits_tuple}; - -// A normal addition - no overflow occurs -#[test] -fn test_add_bytes_to_bits_ok() { - assert!(add_bytes_to_bits(100, 10) == 180); -} - -// A simple failure case - adding 1 to the max value -#[test] -#[should_panic] -fn test_add_bytes_to_bits_overflow() { add_bytes_to_bits(core::u64::MAX, 1); } - -// A normal addition - no overflow occurs (fast path) -#[test] -fn test_add_bytes_to_bits_tuple_ok() { - assert!(add_bytes_to_bits_tuple((5, 100), 10) == (5, 180)); -} - -// The low order value overflows into the high order value -#[test] -fn test_add_bytes_to_bits_tuple_ok2() { - assert!(add_bytes_to_bits_tuple((5, core::u64::MAX), 1) == (6, 7)); -} - -// The value to add is too large to be converted into bits without overflowing -// its type -#[test] -fn test_add_bytes_to_bits_tuple_ok3() { - assert!(add_bytes_to_bits_tuple((5, 0), 0x4000000000000001) == (7, 8)); -} - -// A simple failure case - adding 1 to the max value -#[test] -#[should_panic] -fn test_add_bytes_to_bits_tuple_overflow() { - add_bytes_to_bits_tuple((core::u64::MAX, core::u64::MAX), 1); -} - -// The value to add is too large to convert to bytes without overflowing its -// type, but the high order value from this conversion overflows when added to -// the existing high order value -#[test] -#[should_panic] -fn test_add_bytes_to_bits_tuple_overflow2() { - let value: u64 = core::u64::MAX; - add_bytes_to_bits_tuple((value - 1, 0), 0x8000000000000000); -} diff --git a/byte-tools/src/write_single.rs b/byte-tools/src/write_single.rs new file mode 100644 index 00000000..a075ec38 --- /dev/null +++ b/byte-tools/src/write_single.rs @@ -0,0 +1,39 @@ +use core::{mem, ptr}; + +macro_rules! write_single { + ($dst:expr, $n:expr, $size:expr, $which:ident) => ({ + assert!($size == $dst.len()); + unsafe { + let bytes = mem::transmute::<_, [u8; $size]>($n.$which()); + ptr::copy_nonoverlapping((&bytes).as_ptr(), $dst.as_mut_ptr(), $size); + } + }); +} + +/// Write a u32 into a vector, which must be 4 bytes long. The value is written +/// in little-endian format. +#[inline] +pub fn write_u32_le(dst: &mut [u8], n: u32) { + write_single!(dst, n, 4, to_le); +} + +/// Write a u32 into a vector, which must be 4 bytes long. The value is written +/// in big-endian format. +#[inline] +pub fn write_u32_be(dst: &mut [u8], n: u32) { + write_single!(dst, n, 4, to_be); +} + +/// Write a u64 into a vector, which must be 8 bytes long. The value is written +/// in little-endian format. +#[inline] +pub fn write_u64_le(dst: &mut [u8], n: u64) { + write_single!(dst, n, 8, to_le); +} + +/// Write a u64 into a vector, which must be 8 bytes long. The value is written +/// in big-endian format. +#[inline] +pub fn write_u64_be(dst: &mut [u8], n: u64) { + write_single!(dst, n, 8, to_be); +} diff --git a/byte-tools/src/write_slice.rs b/byte-tools/src/write_slice.rs new file mode 100644 index 00000000..03ccc19c --- /dev/null +++ b/byte-tools/src/write_slice.rs @@ -0,0 +1,44 @@ +use core::ptr; + +macro_rules! write_slice { + ($src:expr, $dst:expr, $size:expr, $which:ident) => ({ + assert_eq!($dst.len(), $size*$src.len()); + unsafe { + ptr::copy_nonoverlapping( + $src.as_ptr() as *const u8, + $dst.as_mut_ptr(), + $dst.len()); + } + for v in $dst.iter_mut() { + *v = v.$which(); + } + }); +} + +/// Write a vector of u32s into a vector of bytes. The values are written in +/// little-endian format. +#[inline] +pub fn write_u32v_le(dst: &mut [u8], src: &[u32]) { + write_slice!(src, dst, 4, to_le); +} + +/// Write a vector of u32s into a vector of bytes. The values are written in +/// big-endian format. +#[inline] +pub fn write_u32v_be(dst: &mut [u8], src: &[u32]) { + write_slice!(src, dst, 4, to_be); +} + +/// Write a vector of u64s into a vector of bytes. The values are written in +/// little-endian format. +#[inline] +pub fn write_u64v_le(dst: &mut [u8], src: &[u64]) { + write_slice!(src, dst, 8, to_le); +} + +/// Write a vector of u64s into a vector of bytes. The values are written in +/// little-endian format. +#[inline] +pub fn write_u64v_be(dst: &mut [u8], src: &[u64]) { + write_slice!(src, dst, 8, to_be); +}