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 {
6262/// one has to specify *alignment* as well, as otherwise the digit following `:` is interpreted as *width*, not *fill*.
6363///
6464/// The *specifier* has several options for types:
65/// - `x` and `X`: output numeric value in hexadecimal notation
65/// - `x` and `X`: output numeric value in hexadecimal notation, or string in hexadecimal bytes
6666/// - `s`:
6767/// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination
6868/// - for slices of u8, print the entire slice as a string without zero-termination
69/// - `b64`: output string as standard base64
6970/// - `e`: output floating point value in scientific notation
7071/// - `d`: output numeric value in decimal notation
7172/// - `b`: output integer value in binary notation
lib/std/io/BufferedWriter.zig+28-28
......@@ -41,7 +41,7 @@ pub fn writer(bw: *BufferedWriter) Writer {
4141
4242const fixed_vtable: Writer.VTable = .{
4343 .writeSplat = fixed_writeSplat,
44 .writeFile = fixed_writeFile,
44 .writeFile = Writer.unimplemented_writeFile,
4545};
4646
4747/// Replaces the `BufferedWriter` with a new one that writes to `buffer` and
......@@ -74,6 +74,10 @@ pub fn flush(bw: *BufferedWriter) anyerror!void {
7474 bw.end = 0;
7575}
7676
77pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {
78 return bw.buffer[bw.end..];
79}
80
7781/// The `data` parameter is mutable because this function needs to mutate the
7882/// fields in order to handle partial writes from `Writer.VTable.writev`.
7983pub 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
9397 return passthru_writeSplat(bw, data, splat);
9498}
9599
100pub fn writev(bw: *BufferedWriter, data: []const []const u8) anyerror!usize {
101 return passthru_writeSplat(bw, data, 1);
102}
103
96104fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
97105 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
98106 const buffer = bw.buffer;
......@@ -523,23 +531,6 @@ pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOp
523531 }
524532}
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
543534pub fn alignBuffer(
544535 bw: *BufferedWriter,
545536 buffer: []const u8,
......@@ -775,19 +766,22 @@ pub fn printValue(
775766 },
776767 .slice => {
777768 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})");
779770 if (max_depth == 0) {
780771 return bw.writeAll("{ ... }");
781772 }
782 if (ptr_info.child == u8) {
783 if (actual_fmt[0] == 's') {
784 return alignBufferOptions(bw, value, options);
785 } else if (actual_fmt[0] == 'x') {
786 return printHex(bw, value, .lower);
787 } else if (actual_fmt[0] == 'X') {
788 return printHex(bw, value, .upper);
789 }
790 }
773 if (ptr_info.child == u8) switch (actual_fmt.len) {
774 1 => switch (actual_fmt[0]) {
775 's' => return alignBufferOptions(bw, value, options),
776 'x' => return printHex(bw, value, .lower),
777 'X' => return printHex(bw, value, .upper),
778 else => {},
779 },
780 3 => if (actual_fmt[0] == 'b' and actual_fmt[1] == '6' and actual_fmt[2] == '4') {
781 return printBase64(bw, value);
782 },
783 else => {},
784 };
791785 try bw.writeAll("{ ");
792786 for (value, 0..) |elem, i| {
793787 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
12891283 }
12901284}
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
12921292test "formatValue max_depth" {
12931293 const Vec2 = struct {
12941294 const SelfType = @This();