1use std::io::Read as _;
48
49use ruzstd::decoding::errors::FrameDecoderError;
50use ruzstd::decoding::{BlockDecodingStrategy, FrameDecoder};
51
52use super::{u64_from_index, DecodeContext, View};
53use crate::CodecError;
54
55const BLOCK_BYTES: u64 = 128 * 1024;
56const MAX_DECODER_WINDOW: u64 = 1_u64 << if usize::BITS == 32 { 30 } else { 31 };
59const OUTPUT_STEP_BYTES: usize = 16 * 1024;
60const FRAME_MAGIC: u32 = 0xfd2f_b528;
61
62pub struct ZstdDecoder<'ctx, 'input> {
64 frame: Option<ActiveFrame<'input>>,
65 _ctx: std::marker::PhantomData<&'ctx DecodeContext<'input>>,
67 input: View<'input>,
69 finished: bool,
70}
71
72struct ActiveFrame<'input> {
74 decoder: FrameDecoder,
75 source: &'input [u8],
78 window: u64,
79 block_maximum: u64,
80 content_size: Option<u64>,
81 produced: u64,
82 buffered: Option<u64>,
84}
85
86struct Inspected {
88 size: usize,
90 data: Option<FrameFacts>,
92}
93
94#[derive(Clone, Copy)]
95struct FrameFacts {
96 window: u64,
98 content_size: Option<u64>,
99}
100
101impl<'input> DecodeContext<'input> {
102 pub fn open_zstd<'ctx>(
109 &'ctx self,
110 source: View<'input>,
111 ) -> Result<ZstdDecoder<'ctx, 'input>, CodecError> {
112 let mut offset = 0;
113 let allowance = self.per_expand_allowance();
114 while offset < source.window().len() {
115 let frame = source
116 .child(source.start() + offset, source.end())
117 .ok_or_else(|| {
118 CodecError::Malformed("Zstandard frame offset escapes input".into())
119 })?;
120 if let Some(facts) = frame_header(frame)?.1 {
121 let buffered = facts
122 .content_size
123 .map_or(facts.window, |size| size.min(facts.window));
124 if buffered > allowance {
125 return Err(CodecError::NotImplemented(format!(
126 "Zstandard window of {buffered} bytes exceeds the \
127 {allowance}-byte per-expansion allowance"
128 )));
129 }
130 if facts.window > MAX_DECODER_WINDOW {
131 return Err(window_unsupported(facts.window));
132 }
133 }
134 let Inspected { size, .. } = inspect_frame(frame)?;
135 if size == 0 || size > frame.window().len() {
136 return Err(CodecError::Malformed("invalid Zstandard frame size".into()));
137 }
138 offset += size;
139 }
140 if offset == 0 {
141 return Err(CodecError::Malformed("empty Zstandard stream".into()));
142 }
143 Ok(ZstdDecoder {
146 frame: None,
147 _ctx: std::marker::PhantomData,
148 input: source
149 .child(source.start(), source.end())
150 .ok_or_else(|| CodecError::Malformed("Zstandard input escapes its view".into()))?,
151 finished: false,
152 })
153 }
154}
155
156impl ZstdDecoder<'_, '_> {
157 pub fn read_chunk(&mut self, bytes: &mut [u8]) -> Result<usize, CodecError> {
159 if self.finished || bytes.is_empty() {
160 return Ok(0);
161 }
162 let capacity = bytes.len().min(OUTPUT_STEP_BYTES);
163 loop {
166 if self.frame.is_none() {
167 if self.input.is_empty() {
168 self.finished = true;
169 return Ok(0);
170 }
171 self.open_next_frame()?;
172 }
173 let Some(frame) = self.frame.as_mut() else {
174 continue;
176 };
177 if frame.decoder.can_collect() == 0 && !frame.decoder.is_finished() {
178 frame.decode_block()?;
179 }
180 let produced = frame
181 .decoder
182 .read(&mut bytes[..capacity])
183 .map_err(|error| malformed(&error))?;
184 frame.produced += u64_from_index(produced);
185 if frame
186 .content_size
187 .is_some_and(|declared| frame.produced > declared)
188 {
189 return Err(CodecError::Malformed(
190 "Zstandard frame regenerates more than its declared content size".into(),
191 ));
192 }
193 if frame.decoder.is_finished() && frame.decoder.can_collect() == 0 {
196 self.close_frame()?;
197 if self.input.is_empty() {
198 self.finished = true;
199 }
200 }
201 if produced != 0 || self.finished {
202 return Ok(produced);
203 }
204 }
205 }
206
207 fn open_next_frame(&mut self) -> Result<(), CodecError> {
209 let inspected = inspect_frame(self.input)?;
210 let source = self
211 .input
212 .take(inspected.size)
213 .ok_or_else(|| CodecError::Malformed("truncated Zstandard stream".into()))?;
214 let Some(facts) = inspected.data else {
215 return Ok(());
216 };
217 let mut decoder = FrameDecoder::new();
218 decoder.set_max_window_size(facts.window);
221 let mut frame = ActiveFrame {
222 decoder,
223 source,
224 window: facts.window,
225 block_maximum: facts.window.min(BLOCK_BYTES),
226 content_size: facts.content_size,
227 produced: 0,
228 buffered: Some(0),
229 };
230 frame
231 .decoder
232 .init(&mut frame.source)
233 .map_err(|error| zstd_error(&error, facts.window))?;
234 self.frame = Some(frame);
235 Ok(())
236 }
237
238 fn close_frame(&mut self) -> Result<(), CodecError> {
240 let Some(frame) = self.frame.take() else {
241 return Ok(());
242 };
243 if let (Some(declared), Some(computed)) = (
246 frame.decoder.get_checksum_from_data(),
247 frame.decoder.get_calculated_checksum(),
248 ) {
249 if declared != computed {
250 return Err(CodecError::Malformed(
251 "Zstandard content checksum mismatch".into(),
252 ));
253 }
254 }
255 if frame
256 .content_size
257 .is_some_and(|declared| declared != frame.produced)
258 {
259 return Err(CodecError::Malformed(
260 "Zstandard frame does not regenerate its declared content size".into(),
261 ));
262 }
263 if !frame.source.is_empty() {
267 return Err(CodecError::Malformed(
268 "Zstandard decoder stopped before the end of its frame".into(),
269 ));
270 }
271 Ok(())
272 }
273}
274
275impl ActiveFrame<'_> {
276 fn decode_block(&mut self) -> Result<(), CodecError> {
278 let header = View::u24_le_at(self.source, 0)
279 .ok_or_else(|| CodecError::Malformed("truncated Zstandard stream".into()))?;
280 let kind = (header >> 1) & 3;
281 let declared = u64::from(header >> 3);
282 if declared > self.block_maximum {
285 return Err(CodecError::Malformed(
286 "Zstandard block size exceeds its frame's maximum".into(),
287 ));
288 }
289 self.decoder
290 .decode_blocks(&mut self.source, BlockDecodingStrategy::UptoBlocks(1))
291 .map_err(|error| zstd_error(&error, self.window))?;
292 let collectable = u64_from_index(self.decoder.can_collect());
297 let length = if self.decoder.is_finished() {
298 Some(collectable)
299 } else if collectable != 0 {
300 self.window.checked_add(collectable)
301 } else if kind == 2 {
302 None
303 } else {
304 self.buffered
305 .and_then(|buffered| buffered.checked_add(declared))
306 };
307 if let Some(length) = length {
308 let before = self.buffered.unwrap_or(self.window);
310 if length
311 .checked_sub(before)
312 .is_some_and(|grown| grown > self.block_maximum)
313 {
314 return Err(CodecError::Malformed(
315 "Zstandard block regenerates more than its frame's maximum".into(),
316 ));
317 }
318 }
319 self.buffered = length.map(|length| length.min(self.window));
321 Ok(())
322 }
323}
324
325fn window_unsupported(window: u64) -> CodecError {
326 CodecError::NotImplemented(format!(
327 "Zstandard window of {window} bytes exceeds the {MAX_DECODER_WINDOW}-byte decoder window"
328 ))
329}
330
331fn malformed(error: &dyn std::fmt::Display) -> CodecError {
332 CodecError::malformed(format_args!("invalid Zstandard stream: {error}"))
333}
334
335fn zstd_error(error: &FrameDecoderError, window: u64) -> CodecError {
336 match error {
337 FrameDecoderError::WindowSizeTooBig { .. } => window_unsupported(window),
338 _ => malformed(error),
339 }
340}
341
342fn frame_header(mut frame: View<'_>) -> Result<(View<'_>, Option<FrameFacts>), CodecError> {
345 let magic = frame.req_u32_le()?;
346 if (0x184d_2a50..=0x184d_2a5f).contains(&magic) {
347 return Ok((frame, None));
348 }
349 if magic != FRAME_MAGIC {
350 return Err(CodecError::Malformed("invalid Zstandard magic".into()));
351 }
352 let flags = frame.req_u8()?;
353 if flags & 8 != 0 {
354 return Err(CodecError::Malformed(
355 "reserved Zstandard frame flag".into(),
356 ));
357 }
358 let single_segment = flags & 32 != 0;
359 let window = if single_segment {
360 0
361 } else {
362 let descriptor = frame.req_u8()?;
363 let base = 1_u64 << (10 + u32::from(descriptor >> 3));
364 base + (base / 8) * u64::from(descriptor & 7)
365 };
366 let dictionary_bytes = match flags & 3 {
367 0 => 0,
368 1 => 1,
369 2 => 2,
370 _ => 4,
371 };
372 frame.req_take(dictionary_bytes)?;
373 let content_size = match flags >> 6 {
374 0 if single_segment => Some(u64::from(frame.req_u8()?)),
375 0 => None,
376 1 => Some(u64::from(frame.req_u16_le()?) + 256),
377 2 => Some(u64::from(frame.req_u32_le()?)),
378 _ => Some(frame.req_u64_le()?),
379 };
380 Ok((
381 frame,
382 Some(FrameFacts {
383 window: if single_segment {
384 content_size.unwrap_or(0)
385 } else {
386 window
387 },
388 content_size,
389 }),
390 ))
391}
392
393fn inspect_frame(frame: View<'_>) -> Result<Inspected, CodecError> {
399 let truncated = || CodecError::Malformed("truncated Zstandard frame".into());
400 let start = frame.read_len();
401 let checksum = frame.unread().get(4).is_some_and(|flags| flags & 4 != 0);
402 let (mut body, facts) = frame_header(frame)?;
403 let Some(facts) = facts else {
404 let length = usize::try_from(body.req_u32_le()?).map_err(|_| truncated())?;
405 body.skip(length).ok_or_else(truncated)?;
406 return Ok(Inspected {
407 size: body.read_len() - start,
408 data: None,
409 });
410 };
411 loop {
412 let header = body.take(3).ok_or_else(truncated)?;
413 let header = u32::from(header[0]) | u32::from(header[1]) << 8 | u32::from(header[2]) << 16;
414 let declared = header >> 3;
415 let kind = (header >> 1) & 3;
416 let stored = match kind {
417 1 => 1,
418 3 => {
419 return Err(CodecError::Malformed(
420 "reserved Zstandard block type".into(),
421 ))
422 }
423 _ => usize::try_from(declared).map_err(|_| truncated())?,
424 };
425 body.skip(stored).ok_or_else(truncated)?;
426 if header & 1 != 0 {
427 break;
428 }
429 }
430 if checksum {
431 body.skip(4).ok_or_else(truncated)?;
432 }
433 Ok(Inspected {
434 size: body.read_len() - start,
435 data: Some(facts),
436 })
437}
438
439#[cfg(test)]
440mod tests {
441 use super::{BLOCK_BYTES, MAX_DECODER_WINDOW};
442 use crate::decode::{DecodeArena, DecodeContext, DecodePolicy, ExpandSpec};
443 use crate::CodecError;
444
445 const MAGIC: [u8; 4] = [0x28, 0xb5, 0x2f, 0xfd];
446
447 fn block(last: bool, kind: u32, size: usize, stored: &[u8]) -> Vec<u8> {
453 let header = u32::try_from(size).expect("block size") << 3 | kind << 1 | u32::from(last);
454 let mut out = header.to_le_bytes()[..3].to_vec();
455 out.extend_from_slice(stored);
456 out
457 }
458
459 fn raw(last: bool, payload: &[u8]) -> Vec<u8> {
460 block(last, 0, payload.len(), payload)
461 }
462
463 fn single_segment(payload: &[u8]) -> Vec<u8> {
465 let mut out = MAGIC.to_vec();
466 out.extend_from_slice(&[0x20, u8::try_from(payload.len()).expect("one-byte size")]);
467 out.extend(raw(true, payload));
468 out
469 }
470
471 fn windowed(window_log: u8, blocks: &[Vec<u8>]) -> Vec<u8> {
473 let mut out = MAGIC.to_vec();
474 out.extend_from_slice(&[0x00, (window_log - 10) << 3]);
475 for block in blocks {
476 out.extend_from_slice(block);
477 }
478 out
479 }
480
481 fn decode_with(bytes: &[u8], policy: &DecodePolicy) -> Result<Vec<u8>, CodecError> {
482 let arena = DecodeArena::new();
483 let (ctx, root) = DecodeContext::from_root_bytes(bytes, &arena, policy).expect("root");
484 let mut decoder = ctx.open_zstd(root)?;
485 let mut output = Vec::new();
486 let mut chunk = [0_u8; 16 * 1024];
487 loop {
488 let read = decoder.read_chunk(&mut chunk)?;
489 if read == 0 {
490 return Ok(output);
491 }
492 output.extend_from_slice(&chunk[..read]);
493 }
494 }
495
496 fn decode(bytes: &[u8]) -> Result<Vec<u8>, CodecError> {
497 decode_with(bytes, &DecodePolicy::service())
498 }
499
500 fn assert_malformed(bytes: &[u8], what: &str) {
501 let result = decode(bytes);
502 assert!(
503 matches!(result, Err(CodecError::Malformed(_))),
504 "{what}: {result:?}"
505 );
506 }
507
508 fn assert_cut_short(bytes: &[u8], what: &str) {
511 let result = decode(bytes);
512 assert!(
513 matches!(
514 result,
515 Err(CodecError::Malformed(_) | CodecError::Truncated { .. })
516 ),
517 "{what}: {result:?}"
518 );
519 }
520
521 fn frame_with_window(window: u64) -> Vec<u8> {
524 let exponent = u8::try_from(window.trailing_zeros() - 10).expect("window exponent");
525 vec![0x28, 0xb5, 0x2f, 0xfd, 0, exponent << 3, 1, 0, 0]
526 }
527
528 #[test]
529 fn zstd_window_past_the_per_expand_allowance_is_unsupported_and_leaves_the_session_open() {
530 let window = 32 << 20;
531 let bytes = frame_with_window(window);
532 let policy = DecodePolicy::service();
533 let arena = DecodeArena::new();
534 let (ctx, root) = DecodeContext::from_root_bytes(&bytes, &arena, &policy).expect("root");
535 let allowance = ctx.per_expand_allowance();
536 assert!(allowance < window);
537 assert!(window < policy.limits.max_decompressed_bytes_per_expand);
538 let Err(CodecError::NotImplemented(message)) = ctx.open_zstd(root) else {
539 panic!("a window above the allowance is unsupported");
540 };
541 assert_eq!(
542 message,
543 format!(
544 "Zstandard window of {window} bytes exceeds the \
545 {allowance}-byte per-expansion allowance"
546 )
547 );
548 assert_eq!(ctx.resource_refusal(), None);
549 let mut writer = ctx
550 .begin_expand(ExpandSpec::Exact(1))
551 .expect("later expansions are admitted");
552 writer.write(b"x").expect("write");
553 writer.finalize().expect("finalize");
554 assert!(ctx.finish_session().is_ok());
555 }
556
557 #[test]
558 fn zstd_window_is_admitted_by_a_smaller_declared_content_size() {
559 let window = 32 << 20;
560 let content = [7_u8; 256];
561 let mut bytes = vec![
564 0x28,
565 0xb5,
566 0x2f,
567 0xfd,
568 0x40,
569 15 << 3,
570 0,
571 0,
572 0x01,
573 0x08,
574 0x00,
575 ];
576 bytes.extend_from_slice(&content);
577 let policy = DecodePolicy::service();
578 let arena = DecodeArena::new();
579 let (ctx, root) = DecodeContext::from_root_bytes(&bytes, &arena, &policy).expect("root");
580 assert!(ctx.per_expand_allowance() < window);
581 let mut decoder = ctx.open_zstd(root).expect("the content size fits");
582 let mut output = Vec::new();
583 let mut chunk = [0; 64];
584 loop {
585 let read = decoder.read_chunk(&mut chunk).expect("read");
586 if read == 0 {
587 break;
588 }
589 output.extend_from_slice(&chunk[..read]);
590 }
591 assert_eq!(output, content);
592 assert_eq!(ctx.resource_refusal(), None);
593 }
594
595 #[test]
596 fn zstd_window_above_the_decoder_is_unsupported_and_leaves_the_session_open() {
597 let window = MAX_DECODER_WINDOW * 2;
598 let mut bytes = frame_with_window(window);
599 bytes.resize(usize::try_from(window / 256).expect("padding length"), 0);
601 let mut policy = DecodePolicy::service();
602 policy.limits.max_decompressed_bytes_per_expand = window;
603 let arena = DecodeArena::new();
604 let (ctx, root) = DecodeContext::from_root_bytes(&bytes, &arena, &policy).expect("root");
605 assert!(ctx.per_expand_allowance() >= window);
606 assert!(matches!(
607 ctx.open_zstd(root),
608 Err(CodecError::NotImplemented(message)) if message.contains("decoder window")
609 ));
610 assert_eq!(ctx.resource_refusal(), None);
611 assert!(ctx.finish_session().is_ok());
612 }
613
614 #[test]
615 fn zstd_checks_each_window_before_decoder_allocation() {
616 let mut bytes = single_segment(b"prefix");
617 bytes.extend_from_slice(&[0x28, 0xb5, 0x2f, 0xfd, 0, 0xa0, 1, 0, 0]);
618 let arena = DecodeArena::new();
619 let (ctx, root) =
620 DecodeContext::from_root_bytes(&bytes, &arena, &DecodePolicy::service()).expect("root");
621 assert!(
622 matches!(ctx.open_zstd(root), Err(CodecError::NotImplemented(message)) if message.starts_with(&format!("Zstandard window of {} bytes", 1_u64 << 30)) && message.ends_with("per-expansion allowance"))
623 );
624 assert_eq!(ctx.resource_refusal(), None);
625 }
626
627 #[test]
628 fn zstd_decodes_a_frame() {
629 let bytes = single_segment(b"payload");
630 let arena = DecodeArena::new();
631 let (ctx, root) =
632 DecodeContext::from_root_bytes(&bytes, &arena, &DecodePolicy::service()).expect("root");
633 let mut decoder = ctx.open_zstd(root).expect("decoder");
634 let mut output = [0; 7];
635 assert_eq!(decoder.read_chunk(&mut output).expect("read"), 7);
636 assert_eq!(&output, b"payload");
637 }
638
639 #[test]
642 fn zstd_decodes_a_frame_that_fills_its_window() {
643 let block_bytes = usize::try_from(BLOCK_BYTES).expect("block size");
644 let mut blocks = vec![block(false, 1, block_bytes, b"x"); 19];
645 blocks.push(block(true, 1, block_bytes, b"x"));
646 let bytes = windowed(20, &blocks);
647 assert_eq!(decode(&bytes).expect("fits"), vec![b'x'; 20 * block_bytes]);
648 }
649
650 #[test]
656 fn zstd_short_frame_under_a_large_window_needs_its_content_size() {
657 let bytes = windowed(28, &[raw(true, b"abcd")]);
658 assert!(matches!(
659 decode(&bytes),
660 Err(CodecError::NotImplemented(message)) if message.ends_with("per-expansion allowance")
661 ));
662 let mut sized = MAGIC.to_vec();
663 sized.extend_from_slice(&[0x80, 18 << 3, 4, 0, 0, 0]); sized.extend(raw(true, b"abcd"));
665 assert_eq!(decode(&sized).expect("frame"), b"abcd");
666 }
667
668 #[test]
669 fn zstd_decodes_every_frame_and_skips_skippable_ones() {
670 let first = single_segment(b"first ");
671 let second = windowed(10, &[raw(false, b"sec"), raw(true, b"ond")]);
672 let skippable = [&[0x53, 0x2a, 0x4d, 0x18, 3, 0, 0, 0][..], b"zzz"].concat();
673
674 assert_eq!(
675 decode(&[first.clone(), second.clone()].concat()).expect("two frames"),
676 b"first second"
677 );
678 assert_eq!(
679 decode(&[skippable.clone(), first.clone(), skippable.clone(), second].concat())
680 .expect("skippable frames between data frames"),
681 b"first second"
682 );
683 assert_eq!(
684 decode(&skippable).expect("only a skippable frame"),
685 b"",
686 "a stream of skippable frames is an empty payload"
687 );
688 assert_malformed(b"", "an empty stream");
689 for tail in [&b"junk"[..], &b"\0"[..], &skippable[..9]] {
691 let bytes = [&first[..], tail].concat();
692 let arena = DecodeArena::new();
693 let (ctx, root) =
694 DecodeContext::from_root_bytes(&bytes, &arena, &DecodePolicy::service())
695 .expect("root");
696 assert!(
697 matches!(
698 ctx.open_zstd(root),
699 Err(CodecError::Malformed(_) | CodecError::Truncated { .. })
700 ),
701 "a {}-byte tail was admitted",
702 tail.len()
703 );
704 }
705 }
706
707 #[test]
708 fn zstd_rle_blocks_and_block_boundaries_decode() {
709 let frame = windowed(
710 10,
711 &[
712 raw(false, b"ab"),
713 block(false, 1, 1000, b"x"),
714 block(false, 0, 0, b""),
715 block(true, 1, 600, b"y"),
716 ],
717 );
718 let mut expected = b"ab".to_vec();
719 expected.extend(std::iter::repeat_n(b'x', 1000));
720 expected.extend(std::iter::repeat_n(b'y', 600));
721 assert_eq!(decode(&frame).expect("frame"), expected);
722 for keep in 0..frame.len() {
723 assert_cut_short(&frame[..keep], "a truncated frame");
724 }
725 }
726
727 #[test]
729 fn zstd_content_checksum_is_compared() {
730 let mut frame = MAGIC.to_vec();
732 frame.extend_from_slice(&[0x04, 0x00]);
733 frame.extend(raw(true, b"abcd"));
734 frame.extend_from_slice(&0xd25d_92cc_u32.to_le_bytes());
735 assert_eq!(decode(&frame).expect("matching checksum"), b"abcd");
736 for index in frame.len() - 4..frame.len() {
737 let mut corrupt = frame.clone();
738 corrupt[index] ^= 1;
739 assert_malformed(&corrupt, "a wrong content checksum");
740 }
741 let payload = frame.len() - 5;
742 frame[payload] ^= 1;
743 assert_malformed(&frame, "a payload its checksum does not cover");
744 }
745
746 #[test]
748 fn zstd_declared_content_size_is_compared() {
749 let mut frame = single_segment(b"abcd");
750 assert_eq!(decode(&frame).expect("exact"), b"abcd");
751 frame[5] = 8;
752 assert_malformed(&frame, "a frame shorter than its declared size");
753 let mut frame = MAGIC.to_vec();
755 frame.extend_from_slice(&[0x40, 0x00, 0x00, 0x00]); frame.extend(raw(true, &[7; 300]));
757 assert_malformed(&frame, "a frame longer than its declared size");
758 frame[6] = 44; assert_eq!(decode(&frame).expect("exact"), [7; 300]);
760 }
761
762 #[test]
764 fn zstd_stops_at_the_declared_content_size() {
765 let block_bytes = usize::try_from(BLOCK_BYTES).expect("block size");
766 let mut frame = MAGIC.to_vec();
767 frame.extend_from_slice(&[0x40, 0x50, 0x00, 0x00]); for index in 0..10 {
769 frame.extend(block(index == 9, 1, block_bytes, b"x"));
770 }
771 let arena = DecodeArena::new();
772 let (ctx, root) =
773 DecodeContext::from_root_bytes(&frame, &arena, &DecodePolicy::service()).expect("root");
774 let mut decoder = ctx.open_zstd(root).expect("decoder");
775 let mut chunk = [0_u8; 16 * 1024];
776 let mut delivered = 0;
777 let error = loop {
778 match decoder.read_chunk(&mut chunk) {
779 Ok(0) => panic!("the frame was accepted"),
780 Ok(read) => delivered += read,
781 Err(error) => break error,
782 }
783 };
784 assert!(matches!(error, CodecError::Malformed(_)), "{error:?}");
785 assert!(
786 delivered <= 256,
787 "{delivered} bytes were delivered past a declared size of 256"
788 );
789 }
790
791 #[test]
793 fn zstd_blocks_are_held_to_their_frames_maximum() {
794 assert_eq!(
795 decode(&windowed(10, &[raw(true, &[1; 1024])])).expect("at the maximum"),
796 [1; 1024]
797 );
798 assert_malformed(
799 &windowed(10, &[raw(true, &[1; 1025])]),
800 "a raw block over its window",
801 );
802 assert_malformed(
803 &windowed(10, &[block(true, 1, 1025, b"x")]),
804 "an RLE block over its window",
805 );
806 let compressed = rle_literals;
807 assert_eq!(
808 decode(&windowed(10, &[compressed(true, 1000)])).expect("under the maximum"),
809 [b'A'; 1000]
810 );
811 assert_malformed(
812 &windowed(10, &[compressed(true, 2000)]),
813 "a compressed block regenerating more than its window, last",
814 );
815 assert_malformed(
816 &windowed(10, &[compressed(false, 2000), raw(true, b"z")]),
817 "a compressed block regenerating more than its window, not last",
818 );
819 assert_malformed(
820 &windowed(10, &[raw(false, &[1; 600]), compressed(true, 1025)]),
821 "a compressed block regenerating more than its window after a raw one",
822 );
823 }
824
825 #[test]
829 fn zstd_block_regenerating_past_128_kib_is_refused() {
830 const SEQUENCES: u8 = 100;
831 let mut content = vec![0x00, SEQUENCES, 0x54, 0, 0, 52];
834 content.extend(std::iter::repeat_n(0xff, 2 * usize::from(SEQUENCES)));
835 content.push(0x01);
836 let frame = windowed(
837 21,
838 &[
839 raw(false, b"abcdefgh"),
840 block(true, 2, content.len(), &content),
841 ],
842 );
843 assert_malformed(&frame, "a block regenerating 13 MiB");
844 }
845
846 const REFERENCE_BLOCK: [u8; 77] = [
852 0x42, 0x43, 0x0c, 0x11, 0xa0, 0x6f, 0x20, 0xe9, 0x9f, 0x94, 0x37, 0x01, 0x80, 0xf3, 0xc9,
853 0x9f, 0x90, 0xaa, 0xdf, 0x42, 0x1e, 0xc7, 0x14, 0x32, 0x84, 0xb7, 0x27, 0x74, 0x4c, 0x21,
854 0x43, 0x38, 0xf8, 0x69, 0xeb, 0x75, 0x8c, 0x9f, 0xaa, 0x15, 0x28, 0x75, 0x90, 0xbf, 0x55,
855 0x66, 0x92, 0xbb, 0x9b, 0x3f, 0xba, 0x17, 0x12, 0xa8, 0x10, 0xe8, 0xeb, 0xff, 0x19, 0xf0,
856 0x35, 0x03, 0x10, 0x66, 0x84, 0x1d, 0xf1, 0x7f, 0xe3, 0x03, 0xac, 0x7c, 0x2b, 0xad, 0x02,
857 0x92, 0x0c,
858 ];
859
860 fn reference_block_plaintext() -> Vec<u8> {
862 let mut out = Vec::new();
863 for line in 0..20 {
864 out.extend_from_slice(format!("line {line:03} of the miss-case fixture\n").as_bytes());
865 }
866 out.truncate(600);
867 out
868 }
869
870 fn rle_literals(last: bool, size: u16) -> Vec<u8> {
873 let header = 1 | 1 << 2 | size << 4;
874 block(
875 last,
876 2,
877 4,
878 &[&header.to_le_bytes()[..], b"A", &[0]].concat(),
879 )
880 }
881
882 #[test]
883 fn zstd_decodes_a_reference_encoded_block_with_sequences() {
884 let plain = reference_block_plaintext();
885 let reference = |last| block(last, 2, REFERENCE_BLOCK.len(), &REFERENCE_BLOCK);
886 assert_eq!(
887 decode(&windowed(10, &[reference(true)])).expect("block"),
888 plain
889 );
890 let expected = [&plain[..], &[b'A'; 1000], b"end"].concat();
892 assert_eq!(
893 decode(&windowed(
894 10,
895 &[
896 reference(false),
897 rle_literals(false, 1000),
898 raw(true, b"end")
899 ]
900 ))
901 .expect("three blocks"),
902 expected
903 );
904 }
905
906 #[test]
913 fn zstd_block_header_maximum_holds_when_the_buffer_length_is_unknown() {
914 let reference = block(false, 2, REFERENCE_BLOCK.len(), &REFERENCE_BLOCK);
915 let raw_literals = |size: u16| {
916 let header = 1_u16 << 2 | size << 4;
917 let stored = [
918 &header.to_le_bytes()[..],
919 &vec![b'A'; usize::from(size)],
920 &[0],
921 ]
922 .concat();
923 block(true, 2, stored.len(), &stored)
924 };
925 assert_eq!(
926 decode(&windowed(10, &[reference.clone(), raw_literals(400)]))
927 .expect("control: a compressed block of raw literals under the maximum")
928 .len(),
929 reference_block_plaintext().len() + 400
930 );
931 assert_malformed(
932 &windowed(10, &[reference.clone(), raw_literals(1025)]),
933 "a compressed block storing more than its window after a compressed one",
934 );
935 assert_malformed(
936 &windowed(10, &[reference.clone(), raw(true, &[1; 1025])]),
937 "a raw block over its window after a compressed one",
938 );
939 assert_malformed(
940 &windowed(10, &[reference, block(true, 1, 1025, b"x")]),
941 "an RLE block over its window after a compressed one",
942 );
943 }
944
945 #[test]
958 fn zstd_known_gap_a_compressed_block_over_its_maximum_under_an_unfilled_window() {
959 let reference = block(false, 2, REFERENCE_BLOCK.len(), &REFERENCE_BLOCK);
960 let expected = [&reference_block_plaintext()[..], &[b'A'; 1100]].concat();
961 assert_eq!(
962 decode(&windowed(10, &[reference, rle_literals(true, 1100)]))
963 .expect("accepted, where libzstd refuses"),
964 expected
965 );
966 assert_malformed(
968 &windowed(
969 10,
970 &[
971 raw(false, &reference_block_plaintext()),
972 rle_literals(true, 1100),
973 ],
974 ),
975 "the same block after a raw one",
976 );
977 }
978
979 #[test]
980 fn zstd_refuses_what_the_frame_format_reserves() {
981 let mut frame = single_segment(b"abcd");
982 frame[4] |= 8;
983 assert_malformed(&frame, "the reserved frame-descriptor bit");
984 assert_malformed(
985 &windowed(10, &[block(true, 3, 0, b"")]),
986 "the reserved block type",
987 );
988 let mut frame = MAGIC.to_vec();
990 frame.extend_from_slice(&[0x01, 0x00, 0x05]);
991 frame.extend(raw(true, b"abcd"));
992 assert_malformed(&frame, "a frame naming a dictionary");
993 frame[6] = 0;
994 assert_eq!(decode(&frame).expect("dictionary ID 0"), b"abcd");
995 }
996
997 #[test]
999 fn zstd_last_bytes_carry_the_frames_verdict() {
1000 let mut frame = MAGIC.to_vec();
1001 frame.extend_from_slice(&[0x04, 0x00]);
1002 frame.extend(raw(true, b"abcd"));
1003 frame.extend_from_slice(&0xd25d_92cd_u32.to_le_bytes());
1004 let arena = DecodeArena::new();
1005 let (ctx, root) =
1006 DecodeContext::from_root_bytes(&frame, &arena, &DecodePolicy::service()).expect("root");
1007 let mut decoder = ctx.open_zstd(root).expect("decoder");
1008 let mut output = [0; 4];
1009 assert!(matches!(
1010 decoder.read_chunk(&mut output),
1011 Err(CodecError::Malformed(_))
1012 ));
1013 }
1014}