| ... | ... | @@ -545,20 +545,76 @@ test fmtChar { |
| 545 | 545 | /// |
| 546 | 546 | /// Everything else is passed through unmodified. |
| 547 | 547 | pub fn stringEscape(bytes: []const u8, w: *Writer) Writer.Error!void { |
| 548 | _ = try stringEscapeCounting(bytes, w); |
| 549 | } |
| 550 | |
| 551 | pub fn stringEscapeCounting(bytes: []const u8, w: *Writer) Writer.Error!usize { |
| 552 | var n: usize = 0; |
| 548 | 553 | for (bytes) |byte| switch (byte) { |
| 549 | | '\t' => try w.writeAll("\\t"), |
| 550 | | '\n' => try w.writeAll("\\n"), |
| 551 | | '\r' => try w.writeAll("\\r"), |
| 552 | | '\\' => try w.writeAll("\\\\"), |
| 553 | | '"' => try w.writeAll("\\\""), |
| 554 | '\t' => { |
| 555 | try w.writeAll("\\t"); |
| 556 | n += 2; |
| 557 | }, |
| 558 | '\n' => { |
| 559 | try w.writeAll("\\n"); |
| 560 | n += 2; |
| 561 | }, |
| 562 | '\r' => { |
| 563 | try w.writeAll("\\r"); |
| 564 | n += 2; |
| 565 | }, |
| 566 | '\\' => { |
| 567 | try w.writeAll("\\\\"); |
| 568 | n += 2; |
| 569 | }, |
| 570 | '"' => { |
| 571 | try w.writeAll("\\\""); |
| 572 | n += 2; |
| 573 | }, |
| 554 | 574 | 0...8, 11, 12, 14...0x1f, 0x7f => { |
| 555 | 575 | try w.writeAll("\\x"); |
| 556 | 576 | try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }); |
| 577 | n += 4; |
| 578 | }, |
| 579 | else => { |
| 580 | try w.writeByte(byte); |
| 581 | n += 1; |
| 557 | 582 | }, |
| 558 | | else => try w.writeByte(byte), |
| 559 | 583 | }; |
| 584 | return n; |
| 560 | 585 | } |
| 561 | 586 | |
| 587 | pub const StringEscapeWriter = struct { |
| 588 | out: *Writer, |
| 589 | writer: Writer, |
| 590 | |
| 591 | pub fn init(out: *Writer, buffer: []u8) @This() { |
| 592 | return .{ |
| 593 | .out = out, |
| 594 | .writer = .{ |
| 595 | .vtable = &.{ .drain = @This().drain }, |
| 596 | .buffer = buffer, |
| 597 | }, |
| 598 | }; |
| 599 | } |
| 600 | |
| 601 | fn drain(w: *Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize { |
| 602 | const sew: *StringEscapeWriter = @alignCast(@fieldParentPtr("writer", w)); |
| 603 | const out = sew.out; |
| 604 | _ = try stringEscapeCounting(w.buffered(), out); |
| 605 | w.end = 0; |
| 606 | var n: usize = 0; |
| 607 | for (data[0 .. data.len - 1]) |bytes| { |
| 608 | n += try stringEscapeCounting(bytes, out); |
| 609 | } |
| 610 | const pattern = data[data.len - 1]; |
| 611 | for (0..splat) |_| { |
| 612 | n += try stringEscapeCounting(pattern, out); |
| 613 | } |
| 614 | return n; |
| 615 | } |
| 616 | }; |
| 617 | |
| 562 | 618 | /// Print as escaped contents of a single-quoted string. |
| 563 | 619 | pub fn charEscape(codepoint: u21, w: *Writer) Writer.Error!void { |
| 564 | 620 | switch (codepoint) { |