authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-15 16:26:19-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:25-07:00
log5356f3a30748ba3504767b68e99d215d6aac839b
tree42669e6a9e800864bcd42eb71474c625047b1cf2
parentc2fc6b0b6cc0d5fa6eb6134ac16ba63c9a0059c4

migrate some std lib


12 files changed, 1170 insertions(+), 1289 deletions(-)

lib/std/Build.zig+6-4
......@@ -2766,8 +2766,9 @@ fn dumpBadDirnameHelp(
27662766 comptime msg: []const u8,
27672767 args: anytype,
27682768) anyerror!void {
2769 var w = debug.lockStdErr2();
2769 var buffered_writer = debug.lockStdErr2();
27702770 defer debug.unlockStdErr();
2771 const w = &buffered_writer;
27712772
27722773 const stderr = io.getStdErr();
27732774 try w.print(msg, args);
......@@ -2784,7 +2785,7 @@ fn dumpBadDirnameHelp(
27842785
27852786 if (asking_step) |as| {
27862787 tty_config.setColor(w, .red) catch {};
2787 try stderr.writer().print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
2788 try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
27882789 tty_config.setColor(w, .reset) catch {};
27892790
27902791 as.dump(stderr);
......@@ -2802,7 +2803,8 @@ pub fn dumpBadGetPathHelp(
28022803 src_builder: *Build,
28032804 asking_step: ?*Step,
28042805) anyerror!void {
2805 var w = stderr.unbufferedWriter();
2806 var buffered_writer = stderr.unbufferedWriter();
2807 const w = &buffered_writer;
28062808 try w.print(
28072809 \\getPath() was called on a GeneratedFile that wasn't built yet.
28082810 \\ source package path: {s}
......@@ -2821,7 +2823,7 @@ pub fn dumpBadGetPathHelp(
28212823 s.dump(stderr);
28222824 if (asking_step) |as| {
28232825 tty_config.setColor(w, .red) catch {};
2824 try stderr.writer().print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
2826 try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
28252827 tty_config.setColor(w, .reset) catch {};
28262828
28272829 as.dump(stderr);
lib/std/Build/Cache.zig+16-15
......@@ -1061,14 +1061,17 @@ pub const Manifest = struct {
10611061 }
10621062
10631063 fn addDepFileMaybePost(self: *Manifest, dir: fs.Dir, dep_file_basename: []const u8) !void {
1064 const dep_file_contents = try dir.readFileAlloc(self.cache.gpa, dep_file_basename, manifest_file_size_max);
1065 defer self.cache.gpa.free(dep_file_contents);
1064 const gpa = self.cache.gpa;
1065 const dep_file_contents = try dir.readFileAlloc(gpa, dep_file_basename, manifest_file_size_max);
1066 defer gpa.free(dep_file_contents);
10661067
1067 var error_buf = std.ArrayList(u8).init(self.cache.gpa);
1068 defer error_buf.deinit();
1068 var error_buf: std.ArrayListUnmanaged(u8) = .empty;
1069 defer error_buf.deinit(gpa);
10691070
1070 var it: DepTokenizer = .{ .bytes = dep_file_contents };
1071 var resolve_buf: std.ArrayListUnmanaged(u8) = .empty;
1072 defer resolve_buf.deinit(gpa);
10711073
1074 var it: DepTokenizer = .{ .bytes = dep_file_contents };
10721075 while (it.next()) |token| {
10731076 switch (token) {
10741077 // We don't care about targets, we only want the prereqs
......@@ -1078,16 +1081,14 @@ pub const Manifest = struct {
10781081 _ = try self.addFile(file_path, null);
10791082 } else try self.addFilePost(file_path),
10801083 .prereq_must_resolve => {
1081 var resolve_buf = std.ArrayList(u8).init(self.cache.gpa);
1082 defer resolve_buf.deinit();
1083
1084 try token.resolve(resolve_buf.writer());
1084 resolve_buf.clearRetainingCapacity();
1085 try token.resolve(gpa, &resolve_buf);
10851086 if (self.manifest_file == null) {
10861087 _ = try self.addFile(resolve_buf.items, null);
10871088 } else try self.addFilePost(resolve_buf.items);
10881089 },
10891090 else => |err| {
1090 try err.printError(error_buf.writer());
1091 try err.printError(gpa, &error_buf);
10911092 log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items });
10921093 return error.InvalidDepFile;
10931094 },
......@@ -1125,13 +1126,13 @@ pub const Manifest = struct {
11251126 if (self.manifest_dirty) {
11261127 self.manifest_dirty = false;
11271128
1128 var contents = std.ArrayList(u8).init(self.cache.gpa);
1129 defer contents.deinit();
1129 const gpa = self.cache.gpa;
1130 var contents: std.ArrayListUnmanaged(u8) = .empty;
1131 defer contents.deinit(gpa);
11301132
1131 const writer = contents.writer();
1132 try writer.writeAll(manifest_header ++ "\n");
1133 try contents.appendSlice(gpa, manifest_header ++ "\n");
11331134 for (self.files.keys()) |file| {
1134 try writer.print("{d} {d} {d} {x} {d} {s}\n", .{
1135 try contents.print(gpa, "{d} {d} {d} {x} {d} {s}\n", .{
11351136 file.stat.size,
11361137 file.stat.inode,
11371138 file.stat.mtime,
lib/std/Build/Cache/DepTokenizer.zig+38-151
......@@ -7,6 +7,7 @@ state: State = .lhs,
77const std = @import("std");
88const testing = std.testing;
99const assert = std.debug.assert;
10const Allocator = std.mem.Allocator;
1011
1112pub fn next(self: *Tokenizer) ?Token {
1213 var start = self.index;
......@@ -362,7 +363,7 @@ pub const Token = union(enum) {
362363 };
363364
364365 /// Resolve escapes in target or prereq. Only valid with .target_must_resolve or .prereq_must_resolve.
365 pub fn resolve(self: Token, writer: anytype) @TypeOf(writer).Error!void {
366 pub fn resolve(self: Token, gpa: Allocator, list: *std.ArrayListUnmanaged(u8)) error{OutOfMemory}!void {
366367 switch (self) {
367368 .target_must_resolve => |bytes| {
368369 var state: enum { start, escape, dollar } = .start;
......@@ -372,27 +373,27 @@ pub const Token = union(enum) {
372373 switch (c) {
373374 '\\' => state = .escape,
374375 '$' => state = .dollar,
375 else => try writer.writeByte(c),
376 else => try list.append(gpa, c),
376377 }
377378 },
378379 .escape => {
379380 switch (c) {
380381 ' ', '#', '\\' => {},
381382 '$' => {
382 try writer.writeByte('\\');
383 try list.append(gpa, '\\');
383384 state = .dollar;
384385 continue;
385386 },
386 else => try writer.writeByte('\\'),
387 else => try list.append(gpa, '\\'),
387388 }
388 try writer.writeByte(c);
389 try list.append(gpa, c);
389390 state = .start;
390391 },
391392 .dollar => {
392 try writer.writeByte('$');
393 try list.append(gpa, '$');
393394 switch (c) {
394395 '$' => {},
395 else => try writer.writeByte(c),
396 else => try list.append(gpa, c),
396397 }
397398 state = .start;
398399 },
......@@ -406,19 +407,19 @@ pub const Token = union(enum) {
406407 .start => {
407408 switch (c) {
408409 '\\' => state = .escape,
409 else => try writer.writeByte(c),
410 else => try list.append(gpa, c),
410411 }
411412 },
412413 .escape => {
413414 switch (c) {
414415 ' ' => {},
415416 '\\' => {
416 try writer.writeByte(c);
417 try list.append(gpa, c);
417418 continue;
418419 },
419 else => try writer.writeByte('\\'),
420 else => try list.append(gpa, '\\'),
420421 }
421 try writer.writeByte(c);
422 try list.append(gpa, c);
422423 state = .start;
423424 },
424425 }
......@@ -428,20 +429,20 @@ pub const Token = union(enum) {
428429 }
429430 }
430431
431 pub fn printError(self: Token, writer: anytype) @TypeOf(writer).Error!void {
432 pub fn printError(self: Token, gpa: Allocator, list: *std.ArrayListUnmanaged(u8)) error{OutOfMemory}!void {
432433 switch (self) {
433434 .target, .target_must_resolve, .prereq, .prereq_must_resolve => unreachable, // not an error
434435 .incomplete_quoted_prerequisite,
435436 .incomplete_target,
436437 => |index_and_bytes| {
437 try writer.print("{s} '", .{self.errStr()});
438 try list.print("{s} '", .{self.errStr()});
438439 if (self == .incomplete_target) {
439440 const tmp = Token{ .target_must_resolve = index_and_bytes.bytes };
440 try tmp.resolve(writer);
441 try tmp.resolve(gpa, list);
441442 } else {
442 try printCharValues(writer, index_and_bytes.bytes);
443 try printCharValues(gpa, list, index_and_bytes.bytes);
443444 }
444 try writer.print("' at position {d}", .{index_and_bytes.index});
445 try list.print(gpa, "' at position {d}", .{index_and_bytes.index});
445446 },
446447 .invalid_target,
447448 .bad_target_escape,
......@@ -450,9 +451,9 @@ pub const Token = union(enum) {
450451 .incomplete_escape,
451452 .expected_colon,
452453 => |index_and_char| {
453 try writer.writeAll("illegal char ");
454 try printUnderstandableChar(writer, index_and_char.char);
455 try writer.print(" at position {d}: {s}", .{ index_and_char.index, self.errStr() });
454 try list.appendSlice("illegal char ");
455 try printUnderstandableChar(gpa, list, index_and_char.char);
456 try list.print(gpa, " at position {d}: {s}", .{ index_and_char.index, self.errStr() });
456457 },
457458 }
458459 }
......@@ -1026,41 +1027,41 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
10261027 defer arena_allocator.deinit();
10271028
10281029 var it: Tokenizer = .{ .bytes = input };
1029 var buffer = std.ArrayList(u8).init(arena);
1030 var resolve_buf = std.ArrayList(u8).init(arena);
1030 var buffer: std.ArrayListUnmanaged(u8) = .empty;
1031 var resolve_buf: std.ArrayListUnmanaged(u8) = .empty;
10311032 var i: usize = 0;
10321033 while (it.next()) |token| {
1033 if (i != 0) try buffer.appendSlice("\n");
1034 if (i != 0) try buffer.appendSlice(arena, "\n");
10341035 switch (token) {
10351036 .target, .prereq => |bytes| {
1036 try buffer.appendSlice(@tagName(token));
1037 try buffer.appendSlice(" = {");
1037 try buffer.appendSlice(arena, @tagName(token));
1038 try buffer.appendSlice(arena, " = {");
10381039 for (bytes) |b| {
1039 try buffer.append(printable_char_tab[b]);
1040 try buffer.append(arena, printable_char_tab[b]);
10401041 }
1041 try buffer.appendSlice("}");
1042 try buffer.appendSlice(arena, "}");
10421043 },
10431044 .target_must_resolve => {
1044 try buffer.appendSlice("target = {");
1045 try token.resolve(resolve_buf.writer());
1045 try buffer.appendSlice(arena, "target = {");
1046 try token.resolve(arena, &resolve_buf);
10461047 for (resolve_buf.items) |b| {
1047 try buffer.append(printable_char_tab[b]);
1048 try buffer.append(arena, printable_char_tab[b]);
10481049 }
10491050 resolve_buf.items.len = 0;
1050 try buffer.appendSlice("}");
1051 try buffer.appendSlice(arena, "}");
10511052 },
10521053 .prereq_must_resolve => {
1053 try buffer.appendSlice("prereq = {");
1054 try token.resolve(resolve_buf.writer());
1054 try buffer.appendSlice(arena, "prereq = {");
1055 try token.resolve(arena, &resolve_buf);
10551056 for (resolve_buf.items) |b| {
1056 try buffer.append(printable_char_tab[b]);
1057 try buffer.append(arena, printable_char_tab[b]);
10571058 }
10581059 resolve_buf.items.len = 0;
1059 try buffer.appendSlice("}");
1060 try buffer.appendSlice(arena, "}");
10601061 },
10611062 else => {
1062 try buffer.appendSlice("ERROR: ");
1063 try token.printError(buffer.writer());
1063 try buffer.appendSlice(arena, "ERROR: ");
1064 try token.printError(arena, &buffer);
10641065 break;
10651066 },
10661067 }
......@@ -1072,121 +1073,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
10721073 return;
10731074 }
10741075
1075 const out = std.io.getStdErr().writer();
1076
1077 try out.writeAll("\n");
1078 try printSection(out, "<<<< input", input);
1079 try printSection(out, "==== expect", expect);
1080 try printSection(out, ">>>> got", buffer.items);
1081 try printRuler(out);
1082
1083 try testing.expect(false);
1084}
1085
1086fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void {
1087 try printLabel(out, label, bytes);
1088 try hexDump(out, bytes);
1089 try printRuler(out);
1090 try out.writeAll(bytes);
1091 try out.writeAll("\n");
1092}
1093
1094fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void {
1095 var buf: [80]u8 = undefined;
1096 const text = try std.fmt.bufPrint(buf[0..], "{s} {d} bytes ", .{ label, bytes.len });
1097 try out.writeAll(text);
1098 var i: usize = text.len;
1099 const end = 79;
1100 while (i < end) : (i += 1) {
1101 try out.writeAll(&[_]u8{label[0]});
1102 }
1103 try out.writeAll("\n");
1104}
1105
1106fn printRuler(out: anytype) !void {
1107 var i: usize = 0;
1108 const end = 79;
1109 while (i < end) : (i += 1) {
1110 try out.writeAll("-");
1111 }
1112 try out.writeAll("\n");
1113}
1114
1115fn hexDump(out: anytype, bytes: []const u8) !void {
1116 const n16 = bytes.len >> 4;
1117 var line: usize = 0;
1118 var offset: usize = 0;
1119 while (line < n16) : (line += 1) {
1120 try hexDump16(out, offset, bytes[offset..][0..16]);
1121 offset += 16;
1122 }
1123
1124 const n = bytes.len & 0x0f;
1125 if (n > 0) {
1126 try printDecValue(out, offset, 8);
1127 try out.writeAll(":");
1128 try out.writeAll(" ");
1129 const end1 = @min(offset + n, offset + 8);
1130 for (bytes[offset..end1]) |b| {
1131 try out.writeAll(" ");
1132 try printHexValue(out, b, 2);
1133 }
1134 const end2 = offset + n;
1135 if (end2 > end1) {
1136 try out.writeAll(" ");
1137 for (bytes[end1..end2]) |b| {
1138 try out.writeAll(" ");
1139 try printHexValue(out, b, 2);
1140 }
1141 }
1142 const short = 16 - n;
1143 var i: usize = 0;
1144 while (i < short) : (i += 1) {
1145 try out.writeAll(" ");
1146 }
1147 if (end2 > end1) {
1148 try out.writeAll(" |");
1149 } else {
1150 try out.writeAll(" |");
1151 }
1152 try printCharValues(out, bytes[offset..end2]);
1153 try out.writeAll("|\n");
1154 offset += n;
1155 }
1156
1157 try printDecValue(out, offset, 8);
1158 try out.writeAll(":");
1159 try out.writeAll("\n");
1160}
1161
1162fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void {
1163 try printDecValue(out, offset, 8);
1164 try out.writeAll(":");
1165 try out.writeAll(" ");
1166 for (bytes[0..8]) |b| {
1167 try out.writeAll(" ");
1168 try printHexValue(out, b, 2);
1169 }
1170 try out.writeAll(" ");
1171 for (bytes[8..16]) |b| {
1172 try out.writeAll(" ");
1173 try printHexValue(out, b, 2);
1174 }
1175 try out.writeAll(" |");
1176 try printCharValues(out, bytes);
1177 try out.writeAll("|\n");
1178}
1179
1180fn printDecValue(out: anytype, value: u64, width: u8) !void {
1181 var buffer: [20]u8 = undefined;
1182 const len = std.fmt.formatIntBuf(buffer[0..], value, 10, .lower, .{ .width = width, .fill = '0' });
1183 try out.writeAll(buffer[0..len]);
1184}
1185
1186fn printHexValue(out: anytype, value: u64, width: u8) !void {
1187 var buffer: [16]u8 = undefined;
1188 const len = std.fmt.formatIntBuf(buffer[0..], value, 16, .lower, .{ .width = width, .fill = '0' });
1189 try out.writeAll(buffer[0..len]);
1076 try testing.expectEqualStrings(expect, buffer.items);
11901077}
11911078
11921079fn printCharValues(out: anytype, bytes: []const u8) !void {
lib/std/array_list.zig+3-28
......@@ -976,37 +976,12 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
976976 @memcpy(self.items[old_len..][0..items.len], items);
977977 }
978978
979 pub const WriterContext = struct {
980 self: *Self,
981 allocator: Allocator,
982 };
983
984 pub const Writer = if (T != u8)
985 @compileError("The Writer interface is only defined for ArrayList(u8) " ++
986 "but the given type is ArrayList(" ++ @typeName(T) ++ ")")
987 else
988 std.io.Writer(WriterContext, Allocator.Error, appendWrite);
989
990 /// Initializes a Writer which will append to the list.
991 pub fn writer(self: *Self, gpa: Allocator) Writer {
992 return .{ .context = .{ .self = self, .allocator = gpa } };
993 }
994
995 /// Same as `append` except it returns the number of bytes written,
996 /// which is always the same as `m.len`. The purpose of this function
997 /// existing is to match `std.io.Writer` API.
998 /// Invalidates element pointers if additional memory is needed.
999 fn appendWrite(context: WriterContext, m: []const u8) Allocator.Error!usize {
1000 try context.self.appendSlice(context.allocator, m);
1001 return m.len;
1002 }
1003
1004979 pub fn print(self: *Self, gpa: Allocator, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {
1005980 comptime assert(T == u8);
1006981 try self.ensureUnusedCapacity(gpa, fmt.len);
1007 var alw: std.io.ArrayListWriter = undefined;
1008 const bw = alw.fromOwned(gpa, self);
1009 defer self.* = alw.toOwned();
982 var aw: std.io.AllocatingWriter = undefined;
983 const bw = aw.fromArrayList(gpa, self);
984 defer self.* = aw.toArrayList();
1010985 bw.print(fmt, args) catch return error.OutOfMemory;
1011986 }
1012987
lib/std/io.zig+2-2
......@@ -301,7 +301,7 @@ pub const AnyWriter = Writer;
301301pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
302302
303303pub const BufferedWriter = @import("io/BufferedWriter.zig");
304pub const ArrayListWriter = @import("io/ArrayListWriter.zig");
304pub const AllocatingWriter = @import("io/AllocatingWriter.zig");
305305
306306pub const BufferedReader = @import("io/buffered_reader.zig").BufferedReader;
307307pub const bufferedReader = @import("io/buffered_reader.zig").bufferedReader;
......@@ -784,7 +784,7 @@ test {
784784 _ = Writer;
785785 _ = CountingWriter;
786786 _ = FixedBufferStream;
787 _ = ArrayListWriter;
787 _ = AllocatingWriter;
788788 _ = @import("io/bit_reader.zig");
789789 _ = @import("io/bit_writer.zig");
790790 _ = @import("io/buffered_atomic_file.zig");
lib/std/io/AllocatingWriter.zig created+169
......@@ -0,0 +1,169 @@
1//! TODO rename to AllocatingWriter.
2//! While it is possible to use `std.ArrayList` as the underlying writer when
3//! using `std.io.BufferedWriter` by populating the `std.io.Writer` interface
4//! and then using an empty buffer, it means that every use of
5//! `std.io.BufferedWriter` will go through the vtable, including for
6//! functions such as `writeByte`. This API instead maintains
7//! `std.io.BufferedWriter` state such that it writes to the unused capacity of
8//! an array list, filling it up completely before making a call through the
9//! vtable, causing a resize. Consequently, the same, optimized, non-generic
10//! machine code that uses `std.io.BufferedReader`, such as formatted printing,
11//! takes the hot paths when using this API.
12
13const std = @import("../std.zig");
14const AllocatingWriter = @This();
15const assert = std.debug.assert;
16
17/// This is missing the data stored in `buffered_writer`. See `getWritten` for
18/// returning a slice that includes both.
19written: []u8,
20allocator: std.mem.Allocator,
21buffered_writer: std.io.BufferedWriter,
22
23const vtable: std.io.Writer.VTable = .{
24 .writev = writev,
25 .writeFile = writeFile,
26};
27
28/// Sets the `AllocatingWriter` to an empty state.
29pub fn init(aw: *AllocatingWriter, allocator: std.mem.Allocator) *std.io.BufferedWriter {
30 aw.* = .{
31 .written = &.{},
32 .allocator = allocator,
33 .buffered_writer = .{
34 .unbuffered_writer = .{
35 .context = aw,
36 .vtable = &vtable,
37 },
38 .buffer = &.{},
39 },
40 };
41 return &aw.buffered_writer;
42}
43
44/// Replaces `array_list` with empty, taking ownership of the memory.
45pub fn fromArrayList(
46 aw: *AllocatingWriter,
47 allocator: std.mem.Allocator,
48 array_list: *std.ArrayListUnmanaged(u8),
49) *std.io.BufferedWriter {
50 aw.* = .{
51 .written = array_list.items,
52 .allocator = allocator,
53 .buffered_writer = .{
54 .unbuffered_writer = .{
55 .context = aw,
56 .vtable = &vtable,
57 },
58 .buffer = array_list.unusedCapacitySlice(),
59 },
60 };
61 array_list.* = .empty;
62 return &aw.buffered_writer;
63}
64
65/// Returns an array list that takes ownership of the allocated memory.
66/// Resets the `AllocatingWriter` to an empty state.
67pub fn toArrayList(aw: *AllocatingWriter) std.ArrayListUnmanaged(u8) {
68 const bw = &aw.buffered_writer;
69 const written = aw.written;
70 const result: std.ArrayListUnmanaged(u8) = .{
71 .items = written.ptr[0 .. written.len + bw.end],
72 .capacity = written.len + bw.buffer.len,
73 };
74 aw.written = &.{};
75 bw.buffer = &.{};
76 bw.end = 0;
77 return result;
78}
79
80fn setArrayList(aw: *AllocatingWriter, list: std.ArrayListUnmanaged(u8)) void {
81 aw.written = list.items;
82 aw.buffered_writer.buffer = list.unusedCapacitySlice();
83}
84
85pub fn getWritten(aw: *AllocatingWriter) []u8 {
86 const bw = &aw.buffered_writer;
87 const end = aw.buffered_writer.end;
88 const result = aw.written.ptr[0 .. aw.written.len + end];
89 bw.buffer = bw.buffer[end..];
90 bw.end = 0;
91 return result;
92}
93
94pub fn clearRetainingCapacity(aw: *AllocatingWriter) void {
95 const bw = &aw.buffered_writer;
96 bw.buffer = aw.written.ptr[0 .. aw.written.len + bw.buffer.len];
97 bw.end = 0;
98 aw.written.len = 0;
99}
100
101fn writev(context: *anyopaque, data: []const []const u8) anyerror!usize {
102 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));
103 const start_len = aw.written.len;
104 const bw = &aw.buffered_writer;
105 assert(data[0].ptr == aw.written.ptr + start_len);
106 var list: std.ArrayListUnmanaged(u8) = .{
107 .items = aw.written.ptr[0 .. start_len + data[0].len],
108 .capacity = start_len + bw.buffer.len,
109 };
110 defer setArrayList(aw, list);
111 const rest = data[1..];
112 var new_capacity: usize = list.capacity;
113 for (rest) |bytes| new_capacity += bytes.len;
114 try list.ensureTotalCapacity(aw.allocator, new_capacity + 1);
115 for (rest) |bytes| list.appendSliceAssumeCapacity(bytes);
116 aw.written = list.items;
117 bw.buffer = list.unusedCapacitySlice();
118 return list.items.len - start_len;
119}
120
121fn writeFile(
122 context: *anyopaque,
123 file: std.fs.File,
124 offset: u64,
125 len: std.io.Writer.VTable.FileLen,
126 headers_and_trailers_full: []const []const u8,
127 headers_len_full: usize,
128) anyerror!usize {
129 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));
130 const gpa = aw.allocator;
131 var list = aw.toArrayList();
132 defer setArrayList(aw, list);
133 const start_len = list.items.len;
134 const headers_and_trailers, const headers_len = if (headers_len_full >= 1) b: {
135 assert(headers_and_trailers_full[0].ptr == list.items.ptr + start_len);
136 list.items.len += headers_and_trailers_full[0].len;
137 break :b .{ headers_and_trailers_full[1..], headers_len_full - 1 };
138 } else .{ headers_and_trailers_full, headers_len_full };
139 const trailers = headers_and_trailers[headers_len..];
140 if (len == .entire_file) {
141 var new_capacity: usize = list.capacity + std.atomic.cache_line;
142 for (headers_and_trailers) |bytes| new_capacity += bytes.len;
143 try list.ensureTotalCapacity(gpa, new_capacity);
144 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
145 const dest = list.items.ptr[list.items.len..list.capacity];
146 const n = try file.pread(dest, offset);
147 if (n == 0) {
148 new_capacity = list.capacity;
149 for (trailers) |bytes| new_capacity += bytes.len;
150 try list.ensureTotalCapacity(gpa, new_capacity);
151 for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes);
152 return list.items.len - start_len;
153 }
154 list.items.len += n;
155 return list.items.len - start_len;
156 }
157 var new_capacity: usize = list.capacity + len.int();
158 for (headers_and_trailers) |bytes| new_capacity += bytes.len;
159 try list.ensureTotalCapacity(gpa, new_capacity);
160 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
161 const dest = list.items.ptr[list.items.len..][0..len.int()];
162 const n = try file.pread(dest, offset);
163 list.items.len += n;
164 if (n < dest.len) {
165 return list.items.len - start_len;
166 }
167 for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes);
168 return list.items.len - start_len;
169}
lib/std/io/ArrayListWriter.zig deleted-127
......@@ -1,127 +0,0 @@
1//! The straightforward way to use `std.ArrayList` as the underlying writer
2//! when using `std.io.BufferedWriter` is to populate the `std.io.Writer`
3//! interface and then use an empty buffer. However, this means that every use
4//! of `std.io.BufferedWriter` will go through the vtable, including for
5//! functions such as `writeByte`. This API instead maintains
6//! `std.io.BufferedWriter` state such that it writes to the unused capacity of
7//! the array list, filling it up completely before making a call through the
8//! vtable, causing a resize. Consequently, the same, optimized, non-generic
9//! machine code that uses `std.io.BufferedReader`, such as formatted printing,
10//! is also used when the underlying writer is backed by `std.ArrayList`.
11
12const std = @import("../std.zig");
13const ArrayListWriter = @This();
14const assert = std.debug.assert;
15
16items: []u8,
17allocator: std.mem.Allocator,
18buffered_writer: std.io.BufferedWriter,
19
20/// Replaces `array_list` with empty, taking ownership of the memory.
21pub fn fromOwned(
22 alw: *ArrayListWriter,
23 allocator: std.mem.Allocator,
24 array_list: *std.ArrayListUnmanaged(u8),
25) *std.io.BufferedWriter {
26 alw.* = .{
27 .allocated_slice = array_list.items,
28 .allocator = allocator,
29 .buffered_writer = .{
30 .unbuffered_writer = .{
31 .context = alw,
32 .vtable = &.{
33 .writev = writev,
34 .writeFile = writeFile,
35 },
36 },
37 .buffer = array_list.unusedCapacitySlice(),
38 },
39 };
40 array_list.* = .empty;
41 return &alw.buffered_writer;
42}
43
44/// Returns the memory back that was borrowed with `fromOwned`.
45pub fn toOwned(alw: *ArrayListWriter) std.ArrayListUnmanaged(u8) {
46 const end = alw.buffered_writer.end;
47 const result: std.ArrayListUnmanaged(u8) = .{
48 .items = alw.items.ptr[0 .. alw.items.len + end],
49 .capacity = alw.buffered_writer.buffer.len - end,
50 };
51 alw.* = undefined;
52 return result;
53}
54
55fn writev(context: *anyopaque, data: []const []const u8) anyerror!usize {
56 const alw: *ArrayListWriter = @alignCast(@ptrCast(context));
57 const start_len = alw.items.len;
58 const bw = &alw.buffered_writer;
59 assert(data[0].ptr == alw.items.ptr + start_len);
60 const bw_end = data[0].len;
61 var list: std.ArrayListUnmanaged(u8) = .{
62 .items = alw.items.ptr[0 .. start_len + bw_end],
63 .capacity = bw.buffer.len - bw_end,
64 };
65 const rest = data[1..];
66 var new_capacity: usize = list.capacity;
67 for (rest) |bytes| new_capacity += bytes.len;
68 try list.ensureTotalCapacity(alw.allocator, new_capacity + 1);
69 for (rest) |bytes| list.appendSliceAssumeCapacity(bytes);
70 alw.items = list.items;
71 bw.buffer = list.unusedCapacitySlice();
72 return list.items.len - start_len;
73}
74
75fn writeFile(
76 context: *anyopaque,
77 file: std.fs.File,
78 offset: u64,
79 len: std.io.Writer.VTable.FileLen,
80 headers_and_trailers_full: []const []const u8,
81 headers_len_full: usize,
82) anyerror!usize {
83 const alw: *ArrayListWriter = @alignCast(@ptrCast(context));
84 const list = alw.array_list;
85 const bw = &alw.buffered_writer;
86 const start_len = list.items.len;
87 const headers_and_trailers, const headers_len = if (headers_len_full >= 1) b: {
88 assert(headers_and_trailers_full[0].ptr == list.items.ptr + start_len);
89 list.items.len += headers_and_trailers_full[0].len;
90 break :b .{ headers_and_trailers_full[1..], headers_len_full - 1 };
91 } else .{ headers_and_trailers_full, headers_len_full };
92 const gpa = alw.allocator;
93 const trailers = headers_and_trailers[headers_len..];
94 if (len == .entire_file) {
95 var new_capacity: usize = list.capacity + std.atomic.cache_line;
96 for (headers_and_trailers) |bytes| new_capacity += bytes.len;
97 try list.ensureTotalCapacity(gpa, new_capacity);
98 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
99 const dest = list.items.ptr[list.items.len..list.capacity];
100 const n = try file.pread(dest, offset);
101 if (n == 0) {
102 new_capacity = list.capacity;
103 for (trailers) |bytes| new_capacity += bytes.len;
104 try list.ensureTotalCapacity(gpa, new_capacity);
105 for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes);
106 bw.buffer = list.unusedCapacitySlice();
107 return list.items.len - start_len;
108 }
109 list.items.len += n;
110 bw.buffer = list.unusedCapacitySlice();
111 return list.items.len - start_len;
112 }
113 var new_capacity: usize = list.capacity + len.int();
114 for (headers_and_trailers) |bytes| new_capacity += bytes.len;
115 try list.ensureTotalCapacity(gpa, new_capacity);
116 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
117 const dest = list.items.ptr[list.items.len..][0..len.int()];
118 const n = try file.pread(dest, offset);
119 list.items.len += n;
120 if (n < dest.len) {
121 bw.buffer = list.unusedCapacitySlice();
122 return list.items.len - start_len;
123 }
124 for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes);
125 bw.buffer = list.unusedCapacitySlice();
126 return list.items.len - start_len;
127}
lib/std/io/BufferedWriter.zig+5
......@@ -19,6 +19,9 @@ end: usize = 0,
1919/// vectors through the underlying write calls as possible.
2020pub const max_buffers_len = 8;
2121
22/// Although `BufferedWriter` can easily satisfy the `Writer` interface, it's
23/// generally more practical to pass a `BufferedWriter` instance itself around,
24/// since it will result in fewer calls across vtable boundaries.
2225pub fn writer(bw: *BufferedWriter) Writer {
2326 return .{
2427 .context = bw,
......@@ -212,6 +215,7 @@ pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize {
212215
213216 const new_end = end + n;
214217 if (new_end <= buffer.len) {
218 @branchHint(.likely);
215219 @memset(buffer[end..][0..n], byte);
216220 bw.end = new_end;
217221 return n;
......@@ -226,6 +230,7 @@ pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize {
226230 bw.end = remainder.len;
227231 return 0;
228232 }
233 assert(bw.buffer.ptr == buffer.ptr); // TODO this is not a valid assertion
229234 @memset(buffer[0..n], byte);
230235 bw.end = n;
231236 return n;
lib/std/io/tty.zig+2-7
......@@ -71,12 +71,7 @@ pub const Config = union(enum) {
7171 reset_attributes: u16,
7272 };
7373
74 pub fn setColor(
75 conf: Config,
76 writer: anytype,
77 color: Color,
78 ) (@typeInfo(@TypeOf(writer.writeAll(""))).error_union.error_set ||
79 windows.SetConsoleTextAttributeError)!void {
74 pub fn setColor(conf: Config, bw: *std.io.BufferedWriter, color: Color) anyerror!void {
8075 nosuspend switch (conf) {
8176 .no_color => return,
8277 .escape_codes => {
......@@ -101,7 +96,7 @@ pub const Config = union(enum) {
10196 .dim => "\x1b[2m",
10297 .reset => "\x1b[0m",
10398 };
104 try writer.writeAll(color_string);
99 try bw.writeAll(color_string);
105100 },
106101 .windows_api => |ctx| if (native_os == .windows) {
107102 const attributes = switch (color) {
lib/std/process/Child.zig+2-1
......@@ -1004,7 +1004,8 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
10041004
10051005fn writeIntFd(fd: i32, value: ErrInt) !void {
10061006 const file: File = .{ .handle = fd };
1007 file.writer().writeInt(u64, @intCast(value), .little) catch return error.SystemResources;
1007 var bw = file.unbufferedWriter();
1008 bw.writeInt(u64, @intCast(value), .little) catch return error.SystemResources;
10081009}
10091010
10101011fn readIntFd(fd: i32) !ErrInt {
lib/std/zig.zig+12-12
......@@ -475,37 +475,37 @@ pub fn stringEscape(
475475 bytes: []const u8,
476476 comptime f: []const u8,
477477 options: std.fmt.FormatOptions,
478 writer: anytype,
478 bw: *std.io.BufferedWriter,
479479) !void {
480480 _ = options;
481481 for (bytes) |byte| switch (byte) {
482 '\n' => try writer.writeAll("\\n"),
483 '\r' => try writer.writeAll("\\r"),
484 '\t' => try writer.writeAll("\\t"),
485 '\\' => try writer.writeAll("\\\\"),
482 '\n' => try bw.writeAll("\\n"),
483 '\r' => try bw.writeAll("\\r"),
484 '\t' => try bw.writeAll("\\t"),
485 '\\' => try bw.writeAll("\\\\"),
486486 '"' => {
487487 if (f.len == 1 and f[0] == '\'') {
488 try writer.writeByte('"');
488 try bw.writeByte('"');
489489 } else if (f.len == 0) {
490 try writer.writeAll("\\\"");
490 try bw.writeAll("\\\"");
491491 } else {
492492 @compileError("expected {} or {'}, found {" ++ f ++ "}");
493493 }
494494 },
495495 '\'' => {
496496 if (f.len == 1 and f[0] == '\'') {
497 try writer.writeAll("\\'");
497 try bw.writeAll("\\'");
498498 } else if (f.len == 0) {
499 try writer.writeByte('\'');
499 try bw.writeByte('\'');
500500 } else {
501501 @compileError("expected {} or {'}, found {" ++ f ++ "}");
502502 }
503503 },
504 ' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte),
504 ' ', '!', '#'...'&', '('...'[', ']'...'~' => try bw.writeByte(byte),
505505 // Use hex escapes for rest any unprintable characters.
506506 else => {
507 try writer.writeAll("\\x");
508 try std.fmt.formatInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }, writer);
507 try bw.writeAll("\\x");
508 try bw.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' });
509509 },
510510 };
511511}
lib/std/zon/stringify.zig+915-942
......@@ -40,15 +40,12 @@ pub const SerializeOptions = struct {
4040/// Serialize the given value as ZON.
4141///
4242/// It is asserted at comptime that `@TypeOf(val)` is not a recursive type.
43pub fn serialize(
44 val: anytype,
45 options: SerializeOptions,
46 writer: anytype,
47) @TypeOf(writer).Error!void {
48 var sz = serializer(writer, .{
49 .whitespace = options.whitespace,
50 });
51 try sz.value(val, .{
43pub fn serialize(val: anytype, options: SerializeOptions, writer: *std.io.BufferedWriter) anyerror!void {
44 var s: Serializer = .{
45 .writer = writer,
46 .options = .{ .whitespace = options.whitespace },
47 };
48 try s.value(val, .{
5249 .emit_codepoint_literals = options.emit_codepoint_literals,
5350 .emit_strings_as_containers = options.emit_strings_as_containers,
5451 .emit_default_optional_fields = options.emit_default_optional_fields,
......@@ -62,13 +59,14 @@ pub fn serialize(
6259pub fn serializeMaxDepth(
6360 val: anytype,
6461 options: SerializeOptions,
65 writer: anytype,
62 writer: *std.io.BufferedWriter,
6663 depth: usize,
67) (@TypeOf(writer).Error || error{ExceededMaxDepth})!void {
68 var sz = serializer(writer, .{
69 .whitespace = options.whitespace,
70 });
71 try sz.valueMaxDepth(val, .{
64) anyerror!void {
65 var s: Serializer = .{
66 .writer = writer,
67 .options = .{ .whitespace = options.whitespace },
68 };
69 try s.valueMaxDepth(val, .{
7270 .emit_codepoint_literals = options.emit_codepoint_literals,
7371 .emit_strings_as_containers = options.emit_strings_as_containers,
7472 .emit_default_optional_fields = options.emit_default_optional_fields,
......@@ -81,44 +79,45 @@ pub fn serializeMaxDepth(
8179pub fn serializeArbitraryDepth(
8280 val: anytype,
8381 options: SerializeOptions,
84 writer: anytype,
85) @TypeOf(writer).Error!void {
86 var sz = serializer(writer, .{
87 .whitespace = options.whitespace,
88 });
89 try sz.valueArbitraryDepth(val, .{
82 writer: *std.io.BufferedWriter,
83) anyerror!void {
84 var s: Serializer = .{
85 .writer = writer,
86 .options = .{ .whitespace = options.whitespace },
87 };
88 try s.valueArbitraryDepth(val, .{
9089 .emit_codepoint_literals = options.emit_codepoint_literals,
9190 .emit_strings_as_containers = options.emit_strings_as_containers,
9291 .emit_default_optional_fields = options.emit_default_optional_fields,
9392 });
9493}
9594
96fn typeIsRecursive(comptime T: type) bool {
97 return comptime typeIsRecursiveImpl(T, &.{});
95inline fn typeIsRecursive(comptime T: type) bool {
96 return comptime typeIsRecursiveInner(T, &.{});
9897}
9998
100fn typeIsRecursiveImpl(comptime T: type, comptime prev_visited: []const type) bool {
99fn typeIsRecursiveInner(comptime T: type, comptime prev_visited: []const type) bool {
101100 for (prev_visited) |V| {
102101 if (V == T) return true;
103102 }
104103 const visited = prev_visited ++ .{T};
105104
106105 return switch (@typeInfo(T)) {
107 .pointer => |pointer| typeIsRecursiveImpl(pointer.child, visited),
108 .optional => |optional| typeIsRecursiveImpl(optional.child, visited),
109 .array => |array| typeIsRecursiveImpl(array.child, visited),
110 .vector => |vector| typeIsRecursiveImpl(vector.child, visited),
106 .pointer => |pointer| typeIsRecursiveInner(pointer.child, visited),
107 .optional => |optional| typeIsRecursiveInner(optional.child, visited),
108 .array => |array| typeIsRecursiveInner(array.child, visited),
109 .vector => |vector| typeIsRecursiveInner(vector.child, visited),
111110 .@"struct" => |@"struct"| for (@"struct".fields) |field| {
112 if (typeIsRecursiveImpl(field.type, visited)) break true;
111 if (typeIsRecursiveInner(field.type, visited)) break true;
113112 } else false,
114113 .@"union" => |@"union"| inline for (@"union".fields) |field| {
115 if (typeIsRecursiveImpl(field.type, visited)) break true;
114 if (typeIsRecursiveInner(field.type, visited)) break true;
116115 } else false,
117116 else => false,
118117 };
119118}
120119
121fn canSerializeType(T: type) bool {
120inline fn canSerializeType(T: type) bool {
122121 comptime return canSerializeTypeInner(T, &.{}, false);
123122}
124123
......@@ -343,12 +342,6 @@ test "std.zon checkValueDepth" {
343342 try expectValueDepthEquals(3, @as([]const []const u8, &.{&.{ 1, 2, 3 }}));
344343}
345344
346/// Options for `Serializer`.
347pub const SerializerOptions = struct {
348 /// If false, only syntactically necessary whitespace is emitted.
349 whitespace: bool = true,
350};
351
352345/// Determines when to emit Unicode code point literals as opposed to integer literals.
353346pub const EmitCodepointLiterals = enum {
354347 /// Never emit Unicode code point literals.
......@@ -440,633 +433,610 @@ pub const SerializeContainerOptions = struct {
440433/// For manual serialization of containers, see:
441434/// * `beginStruct`
442435/// * `beginTuple`
443///
444/// # Example
445/// ```zig
446/// var sz = serializer(writer, .{});
447/// var vec2 = try sz.beginStruct(.{});
448/// try vec2.field("x", 1.5, .{});
449/// try vec2.fieldPrefix();
450/// try sz.value(2.5);
451/// try vec2.end();
452/// ```
453pub fn Serializer(Writer: type) type {
454 return struct {
455 const Self = @This();
456
457 options: SerializerOptions,
458 indent_level: u8,
459 writer: Writer,
460
461 /// Initialize a serializer.
462 fn init(writer: Writer, options: SerializerOptions) Self {
463 return .{
464 .options = options,
465 .writer = writer,
466 .indent_level = 0,
467 };
468 }
436pub const Serializer = struct {
437 options: Options,
438 indent_level: u8 = 0,
439 writer: *std.io.BufferedWriter,
440
441 pub const Options = struct {
442 /// If false, only syntactically necessary whitespace is emitted.
443 whitespace: bool = true,
444 };
469445
470 /// Serialize a value, similar to `serialize`.
471 pub fn value(self: *Self, val: anytype, options: ValueOptions) Writer.Error!void {
472 comptime assert(!typeIsRecursive(@TypeOf(val)));
473 return self.valueArbitraryDepth(val, options);
474 }
446 /// Serialize a value, similar to `serialize`.
447 pub fn value(self: *Serializer, val: anytype, options: ValueOptions) anyerror!void {
448 comptime assert(!typeIsRecursive(@TypeOf(val)));
449 return self.valueArbitraryDepth(val, options);
450 }
475451
476 /// Serialize a value, similar to `serializeMaxDepth`.
477 pub fn valueMaxDepth(
478 self: *Self,
479 val: anytype,
480 options: ValueOptions,
481 depth: usize,
482 ) (Writer.Error || error{ExceededMaxDepth})!void {
483 try checkValueDepth(val, depth);
484 return self.valueArbitraryDepth(val, options);
485 }
452 /// Serialize a value, similar to `serializeMaxDepth`.
453 /// Can return `error.ExceededMaxDepth`.
454 pub fn valueMaxDepth(self: *Serializer, val: anytype, options: ValueOptions, depth: usize) anyerror!void {
455 try checkValueDepth(val, depth);
456 return self.valueArbitraryDepth(val, options);
457 }
486458
487 /// Serialize a value, similar to `serializeArbitraryDepth`.
488 pub fn valueArbitraryDepth(
489 self: *Self,
490 val: anytype,
491 options: ValueOptions,
492 ) Writer.Error!void {
493 comptime assert(canSerializeType(@TypeOf(val)));
494 switch (@typeInfo(@TypeOf(val))) {
495 .int, .comptime_int => if (options.emit_codepoint_literals.emitAsCodepoint(val)) |c| {
496 self.codePoint(c) catch |err| switch (err) {
497 error.InvalidCodepoint => unreachable, // Already validated
498 else => |e| return e,
499 };
500 } else {
501 try self.int(val);
502 },
503 .float, .comptime_float => try self.float(val),
504 .bool, .null => try std.fmt.format(self.writer, "{}", .{val}),
505 .enum_literal => try self.ident(@tagName(val)),
506 .@"enum" => try self.ident(@tagName(val)),
507 .pointer => |pointer| {
508 // Try to serialize as a string
509 const item: ?type = switch (@typeInfo(pointer.child)) {
510 .array => |array| array.child,
511 else => if (pointer.size == .slice) pointer.child else null,
512 };
513 if (item == u8 and
514 (pointer.sentinel() == null or pointer.sentinel() == 0) and
515 !options.emit_strings_as_containers)
516 {
517 return try self.string(val);
518 }
459 /// Serialize a value, similar to `serializeArbitraryDepth`.
460 pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOptions) anyerror!void {
461 comptime assert(canSerializeType(@TypeOf(val)));
462 switch (@typeInfo(@TypeOf(val))) {
463 .int, .comptime_int => if (options.emit_codepoint_literals.emitAsCodepoint(val)) |c| {
464 self.codePoint(c) catch |err| switch (err) {
465 error.InvalidCodepoint => unreachable, // Already validated
466 else => |e| return e,
467 };
468 } else {
469 try self.int(val);
470 },
471 .float, .comptime_float => try self.float(val),
472 .bool, .null => try std.fmt.format(self.writer, "{}", .{val}),
473 .enum_literal => try self.ident(@tagName(val)),
474 .@"enum" => try self.ident(@tagName(val)),
475 .pointer => |pointer| {
476 // Try to serialize as a string
477 const item: ?type = switch (@typeInfo(pointer.child)) {
478 .array => |array| array.child,
479 else => if (pointer.size == .slice) pointer.child else null,
480 };
481 if (item == u8 and
482 (pointer.sentinel() == null or pointer.sentinel() == 0) and
483 !options.emit_strings_as_containers)
484 {
485 return try self.string(val);
486 }
519487
520 // Serialize as either a tuple or as the child type
521 switch (pointer.size) {
522 .slice => try self.tupleImpl(val, options),
523 .one => try self.valueArbitraryDepth(val.*, options),
524 else => comptime unreachable,
525 }
526 },
527 .array => {
528 var container = try self.beginTuple(
529 .{ .whitespace_style = .{ .fields = val.len } },
530 );
531 for (val) |item_val| {
532 try container.fieldArbitraryDepth(item_val, options);
533 }
534 try container.end();
535 },
536 .@"struct" => |@"struct"| if (@"struct".is_tuple) {
537 var container = try self.beginTuple(
538 .{ .whitespace_style = .{ .fields = @"struct".fields.len } },
539 );
540 inline for (val) |field_value| {
541 try container.fieldArbitraryDepth(field_value, options);
542 }
543 try container.end();
544 } else {
545 // Decide which fields to emit
546 const fields, const skipped: [@"struct".fields.len]bool = if (options.emit_default_optional_fields) b: {
547 break :b .{ @"struct".fields.len, @splat(false) };
548 } else b: {
549 var fields = @"struct".fields.len;
550 var skipped: [@"struct".fields.len]bool = @splat(false);
551 inline for (@"struct".fields, &skipped) |field_info, *skip| {
552 if (field_info.default_value_ptr) |ptr| {
553 const default: *const field_info.type = @ptrCast(@alignCast(ptr));
554 const field_value = @field(val, field_info.name);
555 if (std.meta.eql(field_value, default.*)) {
556 skip.* = true;
557 fields -= 1;
558 }
488 // Serialize as either a tuple or as the child type
489 switch (pointer.size) {
490 .slice => try self.tupleImpl(val, options),
491 .one => try self.valueArbitraryDepth(val.*, options),
492 else => comptime unreachable,
493 }
494 },
495 .array => {
496 var container = try self.beginTuple(
497 .{ .whitespace_style = .{ .fields = val.len } },
498 );
499 for (val) |item_val| {
500 try container.fieldArbitraryDepth(item_val, options);
501 }
502 try container.end();
503 },
504 .@"struct" => |@"struct"| if (@"struct".is_tuple) {
505 var container = try self.beginTuple(
506 .{ .whitespace_style = .{ .fields = @"struct".fields.len } },
507 );
508 inline for (val) |field_value| {
509 try container.fieldArbitraryDepth(field_value, options);
510 }
511 try container.end();
512 } else {
513 // Decide which fields to emit
514 const fields, const skipped: [@"struct".fields.len]bool = if (options.emit_default_optional_fields) b: {
515 break :b .{ @"struct".fields.len, @splat(false) };
516 } else b: {
517 var fields = @"struct".fields.len;
518 var skipped: [@"struct".fields.len]bool = @splat(false);
519 inline for (@"struct".fields, &skipped) |field_info, *skip| {
520 if (field_info.default_value_ptr) |ptr| {
521 const default: *const field_info.type = @ptrCast(@alignCast(ptr));
522 const field_value = @field(val, field_info.name);
523 if (std.meta.eql(field_value, default.*)) {
524 skip.* = true;
525 fields -= 1;
559526 }
560527 }
561 break :b .{ fields, skipped };
562 };
563
564 // Emit those fields
565 var container = try self.beginStruct(
566 .{ .whitespace_style = .{ .fields = fields } },
567 );
568 inline for (@"struct".fields, skipped) |field_info, skip| {
569 if (!skip) {
570 try container.fieldArbitraryDepth(
571 field_info.name,
572 @field(val, field_info.name),
573 options,
574 );
575 }
576 }
577 try container.end();
578 },
579 .@"union" => |@"union"| {
580 comptime assert(@"union".tag_type != null);
581 switch (val) {
582 inline else => |pl, tag| if (@TypeOf(pl) == void)
583 try self.writer.print(".{s}", .{@tagName(tag)})
584 else {
585 var container = try self.beginStruct(.{ .whitespace_style = .{ .fields = 1 } });
586
587 try container.fieldArbitraryDepth(
588 @tagName(tag),
589 pl,
590 options,
591 );
592
593 try container.end();
594 },
595528 }
596 },
597 .optional => if (val) |inner| {
598 try self.valueArbitraryDepth(inner, options);
599 } else {
600 try self.writer.writeAll("null");
601 },
602 .vector => |vector| {
603 var container = try self.beginTuple(
604 .{ .whitespace_style = .{ .fields = vector.len } },
605 );
606 for (0..vector.len) |i| {
607 try container.fieldArbitraryDepth(val[i], options);
529 break :b .{ fields, skipped };
530 };
531
532 // Emit those fields
533 var container = try self.beginStruct(
534 .{ .whitespace_style = .{ .fields = fields } },
535 );
536 inline for (@"struct".fields, skipped) |field_info, skip| {
537 if (!skip) {
538 try container.fieldArbitraryDepth(
539 field_info.name,
540 @field(val, field_info.name),
541 options,
542 );
608543 }
609 try container.end();
610 },
544 }
545 try container.end();
546 },
547 .@"union" => |@"union"| {
548 comptime assert(@"union".tag_type != null);
549 switch (val) {
550 inline else => |pl, tag| if (@TypeOf(pl) == void)
551 try self.writer.print(".{s}", .{@tagName(tag)})
552 else {
553 var container = try self.beginStruct(.{ .whitespace_style = .{ .fields = 1 } });
554
555 try container.fieldArbitraryDepth(
556 @tagName(tag),
557 pl,
558 options,
559 );
560
561 try container.end();
562 },
563 }
564 },
565 .optional => if (val) |inner| {
566 try self.valueArbitraryDepth(inner, options);
567 } else {
568 try self.writer.writeAll("null");
569 },
570 .vector => |vector| {
571 var container = try self.beginTuple(
572 .{ .whitespace_style = .{ .fields = vector.len } },
573 );
574 for (0..vector.len) |i| {
575 try container.fieldArbitraryDepth(val[i], options);
576 }
577 try container.end();
578 },
611579
612 else => comptime unreachable,
613 }
580 else => comptime unreachable,
614581 }
582 }
615583
616 /// Serialize an integer.
617 pub fn int(self: *Self, val: anytype) Writer.Error!void {
618 try std.fmt.formatInt(val, 10, .lower, .{}, self.writer);
619 }
620
621 /// Serialize a float.
622 pub fn float(self: *Self, val: anytype) Writer.Error!void {
623 switch (@typeInfo(@TypeOf(val))) {
624 .float => if (std.math.isNan(val)) {
625 return self.writer.writeAll("nan");
626 } else if (std.math.isPositiveInf(val)) {
627 return self.writer.writeAll("inf");
628 } else if (std.math.isNegativeInf(val)) {
629 return self.writer.writeAll("-inf");
630 } else if (std.math.isNegativeZero(val)) {
631 return self.writer.writeAll("-0.0");
632 } else {
633 try std.fmt.format(self.writer, "{d}", .{val});
634 },
635 .comptime_float => if (val == 0) {
636 return self.writer.writeAll("0");
637 } else {
638 try std.fmt.format(self.writer, "{d}", .{val});
639 },
640 else => comptime unreachable,
641 }
642 }
584 /// Serialize an integer.
585 pub fn int(self: *Serializer, val: anytype) anyerror!void {
586 try std.fmt.formatInt(val, 10, .lower, .{}, self.writer);
587 }
643588
644 /// Serialize `name` as an identifier prefixed with `.`.
645 ///
646 /// Escapes the identifier if necessary.
647 pub fn ident(self: *Self, name: []const u8) Writer.Error!void {
648 try self.writer.print(".{p_}", .{std.zig.fmtId(name)});
589 /// Serialize a float.
590 pub fn float(self: *Serializer, val: anytype) anyerror!void {
591 switch (@typeInfo(@TypeOf(val))) {
592 .float => if (std.math.isNan(val)) {
593 return self.writer.writeAll("nan");
594 } else if (std.math.isPositiveInf(val)) {
595 return self.writer.writeAll("inf");
596 } else if (std.math.isNegativeInf(val)) {
597 return self.writer.writeAll("-inf");
598 } else {
599 try std.fmt.format(self.writer, "{d}", .{val});
600 },
601 .comptime_float => try std.fmt.format(self.writer, "{d}", .{val}),
602 else => comptime unreachable,
649603 }
604 }
650605
651 /// Serialize `val` as a Unicode codepoint.
652 ///
653 /// Returns `error.InvalidCodepoint` if `val` is not a valid Unicode codepoint.
654 pub fn codePoint(
655 self: *Self,
656 val: u21,
657 ) (Writer.Error || error{InvalidCodepoint})!void {
658 var buf: [8]u8 = undefined;
659 const len = std.unicode.utf8Encode(val, &buf) catch return error.InvalidCodepoint;
660 const str = buf[0..len];
661 try std.fmt.format(self.writer, "'{'}'", .{std.zig.fmtEscapes(str)});
662 }
663
664 /// Like `value`, but always serializes `val` as a tuple.
665 ///
666 /// Will fail at comptime if `val` is not a tuple, array, pointer to an array, or slice.
667 pub fn tuple(self: *Self, val: anytype, options: ValueOptions) Writer.Error!void {
668 comptime assert(!typeIsRecursive(@TypeOf(val)));
669 try self.tupleArbitraryDepth(val, options);
670 }
606 /// Serialize `name` as an identifier prefixed with `.`.
607 ///
608 /// Escapes the identifier if necessary.
609 pub fn ident(self: *Serializer, name: []const u8) anyerror!void {
610 try self.writer.print(".{p_}", .{std.zig.fmtId(name)});
611 }
671612
672 /// Like `tuple`, but recursive types are allowed.
673 ///
674 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
675 pub fn tupleMaxDepth(
676 self: *Self,
677 val: anytype,
678 options: ValueOptions,
679 depth: usize,
680 ) (Writer.Error || error{ExceededMaxDepth})!void {
681 try checkValueDepth(val, depth);
682 try self.tupleArbitraryDepth(val, options);
683 }
613 /// Serialize `val` as a Unicode codepoint.
614 ///
615 /// Returns `error.InvalidCodepoint` if `val` is not a valid Unicode codepoint.
616 pub fn codePoint(
617 self: *Serializer,
618 val: u21,
619 ) anyerror!void {
620 var buf: [8]u8 = undefined;
621 const len = std.unicode.utf8Encode(val, &buf) catch return error.InvalidCodepoint;
622 const str = buf[0..len];
623 try std.fmt.format(self.writer, "'{'}'", .{std.zig.fmtEscapes(str)});
624 }
684625
685 /// Like `tuple`, but recursive types are allowed.
686 ///
687 /// It is the caller's responsibility to ensure that `val` does not contain cycles.
688 pub fn tupleArbitraryDepth(
689 self: *Self,
690 val: anytype,
691 options: ValueOptions,
692 ) Writer.Error!void {
693 try self.tupleImpl(val, options);
694 }
626 /// Like `value`, but always serializes `val` as a tuple.
627 ///
628 /// Will fail at comptime if `val` is not a tuple, array, pointer to an array, or slice.
629 pub fn tuple(self: *Serializer, val: anytype, options: ValueOptions) anyerror!void {
630 comptime assert(!typeIsRecursive(@TypeOf(val)));
631 try self.tupleArbitraryDepth(val, options);
632 }
695633
696 fn tupleImpl(self: *Self, val: anytype, options: ValueOptions) Writer.Error!void {
697 comptime assert(canSerializeType(@TypeOf(val)));
698 switch (@typeInfo(@TypeOf(val))) {
699 .@"struct" => {
700 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });
701 inline for (val) |item_val| {
702 try container.fieldArbitraryDepth(item_val, options);
703 }
704 try container.end();
705 },
706 .pointer, .array => {
707 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });
708 for (val) |item_val| {
709 try container.fieldArbitraryDepth(item_val, options);
710 }
711 try container.end();
712 },
713 else => comptime unreachable,
714 }
715 }
634 /// Like `tuple`, but recursive types are allowed.
635 ///
636 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
637 pub fn tupleMaxDepth(
638 self: *Serializer,
639 val: anytype,
640 options: ValueOptions,
641 depth: usize,
642 ) anyerror!void {
643 try checkValueDepth(val, depth);
644 try self.tupleArbitraryDepth(val, options);
645 }
646
647 /// Like `tuple`, but recursive types are allowed.
648 ///
649 /// It is the caller's responsibility to ensure that `val` does not contain cycles.
650 pub fn tupleArbitraryDepth(
651 self: *Serializer,
652 val: anytype,
653 options: ValueOptions,
654 ) anyerror!void {
655 try self.tupleImpl(val, options);
656 }
716657
717 /// Like `value`, but always serializes `val` as a string.
718 pub fn string(self: *Self, val: []const u8) Writer.Error!void {
719 try std.fmt.format(self.writer, "\"{}\"", .{std.zig.fmtEscapes(val)});
658 fn tupleImpl(self: *Serializer, val: anytype, options: ValueOptions) anyerror!void {
659 comptime assert(canSerializeType(@TypeOf(val)));
660 switch (@typeInfo(@TypeOf(val))) {
661 .@"struct" => {
662 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });
663 inline for (val) |item_val| {
664 try container.fieldArbitraryDepth(item_val, options);
665 }
666 try container.end();
667 },
668 .pointer, .array => {
669 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });
670 for (val) |item_val| {
671 try container.fieldArbitraryDepth(item_val, options);
672 }
673 try container.end();
674 },
675 else => comptime unreachable,
720676 }
677 }
721678
722 /// Options for formatting multiline strings.
723 pub const MultilineStringOptions = struct {
724 /// If top level is true, whitespace before and after the multiline string is elided.
725 /// If it is true, a newline is printed, then the value, followed by a newline, and if
726 /// whitespace is true any necessary indentation follows.
727 top_level: bool = false,
728 };
679 /// Like `value`, but always serializes `val` as a string.
680 pub fn string(self: *Serializer, val: []const u8) anyerror!void {
681 try std.fmt.format(self.writer, "\"{}\"", .{std.zig.fmtEscapes(val)});
682 }
729683
730 /// Like `value`, but always serializes to a multiline string literal.
731 ///
732 /// Returns `error.InnerCarriageReturn` if `val` contains a CR not followed by a newline,
733 /// since multiline strings cannot represent CR without a following newline.
734 pub fn multilineString(
735 self: *Self,
736 val: []const u8,
737 options: MultilineStringOptions,
738 ) (Writer.Error || error{InnerCarriageReturn})!void {
739 // Make sure the string does not contain any carriage returns not followed by a newline
740 var i: usize = 0;
741 while (i < val.len) : (i += 1) {
742 if (val[i] == '\r') {
743 if (i + 1 < val.len) {
744 if (val[i + 1] == '\n') {
745 i += 1;
746 continue;
747 }
684 /// Options for formatting multiline strings.
685 pub const MultilineStringOptions = struct {
686 /// If top level is true, whitespace before and after the multiline string is elided.
687 /// If it is true, a newline is printed, then the value, followed by a newline, and if
688 /// whitespace is true any necessary indentation follows.
689 top_level: bool = false,
690 };
691
692 /// Like `value`, but always serializes to a multiline string literal.
693 ///
694 /// Returns `error.InnerCarriageReturn` if `val` contains a CR not followed by a newline,
695 /// since multiline strings cannot represent CR without a following newline.
696 pub fn multilineString(
697 self: *Serializer,
698 val: []const u8,
699 options: MultilineStringOptions,
700 ) anyerror!void {
701 // Make sure the string does not contain any carriage returns not followed by a newline
702 var i: usize = 0;
703 while (i < val.len) : (i += 1) {
704 if (val[i] == '\r') {
705 if (i + 1 < val.len) {
706 if (val[i + 1] == '\n') {
707 i += 1;
708 continue;
748709 }
749 return error.InnerCarriageReturn;
750710 }
711 return error.InnerCarriageReturn;
751712 }
713 }
752714
753 if (!options.top_level) {
754 try self.newline();
755 try self.indent();
756 }
715 if (!options.top_level) {
716 try self.newline();
717 try self.indent();
718 }
757719
758 try self.writer.writeAll("\\\\");
759 for (val) |c| {
760 if (c != '\r') {
761 try self.writer.writeByte(c); // We write newlines here even if whitespace off
762 if (c == '\n') {
763 try self.indent();
764 try self.writer.writeAll("\\\\");
765 }
720 try self.writer.writeAll("\\\\");
721 for (val) |c| {
722 if (c != '\r') {
723 try self.writer.writeByte(c); // We write newlines here even if whitespace off
724 if (c == '\n') {
725 try self.indent();
726 try self.writer.writeAll("\\\\");
766727 }
767728 }
768
769 if (!options.top_level) {
770 try self.writer.writeByte('\n'); // Even if whitespace off
771 try self.indent();
772 }
773729 }
774730
775 /// Create a `Struct` for writing ZON structs field by field.
776 pub fn beginStruct(
777 self: *Self,
778 options: SerializeContainerOptions,
779 ) Writer.Error!Struct {
780 return Struct.begin(self, options);
731 if (!options.top_level) {
732 try self.writer.writeByte('\n'); // Even if whitespace off
733 try self.indent();
781734 }
735 }
782736
783 /// Creates a `Tuple` for writing ZON tuples field by field.
784 pub fn beginTuple(
785 self: *Self,
786 options: SerializeContainerOptions,
787 ) Writer.Error!Tuple {
788 return Tuple.begin(self, options);
789 }
737 /// Create a `Struct` for writing ZON structs field by field.
738 pub fn beginStruct(
739 self: *Serializer,
740 options: SerializeContainerOptions,
741 ) anyerror!Struct {
742 return Struct.begin(self, options);
743 }
790744
791 fn indent(self: *Self) Writer.Error!void {
792 if (self.options.whitespace) {
793 try self.writer.writeByteNTimes(' ', 4 * self.indent_level);
794 }
745 /// Creates a `Tuple` for writing ZON tuples field by field.
746 pub fn beginTuple(
747 self: *Serializer,
748 options: SerializeContainerOptions,
749 ) anyerror!Tuple {
750 return Tuple.begin(self, options);
751 }
752
753 fn indent(self: *Serializer) anyerror!void {
754 if (self.options.whitespace) {
755 try self.writer.writeByteNTimes(' ', 4 * self.indent_level);
795756 }
757 }
796758
797 fn newline(self: *Self) Writer.Error!void {
798 if (self.options.whitespace) {
799 try self.writer.writeByte('\n');
800 }
759 fn newline(self: *Serializer) anyerror!void {
760 if (self.options.whitespace) {
761 try self.writer.writeByte('\n');
801762 }
763 }
802764
803 fn newlineOrSpace(self: *Self, len: usize) Writer.Error!void {
804 if (self.containerShouldWrap(len)) {
805 try self.newline();
806 } else {
807 try self.space();
808 }
765 fn newlineOrSpace(self: *Serializer, len: usize) anyerror!void {
766 if (self.containerShouldWrap(len)) {
767 try self.newline();
768 } else {
769 try self.space();
809770 }
771 }
810772
811 fn space(self: *Self) Writer.Error!void {
812 if (self.options.whitespace) {
813 try self.writer.writeByte(' ');
814 }
773 fn space(self: *Serializer) anyerror!void {
774 if (self.options.whitespace) {
775 try self.writer.writeByte(' ');
815776 }
777 }
816778
817 /// Writes ZON tuples field by field.
818 pub const Tuple = struct {
819 container: Container,
779 /// Writes ZON tuples field by field.
780 pub const Tuple = struct {
781 container: Container,
820782
821 fn begin(parent: *Self, options: SerializeContainerOptions) Writer.Error!Tuple {
822 return .{
823 .container = try Container.begin(parent, .anon, options),
824 };
825 }
783 fn begin(parent: *Serializer, options: SerializeContainerOptions) anyerror!Tuple {
784 return .{
785 .container = try Container.begin(parent, .anon, options),
786 };
787 }
826788
827 /// Finishes serializing the tuple.
828 ///
829 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
830 pub fn end(self: *Tuple) Writer.Error!void {
831 try self.container.end();
832 self.* = undefined;
833 }
789 /// Finishes serializing the tuple.
790 ///
791 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
792 pub fn end(self: *Tuple) anyerror!void {
793 try self.container.end();
794 self.* = undefined;
795 }
834796
835 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
836 pub fn field(
837 self: *Tuple,
838 val: anytype,
839 options: ValueOptions,
840 ) Writer.Error!void {
841 try self.container.field(null, val, options);
842 }
797 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
798 pub fn field(
799 self: *Tuple,
800 val: anytype,
801 options: ValueOptions,
802 ) anyerror!void {
803 try self.container.field(null, val, options);
804 }
843805
844 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
845 pub fn fieldMaxDepth(
846 self: *Tuple,
847 val: anytype,
848 options: ValueOptions,
849 depth: usize,
850 ) (Writer.Error || error{ExceededMaxDepth})!void {
851 try self.container.fieldMaxDepth(null, val, options, depth);
852 }
806 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
807 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
808 pub fn fieldMaxDepth(
809 self: *Tuple,
810 val: anytype,
811 options: ValueOptions,
812 depth: usize,
813 ) anyerror!void {
814 try self.container.fieldMaxDepth(null, val, options, depth);
815 }
853816
854 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
855 /// `valueArbitraryDepth`.
856 pub fn fieldArbitraryDepth(
857 self: *Tuple,
858 val: anytype,
859 options: ValueOptions,
860 ) Writer.Error!void {
861 try self.container.fieldArbitraryDepth(null, val, options);
862 }
817 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
818 /// `valueArbitraryDepth`.
819 pub fn fieldArbitraryDepth(
820 self: *Tuple,
821 val: anytype,
822 options: ValueOptions,
823 ) anyerror!void {
824 try self.container.fieldArbitraryDepth(null, val, options);
825 }
863826
864 /// Starts a field with a struct as a value. Returns the struct.
865 pub fn beginStructField(
866 self: *Tuple,
867 options: SerializeContainerOptions,
868 ) Writer.Error!Struct {
869 try self.fieldPrefix();
870 return self.container.serializer.beginStruct(options);
871 }
827 /// Starts a field with a struct as a value. Returns the struct.
828 pub fn beginStructField(
829 self: *Tuple,
830 options: SerializeContainerOptions,
831 ) anyerror!Struct {
832 try self.fieldPrefix();
833 return self.container.serializer.beginStruct(options);
834 }
872835
873 /// Starts a field with a tuple as a value. Returns the tuple.
874 pub fn beginTupleField(
875 self: *Tuple,
876 options: SerializeContainerOptions,
877 ) Writer.Error!Tuple {
878 try self.fieldPrefix();
879 return self.container.serializer.beginTuple(options);
880 }
836 /// Starts a field with a tuple as a value. Returns the tuple.
837 pub fn beginTupleField(
838 self: *Tuple,
839 options: SerializeContainerOptions,
840 ) anyerror!Tuple {
841 try self.fieldPrefix();
842 return self.container.serializer.beginTuple(options);
843 }
881844
882 /// Print a field prefix. This prints any necessary commas, and whitespace as
883 /// configured. Useful if you want to serialize the field value yourself.
884 pub fn fieldPrefix(self: *Tuple) Writer.Error!void {
885 try self.container.fieldPrefix(null);
886 }
887 };
845 /// Print a field prefix. This prints any necessary commas, and whitespace as
846 /// configured. Useful if you want to serialize the field value yourself.
847 pub fn fieldPrefix(self: *Tuple) anyerror!void {
848 try self.container.fieldPrefix(null);
849 }
850 };
888851
889 /// Writes ZON structs field by field.
890 pub const Struct = struct {
891 container: Container,
852 /// Writes ZON structs field by field.
853 pub const Struct = struct {
854 container: Container,
892855
893 fn begin(parent: *Self, options: SerializeContainerOptions) Writer.Error!Struct {
894 return .{
895 .container = try Container.begin(parent, .named, options),
896 };
897 }
856 fn begin(parent: *Serializer, options: SerializeContainerOptions) anyerror!Struct {
857 return .{
858 .container = try Container.begin(parent, .named, options),
859 };
860 }
898861
899 /// Finishes serializing the struct.
900 ///
901 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
902 pub fn end(self: *Struct) Writer.Error!void {
903 try self.container.end();
904 self.* = undefined;
905 }
862 /// Finishes serializing the struct.
863 ///
864 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
865 pub fn end(self: *Struct) anyerror!void {
866 try self.container.end();
867 self.* = undefined;
868 }
906869
907 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
908 pub fn field(
909 self: *Struct,
910 name: []const u8,
911 val: anytype,
912 options: ValueOptions,
913 ) Writer.Error!void {
914 try self.container.field(name, val, options);
915 }
870 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
871 pub fn field(
872 self: *Struct,
873 name: []const u8,
874 val: anytype,
875 options: ValueOptions,
876 ) anyerror!void {
877 try self.container.field(name, val, options);
878 }
916879
917 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
918 pub fn fieldMaxDepth(
919 self: *Struct,
920 name: []const u8,
921 val: anytype,
922 options: ValueOptions,
923 depth: usize,
924 ) (Writer.Error || error{ExceededMaxDepth})!void {
925 try self.container.fieldMaxDepth(name, val, options, depth);
926 }
880 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
881 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
882 pub fn fieldMaxDepth(
883 self: *Struct,
884 name: []const u8,
885 val: anytype,
886 options: ValueOptions,
887 depth: usize,
888 ) anyerror!void {
889 try self.container.fieldMaxDepth(name, val, options, depth);
890 }
927891
928 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
929 /// `valueArbitraryDepth`.
930 pub fn fieldArbitraryDepth(
931 self: *Struct,
932 name: []const u8,
933 val: anytype,
934 options: ValueOptions,
935 ) Writer.Error!void {
936 try self.container.fieldArbitraryDepth(name, val, options);
937 }
892 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
893 /// `valueArbitraryDepth`.
894 pub fn fieldArbitraryDepth(
895 self: *Struct,
896 name: []const u8,
897 val: anytype,
898 options: ValueOptions,
899 ) anyerror!void {
900 try self.container.fieldArbitraryDepth(name, val, options);
901 }
938902
939 /// Starts a field with a struct as a value. Returns the struct.
940 pub fn beginStructField(
941 self: *Struct,
942 name: []const u8,
943 options: SerializeContainerOptions,
944 ) Writer.Error!Struct {
945 try self.fieldPrefix(name);
946 return self.container.serializer.beginStruct(options);
947 }
903 /// Starts a field with a struct as a value. Returns the struct.
904 pub fn beginStructField(
905 self: *Struct,
906 name: []const u8,
907 options: SerializeContainerOptions,
908 ) anyerror!Struct {
909 try self.fieldPrefix(name);
910 return self.container.serializer.beginStruct(options);
911 }
948912
949 /// Starts a field with a tuple as a value. Returns the tuple.
950 pub fn beginTupleField(
951 self: *Struct,
952 name: []const u8,
953 options: SerializeContainerOptions,
954 ) Writer.Error!Tuple {
955 try self.fieldPrefix(name);
956 return self.container.serializer.beginTuple(options);
957 }
913 /// Starts a field with a tuple as a value. Returns the tuple.
914 pub fn beginTupleField(
915 self: *Struct,
916 name: []const u8,
917 options: SerializeContainerOptions,
918 ) anyerror!Tuple {
919 try self.fieldPrefix(name);
920 return self.container.serializer.beginTuple(options);
921 }
958922
959 /// Print a field prefix. This prints any necessary commas, the field name (escaped if
960 /// necessary) and whitespace as configured. Useful if you want to serialize the field
961 /// value yourself.
962 pub fn fieldPrefix(self: *Struct, name: []const u8) Writer.Error!void {
963 try self.container.fieldPrefix(name);
964 }
965 };
923 /// Print a field prefix. This prints any necessary commas, the field name (escaped if
924 /// necessary) and whitespace as configured. Useful if you want to serialize the field
925 /// value yourself.
926 pub fn fieldPrefix(self: *Struct, name: []const u8) anyerror!void {
927 try self.container.fieldPrefix(name);
928 }
929 };
966930
967 const Container = struct {
968 const FieldStyle = enum { named, anon };
931 const Container = struct {
932 const FieldStyle = enum { named, anon };
969933
970 serializer: *Self,
934 serializer: *Serializer,
935 field_style: FieldStyle,
936 options: SerializeContainerOptions,
937 empty: bool,
938
939 fn begin(
940 sz: *Serializer,
971941 field_style: FieldStyle,
972942 options: SerializeContainerOptions,
973 empty: bool,
974
975 fn begin(
976 sz: *Self,
977 field_style: FieldStyle,
978 options: SerializeContainerOptions,
979 ) Writer.Error!Container {
980 if (options.shouldWrap()) sz.indent_level +|= 1;
981 try sz.writer.writeAll(".{");
982 return .{
983 .serializer = sz,
984 .field_style = field_style,
985 .options = options,
986 .empty = true,
987 };
988 }
989
990 fn end(self: *Container) Writer.Error!void {
991 if (self.options.shouldWrap()) self.serializer.indent_level -|= 1;
992 if (!self.empty) {
993 if (self.options.shouldWrap()) {
994 if (self.serializer.options.whitespace) {
995 try self.serializer.writer.writeByte(',');
996 }
997 try self.serializer.newline();
998 try self.serializer.indent();
999 } else if (!self.shouldElideSpaces()) {
1000 try self.serializer.space();
1001 }
1002 }
1003 try self.serializer.writer.writeByte('}');
1004 self.* = undefined;
1005 }
943 ) anyerror!Container {
944 if (options.shouldWrap()) sz.indent_level +|= 1;
945 try sz.writer.writeAll(".{");
946 return .{
947 .serializer = sz,
948 .field_style = field_style,
949 .options = options,
950 .empty = true,
951 };
952 }
1006953
1007 fn fieldPrefix(self: *Container, name: ?[]const u8) Writer.Error!void {
1008 if (!self.empty) {
1009 try self.serializer.writer.writeByte(',');
1010 }
1011 self.empty = false;
954 fn end(self: *Container) anyerror!void {
955 if (self.options.shouldWrap()) self.serializer.indent_level -|= 1;
956 if (!self.empty) {
1012957 if (self.options.shouldWrap()) {
958 if (self.serializer.options.whitespace) {
959 try self.serializer.writer.writeByte(',');
960 }
1013961 try self.serializer.newline();
962 try self.serializer.indent();
1014963 } else if (!self.shouldElideSpaces()) {
1015964 try self.serializer.space();
1016965 }
1017 if (self.options.shouldWrap()) try self.serializer.indent();
1018 if (name) |n| {
1019 try self.serializer.ident(n);
1020 try self.serializer.space();
1021 try self.serializer.writer.writeByte('=');
1022 try self.serializer.space();
1023 }
1024966 }
967 try self.serializer.writer.writeByte('}');
968 self.* = undefined;
969 }
1025970
1026 fn field(
1027 self: *Container,
1028 name: ?[]const u8,
1029 val: anytype,
1030 options: ValueOptions,
1031 ) Writer.Error!void {
1032 comptime assert(!typeIsRecursive(@TypeOf(val)));
1033 try self.fieldArbitraryDepth(name, val, options);
971 fn fieldPrefix(self: *Container, name: ?[]const u8) anyerror!void {
972 if (!self.empty) {
973 try self.serializer.writer.writeByte(',');
1034974 }
1035
1036 fn fieldMaxDepth(
1037 self: *Container,
1038 name: ?[]const u8,
1039 val: anytype,
1040 options: ValueOptions,
1041 depth: usize,
1042 ) (Writer.Error || error{ExceededMaxDepth})!void {
1043 try checkValueDepth(val, depth);
1044 try self.fieldArbitraryDepth(name, val, options);
975 self.empty = false;
976 if (self.options.shouldWrap()) {
977 try self.serializer.newline();
978 } else if (!self.shouldElideSpaces()) {
979 try self.serializer.space();
1045980 }
1046
1047 fn fieldArbitraryDepth(
1048 self: *Container,
1049 name: ?[]const u8,
1050 val: anytype,
1051 options: ValueOptions,
1052 ) Writer.Error!void {
1053 try self.fieldPrefix(name);
1054 try self.serializer.valueArbitraryDepth(val, options);
981 if (self.options.shouldWrap()) try self.serializer.indent();
982 if (name) |n| {
983 try self.serializer.ident(n);
984 try self.serializer.space();
985 try self.serializer.writer.writeByte('=');
986 try self.serializer.space();
1055987 }
988 }
1056989
1057 fn shouldElideSpaces(self: *const Container) bool {
1058 return switch (self.options.whitespace_style) {
1059 .fields => |fields| self.field_style != .named and fields == 1,
1060 else => false,
1061 };
1062 }
1063 };
990 fn field(
991 self: *Container,
992 name: ?[]const u8,
993 val: anytype,
994 options: ValueOptions,
995 ) anyerror!void {
996 comptime assert(!typeIsRecursive(@TypeOf(val)));
997 try self.fieldArbitraryDepth(name, val, options);
998 }
999
1000 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
1001 fn fieldMaxDepth(
1002 self: *Container,
1003 name: ?[]const u8,
1004 val: anytype,
1005 options: ValueOptions,
1006 depth: usize,
1007 ) anyerror!void {
1008 try checkValueDepth(val, depth);
1009 try self.fieldArbitraryDepth(name, val, options);
1010 }
1011
1012 fn fieldArbitraryDepth(
1013 self: *Container,
1014 name: ?[]const u8,
1015 val: anytype,
1016 options: ValueOptions,
1017 ) anyerror!void {
1018 try self.fieldPrefix(name);
1019 try self.serializer.valueArbitraryDepth(val, options);
1020 }
1021
1022 fn shouldElideSpaces(self: *const Container) bool {
1023 return switch (self.options.whitespace_style) {
1024 .fields => |fields| self.field_style != .named and fields == 1,
1025 else => false,
1026 };
1027 }
10641028 };
1065}
1029};
10661030
1067/// Creates a new `Serializer` with the given writer and options.
1068pub fn serializer(writer: anytype, options: SerializerOptions) Serializer(@TypeOf(writer)) {
1069 return .init(writer, options);
1031test Serializer {
1032 var s: Serializer = .{
1033 .writer = std.io.null_writer,
1034 };
1035 var vec2 = try s.beginStruct(.{});
1036 try vec2.field("x", 1.5, .{});
1037 try vec2.fieldPrefix();
1038 try s.value(2.5);
1039 try vec2.end();
10701040}
10711041
10721042fn expectSerializeEqual(
......@@ -1074,10 +1044,12 @@ fn expectSerializeEqual(
10741044 value: anytype,
10751045 options: SerializeOptions,
10761046) !void {
1077 var buf = std.ArrayList(u8).init(std.testing.allocator);
1078 defer buf.deinit();
1079 try serialize(value, options, buf.writer());
1080 try std.testing.expectEqualStrings(expected, buf.items);
1047 var aw: std.io.AllocatingWriter = undefined;
1048 defer aw.deinit();
1049 const bw = aw.init(std.testing.allocator);
1050
1051 try serialize(value, options, bw);
1052 try std.testing.expectEqualStrings(expected, aw.getWritten());
10811053}
10821054
10831055test "std.zon stringify whitespace, high level API" {
......@@ -1174,59 +1146,59 @@ test "std.zon stringify whitespace, high level API" {
11741146}
11751147
11761148test "std.zon stringify whitespace, low level API" {
1177 var buf = std.ArrayList(u8).init(std.testing.allocator);
1178 defer buf.deinit();
1179 var sz = serializer(buf.writer(), .{});
1149 var aw: std.io.AllocatingWriter = undefined;
1150 defer aw.deinit();
1151 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
11801152
1181 inline for (.{ true, false }) |whitespace| {
1182 sz.options = .{ .whitespace = whitespace };
1153 for ([2]bool{ true, false }) |whitespace| {
1154 s.options = .{ .whitespace = whitespace };
11831155
11841156 // Empty containers
11851157 {
1186 var container = try sz.beginStruct(.{});
1158 var container = try s.beginStruct(.{});
11871159 try container.end();
1188 try std.testing.expectEqualStrings(".{}", buf.items);
1189 buf.clearRetainingCapacity();
1160 try std.testing.expectEqualStrings(".{}", aw.getWritten());
1161 aw.clearRetainingCapacity();
11901162 }
11911163
11921164 {
1193 var container = try sz.beginTuple(.{});
1165 var container = try s.beginTuple(.{});
11941166 try container.end();
1195 try std.testing.expectEqualStrings(".{}", buf.items);
1196 buf.clearRetainingCapacity();
1167 try std.testing.expectEqualStrings(".{}", aw.getWritten());
1168 aw.clearRetainingCapacity();
11971169 }
11981170
11991171 {
1200 var container = try sz.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
1172 var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
12011173 try container.end();
1202 try std.testing.expectEqualStrings(".{}", buf.items);
1203 buf.clearRetainingCapacity();
1174 try std.testing.expectEqualStrings(".{}", aw.getWritten());
1175 aw.clearRetainingCapacity();
12041176 }
12051177
12061178 {
1207 var container = try sz.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
1179 var container = try s.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
12081180 try container.end();
1209 try std.testing.expectEqualStrings(".{}", buf.items);
1210 buf.clearRetainingCapacity();
1181 try std.testing.expectEqualStrings(".{}", aw.getWritten());
1182 aw.clearRetainingCapacity();
12111183 }
12121184
12131185 {
1214 var container = try sz.beginStruct(.{ .whitespace_style = .{ .fields = 0 } });
1186 var container = try s.beginStruct(.{ .whitespace_style = .{ .fields = 0 } });
12151187 try container.end();
1216 try std.testing.expectEqualStrings(".{}", buf.items);
1217 buf.clearRetainingCapacity();
1188 try std.testing.expectEqualStrings(".{}", aw.getWritten());
1189 aw.clearRetainingCapacity();
12181190 }
12191191
12201192 {
1221 var container = try sz.beginTuple(.{ .whitespace_style = .{ .fields = 0 } });
1193 var container = try s.beginTuple(.{ .whitespace_style = .{ .fields = 0 } });
12221194 try container.end();
1223 try std.testing.expectEqualStrings(".{}", buf.items);
1224 buf.clearRetainingCapacity();
1195 try std.testing.expectEqualStrings(".{}", aw.getWritten());
1196 aw.clearRetainingCapacity();
12251197 }
12261198
12271199 // Size 1
12281200 {
1229 var container = try sz.beginStruct(.{});
1201 var container = try s.beginStruct(.{});
12301202 try container.field("a", 1, .{});
12311203 try container.end();
12321204 if (whitespace) {
......@@ -1234,15 +1206,15 @@ test "std.zon stringify whitespace, low level API" {
12341206 \\.{
12351207 \\ .a = 1,
12361208 \\}
1237 , buf.items);
1209 , aw.getWritten());
12381210 } else {
1239 try std.testing.expectEqualStrings(".{.a=1}", buf.items);
1211 try std.testing.expectEqualStrings(".{.a=1}", aw.getWritten());
12401212 }
1241 buf.clearRetainingCapacity();
1213 aw.clearRetainingCapacity();
12421214 }
12431215
12441216 {
1245 var container = try sz.beginTuple(.{});
1217 var container = try s.beginTuple(.{});
12461218 try container.field(1, .{});
12471219 try container.end();
12481220 if (whitespace) {
......@@ -1250,62 +1222,62 @@ test "std.zon stringify whitespace, low level API" {
12501222 \\.{
12511223 \\ 1,
12521224 \\}
1253 , buf.items);
1225 , aw.getWritten());
12541226 } else {
1255 try std.testing.expectEqualStrings(".{1}", buf.items);
1227 try std.testing.expectEqualStrings(".{1}", aw.getWritten());
12561228 }
1257 buf.clearRetainingCapacity();
1229 aw.clearRetainingCapacity();
12581230 }
12591231
12601232 {
1261 var container = try sz.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
1233 var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
12621234 try container.field("a", 1, .{});
12631235 try container.end();
12641236 if (whitespace) {
1265 try std.testing.expectEqualStrings(".{ .a = 1 }", buf.items);
1237 try std.testing.expectEqualStrings(".{ .a = 1 }", aw.getWritten());
12661238 } else {
1267 try std.testing.expectEqualStrings(".{.a=1}", buf.items);
1239 try std.testing.expectEqualStrings(".{.a=1}", aw.getWritten());
12681240 }
1269 buf.clearRetainingCapacity();
1241 aw.clearRetainingCapacity();
12701242 }
12711243
12721244 {
12731245 // We get extra spaces here, since we didn't know up front that there would only be one
12741246 // field.
1275 var container = try sz.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
1247 var container = try s.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
12761248 try container.field(1, .{});
12771249 try container.end();
12781250 if (whitespace) {
1279 try std.testing.expectEqualStrings(".{ 1 }", buf.items);
1251 try std.testing.expectEqualStrings(".{ 1 }", aw.getWritten());
12801252 } else {
1281 try std.testing.expectEqualStrings(".{1}", buf.items);
1253 try std.testing.expectEqualStrings(".{1}", aw.getWritten());
12821254 }
1283 buf.clearRetainingCapacity();
1255 aw.clearRetainingCapacity();
12841256 }
12851257
12861258 {
1287 var container = try sz.beginStruct(.{ .whitespace_style = .{ .fields = 1 } });
1259 var container = try s.beginStruct(.{ .whitespace_style = .{ .fields = 1 } });
12881260 try container.field("a", 1, .{});
12891261 try container.end();
12901262 if (whitespace) {
1291 try std.testing.expectEqualStrings(".{ .a = 1 }", buf.items);
1263 try std.testing.expectEqualStrings(".{ .a = 1 }", aw.getWritten());
12921264 } else {
1293 try std.testing.expectEqualStrings(".{.a=1}", buf.items);
1265 try std.testing.expectEqualStrings(".{.a=1}", aw.getWritten());
12941266 }
1295 buf.clearRetainingCapacity();
1267 aw.clearRetainingCapacity();
12961268 }
12971269
12981270 {
1299 var container = try sz.beginTuple(.{ .whitespace_style = .{ .fields = 1 } });
1271 var container = try s.beginTuple(.{ .whitespace_style = .{ .fields = 1 } });
13001272 try container.field(1, .{});
13011273 try container.end();
1302 try std.testing.expectEqualStrings(".{1}", buf.items);
1303 buf.clearRetainingCapacity();
1274 try std.testing.expectEqualStrings(".{1}", aw.getWritten());
1275 aw.clearRetainingCapacity();
13041276 }
13051277
13061278 // Size 2
13071279 {
1308 var container = try sz.beginStruct(.{});
1280 var container = try s.beginStruct(.{});
13091281 try container.field("a", 1, .{});
13101282 try container.field("b", 2, .{});
13111283 try container.end();
......@@ -1315,15 +1287,15 @@ test "std.zon stringify whitespace, low level API" {
13151287 \\ .a = 1,
13161288 \\ .b = 2,
13171289 \\}
1318 , buf.items);
1290 , aw.getWritten());
13191291 } else {
1320 try std.testing.expectEqualStrings(".{.a=1,.b=2}", buf.items);
1292 try std.testing.expectEqualStrings(".{.a=1,.b=2}", aw.getWritten());
13211293 }
1322 buf.clearRetainingCapacity();
1294 aw.clearRetainingCapacity();
13231295 }
13241296
13251297 {
1326 var container = try sz.beginTuple(.{});
1298 var container = try s.beginTuple(.{});
13271299 try container.field(1, .{});
13281300 try container.field(2, .{});
13291301 try container.end();
......@@ -1333,68 +1305,68 @@ test "std.zon stringify whitespace, low level API" {
13331305 \\ 1,
13341306 \\ 2,
13351307 \\}
1336 , buf.items);
1308 , aw.getWritten());
13371309 } else {
1338 try std.testing.expectEqualStrings(".{1,2}", buf.items);
1310 try std.testing.expectEqualStrings(".{1,2}", aw.getWritten());
13391311 }
1340 buf.clearRetainingCapacity();
1312 aw.clearRetainingCapacity();
13411313 }
13421314
13431315 {
1344 var container = try sz.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
1316 var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
13451317 try container.field("a", 1, .{});
13461318 try container.field("b", 2, .{});
13471319 try container.end();
13481320 if (whitespace) {
1349 try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", buf.items);
1321 try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", aw.getWritten());
13501322 } else {
1351 try std.testing.expectEqualStrings(".{.a=1,.b=2}", buf.items);
1323 try std.testing.expectEqualStrings(".{.a=1,.b=2}", aw.getWritten());
13521324 }
1353 buf.clearRetainingCapacity();
1325 aw.clearRetainingCapacity();
13541326 }
13551327
13561328 {
1357 var container = try sz.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
1329 var container = try s.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
13581330 try container.field(1, .{});
13591331 try container.field(2, .{});
13601332 try container.end();
13611333 if (whitespace) {
1362 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);
1334 try std.testing.expectEqualStrings(".{ 1, 2 }", aw.getWritten());
13631335 } else {
1364 try std.testing.expectEqualStrings(".{1,2}", buf.items);
1336 try std.testing.expectEqualStrings(".{1,2}", aw.getWritten());
13651337 }
1366 buf.clearRetainingCapacity();
1338 aw.clearRetainingCapacity();
13671339 }
13681340
13691341 {
1370 var container = try sz.beginStruct(.{ .whitespace_style = .{ .fields = 2 } });
1342 var container = try s.beginStruct(.{ .whitespace_style = .{ .fields = 2 } });
13711343 try container.field("a", 1, .{});
13721344 try container.field("b", 2, .{});
13731345 try container.end();
13741346 if (whitespace) {
1375 try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", buf.items);
1347 try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", aw.getWritten());
13761348 } else {
1377 try std.testing.expectEqualStrings(".{.a=1,.b=2}", buf.items);
1349 try std.testing.expectEqualStrings(".{.a=1,.b=2}", aw.getWritten());
13781350 }
1379 buf.clearRetainingCapacity();
1351 aw.clearRetainingCapacity();
13801352 }
13811353
13821354 {
1383 var container = try sz.beginTuple(.{ .whitespace_style = .{ .fields = 2 } });
1355 var container = try s.beginTuple(.{ .whitespace_style = .{ .fields = 2 } });
13841356 try container.field(1, .{});
13851357 try container.field(2, .{});
13861358 try container.end();
13871359 if (whitespace) {
1388 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);
1360 try std.testing.expectEqualStrings(".{ 1, 2 }", aw.getWritten());
13891361 } else {
1390 try std.testing.expectEqualStrings(".{1,2}", buf.items);
1362 try std.testing.expectEqualStrings(".{1,2}", aw.getWritten());
13911363 }
1392 buf.clearRetainingCapacity();
1364 aw.clearRetainingCapacity();
13931365 }
13941366
13951367 // Size 3
13961368 {
1397 var container = try sz.beginStruct(.{});
1369 var container = try s.beginStruct(.{});
13981370 try container.field("a", 1, .{});
13991371 try container.field("b", 2, .{});
14001372 try container.field("c", 3, .{});
......@@ -1406,15 +1378,15 @@ test "std.zon stringify whitespace, low level API" {
14061378 \\ .b = 2,
14071379 \\ .c = 3,
14081380 \\}
1409 , buf.items);
1381 , aw.getWritten());
14101382 } else {
1411 try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", buf.items);
1383 try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", aw.getWritten());
14121384 }
1413 buf.clearRetainingCapacity();
1385 aw.clearRetainingCapacity();
14141386 }
14151387
14161388 {
1417 var container = try sz.beginTuple(.{});
1389 var container = try s.beginTuple(.{});
14181390 try container.field(1, .{});
14191391 try container.field(2, .{});
14201392 try container.field(3, .{});
......@@ -1426,43 +1398,43 @@ test "std.zon stringify whitespace, low level API" {
14261398 \\ 2,
14271399 \\ 3,
14281400 \\}
1429 , buf.items);
1401 , aw.getWritten());
14301402 } else {
1431 try std.testing.expectEqualStrings(".{1,2,3}", buf.items);
1403 try std.testing.expectEqualStrings(".{1,2,3}", aw.getWritten());
14321404 }
1433 buf.clearRetainingCapacity();
1405 aw.clearRetainingCapacity();
14341406 }
14351407
14361408 {
1437 var container = try sz.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
1409 var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
14381410 try container.field("a", 1, .{});
14391411 try container.field("b", 2, .{});
14401412 try container.field("c", 3, .{});
14411413 try container.end();
14421414 if (whitespace) {
1443 try std.testing.expectEqualStrings(".{ .a = 1, .b = 2, .c = 3 }", buf.items);
1415 try std.testing.expectEqualStrings(".{ .a = 1, .b = 2, .c = 3 }", aw.getWritten());
14441416 } else {
1445 try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", buf.items);
1417 try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", aw.getWritten());
14461418 }
1447 buf.clearRetainingCapacity();
1419 aw.clearRetainingCapacity();
14481420 }
14491421
14501422 {
1451 var container = try sz.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
1423 var container = try s.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
14521424 try container.field(1, .{});
14531425 try container.field(2, .{});
14541426 try container.field(3, .{});
14551427 try container.end();
14561428 if (whitespace) {
1457 try std.testing.expectEqualStrings(".{ 1, 2, 3 }", buf.items);
1429 try std.testing.expectEqualStrings(".{ 1, 2, 3 }", aw.getWritten());
14581430 } else {
1459 try std.testing.expectEqualStrings(".{1,2,3}", buf.items);
1431 try std.testing.expectEqualStrings(".{1,2,3}", aw.getWritten());
14601432 }
1461 buf.clearRetainingCapacity();
1433 aw.clearRetainingCapacity();
14621434 }
14631435
14641436 {
1465 var container = try sz.beginStruct(.{ .whitespace_style = .{ .fields = 3 } });
1437 var container = try s.beginStruct(.{ .whitespace_style = .{ .fields = 3 } });
14661438 try container.field("a", 1, .{});
14671439 try container.field("b", 2, .{});
14681440 try container.field("c", 3, .{});
......@@ -1474,15 +1446,15 @@ test "std.zon stringify whitespace, low level API" {
14741446 \\ .b = 2,
14751447 \\ .c = 3,
14761448 \\}
1477 , buf.items);
1449 , aw.getWritten());
14781450 } else {
1479 try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", buf.items);
1451 try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", aw.getWritten());
14801452 }
1481 buf.clearRetainingCapacity();
1453 aw.clearRetainingCapacity();
14821454 }
14831455
14841456 {
1485 var container = try sz.beginTuple(.{ .whitespace_style = .{ .fields = 3 } });
1457 var container = try s.beginTuple(.{ .whitespace_style = .{ .fields = 3 } });
14861458 try container.field(1, .{});
14871459 try container.field(2, .{});
14881460 try container.field(3, .{});
......@@ -1494,16 +1466,16 @@ test "std.zon stringify whitespace, low level API" {
14941466 \\ 2,
14951467 \\ 3,
14961468 \\}
1497 , buf.items);
1469 , aw.getWritten());
14981470 } else {
1499 try std.testing.expectEqualStrings(".{1,2,3}", buf.items);
1471 try std.testing.expectEqualStrings(".{1,2,3}", aw.getWritten());
15001472 }
1501 buf.clearRetainingCapacity();
1473 aw.clearRetainingCapacity();
15021474 }
15031475
15041476 // Nested objects where the outer container doesn't wrap but the inner containers do
15051477 {
1506 var container = try sz.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
1478 var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
15071479 try container.field("first", .{ 1, 2, 3 }, .{});
15081480 try container.field("second", .{ 4, 5, 6 }, .{});
15091481 try container.end();
......@@ -1518,139 +1490,139 @@ test "std.zon stringify whitespace, low level API" {
15181490 \\ 5,
15191491 \\ 6,
15201492 \\} }
1521 , buf.items);
1493 , aw.getWritten());
15221494 } else {
15231495 try std.testing.expectEqualStrings(
15241496 ".{.first=.{1,2,3},.second=.{4,5,6}}",
1525 buf.items,
1497 aw.getWritten(),
15261498 );
15271499 }
1528 buf.clearRetainingCapacity();
1500 aw.clearRetainingCapacity();
15291501 }
15301502 }
15311503}
15321504
15331505test "std.zon stringify utf8 codepoints" {
1534 var buf = std.ArrayList(u8).init(std.testing.allocator);
1535 defer buf.deinit();
1536 var sz = serializer(buf.writer(), .{});
1506 var aw: std.io.AllocatingWriter = undefined;
1507 defer aw.deinit();
1508 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
15371509
15381510 // Printable ASCII
1539 try sz.int('a');
1540 try std.testing.expectEqualStrings("97", buf.items);
1541 buf.clearRetainingCapacity();
1511 try s.int('a');
1512 try std.testing.expectEqualStrings("97", aw.getWritten());
1513 aw.clearRetainingCapacity();
15421514
1543 try sz.codePoint('a');
1544 try std.testing.expectEqualStrings("'a'", buf.items);
1545 buf.clearRetainingCapacity();
1515 try s.codePoint('a');
1516 try std.testing.expectEqualStrings("'a'", aw.getWritten());
1517 aw.clearRetainingCapacity();
15461518
1547 try sz.value('a', .{ .emit_codepoint_literals = .always });
1548 try std.testing.expectEqualStrings("'a'", buf.items);
1549 buf.clearRetainingCapacity();
1519 try s.value('a', .{ .emit_codepoint_literals = .always });
1520 try std.testing.expectEqualStrings("'a'", aw.getWritten());
1521 aw.clearRetainingCapacity();
15501522
1551 try sz.value('a', .{ .emit_codepoint_literals = .printable_ascii });
1552 try std.testing.expectEqualStrings("'a'", buf.items);
1553 buf.clearRetainingCapacity();
1523 try s.value('a', .{ .emit_codepoint_literals = .printable_ascii });
1524 try std.testing.expectEqualStrings("'a'", aw.getWritten());
1525 aw.clearRetainingCapacity();
15541526
1555 try sz.value('a', .{ .emit_codepoint_literals = .never });
1556 try std.testing.expectEqualStrings("97", buf.items);
1557 buf.clearRetainingCapacity();
1527 try s.value('a', .{ .emit_codepoint_literals = .never });
1528 try std.testing.expectEqualStrings("97", aw.getWritten());
1529 aw.clearRetainingCapacity();
15581530
15591531 // Short escaped codepoint
1560 try sz.int('\n');
1561 try std.testing.expectEqualStrings("10", buf.items);
1562 buf.clearRetainingCapacity();
1532 try s.int('\n');
1533 try std.testing.expectEqualStrings("10", aw.getWritten());
1534 aw.clearRetainingCapacity();
15631535
1564 try sz.codePoint('\n');
1565 try std.testing.expectEqualStrings("'\\n'", buf.items);
1566 buf.clearRetainingCapacity();
1536 try s.codePoint('\n');
1537 try std.testing.expectEqualStrings("'\\n'", aw.getWritten());
1538 aw.clearRetainingCapacity();
15671539
1568 try sz.value('\n', .{ .emit_codepoint_literals = .always });
1569 try std.testing.expectEqualStrings("'\\n'", buf.items);
1570 buf.clearRetainingCapacity();
1540 try s.value('\n', .{ .emit_codepoint_literals = .always });
1541 try std.testing.expectEqualStrings("'\\n'", aw.getWritten());
1542 aw.clearRetainingCapacity();
15711543
1572 try sz.value('\n', .{ .emit_codepoint_literals = .printable_ascii });
1573 try std.testing.expectEqualStrings("10", buf.items);
1574 buf.clearRetainingCapacity();
1544 try s.value('\n', .{ .emit_codepoint_literals = .printable_ascii });
1545 try std.testing.expectEqualStrings("10", aw.getWritten());
1546 aw.clearRetainingCapacity();
15751547
1576 try sz.value('\n', .{ .emit_codepoint_literals = .never });
1577 try std.testing.expectEqualStrings("10", buf.items);
1578 buf.clearRetainingCapacity();
1548 try s.value('\n', .{ .emit_codepoint_literals = .never });
1549 try std.testing.expectEqualStrings("10", aw.getWritten());
1550 aw.clearRetainingCapacity();
15791551
15801552 // Large codepoint
1581 try sz.int('⚡');
1582 try std.testing.expectEqualStrings("9889", buf.items);
1583 buf.clearRetainingCapacity();
1553 try s.int('⚡');
1554 try std.testing.expectEqualStrings("9889", aw.getWritten());
1555 aw.clearRetainingCapacity();
15841556
1585 try sz.codePoint('⚡');
1586 try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", buf.items);
1587 buf.clearRetainingCapacity();
1557 try s.codePoint('⚡');
1558 try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", aw.getWritten());
1559 aw.clearRetainingCapacity();
15881560
1589 try sz.value('⚡', .{ .emit_codepoint_literals = .always });
1590 try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", buf.items);
1591 buf.clearRetainingCapacity();
1561 try s.value('⚡', .{ .emit_codepoint_literals = .always });
1562 try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", aw.getWritten());
1563 aw.clearRetainingCapacity();
15921564
1593 try sz.value('⚡', .{ .emit_codepoint_literals = .printable_ascii });
1594 try std.testing.expectEqualStrings("9889", buf.items);
1595 buf.clearRetainingCapacity();
1565 try s.value('⚡', .{ .emit_codepoint_literals = .printable_ascii });
1566 try std.testing.expectEqualStrings("9889", aw.getWritten());
1567 aw.clearRetainingCapacity();
15961568
1597 try sz.value('⚡', .{ .emit_codepoint_literals = .never });
1598 try std.testing.expectEqualStrings("9889", buf.items);
1599 buf.clearRetainingCapacity();
1569 try s.value('⚡', .{ .emit_codepoint_literals = .never });
1570 try std.testing.expectEqualStrings("9889", aw.getWritten());
1571 aw.clearRetainingCapacity();
16001572
16011573 // Invalid codepoint
1602 try std.testing.expectError(error.InvalidCodepoint, sz.codePoint(0x110000 + 1));
1574 try std.testing.expectError(error.InvalidCodepoint, s.codePoint(0x110000 + 1));
16031575
1604 try sz.int(0x110000 + 1);
1605 try std.testing.expectEqualStrings("1114113", buf.items);
1606 buf.clearRetainingCapacity();
1576 try s.int(0x110000 + 1);
1577 try std.testing.expectEqualStrings("1114113", aw.getWritten());
1578 aw.clearRetainingCapacity();
16071579
1608 try sz.value(0x110000 + 1, .{ .emit_codepoint_literals = .always });
1609 try std.testing.expectEqualStrings("1114113", buf.items);
1610 buf.clearRetainingCapacity();
1580 try s.value(0x110000 + 1, .{ .emit_codepoint_literals = .always });
1581 try std.testing.expectEqualStrings("1114113", aw.getWritten());
1582 aw.clearRetainingCapacity();
16111583
1612 try sz.value(0x110000 + 1, .{ .emit_codepoint_literals = .printable_ascii });
1613 try std.testing.expectEqualStrings("1114113", buf.items);
1614 buf.clearRetainingCapacity();
1584 try s.value(0x110000 + 1, .{ .emit_codepoint_literals = .printable_ascii });
1585 try std.testing.expectEqualStrings("1114113", aw.getWritten());
1586 aw.clearRetainingCapacity();
16151587
1616 try sz.value(0x110000 + 1, .{ .emit_codepoint_literals = .never });
1617 try std.testing.expectEqualStrings("1114113", buf.items);
1618 buf.clearRetainingCapacity();
1588 try s.value(0x110000 + 1, .{ .emit_codepoint_literals = .never });
1589 try std.testing.expectEqualStrings("1114113", aw.getWritten());
1590 aw.clearRetainingCapacity();
16191591
16201592 // Valid codepoint, not a codepoint type
1621 try sz.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .always });
1622 try std.testing.expectEqualStrings("97", buf.items);
1623 buf.clearRetainingCapacity();
1593 try s.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .always });
1594 try std.testing.expectEqualStrings("97", aw.getWritten());
1595 aw.clearRetainingCapacity();
16241596
1625 try sz.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .printable_ascii });
1626 try std.testing.expectEqualStrings("97", buf.items);
1627 buf.clearRetainingCapacity();
1597 try s.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .printable_ascii });
1598 try std.testing.expectEqualStrings("97", aw.getWritten());
1599 aw.clearRetainingCapacity();
16281600
1629 try sz.value(@as(i32, 'a'), .{ .emit_codepoint_literals = .never });
1630 try std.testing.expectEqualStrings("97", buf.items);
1631 buf.clearRetainingCapacity();
1601 try s.value(@as(i32, 'a'), .{ .emit_codepoint_literals = .never });
1602 try std.testing.expectEqualStrings("97", aw.getWritten());
1603 aw.clearRetainingCapacity();
16321604
16331605 // Make sure value options are passed to children
1634 try sz.value(.{ .c = '⚡' }, .{ .emit_codepoint_literals = .always });
1635 try std.testing.expectEqualStrings(".{ .c = '\\xe2\\x9a\\xa1' }", buf.items);
1636 buf.clearRetainingCapacity();
1606 try s.value(.{ .c = '⚡' }, .{ .emit_codepoint_literals = .always });
1607 try std.testing.expectEqualStrings(".{ .c = '\\xe2\\x9a\\xa1' }", aw.getWritten());
1608 aw.clearRetainingCapacity();
16371609
1638 try sz.value(.{ .c = '⚡' }, .{ .emit_codepoint_literals = .never });
1639 try std.testing.expectEqualStrings(".{ .c = 9889 }", buf.items);
1640 buf.clearRetainingCapacity();
1610 try s.value(.{ .c = '⚡' }, .{ .emit_codepoint_literals = .never });
1611 try std.testing.expectEqualStrings(".{ .c = 9889 }", aw.getWritten());
1612 aw.clearRetainingCapacity();
16411613}
16421614
16431615test "std.zon stringify strings" {
1644 var buf = std.ArrayList(u8).init(std.testing.allocator);
1645 defer buf.deinit();
1646 var sz = serializer(buf.writer(), .{});
1616 var aw: std.io.AllocatingWriter = undefined;
1617 defer aw.deinit();
1618 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
16471619
16481620 // Minimal case
1649 try sz.string("abc⚡\n");
1650 try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", buf.items);
1651 buf.clearRetainingCapacity();
1621 try s.string("abc⚡\n");
1622 try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", aw.getWritten());
1623 aw.clearRetainingCapacity();
16521624
1653 try sz.tuple("abc⚡\n", .{});
1625 try s.tuple("abc⚡\n", .{});
16541626 try std.testing.expectEqualStrings(
16551627 \\.{
16561628 \\ 97,
......@@ -1661,14 +1633,14 @@ test "std.zon stringify strings" {
16611633 \\ 161,
16621634 \\ 10,
16631635 \\}
1664 , buf.items);
1665 buf.clearRetainingCapacity();
1636 , aw.getWritten());
1637 aw.clearRetainingCapacity();
16661638
1667 try sz.value("abc⚡\n", .{});
1668 try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", buf.items);
1669 buf.clearRetainingCapacity();
1639 try s.value("abc⚡\n", .{});
1640 try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", aw.getWritten());
1641 aw.clearRetainingCapacity();
16701642
1671 try sz.value("abc⚡\n", .{ .emit_strings_as_containers = true });
1643 try s.value("abc⚡\n", .{ .emit_strings_as_containers = true });
16721644 try std.testing.expectEqualStrings(
16731645 \\.{
16741646 \\ 97,
......@@ -1679,113 +1651,113 @@ test "std.zon stringify strings" {
16791651 \\ 161,
16801652 \\ 10,
16811653 \\}
1682 , buf.items);
1683 buf.clearRetainingCapacity();
1654 , aw.getWritten());
1655 aw.clearRetainingCapacity();
16841656
16851657 // Value options are inherited by children
1686 try sz.value(.{ .str = "abc" }, .{});
1687 try std.testing.expectEqualStrings(".{ .str = \"abc\" }", buf.items);
1688 buf.clearRetainingCapacity();
1658 try s.value(.{ .str = "abc" }, .{});
1659 try std.testing.expectEqualStrings(".{ .str = \"abc\" }", aw.getWritten());
1660 aw.clearRetainingCapacity();
16891661
1690 try sz.value(.{ .str = "abc" }, .{ .emit_strings_as_containers = true });
1662 try s.value(.{ .str = "abc" }, .{ .emit_strings_as_containers = true });
16911663 try std.testing.expectEqualStrings(
16921664 \\.{ .str = .{
16931665 \\ 97,
16941666 \\ 98,
16951667 \\ 99,
16961668 \\} }
1697 , buf.items);
1698 buf.clearRetainingCapacity();
1669 , aw.getWritten());
1670 aw.clearRetainingCapacity();
16991671
17001672 // Arrays (rather than pointers to arrays) of u8s are not considered strings, so that data can
17011673 // round trip correctly.
1702 try sz.value("abc".*, .{});
1674 try s.value("abc".*, .{});
17031675 try std.testing.expectEqualStrings(
17041676 \\.{
17051677 \\ 97,
17061678 \\ 98,
17071679 \\ 99,
17081680 \\}
1709 , buf.items);
1710 buf.clearRetainingCapacity();
1681 , aw.getWritten());
1682 aw.clearRetainingCapacity();
17111683}
17121684
17131685test "std.zon stringify multiline strings" {
1714 var buf = std.ArrayList(u8).init(std.testing.allocator);
1715 defer buf.deinit();
1716 var sz = serializer(buf.writer(), .{});
1686 var aw: std.io.AllocatingWriter = undefined;
1687 defer aw.deinit();
1688 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
17171689
17181690 inline for (.{ true, false }) |whitespace| {
1719 sz.options.whitespace = whitespace;
1691 s.options.whitespace = whitespace;
17201692
17211693 {
1722 try sz.multilineString("", .{ .top_level = true });
1723 try std.testing.expectEqualStrings("\\\\", buf.items);
1724 buf.clearRetainingCapacity();
1694 try s.multilineString("", .{ .top_level = true });
1695 try std.testing.expectEqualStrings("\\\\", aw.getWritten());
1696 aw.clearRetainingCapacity();
17251697 }
17261698
17271699 {
1728 try sz.multilineString("abc⚡", .{ .top_level = true });
1729 try std.testing.expectEqualStrings("\\\\abc⚡", buf.items);
1730 buf.clearRetainingCapacity();
1700 try s.multilineString("abc⚡", .{ .top_level = true });
1701 try std.testing.expectEqualStrings("\\\\abc⚡", aw.getWritten());
1702 aw.clearRetainingCapacity();
17311703 }
17321704
17331705 {
1734 try sz.multilineString("abc⚡\ndef", .{ .top_level = true });
1735 try std.testing.expectEqualStrings("\\\\abc⚡\n\\\\def", buf.items);
1736 buf.clearRetainingCapacity();
1706 try s.multilineString("abc⚡\ndef", .{ .top_level = true });
1707 try std.testing.expectEqualStrings("\\\\abc⚡\n\\\\def", aw.getWritten());
1708 aw.clearRetainingCapacity();
17371709 }
17381710
17391711 {
1740 try sz.multilineString("abc⚡\r\ndef", .{ .top_level = true });
1741 try std.testing.expectEqualStrings("\\\\abc⚡\n\\\\def", buf.items);
1742 buf.clearRetainingCapacity();
1712 try s.multilineString("abc⚡\r\ndef", .{ .top_level = true });
1713 try std.testing.expectEqualStrings("\\\\abc⚡\n\\\\def", aw.getWritten());
1714 aw.clearRetainingCapacity();
17431715 }
17441716
17451717 {
1746 try sz.multilineString("\nabc⚡", .{ .top_level = true });
1747 try std.testing.expectEqualStrings("\\\\\n\\\\abc⚡", buf.items);
1748 buf.clearRetainingCapacity();
1718 try s.multilineString("\nabc⚡", .{ .top_level = true });
1719 try std.testing.expectEqualStrings("\\\\\n\\\\abc⚡", aw.getWritten());
1720 aw.clearRetainingCapacity();
17491721 }
17501722
17511723 {
1752 try sz.multilineString("\r\nabc⚡", .{ .top_level = true });
1753 try std.testing.expectEqualStrings("\\\\\n\\\\abc⚡", buf.items);
1754 buf.clearRetainingCapacity();
1724 try s.multilineString("\r\nabc⚡", .{ .top_level = true });
1725 try std.testing.expectEqualStrings("\\\\\n\\\\abc⚡", aw.getWritten());
1726 aw.clearRetainingCapacity();
17551727 }
17561728
17571729 {
1758 try sz.multilineString("abc\ndef", .{});
1730 try s.multilineString("abc\ndef", .{});
17591731 if (whitespace) {
1760 try std.testing.expectEqualStrings("\n\\\\abc\n\\\\def\n", buf.items);
1732 try std.testing.expectEqualStrings("\n\\\\abc\n\\\\def\n", aw.getWritten());
17611733 } else {
1762 try std.testing.expectEqualStrings("\\\\abc\n\\\\def\n", buf.items);
1734 try std.testing.expectEqualStrings("\\\\abc\n\\\\def\n", aw.getWritten());
17631735 }
1764 buf.clearRetainingCapacity();
1736 aw.clearRetainingCapacity();
17651737 }
17661738
17671739 {
17681740 const str: []const u8 = &.{ 'a', '\r', 'c' };
1769 try sz.string(str);
1770 try std.testing.expectEqualStrings("\"a\\rc\"", buf.items);
1771 buf.clearRetainingCapacity();
1741 try s.string(str);
1742 try std.testing.expectEqualStrings("\"a\\rc\"", aw.getWritten());
1743 aw.clearRetainingCapacity();
17721744 }
17731745
17741746 {
17751747 try std.testing.expectError(
17761748 error.InnerCarriageReturn,
1777 sz.multilineString(@as([]const u8, &.{ 'a', '\r', 'c' }), .{}),
1749 s.multilineString(@as([]const u8, &.{ 'a', '\r', 'c' }), .{}),
17781750 );
17791751 try std.testing.expectError(
17801752 error.InnerCarriageReturn,
1781 sz.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\n' }), .{}),
1753 s.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\n' }), .{}),
17821754 );
17831755 try std.testing.expectError(
17841756 error.InnerCarriageReturn,
1785 sz.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\r', '\n' }), .{}),
1757 s.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\r', '\n' }), .{}),
17861758 );
1787 try std.testing.expectEqualStrings("", buf.items);
1788 buf.clearRetainingCapacity();
1759 try std.testing.expectEqualStrings("", aw.getWritten());
1760 aw.clearRetainingCapacity();
17891761 }
17901762 }
17911763}
......@@ -1931,42 +1903,43 @@ test "std.zon stringify skip default fields" {
19311903}
19321904
19331905test "std.zon depth limits" {
1934 var buf = std.ArrayList(u8).init(std.testing.allocator);
1935 defer buf.deinit();
1906 var aw: std.io.AllocatingWriter = undefined;
1907 defer aw.deinit();
1908 const bw = aw.init(std.testing.allocator);
19361909
19371910 const Recurse = struct { r: []const @This() };
19381911
19391912 // Normal operation
1940 try serializeMaxDepth(.{ 1, .{ 2, 3 } }, .{}, buf.writer(), 16);
1941 try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", buf.items);
1942 buf.clearRetainingCapacity();
1913 try serializeMaxDepth(.{ 1, .{ 2, 3 } }, .{}, bw, 16);
1914 try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", aw.getWritten());
1915 aw.clearRetainingCapacity();
19431916
1944 try serializeArbitraryDepth(.{ 1, .{ 2, 3 } }, .{}, buf.writer());
1945 try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", buf.items);
1946 buf.clearRetainingCapacity();
1917 try serializeArbitraryDepth(.{ 1, .{ 2, 3 } }, .{}, bw);
1918 try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", aw.getWritten());
1919 aw.clearRetainingCapacity();
19471920
19481921 // Max depth failing on non recursive type
19491922 try std.testing.expectError(
19501923 error.ExceededMaxDepth,
1951 serializeMaxDepth(.{ 1, .{ 2, .{ 3, 4 } } }, .{}, buf.writer(), 3),
1924 serializeMaxDepth(.{ 1, .{ 2, .{ 3, 4 } } }, .{}, bw, 3),
19521925 );
1953 try std.testing.expectEqualStrings("", buf.items);
1954 buf.clearRetainingCapacity();
1926 try std.testing.expectEqualStrings("", aw.getWritten());
1927 aw.clearRetainingCapacity();
19551928
19561929 // Max depth passing on recursive type
19571930 {
19581931 const maybe_recurse = Recurse{ .r = &.{} };
1959 try serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 2);
1960 try std.testing.expectEqualStrings(".{ .r = .{} }", buf.items);
1961 buf.clearRetainingCapacity();
1932 try serializeMaxDepth(maybe_recurse, .{}, bw, 2);
1933 try std.testing.expectEqualStrings(".{ .r = .{} }", aw.getWritten());
1934 aw.clearRetainingCapacity();
19621935 }
19631936
19641937 // Unchecked passing on recursive type
19651938 {
19661939 const maybe_recurse = Recurse{ .r = &.{} };
1967 try serializeArbitraryDepth(maybe_recurse, .{}, buf.writer());
1968 try std.testing.expectEqualStrings(".{ .r = .{} }", buf.items);
1969 buf.clearRetainingCapacity();
1940 try serializeArbitraryDepth(maybe_recurse, .{}, bw);
1941 try std.testing.expectEqualStrings(".{ .r = .{} }", aw.getWritten());
1942 aw.clearRetainingCapacity();
19701943 }
19711944
19721945 // Max depth failing on recursive type due to depth
......@@ -1975,10 +1948,10 @@ test "std.zon depth limits" {
19751948 maybe_recurse.r = &.{.{ .r = &.{} }};
19761949 try std.testing.expectError(
19771950 error.ExceededMaxDepth,
1978 serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 2),
1951 serializeMaxDepth(maybe_recurse, .{}, bw, 2),
19791952 );
1980 try std.testing.expectEqualStrings("", buf.items);
1981 buf.clearRetainingCapacity();
1953 try std.testing.expectEqualStrings("", aw.getWritten());
1954 aw.clearRetainingCapacity();
19821955 }
19831956
19841957 // Same but for a slice
......@@ -1988,23 +1961,23 @@ test "std.zon depth limits" {
19881961
19891962 try std.testing.expectError(
19901963 error.ExceededMaxDepth,
1991 serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 2),
1964 serializeMaxDepth(maybe_recurse, .{}, bw, 2),
19921965 );
1993 try std.testing.expectEqualStrings("", buf.items);
1994 buf.clearRetainingCapacity();
1966 try std.testing.expectEqualStrings("", aw.getWritten());
1967 aw.clearRetainingCapacity();
19951968
1996 var sz = serializer(buf.writer(), .{});
1969 var s: Serializer = .{ .writer = bw };
19971970
19981971 try std.testing.expectError(
19991972 error.ExceededMaxDepth,
2000 sz.tupleMaxDepth(maybe_recurse, .{}, 2),
1973 s.tupleMaxDepth(maybe_recurse, .{}, 2),
20011974 );
2002 try std.testing.expectEqualStrings("", buf.items);
2003 buf.clearRetainingCapacity();
1975 try std.testing.expectEqualStrings("", aw.getWritten());
1976 aw.clearRetainingCapacity();
20041977
2005 try sz.tupleArbitraryDepth(maybe_recurse, .{});
2006 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items);
2007 buf.clearRetainingCapacity();
1978 try s.tupleArbitraryDepth(maybe_recurse, .{});
1979 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", aw.getWritten());
1980 aw.clearRetainingCapacity();
20081981 }
20091982
20101983 // A slice succeeding
......@@ -2012,19 +1985,19 @@ test "std.zon depth limits" {
20121985 var temp: [1]Recurse = .{.{ .r = &.{} }};
20131986 const maybe_recurse: []const Recurse = &temp;
20141987
2015 try serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 3);
2016 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items);
2017 buf.clearRetainingCapacity();
1988 try serializeMaxDepth(maybe_recurse, .{}, bw, 3);
1989 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", aw.getWritten());
1990 aw.clearRetainingCapacity();
20181991
2019 var sz = serializer(buf.writer(), .{});
1992 var s: Serializer = .{ .writer = bw };
20201993
2021 try sz.tupleMaxDepth(maybe_recurse, .{}, 3);
2022 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items);
2023 buf.clearRetainingCapacity();
1994 try s.tupleMaxDepth(maybe_recurse, .{}, 3);
1995 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", aw.getWritten());
1996 aw.clearRetainingCapacity();
20241997
2025 try sz.tupleArbitraryDepth(maybe_recurse, .{});
2026 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items);
2027 buf.clearRetainingCapacity();
1998 try s.tupleArbitraryDepth(maybe_recurse, .{});
1999 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", aw.getWritten());
2000 aw.clearRetainingCapacity();
20282001 }
20292002
20302003 // Max depth failing on recursive type due to recursion
......@@ -2035,46 +2008,46 @@ test "std.zon depth limits" {
20352008
20362009 try std.testing.expectError(
20372010 error.ExceededMaxDepth,
2038 serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 128),
2011 serializeMaxDepth(maybe_recurse, .{}, bw, 128),
20392012 );
2040 try std.testing.expectEqualStrings("", buf.items);
2041 buf.clearRetainingCapacity();
2013 try std.testing.expectEqualStrings("", aw.getWritten());
2014 aw.clearRetainingCapacity();
20422015
2043 var sz = serializer(buf.writer(), .{});
2016 var s: Serializer = .{ .writer = bw };
20442017 try std.testing.expectError(
20452018 error.ExceededMaxDepth,
2046 sz.tupleMaxDepth(maybe_recurse, .{}, 128),
2019 s.tupleMaxDepth(maybe_recurse, .{}, 128),
20472020 );
2048 try std.testing.expectEqualStrings("", buf.items);
2049 buf.clearRetainingCapacity();
2021 try std.testing.expectEqualStrings("", aw.getWritten());
2022 aw.clearRetainingCapacity();
20502023 }
20512024
20522025 // Max depth on other parts of the lower level API
20532026 {
2054 var sz = serializer(buf.writer(), .{});
2027 var s: Serializer = .{ .writer = bw };
20552028
20562029 const maybe_recurse: []const Recurse = &.{};
20572030
2058 try std.testing.expectError(error.ExceededMaxDepth, sz.valueMaxDepth(1, .{}, 0));
2059 try sz.valueMaxDepth(2, .{}, 1);
2060 try sz.value(3, .{});
2061 try sz.valueArbitraryDepth(maybe_recurse, .{});
2031 try std.testing.expectError(error.ExceededMaxDepth, s.valueMaxDepth(1, .{}, 0));
2032 try s.valueMaxDepth(2, .{}, 1);
2033 try s.value(3, .{});
2034 try s.valueArbitraryDepth(maybe_recurse, .{});
20622035
2063 var s = try sz.beginStruct(.{});
2064 try std.testing.expectError(error.ExceededMaxDepth, s.fieldMaxDepth("a", 1, .{}, 0));
2065 try s.fieldMaxDepth("b", 4, .{}, 1);
2066 try s.field("c", 5, .{});
2067 try s.fieldArbitraryDepth("d", maybe_recurse, .{});
2068 try s.end();
2036 var wip_struct = try s.beginStruct(.{});
2037 try std.testing.expectError(error.ExceededMaxDepth, wip_struct.fieldMaxDepth("a", 1, .{}, 0));
2038 try wip_struct.fieldMaxDepth("b", 4, .{}, 1);
2039 try wip_struct.field("c", 5, .{});
2040 try wip_struct.fieldArbitraryDepth("d", maybe_recurse, .{});
2041 try wip_struct.end();
20692042
2070 var t = try sz.beginTuple(.{});
2043 var t = try s.beginTuple(.{});
20712044 try std.testing.expectError(error.ExceededMaxDepth, t.fieldMaxDepth(1, .{}, 0));
20722045 try t.fieldMaxDepth(6, .{}, 1);
20732046 try t.field(7, .{});
20742047 try t.fieldArbitraryDepth(maybe_recurse, .{});
20752048 try t.end();
20762049
2077 var a = try sz.beginTuple(.{});
2050 var a = try s.beginTuple(.{});
20782051 try std.testing.expectError(error.ExceededMaxDepth, a.fieldMaxDepth(1, .{}, 0));
20792052 try a.fieldMaxDepth(8, .{}, 1);
20802053 try a.field(9, .{});
......@@ -2095,7 +2068,7 @@ test "std.zon depth limits" {
20952068 \\ 9,
20962069 \\ .{},
20972070 \\}
2098 , buf.items);
2071 , aw.getWritten());
20992072 }
21002073}
21012074
......@@ -2191,42 +2164,42 @@ test "std.zon stringify primitives" {
21912164}
21922165
21932166test "std.zon stringify ident" {
2194 var buf = std.ArrayList(u8).init(std.testing.allocator);
2195 defer buf.deinit();
2196 var sz = serializer(buf.writer(), .{});
2167 var aw: std.io.AllocatingWriter = undefined;
2168 defer aw.deinit();
2169 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
21972170
21982171 try expectSerializeEqual(".{ .a = 0 }", .{ .a = 0 }, .{});
2199 try sz.ident("a");
2200 try std.testing.expectEqualStrings(".a", buf.items);
2201 buf.clearRetainingCapacity();
2172 try s.ident("a");
2173 try std.testing.expectEqualStrings(".a", aw.getWritten());
2174 aw.clearRetainingCapacity();
22022175
2203 try sz.ident("foo_1");
2204 try std.testing.expectEqualStrings(".foo_1", buf.items);
2205 buf.clearRetainingCapacity();
2176 try s.ident("foo_1");
2177 try std.testing.expectEqualStrings(".foo_1", aw.getWritten());
2178 aw.clearRetainingCapacity();
22062179
2207 try sz.ident("_foo_1");
2208 try std.testing.expectEqualStrings("._foo_1", buf.items);
2209 buf.clearRetainingCapacity();
2180 try s.ident("_foo_1");
2181 try std.testing.expectEqualStrings("._foo_1", aw.getWritten());
2182 aw.clearRetainingCapacity();
22102183
2211 try sz.ident("foo bar");
2212 try std.testing.expectEqualStrings(".@\"foo bar\"", buf.items);
2213 buf.clearRetainingCapacity();
2184 try s.ident("foo bar");
2185 try std.testing.expectEqualStrings(".@\"foo bar\"", aw.getWritten());
2186 aw.clearRetainingCapacity();
22142187
2215 try sz.ident("1foo");
2216 try std.testing.expectEqualStrings(".@\"1foo\"", buf.items);
2217 buf.clearRetainingCapacity();
2188 try s.ident("1foo");
2189 try std.testing.expectEqualStrings(".@\"1foo\"", aw.getWritten());
2190 aw.clearRetainingCapacity();
22182191
2219 try sz.ident("var");
2220 try std.testing.expectEqualStrings(".@\"var\"", buf.items);
2221 buf.clearRetainingCapacity();
2192 try s.ident("var");
2193 try std.testing.expectEqualStrings(".@\"var\"", aw.getWritten());
2194 aw.clearRetainingCapacity();
22222195
2223 try sz.ident("true");
2224 try std.testing.expectEqualStrings(".true", buf.items);
2225 buf.clearRetainingCapacity();
2196 try s.ident("true");
2197 try std.testing.expectEqualStrings(".true", aw.getWritten());
2198 aw.clearRetainingCapacity();
22262199
2227 try sz.ident("_");
2228 try std.testing.expectEqualStrings("._", buf.items);
2229 buf.clearRetainingCapacity();
2200 try s.ident("_");
2201 try std.testing.expectEqualStrings("._", aw.getWritten());
2202 aw.clearRetainingCapacity();
22302203
22312204 const Enum = enum {
22322205 @"foo bar",
......@@ -2238,40 +2211,40 @@ test "std.zon stringify ident" {
22382211}
22392212
22402213test "std.zon stringify as tuple" {
2241 var buf = std.ArrayList(u8).init(std.testing.allocator);
2242 defer buf.deinit();
2243 var sz = serializer(buf.writer(), .{});
2214 var aw: std.io.AllocatingWriter = undefined;
2215 defer aw.deinit();
2216 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
22442217
22452218 // Tuples
2246 try sz.tuple(.{ 1, 2 }, .{});
2247 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);
2248 buf.clearRetainingCapacity();
2219 try s.tuple(.{ 1, 2 }, .{});
2220 try std.testing.expectEqualStrings(".{ 1, 2 }", aw.getWritten());
2221 aw.clearRetainingCapacity();
22492222
22502223 // Slice
2251 try sz.tuple(@as([]const u8, &.{ 1, 2 }), .{});
2252 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);
2253 buf.clearRetainingCapacity();
2224 try s.tuple(@as([]const u8, &.{ 1, 2 }), .{});
2225 try std.testing.expectEqualStrings(".{ 1, 2 }", aw.getWritten());
2226 aw.clearRetainingCapacity();
22542227
22552228 // Array
2256 try sz.tuple([2]u8{ 1, 2 }, .{});
2257 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);
2258 buf.clearRetainingCapacity();
2229 try s.tuple([2]u8{ 1, 2 }, .{});
2230 try std.testing.expectEqualStrings(".{ 1, 2 }", aw.getWritten());
2231 aw.clearRetainingCapacity();
22592232}
22602233
22612234test "std.zon stringify as float" {
2262 var buf = std.ArrayList(u8).init(std.testing.allocator);
2263 defer buf.deinit();
2264 var sz = serializer(buf.writer(), .{});
2235 var aw: std.io.AllocatingWriter = undefined;
2236 defer aw.deinit();
2237 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
22652238
22662239 // Comptime float
2267 try sz.float(2.5);
2268 try std.testing.expectEqualStrings("2.5", buf.items);
2269 buf.clearRetainingCapacity();
2240 try s.float(2.5);
2241 try std.testing.expectEqualStrings("2.5", aw.getWritten());
2242 aw.clearRetainingCapacity();
22702243
22712244 // Sized float
2272 try sz.float(@as(f32, 2.5));
2273 try std.testing.expectEqualStrings("2.5", buf.items);
2274 buf.clearRetainingCapacity();
2245 try s.float(@as(f32, 2.5));
2246 try std.testing.expectEqualStrings("2.5", aw.getWritten());
2247 aw.clearRetainingCapacity();
22752248}
22762249
22772250test "std.zon stringify vector" {
......@@ -2363,13 +2336,13 @@ test "std.zon pointers" {
23632336}
23642337
23652338test "std.zon tuple/struct field" {
2366 var buf = std.ArrayList(u8).init(std.testing.allocator);
2367 defer buf.deinit();
2368 var sz = serializer(buf.writer(), .{});
2339 var aw: std.io.AllocatingWriter = undefined;
2340 defer aw.deinit();
2341 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
23692342
23702343 // Test on structs
23712344 {
2372 var root = try sz.beginStruct(.{});
2345 var root = try s.beginStruct(.{});
23732346 {
23742347 var tuple = try root.beginTupleField("foo", .{});
23752348 try tuple.field(0, .{});
......@@ -2395,13 +2368,13 @@ test "std.zon tuple/struct field" {
23952368 \\ .b = 1,
23962369 \\ },
23972370 \\}
2398 , buf.items);
2399 buf.clearRetainingCapacity();
2371 , aw.getWritten());
2372 aw.clearRetainingCapacity();
24002373 }
24012374
24022375 // Test on tuples
24032376 {
2404 var root = try sz.beginTuple(.{});
2377 var root = try s.beginTuple(.{});
24052378 {
24062379 var tuple = try root.beginTupleField(.{});
24072380 try tuple.field(0, .{});
......@@ -2427,7 +2400,7 @@ test "std.zon tuple/struct field" {
24272400 \\ .b = 1,
24282401 \\ },
24292402 \\}
2430 , buf.items);
2431 buf.clearRetainingCapacity();
2403 , aw.getWritten());
2404 aw.clearRetainingCapacity();
24322405 }
24332406}