feat(rust): export public sequence helpers
Move the context-free ZSTD_sequenceBound and ZSTD_mergeBlockDelimiters APIs
into the Rust compression helper module. Preserve the public sequence ABI,
block-delimiter semantics, and unsigned literal-length wrapping behavior while
leaving context-driven sequence generation in C.
Test Plan:
- cargo clippy
- cargo clippy --benches
- cargo clippy --tests
- cargo +nightly fmt
- cargo test zstd_compress_api::tests -- --nocapture
- cargo test --target i686-unknown-linux-gnu zstd_compress_api::tests
Refs: Rust compression-bound export 3e2635f4
This commit is contained in:
@@ -3501,11 +3501,7 @@ static size_t ZSTD_copyBlockSequences(SeqCollector* seqCollector, const SeqStore
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ZSTD_sequenceBound(size_t srcSize) {
|
/* ZSTD_sequenceBound() lives in rust/src/zstd_compress_api.rs. */
|
||||||
const size_t maxNbSeq = (srcSize / ZSTD_MINMATCH_MIN) + 1;
|
|
||||||
const size_t maxNbDelims = (srcSize / ZSTD_BLOCKSIZE_MAX_MIN) + 1;
|
|
||||||
return maxNbSeq + maxNbDelims;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,
|
size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,
|
||||||
size_t outSeqsSize, const void* src, size_t srcSize)
|
size_t outSeqsSize, const void* src, size_t srcSize)
|
||||||
@@ -3542,21 +3538,7 @@ size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,
|
|||||||
return zc->seqCollector.seqIndex;
|
return zc->seqCollector.seqIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, size_t seqsSize) {
|
/* ZSTD_mergeBlockDelimiters() lives in rust/src/zstd_compress_api.rs. */
|
||||||
size_t in = 0;
|
|
||||||
size_t out = 0;
|
|
||||||
for (; in < seqsSize; ++in) {
|
|
||||||
if (sequences[in].offset == 0 && sequences[in].matchLength == 0) {
|
|
||||||
if (in != seqsSize - 1) {
|
|
||||||
sequences[in+1].litLength += sequences[in].litLength;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sequences[out] = sequences[in];
|
|
||||||
++out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unrolled loop to read four size_ts of input at a time. Returns 1 if is RLE, 0 if not. */
|
/* Unrolled loop to read four size_ts of input at a time. Returns 1 if is RLE, 0 if not. */
|
||||||
static int ZSTD_isRLE(const BYTE* src, size_t length) {
|
static int ZSTD_isRLE(const BYTE* src, size_t length) {
|
||||||
|
|||||||
@@ -12,6 +12,18 @@ const ZSTD_MAX_INPUT_SIZE: usize = 0xFF00_FF00_FF00_FF00;
|
|||||||
const ZSTD_MAX_INPUT_SIZE: usize = 0xFF00_FF00;
|
const ZSTD_MAX_INPUT_SIZE: usize = 0xFF00_FF00;
|
||||||
|
|
||||||
const SMALL_INPUT_THRESHOLD: usize = 128 << 10;
|
const SMALL_INPUT_THRESHOLD: usize = 128 << 10;
|
||||||
|
const ZSTD_MINMATCH_MIN: usize = 3;
|
||||||
|
const ZSTD_BLOCKSIZE_MAX_MIN: usize = 1 << 10;
|
||||||
|
|
||||||
|
/// ABI-compatible `ZSTD_Sequence` from `zstd.h`.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
|
pub struct ZstdSequence {
|
||||||
|
offset: u32,
|
||||||
|
lit_length: u32,
|
||||||
|
match_length: u32,
|
||||||
|
rep: u32,
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the maximum one-pass compressed size, matching
|
/// Returns the maximum one-pass compressed size, matching
|
||||||
/// `ZSTD_COMPRESSBOUND()` and its public error contract.
|
/// `ZSTD_COMPRESSBOUND()` and its public error contract.
|
||||||
@@ -29,6 +41,40 @@ pub extern "C" fn ZSTD_compressBound(src_size: usize) -> usize {
|
|||||||
src_size.wrapping_add(src_size >> 8).wrapping_add(margin)
|
src_size.wrapping_add(src_size >> 8).wrapping_add(margin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the maximum number of public sequences generated for an input.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn ZSTD_sequenceBound(src_size: usize) -> usize {
|
||||||
|
(src_size / ZSTD_MINMATCH_MIN + 1) + (src_size / ZSTD_BLOCKSIZE_MAX_MIN + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes explicit block delimiters, carrying their literals into the next
|
||||||
|
/// sequence exactly as the C public helper does.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// When `sequences_size` is nonzero, `sequences` must reference that many
|
||||||
|
/// initialized `ZSTD_Sequence` values.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn ZSTD_mergeBlockDelimiters(
|
||||||
|
sequences: *mut ZstdSequence,
|
||||||
|
sequences_size: usize,
|
||||||
|
) -> usize {
|
||||||
|
let mut output = 0usize;
|
||||||
|
for input in 0..sequences_size {
|
||||||
|
let sequence = unsafe { sequences.add(input).read() };
|
||||||
|
if sequence.offset == 0 && sequence.match_length == 0 {
|
||||||
|
if input != sequences_size - 1 {
|
||||||
|
let next = unsafe { &mut *sequences.add(input + 1) };
|
||||||
|
next.lit_length = next.lit_length.wrapping_add(sequence.lit_length);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsafe { sequences.add(output).write(sequence) };
|
||||||
|
output += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -42,4 +88,49 @@ mod tests {
|
|||||||
assert_eq!(ZSTD_compressBound(SMALL_INPUT_THRESHOLD), 131_584);
|
assert_eq!(ZSTD_compressBound(SMALL_INPUT_THRESHOLD), 131_584);
|
||||||
assert!(ERR_isError(ZSTD_compressBound(ZSTD_MAX_INPUT_SIZE)));
|
assert!(ERR_isError(ZSTD_compressBound(ZSTD_MAX_INPUT_SIZE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sequence_bound_matches_the_public_formula() {
|
||||||
|
assert_eq!(ZSTD_sequenceBound(0), 2);
|
||||||
|
assert_eq!(ZSTD_sequenceBound(3), 3);
|
||||||
|
assert_eq!(ZSTD_sequenceBound(1024), 344);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn merge_block_delimiters_carries_literals_forward() {
|
||||||
|
let mut sequences = [
|
||||||
|
ZstdSequence {
|
||||||
|
offset: 0,
|
||||||
|
lit_length: 3,
|
||||||
|
match_length: 0,
|
||||||
|
rep: 0,
|
||||||
|
},
|
||||||
|
ZstdSequence {
|
||||||
|
offset: 7,
|
||||||
|
lit_length: 1,
|
||||||
|
match_length: 4,
|
||||||
|
rep: 2,
|
||||||
|
},
|
||||||
|
ZstdSequence {
|
||||||
|
offset: 0,
|
||||||
|
lit_length: 9,
|
||||||
|
match_length: 0,
|
||||||
|
rep: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
unsafe { ZSTD_mergeBlockDelimiters(sequences.as_mut_ptr(), sequences.len()) },
|
||||||
|
1
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
sequences[0],
|
||||||
|
ZstdSequence {
|
||||||
|
offset: 7,
|
||||||
|
lit_length: 4,
|
||||||
|
match_length: 4,
|
||||||
|
rep: 2,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user