authorgravatar for r00ster91@proton.meWooster <r00ster91@proton.me> 2022-10-16 17:44:31+02:00
committergravatar for r00ster91@proton.meWooster <r00ster91@proton.me> 2022-12-09 21:57:17+01:00
log19dbc5805c440dbfb22bbd3e75c7ec706655bb19
treef48cf08ae4d484b5887a4f4f25a895c6ffb25d3c
parent626e02a429218fd194111320057fc4f2e8ceecda

fix(perf): remove LUT

This makes it so that we no longer use a LUT (Look-Up Table): * The code is much simpler and easier to understand now. * Using a LUT means we rely on a warm cache. Relying on the cache like this results in inconsistent performance and in many cases codegen will be worse. Also as @topolarity once pointed out, in some cases while it seems like the code may branch, it actually doesn't: https://github.com/ziglang/zig/pull/11629#issuecomment-1213641429 * Other languages' standard libraries don't do this either. JFF I wanted to see what other languages codegen compared to us now: https://rust.godbolt.org/z/Te4ax9Edf, https://zig.godbolt.org/z/nTbYedWKv So we are pretty much on par or better than other languages now.

1 files changed, 44 insertions(+), 158 deletions(-)

lib/std/ascii.zig+44-158
......@@ -12,7 +12,7 @@ const std = @import("std");
1212
1313/// The C0 control codes of the ASCII encoding.
1414///
15/// See also: https://en.wikipedia.org/wiki/C0_and_C1_control_codes and `isControl`.
15/// See also: https://en.wikipedia.org/wiki/C0_and_C1_control_codes and `isControl`
1616pub const control_code = struct {
1717 /// Null.
1818 pub const nul = 0x00;
......@@ -88,188 +88,63 @@ pub const control_code = struct {
8888 pub const xoff = dc3;
8989};
9090
91const tIndex = enum(u3) {
92 Alpha,
93 Hex,
94 Space,
95 Digit,
96 Lower,
97 Upper,
98 // Ctrl, < 0x20 || == DEL
99 // Print, = Graph || == ' '. NOT '\t' et cetera
100 Punct,
101 Graph,
102 //ASCII, | ~0b01111111
103 //isBlank, == ' ' || == '\x09'
104};
105
106const combinedTable = init: {
107 comptime var table: [256]u8 = undefined;
108
109 const mem = std.mem;
110
111 const alpha = [_]u1{
112 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
113 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
114 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
115 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
116 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
117
118 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
119 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
120 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
121 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
122 };
123 const lower = [_]u1{
124 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
128 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
129
130 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
131 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
132 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
133 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
134 };
135 const upper = [_]u1{
136 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
137 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
138 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
139 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
140 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
141
142 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
143 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
144 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
145 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
146 };
147 const digit = [_]u1{
148 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
149 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
150 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
151 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
152 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
153
154 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
155 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
157 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
158 };
159 const hex = [_]u1{
160 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
161 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
162 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
163 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
164 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
165
166 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
167 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
168 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
170 };
171 const space = [_]u1{
172 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
173 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
174 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
175 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
176 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
177
178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
179 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
181 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
182 };
183 const punct = [_]u1{
184 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
185 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
186 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
187 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
188 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
189
190 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
191 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
192 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
193 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
194 };
195 const graph = [_]u1{
196 // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15
197 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
198 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
199 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
200 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
201
202 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
203 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
204 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
205 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
206 };
207
208 comptime var i = 0;
209 inline while (i < 128) : (i += 1) {
210 table[i] =
211 @as(u8, alpha[i]) << @enumToInt(tIndex.Alpha) |
212 @as(u8, hex[i]) << @enumToInt(tIndex.Hex) |
213 @as(u8, space[i]) << @enumToInt(tIndex.Space) |
214 @as(u8, digit[i]) << @enumToInt(tIndex.Digit) |
215 @as(u8, lower[i]) << @enumToInt(tIndex.Lower) |
216 @as(u8, upper[i]) << @enumToInt(tIndex.Upper) |
217 @as(u8, punct[i]) << @enumToInt(tIndex.Punct) |
218 @as(u8, graph[i]) << @enumToInt(tIndex.Graph);
219 }
220 mem.set(u8, table[128..256], 0);
221 break :init table;
222};
223
224fn inTable(c: u8, t: tIndex) bool {
225 return (combinedTable[c] & (@as(u8, 1) << @enumToInt(t))) != 0;
226}
227
228/// Returns whether the character is alphanumeric.
91/// Returns whether the character is alphanumeric: A-Z, a-z, or 0-9.
22992pub fn isAlphanumeric(c: u8) bool {
230 return (combinedTable[c] & ((@as(u8, 1) << @enumToInt(tIndex.Alpha)) |
231 @as(u8, 1) << @enumToInt(tIndex.Digit))) != 0;
93 return switch (c) {
94 'A'...'Z', 'a'...'z', '0'...'9' => true,
95 else => false,
96 };
23297}
23398
234/// Returns whether the character is alphabetic.
99/// Returns whether the character is alphabetic: A-Z or a-z.
235100pub fn isAlphabetic(c: u8) bool {
236 return inTable(c, tIndex.Alpha);
101 return switch (c) {
102 'A'...'Z', 'a'...'z' => true,
103 else => false,
104 };
237105}
238106
239107/// Returns whether the character is a control character.
240/// This is the same as `!isPrint(c)`.
241108///
242/// See also: `control_code`.
109/// See also: `control_code`
243110pub fn isControl(c: u8) bool {
244111 return c <= control_code.us or c == control_code.del;
245112}
246113
247114/// Returns whether the character is a digit.
248115pub fn isDigit(c: u8) bool {
249 return inTable(c, tIndex.Digit);
116 return switch (c) {
117 '0'...'9' => true,
118 else => false,
119 };
250120}
251121
252/// Returns whether the character is a lowercased letter.
122/// Returns whether the character is a lowercase letter.
253123pub fn isLower(c: u8) bool {
254 return inTable(c, tIndex.Lower);
124 return switch (c) {
125 'a'...'z' => true,
126 else => false,
127 };
255128}
256129
257/// Returns whether the character is printable and has some graphical representation.
258/// This also returns `true` for the space character.
259/// This is the same as `!isControl(c)`.
130/// Returns whether the character is printable and has some graphical representation,
131/// including the space character.
260132pub fn isPrint(c: u8) bool {
261 return inTable(c, tIndex.Graph) or c == ' ';
133 return isASCII(c) and !isControl(c);
262134}
263135
264136/// Returns whether this character is included in `whitespace`.
265137pub fn isWhitespace(c: u8) bool {
266 return inTable(c, tIndex.Space);
138 return for (whitespace) |other| {
139 if (c == other)
140 break true;
141 } else false;
267142}
268143
269144/// Whitespace for general use.
270145/// This may be used with e.g. `std.mem.trim` to trim whitespace.
271146///
272/// See also: `isWhitespace`.
147/// See also: `isWhitespace`
273148pub const whitespace = [_]u8{ ' ', '\t', '\n', '\r', control_code.vt, control_code.ff };
274149
275150test "whitespace" {
......@@ -281,14 +156,20 @@ test "whitespace" {
281156 }
282157}
283158
284/// Returns whether the character is an uppercased letter.
159/// Returns whether the character is an uppercase letter.
285160pub fn isUpper(c: u8) bool {
286 return inTable(c, tIndex.Upper);
161 return switch (c) {
162 'A'...'Z' => true,
163 else => false,
164 };
287165}
288166
289/// Returns whether the character is a hexadecimal digit. Case-insensitive.
167/// Returns whether the character is a hexadecimal digit: A-F, a-f, or 0-9.
290168pub fn isHex(c: u8) bool {
291 return inTable(c, tIndex.Hex);
169 return switch (c) {
170 'A'...'F', 'a'...'f', '0'...'9' => true,
171 else => false,
172 };
292173}
293174
294175/// Returns whether the character is a 7-bit ASCII character.
......@@ -322,6 +203,8 @@ test "ASCII character classes" {
322203 try testing.expect(isControl(control_code.nul));
323204 try testing.expect(isControl(control_code.ff));
324205 try testing.expect(isControl(control_code.us));
206 try testing.expect(!isControl(0x80));
207 try testing.expect(!isControl(0xff));
325208
326209 try testing.expect('C' == toUpper('c'));
327210 try testing.expect(':' == toUpper(':'));
......@@ -351,6 +234,7 @@ test "ASCII character classes" {
351234
352235 try testing.expect(!isHex('g'));
353236 try testing.expect(isHex('b'));
237 try testing.expect(isHex('F'));
354238 try testing.expect(isHex('9'));
355239
356240 try testing.expect(!isDigit('~'));
......@@ -361,6 +245,8 @@ test "ASCII character classes" {
361245 try testing.expect(isPrint('@'));
362246 try testing.expect(isPrint('~'));
363247 try testing.expect(!isPrint(control_code.esc));
248 try testing.expect(!isPrint(0x80));
249 try testing.expect(!isPrint(0xff));
364250}
365251
366252/// Writes a lower case copy of `ascii_string` to `output`.