• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! UEFI character handling
2 //!
3 //! UEFI uses both Latin-1 and UCS-2 character encoding, this module implements
4 //! support for the associated character types.
5 
6 use core::fmt::{self, Display, Formatter};
7 
8 /// Character conversion error
9 #[derive(Clone, Copy, Debug)]
10 pub struct CharConversionError;
11 
12 impl Display for CharConversionError {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result13     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
14         write!(f, "{self:?}")
15     }
16 }
17 
18 #[cfg(feature = "unstable")]
19 impl core::error::Error for CharConversionError {}
20 
21 /// A Latin-1 character
22 #[derive(Clone, Copy, Default, Eq, PartialEq, PartialOrd, Ord, Hash)]
23 #[repr(transparent)]
24 pub struct Char8(u8);
25 
26 impl TryFrom<char> for Char8 {
27     type Error = CharConversionError;
28 
try_from(value: char) -> Result<Self, Self::Error>29     fn try_from(value: char) -> Result<Self, Self::Error> {
30         let code_point = u32::from(value);
31         u8::try_from(code_point)
32             .map(Char8)
33             .map_err(|_| CharConversionError)
34     }
35 }
36 
37 impl From<Char8> for char {
from(char: Char8) -> Self38     fn from(char: Char8) -> Self {
39         Self::from(char.0)
40     }
41 }
42 
43 impl From<u8> for Char8 {
from(value: u8) -> Self44     fn from(value: u8) -> Self {
45         Self(value)
46     }
47 }
48 
49 impl From<Char8> for u8 {
from(char: Char8) -> Self50     fn from(char: Char8) -> Self {
51         char.0
52     }
53 }
54 
55 impl fmt::Debug for Char8 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result56     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57         <char as fmt::Debug>::fmt(&From::from(self.0), f)
58     }
59 }
60 
61 impl fmt::Display for Char8 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result62     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63         <char as fmt::Display>::fmt(&From::from(self.0), f)
64     }
65 }
66 
67 impl PartialEq<char> for Char8 {
eq(&self, other: &char) -> bool68     fn eq(&self, other: &char) -> bool {
69         u32::from(self.0) == u32::from(*other)
70     }
71 }
72 
73 /// Latin-1 version of the NUL character
74 pub const NUL_8: Char8 = Char8(0);
75 
76 /// An UCS-2 code point
77 #[derive(Clone, Copy, Default, Eq, PartialEq, PartialOrd, Ord, Hash)]
78 #[repr(transparent)]
79 pub struct Char16(u16);
80 
81 impl Char16 {
82     /// Creates a UCS-2 character from a Rust character without checks.
83     ///
84     /// # Safety
85     /// The caller must be sure that the character is valid.
86     #[must_use]
from_u16_unchecked(val: u16) -> Self87     pub const unsafe fn from_u16_unchecked(val: u16) -> Self {
88         Self(val)
89     }
90 
91     /// Checks if the value is within the ASCII range.
92     #[must_use]
is_ascii(&self) -> bool93     pub const fn is_ascii(&self) -> bool {
94         self.0 <= 127
95     }
96 }
97 
98 impl TryFrom<char> for Char16 {
99     type Error = CharConversionError;
100 
try_from(value: char) -> Result<Self, Self::Error>101     fn try_from(value: char) -> Result<Self, Self::Error> {
102         let code_point = u32::from(value);
103         u16::try_from(code_point)
104             .map(Char16)
105             .map_err(|_| CharConversionError)
106     }
107 }
108 
109 impl From<Char16> for char {
from(char: Char16) -> Self110     fn from(char: Char16) -> Self {
111         u32::from(char.0).try_into().unwrap()
112     }
113 }
114 
115 impl TryFrom<u16> for Char16 {
116     type Error = CharConversionError;
117 
try_from(value: u16) -> Result<Self, Self::Error>118     fn try_from(value: u16) -> Result<Self, Self::Error> {
119         // We leverage char's TryFrom<u32> impl for Unicode validity checking
120         let res: Result<char, _> = u32::from(value).try_into();
121         if let Ok(ch) = res {
122             ch.try_into()
123         } else {
124             Err(CharConversionError)
125         }
126     }
127 }
128 
129 impl From<Char16> for u16 {
from(char: Char16) -> Self130     fn from(char: Char16) -> Self {
131         char.0
132     }
133 }
134 
135 impl fmt::Debug for Char16 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result136     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137         if let Ok(c) = u32::from(self.0).try_into() {
138             <char as fmt::Debug>::fmt(&c, f)
139         } else {
140             write!(f, "Char16({:?})", self.0)
141         }
142     }
143 }
144 
145 impl fmt::Display for Char16 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result146     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147         if let Ok(c) = u32::from(self.0).try_into() {
148             <char as fmt::Display>::fmt(&c, f)
149         } else {
150             write!(f, "{}", core::char::REPLACEMENT_CHARACTER)
151         }
152     }
153 }
154 
155 impl PartialEq<char> for Char16 {
eq(&self, other: &char) -> bool156     fn eq(&self, other: &char) -> bool {
157         u32::from(self.0) == u32::from(*other)
158     }
159 }
160 
161 /// UCS-2 version of the NUL character
162 pub const NUL_16: Char16 = unsafe { Char16::from_u16_unchecked(0) };
163 
164 #[cfg(test)]
165 mod tests {
166     use super::*;
167 
168     #[test]
test_char8_from_char()169     fn test_char8_from_char() {
170         assert_eq!(Char8::try_from('A').unwrap(), Char8(0x41));
171     }
172 
173     #[test]
test_char16_from_char()174     fn test_char16_from_char() {
175         assert_eq!(Char16::try_from('A').unwrap(), Char16(0x41));
176         assert_eq!(Char16::try_from('ꋃ').unwrap(), Char16(0xa2c3));
177     }
178 
179     /// Test that `Char8` and `Char16` can be directly compared with `char`.
180     #[test]
test_char_eq()181     fn test_char_eq() {
182         let primitive_char: char = 'A';
183         assert_eq!(Char8(0x41), primitive_char);
184         assert_eq!(Char16(0x41), primitive_char);
185     }
186 }
187