authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-10-15 14:28:33-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-10-15 14:28:33-04:00
logc0d7f6403665a8b81b0afebf08d1741cca1daef9
tree6918b3309d02cddf49c09590afba8017869ec929
parentf9192adaba0eb344ed12aad9c675cd73b740d2a2
parent4ea3a9ba9f867a74f3f6c8908c3ebba0876771f1
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12448 from r00ster91/ultimateascii

std.ascii: rename functions and other improvements

1 files changed, 210 insertions(+), 31 deletions(-)

lib/std/ascii.zig+210-31
...@@ -1,54 +1,164 @@...@@ -1,54 +1,164 @@
1// Does NOT look at the locale the way C89's toupper(3), isspace() et cetera does.1//! The 7-bit [ASCII](https://en.wikipedia.org/wiki/ASCII) character encoding standard.
2// I could have taken only a u7 to make this clear, but it would be slower2//!
3// It is my opinion that encodings other than UTF-8 should not be supported.3//! This is not to be confused with the 8-bit [extended ASCII](https://en.wikipedia.org/wiki/Extended_ASCII) character encoding.
4//4//!
5// (and 128 bytes is not much to pay).5//! Even though this module concerns itself with 7-bit ASCII,
6// Also does not handle Unicode character classes.6//! functions use `u8` as the type instead of `u7` for convenience and compatibility.
7//7//! Characters outside of the 7-bit range are gracefully handled (e.g. by returning `false`).
8// https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/USASCII_code_chart.png/1200px-USASCII_code_chart.png8//!
9//! See also: https://en.wikipedia.org/wiki/ASCII#Character_set
910
10const std = @import("std");11const std = @import("std");
1112
12/// Contains constants for the C0 control codes of the ASCII encoding.13// TODO: remove all decls marked as DEPRECATED after 0.10.0's release
13/// https://en.wikipedia.org/wiki/C0_and_C1_control_codes14
15/// The C0 control codes of the ASCII encoding.
16///
17/// See also: https://en.wikipedia.org/wiki/C0_and_C1_control_codes and `isControl`.
14pub const control_code = struct {18pub const control_code = struct {
19 // DEPRECATED: use the lowercase variant
15 pub const NUL = 0x00;20 pub const NUL = 0x00;
21 // DEPRECATED: use the lowercase variant
16 pub const SOH = 0x01;22 pub const SOH = 0x01;
23 // DEPRECATED: use the lowercase variant
17 pub const STX = 0x02;24 pub const STX = 0x02;
25 // DEPRECATED: use the lowercase variant
18 pub const ETX = 0x03;26 pub const ETX = 0x03;
27 // DEPRECATED: use the lowercase variant
19 pub const EOT = 0x04;28 pub const EOT = 0x04;
29 // DEPRECATED: use the lowercase variant
20 pub const ENQ = 0x05;30 pub const ENQ = 0x05;
31 // DEPRECATED: use the lowercase variant
21 pub const ACK = 0x06;32 pub const ACK = 0x06;
33 // DEPRECATED: use the lowercase variant
22 pub const BEL = 0x07;34 pub const BEL = 0x07;
35 // DEPRECATED: use the lowercase variant
23 pub const BS = 0x08;36 pub const BS = 0x08;
37 // DEPRECATED: use `ht`
24 pub const TAB = 0x09;38 pub const TAB = 0x09;
39 // DEPRECATED: use the lowercase variant
25 pub const LF = 0x0A;40 pub const LF = 0x0A;
41 // DEPRECATED: use the lowercase variant
26 pub const VT = 0x0B;42 pub const VT = 0x0B;
43 // DEPRECATED: use the lowercase variant
27 pub const FF = 0x0C;44 pub const FF = 0x0C;
45 // DEPRECATED: use the lowercase variant
28 pub const CR = 0x0D;46 pub const CR = 0x0D;
47 // DEPRECATED: use the lowercase variant
29 pub const SO = 0x0E;48 pub const SO = 0x0E;
49 // DEPRECATED: use the lowercase variant
30 pub const SI = 0x0F;50 pub const SI = 0x0F;
51 // DEPRECATED: use the lowercase variant
31 pub const DLE = 0x10;52 pub const DLE = 0x10;
53 // DEPRECATED: use the lowercase variant
32 pub const DC1 = 0x11;54 pub const DC1 = 0x11;
55 // DEPRECATED: use the lowercase variant
33 pub const DC2 = 0x12;56 pub const DC2 = 0x12;
57 // DEPRECATED: use the lowercase variant
34 pub const DC3 = 0x13;58 pub const DC3 = 0x13;
59 // DEPRECATED: use the lowercase variant
35 pub const DC4 = 0x14;60 pub const DC4 = 0x14;
61 // DEPRECATED: use the lowercase variant
36 pub const NAK = 0x15;62 pub const NAK = 0x15;
63 // DEPRECATED: use the lowercase variant
37 pub const SYN = 0x16;64 pub const SYN = 0x16;
65 // DEPRECATED: use the lowercase variant
38 pub const ETB = 0x17;66 pub const ETB = 0x17;
67 // DEPRECATED: use the lowercase variant
39 pub const CAN = 0x18;68 pub const CAN = 0x18;
69 // DEPRECATED: use the lowercase variant
40 pub const EM = 0x19;70 pub const EM = 0x19;
71 // DEPRECATED: use the lowercase variant
41 pub const SUB = 0x1A;72 pub const SUB = 0x1A;
73 // DEPRECATED: use the lowercase variant
42 pub const ESC = 0x1B;74 pub const ESC = 0x1B;
75 // DEPRECATED: use the lowercase variant
43 pub const FS = 0x1C;76 pub const FS = 0x1C;
77 // DEPRECATED: use the lowercase variant
44 pub const GS = 0x1D;78 pub const GS = 0x1D;
79 // DEPRECATED: use the lowercase variant
45 pub const RS = 0x1E;80 pub const RS = 0x1E;
81 // DEPRECATED: use the lowercase variant
46 pub const US = 0x1F;82 pub const US = 0x1F;
4783 // DEPRECATED: use the lowercase variant
48 pub const DEL = 0x7F;84 pub const DEL = 0x7F;
4985 // DEPRECATED: use the lowercase variant
50 pub const XON = 0x11;86 pub const XON = 0x11;
87 // DEPRECATED: use the lowercase variant
51 pub const XOFF = 0x13;88 pub const XOFF = 0x13;
89
90 /// Null.
91 pub const nul = 0x00;
92 /// Start of Heading.
93 pub const soh = 0x01;
94 /// Start of Text.
95 pub const stx = 0x02;
96 /// End of Text.
97 pub const etx = 0x03;
98 /// End of Transmission.
99 pub const eot = 0x04;
100 /// Enquiry.
101 pub const enq = 0x05;
102 /// Acknowledge.
103 pub const ack = 0x06;
104 /// Bell, Alert.
105 pub const bel = 0x07;
106 /// Backspace.
107 pub const bs = 0x08;
108 /// Horizontal Tab, Tab ('\t').
109 pub const ht = 0x09;
110 /// Line Feed, Newline ('\n').
111 pub const lf = 0x0A;
112 /// Vertical Tab.
113 pub const vt = 0x0B;
114 /// Form Feed.
115 pub const ff = 0x0C;
116 /// Carriage Return ('\r').
117 pub const cr = 0x0D;
118 /// Shift Out.
119 pub const so = 0x0E;
120 /// Shift In.
121 pub const si = 0x0F;
122 /// Data Link Escape.
123 pub const dle = 0x10;
124 /// Device Control One (XON).
125 pub const dc1 = 0x11;
126 /// Device Control Two.
127 pub const dc2 = 0x12;
128 /// Device Control Three (XOFF).
129 pub const dc3 = 0x13;
130 /// Device Control Four.
131 pub const dc4 = 0x14;
132 /// Negative Acknowledge.
133 pub const nak = 0x15;
134 /// Synchronous Idle.
135 pub const syn = 0x16;
136 /// End of Transmission Block
137 pub const etb = 0x17;
138 /// Cancel.
139 pub const can = 0x18;
140 /// End of Medium.
141 pub const em = 0x19;
142 /// Substitute.
143 pub const sub = 0x1A;
144 /// Escape.
145 pub const esc = 0x1B;
146 /// File Separator.
147 pub const fs = 0x1C;
148 /// Group Separator.
149 pub const gs = 0x1D;
150 /// Record Separator.
151 pub const rs = 0x1E;
152 /// Unit Separator.
153 pub const us = 0x1F;
154
155 /// Delete.
156 pub const del = 0x7F;
157
158 /// An alias to `dc1`.
159 pub const xon = dc1;
160 /// An alias to `dc3`.
161 pub const xoff = dc3;
52};162};
53163
54const tIndex = enum(u3) {164const tIndex = enum(u3) {
...@@ -188,73 +298,106 @@ fn inTable(c: u8, t: tIndex) bool {...@@ -188,73 +298,106 @@ fn inTable(c: u8, t: tIndex) bool {
188 return (combinedTable[c] & (@as(u8, 1) << @enumToInt(t))) != 0;298 return (combinedTable[c] & (@as(u8, 1) << @enumToInt(t))) != 0;
189}299}
190300
191pub fn isAlNum(c: u8) bool {301/// DEPRECATED: use `isAlphanumeric`
302pub const isAlNum = isAlphanumeric;
303/// DEPRECATED: use `isAlpha`
304pub const isAlpha = isAlphabetic;
305/// DEPRECATED: use `isAlpha`
306pub const isCntrl = isControl;
307/// DEPRECATED: use `isWhitespace`.
308pub const isSpace = isWhitespace;
309/// DEPRECATED: use `whitespace`.
310pub const spaces = whitespace;
311/// DEPRECATED: use `isHex`.
312pub const isXDigit = isHex;
313
314/// Returns whether the character is alphanumeric.
315pub fn isAlphanumeric(c: u8) bool {
192 return (combinedTable[c] & ((@as(u8, 1) << @enumToInt(tIndex.Alpha)) |316 return (combinedTable[c] & ((@as(u8, 1) << @enumToInt(tIndex.Alpha)) |
193 @as(u8, 1) << @enumToInt(tIndex.Digit))) != 0;317 @as(u8, 1) << @enumToInt(tIndex.Digit))) != 0;
194}318}
195319
196pub fn isAlpha(c: u8) bool {320/// Returns whether the character is alphabetic.
321pub fn isAlphabetic(c: u8) bool {
197 return inTable(c, tIndex.Alpha);322 return inTable(c, tIndex.Alpha);
198}323}
199324
200pub fn isCntrl(c: u8) bool {325/// Returns whether the character is a control character.
201 return c < 0x20 or c == 127; //DEL326/// This is the same as `!isPrint(c)`.
327///
328/// See also: `control_code`.
329pub fn isControl(c: u8) bool {
330 return c <= control_code.us or c == control_code.del;
202}331}
203332
333/// Returns whether the character is a digit.
204pub fn isDigit(c: u8) bool {334pub fn isDigit(c: u8) bool {
205 return inTable(c, tIndex.Digit);335 return inTable(c, tIndex.Digit);
206}336}
207337
338/// DEPRECATED: use `isPrint(c) and c != ' '` instead
208pub fn isGraph(c: u8) bool {339pub fn isGraph(c: u8) bool {
209 return inTable(c, tIndex.Graph);340 return inTable(c, tIndex.Graph);
210}341}
211342
343/// Returns whether the character is a lowercased letter.
212pub fn isLower(c: u8) bool {344pub fn isLower(c: u8) bool {
213 return inTable(c, tIndex.Lower);345 return inTable(c, tIndex.Lower);
214}346}
215347
348/// Returns whether the character has some graphical representation and can be printed.
349/// This also returns `true` for the space character.
350/// This is the same as `!isControl(c)`.
216pub fn isPrint(c: u8) bool {351pub fn isPrint(c: u8) bool {
217 return inTable(c, tIndex.Graph) or c == ' ';352 return inTable(c, tIndex.Graph) or c == ' ';
218}353}
219354
355/// DEPRECATED: create your own function based on your needs and what you want to do.
220pub fn isPunct(c: u8) bool {356pub fn isPunct(c: u8) bool {
221 return inTable(c, tIndex.Punct);357 return inTable(c, tIndex.Punct);
222}358}
223359
224pub fn isSpace(c: u8) bool {360/// Returns whether this character is included in `whitespace`.
361pub fn isWhitespace(c: u8) bool {
225 return inTable(c, tIndex.Space);362 return inTable(c, tIndex.Space);
226}363}
227364
228/// All the values for which isSpace() returns true. This may be used with365/// Whitespace for general use.
229/// e.g. std.mem.trim() to trim whiteSpace.366/// This may be used with e.g. `std.mem.trim` to trim whitespace.
230pub const spaces = [_]u8{ ' ', '\t', '\n', '\r', control_code.VT, control_code.FF };367///
368/// See also: `isWhitespace`.
369pub const whitespace = [_]u8{ ' ', '\t', '\n', '\r', control_code.vt, control_code.ff };
231370
232test "spaces" {371test "whitespace" {
233 const testing = std.testing;372 for (whitespace) |char| try std.testing.expect(isWhitespace(char));
234 for (spaces) |space| try testing.expect(isSpace(space));
235373
236 var i: u8 = 0;374 var i: u8 = 0;
237 while (isASCII(i)) : (i += 1) {375 while (isASCII(i)) : (i += 1) {
238 if (isSpace(i)) try testing.expect(std.mem.indexOfScalar(u8, &spaces, i) != null);376 if (isWhitespace(i)) try std.testing.expect(std.mem.indexOfScalar(u8, &whitespace, i) != null);
239 }377 }
240}378}
241379
380/// Returns whether the character is an uppercased letter.
242pub fn isUpper(c: u8) bool {381pub fn isUpper(c: u8) bool {
243 return inTable(c, tIndex.Upper);382 return inTable(c, tIndex.Upper);
244}383}
245384
246pub fn isXDigit(c: u8) bool {385/// Returns whether the character is a hexadecimal digit. This is case-insensitive.
386pub fn isHex(c: u8) bool {
247 return inTable(c, tIndex.Hex);387 return inTable(c, tIndex.Hex);
248}388}
249389
390/// Returns whether the character is a 7-bit ASCII character.
250pub fn isASCII(c: u8) bool {391pub fn isASCII(c: u8) bool {
251 return c < 128;392 return c < 128;
252}393}
253394
395/// DEPRECATED: use `c == ' ' or c == '\t'` or try `isWhitespace`
254pub fn isBlank(c: u8) bool {396pub fn isBlank(c: u8) bool {
255 return (c == ' ') or (c == '\x09');397 return (c == ' ') or (c == '\x09');
256}398}
257399
400/// Uppercases the character and returns it as-is if it's already uppercased or not a letter.
258pub fn toUpper(c: u8) u8 {401pub fn toUpper(c: u8) u8 {
259 if (isLower(c)) {402 if (isLower(c)) {
260 return c & 0b11011111;403 return c & 0b11011111;
...@@ -263,6 +406,7 @@ pub fn toUpper(c: u8) u8 {...@@ -263,6 +406,7 @@ pub fn toUpper(c: u8) u8 {
263 }406 }
264}407}
265408
409/// Lowercases the character and returns it as-is if it's already lowercased or not a letter.
266pub fn toLower(c: u8) u8 {410pub fn toLower(c: u8) u8 {
267 if (isUpper(c)) {411 if (isUpper(c)) {
268 return c | 0b00100000;412 return c | 0b00100000;
...@@ -274,13 +418,50 @@ pub fn toLower(c: u8) u8 {...@@ -274,13 +418,50 @@ pub fn toLower(c: u8) u8 {
274test "ascii character classes" {418test "ascii character classes" {
275 const testing = std.testing;419 const testing = std.testing;
276420
421 try testing.expect(!isControl('a'));
422 try testing.expect(!isControl('z'));
423 try testing.expect(isControl(control_code.nul));
424 try testing.expect(isControl(control_code.ff));
425 try testing.expect(isControl(control_code.us));
426
277 try testing.expect('C' == toUpper('c'));427 try testing.expect('C' == toUpper('c'));
278 try testing.expect(':' == toUpper(':'));428 try testing.expect(':' == toUpper(':'));
279 try testing.expect('\xab' == toUpper('\xab'));429 try testing.expect('\xab' == toUpper('\xab'));
430 try testing.expect(!isUpper('z'));
431
280 try testing.expect('c' == toLower('C'));432 try testing.expect('c' == toLower('C'));
433 try testing.expect(':' == toLower(':'));
434 try testing.expect('\xab' == toLower('\xab'));
435 try testing.expect(!isLower('Z'));
436
437 try testing.expect(isAlphanumeric('Z'));
438 try testing.expect(isAlphanumeric('z'));
439 try testing.expect(isAlphanumeric('5'));
440 try testing.expect(isAlphanumeric('5'));
441 try testing.expect(!isAlphanumeric('!'));
442
443 try testing.expect(!isAlpha('5'));
281 try testing.expect(isAlpha('c'));444 try testing.expect(isAlpha('c'));
282 try testing.expect(!isAlpha('5'));445 try testing.expect(!isAlpha('5'));
283 try testing.expect(isSpace(' '));446
447 try testing.expect(isWhitespace(' '));
448 try testing.expect(isWhitespace('\t'));
449 try testing.expect(isWhitespace('\r'));
450 try testing.expect(isWhitespace('\n'));
451 try testing.expect(!isWhitespace('.'));
452
453 try testing.expect(!isHex('g'));
454 try testing.expect(isHex('b'));
455 try testing.expect(isHex('9'));
456
457 try testing.expect(!isDigit('~'));
458 try testing.expect(isDigit('0'));
459 try testing.expect(isDigit('9'));
460
461 try testing.expect(isPrint(' '));
462 try testing.expect(isPrint('@'));
463 try testing.expect(isPrint('~'));
464 try testing.expect(!isPrint(control_code.esc));
284}465}
285466
286/// Writes a lower case copy of `ascii_string` to `output`.467/// Writes a lower case copy of `ascii_string` to `output`.
...@@ -341,7 +522,7 @@ test "allocUpperString" {...@@ -341,7 +522,7 @@ test "allocUpperString" {
341 try std.testing.expectEqualStrings("ABCDEFGHIJKLMNOPQRST0234+💩!", result);522 try std.testing.expectEqualStrings("ABCDEFGHIJKLMNOPQRST0234+💩!", result);
342}523}
343524
344/// Compares strings `a` and `b` case insensitively and returns whether they are equal.525/// Compares strings `a` and `b` case-insensitively and returns whether they are equal.
345pub fn eqlIgnoreCase(a: []const u8, b: []const u8) bool {526pub fn eqlIgnoreCase(a: []const u8, b: []const u8) bool {
346 if (a.len != b.len) return false;527 if (a.len != b.len) return false;
347 for (a) |a_c, i| {528 for (a) |a_c, i| {
...@@ -397,11 +578,10 @@ test "indexOfIgnoreCase" {...@@ -397,11 +578,10 @@ test "indexOfIgnoreCase" {
397 try std.testing.expect(indexOfIgnoreCase("one two three FouR", "gOur") == null);578 try std.testing.expect(indexOfIgnoreCase("one two three FouR", "gOur") == null);
398 try std.testing.expect(indexOfIgnoreCase("foO", "Foo").? == 0);579 try std.testing.expect(indexOfIgnoreCase("foO", "Foo").? == 0);
399 try std.testing.expect(indexOfIgnoreCase("foo", "fool") == null);580 try std.testing.expect(indexOfIgnoreCase("foo", "fool") == null);
400
401 try std.testing.expect(indexOfIgnoreCase("FOO foo", "fOo").? == 0);581 try std.testing.expect(indexOfIgnoreCase("FOO foo", "fOo").? == 0);
402}582}
403583
404/// Compares two slices of numbers lexicographically. O(n).584/// Returns the lexicographical order of two slices. O(n).
405pub fn orderIgnoreCase(lhs: []const u8, rhs: []const u8) std.math.Order {585pub fn orderIgnoreCase(lhs: []const u8, rhs: []const u8) std.math.Order {
406 const n = std.math.min(lhs.len, rhs.len);586 const n = std.math.min(lhs.len, rhs.len);
407 var i: usize = 0;587 var i: usize = 0;
...@@ -415,8 +595,7 @@ pub fn orderIgnoreCase(lhs: []const u8, rhs: []const u8) std.math.Order {...@@ -415,8 +595,7 @@ pub fn orderIgnoreCase(lhs: []const u8, rhs: []const u8) std.math.Order {
415 return std.math.order(lhs.len, rhs.len);595 return std.math.order(lhs.len, rhs.len);
416}596}
417597
418/// Returns true if lhs < rhs, false otherwise598/// Returns whether the lexicographical order of `lhs` is lower than `rhs`.
419/// TODO rename "IgnoreCase" to "Insensitive" in this entire file.
420pub fn lessThanIgnoreCase(lhs: []const u8, rhs: []const u8) bool {599pub fn lessThanIgnoreCase(lhs: []const u8, rhs: []const u8) bool {
421 return orderIgnoreCase(lhs, rhs) == .lt;600 return orderIgnoreCase(lhs, rhs) == .lt;
422}601}