feat(compress): move sequence leaves to Rust
Move sequence-store byte accounting, chunk derivation, and repcode resolution into the Rust compression module. Move MT raw-sequence buffer conversions into Rust while preserving the existing C adapters and ABI layouts. Test Plan:\n- cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression\n- cargo clippy --manifest-path rust/Cargo.toml\n- cargo clippy --manifest-path rust/Cargo.toml --benches\n- cargo clippy --manifest-path rust/Cargo.toml --tests\n- make -B -C lib -j2 lib\n- make -B -C tests -j2 test-cli-tests
This commit is contained in:
@@ -241,6 +241,13 @@ size_t ZSTD_rust_fastSequenceLengthSum(const ZSTD_Sequence* seqBuf,
|
|||||||
size_t seqBufSize);
|
size_t seqBufSize);
|
||||||
int ZSTD_rust_isRLE(const BYTE* src, size_t length);
|
int ZSTD_rust_isRLE(const BYTE* src, size_t length);
|
||||||
int ZSTD_rust_maybeRLE(const SeqStore_t* seqStore);
|
int ZSTD_rust_maybeRLE(const SeqStore_t* seqStore);
|
||||||
|
size_t ZSTD_rust_countSeqStoreLiteralsBytes(const SeqStore_t* seqStore);
|
||||||
|
size_t ZSTD_rust_countSeqStoreMatchBytes(const SeqStore_t* seqStore);
|
||||||
|
void ZSTD_rust_deriveSeqStoreChunk(SeqStore_t* resultSeqStore,
|
||||||
|
const SeqStore_t* originalSeqStore,
|
||||||
|
size_t startIdx, size_t endIdx);
|
||||||
|
U32 ZSTD_rust_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM],
|
||||||
|
U32 offBase, U32 ll0);
|
||||||
|
|
||||||
typedef char ZSTD_rust_stats_seqdef_layout[(sizeof(SeqDef) == 8) ? 1 : -1];
|
typedef char ZSTD_rust_stats_seqdef_layout[(sizeof(SeqDef) == 8) ? 1 : -1];
|
||||||
typedef char ZSTD_rust_stats_seqstore_long_length_pos[
|
typedef char ZSTD_rust_stats_seqstore_long_length_pos[
|
||||||
@@ -2817,31 +2824,13 @@ ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(SeqStore_t* seqStore, ZSTD_CC
|
|||||||
/* Returns literals bytes represented in a seqStore */
|
/* Returns literals bytes represented in a seqStore */
|
||||||
static size_t ZSTD_countSeqStoreLiteralsBytes(const SeqStore_t* const seqStore)
|
static size_t ZSTD_countSeqStoreLiteralsBytes(const SeqStore_t* const seqStore)
|
||||||
{
|
{
|
||||||
size_t literalsBytes = 0;
|
return ZSTD_rust_countSeqStoreLiteralsBytes(seqStore);
|
||||||
size_t const nbSeqs = (size_t)(seqStore->sequences - seqStore->sequencesStart);
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < nbSeqs; ++i) {
|
|
||||||
SeqDef const seq = seqStore->sequencesStart[i];
|
|
||||||
literalsBytes += seq.litLength;
|
|
||||||
if (i == seqStore->longLengthPos && seqStore->longLengthType == ZSTD_llt_literalLength) {
|
|
||||||
literalsBytes += 0x10000;
|
|
||||||
} }
|
|
||||||
return literalsBytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns match bytes represented in a seqStore */
|
/* Returns match bytes represented in a seqStore */
|
||||||
static size_t ZSTD_countSeqStoreMatchBytes(const SeqStore_t* const seqStore)
|
static size_t ZSTD_countSeqStoreMatchBytes(const SeqStore_t* const seqStore)
|
||||||
{
|
{
|
||||||
size_t matchBytes = 0;
|
return ZSTD_rust_countSeqStoreMatchBytes(seqStore);
|
||||||
size_t const nbSeqs = (size_t)(seqStore->sequences - seqStore->sequencesStart);
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < nbSeqs; ++i) {
|
|
||||||
SeqDef seq = seqStore->sequencesStart[i];
|
|
||||||
matchBytes += seq.mlBase + MINMATCH;
|
|
||||||
if (i == seqStore->longLengthPos && seqStore->longLengthType == ZSTD_llt_matchLength) {
|
|
||||||
matchBytes += 0x10000;
|
|
||||||
} }
|
|
||||||
return matchBytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Derives the seqStore that is a chunk of the originalSeqStore from [startIdx, endIdx).
|
/* Derives the seqStore that is a chunk of the originalSeqStore from [startIdx, endIdx).
|
||||||
@@ -2851,32 +2840,8 @@ static void ZSTD_deriveSeqStoreChunk(SeqStore_t* resultSeqStore,
|
|||||||
const SeqStore_t* originalSeqStore,
|
const SeqStore_t* originalSeqStore,
|
||||||
size_t startIdx, size_t endIdx)
|
size_t startIdx, size_t endIdx)
|
||||||
{
|
{
|
||||||
*resultSeqStore = *originalSeqStore;
|
ZSTD_rust_deriveSeqStoreChunk(resultSeqStore, originalSeqStore,
|
||||||
if (startIdx > 0) {
|
startIdx, endIdx);
|
||||||
resultSeqStore->sequences = originalSeqStore->sequencesStart + startIdx;
|
|
||||||
resultSeqStore->litStart += ZSTD_countSeqStoreLiteralsBytes(resultSeqStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Move longLengthPos into the correct position if necessary */
|
|
||||||
if (originalSeqStore->longLengthType != ZSTD_llt_none) {
|
|
||||||
if (originalSeqStore->longLengthPos < startIdx || originalSeqStore->longLengthPos > endIdx) {
|
|
||||||
resultSeqStore->longLengthType = ZSTD_llt_none;
|
|
||||||
} else {
|
|
||||||
resultSeqStore->longLengthPos -= (U32)startIdx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resultSeqStore->sequencesStart = originalSeqStore->sequencesStart + startIdx;
|
|
||||||
resultSeqStore->sequences = originalSeqStore->sequencesStart + endIdx;
|
|
||||||
if (endIdx == (size_t)(originalSeqStore->sequences - originalSeqStore->sequencesStart)) {
|
|
||||||
/* This accounts for possible last literals if the derived chunk reaches the end of the block */
|
|
||||||
assert(resultSeqStore->lit == originalSeqStore->lit);
|
|
||||||
} else {
|
|
||||||
size_t const literalsBytes = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore);
|
|
||||||
resultSeqStore->lit = resultSeqStore->litStart + literalsBytes;
|
|
||||||
}
|
|
||||||
resultSeqStore->llCode += startIdx;
|
|
||||||
resultSeqStore->mlCode += startIdx;
|
|
||||||
resultSeqStore->ofCode += startIdx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2886,20 +2851,8 @@ static void ZSTD_deriveSeqStoreChunk(SeqStore_t* resultSeqStore,
|
|||||||
static U32
|
static U32
|
||||||
ZSTD_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM], const U32 offBase, const U32 ll0)
|
ZSTD_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM], const U32 offBase, const U32 ll0)
|
||||||
{
|
{
|
||||||
U32 const adjustedRepCode = OFFBASE_TO_REPCODE(offBase) - 1 + ll0; /* [ 0 - 3 ] */
|
|
||||||
assert(OFFBASE_IS_REPCODE(offBase));
|
assert(OFFBASE_IS_REPCODE(offBase));
|
||||||
if (adjustedRepCode == ZSTD_REP_NUM) {
|
return ZSTD_rust_resolveRepcodeToRawOffset(rep, offBase, ll0);
|
||||||
assert(ll0);
|
|
||||||
/* litlength == 0 and offCode == 2 implies selection of first repcode - 1
|
|
||||||
* This is only valid if it results in a valid offset value, aka > 0.
|
|
||||||
* Note : it may happen that `rep[0]==1` in exceptional circumstances.
|
|
||||||
* In which case this function will return 0, which is an invalid offset.
|
|
||||||
* It's not an issue though, since this value will be
|
|
||||||
* compared and discarded within ZSTD_seqStore_resolveOffCodes().
|
|
||||||
*/
|
|
||||||
return rep[0] - 1;
|
|
||||||
}
|
|
||||||
return rep[adjustedRepCode];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -115,6 +115,25 @@ void ZSTDMT_rust_buffer_pool_release(ZSTDMT_RustBufferPool* pool,
|
|||||||
ZSTDMT_RustBuffer ZSTDMT_rust_buffer_pool_resize(ZSTDMT_RustBufferPool* pool,
|
ZSTDMT_RustBuffer ZSTDMT_rust_buffer_pool_resize(ZSTDMT_RustBufferPool* pool,
|
||||||
ZSTDMT_RustBuffer buffer);
|
ZSTDMT_RustBuffer buffer);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
rawSeq* seq;
|
||||||
|
size_t pos;
|
||||||
|
size_t posInSequence;
|
||||||
|
size_t size;
|
||||||
|
size_t capacity;
|
||||||
|
} ZSTDMT_RustRawSeqStore;
|
||||||
|
|
||||||
|
typedef char ZSTDMT_rust_raw_seq_layout[
|
||||||
|
(sizeof(rawSeq) == 3 * sizeof(U32)) ? 1 : -1];
|
||||||
|
typedef char ZSTDMT_rust_raw_seq_store_layout[
|
||||||
|
(sizeof(ZSTDMT_RustRawSeqStore) == sizeof(RawSeqStore_t)
|
||||||
|
&& offsetof(ZSTDMT_RustRawSeqStore, pos) == offsetof(RawSeqStore_t, pos)
|
||||||
|
&& offsetof(ZSTDMT_RustRawSeqStore, capacity) == offsetof(RawSeqStore_t, capacity))
|
||||||
|
? 1 : -1];
|
||||||
|
|
||||||
|
ZSTDMT_RustRawSeqStore ZSTDMT_rust_bufferToSeq(ZSTDMT_RustBuffer buffer);
|
||||||
|
ZSTDMT_RustBuffer ZSTDMT_rust_seqToBuffer(ZSTDMT_RustRawSeqStore seq);
|
||||||
|
|
||||||
unsigned ZSTDMT_rust_computeTargetJobLog(unsigned windowLog, unsigned chainLog,
|
unsigned ZSTDMT_rust_computeTargetJobLog(unsigned windowLog, unsigned chainLog,
|
||||||
int strategy, int enableLdm);
|
int strategy, int enableLdm);
|
||||||
int ZSTDMT_rust_overlapLog(int overlapLog, int strategy);
|
int ZSTDMT_rust_overlapLog(int overlapLog, int strategy);
|
||||||
@@ -234,18 +253,29 @@ static size_t ZSTDMT_sizeof_seqPool(ZSTDMT_seqPool* seqPool)
|
|||||||
|
|
||||||
static RawSeqStore_t bufferToSeq(Buffer buffer)
|
static RawSeqStore_t bufferToSeq(Buffer buffer)
|
||||||
{
|
{
|
||||||
RawSeqStore_t seq = kNullRawSeqStore;
|
ZSTDMT_RustRawSeqStore const rustSeq =
|
||||||
seq.seq = (rawSeq*)buffer.start;
|
ZSTDMT_rust_bufferToSeq((ZSTDMT_RustBuffer){ buffer.start, buffer.capacity });
|
||||||
seq.capacity = buffer.capacity / sizeof(rawSeq);
|
RawSeqStore_t seq = {
|
||||||
|
(rawSeq*)rustSeq.seq,
|
||||||
|
rustSeq.pos,
|
||||||
|
rustSeq.posInSequence,
|
||||||
|
rustSeq.size,
|
||||||
|
rustSeq.capacity
|
||||||
|
};
|
||||||
return seq;
|
return seq;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Buffer seqToBuffer(RawSeqStore_t seq)
|
static Buffer seqToBuffer(RawSeqStore_t seq)
|
||||||
{
|
{
|
||||||
Buffer buffer;
|
ZSTDMT_RustRawSeqStore const rustSeq = {
|
||||||
buffer.start = seq.seq;
|
seq.seq,
|
||||||
buffer.capacity = seq.capacity * sizeof(rawSeq);
|
seq.pos,
|
||||||
return buffer;
|
seq.posInSequence,
|
||||||
|
seq.size,
|
||||||
|
seq.capacity
|
||||||
|
};
|
||||||
|
ZSTDMT_RustBuffer const rustBuffer = ZSTDMT_rust_seqToBuffer(rustSeq);
|
||||||
|
return (Buffer){ rustBuffer.start, rustBuffer.capacity };
|
||||||
}
|
}
|
||||||
|
|
||||||
static RawSeqStore_t ZSTDMT_getSeq(ZSTDMT_seqPool* seqPool)
|
static RawSeqStore_t ZSTDMT_getSeq(ZSTDMT_seqPool* seqPool)
|
||||||
|
|||||||
@@ -272,6 +272,141 @@ pub unsafe extern "C" fn ZSTD_rust_maybeRLE(seq_store: *const SeqStore_t) -> c_i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the literal bytes represented by a sequence store.
|
||||||
|
///
|
||||||
|
/// The stored `u16` literal length may represent one long literal through the
|
||||||
|
/// sequence store's side-band marker. This is the Rust leaf for C's
|
||||||
|
/// `ZSTD_countSeqStoreLiteralsBytes()` helper.
|
||||||
|
#[inline]
|
||||||
|
unsafe fn count_seq_store_literals_bytes(seq_store: *const SeqStore_t) -> usize {
|
||||||
|
let seq_store = unsafe { &*seq_store };
|
||||||
|
let nb_seqs = unsafe { seq_store.sequences.offset_from(seq_store.sequencesStart) } as usize;
|
||||||
|
let mut literals_bytes = 0usize;
|
||||||
|
for index in 0..nb_seqs {
|
||||||
|
let sequence = unsafe { *seq_store.sequencesStart.add(index) };
|
||||||
|
literals_bytes = literals_bytes.wrapping_add(sequence.litLength as usize);
|
||||||
|
if index == seq_store.longLengthPos as usize
|
||||||
|
&& seq_store.longLengthType == ZSTD_LLT_LITERAL_LENGTH
|
||||||
|
{
|
||||||
|
literals_bytes = literals_bytes.wrapping_add(0x10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
literals_bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the match bytes represented by a sequence store.
|
||||||
|
///
|
||||||
|
/// `mlBase` stores `matchLength - MINMATCH`; the side-band long-length marker
|
||||||
|
/// adds the same `0x10000` extension used by the original C helper.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn ZSTD_rust_countSeqStoreLiteralsBytes(
|
||||||
|
seq_store: *const SeqStore_t,
|
||||||
|
) -> usize {
|
||||||
|
unsafe { count_seq_store_literals_bytes(seq_store) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn count_seq_store_match_bytes(seq_store: *const SeqStore_t) -> usize {
|
||||||
|
let seq_store = unsafe { &*seq_store };
|
||||||
|
let nb_seqs = unsafe { seq_store.sequences.offset_from(seq_store.sequencesStart) } as usize;
|
||||||
|
let mut match_bytes = 0usize;
|
||||||
|
for index in 0..nb_seqs {
|
||||||
|
let sequence = unsafe { *seq_store.sequencesStart.add(index) };
|
||||||
|
match_bytes = match_bytes.wrapping_add(sequence.mlBase as usize + MINMATCH);
|
||||||
|
if index == seq_store.longLengthPos as usize
|
||||||
|
&& seq_store.longLengthType == ZSTD_LLT_MATCH_LENGTH
|
||||||
|
{
|
||||||
|
match_bytes = match_bytes.wrapping_add(0x10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match_bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the match bytes represented by a sequence store.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn ZSTD_rust_countSeqStoreMatchBytes(seq_store: *const SeqStore_t) -> usize {
|
||||||
|
unsafe { count_seq_store_match_bytes(seq_store) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Derives the sequence-store view for the half-open sequence range
|
||||||
|
/// `[start_idx, end_idx)`.
|
||||||
|
///
|
||||||
|
/// This is the Rust leaf for C's `ZSTD_deriveSeqStoreChunk()`. The original
|
||||||
|
/// store remains untouched; the result is a shallow copy whose pointers refer
|
||||||
|
/// into the original sequence, literal, and code buffers.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn ZSTD_rust_deriveSeqStoreChunk(
|
||||||
|
result_seq_store: *mut SeqStore_t,
|
||||||
|
original_seq_store: *const SeqStore_t,
|
||||||
|
start_idx: usize,
|
||||||
|
end_idx: usize,
|
||||||
|
) {
|
||||||
|
let original_seq_store = unsafe { &*original_seq_store };
|
||||||
|
let result_seq_store = unsafe { &mut *result_seq_store };
|
||||||
|
*result_seq_store = unsafe { ptr::read(original_seq_store) };
|
||||||
|
|
||||||
|
if start_idx > 0 {
|
||||||
|
result_seq_store.sequences = unsafe { original_seq_store.sequencesStart.add(start_idx) };
|
||||||
|
let literals_bytes =
|
||||||
|
unsafe { count_seq_store_literals_bytes(std::ptr::addr_of!(*result_seq_store)) };
|
||||||
|
result_seq_store.litStart = unsafe { result_seq_store.litStart.add(literals_bytes) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move longLengthPos into the correct position if necessary. */
|
||||||
|
if original_seq_store.longLengthType != 0 {
|
||||||
|
if original_seq_store.longLengthPos as usize > end_idx
|
||||||
|
|| (original_seq_store.longLengthPos as usize) < start_idx
|
||||||
|
{
|
||||||
|
result_seq_store.longLengthType = 0;
|
||||||
|
} else {
|
||||||
|
result_seq_store.longLengthPos = original_seq_store
|
||||||
|
.longLengthPos
|
||||||
|
.wrapping_sub(start_idx as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result_seq_store.sequencesStart = unsafe { original_seq_store.sequencesStart.add(start_idx) };
|
||||||
|
result_seq_store.sequences = unsafe { original_seq_store.sequencesStart.add(end_idx) };
|
||||||
|
|
||||||
|
let original_nb_sequences = unsafe {
|
||||||
|
original_seq_store
|
||||||
|
.sequences
|
||||||
|
.offset_from(original_seq_store.sequencesStart)
|
||||||
|
} as usize;
|
||||||
|
if end_idx == original_nb_sequences {
|
||||||
|
/* This accounts for possible last literals at the end of the block. */
|
||||||
|
debug_assert_eq!(result_seq_store.lit, original_seq_store.lit);
|
||||||
|
} else {
|
||||||
|
let literals_bytes =
|
||||||
|
unsafe { count_seq_store_literals_bytes(std::ptr::addr_of!(*result_seq_store)) };
|
||||||
|
result_seq_store.lit = unsafe { result_seq_store.litStart.add(literals_bytes) };
|
||||||
|
}
|
||||||
|
result_seq_store.llCode = unsafe { result_seq_store.llCode.add(start_idx) };
|
||||||
|
result_seq_store.mlCode = unsafe { result_seq_store.mlCode.add(start_idx) };
|
||||||
|
result_seq_store.ofCode = unsafe { result_seq_store.ofCode.add(start_idx) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves a stored repcode against the current raw-offset history.
|
||||||
|
///
|
||||||
|
/// This is the Rust leaf for C's `ZSTD_resolveRepcodeToRawOffset()`. The
|
||||||
|
/// caller must pass an `off_base` in the repcode range `1..=ZSTD_REP_NUM` and
|
||||||
|
/// an `ll0` value of zero or one.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn ZSTD_rust_resolveRepcodeToRawOffset(
|
||||||
|
rep: *const u32,
|
||||||
|
off_base: u32,
|
||||||
|
ll0: u32,
|
||||||
|
) -> u32 {
|
||||||
|
debug_assert!((1..=ZSTD_REP_NUM as u32).contains(&off_base));
|
||||||
|
let adjusted_rep_code = off_base.wrapping_sub(1).wrapping_add(ll0);
|
||||||
|
let rep = unsafe { std::slice::from_raw_parts(rep, ZSTD_REP_NUM) };
|
||||||
|
if adjusted_rep_code == ZSTD_REP_NUM as u32 {
|
||||||
|
debug_assert_ne!(ll0, 0);
|
||||||
|
rep[0].wrapping_sub(1)
|
||||||
|
} else {
|
||||||
|
rep[adjusted_rep_code as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// C's `SeqCollector` leaf from `zstd_compress_internal.h`.
|
/// C's `SeqCollector` leaf from `zstd_compress_internal.h`.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct SeqCollector {
|
pub struct SeqCollector {
|
||||||
@@ -1598,6 +1733,178 @@ mod tests {
|
|||||||
assert_eq!(unsafe { ZSTD_rust_maybeRLE(&seq_store) }, 0);
|
assert_eq!(unsafe { ZSTD_rust_maybeRLE(&seq_store) }, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sequence_store_byte_counts_include_the_side_band_long_length() {
|
||||||
|
let mut sequences = [
|
||||||
|
SeqDef {
|
||||||
|
offBase: 4,
|
||||||
|
litLength: 2,
|
||||||
|
mlBase: 4,
|
||||||
|
},
|
||||||
|
SeqDef {
|
||||||
|
offBase: 5,
|
||||||
|
litLength: 5,
|
||||||
|
mlBase: 5,
|
||||||
|
},
|
||||||
|
SeqDef {
|
||||||
|
offBase: 6,
|
||||||
|
litLength: 7,
|
||||||
|
mlBase: 6,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let mut literals = [0u8; 32];
|
||||||
|
let sequences_start = sequences.as_mut_ptr();
|
||||||
|
let literals_start = literals.as_mut_ptr();
|
||||||
|
let mut seq_store = SeqStore_t {
|
||||||
|
sequencesStart: sequences_start,
|
||||||
|
sequences: unsafe { sequences_start.add(sequences.len()) },
|
||||||
|
litStart: literals_start,
|
||||||
|
lit: unsafe { literals_start.add(14) },
|
||||||
|
llCode: std::ptr::null_mut(),
|
||||||
|
mlCode: std::ptr::null_mut(),
|
||||||
|
ofCode: std::ptr::null_mut(),
|
||||||
|
maxNbSeq: sequences.len(),
|
||||||
|
maxNbLit: literals.len(),
|
||||||
|
longLengthType: ZSTD_LLT_LITERAL_LENGTH,
|
||||||
|
longLengthPos: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
unsafe { ZSTD_rust_countSeqStoreLiteralsBytes(&seq_store) },
|
||||||
|
2 + 5 + 7 + 0x10000
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
unsafe { ZSTD_rust_countSeqStoreMatchBytes(&seq_store) },
|
||||||
|
(4 + MINMATCH) + (5 + MINMATCH) + (6 + MINMATCH)
|
||||||
|
);
|
||||||
|
|
||||||
|
seq_store.longLengthType = ZSTD_LLT_MATCH_LENGTH;
|
||||||
|
assert_eq!(
|
||||||
|
unsafe { ZSTD_rust_countSeqStoreLiteralsBytes(&seq_store) },
|
||||||
|
2 + 5 + 7
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
unsafe { ZSTD_rust_countSeqStoreMatchBytes(&seq_store) },
|
||||||
|
(4 + MINMATCH) + (5 + MINMATCH) + (6 + MINMATCH) + 0x10000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn derive_seq_store_chunk_shifts_views_and_long_length_position() {
|
||||||
|
let mut sequences = [
|
||||||
|
SeqDef {
|
||||||
|
offBase: 4,
|
||||||
|
litLength: 2,
|
||||||
|
mlBase: 0,
|
||||||
|
},
|
||||||
|
SeqDef {
|
||||||
|
offBase: 5,
|
||||||
|
litLength: 5,
|
||||||
|
mlBase: 1,
|
||||||
|
},
|
||||||
|
SeqDef {
|
||||||
|
offBase: 6,
|
||||||
|
litLength: 7,
|
||||||
|
mlBase: 2,
|
||||||
|
},
|
||||||
|
SeqDef {
|
||||||
|
offBase: 7,
|
||||||
|
litLength: 3,
|
||||||
|
mlBase: 3,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let mut literals = [0u8; 20];
|
||||||
|
let mut ll_codes = [10u8; 4];
|
||||||
|
let mut ml_codes = [20u8; 4];
|
||||||
|
let mut of_codes = [30u8; 4];
|
||||||
|
let sequences_start = sequences.as_mut_ptr();
|
||||||
|
let literals_start = literals.as_mut_ptr();
|
||||||
|
let original = SeqStore_t {
|
||||||
|
sequencesStart: sequences_start,
|
||||||
|
sequences: unsafe { sequences_start.add(sequences.len()) },
|
||||||
|
litStart: literals_start,
|
||||||
|
lit: unsafe { literals_start.add(17) },
|
||||||
|
llCode: ll_codes.as_mut_ptr(),
|
||||||
|
mlCode: ml_codes.as_mut_ptr(),
|
||||||
|
ofCode: of_codes.as_mut_ptr(),
|
||||||
|
maxNbSeq: sequences.len(),
|
||||||
|
maxNbLit: literals.len(),
|
||||||
|
longLengthType: ZSTD_LLT_MATCH_LENGTH,
|
||||||
|
longLengthPos: 2,
|
||||||
|
};
|
||||||
|
let mut result = SeqStore_t {
|
||||||
|
sequencesStart: std::ptr::null_mut(),
|
||||||
|
sequences: std::ptr::null_mut(),
|
||||||
|
litStart: std::ptr::null_mut(),
|
||||||
|
lit: std::ptr::null_mut(),
|
||||||
|
llCode: std::ptr::null_mut(),
|
||||||
|
mlCode: std::ptr::null_mut(),
|
||||||
|
ofCode: std::ptr::null_mut(),
|
||||||
|
maxNbSeq: 0,
|
||||||
|
maxNbLit: 0,
|
||||||
|
longLengthType: 0,
|
||||||
|
longLengthPos: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ZSTD_rust_deriveSeqStoreChunk(&mut result, &original, 1, 3);
|
||||||
|
}
|
||||||
|
assert_eq!(result.sequencesStart, unsafe { sequences_start.add(1) });
|
||||||
|
assert_eq!(result.sequences, unsafe { sequences_start.add(3) });
|
||||||
|
assert_eq!(result.litStart, unsafe { literals_start.add(2) });
|
||||||
|
assert_eq!(result.lit, unsafe { literals_start.add(14) });
|
||||||
|
assert_eq!(result.llCode, unsafe { ll_codes.as_mut_ptr().add(1) });
|
||||||
|
assert_eq!(result.mlCode, unsafe { ml_codes.as_mut_ptr().add(1) });
|
||||||
|
assert_eq!(result.ofCode, unsafe { of_codes.as_mut_ptr().add(1) });
|
||||||
|
assert_eq!(result.longLengthType, ZSTD_LLT_MATCH_LENGTH);
|
||||||
|
assert_eq!(result.longLengthPos, 1);
|
||||||
|
assert_eq!(result.maxNbSeq, original.maxNbSeq);
|
||||||
|
assert_eq!(result.maxNbLit, original.maxNbLit);
|
||||||
|
assert_eq!(
|
||||||
|
unsafe { ZSTD_rust_countSeqStoreMatchBytes(&result) },
|
||||||
|
(1 + MINMATCH) + (2 + MINMATCH) + 0x10000
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ZSTD_rust_deriveSeqStoreChunk(&mut result, &original, 3, 4);
|
||||||
|
}
|
||||||
|
assert_eq!(result.sequencesStart, unsafe { sequences_start.add(3) });
|
||||||
|
assert_eq!(result.sequences, unsafe { sequences_start.add(4) });
|
||||||
|
assert_eq!(result.litStart, unsafe { literals_start.add(14) });
|
||||||
|
assert_eq!(result.lit, original.lit);
|
||||||
|
assert_eq!(result.longLengthType, 0);
|
||||||
|
assert_eq!(result.longLengthPos, original.longLengthPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolve_repcode_to_raw_offset_matches_c_repcode_numbering() {
|
||||||
|
let reps = [11u32, 22, 33];
|
||||||
|
assert_eq!(
|
||||||
|
unsafe { ZSTD_rust_resolveRepcodeToRawOffset(reps.as_ptr(), 1, 0) },
|
||||||
|
11
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
unsafe { ZSTD_rust_resolveRepcodeToRawOffset(reps.as_ptr(), 1, 1) },
|
||||||
|
22
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
unsafe { ZSTD_rust_resolveRepcodeToRawOffset(reps.as_ptr(), 2, 0) },
|
||||||
|
22
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
unsafe { ZSTD_rust_resolveRepcodeToRawOffset(reps.as_ptr(), 2, 1) },
|
||||||
|
33
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
unsafe { ZSTD_rust_resolveRepcodeToRawOffset(reps.as_ptr(), 3, 0) },
|
||||||
|
33
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
unsafe { ZSTD_rust_resolveRepcodeToRawOffset(reps.as_ptr(), 3, 1) },
|
||||||
|
10
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn repcode_updates_match_the_c_sum_type_rules() {
|
fn repcode_updates_match_the_c_sum_type_rules() {
|
||||||
let mut reps = [1, 4, 8];
|
let mut reps = [1, 4, 8];
|
||||||
|
|||||||
@@ -159,6 +159,62 @@ pub struct ZstdMtBuffer {
|
|||||||
pub capacity: usize,
|
pub capacity: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ABI-compatible representation of `rawSeq` from `zstd_compress_internal.h`.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct ZstdMtRawSeq {
|
||||||
|
pub offset: u32,
|
||||||
|
pub litLength: u32,
|
||||||
|
pub matchLength: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ABI-compatible representation of `RawSeqStore_t` from
|
||||||
|
/// `zstd_compress_internal.h`.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct ZstdMtRawSeqStore {
|
||||||
|
pub seq: *mut ZstdMtRawSeq,
|
||||||
|
pub pos: usize,
|
||||||
|
pub posInSequence: usize,
|
||||||
|
pub size: usize,
|
||||||
|
pub capacity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn buffer_to_seq(buffer: ZstdMtBuffer) -> ZstdMtRawSeqStore {
|
||||||
|
ZstdMtRawSeqStore {
|
||||||
|
seq: buffer.start.cast::<ZstdMtRawSeq>(),
|
||||||
|
pos: 0,
|
||||||
|
posInSequence: 0,
|
||||||
|
size: 0,
|
||||||
|
capacity: buffer.capacity / mem::size_of::<ZstdMtRawSeq>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn seq_to_buffer(seq: ZstdMtRawSeqStore) -> ZstdMtBuffer {
|
||||||
|
ZstdMtBuffer {
|
||||||
|
start: seq.seq.cast::<c_void>(),
|
||||||
|
capacity: seq.capacity.wrapping_mul(mem::size_of::<ZstdMtRawSeq>()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a byte buffer into the raw-sequence store view used by the MT
|
||||||
|
/// sequence pool. The capacity is expressed in whole `rawSeq` elements, just
|
||||||
|
/// like the original C conversion leaf.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn ZSTDMT_rust_bufferToSeq(buffer: ZstdMtBuffer) -> ZstdMtRawSeqStore {
|
||||||
|
buffer_to_seq(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the raw-sequence store view back to a byte buffer for pool APIs.
|
||||||
|
/// The returned capacity is measured in bytes and follows C `size_t` wraparound
|
||||||
|
/// semantics for the multiplication.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn ZSTDMT_rust_seqToBuffer(seq: ZstdMtRawSeqStore) -> ZstdMtBuffer {
|
||||||
|
seq_to_buffer(seq)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct BufferPoolState {
|
struct BufferPoolState {
|
||||||
buffer_size: usize,
|
buffer_size: usize,
|
||||||
@@ -721,6 +777,54 @@ mod tests {
|
|||||||
opaque: ptr::null_mut(),
|
opaque: ptr::null_mut(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn raw_seq_buffer_conversion_uses_whole_element_capacity() {
|
||||||
|
let mut sequences = [ZstdMtRawSeq::default(); 3];
|
||||||
|
let element_size = mem::size_of::<ZstdMtRawSeq>();
|
||||||
|
let buffer = ZstdMtBuffer {
|
||||||
|
start: sequences.as_mut_ptr().cast(),
|
||||||
|
capacity: element_size * sequences.len() + element_size - 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let seq = ZSTDMT_rust_bufferToSeq(buffer);
|
||||||
|
assert_eq!(seq.seq, sequences.as_mut_ptr());
|
||||||
|
assert_eq!(seq.pos, 0);
|
||||||
|
assert_eq!(seq.posInSequence, 0);
|
||||||
|
assert_eq!(seq.size, 0);
|
||||||
|
assert_eq!(seq.capacity, sequences.len());
|
||||||
|
|
||||||
|
let roundtrip = ZSTDMT_rust_seqToBuffer(seq);
|
||||||
|
assert_eq!(roundtrip.start, buffer.start);
|
||||||
|
assert_eq!(roundtrip.capacity, element_size * sequences.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn raw_seq_to_buffer_preserves_pointer_and_size_t_multiplication() {
|
||||||
|
let seq = ZstdMtRawSeqStore {
|
||||||
|
seq: ptr::null_mut(),
|
||||||
|
pos: 4,
|
||||||
|
posInSequence: 5,
|
||||||
|
size: 6,
|
||||||
|
capacity: usize::MAX,
|
||||||
|
};
|
||||||
|
let buffer = ZSTDMT_rust_seqToBuffer(seq);
|
||||||
|
assert!(buffer.start.is_null());
|
||||||
|
assert_eq!(
|
||||||
|
buffer.capacity,
|
||||||
|
usize::MAX.wrapping_mul(mem::size_of::<ZstdMtRawSeq>())
|
||||||
|
);
|
||||||
|
|
||||||
|
let empty = ZSTDMT_rust_bufferToSeq(ZstdMtBuffer {
|
||||||
|
start: ptr::null_mut(),
|
||||||
|
capacity: mem::size_of::<ZstdMtRawSeq>() - 1,
|
||||||
|
});
|
||||||
|
assert!(empty.seq.is_null());
|
||||||
|
assert_eq!(empty.capacity, 0);
|
||||||
|
assert_eq!(empty.pos, 0);
|
||||||
|
assert_eq!(empty.posInSequence, 0);
|
||||||
|
assert_eq!(empty.size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn buffer_pool_reuses_and_resizes_buffers() {
|
fn buffer_pool_reuses_and_resizes_buffers() {
|
||||||
let pool = unsafe { ZSTDMT_rust_buffer_pool_create(2, DEFAULT_MEM) };
|
let pool = unsafe { ZSTDMT_rust_buffer_pool_create(2, DEFAULT_MEM) };
|
||||||
|
|||||||
Reference in New Issue
Block a user