Skip to content

Commit

Permalink
Merge pull request rust-osdev#1145 from nicholasbishop/bishop-rework-…
Browse files Browse the repository at this point in the history
…cstr16-macro

Replace `cstr16!` with a declarative macro
  • Loading branch information
phip1611 authored Apr 21, 2024
2 parents 2e5dea4 + ff7cc7a commit 5ade028
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 54 deletions.
4 changes: 4 additions & 0 deletions uefi-macros/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# uefi-macros - [Unreleased]

## Removed
- Removed the `cstr16` macro. Use the `cstr16` declarative macro exported by the
`uefi` crate instead.

# uefi-macros - 0.13.0 (2023-11-12)

## Changed
Expand Down
44 changes: 0 additions & 44 deletions uefi-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,47 +288,3 @@ pub fn cstr8(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
.into(),
}
}

/// Builds a `CStr16` literal at compile time from a string literal.
///
/// This will throw a compile error if an invalid character is in the passed string.
///
/// # Example
/// ```rust
/// # use uefi_macros::cstr16;
/// // Empty string
/// assert_eq!(cstr16!().to_u16_slice_with_nul(), [0]);
/// assert_eq!(cstr16!("").to_u16_slice_with_nul(), [0]);
/// // Non-empty string
/// assert_eq!(cstr16!("test €").to_u16_slice_with_nul(), [116, 101, 115, 116, 32, 8364, 0]);
/// ```
#[proc_macro]
pub fn cstr16(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Accept empty input.
if input.is_empty() {
return quote!(unsafe { ::uefi::CStr16::from_u16_with_nul_unchecked(&[0]) }).into();
}
let input: LitStr = parse_macro_input!(input);
let input = input.value();
// Accept "" input.
if input.is_empty() {
return quote!(unsafe { ::uefi::CStr16::from_u16_with_nul_unchecked(&[0]) }).into();
}

// Accept any non-empty string input.
match input
.chars()
.map(|c| u16::try_from(c as u32))
.collect::<Result<Vec<u16>, _>>()
{
Ok(c) => {
quote!(unsafe { ::uefi::CStr16::from_u16_with_nul_unchecked(&[ #(#c),* , 0 ]) }).into()
}
Err(_) => syn::Error::new_spanned(
input,
"There are UTF-8 characters that can't be transformed to UCS-2 character",
)
.into_compile_error()
.into(),
}
}
2 changes: 1 addition & 1 deletion uefi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ log.workspace = true
ptr_meta.workspace = true
uguid.workspace = true
cfg-if = "1.0.0"
ucs2 = "0.3.2"
ucs2 = "0.3.3"
uefi-macros = "0.13.0"
uefi-raw = "0.5.2"
qemu-exit = { version = "3.0.2", optional = true }
Expand Down
17 changes: 8 additions & 9 deletions uefi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ extern crate alloc;
// see https://github.com/rust-lang/rust/issues/54647
extern crate self as uefi;

/// Re-export ucs2_cstr so that it can be used in the implementation of the
/// cstr16 macro. It is hidden since it's not intended to be used directly.
#[doc(hidden)]
pub use ucs2::ucs2_cstr;

#[macro_use]
extern crate uefi_raw;

Expand All @@ -108,7 +113,7 @@ pub mod data_types;
#[cfg(feature = "alloc")]
pub use data_types::CString16;
pub use data_types::{CStr16, CStr8, Char16, Char8, Event, Guid, Handle, Identify};
pub use uefi_macros::{cstr16, cstr8, entry};
pub use uefi_macros::{cstr8, entry};
pub use uguid::guid;

mod result;
Expand All @@ -133,25 +138,19 @@ pub(crate) mod polyfill;

pub mod helpers;

mod macros;
mod util;

#[cfg(test)]
// Crates that create procedural macros can't unit test the macros they export.
// Therefore, we do some tests here.
mod macro_tests {
use crate::{cstr16, cstr8};
use crate::cstr8;

#[test]
fn cstr8_macro_literal() {
let _empty1 = cstr8!();
let _empty2 = cstr8!("");
let _regular = cstr8!("foobar");
}

#[test]
fn cstr16_macro_literal() {
let _empty1 = cstr16!();
let _empty2 = cstr16!("");
let _regular = cstr16!("foobar");
}
}
36 changes: 36 additions & 0 deletions uefi/src/macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/// Encode a string literal as a [`&CStr16`].
///
/// The encoding is done at compile time, so the result can be used in a
/// `const` item.
///
/// An empty string containing just a null character can be created with either
/// `cstr16!()` or `cstr16!("")`.
///
/// # Example
///
/// ```
/// use uefi::{CStr16, cstr16};
///
/// const S: &CStr16 = cstr16!("abc");
/// assert_eq!(S.to_u16_slice_with_nul(), [97, 98, 99, 0]);
///
/// const EMPTY: &CStr16 = cstr16!();
/// assert_eq!(EMPTY.to_u16_slice_with_nul(), [0]);
/// assert_eq!(cstr16!(""), EMPTY);
/// ```
///
/// [`&CStr16`]: crate::CStr16
#[macro_export]
macro_rules! cstr16 {
() => {{
const S: &[u16] = &[0];
// SAFETY: `S` is a trivially correct UCS-2 C string.
unsafe { $crate::CStr16::from_u16_with_nul_unchecked(S) }
}};
($s:literal) => {{
const S: &[u16] = &$crate::ucs2_cstr!($s);
// SAFETY: the ucs2_cstr macro always produces a valid UCS-2 string with
// a trailing null character.
unsafe { $crate::CStr16::from_u16_with_nul_unchecked(S) }
}};
}

0 comments on commit 5ade028

Please sign in to comment.