1use std::fmt;
7use std::io::Read;
8use std::io::Write;
9use std::io::Seek;
10use std::io::SeekFrom;
11use std::convert::TryInto;
12
13#[derive(PartialEq, Eq, Clone, Copy, Hash)]
15pub struct ChunkId {
16 pub value: [u8; 4]
18}
19
20pub static RIFF_ID: ChunkId = ChunkId { value: [0x52, 0x49, 0x46, 0x46] };
22
23pub static LIST_ID: ChunkId = ChunkId { value: [0x4C, 0x49, 0x53, 0x54] };
25
26pub static SEQT_ID: ChunkId = ChunkId { value: [0x73, 0x65, 0x71, 0x74] };
28
29impl ChunkId {
30 pub fn as_str(&self) -> &str {
40 std::str::from_utf8(&self.value).unwrap()
41 }
42
43 pub fn new(s: &str) -> Result<ChunkId, &str> {
62 let bytes = s.as_bytes();
63 if bytes.len() != 4 {
64 Err("Invalid length")
65 } else {
66 let mut a: [u8; 4] = Default::default();
67 a.copy_from_slice(&bytes[..]);
68 Ok(ChunkId { value: a })
69 }
70 }
71}
72
73impl fmt::Display for ChunkId {
74 fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
75 write!(f, "'{}'", self.as_str())
76 }
77}
78
79impl fmt::Debug for ChunkId {
80 fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
81 write!(f, "{}", self)
82 }
83}
84
85#[derive(PartialEq, Debug)]
86pub enum ChunkContents {
87 Data(ChunkId, Vec<u8>),
88 Children(ChunkId, ChunkId, Vec<ChunkContents>),
89 ChildrenNoType(ChunkId, Vec<ChunkContents>)
90}
91
92impl ChunkContents {
93 pub fn write<T>(&self, writer: &mut T) -> std::io::Result<u64>
94 where T: Seek + Write {
95 match &self {
96 &ChunkContents::Data(id, data) => {
97 if data.len() as u64 > u32::MAX as u64 {
98 use std::io::{Error, ErrorKind};
99 return Err(Error::new(ErrorKind::InvalidData, "Data too big"));
100 }
101
102 let len = data.len() as u32;
103 writer.write_all(&id.value)?;
104 writer.write_all(&len.to_le_bytes())?;
105 writer.write_all(&data)?;
106 if len % 2 != 0 {
107 let single_byte: [u8; 1] = [0];
108 writer.write_all(&single_byte)?;
109 }
110 Ok((8 + len + (len % 2)).into())
111 }
112 &ChunkContents::Children(id, chunk_type, children) => {
113 writer.write_all(&id.value)?;
114 let len_pos = writer.seek(SeekFrom::Current(0))?;
115 let zeros: [u8; 4] = [0, 0, 0, 0];
116 writer.write_all(&zeros)?;
117 writer.write_all(&chunk_type.value)?;
118 let mut total_len: u64 = 4;
119 for child in children {
120 total_len = total_len + child.write(writer)?;
121 }
122
123 if total_len > u32::MAX as u64 {
124 use std::io::{Error, ErrorKind};
125 return Err(Error::new(ErrorKind::InvalidData, "Data too big"));
126 }
127
128 let end_pos = writer.seek(SeekFrom::Current(0))?;
129 writer.seek(SeekFrom::Start(len_pos))?;
130 writer.write_all(&(total_len as u32).to_le_bytes())?;
131 writer.seek(SeekFrom::Start(end_pos))?;
132
133 Ok((8 + total_len + (total_len % 2)).into())
134 }
135 &ChunkContents::ChildrenNoType(id, children) => {
136 writer.write_all(&id.value)?;
137 let len_pos = writer.seek(SeekFrom::Current(0))?;
138 let zeros: [u8; 4] = [0, 0, 0, 0];
139 writer.write_all(&zeros)?;
140 let mut total_len: u64 = 0;
141 for child in children {
142 total_len = total_len + child.write(writer)?;
143 }
144
145 if total_len > u32::MAX as u64 {
146 use std::io::{Error, ErrorKind};
147 return Err(Error::new(ErrorKind::InvalidData, "Data too big"));
148 }
149
150 let end_pos = writer.seek(SeekFrom::Current(0))?;
151 writer.seek(SeekFrom::Start(len_pos))?;
152 writer.write_all(&(total_len as u32).to_le_bytes())?;
153 writer.seek(SeekFrom::Start(end_pos))?;
154
155 Ok((8 + total_len + (total_len % 2)).into())
156 }
157 }
158 }
159}
160
161#[derive(PartialEq, Eq, Debug)]
163pub struct Chunk {
164 pos: u64,
165 id: ChunkId,
166 len: u32,
167}
168
169pub struct Iter<'a, T>
171 where T: Seek + Read {
172 end: u64,
173 cur: u64,
174 stream: &'a mut T
175}
176
177impl<'a, T> Iterator for Iter<'a, T>
178 where T: Seek + Read {
179 type Item = std::io::Result<Chunk>;
180
181 fn next(&mut self) -> Option<Self::Item> {
182 if self.cur >= self.end {
183 return None
184 }
185
186 let chunk = match Chunk::read(&mut self.stream, self.cur) {
187 Ok(chunk) => chunk,
188 Err(err) => return Some(Err(err)),
189 };
190 let len = chunk.len() as u64;
191 self.cur = self.cur + len + 8 + (len % 2);
192 Some(Ok(chunk))
193 }
194}
195
196impl Chunk {
197 pub fn id(&self) -> ChunkId {
199 self.id.clone()
200 }
201
202 pub fn len(&self) -> u32 {
204 self.len
205 }
206
207 pub fn offset(&self) -> u64 {
209 self.pos
210 }
211
212 pub fn read_type<T>(&self, stream: &mut T) -> std::io::Result<ChunkId>
216 where T: Read + Seek {
217 stream.seek(SeekFrom::Start(self.pos + 8))?;
218
219 let mut fourcc : [u8; 4] = [0; 4];
220 stream.read_exact(&mut fourcc)?;
221
222 Ok(ChunkId { value: fourcc })
223 }
224
225 pub fn read<T>(stream: &mut T, pos: u64) -> std::io::Result<Chunk>
227 where T: Read + Seek {
228 stream.seek(SeekFrom::Start(pos))?;
229
230 let mut fourcc : [u8; 4] = [0; 4];
231 stream.read_exact(&mut fourcc)?;
232
233 let mut len : [u8; 4] = [0; 4];
234 stream.read_exact(&mut len)?;
235
236 Ok(Chunk {
237 pos: pos,
238 id: ChunkId { value: fourcc },
239 len: u32::from_le_bytes(len)
240 })
241 }
242
243 pub fn read_contents<T>(&self, stream: &mut T) -> std::io::Result<Vec<u8>>
245 where T: Read + Seek {
246 stream.seek(SeekFrom::Start(self.pos + 8))?;
247
248 let mut data: Vec<u8> = vec![0; self.len.try_into().unwrap()];
249 stream.read_exact(&mut data)?;
250
251 Ok(data)
252 }
253
254 pub fn iter<'a, T>(&self, stream: &'a mut T) -> Iter<'a, T>
259 where T: Seek + Read {
260 Iter {
261 cur: self.pos + 12,
262 end: self.pos + 4 + (self.len as u64),
263 stream: stream
264 }
265 }
266
267 pub fn iter_no_type<'a, T>(&self, stream: &'a mut T) -> Iter<'a, T>
271 where T: Seek + Read {
272 Iter {
273 cur: self.pos + 8,
274 end: self.pos + 4 + (self.len as u64),
275 stream: stream
276 }
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283
284 #[test]
285 fn chunkid_from_str() {
286 assert_eq!(ChunkId::new("RIFF").unwrap(), RIFF_ID);
287 assert_eq!(ChunkId::new("LIST").unwrap(), LIST_ID);
288 assert_eq!(ChunkId::new("seqt").unwrap(), SEQT_ID);
289
290 assert_eq!(ChunkId::new("123 ").unwrap(),
291 ChunkId { value: [0x31, 0x32, 0x33, 0x20] });
292
293 assert_eq!(ChunkId::new("123"), Err("Invalid length"));
294 assert_eq!(ChunkId::new("12345"), Err("Invalid length"));
295 }
296
297 #[test]
298 fn chunkid_to_str() {
299 assert_eq!(RIFF_ID.as_str(), "RIFF");
300 assert_eq!(LIST_ID.as_str(), "LIST");
301 assert_eq!(SEQT_ID.as_str(), "seqt");
302 assert_eq!(ChunkId::new("123 ").unwrap().as_str(), "123 ");
303 }
304
305 #[test]
306 fn chunkid_format() {
307 assert_eq!(format!("{}", RIFF_ID), "'RIFF'");
308 assert_eq!(format!("{}", LIST_ID), "'LIST'");
309 assert_eq!(format!("{}", SEQT_ID), "'seqt'");
310
311 assert_eq!(format!("{:?}", RIFF_ID), "'RIFF'");
312 assert_eq!(format!("{:?}", LIST_ID), "'LIST'");
313 assert_eq!(format!("{:?}", SEQT_ID), "'seqt'");
314 }
315}