]> git.lizzy.rs Git - rust.git/blob - src/libcore/char.rs
core: Split apart the global `core` feature
[rust.git] / src / libcore / char.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Character manipulation.
12 //!
13 //! For more details, see ::rustc_unicode::char (a.k.a. std::char)
14
15 #![allow(non_snake_case)]
16 #![doc(primitive = "char")]
17 #![stable(feature = "rust1", since = "1.0.0")]
18
19 use iter::Iterator;
20 use mem::transmute;
21 use option::Option::{None, Some};
22 use option::Option;
23 use slice::SliceExt;
24
25 // UTF-8 ranges and tags for encoding characters
26 const TAG_CONT: u8    = 0b1000_0000;
27 const TAG_TWO_B: u8   = 0b1100_0000;
28 const TAG_THREE_B: u8 = 0b1110_0000;
29 const TAG_FOUR_B: u8  = 0b1111_0000;
30 const MAX_ONE_B: u32   =     0x80;
31 const MAX_TWO_B: u32   =    0x800;
32 const MAX_THREE_B: u32 =  0x10000;
33
34 /*
35     Lu  Uppercase_Letter        an uppercase letter
36     Ll  Lowercase_Letter        a lowercase letter
37     Lt  Titlecase_Letter        a digraphic character, with first part uppercase
38     Lm  Modifier_Letter         a modifier letter
39     Lo  Other_Letter            other letters, including syllables and ideographs
40     Mn  Nonspacing_Mark         a nonspacing combining mark (zero advance width)
41     Mc  Spacing_Mark            a spacing combining mark (positive advance width)
42     Me  Enclosing_Mark          an enclosing combining mark
43     Nd  Decimal_Number          a decimal digit
44     Nl  Letter_Number           a letterlike numeric character
45     No  Other_Number            a numeric character of other type
46     Pc  Connector_Punctuation   a connecting punctuation mark, like a tie
47     Pd  Dash_Punctuation        a dash or hyphen punctuation mark
48     Ps  Open_Punctuation        an opening punctuation mark (of a pair)
49     Pe  Close_Punctuation       a closing punctuation mark (of a pair)
50     Pi  Initial_Punctuation     an initial quotation mark
51     Pf  Final_Punctuation       a final quotation mark
52     Po  Other_Punctuation       a punctuation mark of other type
53     Sm  Math_Symbol             a symbol of primarily mathematical use
54     Sc  Currency_Symbol         a currency sign
55     Sk  Modifier_Symbol         a non-letterlike modifier symbol
56     So  Other_Symbol            a symbol of other type
57     Zs  Space_Separator         a space character (of various non-zero widths)
58     Zl  Line_Separator          U+2028 LINE SEPARATOR only
59     Zp  Paragraph_Separator     U+2029 PARAGRAPH SEPARATOR only
60     Cc  Control                 a C0 or C1 control code
61     Cf  Format                  a format control character
62     Cs  Surrogate               a surrogate code point
63     Co  Private_Use             a private-use character
64     Cn  Unassigned              a reserved unassigned code point or a noncharacter
65 */
66
67 /// The highest valid code point
68 #[stable(feature = "rust1", since = "1.0.0")]
69 pub const MAX: char = '\u{10ffff}';
70
71 /// Converts a `u32` to an `Option<char>`.
72 ///
73 /// # Examples
74 ///
75 /// ```
76 /// use std::char;
77 ///
78 /// assert_eq!(char::from_u32(0x2764), Some('❤'));
79 /// assert_eq!(char::from_u32(0x110000), None); // invalid character
80 /// ```
81 #[inline]
82 #[stable(feature = "rust1", since = "1.0.0")]
83 pub fn from_u32(i: u32) -> Option<char> {
84     // catch out-of-bounds and surrogates
85     if (i > MAX as u32) || (i >= 0xD800 && i <= 0xDFFF) {
86         None
87     } else {
88         Some(unsafe { transmute(i) })
89     }
90 }
91
92 /// Converts a number to the character representing it.
93 ///
94 /// # Return value
95 ///
96 /// Returns `Some(char)` if `num` represents one digit under `radix`,
97 /// using one character of `0-9` or `a-z`, or `None` if it doesn't.
98 ///
99 /// # Panics
100 ///
101 /// Panics if given an `radix` > 36.
102 ///
103 /// # Examples
104 ///
105 /// ```
106 /// use std::char;
107 ///
108 /// let c = char::from_digit(4, 10);
109 ///
110 /// assert_eq!(c, Some('4'));
111 /// ```
112 #[inline]
113 #[stable(feature = "rust1", since = "1.0.0")]
114 pub fn from_digit(num: u32, radix: u32) -> Option<char> {
115     if radix > 36 {
116         panic!("from_digit: radix is too high (maximum 36)");
117     }
118     if num < radix {
119         unsafe {
120             if num < 10 {
121                 Some(transmute('0' as u32 + num))
122             } else {
123                 Some(transmute('a' as u32 + num - 10))
124             }
125         }
126     } else {
127         None
128     }
129 }
130
131 // NB: the stabilization and documentation for this trait is in
132 // unicode/char.rs, not here
133 #[allow(missing_docs)] // docs in libunicode/u_char.rs
134 #[doc(hidden)]
135 #[unstable(feature = "core_char_ext",
136            reason = "the stable interface is `impl char` in later crate")]
137 pub trait CharExt {
138     fn is_digit(self, radix: u32) -> bool;
139     fn to_digit(self, radix: u32) -> Option<u32>;
140     fn escape_unicode(self) -> EscapeUnicode;
141     fn escape_default(self) -> EscapeDefault;
142     fn len_utf8(self) -> usize;
143     fn len_utf16(self) -> usize;
144     fn encode_utf8(self, dst: &mut [u8]) -> Option<usize>;
145     fn encode_utf16(self, dst: &mut [u16]) -> Option<usize>;
146 }
147
148 impl CharExt for char {
149     #[inline]
150     fn is_digit(self, radix: u32) -> bool {
151         self.to_digit(radix).is_some()
152     }
153
154     #[inline]
155     fn to_digit(self, radix: u32) -> Option<u32> {
156         if radix > 36 {
157             panic!("to_digit: radix is too high (maximum 36)");
158         }
159         let val = match self {
160           '0' ... '9' => self as u32 - '0' as u32,
161           'a' ... 'z' => self as u32 - 'a' as u32 + 10,
162           'A' ... 'Z' => self as u32 - 'A' as u32 + 10,
163           _ => return None,
164         };
165         if val < radix { Some(val) }
166         else { None }
167     }
168
169     #[inline]
170     fn escape_unicode(self) -> EscapeUnicode {
171         EscapeUnicode { c: self, state: EscapeUnicodeState::Backslash }
172     }
173
174     #[inline]
175     fn escape_default(self) -> EscapeDefault {
176         let init_state = match self {
177             '\t' => EscapeDefaultState::Backslash('t'),
178             '\r' => EscapeDefaultState::Backslash('r'),
179             '\n' => EscapeDefaultState::Backslash('n'),
180             '\\' => EscapeDefaultState::Backslash('\\'),
181             '\'' => EscapeDefaultState::Backslash('\''),
182             '"'  => EscapeDefaultState::Backslash('"'),
183             '\x20' ... '\x7e' => EscapeDefaultState::Char(self),
184             _ => EscapeDefaultState::Unicode(self.escape_unicode())
185         };
186         EscapeDefault { state: init_state }
187     }
188
189     #[inline]
190     fn len_utf8(self) -> usize {
191         let code = self as u32;
192         if code < MAX_ONE_B {
193             1
194         } else if code < MAX_TWO_B {
195             2
196         } else if code < MAX_THREE_B {
197             3
198         } else {
199             4
200         }
201     }
202
203     #[inline]
204     fn len_utf16(self) -> usize {
205         let ch = self as u32;
206         if (ch & 0xFFFF) == ch { 1 } else { 2 }
207     }
208
209     #[inline]
210     fn encode_utf8(self, dst: &mut [u8]) -> Option<usize> {
211         encode_utf8_raw(self as u32, dst)
212     }
213
214     #[inline]
215     fn encode_utf16(self, dst: &mut [u16]) -> Option<usize> {
216         encode_utf16_raw(self as u32, dst)
217     }
218 }
219
220 /// Encodes a raw u32 value as UTF-8 into the provided byte buffer,
221 /// and then returns the number of bytes written.
222 ///
223 /// If the buffer is not large enough, nothing will be written into it
224 /// and a `None` will be returned.
225 #[inline]
226 #[unstable(feature = "char_internals",
227            reason = "this function should not be exposed publicly")]
228 pub fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> Option<usize> {
229     // Marked #[inline] to allow llvm optimizing it away
230     if code < MAX_ONE_B && !dst.is_empty() {
231         dst[0] = code as u8;
232         Some(1)
233     } else if code < MAX_TWO_B && dst.len() >= 2 {
234         dst[0] = (code >> 6 & 0x1F) as u8 | TAG_TWO_B;
235         dst[1] = (code & 0x3F) as u8 | TAG_CONT;
236         Some(2)
237     } else if code < MAX_THREE_B && dst.len() >= 3  {
238         dst[0] = (code >> 12 & 0x0F) as u8 | TAG_THREE_B;
239         dst[1] = (code >>  6 & 0x3F) as u8 | TAG_CONT;
240         dst[2] = (code & 0x3F) as u8 | TAG_CONT;
241         Some(3)
242     } else if dst.len() >= 4 {
243         dst[0] = (code >> 18 & 0x07) as u8 | TAG_FOUR_B;
244         dst[1] = (code >> 12 & 0x3F) as u8 | TAG_CONT;
245         dst[2] = (code >>  6 & 0x3F) as u8 | TAG_CONT;
246         dst[3] = (code & 0x3F) as u8 | TAG_CONT;
247         Some(4)
248     } else {
249         None
250     }
251 }
252
253 /// Encodes a raw u32 value as UTF-16 into the provided `u16` buffer,
254 /// and then returns the number of `u16`s written.
255 ///
256 /// If the buffer is not large enough, nothing will be written into it
257 /// and a `None` will be returned.
258 #[inline]
259 #[unstable(feature = "char_internals",
260            reason = "this function should not be exposed publicly")]
261 pub fn encode_utf16_raw(mut ch: u32, dst: &mut [u16]) -> Option<usize> {
262     // Marked #[inline] to allow llvm optimizing it away
263     if (ch & 0xFFFF) == ch && !dst.is_empty() {
264         // The BMP falls through (assuming non-surrogate, as it should)
265         dst[0] = ch as u16;
266         Some(1)
267     } else if dst.len() >= 2 {
268         // Supplementary planes break into surrogates.
269         ch -= 0x1_0000;
270         dst[0] = 0xD800 | ((ch >> 10) as u16);
271         dst[1] = 0xDC00 | ((ch as u16) & 0x3FF);
272         Some(2)
273     } else {
274         None
275     }
276 }
277
278 /// An iterator over the characters that represent a `char`, as escaped by
279 /// Rust's unicode escaping rules.
280 #[derive(Clone)]
281 #[stable(feature = "rust1", since = "1.0.0")]
282 pub struct EscapeUnicode {
283     c: char,
284     state: EscapeUnicodeState
285 }
286
287 #[derive(Clone)]
288 enum EscapeUnicodeState {
289     Backslash,
290     Type,
291     LeftBrace,
292     Value(usize),
293     RightBrace,
294     Done,
295 }
296
297 #[stable(feature = "rust1", since = "1.0.0")]
298 impl Iterator for EscapeUnicode {
299     type Item = char;
300
301     fn next(&mut self) -> Option<char> {
302         match self.state {
303             EscapeUnicodeState::Backslash => {
304                 self.state = EscapeUnicodeState::Type;
305                 Some('\\')
306             }
307             EscapeUnicodeState::Type => {
308                 self.state = EscapeUnicodeState::LeftBrace;
309                 Some('u')
310             }
311             EscapeUnicodeState::LeftBrace => {
312                 let mut n = 0;
313                 while (self.c as u32) >> (4 * (n + 1)) != 0 {
314                     n += 1;
315                 }
316                 self.state = EscapeUnicodeState::Value(n);
317                 Some('{')
318             }
319             EscapeUnicodeState::Value(offset) => {
320                 let v = match ((self.c as i32) >> (offset * 4)) & 0xf {
321                     i @ 0 ... 9 => '0' as i32 + i,
322                     i => 'a' as i32 + (i - 10)
323                 };
324                 if offset == 0 {
325                     self.state = EscapeUnicodeState::RightBrace;
326                 } else {
327                     self.state = EscapeUnicodeState::Value(offset - 1);
328                 }
329                 Some(unsafe { transmute(v) })
330             }
331             EscapeUnicodeState::RightBrace => {
332                 self.state = EscapeUnicodeState::Done;
333                 Some('}')
334             }
335             EscapeUnicodeState::Done => None,
336         }
337     }
338 }
339
340 /// An iterator over the characters that represent a `char`, escaped
341 /// for maximum portability.
342 #[derive(Clone)]
343 #[stable(feature = "rust1", since = "1.0.0")]
344 pub struct EscapeDefault {
345     state: EscapeDefaultState
346 }
347
348 #[derive(Clone)]
349 enum EscapeDefaultState {
350     Backslash(char),
351     Char(char),
352     Done,
353     Unicode(EscapeUnicode),
354 }
355
356 #[stable(feature = "rust1", since = "1.0.0")]
357 impl Iterator for EscapeDefault {
358     type Item = char;
359
360     fn next(&mut self) -> Option<char> {
361         match self.state {
362             EscapeDefaultState::Backslash(c) => {
363                 self.state = EscapeDefaultState::Char(c);
364                 Some('\\')
365             }
366             EscapeDefaultState::Char(c) => {
367                 self.state = EscapeDefaultState::Done;
368                 Some(c)
369             }
370             EscapeDefaultState::Done => None,
371             EscapeDefaultState::Unicode(ref mut iter) => iter.next()
372         }
373     }
374 }