diff --git a/libz-rs-sys/src/lib.rs b/libz-rs-sys/src/lib.rs index 081e551..c6e2e52 100644 --- a/libz-rs-sys/src/lib.rs +++ b/libz-rs-sys/src/lib.rs @@ -1,20 +1,29 @@ #![allow(non_camel_case_types)] #![allow(non_snake_case)] -#![doc = include_str!("../README.md")] #![cfg_attr(not(feature = "std"), no_std)] +#![doc = include_str!("../README.md")] -//! # Safety of `*mut z_stream` +//! # Safety +//! +//! Most of the functions in this module are `unsafe fn`s, meaning that their behavior may be +//! undefined if certain assumptions are broken by the caller. In most cases, documentation +//! in this module refers to the safety assumptions of standard library functions. //! -//! Most functions require an argument of type `*mut z_stream`. Unless -//! otherwise noted, the safety requirements on such arguments are at least that the -//! pointer must be either: +//! In most cases, pointers must be either `NULL` or satisfy the requirements of `&*ptr` or `&mut +//! *ptr`. This requirement maps to the requirements of [`pointer::as_ref`] and [`pointer::as_mut`] +//! for immutable and mutable pointers respectively. //! -//! - A `NULL` pointer -//! - A pointer to a correctly aligned, initialized value of type `z_stream`. +//! For pointer and length pairs, describing some sequence of elements in memory, the requirements +//! of [`core::slice::from_raw_parts`] or [`core::slice::from_raw_parts_mut`] apply. In some cases, +//! the element type `T` is converted into `MaybeUninit`, meaning that while the slice must be +//! valid, the elements in the slice can be uninitialized. Using uninitialized buffers for output +//! is more performant. //! -//! In other words, it must be safe to cast the `*mut z_stream` to a `Option<&mut z_stream>`. It is -//! always safe to provide an argument of type `&mut z_stream`: rust will automatically downcast -//! the argument to `*mut z_stream`. +//! Finally, some functions accept a string argument, which must either be `NULL` or satisfy the +//! requirements of [`core::ffi::CStr::from_ptr`]. +//! +//! [`pointer::as_ref`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_ref +//! [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut use core::mem::MaybeUninit; @@ -56,31 +65,6 @@ macro_rules! prefix { const _: () = compile_error!("Only one of `rust-allocator` and `c-allocator` can be enabled at a time"); -#[allow(unreachable_code)] -const DEFAULT_ZALLOC: Option = '_blk: { - // this `break 'blk'` construction exists to generate just one compile error and not other - // warnings when multiple allocators are configured. - - #[cfg(feature = "c-allocator")] - break '_blk Some(zlib_rs::allocate::Allocator::C.zalloc); - - #[cfg(feature = "rust-allocator")] - break '_blk Some(zlib_rs::allocate::Allocator::RUST.zalloc); - - None -}; - -#[allow(unreachable_code)] -const DEFAULT_ZFREE: Option = '_blk: { - #[cfg(feature = "c-allocator")] - break '_blk Some(zlib_rs::allocate::Allocator::C.zfree); - - #[cfg(feature = "rust-allocator")] - break '_blk Some(zlib_rs::allocate::Allocator::RUST.zfree); - - None -}; - // In spirit this type is `libc::off_t`, but it would be our only libc dependency, and so we // hardcode the type here. This should be correct on most operating systems. If we ever run into // issues with it, we can either special-case or add a feature flag to force a particular width @@ -113,12 +97,9 @@ pub type z_off_t = c_long; /// ``` #[export_name = prefix!(crc32)] pub unsafe extern "C" fn crc32(crc: c_ulong, buf: *const Bytef, len: uInt) -> c_ulong { - if buf.is_null() { - 0 - } else { - // SAFETY: requirements must be satisfied by the caller - let buf = unsafe { core::slice::from_raw_parts(buf, len as usize) }; - zlib_rs::crc32(crc as u32, buf) as c_ulong + match unsafe { slice_from_raw_parts(buf, len as usize) } { + Some(buf) => zlib_rs::crc32(crc as u32, buf) as c_ulong, + None => 0, } } @@ -181,12 +162,9 @@ pub extern "C" fn crc32_combine(crc1: c_ulong, crc2: c_ulong, len2: z_off_t) -> /// ``` #[export_name = prefix!(adler32)] pub unsafe extern "C" fn adler32(adler: c_ulong, buf: *const Bytef, len: uInt) -> c_ulong { - if buf.is_null() { - 1 - } else { - // SAFETY: requirements must be satisfied by the caller - let buf = unsafe { core::slice::from_raw_parts(buf, len as usize) }; - zlib_rs::adler32(adler as u32, buf) as c_ulong + match unsafe { slice_from_raw_parts(buf, len as usize) } { + Some(buf) => zlib_rs::adler32(adler as u32, buf) as c_ulong, + None => 1, } } @@ -248,7 +226,9 @@ pub extern "C" fn adler32_combine(adler1: c_ulong, adler2: c_ulong, len2: z_off_ /// /// The caller must guarantee that /// -/// * The `destLen` pointer satisfies the requirements of [`core::ptr::read`] +/// * Either +/// - `destLen` is `NULL` +/// - `destLen` satisfies the requirements of `&mut *destLen` /// * Either /// - `dest` is `NULL` /// - `dest` and `*destLen` satisfy the requirements of [`core::slice::from_raw_parts_mut::>`] @@ -290,31 +270,22 @@ pub unsafe extern "C" fn uncompress( ) -> c_int { // stock zlib will just dereference a NULL pointer: that's UB. // Hence us returning an error value is compatible - let len = if destLen.is_null() { + let Some(destLen) = (unsafe { destLen.as_mut() }) else { return ReturnCode::StreamError as _; - } else { - // SAFETY: guaranteed by the caller - core::ptr::read(destLen) as usize }; - let output = if dest.is_null() { + let Some(output) = (unsafe { slice_from_raw_parts_uninit_mut(dest, *destLen as usize) }) else { return ReturnCode::StreamError as _; - } else { - // SAFETY: pointer is not NULL, other constraints are guaranteed by the caller - core::slice::from_raw_parts_mut(dest as *mut MaybeUninit, len) }; - let len = sourceLen as usize; - let input = if source.is_null() { + let Some(input) = (unsafe { slice_from_raw_parts(source, sourceLen as usize) }) else { return ReturnCode::StreamError as _; - } else { - // SAFETY: pointer is not NULL, other constraints are guaranteed by the caller - core::slice::from_raw_parts(source, len) }; - let (output, err) = zlib_rs::inflate::uncompress(output, input, InflateConfig::default()); + let config = InflateConfig::default(); + let (output, err) = zlib_rs::inflate::uncompress(output, input, config); - core::ptr::write(destLen, output.len() as _); + *destLen = output.len() as c_ulong; err as c_int } @@ -389,11 +360,11 @@ pub unsafe extern "C" fn inflateEnd(strm: *mut z_stream) -> i32 { /// /// * Either /// - `strm` is `NULL` -/// - `strm` satisfies the requirements of `&mut *(strm as *mut MaybeUninit)` +/// - `strm` satisfies the requirements of `&mut *strm` /// * Either /// - `version` is NULL -/// - `version` satisfies the requirements of [`core::ptr::read::`] -/// * If `strm` is not `NULL`, the following fields must be initialized +/// - `version` satisfies the requirements of [`core::ffi::CStr::from_ptr`] +/// * If `strm` is not `NULL`, the following fields contain valid values /// - `zalloc` /// - `zfree` /// - `opaque` @@ -475,15 +446,15 @@ pub unsafe extern "C" fn inflateBackEnd(_strm: z_streamp) -> c_int { /// - `source` satisfies the requirements of `&mut *strm` and was initialized with [`inflateInit_`] or similar #[export_name = prefix!(inflateCopy)] pub unsafe extern "C" fn inflateCopy(dest: *mut z_stream, source: *const z_stream) -> i32 { - if dest.is_null() { + let Some(dest) = (unsafe { dest.cast::>().as_mut() }) else { return ReturnCode::StreamError as _; - } + }; - if let Some(source) = InflateStream::from_stream_ref(source) { - zlib_rs::inflate::copy(&mut *(dest as *mut MaybeUninit), source) as _ - } else { - ReturnCode::StreamError as _ - } + let Some(source) = (unsafe { InflateStream::from_stream_ref(source) }) else { + return ReturnCode::StreamError as _; + }; + + zlib_rs::inflate::copy(dest, source) as _ } /// Gives information about the current location of the input stream. @@ -585,13 +556,11 @@ pub unsafe extern "C" fn inflateSyncPoint(strm: *mut z_stream) -> i32 { /// /// * Either /// - `strm` is `NULL` -/// - `strm` satisfies the requirements of `&mut *(strm as *mut MaybeUninit)` +/// - `strm` satisfies the requirements of `&mut *strm` /// * Either /// - `version` is NULL -/// - `version` satisfies the requirements of [`core::ptr::read::`] -/// * If `strm` is not `NULL`, the following fields must be initialized -/// - `next_in` -/// - `avail_in` +/// - `version` satisfies the requirements of [`core::ffi::CStr::from_ptr`] +/// * If `strm` is not `NULL`, the following fields contain valid values /// - `zalloc` /// - `zfree` /// - `opaque` @@ -620,13 +589,11 @@ pub unsafe extern "C" fn inflateInit_( /// /// * Either /// - `strm` is `NULL` -/// - `strm` satisfies the requirements of `&mut *(strm as *mut MaybeUninit)` +/// - `strm` satisfies the requirements of `&mut *strm` /// * Either /// - `version` is NULL -/// - `version` satisfies the requirements of [`core::ptr::read::`] -/// * If `strm` is not `NULL`, the following fields must be initialized -/// - `next_in` -/// - `avail_in` +/// - `version` satisfies the requirements of [`core::ffi::CStr::from_ptr`] +/// * If `strm` is not `NULL`, the following fields contain valid values /// - `zalloc` /// - `zfree` /// - `opaque` @@ -652,53 +619,21 @@ pub unsafe extern "C" fn inflateInit2_( /// /// * Either /// - `strm` is `NULL` -/// - `strm` satisfies the requirements of `&mut *(strm as *mut MaybeUninit)` -/// * Either -/// - `version` is NULL -/// - `version` satisfies the requirements of [`core::ptr::read::`] -/// * If `strm` is not `NULL`, the following fields must be initialized -/// - `next_in` -/// - `avail_in` +/// - `strm` satisfies the requirements of `&mut *strm` +/// * If `strm` is not `NULL`, the following fields contain valid values /// - `zalloc` /// - `zfree` /// - `opaque` unsafe extern "C" fn inflateInit2(strm: z_streamp, windowBits: c_int) -> c_int { - if strm.is_null() { - ReturnCode::StreamError as _ - } else { - let config = InflateConfig { - window_bits: windowBits, - }; - - let mut stream = z_stream::default(); - - // SAFETY: the caller guarantees these fields are initialized - unsafe { - stream.next_in = *core::ptr::addr_of!((*strm).next_in); - stream.avail_in = *core::ptr::addr_of!((*strm).avail_in); - stream.zalloc = *core::ptr::addr_of!((*strm).zalloc); - stream.zfree = *core::ptr::addr_of!((*strm).zfree); - stream.opaque = *core::ptr::addr_of!((*strm).opaque); - } - - if stream.zalloc.is_none() { - stream.zalloc = DEFAULT_ZALLOC; - stream.opaque = core::ptr::null_mut(); - } - - if stream.zfree.is_none() { - stream.zfree = DEFAULT_ZFREE; - } - - // SAFETY: the caller guarantees this pointer is writable - unsafe { core::ptr::write(strm, stream) }; + let Some(strm) = (unsafe { strm.as_mut() }) else { + return ReturnCode::StreamError as _; + }; - // SAFETY: we have now properly initialized this memory - // the caller guarantees the safety of `&mut *` - let stream = unsafe { &mut *strm }; + let config = InflateConfig { + window_bits: windowBits, + }; - zlib_rs::inflate::init(stream, config) as _ - } + zlib_rs::inflate::init(strm, config) as _ } /// Inserts bits in the inflate input stream. @@ -722,7 +657,7 @@ unsafe extern "C" fn inflateInit2(strm: z_streamp, windowBits: c_int) -> c_int { /// /// * Either /// - `strm` is `NULL` -/// - `strm` satisfies the requirements of `&mut *(strm as *mut MaybeUninit)` +/// - `strm` satisfies the requirements of `&mut *strm` and was initialized with [`inflateInit_`] or similar #[export_name = prefix!(inflatePrime)] pub unsafe extern "C" fn inflatePrime(strm: *mut z_stream, bits: i32, value: i32) -> i32 { if let Some(stream) = InflateStream::from_stream_mut(strm) { @@ -825,10 +760,9 @@ pub unsafe extern "C" fn inflateSetDictionary( return ReturnCode::StreamError as _; }; - let dict = if dictLength == 0 || dictionary.is_null() { - &[] - } else { - unsafe { core::slice::from_raw_parts(dictionary, dictLength as usize) } + let dict = match dictLength { + 0 => &[], + _ => unsafe { slice_from_raw_parts(dictionary, dictLength as usize) }.unwrap_or(&[]), }; zlib_rs::inflate::set_dictionary(stream, dict) as _ @@ -874,46 +808,16 @@ pub unsafe extern "C" fn inflateSetDictionary( /// - if `head.extra` is not NULL, it must be writable for at least `head.extra_max` bytes /// - if `head.name` is not NULL, it must be writable for at least `head.name_max` bytes /// - if `head.comment` is not NULL, it must be writable for at least `head.comm_max` bytes -/// -/// It is advised to fully initialize this structure with zeros before setting any fiels, -/// for instance using [`gz_header::default`] or [`core::mem::MaybeUninit::zeroed`]. Using -/// a partially uninitialized header struct is extremely dangerous. #[export_name = prefix!(inflateGetHeader)] pub unsafe extern "C" fn inflateGetHeader(strm: z_streamp, head: gz_headerp) -> c_int { - if let Some(stream) = InflateStream::from_stream_mut(strm) { - let header = if head.is_null() { - None - } else { - // SAFETY: the caller guarantees these fields are initialized - let mut header = gz_header::default(); - - unsafe { - header.extra = *core::ptr::addr_of!((*head).extra); - if !header.extra.is_null() { - header.extra_max = *core::ptr::addr_of!((*head).extra_max); - } - header.name = *core::ptr::addr_of!((*head).name); - if !header.name.is_null() { - header.name_max = *core::ptr::addr_of!((*head).name_max); - } - header.comment = *core::ptr::addr_of!((*head).comment); - if !header.comment.is_null() { - header.comm_max = *core::ptr::addr_of!((*head).comm_max); - } - } - - // SAFETY: the caller guarantees this pointer is writable - unsafe { core::ptr::write(head, header) }; - - // SAFETY: we have now properly initialized this memory - // the caller guarantees the safety of `&mut *` - Some(unsafe { &mut *head }) - }; + let Some(stream) = (unsafe { InflateStream::from_stream_mut(strm) }) else { + return ReturnCode::StreamError as _; + }; - zlib_rs::inflate::get_header(stream, header) as i32 - } else { - ReturnCode::StreamError as _ - } + // SAFETY: the caller guarantees the safety of `&mut *` + let header = unsafe { head.as_mut() }; + + zlib_rs::inflate::get_header(stream, header) as i32 } #[doc(hidden)] @@ -940,7 +844,7 @@ pub unsafe extern "C" fn inflateUndermine(strm: *mut z_stream, subvert: i32) -> /// - `strm` is `NULL` /// - `strm` satisfies the requirements of `&mut *strm` and was initialized with [`inflateInit_`] or similar #[export_name = prefix!(inflateResetKeep)] -pub unsafe extern "C" fn inflateResetKeep(strm: *mut z_stream) -> i32 { +pub unsafe extern "C" fn inflateResetKeep(strm: *mut z_stream) -> c_int { if let Some(stream) = InflateStream::from_stream_mut(strm) { zlib_rs::inflate::reset_keep(stream) as _ } else { @@ -980,7 +884,7 @@ pub unsafe extern "C" fn inflateCodesUsed(_strm: *mut z_stream) -> c_ulong { /// - `strm` is `NULL` /// - `strm` satisfies the requirements of `&mut *strm` and was initialized with [`deflateInit_`] or similar #[export_name = prefix!(deflate)] -pub unsafe extern "C" fn deflate(strm: *mut z_stream, flush: i32) -> i32 { +pub unsafe extern "C" fn deflate(strm: *mut z_stream, flush: i32) -> c_int { if let Some(stream) = DeflateStream::from_stream_mut(strm) { match DeflateFlush::try_from(flush) { Ok(flush) => zlib_rs::deflate::deflate(stream, flush) as _, @@ -1014,19 +918,14 @@ pub unsafe extern "C" fn deflate(strm: *mut z_stream, flush: i32) -> i32 { /// - `head` is `NULL` /// - `head` satisfies the requirements of `&mut *head` #[export_name = prefix!(deflateSetHeader)] -pub unsafe extern "C" fn deflateSetHeader(strm: *mut z_stream, head: gz_headerp) -> i32 { - if let Some(stream) = DeflateStream::from_stream_mut(strm) { - zlib_rs::deflate::set_header( - stream, - if head.is_null() { - None - } else { - Some(&mut *head) - }, - ) as _ - } else { - ReturnCode::StreamError as _ - } +pub unsafe extern "C" fn deflateSetHeader(strm: *mut z_stream, head: gz_headerp) -> c_int { + let Some(stream) = (unsafe { DeflateStream::from_stream_mut(strm) }) else { + return ReturnCode::StreamError as _; + }; + + let header = unsafe { head.as_mut() }; + + zlib_rs::deflate::set_header(stream, header) as _ } /// Returns an upper bound on the compressed size after deflation of `sourceLen` bytes. @@ -1135,7 +1034,9 @@ pub unsafe extern "C" fn compress( /// /// The caller must guarantee that /// -/// * The `destLen` pointer satisfies the requirements of [`core::ptr::read`] +/// * Either +/// - `destLen` is `NULL` +/// - `destLen` satisfies the requirements of `&mut *destLen` /// * Either /// - `dest` is `NULL` /// - `dest` and `*destLen` satisfy the requirements of [`core::slice::from_raw_parts_mut::>`] @@ -1152,32 +1053,22 @@ pub unsafe extern "C" fn compress2( ) -> c_int { // stock zlib will just dereference a NULL pointer: that's UB. // Hence us returning an error value is compatible - let len = if destLen.is_null() { + let Some(destLen) = (unsafe { destLen.as_mut() }) else { return ReturnCode::StreamError as _; - } else { - // SAFETY: guaranteed by the caller - core::ptr::read(destLen) as usize }; - let output = if dest.is_null() { + let Some(output) = (unsafe { slice_from_raw_parts_uninit_mut(dest, *destLen as usize) }) else { return ReturnCode::StreamError as _; - } else { - // SAFETY: pointer is not NULL, other constraints are guaranteed by the caller - core::slice::from_raw_parts_mut(dest as *mut MaybeUninit, len) }; - let len = sourceLen as usize; - let input = if source.is_null() { + let Some(input) = (unsafe { slice_from_raw_parts(source, sourceLen as usize) }) else { return ReturnCode::StreamError as _; - } else { - // SAFETY: pointer is not NULL, other constraints are guaranteed by the caller - core::slice::from_raw_parts(source, len) }; let config = DeflateConfig::new(level); let (output, err) = zlib_rs::deflate::compress(output, input, config); - core::ptr::write(destLen, output.len() as _); + *destLen = output.len() as c_ulong; err as c_int } @@ -1303,11 +1194,9 @@ pub unsafe extern "C" fn deflateSetDictionary( dictionary: *const Bytef, dictLength: uInt, ) -> c_int { - let dictionary = if dictionary.is_null() { + let Some(dictionary) = (unsafe { slice_from_raw_parts(dictionary, dictLength as usize) }) + else { return ReturnCode::StreamError as _; - } else { - // SAFETY: pointer is not NULL, other constraints are guaranteed by the caller - core::slice::from_raw_parts(dictionary, dictLength as usize) }; match DeflateStream::from_stream_mut(strm) { @@ -1373,22 +1262,21 @@ pub unsafe extern "C" fn deflatePending( pending: *mut c_uint, bits: *mut c_int, ) -> c_int { - match DeflateStream::from_stream_mut(strm) { - Some(stream) => { - let (current_pending, current_bits) = stream.pending(); + let Some(stream) = (unsafe { DeflateStream::from_stream_mut(strm) }) else { + return ReturnCode::StreamError as _; + }; - if !pending.is_null() { - *pending = current_pending as c_uint; - } + let (current_pending, current_bits) = stream.pending(); - if !bits.is_null() { - *bits = current_bits as c_int; - } + if let Some(pending) = unsafe { pending.as_mut() } { + *pending = current_pending as c_uint; + } - ReturnCode::Ok as _ - } - None => ReturnCode::StreamError as _, + if let Some(bits) = unsafe { bits.as_mut() } { + *bits = current_bits as c_int; } + + ReturnCode::Ok as _ } /// Sets the destination stream as a complete copy of the source stream. @@ -1417,16 +1305,15 @@ pub unsafe extern "C" fn deflatePending( /// - `source` satisfies the requirements of `&mut *strm` and was initialized with [`deflateInit_`] or similar #[export_name = prefix!(deflateCopy)] pub unsafe extern "C" fn deflateCopy(dest: z_streamp, source: z_streamp) -> c_int { - let dest = if dest.is_null() { + let Some(dest) = (unsafe { dest.cast::>().as_mut() }) else { return ReturnCode::StreamError as _; - } else { - &mut *(dest as *mut MaybeUninit<_>) }; - match DeflateStream::from_stream_mut(source) { - Some(source) => zlib_rs::deflate::copy(dest, source) as _, - None => ReturnCode::StreamError as _, - } + let Some(source) = (unsafe { DeflateStream::from_stream_mut(source) }) else { + return ReturnCode::StreamError as _; + }; + + zlib_rs::deflate::copy(dest, source) as _ } /// Initializes the state for compression @@ -1463,11 +1350,11 @@ pub unsafe extern "C" fn deflateCopy(dest: z_streamp, source: z_streamp) -> c_in /// /// * Either /// - `strm` is `NULL` -/// - `strm` satisfies the requirements of `&mut *(strm as *mut MaybeUninit)` +/// - `strm` satisfies the requirements of `&mut *strm` /// * Either /// - `version` is NULL -/// - `version` satisfies the requirements of [`core::ptr::read::`] -/// * If `strm` is not `NULL`, the following fields must be initialized +/// - `version` satisfies the requirements of [`core::ffi::CStr::from_ptr`] +/// * If `strm` is not `NULL`, the following fields contain valid values /// - `zalloc` /// - `zfree` /// - `opaque` @@ -1549,11 +1436,11 @@ pub unsafe extern "C" fn deflateInit_( /// /// * Either /// - `strm` is `NULL` -/// - `strm` satisfies the requirements of `&mut *(strm as *mut MaybeUninit)` +/// - `strm` satisfies the requirements of `&mut *strm` /// * Either /// - `version` is NULL -/// - `version` satisfies the requirements of [`core::ptr::read::`] -/// * If `strm` is not `NULL`, the following fields must be initialized +/// - `version` satisfies the requirements of [`core::ffi::CStr::from_ptr`] +/// * If `strm` is not `NULL`, the following fields contain valid values /// - `zalloc` /// - `zfree` /// - `opaque` @@ -1598,53 +1485,30 @@ pub unsafe extern "C" fn deflateInit2_( stream_size: c_int, ) -> c_int { if !is_version_compatible(version, stream_size) { - ReturnCode::VersionError as _ - } else if strm.is_null() { - ReturnCode::StreamError as _ - } else { - let Ok(method) = Method::try_from(method) else { - return ReturnCode::StreamError as _; - }; - - let Ok(strategy) = Strategy::try_from(strategy) else { - return ReturnCode::StreamError as _; - }; - - let config = DeflateConfig { - level, - method, - window_bits: windowBits, - mem_level: memLevel, - strategy, - }; - - let mut stream = z_stream::default(); - - // SAFETY: the caller guarantees these fields are initialized - unsafe { - stream.zalloc = *core::ptr::addr_of!((*strm).zalloc); - stream.zfree = *core::ptr::addr_of!((*strm).zfree); - stream.opaque = *core::ptr::addr_of!((*strm).opaque); - } + return ReturnCode::VersionError as _; + } - if stream.zalloc.is_none() { - stream.zalloc = DEFAULT_ZALLOC; - stream.opaque = core::ptr::null_mut(); - } + let Some(strm) = (unsafe { strm.as_mut() }) else { + return ReturnCode::StreamError as _; + }; - if stream.zfree.is_none() { - stream.zfree = DEFAULT_ZFREE; - } + let Ok(method) = Method::try_from(method) else { + return ReturnCode::StreamError as _; + }; - // SAFETY: the caller guarantees this pointer is writable - unsafe { core::ptr::write(strm, stream) }; + let Ok(strategy) = Strategy::try_from(strategy) else { + return ReturnCode::StreamError as _; + }; - // SAFETY: we have now properly initialized this memory - // the caller guarantees the safety of `&mut *` - let stream = unsafe { &mut *strm }; + let config = DeflateConfig { + level, + method, + window_bits: windowBits, + mem_level: memLevel, + strategy, + }; - zlib_rs::deflate::init(stream, config) as _ - } + zlib_rs::deflate::init(strm, config) as _ } /// Fine tune deflate's internal compression parameters. @@ -1674,16 +1538,17 @@ pub unsafe extern "C" fn deflateTune( nice_length: c_int, max_chain: c_int, ) -> c_int { - match DeflateStream::from_stream_mut(strm) { - Some(stream) => zlib_rs::deflate::tune( - stream, - good_length as usize, - max_lazy as usize, - nice_length as usize, - max_chain as usize, - ) as _, - None => ReturnCode::StreamError as _, - } + let Some(stream) = (unsafe { DeflateStream::from_stream_mut(strm) }) else { + return ReturnCode::StreamError as _; + }; + + zlib_rs::deflate::tune( + stream, + good_length as usize, + max_lazy as usize, + nice_length as usize, + max_chain as usize, + ) as _ } /// Get the error message for an error. This could be the value returned by e.g. [`compress`] or @@ -1737,12 +1602,11 @@ macro_rules! libz_rs_sys_version { const LIBZ_RS_SYS_VERSION: &str = concat!(libz_rs_sys_version!(), "\0"); unsafe fn is_version_compatible(version: *const c_char, stream_size: i32) -> bool { - if version.is_null() { + let Some(expected_major_version) = (unsafe { version.as_ref() }) else { return false; - } + }; - let expected_major_version = core::ptr::read(version); - if expected_major_version as u8 != LIBZ_RS_SYS_VERSION.as_bytes()[0] { + if *expected_major_version as u8 != LIBZ_RS_SYS_VERSION.as_bytes()[0] { return false; } @@ -1761,5 +1625,36 @@ unsafe fn is_version_compatible(version: *const c_char, stream_size: i32) -> boo /// - The final component is the zlib-rs version used to build this release. #[export_name = prefix!(zlibVersion)] pub const extern "C" fn zlibVersion() -> *const c_char { - LIBZ_RS_SYS_VERSION.as_ptr() as *const c_char + LIBZ_RS_SYS_VERSION.as_ptr().cast::() +} + +/// # Safety +/// +/// Either +/// +/// - `ptr` is `NULL` +/// - `ptr` and `len` satisfy the requirements of [`core::slice::from_raw_parts`] +unsafe fn slice_from_raw_parts<'a, T>(ptr: *const T, len: usize) -> Option<&'a [T]> { + if ptr.is_null() { + None + } else { + Some(unsafe { core::slice::from_raw_parts(ptr, len) }) + } +} + +/// # Safety +/// +/// Either +/// +/// - `ptr` is `NULL` +/// - `ptr` and `len` satisfy the requirements of [`core::slice::from_raw_parts_mut`] +unsafe fn slice_from_raw_parts_uninit_mut<'a, T>( + ptr: *mut T, + len: usize, +) -> Option<&'a mut [MaybeUninit]> { + if ptr.is_null() { + None + } else { + Some(unsafe { core::slice::from_raw_parts_mut(ptr.cast::>(), len) }) + } } diff --git a/test-libz-rs-sys/src/lib.rs b/test-libz-rs-sys/src/lib.rs index 1c420b1..e3df0c5 100644 --- a/test-libz-rs-sys/src/lib.rs +++ b/test-libz-rs-sys/src/lib.rs @@ -327,7 +327,7 @@ mod null { } #[test] - fn inflate_get_header_uninitialized() { + fn inflate_get_header_zeroed() { const FERRIS_BYTES_GZ_NO_HEADER: [u8; 26] = [ 31, 139, 8, 0, 0, 0, 0, 0, 0, 3, 115, 75, 45, 42, 202, 44, 6, 0, 174, 148, 97, 210, 6, 0, 0, 0, @@ -409,7 +409,7 @@ mod null { let mut strm = MaybeUninit::::zeroed(); fn initialize_header_null() -> MaybeUninit { - let mut head = MaybeUninit::::uninit(); + let mut head = MaybeUninit::::zeroed(); unsafe { core::ptr::write( @@ -434,7 +434,7 @@ mod null { name: &mut [u8], comment: &mut [u8], ) -> MaybeUninit { - let mut head = MaybeUninit::::uninit(); + let mut head = MaybeUninit::::zeroed(); unsafe { core::ptr::write( diff --git a/zlib-rs/src/deflate.rs b/zlib-rs/src/deflate.rs index 2b39866..b796de6 100644 --- a/zlib-rs/src/deflate.rs +++ b/zlib-rs/src/deflate.rs @@ -49,30 +49,29 @@ impl<'a> DeflateStream<'a> { /// # Safety /// - /// The `strm` pointer must be either `NULL` or a correctly initalized `z_stream`. Here - /// correctly initalized does not just mean that the pointer is valid and well-aligned, but - /// also that it has been initialized by that `deflateInit_` or `deflateInit2_`. + /// Behavior is undefined if any of the following conditions are violated: + /// + /// - `strm` satisfies the conditions of [`pointer::as_mut`] + /// - if not `NULL`, `strm` as initialized using [`init`] or similar + /// + /// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut #[inline(always)] pub unsafe fn from_stream_mut(strm: *mut z_stream) -> Option<&'a mut Self> { - if strm.is_null() { - return None; - } - - // safety: ptr points to a valid value of type z_stream (if non-null) - let stream = unsafe { &mut *strm }; + { + // Safety: ptr points to a valid value of type z_stream (if non-null) + let stream = unsafe { strm.as_ref() }?; - if stream.zalloc.is_none() || stream.zfree.is_none() { - return None; - } + if stream.zalloc.is_none() || stream.zfree.is_none() { + return None; + } - if stream.state.is_null() { - return None; + if stream.state.is_null() { + return None; + } } - // safety: DeflateStream has the same layout as z_stream - let stream = unsafe { &mut *(strm as *mut DeflateStream) }; - - Some(stream) + // Safety: DeflateStream has an equivalent layout as z_stream + unsafe { strm.cast::().as_mut() } } fn as_z_stream_mut(&mut self) -> &mut z_stream { diff --git a/zlib-rs/src/inflate.rs b/zlib-rs/src/inflate.rs index 30f4640..e579868 100644 --- a/zlib-rs/src/inflate.rs +++ b/zlib-rs/src/inflate.rs @@ -61,58 +61,56 @@ impl<'a> InflateStream<'a> { /// # Safety /// - /// The `strm` pointer must be either `NULL` or a correctly initalized `z_stream`. Here - /// correctly initalized does not just mean that the pointer is valid and well-aligned, but - /// also that it has been initialized by that `inflateInit_` or `inflateInit2_`. + /// Behavior is undefined if any of the following conditions are violated: + /// + /// - `strm` satisfies the conditions of [`pointer::as_ref`] + /// - if not `NULL`, `strm` as initialized using [`init`] or similar + /// + /// [`pointer::as_ref`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_ref #[inline(always)] pub unsafe fn from_stream_ref(strm: *const z_stream) -> Option<&'a Self> { - if strm.is_null() { - return None; - } - - // safety: ptr points to a valid value of type z_stream (if non-null) - let stream = unsafe { &*strm }; + { + // Safety: ptr points to a valid value of type z_stream (if non-null) + let stream = unsafe { strm.as_ref() }?; - if stream.zalloc.is_none() || stream.zfree.is_none() { - return None; - } + if stream.zalloc.is_none() || stream.zfree.is_none() { + return None; + } - if stream.state.is_null() { - return None; + if stream.state.is_null() { + return None; + } } - // safety: InflateStream has the same layout as z_stream - let stream = unsafe { &*(strm as *const InflateStream) }; - - Some(stream) + // Safety: InflateStream has an equivalent layout as z_stream + strm.cast::().as_ref() } /// # Safety /// - /// The `strm` pointer must be either `NULL` or a correctly initalized `z_stream`. Here - /// correctly initalized does not just mean that the pointer is valid and well-aligned, but - /// also that it has been initialized by that `inflateInit_` or `inflateInit2_`. + /// Behavior is undefined if any of the following conditions are violated: + /// + /// - `strm` satisfies the conditions of [`pointer::as_mut`] + /// - if not `NULL`, `strm` as initialized using [`init`] or similar + /// + /// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut #[inline(always)] pub unsafe fn from_stream_mut(strm: *mut z_stream) -> Option<&'a mut Self> { - if strm.is_null() { - return None; - } - - // safety: ptr points to a valid value of type z_stream (if non-null) - let stream = unsafe { &mut *strm }; + { + // Safety: ptr points to a valid value of type z_stream (if non-null) + let stream = unsafe { strm.as_ref() }?; - if stream.zalloc.is_none() || stream.zfree.is_none() { - return None; - } + if stream.zalloc.is_none() || stream.zfree.is_none() { + return None; + } - if stream.state.is_null() { - return None; + if stream.state.is_null() { + return None; + } } - // safety: InflateStream has the same layout as z_stream - let stream = unsafe { &mut *(strm as *mut InflateStream) }; - - Some(stream) + // Safety: InflateStream has an equivalent layout as z_stream + strm.cast::().as_mut() } fn as_z_stream_mut(&mut self) -> &mut z_stream {