#![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::() == 4 } #[inline] pub fn MEM_64bits() -> bool { mem::size_of::() == 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::().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::().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::(); 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"); } }