diff options
author | 2023-06-01 00:19:33 -0700 | |
---|---|---|
committer | 2023-06-01 00:19:33 -0700 | |
commit | a4ccd4e0b4cc19f534bf639f30b7e4218400e1e8 (patch) | |
tree | 250f89bddd6e6c920645db2ad39bb7edf576edf0 /src/string_immutable.zig | |
parent | cb0f76aa73f6b85667b57015a77ac39d9c78aa0b (diff) | |
parent | 689434e012a47b9be897f6d90d6aa211b13dfc19 (diff) | |
download | bun-a4ccd4e0b4cc19f534bf639f30b7e4218400e1e8.tar.gz bun-a4ccd4e0b4cc19f534bf639f30b7e4218400e1e8.tar.zst bun-a4ccd4e0b4cc19f534bf639f30b7e4218400e1e8.zip |
Merge branch 'main' into jarred/portjarred/port
Diffstat (limited to 'src/string_immutable.zig')
-rw-r--r-- | src/string_immutable.zig | 97 |
1 files changed, 96 insertions, 1 deletions
diff --git a/src/string_immutable.zig b/src/string_immutable.zig index f7e7a3657..25d4fb01a 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -38,7 +38,7 @@ pub fn toUTF16Literal(comptime str: []const u8) []const u16 { }; } -const OptionalUsize = std.meta.Int(.unsigned, @bitSizeOf(usize) - 1); +pub const OptionalUsize = std.meta.Int(.unsigned, @bitSizeOf(usize) - 1); pub fn indexOfAny(self: string, comptime str: anytype) ?OptionalUsize { inline for (str) |a| { if (indexOfChar(self, a)) |i| { @@ -689,6 +689,63 @@ pub fn endsWithAny(self: string, str: string) bool { return false; } +// Formats a string to be safe to output in a Github action. +// - Encodes "\n" as "%0A" to support multi-line strings. +// https://github.com/actions/toolkit/issues/193#issuecomment-605394935 +// - Strips ANSI output as it will appear malformed. +pub fn githubActionWriter(writer: anytype, self: string) !void { + var offset: usize = 0; + const end = @truncate(u32, self.len); + while (offset < end) { + if (indexOfNewlineOrNonASCIIOrANSI(self, @truncate(u32, offset))) |i| { + const byte = self[i]; + if (byte > 0x7F) { + offset += @max(wtf8ByteSequenceLength(byte), 1); + continue; + } + if (i > 0) { + try writer.writeAll(self[offset..i]); + } + var n: usize = 1; + if (byte == '\n') { + try writer.writeAll("%0A"); + } else if (i + 1 < end) { + const next = self[i + 1]; + if (byte == '\r' and next == '\n') { + n += 1; + try writer.writeAll("%0A"); + } else if (byte == '\x1b' and next == '[') { + n += 1; + if (i + 2 < end) { + const remain = self[(i + 2)..@min(i + 5, end)]; + if (indexOfChar(remain, 'm')) |j| { + n += j + 1; + } + } + } + } + offset = i + n; + } else { + try writer.writeAll(self[offset..end]); + break; + } + } +} + +pub const GithubActionFormatter = struct { + text: string, + + pub fn format(this: GithubActionFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + try githubActionWriter(writer, this.text); + } +}; + +pub fn githubAction(self: string) strings.GithubActionFormatter { + return GithubActionFormatter{ + .text = self, + }; +} + pub fn quotedWriter(writer: anytype, self: string) !void { var remain = self; if (strings.containsNewlineOrNonASCIIOrQuote(remain)) { @@ -3179,6 +3236,44 @@ pub fn firstNonASCIIWithType(comptime Type: type, slice: Type) ?u32 { return null; } +pub fn indexOfNewlineOrNonASCIIOrANSI(slice_: []const u8, offset: u32) ?u32 { + const slice = slice_[offset..]; + var remaining = slice; + + if (remaining.len == 0) + return null; + + if (comptime Environment.enableSIMD) { + while (remaining.len >= ascii_vector_size) { + const vec: AsciiVector = remaining[0..ascii_vector_size].*; + const cmp = @bitCast(AsciiVectorU1, (vec > max_16_ascii)) | @bitCast(AsciiVectorU1, (vec < min_16_ascii)) | + @bitCast(AsciiVectorU1, vec == @splat(ascii_vector_size, @as(u8, '\r'))) | + @bitCast(AsciiVectorU1, vec == @splat(ascii_vector_size, @as(u8, '\n'))) | + @bitCast(AsciiVectorU1, vec == @splat(ascii_vector_size, @as(u8, '\x1b'))); + + if (@reduce(.Max, cmp) > 0) { + const bitmask = @bitCast(AsciiVectorInt, cmp); + const first = @ctz(bitmask); + + return @as(u32, first) + @intCast(u32, slice.len - remaining.len) + offset; + } + + remaining = remaining[ascii_vector_size..]; + } + + if (comptime Environment.allow_assert) std.debug.assert(remaining.len < ascii_vector_size); + } + + for (remaining) |*char_| { + const char = char_.*; + if (char > 127 or char < 0x20 or char == '\n' or char == '\r' or char == '\x1b') { + return @truncate(u32, (@ptrToInt(char_) - @ptrToInt(slice.ptr))) + offset; + } + } + + return null; +} + pub fn indexOfNewlineOrNonASCII(slice_: []const u8, offset: u32) ?u32 { return indexOfNewlineOrNonASCIICheckStart(slice_, offset, true); } |