Skip to content

Commit

Permalink
Merge pull request #12 from artichoke/lopopolo/factor-out-find-routine
Browse files Browse the repository at this point in the history
Extract `find` and `is_cstr` const fn to an inner `imp` module
  • Loading branch information
lopopolo authored Apr 19, 2022
2 parents 8dccfee + 6294942 commit 2971de8
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 44 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "qed"
version = "1.3.0"
version = "1.3.1"
authors = ["Ryan Lopopolo <[email protected]>"]
license = "MIT"
edition = "2021"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
55 changes: 55 additions & 0 deletions src/imp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#[must_use]
pub const fn find(slice: &[u8], elem: u8) -> Option<usize> {
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"));
}
}
66 changes: 24 additions & 42 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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) }
}};
}

Expand Down Expand Up @@ -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());
}
}

0 comments on commit 2971de8

Please sign in to comment.