ldeflate 0.1.3

Pure Rust parallel DEFLATE/zlib compressor — the write side of linflate. Two-queue gatling scheduling: coalesce small objects, split large streams at full-flush boundaries.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
//! **Axis B — split within one stream.** The write-side mirror of
//! `linflate::deflate_scan`.
//!
//! `deflate_scan::find_all_flushes` *searches* a compressed stream for
//! `00 00 FF FF` full-flush markers so the read side can hand independent
//! segments to independent workers. This module *creates* them: one large object
//! is cut into fixed byte ranges of the **uncompressed** input, each range is
//! compressed by a different worker with a compressor that has never seen the
//! others, and the results are concatenated.
//!
//! ```text
//!   input:   [------- seg 0 -------][------- seg 1 -------][-- seg 2 --]
//!               worker 7               worker 2              worker 19
//!   output:  78 9C │ deflate·FULL     │ deflate·FULL        │ deflate·FIN │ adler32
//!                  └─ 00 00 FF FF ────┴─ 00 00 FF FF ───────┘
//! ```
//!
//! ## Why the concatenation is a legal zlib stream
//!
//! `Z_FULL_FLUSH` does two things and this design leans on both. It byte-aligns
//! the bit stream (so the next segment's first byte lands on a byte boundary and
//! can be appended verbatim), and it **empties the LZ77 window** (so no
//! back-reference in segment *k+1* can point into segment *k*, which is exactly
//! why segment *k+1* could be compressed by a worker that never saw segment
//! *k*). The final segment is `Z_FINISH`, which sets `BFINAL=1`. An RFC-1950
//! header goes in front and the whole-input Adler-32 — assembled from the
//! per-segment checksums via `adler32_combine` — goes behind. A stock decoder
//! (`zlib`, `git`, `flate2`, `linflate`) sees one ordinary stream of blocks.
//!
//! This is pigz's trick. It is not novel; what matters is that it costs a small,
//! *measurable* amount of ratio (each window reset forecloses long-range
//! matches) and that the ratio cost is reported honestly rather than hidden.
//!
//! ## Determinism
//!
//! Segment boundaries are a function of `(input length, segment size)` **only** —
//! never of the worker count, never of completion order. So a 4-thread run and a
//! 32-thread run emit byte-identical output. That is asserted, not assumed
//! ([`crate::tests`] / `tests/determinism.rs`).

use crate::container::Container;
use crate::unit::{DeflateError, Encoder};

/// One segment of a split stream: where it came from and what it produced.
#[derive(Debug, Clone)]
pub struct Segment {
    /// Byte range of the **uncompressed** input this segment covers.
    pub src_start: usize,
    pub src_end: usize,
    /// The segment's compressed bytes (bare DEFLATE, ending in a full-flush
    /// marker unless this is the final segment).
    pub bytes: Vec<u8>,
    /// Checksum of `input[src_start..src_end]` under the container's algorithm
    /// (Adler-32 seeded at 1 for zlib, CRC-32 seeded at 0 for gzip). Computed on
    /// the worker over that worker's own bytes, so it parallelises with the
    /// compression instead of becoming a serial tail.
    pub sum: u32,
}

/// Plan the segment boundaries for an input of `len` bytes at `segment` size.
///
/// Returns `(start, end, is_last)` triples. Deliberately a pure function of its
/// two arguments — this is what makes the compressor deterministic across
/// thread counts.
///
/// # Errors
///
/// [`DeflateError::TooManyPieces`] if `len / segment` exceeds
/// [`crate::limits::MAX_PIECES`]. A `segment` of 1 against a megabyte asks for
/// a million independently compressed, independently checksummed pieces; this
/// refuses **before** the `Vec` is built rather than trying. The ceiling errors
/// instead of clamping `segment` upward because the segment plan is exactly
/// what carries this crate's byte-identical-output promise — see
/// [`crate::limits`].
pub fn plan_segments(
    len: usize,
    segment: usize,
) -> Result<Vec<(usize, usize, bool)>, DeflateError> {
    let n = crate::limits::check_pieces("split segments", len, segment)?;
    if len == 0 {
        return Ok(vec![(0, 0, true)]);
    }
    let segment = segment.max(1);
    Ok((0..n)
        .map(|i| {
            let start = i * segment;
            let end = ((i + 1) * segment).min(len);
            (start, end, i + 1 == n)
        })
        .collect())
}

