1mod entropy;
13mod v05;
14mod v06;
15mod v07;
16
17#[cfg(feature = "hash")]
18use crate::decoding::ContentChecksum;
19use crate::decoding::Dictionary;
20use crate::decoding::FrameSizeError;
21use crate::decoding::errors::LegacyFrameError as Error;
22use alloc::vec::Vec;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26#[non_exhaustive]
27pub enum LegacyVersion {
28 V05,
30 V06,
32 V07,
34}
35
36impl LegacyVersion {
37 pub fn of(src: &[u8]) -> Option<LegacyVersion> {
39 let magic = u32::from_le_bytes(src.get(..4)?.try_into().ok()?);
40 match magic {
41 0xFD2F_B525 => Some(LegacyVersion::V05),
42 0xFD2F_B526 => Some(LegacyVersion::V06),
43 0xFD2F_B527 => Some(LegacyVersion::V07),
44 _ => None,
45 }
46 }
47
48 pub fn minor(self) -> u8 {
50 match self {
51 LegacyVersion::V05 => 5,
52 LegacyVersion::V06 => 6,
53 LegacyVersion::V07 => 7,
54 }
55 }
56
57 pub fn header_size(self, src: &[u8]) -> Option<usize> {
74 header_len(self, src).ok()
75 }
76}
77
78const BLOCK_SIZE_MAX: usize = 128 * 1024;
80
81const STORED_BLOCK_MAX: usize = (1 << 19) - 1;
85
86#[derive(Debug, Clone, Copy)]
88struct BlockLimits {
89 compressed: usize,
90 stored: usize,
91}
92
93impl BlockLimits {
94 fn of(block_max: usize) -> BlockLimits {
98 if block_max >= BLOCK_SIZE_MAX {
99 BlockLimits {
100 compressed: BLOCK_SIZE_MAX,
101 stored: STORED_BLOCK_MAX,
102 }
103 } else {
104 BlockLimits {
105 compressed: block_max,
106 stored: block_max,
107 }
108 }
109 }
110
111 fn of_block(self, block: Block) -> usize {
113 match block {
114 Block::Compressed(_) => self.compressed,
115 Block::Raw(_) | Block::Rle(_) | Block::End => self.stored,
116 }
117 }
118}
119
120fn corrupt(reason: &'static str) -> Error {
121 Error::Corrupted { reason }
122}
123
124pub(crate) fn is_older_dictionary(raw: &[u8]) -> bool {
130 raw.first_chunk::<4>().is_some_and(|magic| {
131 matches!(
132 u32::from_le_bytes(*magic),
133 v05::DICT_MAGIC | v06::DICT_MAGIC
134 )
135 })
136}
137
138#[derive(Clone, Copy)]
140pub(crate) enum LegacyDict<'a> {
141 None,
142 Parsed {
148 dict: &'a Dictionary,
149 header: &'a [u8],
150 },
151}
152
153impl<'a> LegacyDict<'a> {
154 pub(crate) fn of(handle: &'a crate::decoding::DictionaryHandle) -> LegacyDict<'a> {
156 LegacyDict::Parsed {
157 dict: handle.as_dict(),
158 header: handle.serialized_header(),
159 }
160 }
161
162 fn pieces(&self) -> (&'a [u8], &'a [u8]) {
165 match *self {
166 LegacyDict::None => (&[], &[]),
167 LegacyDict::Parsed { dict, header } => (header, &dict.dict_content),
168 }
169 }
170}
171
172#[derive(Clone, Copy)]
175pub(crate) struct History<'a> {
176 head: &'a [u8],
177 tail: &'a [u8],
178}
179
180#[derive(Clone)]
182enum HistoryShape {
183 Content(core::ops::Range<usize>),
185 Whole,
188}
189
190impl HistoryShape {
191 fn of<'a>(&self, dict: LegacyDict<'a>) -> History<'a> {
192 let (head, tail) = dict.pieces();
193 match self {
194 HistoryShape::Content(range) => History {
195 head: &[],
196 tail: &tail[range.clone()],
197 },
198 HistoryShape::Whole => History { head, tail },
199 }
200 }
201}
202
203impl History<'_> {
204 fn len(&self) -> usize {
205 self.head.len() + self.tail.len()
206 }
207
208 fn copy_out<O: Output>(&self, from: usize, len: usize, out: &mut O) {
211 debug_assert!(from + len <= self.len());
212 if from >= self.head.len() {
213 let start = from - self.head.len();
214 out.extend(&self.tail[start..start + len]);
215 return;
216 }
217 let in_head = (self.head.len() - from).min(len);
218 out.extend(&self.head[from..from + in_head]);
219 if len > in_head {
220 out.extend(&self.tail[..len - in_head]);
221 }
222 }
223}
224
225#[derive(Debug, Clone, Copy)]
227pub(crate) struct FrameHeader {
228 pub(crate) version: LegacyVersion,
229 pub(crate) size: usize,
231 pub(crate) window: u64,
233 pub(crate) content_size: Option<u64>,
235 pub(crate) dict_id: u32,
237 pub(crate) checksum: bool,
239}
240
241pub(crate) fn read_header(src: &[u8]) -> Result<FrameHeader, Error> {
243 match LegacyVersion::of(src).ok_or(corrupt("not a v0.5-v0.7 frame"))? {
244 LegacyVersion::V05 => v05::read_header(src),
245 LegacyVersion::V06 => v06::read_header(src),
246 LegacyVersion::V07 => v07::read_header(src),
247 }
248}
249
250pub(crate) fn read_header_whole(src: &[u8]) -> Result<FrameHeader, Error> {
257 match read_header(src) {
258 Err(Error::UnsupportedParameter)
259 if LegacyVersion::of(src).is_some_and(|version| version != LegacyVersion::V05) =>
260 {
261 Err(corrupt("frame header"))
262 }
263 result => result,
264 }
265}
266
267pub(crate) fn declared_content_size(src: &[u8]) -> Option<u64> {
271 read_header(src).ok()?.content_size
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq)]
276enum Block {
277 Compressed(usize),
278 Raw(usize),
279 Rle(usize),
281 End,
282}
283
284impl Block {
285 fn read(src: &[u8]) -> Result<Block, Error> {
286 let header = src.get(..3).ok_or(Error::Truncated)?;
287 let size =
288 usize::from(header[2]) | usize::from(header[1]) << 8 | usize::from(header[0] & 7) << 16;
289 Ok(match header[0] >> 6 {
290 0 => Block::Compressed(size),
291 1 => Block::Raw(size),
292 2 => Block::Rle(size),
293 _ => Block::End,
294 })
295 }
296
297 fn body_len(self) -> usize {
299 match self {
300 Block::Compressed(size) | Block::Raw(size) => size,
301 Block::Rle(_) => 1,
302 Block::End => 0,
303 }
304 }
305
306 fn check(self, version: LegacyVersion, limits: BlockLimits) -> Result<(), Error> {
310 let size = match self {
311 Block::Rle(_) if version != LegacyVersion::V07 => {
312 return Err(corrupt("reserved block type"));
313 }
314 Block::Compressed(size) | Block::Raw(size) | Block::Rle(size) => size,
315 Block::End => 0,
316 };
317 if size > limits.of_block(self) {
318 return Err(corrupt("block larger than the block maximum"));
319 }
320 Ok(())
321 }
322
323 fn decoded_max(self) -> usize {
326 match self {
327 Block::Compressed(_) => BLOCK_SIZE_MAX,
328 Block::Raw(size) | Block::Rle(size) => size,
329 Block::End => 0,
330 }
331 }
332}
333
334fn ends_frame(version: LegacyVersion, block: Block) -> bool {
338 match version {
339 LegacyVersion::V07 => block == Block::End,
340 LegacyVersion::V05 | LegacyVersion::V06 => block.body_len() == 0,
341 }
342}
343
344pub(crate) fn header_len(version: LegacyVersion, src: &[u8]) -> Result<usize, Error> {
348 let descriptor = *src.get(4).ok_or(Error::Truncated)?;
349 let fields = HeaderFields::of(version, descriptor);
350 Ok(5 + fields.window + fields.dict_id + fields.content_size)
351}
352
353impl FrameHeader {
354 #[cfg(feature = "lsm")]
359 pub(crate) fn window_descriptor(&self, src: &[u8]) -> Option<u8> {
360 let fields = HeaderFields::of(self.version, src[4]);
361 (fields.window == 1).then(|| src[5])
362 }
363
364 #[cfg(feature = "lsm")]
366 pub(crate) fn dictionary_id(&self) -> Option<u32> {
367 (self.dict_id != 0).then_some(self.dict_id)
368 }
369}
370
371#[derive(Debug, Clone, Copy)]
374pub(crate) struct HeaderFields {
375 pub(crate) window: usize,
376 pub(crate) dict_id: usize,
377 pub(crate) content_size: usize,
378}
379
380impl HeaderFields {
381 pub(crate) fn of(version: LegacyVersion, descriptor: u8) -> HeaderFields {
382 match version {
383 LegacyVersion::V05 => HeaderFields {
384 window: 0,
385 dict_id: 0,
386 content_size: 0,
387 },
388 LegacyVersion::V06 => HeaderFields {
389 window: 0,
390 dict_id: 0,
391 content_size: [0, 1, 2, 8][usize::from(descriptor >> 6)],
392 },
393 LegacyVersion::V07 => {
394 let single_segment = descriptor & 0x20 != 0;
395 let fcs = [0, 2, 4, 8][usize::from(descriptor >> 6)];
396 HeaderFields {
397 window: usize::from(!single_segment),
398 dict_id: [0, 1, 2, 4][usize::from(descriptor & 3)],
399 content_size: fcs + usize::from(single_segment && fcs == 0),
402 }
403 }
404 }
405 }
406}
407
408pub(crate) fn frame_size(
420 version: LegacyVersion,
421 src: &[u8],
422) -> Result<(usize, u64), FrameSizeError> {
423 let mut at = header_len(version, src).map_err(|_| FrameSizeError::Truncated)?;
424 let mut bound = 0u64;
425 loop {
426 let rest = src.get(at..).ok_or(FrameSizeError::Truncated)?;
427 let block = Block::read(rest).map_err(|_| FrameSizeError::Truncated)?;
428 if block
429 .check(version, BlockLimits::of(BLOCK_SIZE_MAX))
430 .is_err()
431 {
432 return Err(match block {
434 Block::Rle(_) => FrameSizeError::ReservedBlock,
435 _ => FrameSizeError::OversizedBlock,
436 });
437 }
438 at += 3;
439 let body = block.body_len();
440 if body > src.len() - at {
441 return Err(FrameSizeError::Truncated);
442 }
443 if ends_frame(version, block) {
444 break;
445 }
446 at += body;
447 bound += block.decoded_max() as u64;
448 }
449 Ok((at, bound))
450}
451
452pub(crate) trait Output {
455 fn produced(&self) -> usize;
457 fn held(&self) -> usize;
460 fn reserve(&mut self, len: usize) -> Result<(), Error>;
462 fn extend(&mut self, bytes: &[u8]);
463 fn repeat(&mut self, byte: u8, len: usize);
464 fn copy_back(&mut self, offset: usize, len: usize);
467 #[cfg(feature = "hash")]
470 fn since(&self, from: usize) -> &[u8];
471 fn copy_match(&mut self, offset: usize, len: usize, dict: &History<'_>) -> Result<(), Error>
475 where
476 Self: Sized,
477 {
478 copy_match(self, offset, len, dict)
479 }
480}
481
482pub(crate) struct VecOutput<'a> {
484 pub(crate) buf: &'a mut Vec<u8>,
485 pub(crate) start: usize,
486}
487
488impl Output for VecOutput<'_> {
489 fn produced(&self) -> usize {
490 self.held()
491 }
492
493 fn held(&self) -> usize {
494 self.buf.len() - self.start
495 }
496
497 fn reserve(&mut self, len: usize) -> Result<(), Error> {
498 self.buf.reserve(len);
500 Ok(())
501 }
502
503 fn extend(&mut self, bytes: &[u8]) {
504 self.buf.extend_from_slice(bytes);
505 }
506
507 fn repeat(&mut self, byte: u8, len: usize) {
508 self.buf.resize(self.buf.len() + len, byte);
509 }
510
511 fn copy_back(&mut self, offset: usize, len: usize) {
512 let from = self.buf.len() - offset;
516 let mut left = len;
517 while left > 0 {
518 let piece = left.min(self.buf.len() - from);
519 self.buf.extend_from_within(from..from + piece);
520 left -= piece;
521 }
522 }
523
524 #[cfg(feature = "hash")]
525 fn since(&self, from: usize) -> &[u8] {
526 &self.buf[self.start + from..]
527 }
528}
529
530fn copy_repeating(buf: &mut [u8], from: usize, to: usize, len: usize) {
535 debug_assert!(from < to);
536 let mut done = 0;
537 while done < len {
538 let piece = (to + done - from).min(len - done);
539 buf.copy_within(from..from + piece, to + done);
540 done += piece;
541 }
542}
543
544pub(crate) struct SliceOutput<'a> {
546 pub(crate) buf: &'a mut [u8],
547 pub(crate) pos: usize,
548}
549
550impl Output for SliceOutput<'_> {
551 fn produced(&self) -> usize {
552 self.pos
553 }
554
555 fn held(&self) -> usize {
556 self.pos
557 }
558
559 fn reserve(&mut self, len: usize) -> Result<(), Error> {
560 if len > self.buf.len() - self.pos {
561 return Err(Error::OutputTooSmall);
562 }
563 Ok(())
564 }
565
566 fn extend(&mut self, bytes: &[u8]) {
567 self.buf[self.pos..self.pos + bytes.len()].copy_from_slice(bytes);
568 self.pos += bytes.len();
569 }
570
571 fn repeat(&mut self, byte: u8, len: usize) {
572 self.buf[self.pos..self.pos + len].fill(byte);
573 self.pos += len;
574 }
575
576 fn copy_back(&mut self, offset: usize, len: usize) {
577 copy_repeating(self.buf, self.pos - offset, self.pos, len);
578 self.pos += len;
579 }
580
581 #[cfg(feature = "hash")]
582 fn since(&self, from: usize) -> &[u8] {
583 &self.buf[from..self.pos]
584 }
585}
586
587fn copy_match<O: Output>(
598 out: &mut O,
599 offset: usize,
600 len: usize,
601 dict: &History<'_>,
602) -> Result<(), Error> {
603 debug_assert_eq!(out.held(), out.produced());
606 debug_assert!(offset >= 1);
610 let held = out.held();
611 if offset <= held {
612 out.copy_back(offset, len);
613 return Ok(());
614 }
615 let back = offset - held;
616 if back > dict.len() {
617 return Err(corrupt("match reaches before the dictionary"));
618 }
619 let first = len.min(back);
620 dict.copy_out(dict.len() - back, first, out);
621 let rest = len - first;
622 if rest > 0 {
623 let distance = out.held();
625 out.copy_back(distance, rest);
626 }
627 Ok(())
628}
629
630fn execute<O: Output>(
633 out: &mut O,
634 literals: &[u8],
635 lit_pos: &mut usize,
636 lit_len: usize,
637 match_len: usize,
638 offset: usize,
639 dict: &History<'_>,
640) -> Result<(), Error> {
641 out.reserve(
642 lit_len
643 .checked_add(match_len)
644 .ok_or(Error::OutputTooSmall)?,
645 )?;
646 let lits = literals
647 .get(*lit_pos..)
648 .and_then(|rest| rest.get(..lit_len))
649 .ok_or(corrupt("sequence takes more literals than the block has"))?;
650 out.extend(lits);
651 *lit_pos += lit_len;
652 out.copy_match(offset, match_len, dict)
653}
654
655enum Literals {
657 Huffman {
660 header: usize,
661 size: usize,
662 compressed: usize,
663 single_stream: bool,
664 },
665 Repeat {
667 size: usize,
668 compressed: usize,
669 },
670 Raw {
671 header: usize,
672 size: usize,
673 },
674 Rle {
675 header: usize,
676 size: usize,
677 },
678}
679
680impl Literals {
681 fn read(src: &[u8]) -> Result<Literals, Error> {
684 if src.len() < 3 {
687 return Err(corrupt("compressed block too short"));
688 }
689 let b0 = usize::from(src[0]);
690 let b1 = usize::from(src[1]);
691 let b2 = usize::from(src[2]);
692 let size_format = (b0 >> 4) & 3;
693 Ok(match b0 >> 6 {
694 0 => {
695 if src.len() < 5 {
696 return Err(corrupt("compressed block too short"));
697 }
698 let b3 = usize::from(src[3]);
699 let b4 = usize::from(src[4]);
700 let (header, size, compressed, single_stream) = match size_format {
701 0 | 1 => (
702 3,
703 ((b0 & 15) << 6) + (b1 >> 2),
704 ((b1 & 3) << 8) + b2,
705 b0 & 16 != 0,
706 ),
707 2 => (
708 4,
709 ((b0 & 15) << 10) + (b1 << 2) + (b2 >> 6),
710 ((b2 & 63) << 8) + b3,
711 false,
712 ),
713 _ => (
714 5,
715 ((b0 & 15) << 14) + (b1 << 6) + (b2 >> 2),
716 ((b2 & 3) << 16) + (b3 << 8) + b4,
717 false,
718 ),
719 };
720 if size > BLOCK_SIZE_MAX {
721 return Err(corrupt("literal section too large"));
722 }
723 if compressed + header > src.len() {
724 return Err(corrupt("literals run past the block"));
725 }
726 Literals::Huffman {
727 header,
728 size,
729 compressed,
730 single_stream,
731 }
732 }
733 1 => {
734 if size_format != 1 {
736 return Err(corrupt("repeated-table literals in an unknown form"));
737 }
738 let size = ((b0 & 15) << 6) + (b1 >> 2);
739 let compressed = ((b1 & 3) << 8) + b2;
740 if compressed + 3 > src.len() {
741 return Err(corrupt("literals run past the block"));
742 }
743 Literals::Repeat { size, compressed }
744 }
745 kind => {
746 let (header, size) = match size_format {
747 0 | 1 => (1, b0 & 31),
748 2 => (2, ((b0 & 15) << 8) + b1),
749 _ => (3, ((b0 & 15) << 16) + (b1 << 8) + b2),
750 };
751 if kind == 2 {
752 if size + header > src.len() {
753 return Err(corrupt("literals run past the block"));
754 }
755 Literals::Raw { header, size }
756 } else {
757 if header == 3 && src.len() < 4 {
758 return Err(corrupt("compressed block too short"));
759 }
760 if size > BLOCK_SIZE_MAX {
761 return Err(corrupt("literal section too large"));
762 }
763 Literals::Rle { header, size }
764 }
765 }
766 })
767 }
768}
769
770enum LiteralSource {
772 Block(core::ops::Range<usize>),
774 Buffer,
776}
777
778enum VersionDecoder {
781 V05(v05::Decoder),
782 V06(v06::Decoder),
783 V07(v07::Decoder),
784}
785
786impl VersionDecoder {
787 fn version(&self) -> LegacyVersion {
788 match self {
789 VersionDecoder::V05(_) => LegacyVersion::V05,
790 VersionDecoder::V06(_) => LegacyVersion::V06,
791 VersionDecoder::V07(_) => LegacyVersion::V07,
792 }
793 }
794
795 fn heap_bytes(&self) -> usize {
796 match self {
797 VersionDecoder::V05(decoder) => decoder.heap_bytes(),
798 VersionDecoder::V06(decoder) => decoder.heap_bytes(),
799 VersionDecoder::V07(decoder) => decoder.heap_bytes(),
800 }
801 }
802}
803
804#[derive(Default)]
807struct Workspace {
808 decoder: Option<VersionDecoder>,
809 #[cfg(all(feature = "lsm", feature = "hash"))]
811 block_digests: Vec<u32>,
812}
813
814impl Workspace {
815 fn heap_bytes(&self) -> usize {
816 let bytes = self.decoder.as_ref().map_or(0, VersionDecoder::heap_bytes);
817 #[cfg(all(feature = "lsm", feature = "hash"))]
818 let bytes = bytes + self.block_digests.capacity() * core::mem::size_of::<u32>();
819 bytes
820 }
821}
822
823#[derive(Debug, Clone, Copy)]
825pub(crate) struct FrameOptions {
826 #[cfg(feature = "hash")]
828 pub(crate) checksum: ContentChecksum,
829 pub(crate) block_max: usize,
832 #[cfg(all(feature = "lsm", feature = "hash"))]
835 pub(crate) block_digests: bool,
836}
837
838impl Default for FrameOptions {
839 fn default() -> FrameOptions {
840 FrameOptions {
841 #[cfg(feature = "hash")]
842 checksum: ContentChecksum::EmitOnly,
843 block_max: BLOCK_SIZE_MAX,
844 #[cfg(all(feature = "lsm", feature = "hash"))]
845 block_digests: false,
846 }
847 }
848}
849
850struct Budget<'o, O: Output> {
854 out: &'o mut O,
855 end: usize,
856}
857
858impl<O: Output> Output for Budget<'_, O> {
859 fn produced(&self) -> usize {
860 self.out.produced()
861 }
862 fn held(&self) -> usize {
863 self.out.held()
864 }
865 fn reserve(&mut self, len: usize) -> Result<(), Error> {
866 if len > self.end - self.out.produced() {
867 return Err(corrupt("block decodes past the largest block size"));
868 }
869 self.out.reserve(len)
870 }
871 fn extend(&mut self, bytes: &[u8]) {
872 self.out.extend(bytes);
873 }
874 fn repeat(&mut self, byte: u8, len: usize) {
875 self.out.repeat(byte, len);
876 }
877 fn copy_back(&mut self, offset: usize, len: usize) {
878 self.out.copy_back(offset, len);
879 }
880 #[cfg(feature = "hash")]
881 fn since(&self, from: usize) -> &[u8] {
882 self.out.since(from)
883 }
884 fn copy_match(&mut self, offset: usize, len: usize, dict: &History<'_>) -> Result<(), Error> {
885 self.out.copy_match(offset, len, dict)
886 }
887}
888
889struct LegacyFrame {
895 version: LegacyVersion,
896 dict_id: u32,
898 history: HistoryShape,
900 limits: BlockLimits,
902 #[cfg(feature = "hash")]
903 checksum_mode: ContentChecksum,
904 content_size: u64,
906 blocks: usize,
908 has_checksum: bool,
910 stored_checksum: Option<u32>,
912 #[cfg(feature = "hash")]
915 calculated_checksum: Option<u32>,
916 #[cfg(feature = "hash")]
917 hash: Option<twox_hash::XxHash64>,
918 #[cfg(all(feature = "lsm", feature = "hash"))]
919 block_digests: bool,
920}
921
922impl LegacyFrame {
923 fn new(
926 header: FrameHeader,
927 dict: LegacyDict<'_>,
928 options: FrameOptions,
929 workspace: &mut Workspace,
930 ) -> Result<LegacyFrame, Error> {
931 #[cfg(all(feature = "lsm", feature = "hash"))]
932 workspace.block_digests.clear();
933 let decoder = &mut workspace.decoder;
934 if decoder.as_ref().map(VersionDecoder::version) != Some(header.version) {
935 *decoder = Some(match header.version {
936 LegacyVersion::V05 => VersionDecoder::V05(v05::Decoder::empty()),
937 LegacyVersion::V06 => VersionDecoder::V06(v06::Decoder::empty()),
938 LegacyVersion::V07 => VersionDecoder::V07(v07::Decoder::empty()),
939 });
940 }
941 let history = prime(header.dict_id, dict, workspace)?;
942 #[cfg(feature = "hash")]
945 let hash = (header.checksum && options.checksum != ContentChecksum::None)
946 .then(|| twox_hash::XxHash64::with_seed(0));
947 Ok(LegacyFrame {
948 version: header.version,
949 dict_id: header.dict_id,
950 history,
951 limits: BlockLimits::of(options.block_max),
952 #[cfg(feature = "hash")]
953 checksum_mode: options.checksum,
954 content_size: header.content_size.unwrap_or(0),
955 blocks: 0,
956 has_checksum: header.checksum,
957 stored_checksum: None,
958 #[cfg(feature = "hash")]
959 calculated_checksum: None,
960 #[cfg(feature = "hash")]
961 hash,
962 #[cfg(all(feature = "lsm", feature = "hash"))]
963 block_digests: options.block_digests,
964 })
965 }
966
967 fn set_dict(&mut self, dict: LegacyDict<'_>, workspace: &mut Workspace) -> Result<(), Error> {
972 self.history = prime(self.dict_id, dict, workspace)?;
973 Ok(())
974 }
975
976 #[cfg(feature = "hash")]
980 fn set_checksum_mode(&mut self, mode: ContentChecksum) {
981 self.checksum_mode = mode;
982 if mode == ContentChecksum::None {
983 self.hash = None;
984 } else if self.hash.is_none() && self.has_checksum && self.blocks == 0 {
985 self.hash = Some(twox_hash::XxHash64::with_seed(0));
986 }
987 }
988
989 fn block<O: Output>(
993 &mut self,
994 workspace: &mut Workspace,
995 block: Block,
996 body: &[u8],
997 out: &mut O,
998 dict: LegacyDict<'_>,
999 ) -> Result<(), Error> {
1000 let decoder = workspace.decoder.as_mut().expect("readied with the frame");
1001 let before = out.produced();
1002 let mut out = Budget {
1003 end: before + self.limits.of_block(block),
1004 out,
1005 };
1006 let dict = self.history.of(dict);
1007 match block {
1008 Block::Compressed(_) => match decoder {
1009 VersionDecoder::V05(decoder) => decoder.block(body, &mut out, dict)?,
1010 VersionDecoder::V06(decoder) => decoder.block(body, &mut out, dict)?,
1011 VersionDecoder::V07(decoder) => decoder.block(body, &mut out, dict)?,
1012 },
1013 Block::Raw(len) => {
1014 out.reserve(len)?;
1015 out.extend(body);
1016 }
1017 Block::Rle(len) => {
1018 out.reserve(len)?;
1019 out.repeat(body[0], len);
1020 }
1021 Block::End => {}
1022 }
1023 #[cfg(feature = "hash")]
1024 if let Some(hash) = &mut self.hash {
1025 use core::hash::Hasher;
1026 hash.write(out.since(before));
1027 }
1028 #[cfg(all(feature = "lsm", feature = "hash"))]
1029 if self.block_digests {
1030 let digest = twox_hash::XxHash64::oneshot(0, out.since(before));
1031 workspace.block_digests.push(digest as u32);
1033 }
1034 self.blocks += 1;
1035 Ok(())
1036 }
1037
1038 fn finish(&mut self, end: [u8; 3]) -> Result<(), Error> {
1049 if !self.has_checksum {
1052 return Ok(());
1053 }
1054 let expected = u32::from(end[2]) | u32::from(end[1]) << 8 | u32::from(end[0] & 0x3F) << 16;
1055 self.stored_checksum = Some(expected);
1056 #[cfg(feature = "hash")]
1057 if let Some(hash) = &self.hash {
1058 use core::hash::Hasher;
1059 let calculated = ((hash.finish() >> 11) & 0x3F_FFFF) as u32;
1060 self.calculated_checksum = Some(calculated);
1061 if self.checksum_mode == ContentChecksum::Verify && expected != calculated {
1062 return Err(Error::ChecksumMismatch {
1063 expected,
1064 calculated,
1065 });
1066 }
1067 }
1068 Ok(())
1069 }
1070}
1071
1072fn prime(
1077 dict_id: u32,
1078 dict: LegacyDict<'_>,
1079 workspace: &mut Workspace,
1080) -> Result<HistoryShape, Error> {
1081 let (header, content) = dict.pieces();
1082 Ok(
1083 match workspace.decoder.as_mut().expect("readied for the frame") {
1084 VersionDecoder::V07(decoder) => {
1085 let content = decoder.reset(dict)?;
1086 if dict_id != 0 && dict_id != decoder.dict_id {
1087 return Err(Error::DictionaryMismatch {
1088 expected: dict_id,
1089 provided: decoder.dict_id,
1090 });
1091 }
1092 HistoryShape::Content(content)
1093 }
1094 VersionDecoder::V06(decoder) if !header.is_empty() => {
1098 decoder.reset(&[])?;
1099 HistoryShape::Whole
1100 }
1101 VersionDecoder::V05(decoder) if !header.is_empty() => {
1102 decoder.reset(&[])?;
1103 HistoryShape::Whole
1104 }
1105 VersionDecoder::V06(decoder) => HistoryShape::Content(decoder.reset(content)?),
1106 VersionDecoder::V05(decoder) => HistoryShape::Content(decoder.reset(content)?),
1107 },
1108 )
1109}
1110
1111fn decode_frame<O: Output>(
1114 src: &[u8],
1115 dict: LegacyDict<'_>,
1116 options: FrameOptions,
1117 workspace: &mut Workspace,
1118 out: &mut O,
1119) -> Result<(usize, LegacyFrame), Error> {
1120 let header = read_header_whole(src)?;
1121 let mut frame = LegacyFrame::new(header, dict, options, workspace)?;
1122 let mut at = header.size;
1123 loop {
1124 let block_header = src.get(at..at + 3).ok_or(Error::Truncated)?;
1125 let block = Block::read(block_header)?;
1126 block.check(header.version, frame.limits)?;
1127 let end = [block_header[0], block_header[1], block_header[2]];
1128 at += 3;
1129 let body = src.get(at..at + block.body_len()).ok_or(Error::Truncated)?;
1130 if ends_frame(header.version, block) {
1131 frame.finish(end)?;
1132 return Ok((at, frame));
1133 }
1134 frame.block(workspace, block, body, out, dict)?;
1135 at += body.len();
1136 }
1137}
1138
1139pub(crate) struct LegacyStream {
1145 frame: Option<LegacyFrame>,
1147 workspace: Workspace,
1149 dict: Option<crate::decoding::DictionaryHandle>,
1151 window: u64,
1153 window_refused_at: Option<u64>,
1156 buf: Vec<u8>,
1159 end: usize,
1161 earlier: usize,
1163 previous_end: Option<usize>,
1165 delivered: usize,
1167 capacity: usize,
1170 finished: bool,
1172 consumed: u64,
1174 block: Vec<u8>,
1176}
1177
1178impl LegacyStream {
1179 pub(crate) const fn new() -> LegacyStream {
1180 LegacyStream {
1181 frame: None,
1182 workspace: Workspace {
1183 decoder: None,
1184 #[cfg(all(feature = "lsm", feature = "hash"))]
1185 block_digests: Vec::new(),
1186 },
1187 dict: None,
1188 window: 0,
1189 window_refused_at: None,
1190 buf: Vec::new(),
1191 end: 0,
1192 earlier: 0,
1193 previous_end: None,
1194 delivered: 0,
1195 capacity: 0,
1196 finished: false,
1197 consumed: 0,
1198 block: Vec::new(),
1199 }
1200 }
1201
1202 pub(crate) fn start(
1205 &mut self,
1206 header: FrameHeader,
1207 dict: Option<&crate::decoding::DictionaryHandle>,
1208 options: FrameOptions,
1209 ) -> Result<(), Error> {
1210 self.frame = None;
1211 let legacy_dict = dict.map_or(LegacyDict::None, LegacyDict::of);
1212 self.frame = Some(LegacyFrame::new(
1213 header,
1214 legacy_dict,
1215 options,
1216 &mut self.workspace,
1217 )?);
1218 self.hold_dict(dict);
1219 self.window = header.window;
1220 self.window_refused_at = None;
1221 self.buf.clear();
1222 self.end = 0;
1223 self.earlier = 0;
1224 self.previous_end = None;
1225 self.delivered = 0;
1226 debug_assert!(header.window <= 1 << 27);
1229 self.capacity = header.window as usize + BLOCK_SIZE_MAX;
1230 self.finished = false;
1231 self.consumed = header.size as u64;
1232 Ok(())
1233 }
1234
1235 fn hold_dict(&mut self, dict: Option<&crate::decoding::DictionaryHandle>) {
1239 match dict {
1240 None => self.dict = None,
1241 Some(dict) => {
1242 if self
1243 .dict
1244 .as_ref()
1245 .is_none_or(|held| !core::ptr::eq(held.as_dict(), dict.as_dict()))
1246 {
1247 self.dict = Some(dict.clone());
1248 }
1249 }
1250 }
1251 }
1252
1253 pub(crate) fn end(&mut self) {
1256 self.frame = None;
1257 self.window_refused_at = None;
1258 self.buf.clear();
1259 self.end = 0;
1260 self.delivered = 0;
1261 self.finished = false;
1262 }
1263
1264 pub(crate) fn decode_whole<O: Output>(
1269 &mut self,
1270 src: &[u8],
1271 dict: Option<&crate::decoding::DictionaryHandle>,
1272 options: FrameOptions,
1273 out: &mut O,
1274 ) -> Result<usize, Error> {
1275 self.end();
1276 let before = out.produced();
1277 let legacy_dict = dict.map_or(LegacyDict::None, LegacyDict::of);
1278 let (consumed, frame) = decode_frame(src, legacy_dict, options, &mut self.workspace, out)?;
1279 self.hold_dict(dict);
1280 self.frame = Some(frame);
1281 self.earlier = out.produced() - before;
1282 self.previous_end = None;
1283 self.finished = true;
1284 self.consumed = consumed as u64;
1285 Ok(consumed)
1286 }
1287
1288 pub(crate) fn content_size(&self) -> u64 {
1290 self.frame.as_ref().map_or(0, |frame| frame.content_size)
1291 }
1292
1293 pub(crate) fn blocks_decoded(&self) -> usize {
1295 self.frame.as_ref().map_or(0, |frame| frame.blocks)
1296 }
1297
1298 pub(crate) fn stored_checksum(&self) -> Option<u32> {
1300 self.frame.as_ref()?.stored_checksum
1301 }
1302
1303 #[cfg(feature = "hash")]
1306 pub(crate) fn calculated_checksum(&self) -> Option<u32> {
1307 self.frame.as_ref()?.calculated_checksum
1308 }
1309
1310 #[cfg(feature = "hash")]
1313 pub(crate) fn set_checksum_mode(&mut self, mode: ContentChecksum) {
1314 if self.finished {
1315 return;
1316 }
1317 if let Some(frame) = self.frame.as_mut() {
1318 frame.set_checksum_mode(mode);
1319 }
1320 }
1321
1322 pub(crate) fn force_dict(
1325 &mut self,
1326 dict: &crate::decoding::DictionaryHandle,
1327 ) -> Result<(), Error> {
1328 let frame = self.frame.as_mut().ok_or(corrupt("no frame in progress"))?;
1329 frame.set_dict(LegacyDict::of(dict), &mut self.workspace)?;
1330 self.hold_dict(Some(dict));
1331 Ok(())
1332 }
1333
1334 pub(crate) fn dictionary(&self) -> Option<&crate::decoding::DictionaryHandle> {
1336 self.dict.as_ref()
1337 }
1338
1339 #[cfg(all(feature = "lsm", feature = "hash"))]
1341 pub(crate) fn block_digests(&self) -> &[u32] {
1342 &self.workspace.block_digests
1343 }
1344
1345 pub(crate) fn set_window_ceiling(&mut self, limit: u64) {
1348 if self.frame.is_some() && !self.finished {
1349 self.window_refused_at = (self.window > limit).then_some(limit);
1350 }
1351 }
1352
1353 pub(crate) fn window_refusal(&self) -> Option<(u64, u64)> {
1356 Some((self.window, self.window_refused_at?))
1357 }
1358
1359 pub(crate) fn active(&self) -> bool {
1362 self.frame.is_some()
1363 }
1364
1365 pub(crate) fn is_finished(&self) -> bool {
1367 self.finished
1368 }
1369
1370 pub(crate) fn undelivered(&self) -> usize {
1372 self.end - self.delivered
1373 }
1374
1375 pub(crate) fn produced(&self) -> u64 {
1377 (self.earlier + self.end) as u64
1378 }
1379
1380 pub(crate) fn consumed(&self) -> u64 {
1382 self.consumed
1383 }
1384
1385 pub(crate) fn heap_bytes(&self) -> usize {
1388 self.buf.capacity() + self.block.capacity() + self.workspace.heap_bytes()
1389 }
1390
1391 pub(crate) fn input_needed(&self, pending: &[u8]) -> usize {
1396 if self.finished {
1397 return 0;
1398 }
1399 let Ok(block) = Block::read(pending) else {
1400 return 3;
1401 };
1402 match &self.frame {
1403 Some(frame) if block.check(frame.version, frame.limits).is_err() => 3,
1404 _ => 3 + block.body_len(),
1405 }
1406 }
1407
1408 pub(crate) fn decode(
1420 &mut self,
1421 src: &[u8],
1422 target: &mut [u8],
1423 ) -> Result<(usize, usize), Error> {
1424 let mut read = 0;
1425 let mut written = 0;
1426 loop {
1427 written += self.hand_out(&mut target[written..]);
1428 if self.undelivered() > 0 || self.finished {
1429 break;
1430 }
1431 let rest = &src[read..];
1432 let need = self.input_needed(rest);
1433 if need > rest.len() {
1434 break;
1435 }
1436 self.step(&rest[..need])?;
1437 read += need;
1438 }
1439 Ok((read, written))
1440 }
1441
1442 pub(crate) fn hand_out(&mut self, target: &mut [u8]) -> usize {
1444 let n = self.undelivered().min(target.len());
1445 target[..n].copy_from_slice(&self.buf[self.delivered..self.delivered + n]);
1446 self.delivered += n;
1447 n
1448 }
1449
1450 pub(crate) fn undelivered_bytes(&self) -> &[u8] {
1452 &self.buf[self.delivered..self.end]
1453 }
1454
1455 pub(crate) fn mark_delivered(&mut self, n: usize) {
1457 debug_assert!(n <= self.undelivered());
1458 self.delivered += n;
1459 }
1460
1461 pub(crate) fn read_step<R: crate::io::Read>(
1463 &mut self,
1464 source: &mut R,
1465 ) -> Result<(), crate::decoding::errors::FrameDecoderError> {
1466 use crate::blocks::block::BlockType;
1467 use crate::decoding::errors::{
1468 BlockHeaderReadError, DecodeBlockContentError, FrameDecoderError,
1469 };
1470 let mut block = core::mem::take(&mut self.block);
1472 block.clear();
1473 block.resize(3, 0);
1474 let result = source
1475 .read_exact(&mut block)
1476 .map_err(|source| {
1477 FrameDecoderError::FailedToReadBlockHeader(BlockHeaderReadError::ReadError(source))
1478 })
1479 .and_then(|()| {
1480 let need = self.input_needed(&block);
1481 block.resize(need, 0);
1482 let step = match Block::read(&block) {
1484 Ok(Block::Raw(_)) => BlockType::Raw,
1485 Ok(Block::Rle(_)) => BlockType::RLE,
1486 _ => BlockType::Compressed,
1487 };
1488 source.read_exact(&mut block[3..]).map_err(|source| {
1489 FrameDecoderError::FailedToReadBlockBody(DecodeBlockContentError::ReadError {
1490 step,
1491 source,
1492 })
1493 })
1494 })
1495 .and_then(|()| Ok(self.step(&block)?));
1496 self.block = block;
1497 result
1498 }
1499
1500 fn step(&mut self, step: &[u8]) -> Result<(), Error> {
1502 let LegacyStream {
1503 frame,
1504 workspace,
1505 dict,
1506 window,
1507 window_refused_at: _,
1508 buf,
1509 end,
1510 earlier,
1511 previous_end,
1512 delivered,
1513 capacity,
1514 finished,
1515 consumed,
1516 block: _,
1517 } = self;
1518 let frame = frame.as_mut().ok_or(corrupt("no frame in progress"))?;
1519 let block = Block::read(step)?;
1520 block.check(frame.version, frame.limits)?;
1521 *consumed += step.len() as u64;
1522 if ends_frame(frame.version, block) {
1523 frame.finish([step[0], step[1], step[2]])?;
1524 *finished = true;
1525 return Ok(());
1526 }
1527 let block_max = block.decoded_max();
1535 let handed_out = *delivered == *end;
1536 if *end + block_max > *capacity && handed_out && *end > 0 && *end as u64 >= *window {
1537 *earlier += *end;
1538 *previous_end = Some(*end);
1539 *end = 0;
1540 *delivered = 0;
1541 }
1542 let limit = if handed_out {
1545 (*capacity).max(*end + block_max)
1546 } else {
1547 *capacity
1548 };
1549 let dict = dict.as_ref().map_or(LegacyDict::None, LegacyDict::of);
1550 let mut out = SegmentOutput {
1551 buf,
1552 end: *end,
1553 earlier: *earlier,
1554 previous_end: *previous_end,
1555 capacity: limit,
1556 };
1557 let decoded = frame.block(workspace, block, &step[3..], &mut out, dict);
1558 *end = out.end;
1559 decoded
1560 }
1561}
1562
1563struct SegmentOutput<'b> {
1567 buf: &'b mut Vec<u8>,
1568 end: usize,
1569 earlier: usize,
1570 previous_end: Option<usize>,
1571 capacity: usize,
1573}
1574
1575impl SegmentOutput<'_> {
1576 fn room(&mut self, len: usize) -> core::ops::Range<usize> {
1580 let to = self.end + len;
1581 if to > self.buf.capacity() {
1582 let doubled = 2 * self.buf.len();
1585 let target = if to <= self.capacity {
1586 doubled.clamp(to, self.capacity)
1587 } else {
1588 doubled.max(to)
1589 };
1590 self.buf.reserve_exact(target - self.buf.len());
1591 }
1592 if to > self.buf.len() {
1593 self.buf.resize(to, 0);
1594 }
1595 self.end..to
1596 }
1597}
1598
1599impl Output for SegmentOutput<'_> {
1600 fn produced(&self) -> usize {
1601 self.earlier + self.end
1602 }
1603
1604 fn held(&self) -> usize {
1605 self.end
1606 }
1607
1608 fn reserve(&mut self, _len: usize) -> Result<(), Error> {
1609 Ok(())
1611 }
1612
1613 fn extend(&mut self, bytes: &[u8]) {
1614 let at = self.room(bytes.len());
1615 self.buf[at].copy_from_slice(bytes);
1616 self.end += bytes.len();
1617 }
1618
1619 fn repeat(&mut self, byte: u8, len: usize) {
1620 let at = self.room(len);
1621 self.buf[at].fill(byte);
1622 self.end += len;
1623 }
1624
1625 fn copy_back(&mut self, offset: usize, len: usize) {
1626 let from = self.end - offset;
1627 let at = self.room(len);
1628 copy_repeating(self.buf, from, at.start, len);
1629 self.end += len;
1630 }
1631
1632 #[cfg(feature = "hash")]
1633 fn since(&self, from: usize) -> &[u8] {
1634 &self.buf[self.end - (self.produced() - from)..self.end]
1635 }
1636
1637 fn copy_match(&mut self, offset: usize, len: usize, dict: &History<'_>) -> Result<(), Error> {
1638 let Some(previous_end) = self.previous_end else {
1639 return copy_match(self, offset, len, dict);
1641 };
1642 debug_assert!(offset >= 1);
1644 if offset <= self.end {
1645 self.copy_back(offset, len);
1646 return Ok(());
1647 }
1648 let back = offset - self.end;
1651 let from = previous_end
1652 .checked_sub(back)
1653 .filter(|&from| from >= self.end)
1654 .ok_or(corrupt("match reaches before the window"))?;
1655 let first = len.min(back);
1656 let at = self.room(first);
1657 self.buf.copy_within(from..from + first, at.start);
1658 self.end += first;
1659 let rest = len - first;
1660 if rest > 0 {
1661 let distance = self.end;
1663 self.copy_back(distance, rest);
1664 }
1665 Ok(())
1666 }
1667}
1668
1669#[cfg(test)]
1670mod tests;