1mod crc;
2mod read;
3mod traits;
4mod types;
5mod write;
6
7use self::crc::Crc32;
8pub(crate) use self::{read::*, write::*};
9pub use self::{traits::*, types::*};
10use std::{
11 borrow::Cow,
12 io::{self, prelude::*},
13 mem,
14};
15
16pub const MIN_CHUNK_BYTES_SIZE: usize =
19 mem::size_of::<u32>() + mem::size_of::<ChunkType>() + mem::size_of::<u32>();
20
21pub(crate) const MAX_CHUNK_DATA_LENGTH: usize = u32::MAX as usize;
23
24pub(crate) trait ChunkExt: Chunk {
25 #[inline]
27 fn bytes_len(&self) -> usize {
28 MIN_CHUNK_BYTES_SIZE + self.data().len()
29 }
30
31 #[inline]
33 fn is_stream_chunk(&self) -> bool {
34 self.ty() == ChunkType::FDAT || self.ty() == ChunkType::SDAT
35 }
36
37 #[inline]
38 fn write_chunk_in<W: Write>(&self, writer: &mut W) -> io::Result<usize> {
39 writer.write_all(&self.length().to_be_bytes())?;
40 writer.write_all(&self.ty().0)?;
41 writer.write_all(self.data())?;
42 writer.write_all(&self.crc().to_be_bytes())?;
43 Ok(self.bytes_len())
44 }
45
46 #[allow(dead_code)]
52 #[inline]
53 fn to_bytes(&self) -> Vec<u8> {
54 let mut vec = Vec::with_capacity(self.bytes_len());
55 vec.extend_from_slice(&self.length().to_be_bytes());
56 vec.extend_from_slice(&self.ty().0);
57 vec.extend_from_slice(self.data());
58 vec.extend_from_slice(&self.crc().to_be_bytes());
59 vec
60 }
61}
62
63impl<T> ChunkExt for T where T: Chunk {}
64
65#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
67pub struct RawChunk<D = Vec<u8>> {
68 pub(crate) length: u32,
69 pub(crate) ty: ChunkType,
70 pub(crate) data: D,
71 pub(crate) crc: u32,
72}
73
74impl<D> From<(ChunkType, D)> for RawChunk<D>
75where
76 (ChunkType, D): Chunk,
77{
78 #[inline]
79 fn from(value: (ChunkType, D)) -> Self {
80 Self {
81 length: value.length(),
82 crc: value.crc(),
83 ty: value.0,
84 data: value.1,
85 }
86 }
87}
88
89impl<'d> RawChunk<&'d [u8]> {
90 pub(crate) fn from_slice(ty: ChunkType, data: &'d [u8]) -> Self {
91 let chunk = (ty, data);
92 Self {
93 length: chunk.length(),
94 crc: chunk.crc(),
95 ty,
96 data,
97 }
98 }
99}
100
101impl<'a> From<RawChunk<Cow<'a, [u8]>>> for RawChunk<Vec<u8>> {
102 #[inline]
103 fn from(value: RawChunk<Cow<'a, [u8]>>) -> Self {
104 Self {
105 length: value.length,
106 ty: value.ty,
107 data: value.data.into(),
108 crc: value.crc,
109 }
110 }
111}
112
113impl<'a> From<RawChunk<&'a [u8]>> for RawChunk<Vec<u8>> {
114 #[inline]
115 fn from(value: RawChunk<&'a [u8]>) -> Self {
116 Self {
117 length: value.length,
118 ty: value.ty,
119 data: value.data.into(),
120 crc: value.crc,
121 }
122 }
123}
124
125impl From<RawChunk<Vec<u8>>> for RawChunk<Cow<'_, [u8]>> {
126 #[inline]
127 fn from(value: RawChunk<Vec<u8>>) -> Self {
128 Self {
129 length: value.length,
130 ty: value.ty,
131 data: Cow::Owned(value.data),
132 crc: value.crc,
133 }
134 }
135}
136
137impl<'a> From<RawChunk<&'a [u8]>> for RawChunk<Cow<'a, [u8]>> {
138 #[inline]
139 fn from(value: RawChunk<&'a [u8]>) -> Self {
140 Self {
141 length: value.length,
142 ty: value.ty,
143 data: Cow::Borrowed(value.data),
144 crc: value.crc,
145 }
146 }
147}
148
149impl<D> RawChunk<D>
150where
151 Self: Chunk,
152{
153 #[inline]
154 pub(crate) fn as_ref(&self) -> RawChunk<&[u8]> {
155 RawChunk {
156 length: self.length,
157 ty: self.ty,
158 data: self.data(),
159 crc: self.crc,
160 }
161 }
162}
163
164impl<T: AsRef<[u8]>> Chunk for RawChunk<T> {
165 #[inline]
166 fn length(&self) -> u32 {
167 self.length
168 }
169
170 #[inline]
171 fn ty(&self) -> ChunkType {
172 self.ty
173 }
174
175 #[inline]
176 fn data(&self) -> &[u8] {
177 self.data.as_ref()
178 }
179
180 #[inline]
181 fn crc(&self) -> u32 {
182 self.crc
183 }
184}
185
186impl RawChunk {
187 #[inline]
202 pub fn from_data<T: Into<Vec<u8>>>(ty: ChunkType, data: T) -> Self {
203 #[inline]
204 fn inner(ty: ChunkType, data: Vec<u8>) -> RawChunk {
205 let chunk = (ty, &data[..]);
206 RawChunk {
207 length: chunk.length(),
208 crc: chunk.crc(),
209 ty,
210 data,
211 }
212 }
213 inner(ty, data.into())
214 }
215}
216
217impl<T: AsRef<[u8]>> Chunk for (ChunkType, T) {
218 #[inline]
219 fn ty(&self) -> ChunkType {
220 self.0
221 }
222
223 #[inline]
224 fn data(&self) -> &[u8] {
225 self.1.as_ref()
226 }
227}
228
229impl<T: Chunk> Chunk for &T {
230 #[inline]
231 fn ty(&self) -> ChunkType {
232 T::ty(*self)
233 }
234
235 #[inline]
236 fn data(&self) -> &[u8] {
237 T::data(*self)
238 }
239}
240
241impl<T: Chunk> Chunk for &mut T {
242 #[inline]
243 fn ty(&self) -> ChunkType {
244 T::ty(*self)
245 }
246
247 #[inline]
248 fn data(&self) -> &[u8] {
249 T::data(*self)
250 }
251}
252
253#[inline]
254pub(crate) fn chunk_data_split(
255 ty: ChunkType,
256 data: &[u8],
257 mid: usize,
258) -> (RawChunk<&[u8]>, Option<RawChunk<&[u8]>>) {
259 if let Some((first, last)) = data.split_at_checked(mid) {
260 if last.is_empty() {
261 (RawChunk::from_slice(ty, first), None)
262 } else {
263 (
264 RawChunk::from_slice(ty, first),
265 Some(RawChunk::from_slice(ty, last)),
266 )
267 }
268 } else {
269 (RawChunk::from_slice(ty, data), None)
270 }
271}
272
273#[inline]
299pub fn read_as_chunks<R: Read>(
300 mut archive: R,
301) -> io::Result<impl Iterator<Item = io::Result<impl Chunk>>> {
302 struct Chunks<R> {
303 reader: ChunkReader<R>,
304 eoa: bool,
305 }
306 impl<R: Read> Iterator for Chunks<R> {
307 type Item = io::Result<RawChunk>;
308 #[inline]
309 fn next(&mut self) -> Option<Self::Item> {
310 if self.eoa {
311 return None;
312 }
313 Some(self.reader.read_chunk().inspect(|chunk| {
314 self.eoa = chunk.ty() == ChunkType::AEND;
315 }))
316 }
317 }
318 crate::archive::read_pna_header(&mut archive)?;
319
320 Ok(Chunks {
321 reader: ChunkReader::from(archive),
322 eoa: false,
323 })
324}
325
326#[inline]
352pub fn read_chunks_from_slice(
353 archive: &[u8],
354) -> io::Result<impl Iterator<Item = io::Result<impl Chunk + '_>>> {
355 struct Chunks<'a> {
356 reader: &'a [u8],
357 eoa: bool,
358 }
359 impl<'a> Iterator for Chunks<'a> {
360 type Item = io::Result<RawChunk<&'a [u8]>>;
361 #[inline]
362 fn next(&mut self) -> Option<Self::Item> {
363 if self.eoa {
364 return None;
365 }
366 Some(read_chunk_from_slice(self.reader).map(|(chunk, bytes)| {
367 self.eoa = chunk.ty() == ChunkType::AEND;
368 self.reader = bytes;
369 chunk
370 }))
371 }
372 }
373 let archive = crate::archive::read_header_from_slice(archive)?;
374
375 Ok(Chunks {
376 reader: archive,
377 eoa: false,
378 })
379}
380
381#[cfg(test)]
382mod tests {
383 use super::*;
384 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
385 use wasm_bindgen_test::wasm_bindgen_test as test;
386
387 #[test]
388 fn chunk_trait_bounds() {
389 fn check_impl<T: Chunk>() {}
390 check_impl::<RawChunk<Vec<u8>>>();
391 check_impl::<RawChunk<Cow<[u8]>>>();
392 check_impl::<RawChunk<&[u8]>>();
393 check_impl::<RawChunk<[u8; 1]>>();
394 }
395
396 #[test]
397 fn to_bytes() {
398 let data = vec![0xAA, 0xBB, 0xCC, 0xDD];
399 let chunk = RawChunk::from_data(ChunkType::FDAT, data);
400
401 let bytes = chunk.to_bytes();
402
403 assert_eq!(
404 bytes,
405 vec![
406 0x00, 0x00, 0x00, 0x04, 0x46, 0x44, 0x41, 0x54, 0xAA, 0xBB, 0xCC, 0xDD, 0x47, 0xf3, 0x2b, 0x10, ]
411 );
412 }
413
414 #[test]
415 fn data_split_at_zero() {
416 let data = vec![0xAA, 0xBB, 0xCC, 0xDD];
417 let chunk = RawChunk::from_data(ChunkType::FDAT, data);
418 assert_eq!(
419 chunk_data_split(chunk.ty, chunk.data(), 0),
420 (
421 RawChunk::from_slice(ChunkType::FDAT, &[]),
422 Some(RawChunk::from_slice(
423 ChunkType::FDAT,
424 &[0xAA, 0xBB, 0xCC, 0xDD]
425 )),
426 )
427 )
428 }
429
430 #[test]
431 fn data_split_at_middle() {
432 let data = vec![0xAA, 0xBB, 0xCC, 0xDD];
433 let chunk = RawChunk::from_data(ChunkType::FDAT, data);
434 assert_eq!(
435 chunk_data_split(chunk.ty, chunk.data(), 2),
436 (
437 RawChunk::from_slice(ChunkType::FDAT, &[0xAA, 0xBB]),
438 Some(RawChunk::from_slice(ChunkType::FDAT, &[0xCC, 0xDD])),
439 )
440 )
441 }
442
443 #[test]
444 fn data_split_at_just() {
445 let data = vec![0xAA, 0xBB, 0xCC, 0xDD];
446 let chunk = RawChunk::from_data(ChunkType::FDAT, data);
447 assert_eq!(
448 chunk_data_split(chunk.ty, chunk.data(), 4),
449 (
450 RawChunk::from_slice(ChunkType::FDAT, &[0xAA, 0xBB, 0xCC, 0xDD]),
451 None,
452 )
453 )
454 }
455
456 #[test]
457 fn data_split_at_over() {
458 let data = vec![0xAA, 0xBB, 0xCC, 0xDD];
459 let chunk = RawChunk::from_data(ChunkType::FDAT, data);
460 assert_eq!(
461 chunk_data_split(chunk.ty, chunk.data(), 5),
462 (
463 RawChunk::from_slice(ChunkType::FDAT, &[0xAA, 0xBB, 0xCC, 0xDD]),
464 None,
465 )
466 )
467 }
468}