Files
zstd-rs/rust/src/mem.rs
T
ddidderr 089f8e5b4d feat(rust): add common primitive compatibility layer
Introduce the Rust static library and move the shared byte, bitstream, CPU,
error, xxHash, debug, and public-common implementations into it. Thin C shims
preserve the existing header-driven C build while original tests link the Rust
archive.

This establishes the ABI-safe foundation for later codec and CLI ports;
entropy coding, runtime support, codecs, dictionaries, and the CLI remain C.
The top-down migration map documents that boundary and its validation path.

Test Plan:
- cargo fmt --check
- cargo test --all-targets
- cargo clippy --all-targets -- -D warnings
- cargo build --release

Refs: rust/README.md
2026-07-10 20:02:17 +02:00

326 lines
8.0 KiB
Rust

#![allow(non_snake_case)]
use std::mem;
pub type U8 = u8;
pub type S8 = i8;
pub type U16 = u16;
pub type S16 = i16;
pub type U32 = u32;
pub type S32 = i32;
pub type U64 = u64;
pub type S64 = i64;
pub type BYTE = u8;
#[inline]
pub fn MEM_32bits() -> bool {
mem::size_of::<usize>() == 4
}
#[inline]
pub fn MEM_64bits() -> bool {
mem::size_of::<usize>() == 8
}
#[inline]
pub fn MEM_isLittleEndian() -> bool {
cfg!(target_endian = "little")
}
#[inline]
pub unsafe fn MEM_read16(mem_ptr: *const std::ffi::c_void) -> U16 {
(mem_ptr as *const U16).read_unaligned()
}
#[inline]
pub unsafe fn MEM_read32(mem_ptr: *const std::ffi::c_void) -> U32 {
(mem_ptr as *const U32).read_unaligned()
}
#[inline]
pub unsafe fn MEM_read64(mem_ptr: *const std::ffi::c_void) -> U64 {
(mem_ptr as *const U64).read_unaligned()
}
#[inline]
pub unsafe fn MEM_readST(mem_ptr: *const std::ffi::c_void) -> usize {
(mem_ptr as *const usize).read_unaligned()
}
#[inline]
pub unsafe fn MEM_write16(mem_ptr: *mut std::ffi::c_void, value: U16) {
(mem_ptr as *mut U16).write_unaligned(value);
}
#[inline]
pub unsafe fn MEM_write32(mem_ptr: *mut std::ffi::c_void, value: U32) {
(mem_ptr as *mut U32).write_unaligned(value);
}
#[inline]
pub unsafe fn MEM_write64(mem_ptr: *mut std::ffi::c_void, value: U64) {
(mem_ptr as *mut U64).write_unaligned(value);
}
#[inline]
pub unsafe fn MEM_readLE16(mem_ptr: *const std::ffi::c_void) -> U16 {
U16::from_le(MEM_read16(mem_ptr))
}
#[inline]
pub unsafe fn MEM_readLE24(mem_ptr: *const std::ffi::c_void) -> U32 {
let low = MEM_readLE16(mem_ptr);
let high = *mem_ptr.cast::<U8>().add(2) as U32;
(low as U32) | (high << 16)
}
#[inline]
pub unsafe fn MEM_readLE32(mem_ptr: *const std::ffi::c_void) -> U32 {
U32::from_le(MEM_read32(mem_ptr))
}
#[inline]
pub unsafe fn MEM_readLE64(mem_ptr: *const std::ffi::c_void) -> U64 {
U64::from_le(MEM_read64(mem_ptr))
}
#[inline]
pub unsafe fn MEM_readLEST(mem_ptr: *const std::ffi::c_void) -> usize {
if MEM_32bits() {
MEM_readLE32(mem_ptr) as usize
} else {
MEM_readLE64(mem_ptr) as usize
}
}
#[inline]
pub unsafe fn MEM_writeLE16(mem_ptr: *mut std::ffi::c_void, val: U16) {
MEM_write16(mem_ptr, val.to_le());
}
#[inline]
pub unsafe fn MEM_writeLE24(mem_ptr: *mut std::ffi::c_void, val: U32) {
MEM_writeLE16(mem_ptr, val as U16);
*mem_ptr.cast::<U8>().add(2) = (val >> 16) as U8;
}
#[inline]
pub unsafe fn MEM_writeLE32(mem_ptr: *mut std::ffi::c_void, val: U32) {
MEM_write32(mem_ptr, val.to_le());
}
#[inline]
pub unsafe fn MEM_writeLE64(mem_ptr: *mut std::ffi::c_void, val: U64) {
MEM_write64(mem_ptr, val.to_le());
}
#[inline]
pub unsafe fn MEM_writeLEST(mem_ptr: *mut std::ffi::c_void, val: usize) {
if MEM_32bits() {
MEM_writeLE32(mem_ptr, val as U32);
} else {
MEM_writeLE64(mem_ptr, val as U64);
}
}
#[inline]
pub unsafe fn MEM_readBE32(mem_ptr: *const std::ffi::c_void) -> U32 {
U32::from_be(MEM_read32(mem_ptr))
}
#[inline]
pub unsafe fn MEM_writeBE32(mem_ptr: *mut std::ffi::c_void, val: U32) {
MEM_write32(mem_ptr, val.to_be());
}
#[inline]
pub unsafe fn MEM_readBE64(mem_ptr: *const std::ffi::c_void) -> U64 {
U64::from_be(MEM_read64(mem_ptr))
}
#[inline]
pub unsafe fn MEM_writeBE64(mem_ptr: *mut std::ffi::c_void, val: U64) {
MEM_write64(mem_ptr, val.to_be());
}
#[inline]
pub unsafe fn MEM_readBEST(mem_ptr: *const std::ffi::c_void) -> usize {
if MEM_32bits() {
MEM_readBE32(mem_ptr) as usize
} else {
MEM_readBE64(mem_ptr) as usize
}
}
#[inline]
pub unsafe fn MEM_writeBEST(mem_ptr: *mut std::ffi::c_void, val: usize) {
if MEM_32bits() {
MEM_writeBE32(mem_ptr, val as U32);
} else {
MEM_writeBE64(mem_ptr, val as U64);
}
}
#[inline]
pub fn MEM_swap32(in_val: U32) -> U32 {
in_val.swap_bytes()
}
#[inline]
pub fn MEM_swap64(in_val: U64) -> U64 {
in_val.swap_bytes()
}
#[inline]
pub fn MEM_swapST(in_val: usize) -> usize {
if MEM_32bits() {
MEM_swap32(in_val as U32) as usize
} else {
MEM_swap64(in_val as U64) as usize
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ZstdOverlap {
NoOverlap,
OverlapSrcBeforeDst,
}
/// # Safety
/// `dst`/`src` must be valid for the wildcopy contract (may over-read/write up to 32 bytes).
#[inline]
pub unsafe fn ZSTD_wildcopy(
dst: *mut std::ffi::c_void,
src: *const std::ffi::c_void,
length: isize,
ovtype: ZstdOverlap,
) {
const WILDCOPY_VECLEN: isize = 16;
let mut op = dst as *mut U8;
let mut ip = src as *const U8;
let oend = op.wrapping_offset(length);
let diff = (op as isize).wrapping_sub(ip as isize);
if ovtype == ZstdOverlap::OverlapSrcBeforeDst && diff < WILDCOPY_VECLEN {
loop {
std::ptr::copy_nonoverlapping(ip, op, 8);
op = op.add(8);
ip = ip.add(8);
if op >= oend {
break;
}
}
} else {
std::ptr::copy_nonoverlapping(ip, op, 16);
if length <= 16 {
return;
}
op = op.add(16);
ip = ip.add(16);
while op < oend {
std::ptr::copy_nonoverlapping(ip, op, 16);
op = op.add(16);
ip = ip.add(16);
std::ptr::copy_nonoverlapping(ip, op, 16);
op = op.add(16);
ip = ip.add(16);
}
}
}
/// # Safety
/// `dst` must be valid for `dst_capacity` bytes; `src` for `src_size` bytes.
#[inline]
pub unsafe fn ZSTD_limitCopy(
dst: *mut std::ffi::c_void,
dst_capacity: usize,
src: *const std::ffi::c_void,
src_size: usize,
) -> usize {
let length = std::cmp::min(dst_capacity, src_size);
if length > 0 {
std::ptr::copy_nonoverlapping(src as *const U8, dst as *mut U8, length);
}
length
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::c_void;
#[test]
fn unaligned_endian_io_round_trips() {
let mut bytes = [0u8; 24];
unsafe {
let p = bytes.as_mut_ptr().add(1).cast::<c_void>();
MEM_writeLE16(p, 0x1234);
assert_eq!(&bytes[1..3], &[0x34, 0x12]);
assert_eq!(MEM_readLE16(p), 0x1234);
MEM_writeLE24(p, 0x00ab_cdef);
assert_eq!(&bytes[1..4], &[0xef, 0xcd, 0xab]);
assert_eq!(MEM_readLE24(p), 0x00ab_cdef);
MEM_writeLE64(p, 0x0123_4567_89ab_cdef);
assert_eq!(MEM_readLE64(p), 0x0123_4567_89ab_cdef);
MEM_writeBE64(p, 0x0123_4567_89ab_cdef);
assert_eq!(&bytes[1..9], &[1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
assert_eq!(MEM_readBE64(p), 0x0123_4567_89ab_cdef);
}
}
#[test]
fn wildcopy_always_performs_its_first_copy() {
let mut bytes = [0u8; 96];
for (index, byte) in bytes[..32].iter_mut().enumerate() {
*byte = index as u8;
}
unsafe {
ZSTD_wildcopy(
bytes.as_mut_ptr().add(64).cast(),
bytes.as_ptr().cast(),
0,
ZstdOverlap::NoOverlap,
);
}
assert_eq!(&bytes[64..80], &bytes[..16]);
}
#[test]
fn short_offset_wildcopy_is_do_while_like() {
let mut bytes = [0u8; 32];
for (index, byte) in bytes[..8].iter_mut().enumerate() {
*byte = (index + 1) as u8;
}
unsafe {
ZSTD_wildcopy(
bytes.as_mut_ptr().add(8).cast(),
bytes.as_ptr().cast(),
0,
ZstdOverlap::OverlapSrcBeforeDst,
);
}
assert_eq!(&bytes[8..16], &[1, 2, 3, 4, 5, 6, 7, 8]);
}
#[test]
fn limit_copy_reports_the_bytes_written() {
let src = *b"abcdef";
let mut dst = [0u8; 4];
let copied = unsafe {
ZSTD_limitCopy(
dst.as_mut_ptr().cast(),
dst.len(),
src.as_ptr().cast(),
src.len(),
)
};
assert_eq!(copied, 4);
assert_eq!(&dst, b"abcd");
}
}