]> git.lizzy.rs Git - rust.git/blob - src/libcore/char.rs
Merge pull request #20510 from tshepang/patch-6
[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 ::unicode::char (a.k.a. std::char)
14
15 #![allow(non_snake_case)]
16 #![doc(primitive = "char")]
17
18 use iter::Iterator;
19 use mem::transmute;
20 use option::Option::{None, Some};
21 use option::Option;
22 use slice::SliceExt;
23
24 // UTF-8 ranges and tags for encoding characters
25 static TAG_CONT: u8    = 0b1000_0000u8;
26 static TAG_TWO_B: u8   = 0b1100_0000u8;
27 static TAG_THREE_B: u8 = 0b1110_0000u8;
28 static TAG_FOUR_B: u8  = 0b1111_0000u8;
29 static MAX_ONE_B: u32   =     0x80u32;
30 static MAX_TWO_B: u32   =    0x800u32;
31 static MAX_THREE_B: u32 =  0x10000u32;
32
33 /*
34     Lu  Uppercase_Letter        an uppercase letter
35     Ll  Lowercase_Letter        a lowercase letter
36     Lt  Titlecase_Letter        a digraphic character, with first part uppercase
37     Lm  Modifier_Letter         a modifier letter
38     Lo  Other_Letter            other letters, including syllables and ideographs
39     Mn  Nonspacing_Mark         a nonspacing combining mark (zero advance width)
40     Mc  Spacing_Mark            a spacing combining mark (positive advance width)
41     Me  Enclosing_Mark          an enclosing combining mark
42     Nd  Decimal_Number          a decimal digit
43     Nl  Letter_Number           a letterlike numeric character
44     No  Other_Number            a numeric character of other type
45     Pc  Connector_Punctuation   a connecting punctuation mark, like a tie
46     Pd  Dash_Punctuation        a dash or hyphen punctuation mark
47     Ps  Open_Punctuation        an opening punctuation mark (of a pair)
48     Pe  Close_Punctuation       a closing punctuation mark (of a pair)
49     Pi  Initial_Punctuation     an initial quotation mark
50     Pf  Final_Punctuation       a final quotation mark
51     Po  Other_Punctuation       a punctuation mark of other type
52     Sm  Math_Symbol             a symbol of primarily mathematical use
53     Sc  Currency_Symbol         a currency sign
54     Sk  Modifier_Symbol         a non-letterlike modifier symbol
55     So  Other_Symbol            a symbol of other type
56     Zs  Space_Separator         a space character (of various non-zero widths)
57     Zl  Line_Separator          U+2028 LINE SEPARATOR only
58     Zp  Paragraph_Separator     U+2029 PARAGRAPH SEPARATOR only
59     Cc  Control                 a C0 or C1 control code
60     Cf  Format                  a format control character
61     Cs  Surrogate               a surrogate code point
62     Co  Private_Use             a private-use character
63     Cn  Unassigned              a reserved unassigned code point or a noncharacter
64 */
65
66 /// The highest valid code point
67 #[stable]
68 pub const MAX: char = '\u{10ffff}';
69
70 /// Converts from `u32` to a `char`
71 #[inline]
72 #[unstable = "pending decisions about costructors for primitives"]
73 pub fn from_u32(i: u32) -> Option<char> {
74     // catch out-of-bounds and surrogates
75     if (i > MAX as u32) || (i >= 0xD800 && i <= 0xDFFF) {
76         None
77     } else {
78         Some(unsafe { transmute(i) })
79     }
80 }
81
82 ///
83 /// Converts a number to the character representing it
84 ///
85 /// # Return value
86 ///
87 /// Returns `Some(char)` if `num` represents one digit under `radix`,
88 /// using one character of `0-9` or `a-z`, or `None` if it doesn't.
89 ///
90 /// # Panics
91 ///
92 /// Panics if given an `radix` > 36.
93 ///
94 #[inline]
95 #[unstable = "pending decisions about costructors for primitives"]
96 pub fn from_digit(num: uint, radix: uint) -> Option<char> {
97     if radix > 36 {
98         panic!("from_digit: radix is too high (maximum 36)");
99     }
100     if num < radix {
101         unsafe {
102             if num < 10 {
103                 Some(transmute(('0' as uint + num) as u32))
104             } else {
105                 Some(transmute(('a' as uint + num - 10u) as u32))
106             }
107         }
108     } else {
109         None
110     }
111 }
112
113 /// Basic `char` manipulations.
114 #[experimental = "trait organization may change"]
115 pub trait Char {
116     /// Checks if a `char` parses as a numeric digit in the given radix.
117     ///
118     /// Compared to `is_numeric()`, this function only recognizes the characters
119     /// `0-9`, `a-z` and `A-Z`.
120     ///
121     /// # Return value
122     ///
123     /// Returns `true` if `c` is a valid digit under `radix`, and `false`
124     /// otherwise.
125     ///
126     /// # Panics
127     ///
128     /// Panics if given a radix > 36.
129     #[unstable = "pending error conventions"]
130     fn is_digit(self, radix: uint) -> bool;
131
132     /// Converts a character to the corresponding digit.
133     ///
134     /// # Return value
135     ///
136     /// If `c` is between '0' and '9', the corresponding value between 0 and
137     /// 9. If `c` is 'a' or 'A', 10. If `c` is 'b' or 'B', 11, etc. Returns
138     /// none if the character does not refer to a digit in the given radix.
139     ///
140     /// # Panics
141     ///
142     /// Panics if given a radix outside the range [0..36].
143     #[unstable = "pending error conventions, trait organization"]
144     fn to_digit(self, radix: uint) -> Option<uint>;
145
146     /// Returns an iterator that yields the hexadecimal Unicode escape
147     /// of a character, as `char`s.
148     ///
149     /// All characters are escaped with Rust syntax of the form `\\u{NNNN}`
150     /// where `NNNN` is the shortest hexadecimal representation of the code
151     /// point.
152     #[unstable = "pending error conventions, trait organization"]
153     fn escape_unicode(self) -> EscapeUnicode;
154
155     /// Returns an iterator that yields the 'default' ASCII and
156     /// C++11-like literal escape of a character, as `char`s.
157     ///
158     /// The default is chosen with a bias toward producing literals that are
159     /// legal in a variety of languages, including C++11 and similar C-family
160     /// languages. The exact rules are:
161     ///
162     /// * Tab, CR and LF are escaped as '\t', '\r' and '\n' respectively.
163     /// * Single-quote, double-quote and backslash chars are backslash-
164     ///   escaped.
165     /// * Any other chars in the range [0x20,0x7e] are not escaped.
166     /// * Any other chars are given hex Unicode escapes; see `escape_unicode`.
167     #[unstable = "pending error conventions, trait organization"]
168     fn escape_default(self) -> EscapeDefault;
169
170     /// Returns the amount of bytes this character would need if encoded in
171     /// UTF-8.
172     #[unstable = "pending trait organization"]
173     fn len_utf8(self) -> uint;
174
175     /// Returns the amount of bytes this character would need if encoded in
176     /// UTF-16.
177     #[unstable = "pending trait organization"]
178     fn len_utf16(self) -> uint;
179
180     /// Encodes this character as UTF-8 into the provided byte buffer,
181     /// and then returns the number of bytes written.
182     ///
183     /// If the buffer is not large enough, nothing will be written into it
184     /// and a `None` will be returned.
185     #[unstable = "pending trait organization"]
186     fn encode_utf8(&self, dst: &mut [u8]) -> Option<uint>;
187
188     /// Encodes this character as UTF-16 into the provided `u16` buffer,
189     /// and then returns the number of `u16`s written.
190     ///
191     /// If the buffer is not large enough, nothing will be written into it
192     /// and a `None` will be returned.
193     #[unstable = "pending trait organization"]
194     fn encode_utf16(&self, dst: &mut [u16]) -> Option<uint>;
195 }
196
197 #[experimental = "trait is experimental"]
198 impl Char for char {
199     #[unstable = "pending trait organization"]
200     fn is_digit(self, radix: uint) -> bool {
201         match self.to_digit(radix) {
202             Some(_) => true,
203             None    => false,
204         }
205     }
206
207     #[unstable = "pending trait organization"]
208     fn to_digit(self, radix: uint) -> Option<uint> {
209         if radix > 36 {
210             panic!("to_digit: radix is too high (maximum 36)");
211         }
212         let val = match self {
213           '0' ... '9' => self as uint - ('0' as uint),
214           'a' ... 'z' => self as uint + 10u - ('a' as uint),
215           'A' ... 'Z' => self as uint + 10u - ('A' as uint),
216           _ => return None,
217         };
218         if val < radix { Some(val) }
219         else { None }
220     }
221
222     #[unstable = "pending error conventions, trait organization"]
223     fn escape_unicode(self) -> EscapeUnicode {
224         EscapeUnicode { c: self, state: EscapeUnicodeState::Backslash }
225     }
226
227     #[unstable = "pending error conventions, trait organization"]
228     fn escape_default(self) -> EscapeDefault {
229         let init_state = match self {
230             '\t' => EscapeDefaultState::Backslash('t'),
231             '\r' => EscapeDefaultState::Backslash('r'),
232             '\n' => EscapeDefaultState::Backslash('n'),
233             '\\' => EscapeDefaultState::Backslash('\\'),
234             '\'' => EscapeDefaultState::Backslash('\''),
235             '"'  => EscapeDefaultState::Backslash('"'),
236             '\x20' ... '\x7e' => EscapeDefaultState::Char(self),
237             _ => EscapeDefaultState::Unicode(self.escape_unicode())
238         };
239         EscapeDefault { state: init_state }
240     }
241
242     #[inline]
243     #[unstable = "pending trait organization"]
244     fn len_utf8(self) -> uint {
245         let code = self as u32;
246         match () {
247             _ if code < MAX_ONE_B   => 1u,
248             _ if code < MAX_TWO_B   => 2u,
249             _ if code < MAX_THREE_B => 3u,
250             _  => 4u,
251         }
252     }
253
254     #[inline]
255     #[unstable = "pending trait organization"]
256     fn len_utf16(self) -> uint {
257         let ch = self as u32;
258         if (ch & 0xFFFF_u32) == ch { 1 } else { 2 }
259     }
260
261     #[inline]
262     #[unstable = "pending error conventions, trait organization"]
263     fn encode_utf8<'a>(&self, dst: &'a mut [u8]) -> Option<uint> {
264         // Marked #[inline] to allow llvm optimizing it away
265         let code = *self as u32;
266         if code < MAX_ONE_B && dst.len() >= 1 {
267             dst[0] = code as u8;
268             Some(1)
269         } else if code < MAX_TWO_B && dst.len() >= 2 {
270             dst[0] = (code >> 6u & 0x1F_u32) as u8 | TAG_TWO_B;
271             dst[1] = (code & 0x3F_u32) as u8 | TAG_CONT;
272             Some(2)
273         } else if code < MAX_THREE_B && dst.len() >= 3  {
274             dst[0] = (code >> 12u & 0x0F_u32) as u8 | TAG_THREE_B;
275             dst[1] = (code >>  6u & 0x3F_u32) as u8 | TAG_CONT;
276             dst[2] = (code & 0x3F_u32) as u8 | TAG_CONT;
277             Some(3)
278         } else if dst.len() >= 4 {
279             dst[0] = (code >> 18u & 0x07_u32) as u8 | TAG_FOUR_B;
280             dst[1] = (code >> 12u & 0x3F_u32) as u8 | TAG_CONT;
281             dst[2] = (code >>  6u & 0x3F_u32) as u8 | TAG_CONT;
282             dst[3] = (code & 0x3F_u32) as u8 | TAG_CONT;
283             Some(4)
284         } else {
285             None
286         }
287     }
288
289     #[inline]
290     #[unstable = "pending error conventions, trait organization"]
291     fn encode_utf16(&self, dst: &mut [u16]) -> Option<uint> {
292         // Marked #[inline] to allow llvm optimizing it away
293         let mut ch = *self as u32;
294         if (ch & 0xFFFF_u32) == ch  && dst.len() >= 1 {
295             // The BMP falls through (assuming non-surrogate, as it should)
296             dst[0] = ch as u16;
297             Some(1)
298         } else if dst.len() >= 2 {
299             // Supplementary planes break into surrogates.
300             ch -= 0x1_0000_u32;
301             dst[0] = 0xD800_u16 | ((ch >> 10) as u16);
302             dst[1] = 0xDC00_u16 | ((ch as u16) & 0x3FF_u16);
303             Some(2)
304         } else {
305             None
306         }
307     }
308 }
309
310 /// An iterator over the characters that represent a `char`, as escaped by
311 /// Rust's unicode escaping rules.
312 #[derive(Clone)]
313 pub struct EscapeUnicode {
314     c: char,
315     state: EscapeUnicodeState
316 }
317
318 #[derive(Clone)]
319 enum EscapeUnicodeState {
320     Backslash,
321     Type,
322     LeftBrace,
323     Value(uint),
324     RightBrace,
325     Done,
326 }
327
328 impl Iterator for EscapeUnicode {
329     type Item = char;
330
331     fn next(&mut self) -> Option<char> {
332         match self.state {
333             EscapeUnicodeState::Backslash => {
334                 self.state = EscapeUnicodeState::Type;
335                 Some('\\')
336             }
337             EscapeUnicodeState::Type => {
338                 self.state = EscapeUnicodeState::LeftBrace;
339                 Some('u')
340             }
341             EscapeUnicodeState::LeftBrace => {
342                 let mut n = 0u;
343                 while (self.c as u32) >> (4 * (n + 1)) != 0 {
344                     n += 1;
345                 }
346                 self.state = EscapeUnicodeState::Value(n);
347                 Some('{')
348             }
349             EscapeUnicodeState::Value(offset) => {
350                 let v = match ((self.c as i32) >> (offset * 4)) & 0xf {
351                     i @ 0 ... 9 => '0' as i32 + i,
352                     i => 'a' as i32 + (i - 10)
353                 };
354                 if offset == 0 {
355                     self.state = EscapeUnicodeState::RightBrace;
356                 } else {
357                     self.state = EscapeUnicodeState::Value(offset - 1);
358                 }
359                 Some(unsafe { transmute(v) })
360             }
361             EscapeUnicodeState::RightBrace => {
362                 self.state = EscapeUnicodeState::Done;
363                 Some('}')
364             }
365             EscapeUnicodeState::Done => None,
366         }
367     }
368 }
369
370 /// An iterator over the characters that represent a `char`, escaped
371 /// for maximum portability.
372 #[derive(Clone)]
373 pub struct EscapeDefault {
374     state: EscapeDefaultState
375 }
376
377 #[derive(Clone)]
378 enum EscapeDefaultState {
379     Backslash(char),
380     Char(char),
381     Done,
382     Unicode(EscapeUnicode),
383 }
384
385 impl Iterator for EscapeDefault {
386     type Item = char;
387
388     fn next(&mut self) -> Option<char> {
389         match self.state {
390             EscapeDefaultState::Backslash(c) => {
391                 self.state = EscapeDefaultState::Char(c);
392                 Some('\\')
393             }
394             EscapeDefaultState::Char(c) => {
395                 self.state = EscapeDefaultState::Done;
396                 Some(c)
397             }
398             EscapeDefaultState::Done => None,
399             EscapeDefaultState::Unicode(ref mut iter) => iter.next()
400         }
401     }
402 }