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");...@@ -12,7 +12,7 @@ const std = @import("std");
1212
13/// The C0 control codes of the ASCII encoding.13/// The C0 control codes of the ASCII encoding.
14///14///
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`
16pub const control_code = struct {16pub const control_code = struct {
17 /// Null.17 /// Null.
18 pub const nul = 0x00;18 pub const nul = 0x00;
...@@ -88,188 +88,63 @@ pub const control_code = struct {...@@ -88,188 +88,63 @@ pub const control_code = struct {
88 pub const xoff = dc3;88 pub const xoff = dc3;
89};89};
9090
91const tIndex = enum(u3) {91/// Returns whether the character is alphanumeric: A-Z, a-z, or 0-9.
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.
229pub fn isAlphanumeric(c: u8) bool {92pub fn isAlphanumeric(c: u8) bool {
230 return (combinedTable[c] & ((@as(u8, 1) << @enumToInt(tIndex.Alpha)) |93 return switch (c) {
231 @as(u8, 1) << @enumToInt(tIndex.Digit))) != 0;94 'A'...'Z', 'a'...'z', '0'...'9' => true,
95 else => false,
96 };
232}97}
23398
234/// Returns whether the character is alphabetic.99/// Returns whether the character is alphabetic: A-Z or a-z.
235pub fn isAlphabetic(c: u8) bool {100pub 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 };
237}105}
238106
239/// Returns whether the character is a control character.107/// Returns whether the character is a control character.
240/// This is the same as `!isPrint(c)`.
241///108///
242/// See also: `control_code`.109/// See also: `control_code`
243pub fn isControl(c: u8) bool {110pub fn isControl(c: u8) bool {
244 return c <= control_code.us or c == control_code.del;111 return c <= control_code.us or c == control_code.del;
245}112}
246113
247/// Returns whether the character is a digit.114/// Returns whether the character is a digit.
248pub fn isDigit(c: u8) bool {115pub fn isDigit(c: u8) bool {
249 return inTable(c, tIndex.Digit);116 return switch (c) {
117 '0'...'9' => true,
118 else => false,
119 };
250}120}
251121
252/// Returns whether the character is a lowercased letter.122/// Returns whether the character is a lowercase letter.
253pub fn isLower(c: u8) bool {123pub fn isLower(c: u8) bool {
254 return inTable(c, tIndex.Lower);124 return switch (c) {
125 'a'...'z' => true,
126 else => false,
127 };
255}128}
256129
257/// Returns whether the character is printable and has some graphical representation.130/// Returns whether the character is printable and has some graphical representation,
258/// This also returns `true` for the space character.131/// including the space character.
259/// This is the same as `!isControl(c)`.
260pub fn isPrint(c: u8) bool {132pub fn isPrint(c: u8) bool {
261 return inTable(c, tIndex.Graph) or c == ' ';133 return isASCII(c) and !isControl(c);
262}134}
263135
264/// Returns whether this character is included in `whitespace`.136/// Returns whether this character is included in `whitespace`.
265pub fn isWhitespace(c: u8) bool {137pub 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;
267}142}
268143
269/// Whitespace for general use.144/// Whitespace for general use.
270/// This may be used with e.g. `std.mem.trim` to trim whitespace.145/// This may be used with e.g. `std.mem.trim` to trim whitespace.
271///146///
272/// See also: `isWhitespace`.147/// See also: `isWhitespace`
273pub const whitespace = [_]u8{ ' ', '\t', '\n', '\r', control_code.vt, control_code.ff };148pub const whitespace = [_]u8{ ' ', '\t', '\n', '\r', control_code.vt, control_code.ff };
274149
275test "whitespace" {150test "whitespace" {
...@@ -281,14 +156,20 @@ test "whitespace" {...@@ -281,14 +156,20 @@ test "whitespace" {
281 }156 }
282}157}
283158
284/// Returns whether the character is an uppercased letter.159/// Returns whether the character is an uppercase letter.
285pub fn isUpper(c: u8) bool {160pub fn isUpper(c: u8) bool {
286 return inTable(c, tIndex.Upper);161 return switch (c) {
162 'A'...'Z' => true,
163 else => false,
164 };
287}165}
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.
290pub fn isHex(c: u8) bool {168pub 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 };
292}173}
293174
294/// Returns whether the character is a 7-bit ASCII character.175/// Returns whether the character is a 7-bit ASCII character.
...@@ -322,6 +203,8 @@ test "ASCII character classes" {...@@ -322,6 +203,8 @@ test "ASCII character classes" {
322 try testing.expect(isControl(control_code.nul));203 try testing.expect(isControl(control_code.nul));
323 try testing.expect(isControl(control_code.ff));204 try testing.expect(isControl(control_code.ff));
324 try testing.expect(isControl(control_code.us));205 try testing.expect(isControl(control_code.us));
206 try testing.expect(!isControl(0x80));
207 try testing.expect(!isControl(0xff));
325208
326 try testing.expect('C' == toUpper('c'));209 try testing.expect('C' == toUpper('c'));
327 try testing.expect(':' == toUpper(':'));210 try testing.expect(':' == toUpper(':'));
...@@ -351,6 +234,7 @@ test "ASCII character classes" {...@@ -351,6 +234,7 @@ test "ASCII character classes" {
351234
352 try testing.expect(!isHex('g'));235 try testing.expect(!isHex('g'));
353 try testing.expect(isHex('b'));236 try testing.expect(isHex('b'));
237 try testing.expect(isHex('F'));
354 try testing.expect(isHex('9'));238 try testing.expect(isHex('9'));
355239
356 try testing.expect(!isDigit('~'));240 try testing.expect(!isDigit('~'));
...@@ -361,6 +245,8 @@ test "ASCII character classes" {...@@ -361,6 +245,8 @@ test "ASCII character classes" {
361 try testing.expect(isPrint('@'));245 try testing.expect(isPrint('@'));
362 try testing.expect(isPrint('~'));246 try testing.expect(isPrint('~'));
363 try testing.expect(!isPrint(control_code.esc));247 try testing.expect(!isPrint(control_code.esc));
248 try testing.expect(!isPrint(0x80));
249 try testing.expect(!isPrint(0xff));
364}250}
365251
366/// Writes a lower case copy of `ascii_string` to `output`.252/// Writes a lower case copy of `ascii_string` to `output`.