/// Compress one segment. Called on a worker thread, with `enc` being that
/// worker's reusable encoder.
///
/// `is_last` selects `Z_FINISH` (BFINAL=1) over `Z_FULL_FLUSH`.
pub fn compress_segment(
    enc: &mut Encoder,
    src: &[u8],
    start: usize,
    end: usize,
    is_last: bool,
    container: Container,
) -> Result<Segment, DeflateError> {
    compress_segment_skipping(enc, src, start, end, is_last, container, false)
}

/// [`compress_segment`], optionally storing the segment instead of deflating it.
///
/// `store` is decided by the caller from the **whole** object (see
/// [`crate::precompressed`]), never from this segment's slice: a segment cut out
/// of the middle of a PNG carries no magic bytes of its own, and a decision made
/// per slice would make the output depend on where the cuts landed.
///
/// A stored non-final segment ends on a block boundary, which is the same seam
/// `Z_FULL_FLUSH` leaves, so stored and deflated segments concatenate freely.
pub fn compress_segment_skipping(
    enc: &mut Encoder,
    src: &[u8],
    start: usize,
    end: usize,
    is_last: bool,
    container: Container,
    store: bool,
) -> Result<Segment, DeflateError> {
    debug_assert!(!enc.is_zlib_wrapped(), "segments must be bare DEFLATE");
    let chunk = &src[start..end];
    let mut bytes = Vec::new();
    if store {
        bytes = crate::precompressed::stored_deflate(chunk, is_last);
    } else if is_last {
        enc.compress_into(chunk, &mut bytes)?;
    } else {
        enc.compress_segment_into(chunk, &mut bytes)?;
    }
    let sum = container.checksum(chunk);
    Ok(Segment {
        src_start: start,
        src_end: end,
        bytes,
        sum,
    })
}

/// Assemble planned segments (in stream order) into one complete zlib stream.
///
/// Panics in debug if the segments are not contiguous and in order — that would
/// be a scheduler bug, and a silently mis-ordered assembly produces a stream
/// that inflates to *wrong bytes* rather than failing, which is the worst
/// possible failure mode.
pub fn assemble(level: u32, segments: &[Segment], container: Container) -> Vec<u8> {
    assemble_checksummed(level, segments, container).0
}

/// [`assemble`], also returning the folded whole-stream checksum.
///
/// ZIP stores each entry's CRC-32 in its local and central headers rather than
/// in a trailer, so it needs the folded value that [`assemble`] would otherwise
/// consume and discard.
pub fn assemble_checksummed(
    level: u32,
    segments: &[Segment],
    container: Container,
) -> (Vec<u8>, u32) {
    debug_assert!(!segments.is_empty(), "assemble needs at least one segment");
    debug_assert!(
        segments.windows(2).all(|w| w[0].src_end == w[1].src_start),
        "segments must be contiguous and in stream order"
    );

    let body: usize = segments.iter().map(|s| s.bytes.len()).sum();
    let total: u64 = segments
        .iter()
        .map(|s| (s.src_end - s.src_start) as u64)
        .sum();
    let mut out = Vec::with_capacity(12 + body + 8);
    out.extend_from_slice(&container.header(level));

    // Fold the per-segment checksums into the whole-stream one. This is the
    // only reason `adler32_combine` / `crc32_combine` exist in this crate.
    let mut sum = container.checksum_init();
    for s in segments {
        out.extend_from_slice(&s.bytes);
        let n = (s.src_end - s.src_start) as u64;
        sum = container.checksum_combine(sum, s.sum, n);
    }
    out.extend_from_slice(&container.trailer(sum, total));
    (out, sum)
}

