authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-18 23:32:25-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:26-07:00
logd87b59f5a59bbdc96c617ac90f2e1a3cd39bd7b9
treee660b69d8eb2a4f9ae00ea680cc53a430a62ecd1
parent8464efa5dd792de25ee8faa413c89f1daf8b5efb

std.fmt.format: support base 64 encoding


2 files changed, 30 insertions(+), 29 deletions(-)

lib/std/fmt.zig+2-1
...@@ -62,10 +62,11 @@ pub const Options = struct {...@@ -62,10 +62,11 @@ pub const Options = struct {
62/// one has to specify *alignment* as well, as otherwise the digit following `:` is interpreted as *width*, not *fill*.62/// one has to specify *alignment* as well, as otherwise the digit following `:` is interpreted as *width*, not *fill*.
63///63///
64/// The *specifier* has several options for types:64/// The *specifier* has several options for types:
65/// - `x` and `X`: output numeric value in hexadecimal notation65/// - `x` and `X`: output numeric value in hexadecimal notation, or string in hexadecimal bytes
66/// - `s`:66/// - `s`:
67/// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination67/// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination
68/// - for slices of u8, print the entire slice as a string without zero-termination68/// - for slices of u8, print the entire slice as a string without zero-termination
69/// - `b64`: output string as standard base64
69/// - `e`: output floating point value in scientific notation70/// - `e`: output floating point value in scientific notation
70/// - `d`: output numeric value in decimal notation71/// - `d`: output numeric value in decimal notation
71/// - `b`: output integer value in binary notation72/// - `b`: output integer value in binary notation
lib/std/io/BufferedWriter.zig+28-28
...@@ -41,7 +41,7 @@ pub fn writer(bw: *BufferedWriter) Writer {...@@ -41,7 +41,7 @@ pub fn writer(bw: *BufferedWriter) Writer {
4141
42const fixed_vtable: Writer.VTable = .{42const fixed_vtable: Writer.VTable = .{
43 .writeSplat = fixed_writeSplat,43 .writeSplat = fixed_writeSplat,
44 .writeFile = fixed_writeFile,44 .writeFile = Writer.unimplemented_writeFile,
45};45};
4646
47/// Replaces the `BufferedWriter` with a new one that writes to `buffer` and47/// Replaces the `BufferedWriter` with a new one that writes to `buffer` and
...@@ -74,6 +74,10 @@ pub fn flush(bw: *BufferedWriter) anyerror!void {...@@ -74,6 +74,10 @@ pub fn flush(bw: *BufferedWriter) anyerror!void {
74 bw.end = 0;74 bw.end = 0;
75}75}
7676
77pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {
78 return bw.buffer[bw.end..];
79}
80
77/// The `data` parameter is mutable because this function needs to mutate the81/// The `data` parameter is mutable because this function needs to mutate the
78/// fields in order to handle partial writes from `Writer.VTable.writev`.82/// fields in order to handle partial writes from `Writer.VTable.writev`.
79pub fn writevAll(bw: *BufferedWriter, data: [][]const u8) anyerror!void {83pub fn writevAll(bw: *BufferedWriter, data: [][]const u8) anyerror!void {
...@@ -93,6 +97,10 @@ pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) a...@@ -93,6 +97,10 @@ pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) a
93 return passthru_writeSplat(bw, data, splat);97 return passthru_writeSplat(bw, data, splat);
94}98}
9599
100pub fn writev(bw: *BufferedWriter, data: []const []const u8) anyerror!usize {
101 return passthru_writeSplat(bw, data, 1);
102}
103
96fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {104fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
97 const bw: *BufferedWriter = @alignCast(@ptrCast(context));105 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
98 const buffer = bw.buffer;106 const buffer = bw.buffer;
...@@ -523,23 +531,6 @@ pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOp...@@ -523,23 +531,6 @@ pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOp
523 }531 }
524}532}
525533
526fn fixed_writeFile(
527 context: *anyopaque,
528 file: std.fs.File,
529 offset: u64,
530 len: Writer.VTable.FileLen,
531 headers_and_trailers: []const []const u8,
532 headers_len: usize,
533) anyerror!usize {
534 _ = context;
535 _ = file;
536 _ = offset;
537 _ = len;
538 _ = headers_and_trailers;
539 _ = headers_len;
540 return error.Unimplemented;
541}
542
543pub fn alignBuffer(534pub fn alignBuffer(
544 bw: *BufferedWriter,535 bw: *BufferedWriter,
545 buffer: []const u8,536 buffer: []const u8,
...@@ -775,19 +766,22 @@ pub fn printValue(...@@ -775,19 +766,22 @@ pub fn printValue(
775 },766 },
776 .slice => {767 .slice => {
777 if (actual_fmt.len == 0)768 if (actual_fmt.len == 0)
778 @compileError("cannot format slice without a specifier (i.e. {s}, {x}, or {any})");769 @compileError("cannot format slice without a specifier (i.e. {s}, {x}, {b64}, or {any})");
779 if (max_depth == 0) {770 if (max_depth == 0) {
780 return bw.writeAll("{ ... }");771 return bw.writeAll("{ ... }");
781 }772 }
782 if (ptr_info.child == u8) {773 if (ptr_info.child == u8) switch (actual_fmt.len) {
783 if (actual_fmt[0] == 's') {774 1 => switch (actual_fmt[0]) {
784 return alignBufferOptions(bw, value, options);775 's' => return alignBufferOptions(bw, value, options),
785 } else if (actual_fmt[0] == 'x') {776 'x' => return printHex(bw, value, .lower),
786 return printHex(bw, value, .lower);777 'X' => return printHex(bw, value, .upper),
787 } else if (actual_fmt[0] == 'X') {778 else => {},
788 return printHex(bw, value, .upper);779 },
789 }780 3 => if (actual_fmt[0] == 'b' and actual_fmt[1] == '6' and actual_fmt[2] == '4') {
790 }781 return printBase64(bw, value);
782 },
783 else => {},
784 };
791 try bw.writeAll("{ ");785 try bw.writeAll("{ ");
792 for (value, 0..) |elem, i| {786 for (value, 0..) |elem, i| {
793 try printValue(bw, actual_fmt, options, elem, max_depth - 1);787 try printValue(bw, actual_fmt, options, elem, max_depth - 1);
...@@ -1289,6 +1283,12 @@ pub fn printHex(bw: *BufferedWriter, bytes: []const u8, case: std.fmt.Case) anye...@@ -1289,6 +1283,12 @@ pub fn printHex(bw: *BufferedWriter, bytes: []const u8, case: std.fmt.Case) anye
1289 }1283 }
1290}1284}
12911285
1286pub fn printBase64(bw: *BufferedWriter, bytes: []const u8) anyerror!void {
1287 var chunker = std.mem.window(u8, bytes, 3, 3);
1288 var temp: [5]u8 = undefined;
1289 while (chunker.next()) |chunk| try bw.writeAll(std.base64.standard.Encoder.encode(&temp, chunk));
1290}
1291
1292test "formatValue max_depth" {1292test "formatValue max_depth" {
1293 const Vec2 = struct {1293 const Vec2 = struct {
1294 const SelfType = @This();1294 const SelfType = @This();