1use super::block_decoder::{self, BlockDecoder};
57use super::buffer_backend::{BufferBackend, WILDCOPY_OVERLENGTH};
58use super::decode_buffer::DecodeBuffer;
59use super::errors::{
60 BlockHeaderReadError, DecodeBlockContentError, FrameHeaderError, ReadFrameHeaderError,
61};
62use super::frame;
63use super::ringbuffer::RingBuffer;
64use super::scratch::{ByteScratch, FSEScratch, HuffmanScratch, Workspace, WorkspaceRef};
65use super::user_slice_buf::UserSliceBackend;
66use crate::blocks::block::{BlockHeader, BlockType};
67use crate::common::{MAGIC_NUM, MAX_DECODER_WINDOW_SIZE, MIN_WINDOW_SIZE};
68use core::marker::PhantomData;
69use core::ptr::NonNull;
70
71pub const COMPRESSED_BLOCK_UNITS: u64 = (1 << crate::huff0::MAX_MAX_NUM_BITS)
76 + (1 << super::sequence_section_decoder::LL_MAX_LOG)
77 + (1 << super::sequence_section_decoder::ML_MAX_LOG)
78 + (1 << super::sequence_section_decoder::OF_MAX_LOG);
79
80const MAX_FRAME_HEADER: usize = 18;
83
84const TABLE_ALIGN: usize = {
86 let fse = core::mem::align_of::<FSEScratch>();
87 let huf = core::mem::align_of::<HuffmanScratch>();
88 if fse > huf { fse } else { huf }
89};
90
91pub fn workspace_bytes(max_window_size: u64) -> Result<usize, WorkspaceConfigError> {
116 Layout::for_window(max_window_size).map(|layout| layout.total)
117}
118
119struct Layout {
121 huf_offset: usize,
122 tables_len: usize,
123 ring_cap: usize,
124 literals_len: usize,
125 staging_len: usize,
126 total: usize,
127}
128
129impl Layout {
130 fn for_window(max_window_size: u64) -> Result<Layout, WorkspaceConfigError> {
131 use WorkspaceConfigError::SizeOverflow;
132 if !(MIN_WINDOW_SIZE..=MAX_DECODER_WINDOW_SIZE).contains(&max_window_size) {
133 return Err(WorkspaceConfigError::WindowLimitOutOfRange {
134 requested: max_window_size,
135 });
136 }
137 let window = usize::try_from(max_window_size).map_err(|_| SizeOverflow)?;
138 let block_max = block_decoder::block_maximum(window);
139 let huf_offset = core::mem::size_of::<FSEScratch>()
140 .next_multiple_of(core::mem::align_of::<HuffmanScratch>());
141 let tables_len = huf_offset + core::mem::size_of::<HuffmanScratch>();
142 let ring_cap = window
147 .checked_add(2 * block_max)
148 .and_then(|n| n.checked_add(1))
149 .ok_or(SizeOverflow)?;
150 let literals_len = block_max + WILDCOPY_OVERLENGTH;
151 let staging_len = block_max;
152 let total = [
153 tables_len,
154 ring_cap,
155 WILDCOPY_OVERLENGTH,
156 literals_len,
157 staging_len,
158 ]
159 .iter()
160 .try_fold(TABLE_ALIGN - 1, |sum, &part| sum.checked_add(part))
161 .ok_or(SizeOverflow)?;
162 Ok(Layout {
163 huf_offset,
164 tables_len,
165 ring_cap,
166 literals_len,
167 staging_len,
168 total,
169 })
170 }
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq)]
175pub struct DecodeLimits {
176 pub max_window_size: u64,
179 pub max_output: u64,
181}
182
183impl DecodeLimits {
184 pub const fn new(max_window_size: u64) -> Self {
186 Self {
187 max_window_size,
188 max_output: u64::MAX,
189 }
190 }
191
192 pub const fn with_max_output(self, max_output: u64) -> Self {
194 Self { max_output, ..self }
195 }
196}
197
198#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208pub struct WorkBudget {
209 remaining: u64,
210}
211
212impl WorkBudget {
213 pub const fn new(units: u64) -> Self {
215 Self { remaining: units }
216 }
217
218 pub const fn remaining(&self) -> u64 {
220 self.remaining
221 }
222
223 pub fn add(&mut self, units: u64) {
226 self.remaining = self.remaining.saturating_add(units);
227 }
228
229 fn charge(&mut self, units: u64) -> Result<(), WorkspaceDecodeError> {
230 match self.remaining.checked_sub(units) {
231 Some(left) => {
232 self.remaining = left;
233 Ok(())
234 }
235 None => Err(WorkspaceDecodeError::Limit(LimitExceeded::Work {
236 needed: units,
237 remaining: self.remaining,
238 })),
239 }
240 }
241}
242
243#[derive(Debug, Clone, Copy, PartialEq, Eq)]
245pub struct Progress {
246 pub consumed: usize,
248 pub produced: usize,
250 pub state: StepState,
252}
253
254#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256#[non_exhaustive]
257pub enum StepState {
258 NeedInput,
261 NeedOutput,
263 Done,
265}
266
267#[derive(Debug, Clone, Copy, PartialEq, Eq)]
270#[non_exhaustive]
271pub enum WorkspaceConfigError {
272 WindowLimitOutOfRange {
275 requested: u64,
277 },
278 SizeOverflow,
280 WorkspaceTooSmall {
282 needed: usize,
284 provided: usize,
286 },
287}
288
289impl core::fmt::Display for WorkspaceConfigError {
290 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
291 match self {
292 Self::WindowLimitOutOfRange { requested } => write!(
293 f,
294 "window limit {requested} lies outside {MIN_WINDOW_SIZE}..={MAX_DECODER_WINDOW_SIZE}"
295 ),
296 Self::SizeOverflow => write!(f, "workspace size does not fit in usize"),
297 Self::WorkspaceTooSmall { needed, provided } => write!(
298 f,
299 "workspace of {provided} bytes is smaller than the {needed} the limits require"
300 ),
301 }
302 }
303}
304
305#[cfg(feature = "std")]
306impl std::error::Error for WorkspaceConfigError {}
307
308#[derive(Debug)]
311#[non_exhaustive]
312pub enum WorkspaceDecodeError {
313 Limit(LimitExceeded),
315 Malformed(MalformedInput),
317 Unsupported(Unsupported),
319 Truncated,
321 Failed,
323}
324
325#[derive(Debug, Clone, Copy, PartialEq, Eq)]
327#[non_exhaustive]
328pub enum LimitExceeded {
329 Window {
331 requested: u64,
333 limit: u64,
335 },
336 Output {
338 limit: u64,
340 },
341 Work {
343 needed: u64,
345 remaining: u64,
347 },
348}
349
350#[derive(Debug, Clone, Copy, PartialEq, Eq)]
352#[non_exhaustive]
353pub enum Unsupported {
354 Dictionary {
356 id: u32,
358 },
359}
360
361#[derive(Debug)]
363#[non_exhaustive]
364pub enum MalformedInput {
365 FrameHeader(ReadFrameHeaderError),
367 Window(FrameHeaderError),
369 BlockHeader(BlockHeaderReadError),
371 BlockTooLarge {
374 size: usize,
376 maximum: usize,
378 },
379 Block(DecodeBlockContentError),
381 ContentSize {
383 declared: u64,
385 produced: u64,
387 },
388 Checksum {
390 expected: u32,
392 calculated: u32,
394 },
395}
396
397impl core::fmt::Display for WorkspaceDecodeError {
398 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
399 match self {
400 Self::Limit(limit) => match limit {
401 LimitExceeded::Window { requested, limit } => write!(
402 f,
403 "frame window of {requested} bytes exceeds the {limit}-byte limit"
404 ),
405 LimitExceeded::Output { limit } => {
406 write!(f, "output exceeds the {limit}-byte limit")
407 }
408 LimitExceeded::Work { needed, remaining } => write!(
409 f,
410 "work of {needed} units exceeds the {remaining} remaining"
411 ),
412 },
413 Self::Malformed(malformed) => match malformed {
414 MalformedInput::FrameHeader(e) => write!(f, "invalid frame header: {e}"),
415 MalformedInput::Window(e) => write!(f, "invalid frame window: {e}"),
416 MalformedInput::BlockHeader(e) => write!(f, "invalid block header: {e}"),
417 MalformedInput::BlockTooLarge { size, maximum } => write!(
418 f,
419 "block of {size} bytes exceeds the frame's block maximum of {maximum}"
420 ),
421 MalformedInput::Block(e) => write!(f, "invalid block: {e}"),
422 MalformedInput::ContentSize { declared, produced } => write!(
423 f,
424 "frame declares {declared} bytes but its blocks produce {produced}"
425 ),
426 MalformedInput::Checksum {
427 expected,
428 calculated,
429 } => write!(
430 f,
431 "content checksum {calculated:#010x} does not match the stored {expected:#010x}"
432 ),
433 },
434 Self::Unsupported(Unsupported::Dictionary { id }) => {
435 write!(
436 f,
437 "frame needs dictionary {id}, which this decoder does not take"
438 )
439 }
440 Self::Truncated => write!(f, "input ended in the middle of a frame"),
441 Self::Failed => write!(f, "an earlier step failed; reset the decoder"),
442 }
443 }
444}
445
446#[cfg(feature = "std")]
447impl std::error::Error for WorkspaceDecodeError {}
448
449struct SliceScratch<'w> {
457 buf: &'w mut [u8],
458 len: usize,
459}
460
461impl ByteScratch for SliceScratch<'_> {
462 #[inline(always)]
463 fn len(&self) -> usize {
464 self.len
465 }
466 #[inline(always)]
467 fn clear(&mut self) {
468 self.len = 0;
469 }
470 #[inline(always)]
471 fn truncate(&mut self, len: usize) {
472 self.len = self.len.min(len);
473 }
474 #[inline(always)]
475 fn reserve(&mut self, additional: usize) {
476 assert!(
477 self.len
478 .checked_add(additional)
479 .is_some_and(|end| end <= self.buf.len()),
480 "literals exceed the workspace region sized for a block"
481 );
482 }
483 #[inline(always)]
484 fn push(&mut self, byte: u8) {
485 if let Some(slot) = self.buf.get_mut(self.len) {
486 *slot = byte;
487 }
488 self.len += 1;
489 }
490 fn resize(&mut self, new_len: usize, value: u8) {
491 if new_len > self.len {
492 self.buf[self.len..new_len].fill(value);
493 }
494 self.len = new_len;
495 }
496 fn extend_from_slice(&mut self, bytes: &[u8]) {
497 let end = self.len + bytes.len();
498 self.buf[self.len..end].copy_from_slice(bytes);
499 self.len = end;
500 }
501 #[inline(always)]
502 fn as_mut_ptr(&mut self) -> *mut u8 {
503 self.buf.as_mut_ptr()
504 }
505 #[inline(always)]
506 unsafe fn set_len(&mut self, new_len: usize) {
507 assert!(new_len <= self.buf.len());
508 self.len = new_len;
509 }
510 #[inline(always)]
511 fn as_slice(&self) -> &[u8] {
512 &self.buf[..self.len.min(self.buf.len())]
513 }
514 #[inline(always)]
515 fn as_mut_slice(&mut self) -> &mut [u8] {
516 let len = self.len.min(self.buf.len());
517 &mut self.buf[..len]
518 }
519}
520
521enum Window<'w> {
527 Idle,
529 Ring(DecodeBuffer<RingBuffer>),
530 Linear(DecodeBuffer<UserSliceBackend<'w>>),
531}
532
533macro_rules! with_window {
536 ($window:expr, $buffer:ident => $body:expr, idle => $idle:expr) => {
537 match $window {
538 Window::Ring($buffer) => $body,
539 Window::Linear($buffer) => $body,
540 Window::Idle => $idle,
541 }
542 };
543}
544
545fn produced_so_far<B: BufferBackend>(buffer: &DecodeBuffer<B>) -> u64 {
549 if B::INLINE_EXEC_MAINTAINS_OUTPUT_COUNTER {
550 buffer.total_output()
551 } else {
552 buffer.buffer_ref().tail() as u64
553 }
554}
555
556struct BlockScratch<'a, 'w, B: BufferBackend> {
559 huf: &'a mut HuffmanScratch,
560 fse: &'a mut FSEScratch,
561 buffer: &'a mut DecodeBuffer<B>,
562 offset_hist: &'a mut [u32; 3],
563 literals: &'a mut SliceScratch<'w>,
564 block_content: &'a mut SliceScratch<'w>,
567}
568
569impl<'w, B: BufferBackend> Workspace for BlockScratch<'_, 'w, B> {
570 type Backend = B;
571 type Bytes = SliceScratch<'w>;
572 fn split(&mut self) -> WorkspaceRef<'_, B, SliceScratch<'w>> {
573 WorkspaceRef {
574 huf: &mut *self.huf,
575 fse: &mut *self.fse,
576 buffer: &mut *self.buffer,
577 offset_hist: &mut *self.offset_hist,
578 literals_buffer: &mut *self.literals,
579 block_content_buffer: &mut *self.block_content,
580 }
581 }
582}
583
584fn run_block<B: BufferBackend>(
587 decoder: &mut BlockDecoder,
588 scratch: &mut BlockScratch<'_, '_, B>,
589 header: &BlockHeader,
590 mut content: &[u8],
591) -> Result<u64, DecodeBlockContentError> {
592 let before = produced_so_far(scratch.buffer);
593 decoder.decode_block_content_from_slice(header, scratch, None, &mut content)?;
594 Ok(produced_so_far(scratch.buffer) - before)
595}
596
597#[derive(Clone, Copy, PartialEq, Eq, Debug)]
599enum Phase {
600 Magic,
602 Descriptor,
604 FrameHeader,
606 SkippableSize,
608 Skipping { remaining: u64 },
610 BlockHeader,
612 BlockBody(PendingBlock),
614 Checksum,
616}
617
618#[derive(Clone, Copy, PartialEq, Eq, Debug)]
620struct PendingBlock {
621 last_block: bool,
622 block_type: BlockType,
623 decompressed_size: u32,
624 content_size: u32,
625}
626
627impl PendingBlock {
628 fn header(self) -> BlockHeader {
629 BlockHeader {
630 last_block: self.last_block,
631 block_type: self.block_type,
632 decompressed_size: self.decompressed_size,
633 content_size: self.content_size,
634 }
635 }
636}
637
638#[derive(Clone, Copy)]
640struct FrameState {
641 declared_size: Option<u64>,
642 has_checksum: bool,
643 block_maximum: usize,
644 produced: u64,
645}
646
647pub struct WorkspaceDecoder<'w> {
651 huf: &'w mut HuffmanScratch,
652 fse: &'w mut FSEScratch,
653 offset_hist: [u32; 3],
654 literals: SliceScratch<'w>,
655 block_content: SliceScratch<'w>,
656 window: Window<'w>,
657 region: NonNull<u8>,
661 region_len: usize,
663 ring_cap: usize,
664 _region: PhantomData<&'w mut [u8]>,
665 staging: &'w mut [u8],
666 staged: usize,
667 block_decoder: BlockDecoder,
668 kernel: crate::cpu_kernel::CpuKernelTag,
669 limits: DecodeLimits,
670 phase: Phase,
671 header: [u8; MAX_FRAME_HEADER],
672 header_len: usize,
673 header_target: usize,
674 frame: FrameState,
675 blocks_done: bool,
678 stream_produced: u64,
679 failed: bool,
680}
681
682unsafe impl Send for WorkspaceDecoder<'_> {}
687
688impl<'w> WorkspaceDecoder<'w> {
689 pub fn new(
699 workspace: &'w mut [u8],
700 limits: DecodeLimits,
701 ) -> Result<Self, WorkspaceConfigError> {
702 let layout = Layout::for_window(limits.max_window_size)?;
703 let provided = workspace.len();
704 if provided < layout.total {
705 return Err(WorkspaceConfigError::WorkspaceTooSmall {
706 needed: layout.total,
707 provided,
708 });
709 }
710 let skip = workspace.as_ptr().align_offset(TABLE_ALIGN);
713 debug_assert!(skip < TABLE_ALIGN);
714 let rest = &mut workspace[skip..];
715 let (tables, rest) = rest.split_at_mut(layout.tables_len);
716 let (ring, rest) = rest.split_at_mut(layout.ring_cap + WILDCOPY_OVERLENGTH);
717 let (literals, rest) = rest.split_at_mut(layout.literals_len);
718 let staging = &mut rest[..layout.staging_len];
719
720 let tables = tables.as_mut_ptr();
721 let fse_ptr = tables.cast::<FSEScratch>();
722 let (fse, huf) = unsafe {
730 let huf_ptr = tables.add(layout.huf_offset).cast::<HuffmanScratch>();
731 FSEScratch::init_in_place(fse_ptr);
732 HuffmanScratch::init_in_place(huf_ptr);
733 (&mut *fse_ptr, &mut *huf_ptr)
734 };
735 let region_len = ring.len();
736 let region = NonNull::from(ring).cast::<u8>();
737
738 let kernel = crate::cpu_kernel::detect_cpu_kernel();
739 Ok(WorkspaceDecoder {
740 huf,
741 fse,
742 offset_hist: [1, 4, 8],
743 literals: SliceScratch {
744 buf: literals,
745 len: 0,
746 },
747 block_content: SliceScratch {
748 buf: &mut [],
749 len: 0,
750 },
751 window: Window::Idle,
752 region,
753 region_len,
754 ring_cap: layout.ring_cap,
755 _region: PhantomData,
756 staging,
757 staged: 0,
758 block_decoder: block_decoder::with_kernel(kernel),
759 kernel,
760 limits,
761 phase: Phase::Magic,
762 header: [0; MAX_FRAME_HEADER],
763 header_len: 0,
764 header_target: 4,
765 frame: FrameState {
766 declared_size: None,
767 has_checksum: false,
768 block_maximum: 0,
769 produced: 0,
770 },
771 blocks_done: false,
772 stream_produced: 0,
773 failed: false,
774 })
775 }
776
777 pub fn reset(&mut self) {
780 self.window = Window::Idle;
781 self.literals.clear();
782 self.staged = 0;
783 self.block_decoder = block_decoder::with_kernel(self.kernel);
784 self.phase = Phase::Magic;
785 self.header_len = 0;
786 self.header_target = 4;
787 self.blocks_done = false;
788 self.stream_produced = 0;
789 self.failed = false;
790 }
791
792 pub fn total_output(&self) -> u64 {
794 self.stream_produced
795 }
796
797 pub fn step(
814 &mut self,
815 input: &[u8],
816 input_is_final: bool,
817 output: &mut [u8],
818 work: &mut WorkBudget,
819 ) -> Result<Progress, WorkspaceDecodeError> {
820 if self.failed {
821 return Err(WorkspaceDecodeError::Failed);
822 }
823 let result = self.step_inner(input, input_is_final, output, work);
824 if result.is_err() {
825 self.failed = true;
826 }
827 result
828 }
829
830 fn step_inner(
831 &mut self,
832 input: &[u8],
833 input_is_final: bool,
834 output: &mut [u8],
835 work: &mut WorkBudget,
836 ) -> Result<Progress, WorkspaceDecodeError> {
837 let mut consumed = 0;
838 let mut produced = 0;
839 loop {
840 produced += self.deliver(&mut output[produced..]);
841 if self.pending() != 0 {
842 return Ok(Progress {
843 consumed,
844 produced,
845 state: StepState::NeedOutput,
846 });
847 }
848 let rest = &input[consumed..];
849 match self.phase {
850 Phase::Skipping { remaining } => {
851 if remaining == 0 {
852 self.expect_frame();
853 continue;
854 }
855 if rest.is_empty() {
856 return self.out_of_input(input_is_final, consumed, produced);
857 }
858 let take = rest
859 .len()
860 .min(usize::try_from(remaining).unwrap_or(usize::MAX));
861 work.charge(take as u64)?;
862 consumed += take;
863 self.phase = Phase::Skipping {
864 remaining: remaining - take as u64,
865 };
866 }
867 Phase::BlockBody(block) if block.block_type == BlockType::Raw => {
868 let remaining = block.content_size as usize - self.staged;
872 if remaining == 0 {
873 self.finish_block(block)?;
874 continue;
875 }
876 if rest.is_empty() {
877 return self.out_of_input(input_is_final, consumed, produced);
878 }
879 let take = rest.len().min(remaining);
880 work.charge(2 * take as u64)?;
882 self.push_raw(&rest[..take])?;
883 self.staged += take;
884 consumed += take;
885 }
886 Phase::BlockBody(block) => {
887 let size = block.content_size as usize;
888 if self.staged == 0 && rest.len() >= size {
889 work.charge(size as u64)?;
890 consumed += size;
891 self.decode_block(block, &rest[..size], work)?;
892 continue;
893 }
894 if rest.is_empty() {
895 return self.out_of_input(input_is_final, consumed, produced);
896 }
897 let take = rest.len().min(size - self.staged);
898 work.charge(take as u64)?;
899 self.staging[self.staged..self.staged + take].copy_from_slice(&rest[..take]);
900 self.staged += take;
901 consumed += take;
902 if self.staged == size {
903 self.staged = 0;
904 let staging = core::mem::take(&mut self.staging);
908 let decoded = self.decode_block(block, &staging[..size], work);
909 self.staging = staging;
910 decoded?;
911 }
912 }
913 _ => {
914 if self.header_len < self.header_target {
915 if rest.is_empty() {
916 return self.out_of_input(input_is_final, consumed, produced);
917 }
918 let take = rest.len().min(self.header_target - self.header_len);
919 work.charge(take as u64)?;
920 self.header[self.header_len..self.header_len + take]
921 .copy_from_slice(&rest[..take]);
922 self.header_len += take;
923 consumed += take;
924 }
925 if self.header_len == self.header_target {
926 self.header_complete()?;
927 }
928 }
929 }
930 }
931 }
932
933 fn out_of_input(
935 &self,
936 input_is_final: bool,
937 consumed: usize,
938 produced: usize,
939 ) -> Result<Progress, WorkspaceDecodeError> {
940 if !input_is_final {
941 return Ok(Progress {
942 consumed,
943 produced,
944 state: StepState::NeedInput,
945 });
946 }
947 if self.phase == Phase::Magic && self.header_len == 0 {
948 return Ok(Progress {
949 consumed,
950 produced,
951 state: StepState::Done,
952 });
953 }
954 Err(WorkspaceDecodeError::Truncated)
955 }
956
957 fn pending(&self) -> usize {
960 let blocks_done = self.blocks_done;
961 with_window!(&self.window, buffer => if blocks_done {
962 buffer.can_drain()
963 } else {
964 buffer.can_drain_to_window_size().unwrap_or(0)
965 }, idle => 0)
966 }
967
968 fn deliver(&mut self, output: &mut [u8]) -> usize {
969 if output.is_empty() {
970 return 0;
971 }
972 let blocks_done = self.blocks_done;
973 let drained = with_window!(&mut self.window, buffer => if blocks_done {
974 buffer.read_all(output)
975 } else {
976 buffer.read_reporting_pending(output).map(|(read, _)| read)
977 }, idle => Ok(0));
978 drained.unwrap_or(0)
980 }
981
982 fn expect_frame(&mut self) {
983 self.phase = Phase::Magic;
984 self.header_len = 0;
985 self.header_target = 4;
986 }
987
988 fn expect_header(&mut self, phase: Phase, target: usize) {
989 self.phase = phase;
990 self.header_target = target;
991 }
992
993 fn header_complete(&mut self) -> Result<(), WorkspaceDecodeError> {
995 use WorkspaceDecodeError::Malformed;
996 match self.phase {
997 Phase::Magic => {
998 let magic = u32::from_le_bytes([
999 self.header[0],
1000 self.header[1],
1001 self.header[2],
1002 self.header[3],
1003 ]);
1004 if (0x184D2A50..=0x184D2A5F).contains(&magic) {
1005 self.expect_header(Phase::SkippableSize, 8);
1006 } else if magic == MAGIC_NUM {
1007 self.expect_header(Phase::Descriptor, 5);
1008 } else {
1009 return Err(Malformed(MalformedInput::FrameHeader(
1010 ReadFrameHeaderError::BadMagicNumber(magic),
1011 )));
1012 }
1013 }
1014 Phase::SkippableSize => {
1015 let length = u32::from_le_bytes([
1016 self.header[4],
1017 self.header[5],
1018 self.header[6],
1019 self.header[7],
1020 ]);
1021 self.phase = Phase::Skipping {
1022 remaining: u64::from(length),
1023 };
1024 }
1025 Phase::Descriptor => {
1026 let length = frame::measure_header_from_slice(&self.header[..5])
1027 .map_err(|e| Malformed(MalformedInput::FrameHeader(e)))?;
1028 self.expect_header(Phase::FrameHeader, length);
1029 }
1030 Phase::FrameHeader => self.start_frame()?,
1031 Phase::BlockHeader => self.start_block()?,
1032 Phase::Checksum => {
1033 self.check_checksum()?;
1034 self.expect_frame();
1035 }
1036 Phase::Skipping { .. } | Phase::BlockBody(_) => {
1037 unreachable!("phases without a header buffer are handled by the step loop")
1038 }
1039 }
1040 Ok(())
1041 }
1042
1043 fn start_frame(&mut self) -> Result<(), WorkspaceDecodeError> {
1044 use WorkspaceDecodeError::{Limit, Malformed};
1045 let mut source = &self.header[..self.header_target];
1046 let (header, _) = frame::read_frame_header_from_slice(&mut source, false)
1047 .map_err(|e| Malformed(MalformedInput::FrameHeader(e)))?;
1048 if let Some(id) = header.dictionary_id() {
1049 return Err(WorkspaceDecodeError::Unsupported(Unsupported::Dictionary {
1050 id,
1051 }));
1052 }
1053 let window = header
1054 .window_size()
1055 .map_err(|e| Malformed(MalformedInput::Window(e)))?;
1056 if window > self.limits.max_window_size {
1057 return Err(Limit(LimitExceeded::Window {
1058 requested: window,
1059 limit: self.limits.max_window_size,
1060 }));
1061 }
1062 let declared_size = header.fcs_declared().then(|| header.frame_content_size());
1063 if let Some(declared) = declared_size
1064 && self
1065 .stream_produced
1066 .checked_add(declared)
1067 .is_none_or(|total| total > self.limits.max_output)
1068 {
1069 return Err(Limit(LimitExceeded::Output {
1070 limit: self.limits.max_output,
1071 }));
1072 }
1073 let window = window as usize;
1075 let has_checksum = header.descriptor.content_checksum_flag();
1076
1077 self.window = Window::Idle;
1080 self.window = if header.descriptor.single_segment_flag() {
1081 let region =
1085 unsafe { core::slice::from_raw_parts_mut(self.region.as_ptr(), self.region_len) };
1086 Window::Linear(DecodeBuffer::from_backend(
1087 UserSliceBackend::from_slice(region),
1088 window,
1089 ))
1090 } else {
1091 let ring = unsafe { RingBuffer::from_borrowed(self.region, self.ring_cap) };
1095 Window::Ring(DecodeBuffer::from_backend(ring, window))
1096 };
1097 with_window!(&mut self.window, buffer => {
1098 buffer.set_declared_content(declared_size);
1099 #[cfg(feature = "hash")]
1100 buffer.set_compute_hash(has_checksum);
1101 }, idle => {});
1102 self.offset_hist = [1, 4, 8];
1103 self.literals.clear();
1104 super::scratch::reset_entropy(self.huf, self.fse);
1105
1106 self.frame = FrameState {
1107 declared_size,
1108 has_checksum,
1109 block_maximum: block_decoder::block_maximum(window),
1110 produced: 0,
1111 };
1112 self.blocks_done = false;
1113 self.header_len = 0;
1114 self.expect_header(Phase::BlockHeader, 3);
1115 Ok(())
1116 }
1117
1118 fn start_block(&mut self) -> Result<(), WorkspaceDecodeError> {
1119 use WorkspaceDecodeError::Malformed;
1120 let (header, _) = self
1121 .block_decoder
1122 .read_block_header(&self.header[..3])
1123 .map_err(|e| Malformed(MalformedInput::BlockHeader(e)))?;
1124 let size = match header.block_type {
1127 BlockType::RLE => header.decompressed_size,
1128 _ => header.content_size,
1129 } as usize;
1130 let maximum = self.frame.block_maximum;
1131 if size > maximum {
1132 return Err(Malformed(MalformedInput::BlockTooLarge { size, maximum }));
1133 }
1134 self.header_len = 0;
1135 self.staged = 0;
1136 self.phase = Phase::BlockBody(PendingBlock {
1137 last_block: header.last_block,
1138 block_type: header.block_type,
1139 decompressed_size: header.decompressed_size,
1140 content_size: header.content_size,
1141 });
1142 Ok(())
1143 }
1144
1145 fn decode_block(
1148 &mut self,
1149 block: PendingBlock,
1150 content: &[u8],
1151 work: &mut WorkBudget,
1152 ) -> Result<(), WorkspaceDecodeError> {
1153 use WorkspaceDecodeError::{Limit, Malformed};
1154 let output_bound = match block.block_type {
1155 BlockType::Compressed => self.frame.block_maximum as u64 + COMPRESSED_BLOCK_UNITS,
1156 _ => u64::from(block.decompressed_size),
1157 };
1158 work.charge(output_bound)?;
1159 let output_limit = Limit(LimitExceeded::Output {
1160 limit: self.limits.max_output,
1161 });
1162 if block.block_type != BlockType::Compressed
1163 && self
1164 .stream_produced
1165 .checked_add(u64::from(block.decompressed_size))
1166 .is_none_or(|total| total > self.limits.max_output)
1167 {
1168 return Err(output_limit);
1169 }
1170
1171 let header = block.header();
1172 let produced = with_window!(&mut self.window, buffer => run_block(
1173 &mut self.block_decoder,
1174 &mut BlockScratch {
1175 huf: &mut *self.huf,
1176 fse: &mut *self.fse,
1177 buffer,
1178 offset_hist: &mut self.offset_hist,
1179 literals: &mut self.literals,
1180 block_content: &mut self.block_content,
1181 },
1182 &header,
1183 content,
1184 ), idle => unreachable!("a block is decoded only inside a started frame"))
1185 .map_err(|e| Malformed(MalformedInput::Block(e)))?;
1186
1187 self.stream_produced += produced;
1188 self.frame.produced += produced;
1189 if self.stream_produced > self.limits.max_output {
1190 return Err(output_limit);
1191 }
1192 if let Some(declared) = self.frame.declared_size
1193 && self.frame.produced > declared
1194 {
1195 return Err(Malformed(MalformedInput::ContentSize {
1196 declared,
1197 produced: self.frame.produced,
1198 }));
1199 }
1200 self.finish_block(block)
1201 }
1202
1203 fn push_raw(&mut self, bytes: &[u8]) -> Result<(), WorkspaceDecodeError> {
1206 use crate::cpu_kernel::{BaselineKernel, CpuKernelTag, ScalarKernel};
1207 use WorkspaceDecodeError::{Limit, Malformed};
1208 let len = bytes.len() as u64;
1209 let stream_total = self.stream_produced + len;
1210 if stream_total > self.limits.max_output {
1211 return Err(Limit(LimitExceeded::Output {
1212 limit: self.limits.max_output,
1213 }));
1214 }
1215 let frame_total = self.frame.produced + len;
1216 if let Some(declared) = self.frame.declared_size
1217 && frame_total > declared
1218 {
1219 return Err(Malformed(MalformedInput::ContentSize {
1220 declared,
1221 produced: frame_total,
1222 }));
1223 }
1224 let scalar = self.kernel == CpuKernelTag::Scalar;
1227 let pushed = with_window!(&mut self.window, buffer => if scalar {
1228 buffer.try_push::<ScalarKernel>(bytes)
1229 } else {
1230 buffer.try_push::<BaselineKernel>(bytes)
1231 }, idle => unreachable!("a block is decoded only inside a started frame"));
1232 pushed.map_err(|_| {
1236 Malformed(MalformedInput::Block(
1237 DecodeBlockContentError::BackendOverflow {
1238 step: BlockType::Raw,
1239 },
1240 ))
1241 })?;
1242 self.stream_produced = stream_total;
1243 self.frame.produced = frame_total;
1244 Ok(())
1245 }
1246
1247 fn finish_block(&mut self, block: PendingBlock) -> Result<(), WorkspaceDecodeError> {
1250 if let Some(declared) = self.frame.declared_size
1251 && block.last_block
1252 && self.frame.produced != declared
1253 {
1254 return Err(WorkspaceDecodeError::Malformed(
1255 MalformedInput::ContentSize {
1256 declared,
1257 produced: self.frame.produced,
1258 },
1259 ));
1260 }
1261 self.staged = 0;
1262 self.header_len = 0;
1263 if !block.last_block {
1264 self.expect_header(Phase::BlockHeader, 3);
1265 } else {
1266 self.blocks_done = true;
1267 if self.frame.has_checksum {
1268 self.expect_header(Phase::Checksum, 4);
1269 } else {
1270 self.expect_frame();
1271 }
1272 }
1273 Ok(())
1274 }
1275
1276 fn check_checksum(&mut self) -> Result<(), WorkspaceDecodeError> {
1281 #[cfg(feature = "hash")]
1282 {
1283 use core::hash::Hasher;
1284 let expected = u32::from_le_bytes([
1285 self.header[0],
1286 self.header[1],
1287 self.header[2],
1288 self.header[3],
1289 ]);
1290 let calculated =
1291 with_window!(&self.window, buffer => buffer.hash.finish(), idle => 0) as u32;
1292 if expected != calculated {
1293 return Err(WorkspaceDecodeError::Malformed(MalformedInput::Checksum {
1294 expected,
1295 calculated,
1296 }));
1297 }
1298 }
1299 Ok(())
1300 }
1301}
1302
1303#[cfg(test)]
1304mod tests;