authorgravatar for shawn@git.icuShawn Landden <shawn@git.icu> 2019-03-21 20:33:37-06:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-03-22 16:25:56-04:00
logb76398c9939372ebd2e7263e2aec76503f48b146
tree6f6c2ede6afb8818bdb5e74e535e87fb1ff78942
parent6272847917be186fa83f92b0580ed27f459014bd

std: add ascii with C ASCII character classes

Does NOT look at the locale the way the C functions do. int isalnum(int c); int isalpha(int c); int iscntrl(int c); int isdigit(int c); int isgraph(int c); int islower(int c); int isprint(int c); int ispunct(int c); int isspace(int c); int isupper(int c); int isxdigit(int c); int isascii(int c); int isblank(int c); int toupper(int c); int tolower(int c); Tested to match glibc (when using C locale) with this program: const c = @cImport({ // See https://github.com/ziglang/zig/issues/515 @cDefine("_NO_CRT_STDIO_INLINE", "1"); @cInclude("stdio.h"); @cInclude("string.h"); @cInclude("ctype.h"); }); const std = @import("std"); const ascii = std.ascii; const abort = std.os.abort; export fn main(argc: c_int, argv: **u8) c_int { var i: u8 = undefined; i = 0; while (true) { if (ascii.isAlNum(i) != (c.isalnum(i) > 0)) { abort(); } if (ascii.isAlpha(i) != (c.isalpha(i) > 0)) { abort(); } if (ascii.isCtrl(i) != (c.iscntrl(i) > 0)) { abort(); } if (ascii.isDigit(i) != (c.isdigit(i) > 0)) { abort(); } if (ascii.isGraph(i) != (c.isgraph(i) > 0)) { abort(); } if (ascii.isLower(i) != (c.islower(i) > 0)) { abort(); } if (ascii.isPrint(i) != (c.isprint(i) > 0)) { abort(); } if (ascii.isPunct(i) != (c.ispunct(i) > 0)) { abort(); } if (ascii.isSpace(i) != (c.isspace(i) > 0)) { abort(); } if (ascii.isUpper(i) != (c.isupper(i) > 0)) { abort(); } if (ascii.isXDigit(i) != (c.isxdigit(i) > 0)) { abort(); } if (i == 255) { break; } i += 1; } _ = c.printf(c"Success!\n"); return 0; }

3 files changed, 234 insertions(+), 0 deletions(-)

CMakeLists.txt+1
...@@ -446,6 +446,7 @@ set(ZIG_CPP_SOURCES...@@ -446,6 +446,7 @@ set(ZIG_CPP_SOURCES
446446
447set(ZIG_STD_FILES447set(ZIG_STD_FILES
448 "array_list.zig"448 "array_list.zig"
449 "ascii.zig"
449 "atomic.zig"450 "atomic.zig"
450 "atomic/int.zig"451 "atomic/int.zig"
451 "atomic/queue.zig"452 "atomic/queue.zig"
std/ascii.zig created+232
...@@ -0,0 +1,232 @@
1// Does NOT look at the locale the way C89's toupper(3), isspace() et cetera does.
2// I could have taken only a u7 to make this clear, but it would be slower
3// It is my opinion that encodings other than UTF-8 should not be supported.
4//
5// (and 128 bytes is not much to pay).
6// Also does not handle Unicode character classes.
7//
8// https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/USASCII_code_chart.png/1200px-USASCII_code_chart.png
9
10const tIndex = enum(u3) {
11 Alpha,
12 Hex,
13 Space,
14 Digit,
15 Lower,
16 Upper,
17 // Ctrl, < 0x20 || == DEL
18 // Print, = Graph || == ' '. NOT '\t' et cetera
19 Punct,
20 Graph,
21 //ASCII, | ~0b01111111
22 //isBlank, == ' ' || == '\x09'
23};
24
25const combinedTable = init: {
26 comptime var table: [256]u8 = undefined;
27
28 const std = @import("std");
29 const mem = std.mem;
30
31 const alpha = []u1{
32 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
33 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
34 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
35 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
36 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37
38 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
39 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
40 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
41 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
42 };
43 const lower = []u1{
44 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
45 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
46 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
47 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
48 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
49
50 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
53 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
54 };
55 const upper = []u1{
56 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
57 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
58 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
61
62 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
63 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
64 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
65 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
66 };
67 const digit = []u1{
68 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
69 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
70 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
71 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
72 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
73
74 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
76 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
77 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
78 };
79 const hex = []u1{
80 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
81 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
82 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
83 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
84 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
85
86 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
87 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
88 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
89 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
90 };
91 const space = []u1{
92 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
93 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
94 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
95 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
96 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
97
98 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
99 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
100 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
101 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
102 };
103 const punct = []u1{
104 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
105 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
106 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
107 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
108 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
109
110 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
111 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
112 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
113 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
114 };
115 const graph = []u1{
116 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
117 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
120 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
121
122 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
123 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
124 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
125 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
126 };
127
128 comptime var i = 0;
129 inline while (i < 128) : (i += 1) {
130 table[i] =
131 u8(alpha[i]) << @enumToInt(tIndex.Alpha) |
132 u8(hex[i]) << @enumToInt(tIndex.Hex) |
133 u8(space[i]) << @enumToInt(tIndex.Space) |
134 u8(digit[i]) << @enumToInt(tIndex.Digit) |
135 u8(lower[i]) << @enumToInt(tIndex.Lower) |
136 u8(upper[i]) << @enumToInt(tIndex.Upper) |
137 u8(punct[i]) << @enumToInt(tIndex.Punct) |
138 u8(graph[i]) << @enumToInt(tIndex.Graph);
139 }
140 mem.set(u8, table[128..256], 0);
141 break :init table;
142};
143
144fn inTable(c: u8, t: tIndex) bool {
145 return (combinedTable[c] & (u8(1) << @enumToInt(t))) != 0;
146}
147
148pub fn isAlNum(c: u8) bool {
149 return (combinedTable[c] & ((u8(1) << @enumToInt(tIndex.Alpha)) |
150 u8(1) << @enumToInt(tIndex.Digit))) != 0;
151}
152
153pub fn isAlpha(c: u8) bool {
154 return inTable(c, tIndex.Alpha);
155}
156
157pub fn isCtrl(c: u8) bool {
158 return c < 0x20 or c == 127; //DEL
159}
160
161pub fn isCntrl(c: u8) bool {
162 return isCtrl(c);
163}
164
165pub fn isDigit(c: u8) bool {
166 return inTable(c, tIndex.Digit);
167}
168
169pub fn isGraph(c: u8) bool {
170 return inTable(c, tIndex.Graph);
171}
172
173pub fn isLower(c: u8) bool {
174 return inTable(c, tIndex.Lower);
175}
176
177pub fn isPrint(c: u8) bool {
178 return inTable(c, tIndex.Graph) or c == ' ';
179}
180
181pub fn isPunct(c: u8) bool {
182 return inTable(c, tIndex.Punct);
183}
184
185pub fn isSpace(c: u8) bool {
186 return inTable(c, tIndex.Space);
187}
188
189pub fn isUpper(c: u8) bool {
190 return inTable(c, tIndex.Upper);
191}
192
193pub fn isXDigit(c: u8) bool {
194 return inTable(c, tIndex.Hex);
195}
196
197pub fn isASCII(c: u8) bool {
198 return c < 128;
199}
200
201pub fn isBlank(c: u8) bool {
202 return (c == ' ') or (c == '\x09');
203}
204
205pub fn toUpper(c: u8) u8 {
206 if (isLower(c)) {
207 return c - 0x20;
208 } else {
209 return c;
210 }
211}
212
213pub fn toLower(c: u8) u8 {
214 if (isUpper(c)) {
215 return c + 0x20;
216 } else {
217 return c;
218 }
219}
220
221test "ascii character classes" {
222 const std = @import("std");
223 const testing = std.testing;
224
225 testing.expect('C' == toUpper('c'));
226 testing.expect(':' == toUpper(':'));
227 testing.expect('\xab' == toUpper('\xab'));
228 testing.expect('c' == toLower('C'));
229 testing.expect(isAlpha('c'));
230 testing.expect(!isAlpha('5'));
231 testing.expect(isSpace(' '));
232}
std/std.zig+1
...@@ -42,6 +42,7 @@ pub const pdb = @import("pdb.zig");...@@ -42,6 +42,7 @@ pub const pdb = @import("pdb.zig");
42pub const rand = @import("rand.zig");42pub const rand = @import("rand.zig");
43pub const rb = @import("rb.zig");43pub const rb = @import("rb.zig");
44pub const sort = @import("sort.zig");44pub const sort = @import("sort.zig");
45pub const ascii = @import("ascii.zig");
45pub const testing = @import("testing.zig");46pub const testing = @import("testing.zig");
46pub const unicode = @import("unicode.zig");47pub const unicode = @import("unicode.zig");
47pub const valgrind = @import("valgrind.zig");48pub const valgrind = @import("valgrind.zig");