1use super::block_decoder::{self, BlockDecoder};
59use super::buffer_backend::{BufferBackend, WILDCOPY_OVERLENGTH};
60use super::decode_buffer::DecodeBuffer;
61use super::errors::{
62 BlockHeaderReadError, DecodeBlockContentError, FrameHeaderError, ReadFrameHeaderError,
63};
64use super::frame;
65use super::ringbuffer::RingBuffer;
66use super::scratch::{ByteScratch, FSEScratch, HuffmanScratch, Workspace, WorkspaceRef};
67use super::user_slice_buf::UserSliceBackend;
68use crate::blocks::block::{BlockHeader, BlockType};
69use crate::common::{MAGIC_NUM, MAX_DECODER_WINDOW_SIZE, MIN_WINDOW_SIZE};
70use core::marker::PhantomData;
71use core::ptr::NonNull;
72
73pub const COMPRESSED_BLOCK_UNITS: u64 = (1 << crate::huff0::MAX_MAX_NUM_BITS)
78 + (1 << super::sequence_section_decoder::LL_MAX_LOG)
79 + (1 << super::sequence_section_decoder::ML_MAX_LOG)
80 + (1 << super::sequence_section_decoder::OF_MAX_LOG);
81
82const MAX_FRAME_HEADER: usize = 18;
85
86const TABLE_ALIGN: usize = {
88 let fse = core::mem::align_of::<FSEScratch>();
89 let huf = core::mem::align_of::<HuffmanScratch>();
90 if fse > huf { fse } else { huf }
91};
92
93pub fn workspace_bytes(max_window_size: u64) -> Result<usize, WorkspaceConfigError> {
118 Layout::for_window(max_window_size).map(|layout| layout.total)
119}
120
121struct Layout {
123 huf_offset: usize,
124 tables_len: usize,
125 ring_cap: usize,
126 literals_len: usize,
127 staging_len: usize,
128 total: usize,
129}
130
131impl Layout {
132 fn for_window(max_window_size: u64) -> Result<Layout, WorkspaceConfigError> {
133 use WorkspaceConfigError::SizeOverflow;
134 if !(MIN_WINDOW_SIZE..=MAX_DECODER_WINDOW_SIZE).contains(&max_window_size) {
135 return Err(WorkspaceConfigError::WindowLimitOutOfRange {
136 requested: max_window_size,
137 });
138 }
139 let window = usize::try_from(max_window_size).map_err(|_| SizeOverflow)?;
140 let block_max = block_decoder::block_maximum(window);
141 let huf_offset = core::mem::size_of::<FSEScratch>()
142 .next_multiple_of(core::mem::align_of::<HuffmanScratch>());
143 let tables_len = huf_offset + core::mem::size_of::<HuffmanScratch>();
144 let ring_cap = window
149 .checked_add(2 * block_max)
150 .and_then(|n| n.checked_add(1))
151 .ok_or(SizeOverflow)?;
152 let literals_len = block_max + WILDCOPY_OVERLENGTH;
153 let staging_len = block_max;
154 let total = [
155 tables_len,
156 ring_cap,
157 WILDCOPY_OVERLENGTH,
158 literals_len,
159 staging_len,
160 ]
161 .iter()
162 .try_fold(TABLE_ALIGN - 1, |sum, &part| sum.checked_add(part))
163 .ok_or(SizeOverflow)?;
164 Ok(Layout {
165 huf_offset,
166 tables_len,
167 ring_cap,
168 literals_len,
169 staging_len,
170 total,
171 })
172 }
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq)]
177pub struct DecodeLimits {
178 pub max_window_size: u64,
181 pub max_output: u64,
183}
184
185impl DecodeLimits {
186 pub const fn new(max_window_size: u64) -> Self {
188 Self {
189 max_window_size,
190 max_output: u64::MAX,
191 }
192 }
193
194 pub const fn with_max_output(self, max_output: u64) -> Self {
196 Self { max_output, ..self }
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
210pub struct WorkBudget {
211 remaining: u64,
212}
213
214impl WorkBudget {
215 pub const fn new(units: u64) -> Self {
217 Self { remaining: units }
218 }
219
220 pub const fn remaining(&self) -> u64 {
222 self.remaining
223 }
224
225 pub fn add(&mut self, units: u64) {
228 self.remaining = self.remaining.saturating_add(units);
229 }
230
231 fn charge(&mut self, units: u64) -> Result<(), WorkspaceDecodeError> {
232 match self.remaining.checked_sub(units) {
233 Some(left) => {
234 self.remaining = left;
235 Ok(())
236 }
237 None => Err(WorkspaceDecodeError::Limit(LimitExceeded::Work {
238 needed: units,
239 remaining: self.remaining,
240 })),
241 }
242 }
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247pub struct Progress {
248 pub consumed: usize,
250 pub produced: usize,
252 pub state: StepState,
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
258#[non_exhaustive]
259pub enum StepState {
260 NeedInput,
263 NeedOutput,
265 Done,
267}
268
269#[derive(Debug, Clone, Copy, PartialEq, Eq)]
272#[non_exhaustive]
273pub enum WorkspaceConfigError {
274 WindowLimitOutOfRange {
277 requested: u64,
279 },
280 SizeOverflow,
282 WorkspaceTooSmall {
284 needed: usize,
286 provided: usize,
288 },
289}
290
291impl core::fmt::Display for WorkspaceConfigError {
292 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
293 match self {
294 Self::WindowLimitOutOfRange { requested } => write!(
295 f,
296 "window limit {requested} lies outside {MIN_WINDOW_SIZE}..={MAX_DECODER_WINDOW_SIZE}"
297 ),
298 Self::SizeOverflow => write!(f, "workspace size does not fit in usize"),
299 Self::WorkspaceTooSmall { needed, provided } => write!(
300 f,
301 "workspace of {provided} bytes is smaller than the {needed} the limits require"
302 ),
303 }
304 }
305}
306
307#[cfg(feature = "std")]
308impl std::error::Error for WorkspaceConfigError {}
309
310#[derive(Debug)]
313#[non_exhaustive]
314pub enum WorkspaceDecodeError {
315 Limit(LimitExceeded),
317 Malformed(MalformedInput),
319 Unsupported(Unsupported),
321 Truncated,
323 Failed,
325}
326
327#[derive(Debug, Clone, Copy, PartialEq, Eq)]
329#[non_exhaustive]
330pub enum LimitExceeded {
331 Window {
333 requested: u64,
335 limit: u64,
337 },
338 Output {
340 limit: u64,
342 },
343 Work {
345 needed: u64,
347 remaining: u64,
349 },
350}
351
352#[derive(Debug, Clone, Copy, PartialEq, Eq)]
354#[non_exhaustive]
355pub enum Unsupported {
356 Dictionary {
358 id: u32,
360 },
361 #[cfg(feature = "legacy")]
365 LegacyFrame {
366 version: crate::decoding::LegacyVersion,
368 },
369}
370
371#[derive(Debug)]
373#[non_exhaustive]
374pub enum MalformedInput {
375 FrameHeader(ReadFrameHeaderError),
377 Window(FrameHeaderError),
379 BlockHeader(BlockHeaderReadError),
381 BlockTooLarge {
384 size: usize,
386 maximum: usize,
388 },
389 Block(DecodeBlockContentError),
391 ContentSize {
393 declared: u64,
395 produced: u64,
397 },
398 Checksum {
400 expected: u32,
402 calculated: u32,
404 },
405}
406
407impl core::fmt::Display for WorkspaceDecodeError {
408 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
409 match self {
410 Self::Limit(limit) => match limit {
411 LimitExceeded::Window { requested, limit } => write!(
412 f,
413 "frame window of {requested} bytes exceeds the {limit}-byte limit"
414 ),
415 LimitExceeded::Output { limit } => {
416 write!(f, "output exceeds the {limit}-byte limit")
417 }
418 LimitExceeded::Work { needed, remaining } => write!(
419 f,
420 "work of {needed} units exceeds the {remaining} remaining"
421 ),
422 },
423 Self::Malformed(malformed) => match malformed {
424 MalformedInput::FrameHeader(e) => write!(f, "invalid frame header: {e}"),
425 MalformedInput::Window(e) => write!(f, "invalid frame window: {e}"),
426 MalformedInput::BlockHeader(e) => write!(f, "invalid block header: {e}"),
427 MalformedInput::BlockTooLarge { size, maximum } => write!(
428 f,
429 "block of {size} bytes exceeds the frame's block maximum of {maximum}"
430 ),
431 MalformedInput::Block(e) => write!(f, "invalid block: {e}"),
432 MalformedInput::ContentSize { declared, produced } => write!(
433 f,
434 "frame declares {declared} bytes but its blocks produce {produced}"
435 ),
436 MalformedInput::Checksum {
437 expected,
438 calculated,
439 } => write!(
440 f,
441 "content checksum {calculated:#010x} does not match the stored {expected:#010x}"
442 ),
443 },
444 Self::Unsupported(Unsupported::Dictionary { id }) => {
445 write!(
446 f,
447 "frame needs dictionary {id}, which this decoder does not take"
448 )
449 }
450 #[cfg(feature = "legacy")]
451 Self::Unsupported(Unsupported::LegacyFrame { version }) => write!(
452 f,
453 "v0.{} frame, which this decoder does not read",
454 version.minor()
455 ),
456 Self::Truncated => write!(f, "input ended in the middle of a frame"),
457 Self::Failed => write!(f, "an earlier step failed; reset the decoder"),
458 }
459 }
460}
461
462#[cfg(feature = "std")]
463impl std::error::Error for WorkspaceDecodeError {}
464
465struct SliceScratch<'w> {
473 buf: &'w mut [u8],
474 len: usize,
475}
476
477impl ByteScratch for SliceScratch<'_> {
478 #[inline(always)]
479 fn len(&self) -> usize {
480 self.len
481 }
482 #[inline(always)]
483 fn clear(&mut self) {
484 self.len = 0;
485 }
486 #[inline(always)]
487 fn truncate(&mut self, len: usize) {
488 self.len = self.len.min(len);
489 }
490 #[inline(always)]
491 fn reserve(&mut self, additional: usize) {
492 assert!(
493 self.len
494 .checked_add(additional)
495 .is_some_and(|end| end <= self.buf.len()),
496 "literals exceed the workspace region sized for a block"
497 );
498 }
499 #[inline(always)]
500 fn push(&mut self, byte: u8) {
501 if let Some(slot) = self.buf.get_mut(self.len) {
502 *slot = byte;
503 }
504 self.len += 1;
505 }
506 fn resize(&mut self, new_len: usize, value: u8) {
507 if new_len > self.len {
508 self.buf[self.len..new_len].fill(value);
509 }
510 self.len = new_len;
511 }
512 fn extend_from_slice(&mut self, bytes: &[u8]) {
513 let end = self.len + bytes.len();
514 self.buf[self.len..end].copy_from_slice(bytes);
515 self.len = end;
516 }
517 #[inline(always)]
518 fn as_mut_ptr(&mut self) -> *mut u8 {
519 self.buf.as_mut_ptr()
520 }
521 #[inline(always)]
522 unsafe fn set_len(&mut self, new_len: usize) {
523 assert!(new_len <= self.buf.len());
524 self.len = new_len;
525 }
526 #[inline(always)]
527 fn as_slice(&self) -> &[u8] {
528 &self.buf[..self.len.min(self.buf.len())]
529 }
530 #[inline(always)]
531 fn as_mut_slice(&mut self) -> &mut [u8] {
532 let len = self.len.min(self.buf.len());
533 &mut self.buf[..len]
534 }
535}
536
537enum Window<'w> {
543 Idle,
545 Ring(DecodeBuffer<RingBuffer>),
546 Linear(DecodeBuffer<UserSliceBackend<'w>>),
547}
548
549macro_rules! with_window {
552 ($window:expr, $buffer:ident => $body:expr, idle => $idle:expr) => {
553 match $window {
554 Window::Ring($buffer) => $body,
555 Window::Linear($buffer) => $body,
556 Window::Idle => $idle,
557 }
558 };
559}
560
561fn produced_so_far<B: BufferBackend>(buffer: &DecodeBuffer<B>) -> u64 {
565 if B::INLINE_EXEC_MAINTAINS_OUTPUT_COUNTER {
566 buffer.total_output()
567 } else {
568 buffer.buffer_ref().tail() as u64
569 }
570}
571
572struct BlockScratch<'a, 'w, B: BufferBackend> {
575 huf: &'a mut HuffmanScratch,
576 fse: &'a mut FSEScratch,
577 buffer: &'a mut DecodeBuffer<B>,
578 offset_hist: &'a mut [u32; 3],
579 literals: &'a mut SliceScratch<'w>,
580 block_content: &'a mut SliceScratch<'w>,
583}
584
585impl<'w, B: BufferBackend> Workspace for BlockScratch<'_, 'w, B> {
586 type Backend = B;
587 type Bytes = SliceScratch<'w>;
588 fn split(&mut self) -> WorkspaceRef<'_, B, SliceScratch<'w>> {
589 WorkspaceRef {
590 huf: &mut *self.huf,
591 fse: &mut *self.fse,
592 buffer: &mut *self.buffer,
593 offset_hist: &mut *self.offset_hist,
594 literals_buffer: &mut *self.literals,
595 block_content_buffer: &mut *self.block_content,
596 }
597 }
598}
599
600fn run_block<B: BufferBackend>(
603 decoder: &mut BlockDecoder,
604 scratch: &mut BlockScratch<'_, '_, B>,
605 header: &BlockHeader,
606 mut content: &[u8],
607) -> Result<u64, DecodeBlockContentError> {
608 let before = produced_so_far(scratch.buffer);
609 decoder.decode_block_content_from_slice(header, scratch, None, &mut content)?;
610 Ok(produced_so_far(scratch.buffer) - before)
611}
612
613#[derive(Clone, Copy, PartialEq, Eq, Debug)]
615enum Phase {
616 Magic,
618 Descriptor,
620 FrameHeader,
622 SkippableSize,
624 Skipping { remaining: u64 },
626 BlockHeader,
628 BlockBody(PendingBlock),
630 Checksum,
632}
633
634#[derive(Clone, Copy, PartialEq, Eq, Debug)]
636struct PendingBlock {
637 last_block: bool,
638 block_type: BlockType,
639 decompressed_size: u32,
640 content_size: u32,
641}
642
643impl PendingBlock {
644 fn header(self) -> BlockHeader {
645 BlockHeader {
646 last_block: self.last_block,
647 block_type: self.block_type,
648 decompressed_size: self.decompressed_size,
649 content_size: self.content_size,
650 }
651 }
652}
653
654#[derive(Clone, Copy)]
656struct FrameState {
657 declared_size: Option<u64>,
658 has_checksum: bool,
659 block_maximum: usize,
660 produced: u64,
661}
662
663pub struct WorkspaceDecoder<'w> {
667 huf: &'w mut HuffmanScratch,
668 fse: &'w mut FSEScratch,
669 offset_hist: [u32; 3],
670 literals: SliceScratch<'w>,
671 block_content: SliceScratch<'w>,
672 window: Window<'w>,
673 region: NonNull<u8>,
677 region_len: usize,
679 ring_cap: usize,
680 _region: PhantomData<&'w mut [u8]>,
681 staging: &'w mut [u8],
682 staged: usize,
683 block_decoder: BlockDecoder,
684 kernel: crate::cpu_kernel::CpuKernelTag,
685 limits: DecodeLimits,
686 phase: Phase,
687 header: [u8; MAX_FRAME_HEADER],
688 header_len: usize,
689 header_target: usize,
690 frame: FrameState,
691 blocks_done: bool,
694 stream_produced: u64,
695 failed: bool,
696}
697
698unsafe impl Send for WorkspaceDecoder<'_> {}
703
704impl<'w> WorkspaceDecoder<'w> {
705 pub fn new(
715 workspace: &'w mut [u8],
716 limits: DecodeLimits,
717 ) -> Result<Self, WorkspaceConfigError> {
718 let layout = Layout::for_window(limits.max_window_size)?;
719 let provided = workspace.len();
720 if provided < layout.total {
721 return Err(WorkspaceConfigError::WorkspaceTooSmall {
722 needed: layout.total,
723 provided,
724 });
725 }
726 let skip = workspace.as_ptr().align_offset(TABLE_ALIGN);
729 debug_assert!(skip < TABLE_ALIGN);
730 let rest = &mut workspace[skip..];
731 let (tables, rest) = rest.split_at_mut(layout.tables_len);
732 let (ring, rest) = rest.split_at_mut(layout.ring_cap + WILDCOPY_OVERLENGTH);
733 let (literals, rest) = rest.split_at_mut(layout.literals_len);
734 let staging = &mut rest[..layout.staging_len];
735
736 let tables = tables.as_mut_ptr();
737 let fse_ptr = tables.cast::<FSEScratch>();
738 let (fse, huf) = unsafe {
746 let huf_ptr = tables.add(layout.huf_offset).cast::<HuffmanScratch>();
747 FSEScratch::init_in_place(fse_ptr);
748 HuffmanScratch::init_in_place(huf_ptr);
749 (&mut *fse_ptr, &mut *huf_ptr)
750 };
751 let region_len = ring.len();
752 let region = NonNull::from(ring).cast::<u8>();
753
754 let kernel = crate::cpu_kernel::detect_cpu_kernel();
755 Ok(WorkspaceDecoder {
756 huf,
757 fse,
758 offset_hist: [1, 4, 8],
759 literals: SliceScratch {
760 buf: literals,
761 len: 0,
762 },
763 block_content: SliceScratch {
764 buf: &mut [],
765 len: 0,
766 },
767 window: Window::Idle,
768 region,
769 region_len,
770 ring_cap: layout.ring_cap,
771 _region: PhantomData,
772 staging,
773 staged: 0,
774 block_decoder: block_decoder::with_kernel(kernel),
775 kernel,
776 limits,
777 phase: Phase::Magic,
778 header: [0; MAX_FRAME_HEADER],
779 header_len: 0,
780 header_target: 4,
781 frame: FrameState {
782 declared_size: None,
783 has_checksum: false,
784 block_maximum: 0,
785 produced: 0,
786 },
787 blocks_done: false,
788 stream_produced: 0,
789 failed: false,
790 })
791 }
792
793 pub fn reset(&mut self) {
796 self.window = Window::Idle;
797 self.literals.clear();
798 self.staged = 0;
799 self.block_decoder = block_decoder::with_kernel(self.kernel);
800 self.phase = Phase::Magic;
801 self.header_len = 0;
802 self.header_target = 4;
803 self.blocks_done = false;
804 self.stream_produced = 0;
805 self.failed = false;
806 }
807
808 pub fn total_output(&self) -> u64 {
810 self.stream_produced
811 }
812
813 pub fn step(
830 &mut self,
831 input: &[u8],
832 input_is_final: bool,
833 output: &mut [u8],
834 work: &mut WorkBudget,
835 ) -> Result<Progress, WorkspaceDecodeError> {
836 if self.failed {
837 return Err(WorkspaceDecodeError::Failed);
838 }
839 let result = self.step_inner(input, input_is_final, output, work);
840 if result.is_err() {
841 self.failed = true;
842 }
843 result
844 }
845
846 fn step_inner(
847 &mut self,
848 input: &[u8],
849 input_is_final: bool,
850 output: &mut [u8],
851 work: &mut WorkBudget,
852 ) -> Result<Progress, WorkspaceDecodeError> {
853 let mut consumed = 0;
854 let mut produced = 0;
855 loop {
856 produced += self.deliver(&mut output[produced..]);
857 if self.pending() != 0 {
858 return Ok(Progress {
859 consumed,
860 produced,
861 state: StepState::NeedOutput,
862 });
863 }
864 let rest = &input[consumed..];
865 match self.phase {
866 Phase::Skipping { remaining } => {
867 if remaining == 0 {
868 self.expect_frame();
869 continue;
870 }
871 if rest.is_empty() {
872 return self.out_of_input(input_is_final, consumed, produced);
873 }
874 let take = rest
875 .len()
876 .min(usize::try_from(remaining).unwrap_or(usize::MAX));
877 work.charge(take as u64)?;
878 consumed += take;
879 self.phase = Phase::Skipping {
880 remaining: remaining - take as u64,
881 };
882 }
883 Phase::BlockBody(block) if block.block_type == BlockType::Raw => {
884 let remaining = block.content_size as usize - self.staged;
888 if remaining == 0 {
889 self.finish_block(block)?;
890 continue;
891 }
892 if rest.is_empty() {
893 return self.out_of_input(input_is_final, consumed, produced);
894 }
895 let take = rest.len().min(remaining);
896 work.charge(2 * take as u64)?;
898 self.push_raw(&rest[..take])?;
899 self.staged += take;
900 consumed += take;
901 }
902 Phase::BlockBody(block) => {
903 let size = block.content_size as usize;
904 if self.staged == 0 && rest.len() >= size {
905 work.charge(size as u64)?;
906 consumed += size;
907 self.decode_block(block, &rest[..size], work)?;
908 continue;
909 }
910 if rest.is_empty() {
911 return self.out_of_input(input_is_final, consumed, produced);
912 }
913 let take = rest.len().min(size - self.staged);
914 work.charge(take as u64)?;
915 self.staging[self.staged..self.staged + take].copy_from_slice(&rest[..take]);
916 self.staged += take;
917 consumed += take;
918 if self.staged == size {
919 self.staged = 0;
920 let staging = core::mem::take(&mut self.staging);
924 let decoded = self.decode_block(block, &staging[..size], work);
925 self.staging = staging;
926 decoded?;
927 }
928 }
929 _ => {
930 if self.header_len < self.header_target {
931 if rest.is_empty() {
932 return self.out_of_input(input_is_final, consumed, produced);
933 }
934 let take = rest.len().min(self.header_target - self.header_len);
935 work.charge(take as u64)?;
936 self.header[self.header_len..self.header_len + take]
937 .copy_from_slice(&rest[..take]);
938 self.header_len += take;
939 consumed += take;
940 }
941 if self.header_len == self.header_target {
942 self.header_complete()?;
943 }
944 }
945 }
946 }
947 }
948
949 fn out_of_input(
951 &self,
952 input_is_final: bool,
953 consumed: usize,
954 produced: usize,
955 ) -> Result<Progress, WorkspaceDecodeError> {
956 if !input_is_final {
957 return Ok(Progress {
958 consumed,
959 produced,
960 state: StepState::NeedInput,
961 });
962 }
963 if self.phase == Phase::Magic && self.header_len == 0 {
964 return Ok(Progress {
965 consumed,
966 produced,
967 state: StepState::Done,
968 });
969 }
970 Err(WorkspaceDecodeError::Truncated)
971 }
972
973 fn pending(&self) -> usize {
975 with_window!(&self.window, buffer => buffer.undelivered(), idle => 0)
976 }
977
978 fn deliver(&mut self, output: &mut [u8]) -> usize {
979 if output.is_empty() {
980 return 0;
981 }
982 let blocks_done = self.blocks_done;
983 let drained = with_window!(&mut self.window, buffer => if blocks_done {
984 buffer.read_all(output)
985 } else {
986 buffer.read_reporting_pending(output).map(|(read, _)| read)
987 }, idle => Ok(0));
988 drained.unwrap_or(0)
990 }
991
992 fn expect_frame(&mut self) {
993 self.phase = Phase::Magic;
994 self.header_len = 0;
995 self.header_target = 4;
996 }
997
998 fn expect_header(&mut self, phase: Phase, target: usize) {
999 self.phase = phase;
1000 self.header_target = target;
1001 }
1002
1003 fn header_complete(&mut self) -> Result<(), WorkspaceDecodeError> {
1005 use WorkspaceDecodeError::Malformed;
1006 match self.phase {
1007 Phase::Magic => {
1008 let magic = u32::from_le_bytes([
1009 self.header[0],
1010 self.header[1],
1011 self.header[2],
1012 self.header[3],
1013 ]);
1014 if (0x184D2A50..=0x184D2A5F).contains(&magic) {
1015 self.expect_header(Phase::SkippableSize, 8);
1016 } else if magic == MAGIC_NUM {
1017 self.expect_header(Phase::Descriptor, 5);
1018 } else {
1019 #[cfg(feature = "legacy")]
1020 if let Some(version) = crate::decoding::LegacyVersion::of(&magic.to_le_bytes())
1021 {
1022 return Err(WorkspaceDecodeError::Unsupported(
1023 Unsupported::LegacyFrame { version },
1024 ));
1025 }
1026 return Err(Malformed(MalformedInput::FrameHeader(
1027 ReadFrameHeaderError::BadMagicNumber(magic),
1028 )));
1029 }
1030 }
1031 Phase::SkippableSize => {
1032 let length = u32::from_le_bytes([
1033 self.header[4],
1034 self.header[5],
1035 self.header[6],
1036 self.header[7],
1037 ]);
1038 self.phase = Phase::Skipping {
1039 remaining: u64::from(length),
1040 };
1041 }
1042 Phase::Descriptor => {
1043 let length = frame::measure_header_from_slice(&self.header[..5])
1044 .map_err(|e| Malformed(MalformedInput::FrameHeader(e)))?;
1045 self.expect_header(Phase::FrameHeader, length);
1046 }
1047 Phase::FrameHeader => self.start_frame()?,
1048 Phase::BlockHeader => self.start_block()?,
1049 Phase::Checksum => {
1050 self.check_checksum()?;
1051 self.expect_frame();
1052 }
1053 Phase::Skipping { .. } | Phase::BlockBody(_) => {
1054 unreachable!("phases without a header buffer are handled by the step loop")
1055 }
1056 }
1057 Ok(())
1058 }
1059
1060 fn start_frame(&mut self) -> Result<(), WorkspaceDecodeError> {
1061 use WorkspaceDecodeError::{Limit, Malformed};
1062 let mut source = &self.header[..self.header_target];
1063 let (header, _) = frame::read_frame_header_from_slice(&mut source, false)
1064 .map_err(|e| Malformed(MalformedInput::FrameHeader(e)))?;
1065 if let Some(id) = header.dictionary_id() {
1066 return Err(WorkspaceDecodeError::Unsupported(Unsupported::Dictionary {
1067 id,
1068 }));
1069 }
1070 let window = header
1071 .window_size()
1072 .map_err(|e| Malformed(MalformedInput::Window(e)))?;
1073 if window > self.limits.max_window_size {
1074 return Err(Limit(LimitExceeded::Window {
1075 requested: window,
1076 limit: self.limits.max_window_size,
1077 }));
1078 }
1079 let declared_size = header.fcs_declared().then(|| header.frame_content_size());
1080 if let Some(declared) = declared_size
1081 && self
1082 .stream_produced
1083 .checked_add(declared)
1084 .is_none_or(|total| total > self.limits.max_output)
1085 {
1086 return Err(Limit(LimitExceeded::Output {
1087 limit: self.limits.max_output,
1088 }));
1089 }
1090 let window = window as usize;
1092 let has_checksum = header.descriptor.content_checksum_flag();
1093
1094 self.window = Window::Idle;
1097 self.window = if header.descriptor.single_segment_flag() {
1098 let region =
1102 unsafe { core::slice::from_raw_parts_mut(self.region.as_ptr(), self.region_len) };
1103 Window::Linear(DecodeBuffer::from_backend(
1104 UserSliceBackend::from_slice(region),
1105 window,
1106 ))
1107 } else {
1108 let ring = unsafe { RingBuffer::from_borrowed(self.region, self.ring_cap) };
1112 Window::Ring(DecodeBuffer::from_backend(ring, window))
1113 };
1114 with_window!(&mut self.window, buffer => {
1115 buffer.set_declared_content(declared_size);
1116 #[cfg(feature = "hash")]
1117 buffer.set_compute_hash(has_checksum);
1118 }, idle => {});
1119 self.offset_hist = [1, 4, 8];
1120 self.literals.clear();
1121 super::scratch::reset_entropy(self.huf, self.fse);
1122
1123 self.frame = FrameState {
1124 declared_size,
1125 has_checksum,
1126 block_maximum: block_decoder::block_maximum(window),
1127 produced: 0,
1128 };
1129 self.blocks_done = false;
1130 self.header_len = 0;
1131 self.expect_header(Phase::BlockHeader, 3);
1132 Ok(())
1133 }
1134
1135 fn start_block(&mut self) -> Result<(), WorkspaceDecodeError> {
1136 use WorkspaceDecodeError::Malformed;
1137 let (header, _) = self
1138 .block_decoder
1139 .read_block_header(&self.header[..3])
1140 .map_err(|e| Malformed(MalformedInput::BlockHeader(e)))?;
1141 let size = match header.block_type {
1144 BlockType::RLE => header.decompressed_size,
1145 _ => header.content_size,
1146 } as usize;
1147 let maximum = self.frame.block_maximum;
1148 if size > maximum {
1149 return Err(Malformed(MalformedInput::BlockTooLarge { size, maximum }));
1150 }
1151 self.header_len = 0;
1152 self.staged = 0;
1153 self.phase = Phase::BlockBody(PendingBlock {
1154 last_block: header.last_block,
1155 block_type: header.block_type,
1156 decompressed_size: header.decompressed_size,
1157 content_size: header.content_size,
1158 });
1159 Ok(())
1160 }
1161
1162 fn decode_block(
1165 &mut self,
1166 block: PendingBlock,
1167 content: &[u8],
1168 work: &mut WorkBudget,
1169 ) -> Result<(), WorkspaceDecodeError> {
1170 use WorkspaceDecodeError::{Limit, Malformed};
1171 let output_bound = match block.block_type {
1172 BlockType::Compressed => self.frame.block_maximum as u64 + COMPRESSED_BLOCK_UNITS,
1173 _ => u64::from(block.decompressed_size),
1174 };
1175 work.charge(output_bound)?;
1176 let output_limit = Limit(LimitExceeded::Output {
1177 limit: self.limits.max_output,
1178 });
1179 if block.block_type != BlockType::Compressed
1180 && self
1181 .stream_produced
1182 .checked_add(u64::from(block.decompressed_size))
1183 .is_none_or(|total| total > self.limits.max_output)
1184 {
1185 return Err(output_limit);
1186 }
1187
1188 let header = block.header();
1189 let produced = with_window!(&mut self.window, buffer => run_block(
1190 &mut self.block_decoder,
1191 &mut BlockScratch {
1192 huf: &mut *self.huf,
1193 fse: &mut *self.fse,
1194 buffer,
1195 offset_hist: &mut self.offset_hist,
1196 literals: &mut self.literals,
1197 block_content: &mut self.block_content,
1198 },
1199 &header,
1200 content,
1201 ), idle => unreachable!("a block is decoded only inside a started frame"))
1202 .map_err(|e| Malformed(MalformedInput::Block(e)))?;
1203
1204 self.stream_produced += produced;
1205 self.frame.produced += produced;
1206 if self.stream_produced > self.limits.max_output {
1207 return Err(output_limit);
1208 }
1209 if let Some(declared) = self.frame.declared_size
1210 && self.frame.produced > declared
1211 {
1212 return Err(Malformed(MalformedInput::ContentSize {
1213 declared,
1214 produced: self.frame.produced,
1215 }));
1216 }
1217 self.finish_block(block)
1218 }
1219
1220 fn push_raw(&mut self, bytes: &[u8]) -> Result<(), WorkspaceDecodeError> {
1223 use crate::cpu_kernel::{BaselineKernel, CpuKernelTag, ScalarKernel};
1224 use WorkspaceDecodeError::{Limit, Malformed};
1225 let len = bytes.len() as u64;
1226 let stream_total = self.stream_produced + len;
1227 if stream_total > self.limits.max_output {
1228 return Err(Limit(LimitExceeded::Output {
1229 limit: self.limits.max_output,
1230 }));
1231 }
1232 let frame_total = self.frame.produced + len;
1233 if let Some(declared) = self.frame.declared_size
1234 && frame_total > declared
1235 {
1236 return Err(Malformed(MalformedInput::ContentSize {
1237 declared,
1238 produced: frame_total,
1239 }));
1240 }
1241 let scalar = self.kernel == CpuKernelTag::Scalar;
1244 let pushed = with_window!(&mut self.window, buffer => if scalar {
1245 buffer.try_push::<ScalarKernel>(bytes)
1246 } else {
1247 buffer.try_push::<BaselineKernel>(bytes)
1248 }, idle => unreachable!("a block is decoded only inside a started frame"));
1249 pushed.map_err(|_| {
1253 Malformed(MalformedInput::Block(
1254 DecodeBlockContentError::BackendOverflow {
1255 step: BlockType::Raw,
1256 },
1257 ))
1258 })?;
1259 self.stream_produced = stream_total;
1260 self.frame.produced = frame_total;
1261 Ok(())
1262 }
1263
1264 fn finish_block(&mut self, block: PendingBlock) -> Result<(), WorkspaceDecodeError> {
1267 if let Some(declared) = self.frame.declared_size
1268 && block.last_block
1269 && self.frame.produced != declared
1270 {
1271 return Err(WorkspaceDecodeError::Malformed(
1272 MalformedInput::ContentSize {
1273 declared,
1274 produced: self.frame.produced,
1275 },
1276 ));
1277 }
1278 self.staged = 0;
1279 self.header_len = 0;
1280 if !block.last_block {
1281 self.expect_header(Phase::BlockHeader, 3);
1282 } else {
1283 self.blocks_done = true;
1284 if self.frame.has_checksum {
1285 self.expect_header(Phase::Checksum, 4);
1286 } else {
1287 self.expect_frame();
1288 }
1289 }
1290 Ok(())
1291 }
1292
1293 fn check_checksum(&mut self) -> Result<(), WorkspaceDecodeError> {
1298 #[cfg(feature = "hash")]
1299 {
1300 use core::hash::Hasher;
1301 let expected = u32::from_le_bytes([
1302 self.header[0],
1303 self.header[1],
1304 self.header[2],
1305 self.header[3],
1306 ]);
1307 let calculated =
1308 with_window!(&self.window, buffer => buffer.hash.finish(), idle => 0) as u32;
1309 if expected != calculated {
1310 return Err(WorkspaceDecodeError::Malformed(MalformedInput::Checksum {
1311 expected,
1312 calculated,
1313 }));
1314 }
1315 }
1316 Ok(())
1317 }
1318}
1319
1320#[cfg(test)]
1321mod tests;