Skip to main content

heddle_pack/store/pack/
compact_frame.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use crate::store::Result;
4
5/// zstd parameters for one compact metadata frame.
6///
7/// Decoding never consults these: a frame carries its own zstd framing, so a
8/// store written at any level stays readable. Only the encoder's CPU/bytes
9/// trade-off moves. The 2^27-byte long-distance window matches the #1325
10/// falsifier at every level.
11#[derive(Clone, Copy, Debug)]
12pub struct CompactFrameCompression {
13    pub level: i32,
14    pub window_log: u32,
15    pub long_distance_matching: bool,
16}
17
18impl CompactFrameCompression {
19    /// Local repack policy: retain the pre-adoption level 19 encoder.
20    pub const DEFAULT: Self = Self {
21        level: 19,
22        window_log: 27,
23        long_distance_matching: true,
24    };
25    /// Fast zstd for adoption/import packs only (owner decision 2026-10-07).
26    /// Weft adoption_pack must pass this to `compress_compact_frame_with`.
27    pub const ADOPTION: Self = Self {
28        level: 3,
29        window_log: 27,
30        long_distance_matching: true,
31    };
32}
33
34impl Default for CompactFrameCompression {
35    fn default() -> Self {
36        Self::DEFAULT
37    }
38}
39
40/// Compress one compact metadata frame with [`CompactFrameCompression::DEFAULT`].
41///
42/// Incompressible input remains raw so the pack reader's size discriminator is
43/// unambiguous. Builds without `zstd` retain the lossless compact encoding but
44/// store its frames raw.
45pub fn compress_compact_frame(data: &[u8]) -> Result<Vec<u8>> {
46    compress_compact_frame_with(data, CompactFrameCompression::DEFAULT)
47}
48
49/// Compress one compact metadata frame with explicit zstd parameters. The
50/// raw-when-incompressible rule and the `zstd`-less fallback are unchanged.
51pub fn compress_compact_frame_with(
52    data: &[u8],
53    options: CompactFrameCompression,
54) -> Result<Vec<u8>> {
55    #[cfg(feature = "zstd")]
56    {
57        use std::io::Write;
58
59        let mut compressed = Vec::new();
60        let mut encoder = zstd::stream::write::Encoder::new(&mut compressed, options.level)?;
61        encoder.window_log(options.window_log)?;
62        encoder.long_distance_matching(options.long_distance_matching)?;
63        encoder.include_checksum(true)?;
64        encoder.set_pledged_src_size(Some(data.len() as u64))?;
65        encoder.write_all(data)?;
66        encoder.finish()?;
67        if compressed.len() < data.len() {
68            return Ok(compressed);
69        }
70    }
71    #[cfg(not(feature = "zstd"))]
72    {
73        let _ = options;
74    }
75    Ok(data.to_vec())
76}
77
78#[cfg(all(test, feature = "zstd"))]
79mod tests {
80    use super::*;
81    use crate::store::pack::{decompress_pack_payload, has_zstd_magic};
82
83    fn lineage_input() -> Vec<u8> {
84        // Repetitive like a State/tree lineage frame, with enough variation
85        // that level 19 and level 3 produce different byte streams.
86        (0..32_768u32)
87            .flat_map(|i| format!("directory version {}\n", i % 97).into_bytes())
88            .collect()
89    }
90
91    #[test]
92    fn solid_compression_round_trips_and_carries_a_zstd_checksum() {
93        let input = b"directory version\n".repeat(32_768);
94        let compressed = compress_compact_frame(&input).unwrap();
95        assert!(has_zstd_magic(&compressed));
96        assert!(compressed.len() < input.len());
97        assert_eq!(
98            decompress_pack_payload(&compressed, input.len()).unwrap(),
99            input
100        );
101    }
102
103    #[test]
104    fn local_default_is_level_nineteen_and_adoption_is_level_three() {
105        let options = CompactFrameCompression::DEFAULT;
106        let adoption = CompactFrameCompression::ADOPTION;
107        assert_eq!(options.level, 19);
108        assert_eq!(adoption.level, 3);
109        assert_eq!(adoption.window_log, 27);
110        assert!(adoption.long_distance_matching);
111        assert_eq!(options.window_log, 27);
112        assert!(options.long_distance_matching);
113        let input = lineage_input();
114        assert_eq!(
115            compress_compact_frame(&input).unwrap(),
116            compress_compact_frame_with(&input, CompactFrameCompression::DEFAULT).unwrap(),
117            "the plain entry point is exactly the DEFAULT policy"
118        );
119    }
120
121    #[test]
122    fn pre_change_level_nineteen_fixture_and_adoption_frames_decode() {
123        let input = lineage_input();
124        // Encoded once by the unmodified encoder from 660f99cbe9360b6f
125        // (level 19, window_log 27, LDM, checksum and pledged input size).
126        let solid = include_bytes!("../../../tests/fixtures/compact-level19.zstd");
127        let fast = compress_compact_frame_with(&input, CompactFrameCompression::ADOPTION).unwrap();
128        assert!(has_zstd_magic(solid) && has_zstd_magic(&fast));
129        assert_ne!(
130            solid.as_slice(),
131            fast.as_slice(),
132            "the two levels must exercise different encoders"
133        );
134        // A store written at level 19 before the policy change decodes with
135        // the same reader as a fast frame; decoding carries no level.
136        assert_eq!(decompress_pack_payload(solid, input.len()).unwrap(), input);
137        assert_eq!(decompress_pack_payload(&fast, input.len()).unwrap(), input);
138    }
139
140    #[test]
141    fn explicit_options_change_the_encoder() {
142        let input = lineage_input();
143        let no_ldm = compress_compact_frame_with(
144            &input,
145            CompactFrameCompression {
146                level: 1,
147                window_log: 20,
148                long_distance_matching: false,
149            },
150        )
151        .unwrap();
152        assert!(has_zstd_magic(&no_ldm));
153        assert_eq!(
154            decompress_pack_payload(&no_ldm, input.len()).unwrap(),
155            input
156        );
157        assert_ne!(
158            no_ldm,
159            compress_compact_frame_with(&input, CompactFrameCompression::DEFAULT).unwrap()
160        );
161    }
162
163    #[test]
164    fn incompressible_input_stays_raw_under_every_policy() {
165        // xorshift64 noise: no repeated structure for either encoder to find.
166        let mut word = 0x9E37_79B9_7F4A_7C15_u64;
167        let input: Vec<u8> = (0..4096)
168            .map(|_| {
169                word ^= word << 13;
170                word ^= word >> 7;
171                word ^= word << 17;
172                (word >> 56) as u8
173            })
174            .collect();
175        for options in [
176            CompactFrameCompression::DEFAULT,
177            CompactFrameCompression::ADOPTION,
178        ] {
179            assert_eq!(compress_compact_frame_with(&input, options).unwrap(), input);
180        }
181    }
182}