#[derive(Clone, PartialEq, Eq, Debug)]
pub enum FromSliceWithNulError {
InteriorNul(usize),
NotNulTerminated,
}
#[derive(Eq, Ord, PartialEq, PartialOrd)]
pub struct EfiStr16 {
inner: [u16],
}
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct EfiString16 {
inner: alloc::boxed::Box<[u16]>,
}
impl EfiStr16 {
pub unsafe fn from_ptr<'a>(ptr: *const u16) -> &'a EfiStr16 {
let mut len: isize = 0;
while ptr.offset(len).read() != 0 {
len += 1;
}
Self::from_slice_with_nul_unchecked(
core::slice::from_raw_parts(ptr, len as usize + 1)
)
}
pub unsafe fn from_slice_with_nul_unchecked<'a>(slice: &[u16]) -> &EfiStr16 {
&*(slice as *const [u16] as *const EfiStr16)
}
pub fn from_slice_with_nul<'a>(slice: &[u16]) -> Result<&EfiStr16, FromSliceWithNulError> {
let n = slice.len();
for i in 0..n {
if slice[i] == 0 {
if i + 1 == n {
return unsafe { Ok(Self::from_slice_with_nul_unchecked(slice)) };
} else {
return Err(FromSliceWithNulError::InteriorNul(i));
}
}
}
Err(FromSliceWithNulError::NotNulTerminated)
}
pub fn as_ptr(&self) -> *const u16 {
self.inner.as_ptr()
}
pub fn as_slice_with_nul(&self) -> &[u16] {
&self.inner
}
pub fn as_slice(&self) -> &[u16] {
let s = self.as_slice_with_nul();
&s[..s.len() - 1]
}
pub fn to_string(&self) -> Result<alloc::string::String, alloc::string::FromUtf16Error> {
alloc::string::String::from_utf16(self.as_slice())
}
pub fn to_string_lossy(&self) -> alloc::string::String {
alloc::string::String::from_utf16_lossy(self.as_slice())
}
}
impl Default for &EfiStr16 {
fn default() -> Self {
const DEFAULT: &[u16] = &[0];
unsafe { EfiStr16::from_slice_with_nul_unchecked(DEFAULT) }
}
}
impl Default for alloc::boxed::Box<EfiStr16> {
fn default() -> Self {
<&EfiStr16 as Default>::default().into()
}
}
impl From<&EfiStr16> for alloc::boxed::Box<EfiStr16> {
fn from(s: &EfiStr16) -> alloc::boxed::Box<EfiStr16> {
let boxed: alloc::boxed::Box<[u16]> = alloc::boxed::Box::from(s.as_slice_with_nul());
unsafe {
alloc::boxed::Box::from_raw(
alloc::boxed::Box::into_raw(boxed) as *mut EfiStr16
)
}
}
}
impl Clone for alloc::boxed::Box<EfiStr16> {
fn clone(&self) -> Self {
(**self).into()
}
}
impl core::fmt::Debug for EfiStr16 {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
fn hexify_4bit(v: u8) -> char {
match v {
0x0..=0x9 => (b'0' + v) as char,
0xa..=0xf => (b'a' + (v - 0xa)) as char,
_ => panic!{},
}
}
fn hexify_16bit(v: u16) -> [char; 4] {
[
hexify_4bit(((v >> 12) & 0x000f) as u8),
hexify_4bit(((v >> 8) & 0x000f) as u8),
hexify_4bit(((v >> 4) & 0x000f) as u8),
hexify_4bit(((v >> 0) & 0x000f) as u8),
]
}
write!(f, "\"")?;
for entry in self.as_slice().iter() {
match *entry {
0x0000..=0x00ff => {
for c in core::ascii::escape_default(*entry as u8) {
core::fmt::Write::write_char(f, c as char)?;
}
},
_ => {
let a = hexify_16bit(*entry);
write!(f, "\\u")?;
core::fmt::Write::write_char(f, a[0])?;
core::fmt::Write::write_char(f, a[1])?;
core::fmt::Write::write_char(f, a[2])?;
core::fmt::Write::write_char(f, a[3])?;
},
}
}
write!(f, "\"")
}
}
impl EfiString16 {
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn efistr16_constructors() {
let original: &[u16] = &[0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0];
{
let s = unsafe { EfiStr16::from_ptr(original.as_ptr()) };
assert_eq!{s.as_ptr(), original.as_ptr()};
assert_eq!{s.as_slice().len(), 6};
assert_eq!{s.as_slice()[0], 0x41};
assert_eq!{s.as_slice_with_nul(), original};
}
{
let s = unsafe { EfiStr16::from_slice_with_nul_unchecked(original) };
assert_eq!{s.as_ptr(), original.as_ptr()};
assert_eq!{s.as_slice().len(), 6};
assert_eq!{s.as_slice()[0], 0x41};
assert_eq!{s.as_slice_with_nul(), original};
}
{
let s = EfiStr16::from_slice_with_nul(original).unwrap();
assert_eq!{s.as_ptr(), original.as_ptr()};
assert_eq!{s.as_slice().len(), 6};
assert_eq!{s.as_slice()[0], 0x41};
assert_eq!{s.as_slice_with_nul(), original};
}
{
assert_eq!{
EfiStr16::from_slice_with_nul(
&[],
).err().unwrap(),
FromSliceWithNulError::NotNulTerminated,
};
assert_eq!{
EfiStr16::from_slice_with_nul(
&[0x0041],
).err().unwrap(),
FromSliceWithNulError::NotNulTerminated,
};
assert!{
EfiStr16::from_slice_with_nul(
&[0x0041, 0x0000],
).is_ok()
};
assert_eq!{
EfiStr16::from_slice_with_nul(
&[0x0000, 0x0041, 0x0000],
).err().unwrap(),
FromSliceWithNulError::InteriorNul(0),
};
assert_eq!{
EfiStr16::from_slice_with_nul(
&[0x0041, 0x0000, 0x0000],
).err().unwrap(),
FromSliceWithNulError::InteriorNul(1),
};
assert_eq!{
EfiStr16::from_slice_with_nul(
&[0x0000, 0x0041, 0x0000, 0x0042, 0x0000],
).err().unwrap(),
FromSliceWithNulError::InteriorNul(0),
};
}
{
let s: &EfiStr16 = Default::default();
assert_eq!{s.as_slice_with_nul(), &[0]};
}
}
#[test]
fn efistr16_compare() {
let slice: &[u16] = &[
0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0,
0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0,
];
let string1 = unsafe { EfiStr16::from_slice_with_nul_unchecked(&slice[0..7]) };
let string2 = unsafe { EfiStr16::from_slice_with_nul_unchecked(&slice[7..14]) };
assert_eq!{string1, string2};
assert_eq!{string1.cmp(string2), core::cmp::Ordering::Equal};
}
#[test]
fn efistr16_converters() {
let slice_good: &[u16] = &[0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0];
let slice_bad: &[u16] = &[0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0xd800, 0];
let string_good: &EfiStr16 = unsafe { EfiStr16::from_slice_with_nul_unchecked(slice_good) };
let string_bad: &EfiStr16 = unsafe { EfiStr16::from_slice_with_nul_unchecked(slice_bad) };
assert_eq!{string_good.to_string().unwrap(), "ABCDEF"};
assert!{string_bad.to_string().is_err()};
assert_eq!{string_good.to_string_lossy(), "ABCDEF"};
assert_eq!{string_bad.to_string_lossy(), "ABCDE�"};
}
#[test]
fn efistr16_debug() {
let slice: &[u16] = &[
0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046,
0x0001, 0x000a, 0xabcd,
0,
];
let string = unsafe { EfiStr16::from_slice_with_nul_unchecked(slice) };
assert_eq!{
format!{"{:?}", string},
"\"ABCDEF\\x01\\n\\uabcd\"",
};
}
#[test]
fn efistr16_box() {
let slice: &[u16] = &[0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0];
let string = unsafe { EfiStr16::from_slice_with_nul_unchecked(slice) };
let boxed: alloc::boxed::Box<EfiStr16> = alloc::boxed::Box::from(string);
assert_eq!{string.as_slice_with_nul(), slice};
assert_eq!{boxed.as_slice_with_nul(), slice};
assert_eq!{boxed.clone().as_slice_with_nul(), slice};
assert_eq!{
<alloc::boxed::Box<EfiStr16> as Default>::default().as_slice_with_nul(),
<&EfiStr16 as Default>::default().as_slice_with_nul(),
};
}
}