1use std::io::{Error, ErrorKind, Result, Write};
6
7use crate::types::{TypeID, UNALIGNED_CHUNKS};
8
9pub enum Chunk {
10 Envelope {
11 envelope_id: TypeID,
12 id: TypeID,
13 chunks: Vec<Chunk>,
14 little_endian: bool,
15 aligned: bool
16 },
17 Data {
18 id: TypeID,
19 data: Vec<u8>,
20 little_endian: bool
21 },
22}
23impl Chunk {
24 pub fn from_data(data: &[u8], little_endian: Option<bool>) -> Result<Self> {
25 Chunk::from_data_full(data, 0, data.len(), little_endian, None)
26 }
27
28 pub fn from_data_with_size(data: &[u8], size_override: usize, little_endian: Option<bool>) -> Result<Self> {
29 Chunk::from_data_full(data, 0, data.len(), little_endian, Some(size_override))
30 }
31
32 pub fn is_little_endian(&self) -> bool {
33 match self {
34 Chunk::Envelope { little_endian, .. } => *little_endian,
35 Chunk::Data { little_endian, .. } => *little_endian
36 }
37 }
38
39 fn from_data_full(data: &[u8], index: usize, last_index: usize, little_endian: Option<bool>,
40 size_override: Option<usize>) -> Result<Self> {
41 if index + 8 > last_index {
42 return Err(Error::new(ErrorKind::InvalidData, "invalid data"));
43 }
44 let mut id = Self::chunk_id(&data, index, little_endian.unwrap_or(false));
45 let little_endian = if let Some(v) = little_endian {
46 v
47 } else {
48 if id.is_envelope() {
49 false
50 } else if id.is_le_envelope() {
51 id = id.reverse(); true
53 } else {
54 return Err(Error::new(ErrorKind::InvalidInput, "data chunk needs 'little_endian' setting"));
55 }
56 };
57
58 let size = size_override.unwrap_or_else(
59 || Self::chunk_size(&data, index+4, little_endian));
60 if index + 8 + size > last_index {
62 return Err(Error::new(ErrorKind::InvalidData, "invalid data"));
63 }
64 if id.is_envelope() {
65 let aligned = UNALIGNED_CHUNKS.contains(&&id);
66 if size < 4 {
67 return Err(Error::new(ErrorKind::InvalidData, "invalid data"));
68 }
69 let data_id = Self::chunk_id(&data, index+8, little_endian);
70 let mut i = index + 12;
71 let mut chunks = Vec::new();
73 while i < index + 8 + size {
74 let chunk = Self::from_data_full(&data, i, index+8+size, Some(little_endian), None)?;
75 i += chunk.size();
76 if aligned && i % 2 != 0 {
77 i += 1;
78 }
79 chunks.push(chunk);
80 }
81 Ok(Chunk::Envelope{ envelope_id: id, id: data_id, chunks, little_endian, aligned })
82 } else {
83 Ok(Chunk::Data{ id, data: data[index+8..index+8+size].to_vec(), little_endian })
84 }
85 }
86
87 fn chunk_id(data: &[u8], index: usize, little_endian: bool) -> TypeID {
88 let ptr = data[index..].as_ptr() as *const [u8; 4];
90 let arr = unsafe { &*ptr };
91
92 TypeID::from_data(arr, little_endian)
93 }
94
95 fn chunk_size(data: &[u8], index: usize, little_endian: bool) -> usize {
96 let ptr = data[index..].as_ptr() as *const [u8; 4];
98 let arr = unsafe { &*ptr };
99 if little_endian {
100 u32::from_le_bytes(*arr) as usize
101 } else {
102 u32::from_be_bytes(*arr) as usize
103 }
104 }
105
106 pub fn create(envelope_id: TypeID, id: TypeID, little_endian: bool) -> Self {
107 let aligned = UNALIGNED_CHUNKS.contains(&&id);
108 Chunk::Envelope{ envelope_id, id, chunks: Vec::new(), little_endian, aligned }
109 }
110
111 pub fn append_chunk(&mut self, chunk: Chunk) {
122 if let &mut Chunk::Envelope{ ref mut chunks, .. } = self {
123 chunks.push(chunk);
124 } else {
125 panic!("Cannot add nested chunks to a data chunk");
126 }
127 }
128
129 fn write_type_id<W: Write>(id: &TypeID, w: &mut W, little_endian: bool) -> Result<()> {
130 let mut data = [0u8;4];
131 id.to_data(&mut data, little_endian);
132 w.write_all(&data)
133 }
134
135 fn write_u32<W: Write>(value: u32, w: &mut W, little_endian: bool) -> Result<()> {
136 let data = if little_endian {
137 value.to_le_bytes()
138 } else {
139 value.to_be_bytes()
140 };
141 w.write_all(&data)
142 }
143
144 pub fn write<W: Write>(&self, w: &mut W) -> Result<()> {
145 match self {
146 Chunk::Envelope{ envelope_id, id, chunks, little_endian, aligned } => {
147 Self::write_type_id(envelope_id, w, *little_endian)?;
148 let size = self.size() - 8;
149 Self::write_u32(size as u32, w, *little_endian)?;
150 Self::write_type_id(id, w, *little_endian)?;
151 for chunk in chunks {
152 chunk.write(w)?;
153 }
154 if *aligned && size % 2 != 0 {
155 w.write_all(&[0u8])?;
156 }
157 },
158 Chunk::Data{ id, data, little_endian } => {
159 Self::write_type_id(id, w, *little_endian)?;
160 let size = self.size() - 8;
161 Self::write_u32(size as u32, w, *little_endian)?;
162 w.write_all(data)?;
163 if size % 2 != 0 {
166 w.write_all(&[0u8])?;
167 }
168 }
169 }
170 Ok(())
171 }
172
173 fn size(&self) -> usize {
174 match self {
175 &Chunk::Envelope{ ref chunks, aligned, .. } => {
176 let mut size = 12;
177 for chunk in chunks {
178 size += chunk.size();
179 if aligned && size % 2 != 0 {
180 size += 1;
181 }
182 }
183 size
184 },
185 &Chunk::Data{ id:_, ref data, little_endian:_ } => 8 + data.len(),
186 }
187 }
188
189 pub fn has_envelope_type(&self, envelope_type_id: TypeID, type_id: TypeID) -> bool {
190 match self {
191 &Chunk::Envelope{ envelope_id, id, .. } =>
192 envelope_type_id == envelope_id && type_id == id,
193 _ => false,
194 }
195 }
196
197 pub fn has_data_type(&self, type_id: TypeID) -> bool {
198 match self {
199 &Chunk::Data{ id, .. } => type_id == id,
200 _ => false,
201 }
202 }
203
204
205 pub fn data_chunks(&self) -> Vec<(TypeID,&Vec<u8>)> {
206 let mut vec = Vec::new();
207 match self {
208 &Chunk::Envelope{ ref chunks, .. } => {
209 for chunk in chunks {
210 match chunk {
211 &Chunk::Data{ id, ref data, little_endian:_ } => vec.push((id, data)),
212 _ => (),
213 }
214 }
215 },
216 _ => (),
217 }
218 vec
219 }
220
221 pub fn all_chunks(&self) -> Vec<(TypeID,&Chunk)> {
222 let mut vec = Vec::new();
223 match self {
224 &Chunk::Envelope{ ref chunks, .. } => {
225 for chunk in chunks {
226 match chunk {
227 &Chunk::Data{ id, .. } => vec.push((id, chunk)),
228 &Chunk::Envelope{ id, .. } => vec.push((id, chunk)),
229 }
230 }
231 },
232 _ => (),
233 }
234 vec
235 }
236}
237
238impl std::fmt::Debug for Chunk {
239 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
240 match self {
241 Chunk::Envelope { envelope_id, id, chunks, little_endian, aligned } => {
242 f.debug_struct("Chunk::Envelope")
243 .field("envelope", &envelope_id)
244 .field("id", &id)
245 .field("chunks.len", &chunks.len())
246 .field("little_endian", &little_endian)
247 .field("aligned", &aligned)
248 .finish()
249 }
250 Chunk::Data { id, data, little_endian } => {
251 f.debug_struct("Chunk::Data")
252 .field("id", &id)
253 .field("data.len", &data.len())
254 .field("little_endian", &little_endian)
255 .finish()
256 }
257 }
258 }
259}