diff options
Diffstat (limited to 'src/js_printer.zig')
-rw-r--r-- | src/js_printer.zig | 183 |
1 files changed, 143 insertions, 40 deletions
diff --git a/src/js_printer.zig b/src/js_printer.zig index a3c439ca3..868757b7d 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -265,6 +265,29 @@ pub fn NewPrinter( } } + fn fmt(p: *Printer, comptime str: string, args: anytype) !void { + const len = @call( + .{ + .modifier = .always_inline, + }, + std.fmt.count, + .{ str, args }, + ); + var ptr = try p.writer.reserveNext( + len, + ); + + const written = @call( + .{ + .modifier = .always_inline, + }, + std.fmt.bufPrint, + .{ ptr[0..len], str, args }, + ) catch unreachable; + + p.writer.advance(written.len); + } + pub fn print(p: *Printer, str: anytype) void { switch (@TypeOf(str)) { comptime_int, u16, u8 => { @@ -538,12 +561,6 @@ pub fn NewPrinter( backtick_cost += 1; } }, - '\r', '\n' => { - if (comptime isDebug) { - std.debug.assert(allow_backtick); - } - return '`'; - }, else => {}, } i += 1; @@ -578,22 +595,22 @@ pub fn NewPrinter( } pub fn printQuotedUTF16(e: *Printer, text: []const u16, quote: u8) void { - // utf-8 is a max of 4 bytes - // we leave two extra chars for "\" and "u" - var temp = [6]u8{ 0, 0, 0, 0, 0, 0 }; var i: usize = 0; const n: usize = text.len; // e(text.len) catch unreachable; while (i < n) { - const c = @as(u21, text[i]); + const CodeUnitType = u21; + + const c = @as(CodeUnitType, text[i]); i += 1; - var r: u21 = 0; + var r: CodeUnitType = 0; var width: u3 = 0; // TODO: here switch (c) { + // Special-case the null character since it may mess with code written in C // that treats null characters as the end of the string. 0x00 => { @@ -605,6 +622,38 @@ pub fn NewPrinter( } }, + 'a'...'z', + 'A'...'Z', + '0'...'9', + '_', + '-', + '(', + '[', + '{', + '<', + '>', + ')', + ']', + '}', + ',', + ':', + ';', + '.', + '?', + '!', + '@', + '#', + '%', + '^', + '&', + '*', + '+', + '=', + ' ', + => { + e.print(@intCast(u8, c)); + }, + // Special-case the bell character since it may cause dumping this file to // the terminal to make a sound, which is undesirable. Note that we can't // use an octal literal to print this shorter since octal literals are not @@ -620,7 +669,7 @@ pub fn NewPrinter( }, '\n' => { if (quote == '`') { - e.print("\n"); + e.print('\n'); } else { e.print("\\n"); } @@ -633,32 +682,34 @@ pub fn NewPrinter( e.print("\\v"); }, // "\\" - 92 => { + '\\' => { e.print("\\\\"); }, + '\'' => { if (quote == '\'') { - e.print("\\"); + e.print('\\'); } e.print("'"); }, + '"' => { if (quote == '"') { - e.print("\\"); + e.print('\\'); } e.print("\""); }, '`' => { if (quote == '`') { - e.print("\\"); + e.print('\\'); } e.print("`"); }, '$' => { if (quote == '`' and i < n and text[i] == '{') { - e.print("\\"); + e.print('\\'); } e.print('$'); @@ -672,19 +723,16 @@ pub fn NewPrinter( 0xFEFF => { e.print("\\uFEFF"); }, + else => { switch (c) { - // Common case: just append a single byte - // we know it's not 0 since we already checked - 1...last_ascii => { - e.print(@intCast(u8, c)); - }, + first_high_surrogate...last_high_surrogate => { // Is there a next character? if (i < n) { - const c2 = text[i]; + const c2: CodeUnitType = @as(CodeUnitType, text[i]); if (c2 >= first_high_surrogate and c2 <= last_low_surrogate) { // this is some magic to me @@ -692,48 +740,62 @@ pub fn NewPrinter( i += 1; // Escape this character if UTF-8 isn't allowed if (ascii_only) { - // this is more magic!! - const bytes = [_]u8{ + var ptr = e.writer.reserve(12) catch unreachable; + ptr[0..12].* = [_]u8{ '\\', 'u', hex_chars[c >> 12], hex_chars[(c >> 8) & 15], hex_chars[(c >> 4) & 15], hex_chars[c & 15], '\\', 'u', hex_chars[c2 >> 12], hex_chars[(c2 >> 8) & 15], hex_chars[(c2 >> 4) & 15], hex_chars[c2 & 15], }; - e.print(&bytes); + e.writer.advance(12); continue; // Otherwise, encode to UTF-8 } else { - width = std.unicode.utf8Encode(r, &temp) catch unreachable; - e.print(temp[0..width]); + var ptr = e.writer.reserve(4) catch unreachable; + e.writer.advance(strings.encodeWTF8RuneT(ptr[0..4], CodeUnitType, r)); continue; } } } - // Write an unpaired high surrogate - temp = [_]u8{ '\\', 'u', hex_chars[c >> 12], hex_chars[(c >> 8) & 15], hex_chars[(c >> 4) & 15], hex_chars[c & 15] }; - e.print(&temp); + { + // Write an unpaired high surrogate + var ptr = e.writer.reserve(6) catch unreachable; + ptr[0..6].* = [_]u8{ '\\', 'u', hex_chars[c >> 12], hex_chars[(c >> 8) & 15], hex_chars[(c >> 4) & 15], hex_chars[c & 15] }; + e.writer.advance(6); + } }, // Is this an unpaired low surrogate or four-digit hex escape? first_low_surrogate...last_low_surrogate => { // Write an unpaired high surrogate - temp = [_]u8{ '\\', 'u', hex_chars[c >> 12], hex_chars[(c >> 8) & 15], hex_chars[(c >> 4) & 15], hex_chars[c & 15] }; - e.print(&temp); + var ptr = e.writer.reserve(6) catch unreachable; + ptr[0..6].* = [_]u8{ '\\', 'u', hex_chars[c >> 12], hex_chars[(c >> 8) & 15], hex_chars[(c >> 4) & 15], hex_chars[c & 15] }; + e.writer.advance(6); }, else => { // this extra branch should get compiled if (ascii_only) { if (c > 0xFF) { + var ptr = e.writer.reserve(6) catch unreachable; // Write an unpaired high surrogate - temp = [_]u8{ '\\', 'u', hex_chars[c >> 12], hex_chars[(c >> 8) & 15], hex_chars[(c >> 4) & 15], hex_chars[c & 15] }; - e.print(&temp); + ptr[0..6].* = [_]u8{ '\\', 'u', hex_chars[c >> 12], hex_chars[(c >> 8) & 15], hex_chars[(c >> 4) & 15], hex_chars[c & 15] }; + e.writer.advance(6); } else { // Can this be a two-digit hex escape? - const quad = [_]u8{ '\\', 'x', hex_chars[c >> 4], hex_chars[c & 15] }; - e.print(&quad); + var ptr = e.writer.reserve(4) catch unreachable; + ptr[0..4].* = [_]u8{ '\\', 'x', hex_chars[c >> 4], hex_chars[c & 15] }; + e.writer.advance(4); } } else { - width = std.unicode.utf8Encode(c, &temp) catch unreachable; - e.print(temp[0..width]); + // chars < 255 as two digit hex escape + if (c < 0xFF) { + var ptr = e.writer.reserve(4) catch unreachable; + ptr[0..4].* = [_]u8{ '\\', 'x', hex_chars[c >> 4], hex_chars[c & 15] }; + e.writer.advance(4); + continue; + } + + var ptr = e.writer.reserve(4) catch return; + e.writer.advance(strings.encodeWTF8RuneT(ptr[0..4], CodeUnitType, c)); } }, } @@ -3849,6 +3911,8 @@ pub fn NewWriter( writeAllFn: fn (ctx: *ContextType, buf: anytype) anyerror!usize, getLastByte: fn (ctx: *const ContextType) u8, getLastLastByte: fn (ctx: *const ContextType) u8, + reserveNext: fn (ctx: *ContextType, count: u32) anyerror![*]u8, + advanceBy: fn (ctx: *ContextType, count: u32) void, ) type { return struct { const Self = @This(); @@ -3900,6 +3964,15 @@ pub fn NewWriter( return @call(.{ .modifier = .always_inline }, getLastLastByte, .{&writer.ctx}); } + pub fn reserve(writer: *Self, count: u32) anyerror![*]u8 { + return try reserveNext(&writer.ctx, count); + } + + pub fn advance(writer: *Self, count: u32) void { + advanceBy(&writer.ctx, count); + writer.written += @intCast(i32, count); + } + pub const Error = error{FormatError}; pub fn writeAll(writer: *Self, bytes: anytype) Error!usize { @@ -4015,6 +4088,16 @@ const FileWriterInternal = struct { return if (buffer.list.items.len > 1) buffer.list.items[buffer.list.items.len - 2] else 0; } + pub fn reserveNext(ctx: *FileWriterInternal, count: u32) anyerror![*]u8 { + try buffer.growIfNeeded(count); + return @ptrCast([*]u8, &buffer.list.items.ptr[buffer.list.items.len]); + } + pub fn advanceBy(ctx: *FileWriterInternal, count: u32) void { + if (comptime Environment.isDebug) std.debug.assert(buffer.list.items.len + count < buffer.list.capacity); + + buffer.list.items = buffer.list.items.ptr[0 .. buffer.list.items.len + count]; + } + pub fn done( ctx: *FileWriterInternal, ) anyerror!void { @@ -4101,6 +4184,16 @@ pub const BufferWriter = struct { return if (ctx.buffer.list.items.len > 1) ctx.buffer.list.items[ctx.buffer.list.items.len - 2] else 0; } + pub fn reserveNext(ctx: *BufferWriter, count: u32) anyerror![*]u8 { + try ctx.buffer.growIfNeeded(count); + return @ptrCast([*]u8, &ctx.buffer.list.items.ptr[ctx.buffer.list.items.len]); + } + pub fn advanceBy(ctx: *BufferWriter, count: u32) void { + if (comptime Environment.isDebug) std.debug.assert(ctx.buffer.list.items.len + count < ctx.buffer.list.capacity); + + ctx.buffer.list.items = ctx.buffer.list.items.ptr[0 .. ctx.buffer.list.items.len + count]; + } + pub fn reset(ctx: *BufferWriter) void { ctx.buffer.reset(); ctx.approximate_newline_count = 0; @@ -4127,8 +4220,18 @@ pub const BufferPrinter = NewWriter( BufferWriter.writeAll, BufferWriter.getLastByte, BufferWriter.getLastLastByte, + BufferWriter.reserveNext, + BufferWriter.advanceBy, +); +pub const FileWriter = NewWriter( + FileWriterInternal, + FileWriterInternal.writeByte, + FileWriterInternal.writeAll, + FileWriterInternal.getLastByte, + FileWriterInternal.getLastLastByte, + FileWriterInternal.reserveNext, + FileWriterInternal.advanceBy, ); -pub const FileWriter = NewWriter(FileWriterInternal, FileWriterInternal.writeByte, FileWriterInternal.writeAll, FileWriterInternal.getLastByte, FileWriterInternal.getLastLastByte); pub fn NewFileWriter(file: std.fs.File) FileWriter { var internal = FileWriterInternal.init(file); return FileWriter.init(internal); |