mirror of
https://github.com/rust-osdev/uefi-rs
synced 2026-08-26 18:26:05 -04:00
Merge pull request #2031 from cwize1/char16Macro
uefi: add convenient char16!() macro
This commit is contained in:
commit
a34b22476b
8 changed files with 88 additions and 77 deletions
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
## Changed
|
||||
- Made memory map types `#[repr(C)]`
|
||||
- Added `char16!` const-compatible macro as convenient replacement for `Char16::try_from().unwrap()`
|
||||
|
||||
## Removed
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@
|
|||
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::char16;
|
||||
|
||||
/// Character conversion error
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct CharConversionError;
|
||||
|
||||
impl Display for CharConversionError {
|
||||
|
|
@ -80,6 +82,18 @@ pub const NUL_8: Char8 = Char8(0);
|
|||
pub struct Char16(u16);
|
||||
|
||||
impl Char16 {
|
||||
/// Creates a UCS-2 character from a Rust character.
|
||||
///
|
||||
/// Same as `<Char16 as TryFrom<char>>::try_from` but usable in a const context.
|
||||
pub const fn try_from_char(value: char) -> Result<Self, CharConversionError> {
|
||||
let code_point = value as u32;
|
||||
if code_point > (u16::MAX as u32) {
|
||||
Err(CharConversionError)
|
||||
} else {
|
||||
Ok(Self(code_point as u16))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a UCS-2 character from a Rust character without checks.
|
||||
///
|
||||
/// # Safety
|
||||
|
|
@ -160,8 +174,7 @@ impl PartialEq<char> for Char16 {
|
|||
}
|
||||
|
||||
/// UCS-2 version of the NUL character
|
||||
// SAFETY: The character is valid.
|
||||
pub const NUL_16: Char16 = unsafe { Char16::from_u16_unchecked(0) };
|
||||
pub const NUL_16: Char16 = char16!('\0');
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
|
@ -169,13 +182,22 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_char8_from_char() {
|
||||
assert_eq!(Char8::try_from('A').unwrap(), Char8(0x41));
|
||||
assert_eq!(Char8::try_from('A'), Ok(Char8(0x41)));
|
||||
assert_eq!(Char8::try_from('ꋃ'), Err(CharConversionError {}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char16_from_char() {
|
||||
assert_eq!(Char16::try_from('A').unwrap(), Char16(0x41));
|
||||
assert_eq!(Char16::try_from('ꋃ').unwrap(), Char16(0xa2c3));
|
||||
assert_eq!(Char16::try_from('A'), Ok(Char16(0x41)));
|
||||
assert_eq!(Char16::try_from('ꋃ'), Ok(Char16(0xa2c3)));
|
||||
assert_eq!(Char16::try_from('😀'), Err(CharConversionError {}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char16_try_from_char() {
|
||||
assert_eq!(Char16::try_from_char('A'), Ok(Char16(0x41)));
|
||||
assert_eq!(Char16::try_from_char('ꋃ'), Ok(Char16(0xa2c3)));
|
||||
assert_eq!(Char16::try_from_char('😀'), Err(CharConversionError {}));
|
||||
}
|
||||
|
||||
/// Test that `Char8` and `Char16` can be directly compared with `char`.
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ impl<StrType: AsRef<str> + ?Sized> EqStrUntilNul<StrType> for CString16 {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cstr16;
|
||||
use crate::{char16, cstr16};
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
|
||||
|
|
@ -261,7 +261,7 @@ mod tests {
|
|||
fn test_cstring16_from_str() {
|
||||
assert_eq!(
|
||||
CString16::try_from("x").unwrap(),
|
||||
CString16(vec![Char16::try_from('x').unwrap(), NUL_16])
|
||||
CString16(vec![char16!('x'), NUL_16])
|
||||
);
|
||||
|
||||
assert_eq!(CString16::try_from("😀"), Err(FromStrError::InvalidChar));
|
||||
|
|
@ -331,14 +331,7 @@ mod tests {
|
|||
let owned: CString16 = s1.to_owned();
|
||||
let s2: &CStr16 = owned.borrow();
|
||||
assert_eq!(s1, s2);
|
||||
assert_eq!(
|
||||
owned.0,
|
||||
[
|
||||
Char16::try_from('a').unwrap(),
|
||||
Char16::try_from('b').unwrap(),
|
||||
NUL_16
|
||||
]
|
||||
);
|
||||
assert_eq!(owned.0, [char16!('a'), char16!('b'), NUL_16]);
|
||||
}
|
||||
|
||||
/// This tests the following UCS-2 string functions:
|
||||
|
|
@ -351,12 +344,12 @@ mod tests {
|
|||
let mut str1 = CString16::new();
|
||||
assert_eq!(str1.num_bytes(), 2, "Should have null character");
|
||||
assert_eq!(str1.num_chars(), 0);
|
||||
str1.push(Char16::try_from('h').unwrap());
|
||||
str1.push(Char16::try_from('i').unwrap());
|
||||
str1.push(char16!('h'));
|
||||
str1.push(char16!('i'));
|
||||
assert_eq!(str1.num_chars(), 2);
|
||||
|
||||
let mut str2 = CString16::new();
|
||||
str2.push(Char16::try_from('!').unwrap());
|
||||
str2.push(char16!('!'));
|
||||
|
||||
str2.push_str(str1.as_ref());
|
||||
assert_eq!(str2.num_chars(), 3);
|
||||
|
|
@ -374,8 +367,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_char_replace_all_in_place() {
|
||||
let mut input = CString16::try_from("foo/bar/foobar//").unwrap();
|
||||
let search = Char16::try_from('/').unwrap();
|
||||
let replace = Char16::try_from('\\').unwrap();
|
||||
let search = char16!('/');
|
||||
let replace = char16!('\\');
|
||||
input.replace_char(search, replace);
|
||||
|
||||
let input = String::from(&input);
|
||||
|
|
|
|||
|
|
@ -877,7 +877,7 @@ where
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{cstr8, cstr16};
|
||||
use crate::{char16, cstr8, cstr16};
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
|
||||
|
|
@ -958,31 +958,19 @@ mod tests {
|
|||
|
||||
// Invalid: no nul character.
|
||||
assert_eq!(
|
||||
CStr16::from_char16_until_nul(&[
|
||||
Char16::try_from('a').unwrap(),
|
||||
Char16::try_from('b').unwrap(),
|
||||
]),
|
||||
CStr16::from_char16_until_nul(&[char16!('a'), char16!('b'),]),
|
||||
Err(FromSliceUntilNulError::NoNul)
|
||||
);
|
||||
|
||||
// Valid: trailing nul.
|
||||
assert_eq!(
|
||||
CStr16::from_char16_until_nul(&[
|
||||
Char16::try_from('a').unwrap(),
|
||||
Char16::try_from('b').unwrap(),
|
||||
NUL_16,
|
||||
]),
|
||||
CStr16::from_char16_until_nul(&[char16!('a'), char16!('b'), NUL_16,]),
|
||||
Ok(cstr16!("ab"))
|
||||
);
|
||||
|
||||
// Valid: interior nul.
|
||||
assert_eq!(
|
||||
CStr16::from_char16_until_nul(&[
|
||||
Char16::try_from('a').unwrap(),
|
||||
NUL_16,
|
||||
Char16::try_from('b').unwrap(),
|
||||
NUL_16
|
||||
]),
|
||||
CStr16::from_char16_until_nul(&[char16!('a'), NUL_16, char16!('b'), NUL_16]),
|
||||
Ok(cstr16!("a"))
|
||||
);
|
||||
}
|
||||
|
|
@ -997,31 +985,19 @@ mod tests {
|
|||
|
||||
// Invalid: interior null.
|
||||
assert_eq!(
|
||||
CStr16::from_char16_with_nul(&[
|
||||
Char16::try_from('a').unwrap(),
|
||||
NUL_16,
|
||||
Char16::try_from('b').unwrap(),
|
||||
NUL_16
|
||||
]),
|
||||
CStr16::from_char16_with_nul(&[char16!('a'), NUL_16, char16!('b'), NUL_16]),
|
||||
Err(FromSliceWithNulError::InteriorNul(1))
|
||||
);
|
||||
|
||||
// Invalid: no trailing null.
|
||||
assert_eq!(
|
||||
CStr16::from_char16_with_nul(&[
|
||||
Char16::try_from('a').unwrap(),
|
||||
Char16::try_from('b').unwrap(),
|
||||
]),
|
||||
CStr16::from_char16_with_nul(&[char16!('a'), char16!('b'),]),
|
||||
Err(FromSliceWithNulError::NotNulTerminated)
|
||||
);
|
||||
|
||||
// Valid.
|
||||
assert_eq!(
|
||||
CStr16::from_char16_with_nul(&[
|
||||
Char16::try_from('a').unwrap(),
|
||||
Char16::try_from('b').unwrap(),
|
||||
NUL_16,
|
||||
]),
|
||||
CStr16::from_char16_with_nul(&[char16!('a'), char16!('b'), NUL_16,]),
|
||||
Ok(cstr16!("ab"))
|
||||
);
|
||||
}
|
||||
|
|
@ -1109,11 +1085,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_cstr16_as_slice() {
|
||||
let string: &CStr16 = cstr16!("a");
|
||||
assert_eq!(string.as_slice(), &[Char16::try_from('a').unwrap()]);
|
||||
assert_eq!(
|
||||
string.as_slice_with_nul(),
|
||||
&[Char16::try_from('a').unwrap(), NUL_16]
|
||||
);
|
||||
assert_eq!(string.as_slice(), &[char16!('a')]);
|
||||
assert_eq!(string.as_slice_with_nul(), &[char16!('a'), NUL_16]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -24,13 +24,12 @@ pub use path::{Components, Path};
|
|||
pub use pathbuf::PathBuf;
|
||||
|
||||
use crate::data_types::chars::NUL_16;
|
||||
use crate::{CStr16, Char16, cstr16};
|
||||
use crate::{CStr16, Char16, char16, cstr16};
|
||||
pub use validation::PathError;
|
||||
pub(super) use validation::validate_path;
|
||||
|
||||
/// The default separator for paths.
|
||||
// SAFETY: The memory is valid.
|
||||
pub const SEPARATOR: Char16 = unsafe { Char16::from_u16_unchecked('\\' as u16) };
|
||||
pub const SEPARATOR: Char16 = char16!('\\');
|
||||
|
||||
/// Stringified version of [`SEPARATOR`].
|
||||
pub const SEPARATOR_STR: &CStr16 = cstr16!("\\");
|
||||
|
|
@ -38,18 +37,17 @@ pub const SEPARATOR_STR: &CStr16 = cstr16!("\\");
|
|||
/// Deny list of characters for path components. UEFI supports FAT-like file
|
||||
/// systems. According to <https://en.wikipedia.org/wiki/Comparison_of_file_systems>,
|
||||
/// paths should not contain these symbols.
|
||||
// SAFETY: The memory is valid.
|
||||
pub const CHARACTER_DENY_LIST: [Char16; 10] = unsafe {
|
||||
pub const CHARACTER_DENY_LIST: [Char16; 10] = {
|
||||
[
|
||||
NUL_16,
|
||||
Char16::from_u16_unchecked('"' as u16),
|
||||
Char16::from_u16_unchecked('*' as u16),
|
||||
Char16::from_u16_unchecked('/' as u16),
|
||||
Char16::from_u16_unchecked(':' as u16),
|
||||
Char16::from_u16_unchecked('<' as u16),
|
||||
Char16::from_u16_unchecked('>' as u16),
|
||||
Char16::from_u16_unchecked('?' as u16),
|
||||
char16!('"'),
|
||||
char16!('*'),
|
||||
char16!('/'),
|
||||
char16!(':'),
|
||||
char16!('<'),
|
||||
char16!('>'),
|
||||
char16!('?'),
|
||||
SEPARATOR,
|
||||
Char16::from_u16_unchecked('|' as u16),
|
||||
char16!('|'),
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use crate::fs::SEPARATOR;
|
||||
use crate::fs::path::Path;
|
||||
use crate::{CStr16, CString16, Char16};
|
||||
use crate::{CStr16, CString16, Char16, char16};
|
||||
use core::fmt::{Display, Formatter};
|
||||
|
||||
/// A path buffer similar to the `PathBuf` of the standard library, but based on
|
||||
|
|
@ -21,8 +21,7 @@ impl PathBuf {
|
|||
|
||||
/// Constructor that replaces all occurrences of `/` with `\`.
|
||||
fn new_from_cstring16(mut string: CString16) -> Self {
|
||||
// SAFETY: The memory is valid.
|
||||
const SEARCH: Char16 = unsafe { Char16::from_u16_unchecked('/' as u16) };
|
||||
const SEARCH: Char16 = char16!('/');
|
||||
string.replace_char(SEARCH, SEPARATOR);
|
||||
Self(string)
|
||||
}
|
||||
|
|
@ -31,8 +30,7 @@ impl PathBuf {
|
|||
///
|
||||
/// UNIX separators (`/`) will be replaced by [`SEPARATOR`] on the fly.
|
||||
pub fn push<P: AsRef<Path>>(&mut self, path: P) {
|
||||
// SAFETY: The memory is valid.
|
||||
const SEARCH: Char16 = unsafe { Char16::from_u16_unchecked('/' as u16) };
|
||||
const SEARCH: Char16 = char16!('/');
|
||||
|
||||
// do nothing on empty path
|
||||
if path.as_ref().is_empty() {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,32 @@ macro_rules! cstr8 {
|
|||
}};
|
||||
}
|
||||
|
||||
/// Encodes a char literal as a [`Char16`].
|
||||
///
|
||||
/// The encoding is done at compile time, so the result can be used in a
|
||||
/// `const` item.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use uefi::{CStr8, cstr8};
|
||||
///
|
||||
/// const S: &CStr8 = cstr8!("ÿ");
|
||||
/// assert_eq!(S.as_bytes(), [255, 0]);
|
||||
/// ```
|
||||
///
|
||||
/// [`Char16`]: crate::Char16
|
||||
#[macro_export]
|
||||
macro_rules! char16 {
|
||||
($s:literal) => {{
|
||||
const C: $crate::Char16 = match $crate::Char16::try_from_char($s) {
|
||||
Ok(c) => c,
|
||||
Err(_) => panic!("input contains a character which cannot be represented in UCS-2"),
|
||||
};
|
||||
C
|
||||
}};
|
||||
}
|
||||
|
||||
/// Encode a string literal as a [`&CStr16`].
|
||||
///
|
||||
/// The encoding is done at compile time, so the result can be used in a
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ impl Input {
|
|||
/// ```
|
||||
/// use log::info;
|
||||
/// use uefi::proto::console::text::{Input, Key, ScanCode};
|
||||
/// use uefi::{boot, Char16, Result, ResultExt};
|
||||
/// use uefi::{boot, char16, Result, ResultExt};
|
||||
///
|
||||
/// fn read_keyboard_events(input: &mut Input) -> Result {
|
||||
/// loop {
|
||||
|
|
@ -55,7 +55,7 @@ impl Input {
|
|||
/// let mut events = [input.wait_for_key_event().unwrap()];
|
||||
/// boot::wait_for_event(&mut events).discard_errdata()?;
|
||||
///
|
||||
/// let u_key = Char16::try_from('u').unwrap();
|
||||
/// let u_key = char16!('u');
|
||||
/// match input.read_key()? {
|
||||
/// // Example of handling a printable key: print a message when
|
||||
/// // the 'u' key is pressed.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue