1use crate::{hint, Error, Parcel, Settings};
2use std::io::prelude::*;
3use std::{marker, mem};
4
5#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
58pub struct Aligned<T, ToSizeOfType>
59 where T: Parcel,
60 ToSizeOfType: Sized {
61 pub value: T,
63 _phantom: marker::PhantomData<ToSizeOfType>,
64}
65
66impl<T, ToSizeOfType> Aligned<T, ToSizeOfType>
67 where T: Parcel,
68 ToSizeOfType: Sized {
69 pub fn new(value: T) -> Self {
71 Aligned { value, _phantom: marker::PhantomData }
72 }
73
74 pub fn align_to_bytes() -> usize {
76 mem::size_of::<ToSizeOfType>()
77 }
78}
79
80impl<T, ToSizeOfType> Parcel for Aligned<T, ToSizeOfType>
81 where T: Parcel,
82 ToSizeOfType: Sized {
83 const TYPE_NAME: &'static str = "Aligned";
84
85 fn read_field(read: &mut dyn Read,
86 settings: &Settings,
87 hints: &mut hint::Hints) -> Result<Self, Error> {
88 let inner_value = T::read_field(read, settings, hints)?;
89 let value_size = inner_value.raw_bytes_field(settings, hints).unwrap().len();
90 let padding_size = calculate_padding(Self::align_to_bytes(), value_size);
91
92 for _ in 0..padding_size {
93 let padding_byte = u8::read(read, settings)?;
94
95 assert_eq!(0x00, padding_byte, "padding bytes should be zero");
97 }
98
99 Ok(Aligned { value: inner_value, _phantom: marker::PhantomData })
100 }
101
102 fn write_field(&self,
103 write: &mut dyn Write,
104 settings: &Settings,
105 hints: &mut hint::Hints) -> Result<(), Error> {
106 let unaligned_bytes = self.value.raw_bytes_field(settings, hints)?;
107 let aligned_bytes = align_to(Self::align_to_bytes(), 0x00, unaligned_bytes);
108 write.write(&aligned_bytes)?;
109 Ok(())
110 }
111}
112
113impl<T, ToSizeOfType> From<T> for Aligned<T, ToSizeOfType>
114 where T: Parcel,
115 ToSizeOfType: Sized {
116 fn from(value: T) -> Self {
117 Aligned { value, _phantom: marker::PhantomData }
118 }
119}
120
121fn align_to(align_to: usize,
123 padding_byte: u8,
124 bytes: Vec<u8>) -> Vec<u8> {
125 let extra_padding_needed = calculate_padding(align_to, bytes.len());
128
129 let extra_padding = (0..).into_iter().take(extra_padding_needed).map(|_| padding_byte);
130
131 let bytes: Vec<_> = bytes.into_iter().chain(extra_padding).collect();
132 assert_eq!(0, bytes.len() % align_to,
133 "failed to align");
134 bytes
135}
136
137fn calculate_padding(align_to: usize,
138 unaligned_size: usize) -> usize {
139 (align_to - (unaligned_size % align_to)) % align_to
142}
143
144#[cfg(test)]
145mod test {
146 use super::*;
147
148 mod alignment_calculations {
149 use super::*;
150
151 #[test]
152 fn test_aligning_when_none_needed() {
153 assert_eq!(vec![1, 2], align_to(1, 0x00, vec![1, 2]));
154 assert_eq!(vec![1, 2], align_to(2, 0x00, vec![1, 2]));
155 }
156
157 #[test]
158 fn test_align_to_3_with_size_2() {
159 assert_eq!(vec![1, 2, 0], align_to(3, 0x00, vec![1, 2]));
160 }
161
162 #[test]
163 fn test_align_to_4_with_size_2() {
164 assert_eq!(vec![1, 2, 0xff, 0xff], align_to(4, 0xff, vec![1, 2]));
165 }
166
167 #[test]
168 fn test_align_to_3_with_size_5() {
169 assert_eq!(vec![1, 2, 3, 4, 5, 0], align_to(3, 0x00, vec![1, 2, 3, 4, 5]));
170 }
171
172 #[test]
173 fn test_align_to_4_with_size_97() {
174 let original = [1; 97];
175 let aligned = align_to(4, 0x00, original.to_vec());
176
177 let count_ones = aligned.iter().filter(|&&i| i == 1).count();
178 let count_zeros = aligned.iter().filter(|&&i| i == 0).count();
179
180 assert_eq!(97, count_ones);
181 assert_eq!(3, count_zeros);
182 }
183 }
184}
185