]> git.lizzy.rs Git - rust.git/blob - library/core/src/ascii.rs
Run the tools builder on all PRs
[rust.git] / library / core / src / ascii.rs
1 //! Operations on ASCII strings and characters.
2 //!
3 //! Most string operations in Rust act on UTF-8 strings. However, at times it
4 //! makes more sense to only consider the ASCII character set for a specific
5 //! operation.
6 //!
7 //! The [`escape_default`] function provides an iterator over the bytes of an
8 //! escaped version of the character given.
9
10 #![stable(feature = "core_ascii", since = "1.26.0")]
11
12 use crate::fmt;
13 use crate::iter::FusedIterator;
14 use crate::ops::Range;
15 use crate::str::from_utf8_unchecked;
16
17 /// An iterator over the escaped version of a byte.
18 ///
19 /// This `struct` is created by the [`escape_default`] function. See its
20 /// documentation for more.
21 #[must_use = "iterators are lazy and do nothing unless consumed"]
22 #[stable(feature = "rust1", since = "1.0.0")]
23 #[derive(Clone)]
24 pub struct EscapeDefault {
25     range: Range<u8>,
26     data: [u8; 4],
27 }
28
29 /// Returns an iterator that produces an escaped version of a `u8`.
30 ///
31 /// The default is chosen with a bias toward producing literals that are
32 /// legal in a variety of languages, including C++11 and similar C-family
33 /// languages. The exact rules are:
34 ///
35 /// * Tab is escaped as `\t`.
36 /// * Carriage return is escaped as `\r`.
37 /// * Line feed is escaped as `\n`.
38 /// * Single quote is escaped as `\'`.
39 /// * Double quote is escaped as `\"`.
40 /// * Backslash is escaped as `\\`.
41 /// * Any character in the 'printable ASCII' range `0x20` .. `0x7e`
42 ///   inclusive is not escaped.
43 /// * Any other chars are given hex escapes of the form '\xNN'.
44 /// * Unicode escapes are never generated by this function.
45 ///
46 /// # Examples
47 ///
48 /// ```
49 /// use std::ascii;
50 ///
51 /// let escaped = ascii::escape_default(b'0').next().unwrap();
52 /// assert_eq!(b'0', escaped);
53 ///
54 /// let mut escaped = ascii::escape_default(b'\t');
55 ///
56 /// assert_eq!(b'\\', escaped.next().unwrap());
57 /// assert_eq!(b't', escaped.next().unwrap());
58 ///
59 /// let mut escaped = ascii::escape_default(b'\r');
60 ///
61 /// assert_eq!(b'\\', escaped.next().unwrap());
62 /// assert_eq!(b'r', escaped.next().unwrap());
63 ///
64 /// let mut escaped = ascii::escape_default(b'\n');
65 ///
66 /// assert_eq!(b'\\', escaped.next().unwrap());
67 /// assert_eq!(b'n', escaped.next().unwrap());
68 ///
69 /// let mut escaped = ascii::escape_default(b'\'');
70 ///
71 /// assert_eq!(b'\\', escaped.next().unwrap());
72 /// assert_eq!(b'\'', escaped.next().unwrap());
73 ///
74 /// let mut escaped = ascii::escape_default(b'"');
75 ///
76 /// assert_eq!(b'\\', escaped.next().unwrap());
77 /// assert_eq!(b'"', escaped.next().unwrap());
78 ///
79 /// let mut escaped = ascii::escape_default(b'\\');
80 ///
81 /// assert_eq!(b'\\', escaped.next().unwrap());
82 /// assert_eq!(b'\\', escaped.next().unwrap());
83 ///
84 /// let mut escaped = ascii::escape_default(b'\x9d');
85 ///
86 /// assert_eq!(b'\\', escaped.next().unwrap());
87 /// assert_eq!(b'x', escaped.next().unwrap());
88 /// assert_eq!(b'9', escaped.next().unwrap());
89 /// assert_eq!(b'd', escaped.next().unwrap());
90 /// ```
91 #[stable(feature = "rust1", since = "1.0.0")]
92 pub fn escape_default(c: u8) -> EscapeDefault {
93     let (data, len) = match c {
94         b'\t' => ([b'\\', b't', 0, 0], 2),
95         b'\r' => ([b'\\', b'r', 0, 0], 2),
96         b'\n' => ([b'\\', b'n', 0, 0], 2),
97         b'\\' => ([b'\\', b'\\', 0, 0], 2),
98         b'\'' => ([b'\\', b'\'', 0, 0], 2),
99         b'"' => ([b'\\', b'"', 0, 0], 2),
100         b'\x20'..=b'\x7e' => ([c, 0, 0, 0], 1),
101         _ => {
102             let hex_digits: &[u8; 16] = b"0123456789abcdef";
103             ([b'\\', b'x', hex_digits[(c >> 4) as usize], hex_digits[(c & 0xf) as usize]], 4)
104         }
105     };
106
107     return EscapeDefault { range: 0..len, data };
108 }
109
110 #[stable(feature = "rust1", since = "1.0.0")]
111 impl Iterator for EscapeDefault {
112     type Item = u8;
113
114     #[inline]
115     fn next(&mut self) -> Option<u8> {
116         self.range.next().map(|i| self.data[i as usize])
117     }
118     fn size_hint(&self) -> (usize, Option<usize>) {
119         self.range.size_hint()
120     }
121     fn last(mut self) -> Option<u8> {
122         self.next_back()
123     }
124 }
125 #[stable(feature = "rust1", since = "1.0.0")]
126 impl DoubleEndedIterator for EscapeDefault {
127     fn next_back(&mut self) -> Option<u8> {
128         self.range.next_back().map(|i| self.data[i as usize])
129     }
130 }
131 #[stable(feature = "rust1", since = "1.0.0")]
132 impl ExactSizeIterator for EscapeDefault {}
133 #[stable(feature = "fused", since = "1.26.0")]
134 impl FusedIterator for EscapeDefault {}
135
136 #[stable(feature = "ascii_escape_display", since = "1.39.0")]
137 impl fmt::Display for EscapeDefault {
138     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139         // SAFETY: ok because `escape_default` created only valid utf-8 data
140         f.write_str(unsafe {
141             from_utf8_unchecked(&self.data[(self.range.start as usize)..(self.range.end as usize)])
142         })
143     }
144 }
145
146 #[stable(feature = "std_debug", since = "1.16.0")]
147 impl fmt::Debug for EscapeDefault {
148     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149         f.debug_struct("EscapeDefault").finish_non_exhaustive()
150     }
151 }