use {Error, Parcel, Settings};
use hint;
use std::io::prelude::*;
use std::{marker, mem};
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Aligned<T, ToSizeOfType>
where T: Parcel,
ToSizeOfType: Sized {
pub value: T,
_phantom: marker::PhantomData<ToSizeOfType>,
}
impl<T, ToSizeOfType> Aligned<T, ToSizeOfType>
where T: Parcel,
ToSizeOfType: Sized {
pub fn new(value: T) -> Self {
Aligned { value, _phantom: marker::PhantomData }
}
pub fn align_to_bytes() -> usize {
mem::size_of::<ToSizeOfType>()
}
}
impl<T, ToSizeOfType> Parcel for Aligned<T, ToSizeOfType>
where T: Parcel,
ToSizeOfType: Sized {
const TYPE_NAME: &'static str = "Aligned";
fn read_field(read: &mut Read,
settings: &Settings,
hints: &mut hint::Hints) -> Result<Self, Error> {
let inner_value = T::read_field(read, settings, hints)?;
let value_size = inner_value.raw_bytes_field(settings, hints).unwrap().len();
let padding_size = calculate_padding(Self::align_to_bytes(), value_size);
for _ in 0..padding_size {
let padding_byte = u8::read(read, settings)?;
assert_eq!(0x00, padding_byte, "padding bytes should be zero");
}
Ok(Aligned { value: inner_value, _phantom: marker::PhantomData })
}
fn write_field(&self,
write: &mut Write,
settings: &Settings,
hints: &mut hint::Hints) -> Result<(), Error> {
let unaligned_bytes = self.value.raw_bytes_field(settings, hints)?;
let aligned_bytes = align_to(Self::align_to_bytes(), 0x00, unaligned_bytes);
write.write(&aligned_bytes)?;
Ok(())
}
}
impl<T, ToSizeOfType> From<T> for Aligned<T, ToSizeOfType>
where T: Parcel,
ToSizeOfType: Sized {
fn from(value: T) -> Self {
Aligned { value, _phantom: marker::PhantomData }
}
}
fn align_to(align_to: usize,
padding_byte: u8,
bytes: Vec<u8>) -> Vec<u8> {
let extra_padding_needed = calculate_padding(align_to, bytes.len());
let extra_padding = (0..).into_iter().take(extra_padding_needed).map(|_| padding_byte);
let bytes: Vec<_> = bytes.into_iter().chain(extra_padding).collect();
assert_eq!(0, bytes.len() % align_to,
"failed to align");
bytes
}
fn calculate_padding(align_to: usize,
unaligned_size: usize) -> usize {
(align_to - (unaligned_size % align_to)) % align_to
}
#[cfg(test)]
mod test {
use super::*;
mod alignment_calculations {
use super::*;
#[test]
fn test_aligning_when_none_needed() {
assert_eq!(vec![1, 2], align_to(1, 0x00, vec![1, 2]));
assert_eq!(vec![1, 2], align_to(2, 0x00, vec![1, 2]));
}
#[test]
fn test_align_to_3_with_size_2() {
assert_eq!(vec![1, 2, 0], align_to(3, 0x00, vec![1, 2]));
}
#[test]
fn test_align_to_4_with_size_2() {
assert_eq!(vec![1, 2, 0xff, 0xff], align_to(4, 0xff, vec![1, 2]));
}
#[test]
fn test_align_to_3_with_size_5() {
assert_eq!(vec![1, 2, 3, 4, 5, 0], align_to(3, 0x00, vec![1, 2, 3, 4, 5]));
}
#[test]
fn test_align_to_4_with_size_97() {
let original = [1; 97];
let aligned = align_to(4, 0x00, original.to_vec());
let count_ones = aligned.iter().filter(|&&i| i == 1).count();
let count_zeros = aligned.iter().filter(|&&i| i == 0).count();
assert_eq!(97, count_ones);
assert_eq!(3, count_zeros);
}
}
}