]> git.lizzy.rs Git - rust.git/blob - library/core/src/char/convert.rs
Auto merge of #2747 - RalfJung:rustup, r=RalfJung
[rust.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 }