]> git.lizzy.rs Git - rust.git/blob - src/libcore/char.rs
rollup merge of #19793: tomjakubowski/metadata-const-attrs
[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 mem::transmute;
19 use ops::FnMut;
20 use option::Option;
21 use option::Option::{None, Some};
22 use iter::{range_step, Iterator, RangeStep};
23 use slice::SlicePrelude;
24
25 // UTF-8 ranges and tags for encoding characters
26 static TAG_CONT: u8    = 0b1000_0000u8;
27 static TAG_TWO_B: u8   = 0b1100_0000u8;
28 static TAG_THREE_B: u8 = 0b1110_0000u8;
29 static TAG_FOUR_B: u8  = 0b1111_0000u8;
30 static MAX_ONE_B: u32   =     0x80u32;
31 static MAX_TWO_B: u32   =    0x800u32;
32 static MAX_THREE_B: u32 =  0x10000u32;
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]
69 pub const MAX: char = '\u{10ffff}';
70
71 /// Converts from `u32` to a `char`
72 #[inline]
73 #[unstable = "pending decisions about costructors for primitives"]
74 pub fn from_u32(i: u32) -> Option<char> {
75     // catch out-of-bounds and surrogates
76     if (i > MAX as u32) || (i >= 0xD800 && i <= 0xDFFF) {
77         None
78     } else {
79         Some(unsafe { transmute(i) })
80     }
81 }
82
83 ///
84 /// Checks if a `char` parses as a numeric digit in the given radix
85 ///
86 /// Compared to `is_numeric()`, this function only recognizes the
87 /// characters `0-9`, `a-z` and `A-Z`.
88 ///
89 /// # Return value
90 ///
91 /// Returns `true` if `c` is a valid digit under `radix`, and `false`
92 /// otherwise.
93 ///
94 /// # Panics
95 ///
96 /// Panics if given a `radix` > 36.
97 ///
98 /// # Note
99 ///
100 /// This just wraps `to_digit()`.
101 ///
102 #[inline]
103 #[deprecated = "use the Char::is_digit method"]
104 pub fn is_digit_radix(c: char, radix: uint) -> bool {
105     c.is_digit(radix)
106 }
107
108 ///
109 /// Converts a `char` to the corresponding digit
110 ///
111 /// # Return value
112 ///
113 /// If `c` is between '0' and '9', the corresponding value
114 /// between 0 and 9. If `c` is 'a' or 'A', 10. If `c` is
115 /// 'b' or 'B', 11, etc. Returns none if the `char` does not
116 /// refer to a digit in the given radix.
117 ///
118 /// # Panics
119 ///
120 /// Panics if given a `radix` outside the range `[0..36]`.
121 ///
122 #[inline]
123 #[deprecated = "use the Char::to_digit method"]
124 pub fn to_digit(c: char, radix: uint) -> Option<uint> {
125     c.to_digit(radix)
126 }
127
128 ///
129 /// Converts a number to the character representing it
130 ///
131 /// # Return value
132 ///
133 /// Returns `Some(char)` if `num` represents one digit under `radix`,
134 /// using one character of `0-9` or `a-z`, or `None` if it doesn't.
135 ///
136 /// # Panics
137 ///
138 /// Panics if given an `radix` > 36.
139 ///
140 #[inline]
141 #[unstable = "pending decisions about costructors for primitives"]
142 pub fn from_digit(num: uint, radix: uint) -> Option<char> {
143     if radix > 36 {
144         panic!("from_digit: radix is to high (maximum 36)");
145     }
146     if num < radix {
147         unsafe {
148             if num < 10 {
149                 Some(transmute(('0' as uint + num) as u32))
150             } else {
151                 Some(transmute(('a' as uint + num - 10u) as u32))
152             }
153         }
154     } else {
155         None
156     }
157 }
158
159 ///
160 /// Returns the hexadecimal Unicode escape of a `char`
161 ///
162 /// The rules are as follows:
163 ///
164 /// - chars in [0,0xff] get 2-digit escapes: `\\xNN`
165 /// - chars in [0x100,0xffff] get 4-digit escapes: `\\u{NNNN}`
166 /// - chars above 0x10000 get 8-digit escapes: `\\u{{NNN}NNNNN}`
167 ///
168 #[deprecated = "use the Char::escape_unicode method"]
169 pub fn escape_unicode<F>(c: char, mut f: F) where F: FnMut(char) {
170     for char in c.escape_unicode() {
171         f(char);
172     }
173 }
174
175 ///
176 /// Returns a 'default' ASCII and C++11-like literal escape of a `char`
177 ///
178 /// The default is chosen with a bias toward producing literals that are
179 /// legal in a variety of languages, including C++11 and similar C-family
180 /// languages. The exact rules are:
181 ///
182 /// - Tab, CR and LF are escaped as '\t', '\r' and '\n' respectively.
183 /// - Single-quote, double-quote and backslash chars are backslash-escaped.
184 /// - Any other chars in the range [0x20,0x7e] are not escaped.
185 /// - Any other chars are given hex Unicode escapes; see `escape_unicode`.
186 ///
187 #[deprecated = "use the Char::escape_default method"]
188 pub fn escape_default<F>(c: char, mut f: F) where F: FnMut(char) {
189     for c in c.escape_default() {
190         f(c);
191     }
192 }
193
194 /// Returns the amount of bytes this `char` would need if encoded in UTF-8
195 #[inline]
196 #[deprecated = "use the Char::len_utf8 method"]
197 pub fn len_utf8_bytes(c: char) -> uint {
198     c.len_utf8()
199 }
200
201 /// Basic `char` manipulations.
202 #[experimental = "trait organization may change"]
203 pub trait Char {
204     /// Checks if a `char` parses as a numeric digit in the given radix.
205     ///
206     /// Compared to `is_numeric()`, this function only recognizes the characters
207     /// `0-9`, `a-z` and `A-Z`.
208     ///
209     /// # Return value
210     ///
211     /// Returns `true` if `c` is a valid digit under `radix`, and `false`
212     /// otherwise.
213     ///
214     /// # Panics
215     ///
216     /// Panics if given a radix > 36.
217     #[deprecated = "use is_digit"]
218     fn is_digit_radix(self, radix: uint) -> bool;
219
220     /// Checks if a `char` parses as a numeric digit in the given radix.
221     ///
222     /// Compared to `is_numeric()`, this function only recognizes the characters
223     /// `0-9`, `a-z` and `A-Z`.
224     ///
225     /// # Return value
226     ///
227     /// Returns `true` if `c` is a valid digit under `radix`, and `false`
228     /// otherwise.
229     ///
230     /// # Panics
231     ///
232     /// Panics if given a radix > 36.
233     #[unstable = "pending error conventions"]
234     fn is_digit(self, radix: uint) -> bool;
235
236     /// Converts a character to the corresponding digit.
237     ///
238     /// # Return value
239     ///
240     /// If `c` is between '0' and '9', the corresponding value between 0 and
241     /// 9. If `c` is 'a' or 'A', 10. If `c` is 'b' or 'B', 11, etc. Returns
242     /// none if the character does not refer to a digit in the given radix.
243     ///
244     /// # Panics
245     ///
246     /// Panics if given a radix outside the range [0..36].
247     #[unstable = "pending error conventions, trait organization"]
248     fn to_digit(self, radix: uint) -> Option<uint>;
249
250     /// Converts a number to the character representing it.
251     ///
252     /// # Return value
253     ///
254     /// Returns `Some(char)` if `num` represents one digit under `radix`,
255     /// using one character of `0-9` or `a-z`, or `None` if it doesn't.
256     ///
257     /// # Panics
258     ///
259     /// Panics if given a radix > 36.
260     #[deprecated = "use the char::from_digit free function"]
261     fn from_digit(num: uint, radix: uint) -> Option<Self>;
262
263     /// Converts from `u32` to a `char`
264     #[deprecated = "use the char::from_u32 free function"]
265     fn from_u32(i: u32) -> Option<char>;
266
267     /// Returns an iterator that yields the hexadecimal Unicode escape
268     /// of a character, as `char`s.
269     ///
270     /// The rules are as follows:
271     ///
272     /// * Characters in [0,0xff] get 2-digit escapes: `\\xNN`
273     /// * Characters in [0x100,0xffff] get 4-digit escapes: `\\u{NNNN}`.
274     /// * Characters above 0x10000 get 8-digit escapes: `\\u{{NNN}NNNNN}`.
275     #[unstable = "pending error conventions, trait organization"]
276     fn escape_unicode(self) -> UnicodeEscapedChars;
277
278     /// Returns an iterator that yields the 'default' ASCII and
279     /// C++11-like literal escape of a character, as `char`s.
280     ///
281     /// The default is chosen with a bias toward producing literals that are
282     /// legal in a variety of languages, including C++11 and similar C-family
283     /// languages. The exact rules are:
284     ///
285     /// * Tab, CR and LF are escaped as '\t', '\r' and '\n' respectively.
286     /// * Single-quote, double-quote and backslash chars are backslash-
287     ///   escaped.
288     /// * Any other chars in the range [0x20,0x7e] are not escaped.
289     /// * Any other chars are given hex Unicode escapes; see `escape_unicode`.
290     #[unstable = "pending error conventions, trait organization"]
291     fn escape_default(self) -> DefaultEscapedChars;
292
293     /// Returns the amount of bytes this character would need if encoded in
294     /// UTF-8.
295     #[deprecated = "use len_utf8"]
296     fn len_utf8_bytes(self) -> uint;
297
298     /// Returns the amount of bytes this character would need if encoded in
299     /// UTF-8.
300     #[unstable = "pending trait organization"]
301     fn len_utf8(self) -> uint;
302
303     /// Returns the amount of bytes this character would need if encoded in
304     /// UTF-16.
305     #[unstable = "pending trait organization"]
306     fn len_utf16(self) -> uint;
307
308     /// Encodes this character as UTF-8 into the provided byte buffer,
309     /// and then returns the number of bytes written.
310     ///
311     /// If the buffer is not large enough, nothing will be written into it
312     /// and a `None` will be returned.
313     #[unstable = "pending trait organization"]
314     fn encode_utf8(&self, dst: &mut [u8]) -> Option<uint>;
315
316     /// Encodes this character as UTF-16 into the provided `u16` buffer,
317     /// and then returns the number of `u16`s written.
318     ///
319     /// If the buffer is not large enough, nothing will be written into it
320     /// and a `None` will be returned.
321     #[unstable = "pending trait organization"]
322     fn encode_utf16(&self, dst: &mut [u16]) -> Option<uint>;
323 }
324
325 #[experimental = "trait is experimental"]
326 impl Char for char {
327     #[deprecated = "use is_digit"]
328     fn is_digit_radix(self, radix: uint) -> bool { self.is_digit(radix) }
329
330     #[unstable = "pending trait organization"]
331     fn is_digit(self, radix: uint) -> bool {
332         match self.to_digit(radix) {
333             Some(_) => true,
334             None    => false,
335         }
336     }
337
338     #[unstable = "pending trait organization"]
339     fn to_digit(self, radix: uint) -> Option<uint> {
340         if radix > 36 {
341             panic!("to_digit: radix is too high (maximum 36)");
342         }
343         let val = match self {
344           '0' ... '9' => self as uint - ('0' as uint),
345           'a' ... 'z' => self as uint + 10u - ('a' as uint),
346           'A' ... 'Z' => self as uint + 10u - ('A' as uint),
347           _ => return None,
348         };
349         if val < radix { Some(val) }
350         else { None }
351     }
352
353     #[deprecated = "use the char::from_digit free function"]
354     fn from_digit(num: uint, radix: uint) -> Option<char> { from_digit(num, radix) }
355
356     #[inline]
357     #[deprecated = "use the char::from_u32 free function"]
358     fn from_u32(i: u32) -> Option<char> { from_u32(i) }
359
360     #[unstable = "pending error conventions, trait organization"]
361     fn escape_unicode(self) -> UnicodeEscapedChars {
362         UnicodeEscapedChars { c: self, state: UnicodeEscapedCharsState::Backslash }
363     }
364
365     #[unstable = "pending error conventions, trait organization"]
366     fn escape_default(self) -> DefaultEscapedChars {
367         let init_state = match self {
368             '\t' => DefaultEscapedCharsState::Backslash('t'),
369             '\r' => DefaultEscapedCharsState::Backslash('r'),
370             '\n' => DefaultEscapedCharsState::Backslash('n'),
371             '\\' => DefaultEscapedCharsState::Backslash('\\'),
372             '\'' => DefaultEscapedCharsState::Backslash('\''),
373             '"'  => DefaultEscapedCharsState::Backslash('"'),
374             '\x20' ... '\x7e' => DefaultEscapedCharsState::Char(self),
375             _ => DefaultEscapedCharsState::Unicode(self.escape_unicode())
376         };
377         DefaultEscapedChars { state: init_state }
378     }
379
380     #[inline]
381     #[deprecated = "use len_utf8"]
382     fn len_utf8_bytes(self) -> uint { self.len_utf8() }
383
384     #[inline]
385     #[unstable = "pending trait organization"]
386     fn len_utf8(self) -> uint {
387         let code = self as u32;
388         match () {
389             _ if code < MAX_ONE_B   => 1u,
390             _ if code < MAX_TWO_B   => 2u,
391             _ if code < MAX_THREE_B => 3u,
392             _  => 4u,
393         }
394     }
395
396     #[inline]
397     #[unstable = "pending trait organization"]
398     fn len_utf16(self) -> uint {
399         let ch = self as u32;
400         if (ch & 0xFFFF_u32) == ch { 1 } else { 2 }
401     }
402
403     #[inline]
404     #[unstable = "pending error conventions, trait organization"]
405     fn encode_utf8<'a>(&self, dst: &'a mut [u8]) -> Option<uint> {
406         // Marked #[inline] to allow llvm optimizing it away
407         let code = *self as u32;
408         if code < MAX_ONE_B && dst.len() >= 1 {
409             dst[0] = code as u8;
410             Some(1)
411         } else if code < MAX_TWO_B && dst.len() >= 2 {
412             dst[0] = (code >> 6u & 0x1F_u32) as u8 | TAG_TWO_B;
413             dst[1] = (code & 0x3F_u32) as u8 | TAG_CONT;
414             Some(2)
415         } else if code < MAX_THREE_B && dst.len() >= 3  {
416             dst[0] = (code >> 12u & 0x0F_u32) as u8 | TAG_THREE_B;
417             dst[1] = (code >>  6u & 0x3F_u32) as u8 | TAG_CONT;
418             dst[2] = (code & 0x3F_u32) as u8 | TAG_CONT;
419             Some(3)
420         } else if dst.len() >= 4 {
421             dst[0] = (code >> 18u & 0x07_u32) as u8 | TAG_FOUR_B;
422             dst[1] = (code >> 12u & 0x3F_u32) as u8 | TAG_CONT;
423             dst[2] = (code >>  6u & 0x3F_u32) as u8 | TAG_CONT;
424             dst[3] = (code & 0x3F_u32) as u8 | TAG_CONT;
425             Some(4)
426         } else {
427             None
428         }
429     }
430
431     #[inline]
432     #[unstable = "pending error conventions, trait organization"]
433     fn encode_utf16(&self, dst: &mut [u16]) -> Option<uint> {
434         // Marked #[inline] to allow llvm optimizing it away
435         let mut ch = *self as u32;
436         if (ch & 0xFFFF_u32) == ch  && dst.len() >= 1 {
437             // The BMP falls through (assuming non-surrogate, as it should)
438             dst[0] = ch as u16;
439             Some(1)
440         } else if dst.len() >= 2 {
441             // Supplementary planes break into surrogates.
442             ch -= 0x1_0000_u32;
443             dst[0] = 0xD800_u16 | ((ch >> 10) as u16);
444             dst[1] = 0xDC00_u16 | ((ch as u16) & 0x3FF_u16);
445             Some(2)
446         } else {
447             None
448         }
449     }
450 }
451
452 /// An iterator over the characters that represent a `char`, as escaped by
453 /// Rust's unicode escaping rules.
454 pub struct UnicodeEscapedChars {
455     c: char,
456     state: UnicodeEscapedCharsState
457 }
458
459 enum UnicodeEscapedCharsState {
460     Backslash,
461     Type,
462     Value(RangeStep<i32>),
463 }
464
465 impl Iterator<char> for UnicodeEscapedChars {
466     fn next(&mut self) -> Option<char> {
467         match self.state {
468             UnicodeEscapedCharsState::Backslash => {
469                 self.state = UnicodeEscapedCharsState::Type;
470                 Some('\\')
471             }
472             UnicodeEscapedCharsState::Type => {
473                 let (typechar, pad) = if self.c <= '\x7f' { ('x', 2) }
474                                       else if self.c <= '\u{ffff}' { ('u', 4) }
475                                       else { ('U', 8) };
476                 self.state = UnicodeEscapedCharsState::Value(range_step(4 * (pad - 1), -1, -4i32));
477                 Some(typechar)
478             }
479             UnicodeEscapedCharsState::Value(ref mut range_step) => match range_step.next() {
480                 Some(offset) => {
481                     let offset = offset as uint;
482                     let v = match ((self.c as i32) >> offset) & 0xf {
483                         i @ 0 ... 9 => '0' as i32 + i,
484                         i => 'a' as i32 + (i - 10)
485                     };
486                     Some(unsafe { transmute(v) })
487                 }
488                 None => None
489             }
490         }
491     }
492 }
493
494 /// An iterator over the characters that represent a `char`, escaped
495 /// for maximum portability.
496 pub struct DefaultEscapedChars {
497     state: DefaultEscapedCharsState
498 }
499
500 enum DefaultEscapedCharsState {
501     Backslash(char),
502     Char(char),
503     Done,
504     Unicode(UnicodeEscapedChars),
505 }
506
507 impl Iterator<char> for DefaultEscapedChars {
508     fn next(&mut self) -> Option<char> {
509         match self.state {
510             DefaultEscapedCharsState::Backslash(c) => {
511                 self.state = DefaultEscapedCharsState::Char(c);
512                 Some('\\')
513             }
514             DefaultEscapedCharsState::Char(c) => {
515                 self.state = DefaultEscapedCharsState::Done;
516                 Some(c)
517             }
518             DefaultEscapedCharsState::Done => None,
519             DefaultEscapedCharsState::Unicode(ref mut iter) => iter.next()
520         }
521     }
522 }
523