From 6294942dfda81f0ec802daea511eb0687a3729ab Mon Sep 17 00:00:00 2001 From: Ryan Lopopolo Date: Mon, 18 Apr 2022 23:01:35 -0700 Subject: [PATCH] Extract `find` and `is_cstr` const fn to an inner `imp` module Prepare for v1.3.1 release. --- Cargo.toml | 2 +- README.md | 2 +- src/imp.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 66 ++++++++++++++++++++---------------------------------- 4 files changed, 81 insertions(+), 44 deletions(-) create mode 100644 src/imp.rs diff --git a/Cargo.toml b/Cargo.toml index d26fbcd..e04a7d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qed" -version = "1.3.0" +version = "1.3.1" authors = ["Ryan Lopopolo "] license = "MIT" edition = "2021" diff --git a/README.md b/README.md index 888901c..2fc7e5d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -qed = "1.3.0" +qed = "1.3.1" ``` Then make compile time assertions like: diff --git a/src/imp.rs b/src/imp.rs new file mode 100644 index 0000000..0725a64 --- /dev/null +++ b/src/imp.rs @@ -0,0 +1,55 @@ +#[must_use] +pub const fn find(slice: &[u8], elem: u8) -> Option { + let mut idx = 0; + loop { + if idx == slice.len() { + return None; + } + if slice[idx] == elem { + return Some(idx); + } + idx += 1; + } +} + +#[must_use] +pub const fn is_cstr(slice: &[u8]) -> bool { + matches!(find(slice, 0), Some(nul_pos) if nul_pos + 1 == slice.len()) +} + +#[cfg(test)] +mod tests { + use super::{find, is_cstr}; + + #[test] + fn find_nul_byte() { + assert_eq!(find(b"", 0), None); + assert_eq!(find(b"abc", 0), None); + assert_eq!(find(b"abc\xFFxyz", 0), None); + + assert_eq!(find(b"abc\0xyz", 0), Some(3)); + assert_eq!(find(b"abc\0xyz\0", 0), Some(3)); + assert_eq!(find(b"abc\xFF\0xyz", 0), Some(4)); + assert_eq!(find(b"abc\xFF\0xyz\0", 0), Some(4)); + + assert_eq!(find(b"\0", 0), Some(0)); + assert_eq!(find(b"abc\0", 0), Some(3)); + assert_eq!(find(b"abc\xFFxyz\0", 0), Some(7)); + } + + #[test] + fn check_is_cstr() { + assert!(!is_cstr(b"")); + assert!(!is_cstr(b"abc")); + assert!(!is_cstr(b"abc\xFFxyz")); + + assert!(!is_cstr(b"abc\0xyz")); + assert!(!is_cstr(b"abc\0xyz\0")); + assert!(!is_cstr(b"abc\xFF\0xyz")); + assert!(!is_cstr(b"abc\xFF\0xyz\0")); + + assert!(is_cstr(b"\0")); + assert!(is_cstr(b"abc\0")); + assert!(is_cstr(b"abc\xFFxyz\0")); + } +} diff --git a/src/lib.rs b/src/lib.rs index 23b9298..3c797fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,7 @@ //! ``` #![no_std] -#![doc(html_root_url = "https://docs.rs/qed/1.3.0")] +#![doc(html_root_url = "https://docs.rs/qed/1.3.1")] #[cfg(any(test, doc))] extern crate std; @@ -53,6 +53,10 @@ extern crate std; #[doc = include_str!("../README.md")] mod readme {} +#[doc(hidden)] +#[allow(missing_docs)] +pub mod imp; + /// Asserts that a boolean expression is true at compile time. /// /// This will result in a compile time type error if the boolean expression does @@ -274,25 +278,11 @@ macro_rules! const_assert_size_eq { /// ``` #[macro_export] macro_rules! const_assert_bytes_has_no_nul { - ($bytes:expr $(,)?) => { - $crate::const_assert!({ - const fn byte_slice_contains(slice: &[u8], elem: u8) -> bool { - let mut idx = 0; - loop { - if idx >= slice.len() { - return false; - } - if slice[idx] == elem { - return true; - } - idx += 1; - } - } - - const BYTES: &[u8] = $bytes; - !byte_slice_contains(BYTES, 0_u8) - }); - }; + ($bytes:expr $(,)?) => {{ + const _: &[u8] = $bytes; + + $crate::const_assert!($crate::imp::find($bytes, 0_u8).is_none()); + }}; } /// Construct a const [`CStr`] from the given bytes at compile time and assert @@ -345,27 +335,10 @@ macro_rules! const_assert_bytes_has_no_nul { #[macro_export] macro_rules! const_cstr_from_bytes { ($bytes:expr $(,)?) => {{ - const BYTES: &[u8] = $bytes; - - $crate::const_assert!({ - const fn byte_slice_is_cstr(slice: &[u8]) -> bool { - let mut idx = slice.len() - 1; - if slice[idx] != 0 { - return false; - } - loop { - if idx == 0 { - return true; - } - idx -= 1; - if slice[idx] == 0 { - return false; - } - } - } - - byte_slice_is_cstr(BYTES) - }); + const _: &[u8] = $bytes; + + $crate::const_assert!($crate::imp::is_cstr($bytes)); + // SAFETY // // The compile time assert above ensures the given bytes: @@ -376,7 +349,7 @@ macro_rules! const_cstr_from_bytes { // which meets the safety criteria for `CStr::from_bytes_with_nul_unchecked`. // // https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#method.from_bytes_with_nul_unchecked - unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(BYTES) } + unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked($bytes) } }}; } @@ -524,4 +497,13 @@ mod tests { assert_eq!(CSTR.to_bytes(), b"Array"); assert!(EMPTY.to_bytes().is_empty()); } + + #[test] + fn const_assert_bytes_has_no_nul_none_shadow() { + #[allow(dead_code)] + #[allow(non_upper_case_globals)] + const None: () = (); + + crate::const_assert_bytes_has_no_nul!("abcdefg".as_bytes()); + } }