aboutsummaryrefslogtreecommitdiff
path: root/src/string_immutable.zig
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-06-01 00:19:33 -0700
committerGravatar GitHub <noreply@github.com> 2023-06-01 00:19:33 -0700
commita4ccd4e0b4cc19f534bf639f30b7e4218400e1e8 (patch)
tree250f89bddd6e6c920645db2ad39bb7edf576edf0 /src/string_immutable.zig
parentcb0f76aa73f6b85667b57015a77ac39d9c78aa0b (diff)
parent689434e012a47b9be897f6d90d6aa211b13dfc19 (diff)
downloadbun-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.zig97
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);
}