]> git.proxmox.com Git - rustc.git/blob - library/core/src/char/convert.rs
f1a51a550f5792216c8d723818e1a5f7ec38fd2c
[rustc.git] / library / core / src / char / convert.rs
1 //! Character conversions.
2
3 use crate::char::TryFromCharError;
4 use crate::convert::TryFrom;
5 use crate::fmt;
6 use crate::mem::transmute;
7 use crate::str::FromStr;
8
9 /// Converts a `u32` to a `char`. See [`char::from_u32`].
10 #[must_use]
11 #[inline]
12 pub(super) const fn from_u32(i: u32) -> Option<char> {
13 // FIXME: once Result::ok is const fn, use it here
14 match char_try_from_u32(i) {
15 Ok(c) => Some(c),
16 Err(_) => None,
17 }
18 }
19
20 /// Converts a `u32` to a `char`, ignoring validity. See [`char::from_u32_unchecked`].
21 #[inline]
22 #[must_use]
23 pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char {
24 // SAFETY: the caller must guarantee that `i` is a valid char value.
25 if cfg!(debug_assertions) { char::from_u32(i).unwrap() } else { unsafe { transmute(i) } }
26 }
27
28 #[stable(feature = "char_convert", since = "1.13.0")]
29 #[rustc_const_unstable(feature = "const_convert", issue = "88674")]
30 impl const From<char> for u32 {
31 /// Converts a [`char`] into a [`u32`].
32 ///
33 /// # Examples
34 ///
35 /// ```
36 /// use std::mem;
37 ///
38 /// let c = 'c';
39 /// let u = u32::from(c);
40 /// assert!(4 == mem::size_of_val(&u))
41 /// ```
42 #[inline]
43 fn from(c: char) -> Self {
44 c as u32
45 }
46 }
47
48 #[stable(feature = "more_char_conversions", since = "1.51.0")]
49 #[rustc_const_unstable(feature = "const_convert", issue = "88674")]
50 impl const From<char> for u64 {
51 /// Converts a [`char`] into a [`u64`].
52 ///
53 /// # Examples
54 ///
55 /// ```
56 /// use std::mem;
57 ///
58 /// let c = '👤';
59 /// let u = u64::from(c);
60 /// assert!(8 == mem::size_of_val(&u))
61 /// ```
62 #[inline]
63 fn from(c: char) -> Self {
64 // The char is casted to the value of the code point, then zero-extended to 64 bit.
65 // See [https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics]
66 c as u64
67 }
68 }
69
70 #[stable(feature = "more_char_conversions", since = "1.51.0")]
71 #[rustc_const_unstable(feature = "const_convert", issue = "88674")]
72 impl const From<char> for u128 {
73 /// Converts a [`char`] into a [`u128`].
74 ///
75 /// # Examples
76 ///
77 /// ```
78 /// use std::mem;
79 ///
80 /// let c = 'âš™';
81 /// let u = u128::from(c);
82 /// assert!(16 == mem::size_of_val(&u))
83 /// ```
84 #[inline]
85 fn from(c: char) -> Self {
86 // The char is casted to the value of the code point, then zero-extended to 128 bit.
87 // See [https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics]
88 c as u128
89 }
90 }
91
92 /// Map `char` with code point in U+0000..=U+00FF to byte in 0x00..=0xFF with same value, failing
93 /// if the code point is greater than U+00FF.
94 ///
95 /// See [`impl From<u8> for char`](char#impl-From<u8>-for-char) for details on the encoding.
96 #[stable(feature = "u8_from_char", since = "1.59.0")]
97 impl TryFrom<char> for u8 {
98 type Error = TryFromCharError;
99
100 #[inline]
101 fn try_from(c: char) -> Result<u8, Self::Error> {
102 u8::try_from(u32::from(c)).map_err(|_| TryFromCharError(()))
103 }
104 }
105
106 /// Maps a byte in 0x00..=0xFF to a `char` whose code point has the same value, in U+0000..=U+00FF.
107 ///
108 /// Unicode is designed such that this effectively decodes bytes
109 /// with the character encoding that IANA calls ISO-8859-1.
110 /// This encoding is compatible with ASCII.
111 ///
112 /// Note that this is different from ISO/IEC 8859-1 a.k.a. ISO 8859-1 (with one less hyphen),
113 /// which leaves some "blanks", byte values that are not assigned to any character.
114 /// ISO-8859-1 (the IANA one) assigns them to the C0 and C1 control codes.
115 ///
116 /// Note that this is *also* different from Windows-1252 a.k.a. code page 1252,
117 /// which is a superset ISO/IEC 8859-1 that assigns some (not all!) blanks
118 /// to punctuation and various Latin characters.
119 ///
120 /// To confuse things further, [on the Web](https://encoding.spec.whatwg.org/)
121 /// `ascii`, `iso-8859-1`, and `windows-1252` are all aliases
122 /// for a superset of Windows-1252 that fills the remaining blanks with corresponding
123 /// C0 and C1 control codes.
124 #[stable(feature = "char_convert", since = "1.13.0")]
125 #[rustc_const_unstable(feature = "const_convert", issue = "88674")]
126 impl const From<u8> for char {
127 /// Converts a [`u8`] into a [`char`].
128 ///
129 /// # Examples
130 ///
131 /// ```
132 /// use std::mem;
133 ///
134 /// let u = 32 as u8;
135 /// let c = char::from(u);
136 /// assert!(4 == mem::size_of_val(&c))
137 /// ```
138 #[inline]
139 fn from(i: u8) -> Self {
140 i as char
141 }
142 }
143
144 /// An error which can be returned when parsing a char.
145 ///
146 /// This `struct` is created when using the [`char::from_str`] method.
147 #[stable(feature = "char_from_str", since = "1.20.0")]
148 #[derive(Clone, Debug, PartialEq, Eq)]
149 pub struct ParseCharError {
150 kind: CharErrorKind,
151 }
152
153 impl ParseCharError {
154 #[unstable(
155 feature = "char_error_internals",
156 reason = "this method should not be available publicly",
157 issue = "none"
158 )]
159 #[doc(hidden)]
160 pub fn __description(&self) -> &str {
161 match self.kind {
162 CharErrorKind::EmptyString => "cannot parse char from empty string",
163 CharErrorKind::TooManyChars => "too many characters in string",
164 }
165 }
166 }
167
168 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
169 enum CharErrorKind {
170 EmptyString,
171 TooManyChars,
172 }
173
174 #[stable(feature = "char_from_str", since = "1.20.0")]
175 impl fmt::Display for ParseCharError {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 self.__description().fmt(f)
178 }
179 }
180
181 #[stable(feature = "char_from_str", since = "1.20.0")]
182 impl FromStr for char {
183 type Err = ParseCharError;
184
185 #[inline]
186 fn from_str(s: &str) -> Result<Self, Self::Err> {
187 let mut chars = s.chars();
188 match (chars.next(), chars.next()) {
189 (None, _) => Err(ParseCharError { kind: CharErrorKind::EmptyString }),
190 (Some(c), None) => Ok(c),
191 _ => Err(ParseCharError { kind: CharErrorKind::TooManyChars }),
192 }
193 }
194 }
195
196 #[inline]
197 const fn char_try_from_u32(i: u32) -> Result<char, CharTryFromError> {
198 // This is an optimized version of the check
199 // (i > MAX as u32) || (i >= 0xD800 && i <= 0xDFFF),
200 // which can also be written as
201 // i >= 0x110000 || (i >= 0xD800 && i < 0xE000).
202 //
203 // The XOR with 0xD800 permutes the ranges such that 0xD800..0xE000 is
204 // mapped to 0x0000..0x0800, while keeping all the high bits outside 0xFFFF the same.
205 // In particular, numbers >= 0x110000 stay in this range.
206 //
207 // Subtracting 0x800 causes 0x0000..0x0800 to wrap, meaning that a single
208 // unsigned comparison against 0x110000 - 0x800 will detect both the wrapped
209 // surrogate range as well as the numbers originally larger than 0x110000.
210 //
211 if (i ^ 0xD800).wrapping_sub(0x800) >= 0x110000 - 0x800 {
212 Err(CharTryFromError(()))
213 } else {
214 // SAFETY: checked that it's a legal unicode value
215 Ok(unsafe { transmute(i) })
216 }
217 }
218
219 #[stable(feature = "try_from", since = "1.34.0")]
220 impl TryFrom<u32> for char {
221 type Error = CharTryFromError;
222
223 #[inline]
224 fn try_from(i: u32) -> Result<Self, Self::Error> {
225 char_try_from_u32(i)
226 }
227 }
228
229 /// The error type returned when a conversion from [`prim@u32`] to [`prim@char`] fails.
230 ///
231 /// This `struct` is created by the [`char::try_from<u32>`](char#impl-TryFrom<u32>-for-char) method.
232 /// See its documentation for more.
233 #[stable(feature = "try_from", since = "1.34.0")]
234 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
235 pub struct CharTryFromError(());
236
237 #[stable(feature = "try_from", since = "1.34.0")]
238 impl fmt::Display for CharTryFromError {
239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240 "converted integer out of range for `char`".fmt(f)
241 }
242 }
243
244 /// Converts a digit in the given radix to a `char`. See [`char::from_digit`].
245 #[inline]
246 #[must_use]
247 pub(super) const fn from_digit(num: u32, radix: u32) -> Option<char> {
248 if radix > 36 {
249 panic!("from_digit: radix is too high (maximum 36)");
250 }
251 if num < radix {
252 let num = num as u8;
253 if num < 10 { Some((b'0' + num) as char) } else { Some((b'a' + num - 10) as char) }
254 } else {
255 None
256 }
257 }