/// Serial reference implementation of the split path — used by tests as the
/// oracle the parallel scheduler must match byte-for-byte, and by
/// [`crate::plan`] when a caller asks for one worker.
pub fn split_compress_serial(
    src: &[u8],
    level: u32,
    segment: usize,
) -> Result<Vec<u8>, DeflateError> {
    split_compress_serial_in(src, level, segment, Container::Zlib)
}

/// [`split_compress_serial`] in an explicit container.
pub fn split_compress_serial_in(
    src: &[u8],
    level: u32,
    segment: usize,
    container: Container,
) -> Result<Vec<u8>, DeflateError> {
    let mut enc = Encoder::new(level, false)?;
    let plan = plan_segments(src.len(), segment)?;
    let mut segs = Vec::with_capacity(plan.len());
    for (start, end, last) in plan {
        segs.push(compress_segment(
            &mut enc, src, start, end, last, container,
        )?);
    }
    Ok(assemble(level, &segs, container))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::plan::Config;
    use std::io::Read;

    fn corpus(n: usize) -> Vec<u8> {
        // Compressible, but with enough entropy that segmentation actually
        // costs ratio — a corpus of all-zeros would hide any boundary bug
        // because every segment compresses to nothing (LAW 2: identity value).
        let words: &[&[u8]] = &[
            b"fn main() { println!(\"hello\"); }\n",
            b"use std::collections::HashMap;\n",
            b"    let mut total = 0usize;\n",
            b"// TODO: revisit this once the planner lands\n",
        ];
        let mut out = Vec::with_capacity(n + 64);
        let mut s: u64 = 0x9E3779B97F4A7C15;
        while out.len() < n {
            s = s
                .wrapping_mul(6364136223846793005)
                .wrapping_add(1442695040888963407);
            out.extend_from_slice(words[(s >> 33) as usize % words.len()]);
        }
        out.truncate(n);
        out
    }

    /// Assert one (len, seg) pair round-trips through a stock decoder.
    fn round_trips(len: usize, seg: usize) {
        let src = corpus(len);
        let z = split_compress_serial(&src, 6, seg).unwrap();
        let mut got = Vec::new();
        flate2::read::ZlibDecoder::new(&z[..])
            .read_to_end(&mut got)
            .unwrap_or_else(|e| panic!("len={len} seg={seg}: inflate failed: {e}"));
        assert_eq!(got, src, "len={len} seg={seg}: bytes differ");
    }

    /// **Degenerate segment sizes, against SMALL inputs.** `seg = 1` and
    /// `seg = 2` are exactly the boundary cases worth testing — an empty final
    /// segment, a one-byte final segment, a segment count that is not a divisor
    /// — and they are cheap here because the largest input is 8 KiB, i.e. at
    /// most ~8 k pieces.
    ///
    /// **Do not re-cross these with the large inputs below.** `seg = 1` against
    /// a megabyte is a million independently compressed, framed and checksummed
    /// pieces; that cross is what OOM-killed this box on 2026-08-03 (the
    /// original loop ran `len` up to 1_000_000 against `seg = 1` and took over
    /// 6 minutes even under an address-space cap). The two loops together cover
    /// strictly more shapes than the cross did and finish in under a second.
    /// The library now refuses the absurd end of that cross outright — see
    /// `a_degenerate_segment_size_is_refused_not_attempted`.
    #[test]
    fn degenerate_segment_sizes_round_trip_on_small_inputs() {
        for &len in &[0usize, 1, 2, 3, 7, 8, 4095, 4096, 4097, 8192] {
            for &seg in &[1usize, 2, 3, 7, 4096] {
                round_trips(len, seg);
            }
        }
    }

    /// **Large inputs, against REALISTIC segment sizes.** The size axis, kept
    /// separate from the degenerate-segment axis above for the reason stated
    /// there. Every pair here stays in the low hundreds of pieces.
    #[test]
    fn large_inputs_round_trip_at_realistic_segment_sizes() {
        for &len in &[100_000usize, 1_000_000] {
            for &seg in &[4096usize, 65_536, 1 << 20] {
                round_trips(len, seg);
            }
        }
    }

    /// **The OOM guard, split side.** A `segment` size that would cut the input
    /// into an absurd number of pieces must come back as an `Err` — from the
    /// planner, from the serial path, and from the public `compress_split`
    /// entry point, because all three route through `plan_segments`.
    ///
    /// Seen red on 2026-08-03 with the ceiling check removed from
    /// `plan_segments`: every case below returned `Ok`, building a
    /// million-element plan, and this test failed on the first `expect_err`.
    #[test]
    fn a_degenerate_segment_size_is_refused_not_attempted() {
        use crate::limits::MAX_PIECES;

        // The planner itself — the one writer all three paths share.
        let just_over = MAX_PIECES + 1;
        let e = plan_segments(just_over, 1).expect_err("one segment per byte must be refused");
        assert!(
            matches!(e, DeflateError::TooManyPieces { pieces, max, .. }
                     if pieces == just_over && max == MAX_PIECES),
            "wrong error: {e}"
        );
        assert!(
            e.to_string().contains("split segments"),
            "message does not name the axis: {e}"
        );

        // The shape the old cross-product test ran: 1_000_000 bytes at seg = 1.
        assert!(
            plan_segments(1_000_000, 1).is_err(),
            "a million one-byte segments were planned"
        );
        assert!(
            plan_segments(1_000_000, 0).is_err(),
            "segment=0 escaped the ceiling"
        );
        assert!(
            split_compress_serial(&vec![b'x'; 1_000_000], 6, 1).is_err(),
            "the serial split path attempted a million one-byte segments"
        );
        assert!(
            crate::compress_split(
                &vec![b'x'; 1_000_000],
                &Config {
                    segment: 1,
                    ..Config::default()
                }
            )
            .is_err(),
            "the public compress_split entry point bypassed the ceiling"
        );
        assert!(
            crate::compress_objects(
                &[&vec![b'x'; 1_000_000][..]],
                &Config {
                    split_above: 1,
                    segment: 1,
                    ..Config::default()
                },
            )
            .is_err(),
            "compress_objects bypassed the ceiling"
        );

        // Not vacuous: at the ceiling and at every realistic segment size the
        // planner still says yes.
        assert_eq!(plan_segments(MAX_PIECES, 1).unwrap().len(), MAX_PIECES);
        assert!(
            plan_segments(256 << 30, 1 << 20).is_ok(),
            "256 GiB at 1 MiB segments"
        );
        assert!(split_compress_serial(&vec![b'x'; 1_000_000], 6, 4096).is_ok());
    }

    /// RED-when-broken: the Adler-32 trailer must be *checked* by the decoder.
    /// `flate2`'s `ZlibDecoder` validates it — so corrupting one trailer byte
    /// must make the read above FAIL. Without this, the round-trip test above
    /// would pass even if `adler32_combine` were returning garbage, because the
    /// payload bytes would still be right. This is the guard on the guard.
    #[test]
    fn a_wrong_adler_trailer_is_actually_rejected() {
        let src = corpus(200_000);
        let mut z = split_compress_serial(&src, 6, 16_384).unwrap();
        // Sanity: it reads clean first.
        let mut ok = Vec::new();
        flate2::read::ZlibDecoder::new(&z[..])
            .read_to_end(&mut ok)
            .unwrap();
        assert_eq!(ok, src);

        // Now break the trailer. If the decoder did not verify it, this would
        // still succeed and the test above would be worthless.
        let n = z.len();
        z[n - 1] ^= 0xff;
        let mut bad = Vec::new();
        let r = flate2::read::ZlibDecoder::new(&z[..]).read_to_end(&mut bad);
        assert!(
            r.is_err(),
            "the decoder did NOT verify the Adler-32 trailer — the round-trip \
             tests in this module prove nothing about adler32_combine"
        );
    }

    /// RED-when-broken: if the final segment were emitted with FULL_FLUSH
    /// instead of FINISH, no `BFINAL=1` block would ever be written. Prove a
    /// decoder actually notices — otherwise `compress_segment`'s `is_last`
    /// branch is untested.
    #[test]
    fn a_stream_with_no_final_block_is_rejected() {
        let src = corpus(50_000);
        let mut enc = Encoder::new(6, false).unwrap();
        let plan = plan_segments(src.len(), 8192).unwrap();
        let mut segs = Vec::new();
        for (start, end, _last) in plan {
            // Deliberately pass is_last = false for EVERY segment.
            segs.push(
                compress_segment(&mut enc, &src, start, end, false, Container::Zlib).unwrap(),
            );
        }
        let z = assemble(6, &segs, Container::Zlib);
        let mut got = Vec::new();
        let r = flate2::read::ZlibDecoder::new(&z[..]).read_to_end(&mut got);
        assert!(
            r.is_err() || got != src,
            "a stream whose last block is not BFINAL was accepted as complete — \
             the `is_last` branch of compress_segment is not being tested"
        );
    }

    /// The plan is a pure function of (len, segment): contiguous, covering, in
    /// order, exactly one `is_last`.
    ///
    /// Same rule as the round-trip tests above: degenerate `seg` is paired with
    /// small `len`, big `len` with realistic `seg`. Crossing them asks the
    /// planner for a piece count it now (correctly) refuses, and asked the old
    /// planner for a million-element `Vec`.
    #[test]
    fn segment_plan_is_contiguous_and_covering() {
        let check = |len: usize, seg: usize| {
            let p = plan_segments(len, seg)
                .unwrap_or_else(|e| panic!("len={len} seg={seg}: refused: {e}"));
            assert!(!p.is_empty());
            assert_eq!(p[0].0, 0, "len={len} seg={seg}: must start at 0");
            assert_eq!(
                p.last().unwrap().1,
                len,
                "len={len} seg={seg}: must cover to len"
            );
            assert_eq!(
                p.iter().filter(|(_, _, l)| *l).count(),
                1,
                "len={len} seg={seg}: exactly one final segment"
            );
            for w in p.windows(2) {
                assert_eq!(w[0].1, w[1].0, "len={len} seg={seg}: gap or overlap");
                assert!(
                    !w[0].2,
                    "len={len} seg={seg}: is_last set on a non-final segment"
                );
            }
        };
        for &len in &[0usize, 1, 999, 1000, 1001, 9999] {
            for &seg in &[1usize, 7, 1000] {
                check(len, seg);
            }
        }
        for &len in &[1usize << 20, (1 << 20) + 1, 64 << 20] {
            for &seg in &[1000usize, 1 << 16, 1 << 20] {
                check(len, seg);
            }
        }
    }

    /// Splitting costs ratio. Measure it here so the cost is a documented
    /// number rather than a surprise — and so a future change that silently
    /// stops resetting the window (which would improve ratio but break
    /// segment independence) is visible.
    #[test]
    fn splitting_costs_ratio_and_the_cost_is_bounded() {
        let src = corpus(4 << 20);
        let whole = crate::unit::zlib_compress(&src, 6).unwrap();
        for &seg in &[1 << 20, 256 << 10, 64 << 10] {
            let split = split_compress_serial(&src, 6, seg).unwrap();
            let overhead = split.len() as f64 / whole.len() as f64;
            assert!(
                overhead >= 1.0,
                "seg={seg}: split output ({}) SMALLER than unsplit ({}) — impossible \
                 unless the window is not actually being reset",
                split.len(),
                whole.len()
            );
            assert!(
                overhead < 1.25,
                "seg={seg}: split costs {:.1}% more than unsplit — far beyond the \
                 expected few percent; a boundary is being emitted per block, not per segment",
                (overhead - 1.0) * 100.0
            );
        }
    }
}