aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-09-12 00:37:59 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-09-12 00:37:59 -0700
commitdfb65ef1ca0406e9679f21ae316b6a0878b56271 (patch)
tree1a99326014d40b68de8f66454605b45c2dc1233d
parent57ca04444b0397c5fd92177382627cf5113282b5 (diff)
downloadbun-dfb65ef1ca0406e9679f21ae316b6a0878b56271.tar.gz
bun-dfb65ef1ca0406e9679f21ae316b6a0878b56271.tar.zst
bun-dfb65ef1ca0406e9679f21ae316b6a0878b56271.zip
Reduce number of allocations for serializing error messages
-rw-r--r--src/logger.zig108
-rw-r--r--src/string_builder.zig32
-rw-r--r--src/strings.zig38
3 files changed, 125 insertions, 53 deletions
diff --git a/src/logger.zig b/src/logger.zig
index 397a49a11..b693584a4 100644
--- a/src/logger.zig
+++ b/src/logger.zig
@@ -10,6 +10,7 @@ const unicode = std.unicode;
const expect = std.testing.expect;
const assert = std.debug.assert;
const ArrayList = std.ArrayList;
+const StringBuilder = @import("./string_builder.zig");
pub const Kind = enum(i8) {
err,
@@ -122,13 +123,18 @@ pub const Location = struct {
pub fn init_or_nil(_source: ?*const Source, r: Range) ?Location {
if (_source) |source| {
var data = source.initErrorPosition(r.loc);
+ var full_line = source.contents[data.line_start..data.line_end];
+ if (full_line.len > 80 + data.column_count) {
+ full_line = full_line[std.math.max(data.column_count, 40) - 40 .. std.math.min(data.column_count + 40, full_line.len - 40) + 40];
+ }
+
return Location{
.file = source.path.pretty,
.namespace = source.path.namespace,
.line = usize2Loc(data.line_count).start,
.column = usize2Loc(data.column_count).start,
- .length = source.contents.len,
- .line_text = source.contents[data.line_start..data.line_end],
+ .length = full_line.len,
+ .line_text = full_line,
.offset = @intCast(usize, std.math.max(r.loc.start, 0)),
};
} else {
@@ -404,6 +410,32 @@ pub const Log = struct {
}
pub fn appendTo(self: *Log, other: *Log) !void {
+ var notes_count: usize = 0;
+
+ for (self.msgs.items) |msg_| {
+ const msg: Msg = msg_;
+ if (msg.notes) |notes| {
+ for (notes) |note| {
+ notes_count += @intCast(usize, @boolToInt(note.text.len > 0));
+ }
+ }
+ }
+
+ if (notes_count > 0) {
+ var notes = try other.msgs.allocator.alloc(Data, notes_count);
+ var note_i: usize = 0;
+ for (self.msgs.items) |*msg| {
+ if (msg.notes) |current_notes| {
+ var start_note_i: usize = note_i;
+ for (current_notes) |note| {
+ notes[note_i] = note;
+ note_i += 1;
+ }
+ msg.notes = notes[start_note_i..note_i];
+ }
+ }
+ }
+
try other.msgs.appendSlice(self.msgs.items);
other.warnings += self.warnings;
other.errors += self.errors;
@@ -416,22 +448,67 @@ pub const Log = struct {
other.errors += self.errors;
if (source.contents_is_recycled) {
- var i: usize = 0;
- var j: usize = other.msgs.items.len - self.msgs.items.len;
+ var string_builder = StringBuilder{};
+ var notes_count: usize = 0;
+ {
+ var i: usize = 0;
+ var j: usize = other.msgs.items.len - self.msgs.items.len;
+
+ while (i < self.msgs.items.len) : ({
+ i += 1;
+ j += 1;
+ }) {
+ const msg = self.msgs.items[i];
+ if (msg.data.location) |location| {
+ if (location.line_text) |line_text| {
- while (i < self.msgs.items.len) : ({
- i += 1;
- j += 1;
- }) {
- const msg = self.msgs.items[i];
- if (msg.data.location) |location| {
- if (location.line_text) |line_text| {
- other.msgs.items[j].data.location.?.line_text = try other.msgs.allocator.dupe(
- u8,
// Naively truncate to 690 characters per line.
// This doesn't catch where an error occurred for extremely long, minified lines.
- line_text[0..std.math.min(line_text.len, 690)],
- );
+ string_builder.count(line_text[0..std.math.min(line_text.len, 690)]);
+ }
+ }
+
+ if (msg.notes) |notes| {
+ notes_count += notes.len;
+ for (notes) |note| {
+ string_builder.count(note.text);
+ }
+ }
+ }
+ }
+
+ try string_builder.allocate(other.msgs.allocator);
+ var notes_buf = try other.msgs.allocator.alloc(Data, notes_count);
+ var note_i: usize = 0;
+
+ {
+ var i: usize = 0;
+ var j: usize = other.msgs.items.len - self.msgs.items.len;
+
+ while (i < self.msgs.items.len) : ({
+ i += 1;
+ j += 1;
+ }) {
+ const msg = self.msgs.items[i];
+
+ if (msg.data.location) |location| {
+ if (location.line_text) |line_text| {
+ other.msgs.items[j].data.location.?.line_text = string_builder.append(
+ // Naively truncate to 690 characters per line.
+ // This doesn't catch where an error occurred for extremely long, minified lines.
+ line_text[0..std.math.min(line_text.len, 690)],
+ );
+ }
+ }
+
+ if (msg.notes) |notes| {
+ var start_notes_i: usize = note_i;
+ for (notes) |note| {
+ notes_buf[note_i] = note;
+ notes_buf[note_i].text = string_builder.append(note.text);
+ note_i += 1;
+ }
+ other.msgs.items[j].notes = notes_buf[start_notes_i..note_i];
}
}
}
@@ -833,6 +910,7 @@ pub const Source = struct {
else => {},
}
}
+
return ErrorPosition{
.line_start = if (line_start > 0) line_start - 1 else line_start,
.line_end = line_end,
diff --git a/src/string_builder.zig b/src/string_builder.zig
new file mode 100644
index 000000000..26782b896
--- /dev/null
+++ b/src/string_builder.zig
@@ -0,0 +1,32 @@
+usingnamespace @import("string_types.zig");
+const Allocator = @import("std").mem.Allocator;
+const assert = @import("std").debug.assert;
+const copy = @import("std").mem.copy;
+
+const StringBuilder = @This();
+
+len: usize = 0,
+cap: usize = 0,
+ptr: ?[*]u8 = null,
+
+pub fn count(this: *StringBuilder, slice: string) void {
+ this.cap += slice.len;
+}
+
+pub fn allocate(this: *StringBuilder, allocator: *Allocator) !void {
+ var slice = try allocator.alloc(u8, this.cap);
+ this.ptr = slice.ptr;
+ this.len = 0;
+}
+
+pub fn append(this: *StringBuilder, slice: string) string {
+ assert(this.len <= this.cap); // didn't count everything
+ assert(this.ptr != null); // must call allocate first
+
+ copy(u8, this.ptr.?[this.len..this.cap], slice);
+ const result = this.ptr.?[this.len..this.cap][0..slice.len];
+ this.len += slice.len;
+
+ assert(this.len <= this.cap);
+ return result;
+}
diff --git a/src/strings.zig b/src/strings.zig
index 189116b2b..4813c1f91 100644
--- a/src/strings.zig
+++ b/src/strings.zig
@@ -9,44 +9,6 @@ pub const MutableString = mutable.MutableString;
pub const eql = std.meta.eql;
-pub fn NewStringBuilder(comptime size: usize) type {
- return struct {
- const This = @This();
- buffer: [*]u8 = undefined,
- remain: []u8 = undefined,
-
- pub fn init() This {
- return This{};
- }
-
- pub fn load(this: *This) void {
- this.remain = (&this.buffer)[0..size];
- }
-
- pub fn append(this: *This, _str: string) void {
- std.mem.copy(u8, this.remain, _str);
- this.remain = this.remain[_str.len..];
- }
-
- pub fn str(this: *This) string {
- var buf = this.buffer[0 .. @ptrToInt(this.remain.ptr) - @ptrToInt(&this.buffer)];
- // Always leave a sentinel so that anything that expects a sentinel Just Works
- // specifically, the reason for this is so C-based APIs can be used without an extra copy.
- // one byte is cheap...right?
- this.buffer[buf.len] = 0;
- return buf;
- }
-
- pub fn pop(this: *This, count: usize) string {
- this.remain = this.buffer[0 .. @ptrToInt(this.remain.ptr) - @ptrToInt(&this.buffer) - count];
- }
-
- pub fn reset(this: *This) void {
- this.load();
- }
- };
-}
-
pub fn nql(a: anytype, b: @TypeOf(a)) bool {
return !eql(a, b);
}