1use alloc::borrow::ToOwned;
2use alloc::vec::Vec;
3use core::borrow::Borrow;
4use core::fmt::{Formatter, Write};
5use core::mem::transmute;
6use core::ops::Deref;
7use core::slice::{from_raw_parts, IterMut};
8use core::str::FromStr;
9use thiserror::Error;
10
11#[repr(transparent)]
15#[derive(Debug, PartialEq, Eq)]
16pub struct EfiStr([u16]);
17
18impl EfiStr {
19 pub const EMPTY: &'static Self = unsafe { Self::new_unchecked(&[0]) };
21
22 pub const unsafe fn new_unchecked(data: &[u16]) -> &Self {
29 &*(data as *const [u16] as *const Self)
31 }
32
33 pub unsafe fn from_ptr<'a>(ptr: *const u16) -> &'a Self {
36 let mut len = 0;
37
38 while *ptr.add(len) != 0 {
39 len += 1;
40 }
41
42 Self::new_unchecked(from_raw_parts(ptr, len + 1))
43 }
44
45 pub const fn as_ptr(&self) -> *const u16 {
47 self.0.as_ptr()
48 }
49
50 pub const fn len(&self) -> usize {
52 self.0.len() - 1
53 }
54
55 pub const fn is_empty(&self) -> bool {
57 self.len() == 0
58 }
59
60 pub const fn display(&self) -> impl core::fmt::Display + '_ {
63 Display(self)
64 }
65}
66
67impl AsRef<EfiStr> for EfiStr {
68 fn as_ref(&self) -> &EfiStr {
69 self
70 }
71}
72
73impl AsRef<[u8]> for EfiStr {
74 fn as_ref(&self) -> &[u8] {
75 let ptr = self.0.as_ptr().cast();
76 let len = self.0.len() * 2;
77
78 unsafe { from_raw_parts(ptr, len) }
80 }
81}
82
83impl AsRef<[u16]> for EfiStr {
84 fn as_ref(&self) -> &[u16] {
85 &self.0
86 }
87}
88
89impl ToOwned for EfiStr {
90 type Owned = EfiString;
91
92 fn to_owned(&self) -> Self::Owned {
93 EfiString(self.0.to_vec())
94 }
95}
96
97struct Display<'a>(&'a EfiStr);
99
100impl core::fmt::Display for Display<'_> {
101 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
102 let mut ptr = self.0.as_ptr();
104
105 while unsafe { *ptr != 0 } {
106 let c = unsafe { *ptr };
107 let c = char::from_u32(c.into()).unwrap_or(char::REPLACEMENT_CHARACTER);
108
109 f.write_char(c)?;
110
111 unsafe { ptr = ptr.add(1) };
112 }
113
114 Ok(())
115 }
116}
117
118#[derive(Debug)]
120pub struct EfiString(Vec<u16>);
121
122impl EfiString {
123 pub fn push(&mut self, c: EfiChar) {
124 self.0.pop();
125 self.0.push(c.0);
126 self.0.push(0);
127 }
128
129 pub fn push_str<S: AsRef<str>>(&mut self, s: S) -> Result<(), EfiStringError> {
130 let s = s.as_ref();
131 let l = self.0.len();
132
133 self.0.pop();
134
135 for (i, c) in s.chars().enumerate() {
136 let e = match c {
137 '\0' => EfiStringError::HasNul(i),
138 '\u{10000}'.. => EfiStringError::UnsupportedChar(i, c),
139 c => {
140 self.0.push(c.encode_utf16(&mut [0; 1])[0]);
141 continue;
142 }
143 };
144
145 unsafe { self.0.set_len(l) };
146 self.0[l - 1] = 0;
147
148 return Err(e);
149 }
150
151 self.0.push(0);
152
153 Ok(())
154 }
155}
156
157impl FromStr for EfiString {
158 type Err = EfiStringError;
159
160 fn from_str(s: &str) -> Result<Self, Self::Err> {
161 let mut v = Self(Vec::with_capacity(s.len() + 1));
162 v.0.push(0);
163 v.push_str(s)?;
164 Ok(v)
165 }
166}
167
168impl Deref for EfiString {
169 type Target = EfiStr;
170
171 fn deref(&self) -> &Self::Target {
172 unsafe { EfiStr::new_unchecked(&self.0) }
173 }
174}
175
176impl AsRef<EfiStr> for EfiString {
177 fn as_ref(&self) -> &EfiStr {
178 self.deref()
179 }
180}
181
182impl Borrow<EfiStr> for EfiString {
183 fn borrow(&self) -> &EfiStr {
184 self.deref()
185 }
186}
187
188impl<'a> IntoIterator for &'a mut EfiString {
189 type Item = &'a mut EfiChar;
190 type IntoIter = IterMut<'a, EfiChar>;
191
192 fn into_iter(self) -> Self::IntoIter {
193 let len = self.0.len() - 1; unsafe { transmute(self.0[..len].iter_mut()) }
197 }
198}
199
200#[repr(transparent)]
202pub struct EfiChar(u16);
203
204impl EfiChar {
205 pub const FULL_STOP: Self = Self(b'.' as u16);
206 pub const REVERSE_SOLIDUS: Self = Self(b'\\' as u16);
207}
208
209impl PartialEq<u8> for EfiChar {
210 fn eq(&self, other: &u8) -> bool {
211 self.0 == (*other).into()
212 }
213}
214
215#[derive(Debug, Error)]
217pub enum EfiStringError {
218 #[error("the value contains NUL character")]
219 HasNul(usize),
220
221 #[error("the value contains character outside Basic Multilingual Plane")]
222 UnsupportedChar(usize, char),
223}