Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract find and is_cstr const fn to an inner imp module #12

Merged
merged 1 commit into from
Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <rjl@hyperbo.la>"]
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());
}
}