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(...@@ -2766,8 +2766,9 @@ fn dumpBadDirnameHelp(
2766 comptime msg: []const u8,2766 comptime msg: []const u8,
2767 args: anytype,2767 args: anytype,
2768) anyerror!void {2768) anyerror!void {
2769 var w = debug.lockStdErr2();2769 var buffered_writer = debug.lockStdErr2();
2770 defer debug.unlockStdErr();2770 defer debug.unlockStdErr();
2771 const w = &buffered_writer;
27712772
2772 const stderr = io.getStdErr();2773 const stderr = io.getStdErr();
2773 try w.print(msg, args);2774 try w.print(msg, args);
...@@ -2784,7 +2785,7 @@ fn dumpBadDirnameHelp(...@@ -2784,7 +2785,7 @@ fn dumpBadDirnameHelp(
27842785
2785 if (asking_step) |as| {2786 if (asking_step) |as| {
2786 tty_config.setColor(w, .red) catch {};2787 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});
2788 tty_config.setColor(w, .reset) catch {};2789 tty_config.setColor(w, .reset) catch {};
27892790
2790 as.dump(stderr);2791 as.dump(stderr);
...@@ -2802,7 +2803,8 @@ pub fn dumpBadGetPathHelp(...@@ -2802,7 +2803,8 @@ pub fn dumpBadGetPathHelp(
2802 src_builder: *Build,2803 src_builder: *Build,
2803 asking_step: ?*Step,2804 asking_step: ?*Step,
2804) anyerror!void {2805) anyerror!void {
2805 var w = stderr.unbufferedWriter();2806 var buffered_writer = stderr.unbufferedWriter();
2807 const w = &buffered_writer;
2806 try w.print(2808 try w.print(
2807 \\getPath() was called on a GeneratedFile that wasn't built yet.2809 \\getPath() was called on a GeneratedFile that wasn't built yet.
2808 \\ source package path: {s}2810 \\ source package path: {s}
...@@ -2821,7 +2823,7 @@ pub fn dumpBadGetPathHelp(...@@ -2821,7 +2823,7 @@ pub fn dumpBadGetPathHelp(
2821 s.dump(stderr);2823 s.dump(stderr);
2822 if (asking_step) |as| {2824 if (asking_step) |as| {
2823 tty_config.setColor(w, .red) catch {};2825 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});
2825 tty_config.setColor(w, .reset) catch {};2827 tty_config.setColor(w, .reset) catch {};
28262828
2827 as.dump(stderr);2829 as.dump(stderr);
lib/std/Build/Cache.zig+16-15
...@@ -1061,14 +1061,17 @@ pub const Manifest = struct {...@@ -1061,14 +1061,17 @@ pub const Manifest = struct {
1061 }1061 }
10621062
1063 fn addDepFileMaybePost(self: *Manifest, dir: fs.Dir, dep_file_basename: []const u8) !void {1063 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);1064 const gpa = self.cache.gpa;
1065 defer self.cache.gpa.free(dep_file_contents);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 var error_buf: std.ArrayListUnmanaged(u8) = .empty;
1068 defer error_buf.deinit();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 };
1072 while (it.next()) |token| {1075 while (it.next()) |token| {
1073 switch (token) {1076 switch (token) {
1074 // We don't care about targets, we only want the prereqs1077 // We don't care about targets, we only want the prereqs
...@@ -1078,16 +1081,14 @@ pub const Manifest = struct {...@@ -1078,16 +1081,14 @@ pub const Manifest = struct {
1078 _ = try self.addFile(file_path, null);1081 _ = try self.addFile(file_path, null);
1079 } else try self.addFilePost(file_path),1082 } else try self.addFilePost(file_path),
1080 .prereq_must_resolve => {1083 .prereq_must_resolve => {
1081 var resolve_buf = std.ArrayList(u8).init(self.cache.gpa);1084 resolve_buf.clearRetainingCapacity();
1082 defer resolve_buf.deinit();1085 try token.resolve(gpa, &resolve_buf);
1083
1084 try token.resolve(resolve_buf.writer());
1085 if (self.manifest_file == null) {1086 if (self.manifest_file == null) {
1086 _ = try self.addFile(resolve_buf.items, null);1087 _ = try self.addFile(resolve_buf.items, null);
1087 } else try self.addFilePost(resolve_buf.items);1088 } else try self.addFilePost(resolve_buf.items);
1088 },1089 },
1089 else => |err| {1090 else => |err| {
1090 try err.printError(error_buf.writer());1091 try err.printError(gpa, &error_buf);
1091 log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items });1092 log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items });
1092 return error.InvalidDepFile;1093 return error.InvalidDepFile;
1093 },1094 },
...@@ -1125,13 +1126,13 @@ pub const Manifest = struct {...@@ -1125,13 +1126,13 @@ pub const Manifest = struct {
1125 if (self.manifest_dirty) {1126 if (self.manifest_dirty) {
1126 self.manifest_dirty = false;1127 self.manifest_dirty = false;
11271128
1128 var contents = std.ArrayList(u8).init(self.cache.gpa);1129 const gpa = self.cache.gpa;
1129 defer contents.deinit();1130 var contents: std.ArrayListUnmanaged(u8) = .empty;
1131 defer contents.deinit(gpa);
11301132
1131 const writer = contents.writer();1133 try contents.appendSlice(gpa, manifest_header ++ "\n");
1132 try writer.writeAll(manifest_header ++ "\n");
1133 for (self.files.keys()) |file| {1134 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", .{
1135 file.stat.size,1136 file.stat.size,
1136 file.stat.inode,1137 file.stat.inode,
1137 file.stat.mtime,1138 file.stat.mtime,
lib/std/Build/Cache/DepTokenizer.zig+38-151
...@@ -7,6 +7,7 @@ state: State = .lhs,...@@ -7,6 +7,7 @@ state: State = .lhs,
7const std = @import("std");7const std = @import("std");
8const testing = std.testing;8const testing = std.testing;
9const assert = std.debug.assert;9const assert = std.debug.assert;
10const Allocator = std.mem.Allocator;
1011
11pub fn next(self: *Tokenizer) ?Token {12pub fn next(self: *Tokenizer) ?Token {
12 var start = self.index;13 var start = self.index;
...@@ -362,7 +363,7 @@ pub const Token = union(enum) {...@@ -362,7 +363,7 @@ pub const Token = union(enum) {
362 };363 };
363364
364 /// Resolve escapes in target or prereq. Only valid with .target_must_resolve or .prereq_must_resolve.365 /// 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 {
366 switch (self) {367 switch (self) {
367 .target_must_resolve => |bytes| {368 .target_must_resolve => |bytes| {
368 var state: enum { start, escape, dollar } = .start;369 var state: enum { start, escape, dollar } = .start;
...@@ -372,27 +373,27 @@ pub const Token = union(enum) {...@@ -372,27 +373,27 @@ pub const Token = union(enum) {
372 switch (c) {373 switch (c) {
373 '\\' => state = .escape,374 '\\' => state = .escape,
374 '$' => state = .dollar,375 '$' => state = .dollar,
375 else => try writer.writeByte(c),376 else => try list.append(gpa, c),
376 }377 }
377 },378 },
378 .escape => {379 .escape => {
379 switch (c) {380 switch (c) {
380 ' ', '#', '\\' => {},381 ' ', '#', '\\' => {},
381 '$' => {382 '$' => {
382 try writer.writeByte('\\');383 try list.append(gpa, '\\');
383 state = .dollar;384 state = .dollar;
384 continue;385 continue;
385 },386 },
386 else => try writer.writeByte('\\'),387 else => try list.append(gpa, '\\'),
387 }388 }
388 try writer.writeByte(c);389 try list.append(gpa, c);
389 state = .start;390 state = .start;
390 },391 },
391 .dollar => {392 .dollar => {
392 try writer.writeByte('$');393 try list.append(gpa, '$');
393 switch (c) {394 switch (c) {
394 '$' => {},395 '$' => {},
395 else => try writer.writeByte(c),396 else => try list.append(gpa, c),
396 }397 }
397 state = .start;398 state = .start;
398 },399 },
...@@ -406,19 +407,19 @@ pub const Token = union(enum) {...@@ -406,19 +407,19 @@ pub const Token = union(enum) {
406 .start => {407 .start => {
407 switch (c) {408 switch (c) {
408 '\\' => state = .escape,409 '\\' => state = .escape,
409 else => try writer.writeByte(c),410 else => try list.append(gpa, c),
410 }411 }
411 },412 },
412 .escape => {413 .escape => {
413 switch (c) {414 switch (c) {
414 ' ' => {},415 ' ' => {},
415 '\\' => {416 '\\' => {
416 try writer.writeByte(c);417 try list.append(gpa, c);
417 continue;418 continue;
418 },419 },
419 else => try writer.writeByte('\\'),420 else => try list.append(gpa, '\\'),
420 }421 }
421 try writer.writeByte(c);422 try list.append(gpa, c);
422 state = .start;423 state = .start;
423 },424 },
424 }425 }
...@@ -428,20 +429,20 @@ pub const Token = union(enum) {...@@ -428,20 +429,20 @@ pub const Token = union(enum) {
428 }429 }
429 }430 }
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 {
432 switch (self) {433 switch (self) {
433 .target, .target_must_resolve, .prereq, .prereq_must_resolve => unreachable, // not an error434 .target, .target_must_resolve, .prereq, .prereq_must_resolve => unreachable, // not an error
434 .incomplete_quoted_prerequisite,435 .incomplete_quoted_prerequisite,
435 .incomplete_target,436 .incomplete_target,
436 => |index_and_bytes| {437 => |index_and_bytes| {
437 try writer.print("{s} '", .{self.errStr()});438 try list.print("{s} '", .{self.errStr()});
438 if (self == .incomplete_target) {439 if (self == .incomplete_target) {
439 const tmp = Token{ .target_must_resolve = index_and_bytes.bytes };440 const tmp = Token{ .target_must_resolve = index_and_bytes.bytes };
440 try tmp.resolve(writer);441 try tmp.resolve(gpa, list);
441 } else {442 } else {
442 try printCharValues(writer, index_and_bytes.bytes);443 try printCharValues(gpa, list, index_and_bytes.bytes);
443 }444 }
444 try writer.print("' at position {d}", .{index_and_bytes.index});445 try list.print(gpa, "' at position {d}", .{index_and_bytes.index});
445 },446 },
446 .invalid_target,447 .invalid_target,
447 .bad_target_escape,448 .bad_target_escape,
...@@ -450,9 +451,9 @@ pub const Token = union(enum) {...@@ -450,9 +451,9 @@ pub const Token = union(enum) {
450 .incomplete_escape,451 .incomplete_escape,
451 .expected_colon,452 .expected_colon,
452 => |index_and_char| {453 => |index_and_char| {
453 try writer.writeAll("illegal char ");454 try list.appendSlice("illegal char ");
454 try printUnderstandableChar(writer, index_and_char.char);455 try printUnderstandableChar(gpa, list, index_and_char.char);
455 try writer.print(" at position {d}: {s}", .{ index_and_char.index, self.errStr() });456 try list.print(gpa, " at position {d}: {s}", .{ index_and_char.index, self.errStr() });
456 },457 },
457 }458 }
458 }459 }
...@@ -1026,41 +1027,41 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {...@@ -1026,41 +1027,41 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
1026 defer arena_allocator.deinit();1027 defer arena_allocator.deinit();
10271028
1028 var it: Tokenizer = .{ .bytes = input };1029 var it: Tokenizer = .{ .bytes = input };
1029 var buffer = std.ArrayList(u8).init(arena);1030 var buffer: std.ArrayListUnmanaged(u8) = .empty;
1030 var resolve_buf = std.ArrayList(u8).init(arena);1031 var resolve_buf: std.ArrayListUnmanaged(u8) = .empty;
1031 var i: usize = 0;1032 var i: usize = 0;
1032 while (it.next()) |token| {1033 while (it.next()) |token| {
1033 if (i != 0) try buffer.appendSlice("\n");1034 if (i != 0) try buffer.appendSlice(arena, "\n");
1034 switch (token) {1035 switch (token) {
1035 .target, .prereq => |bytes| {1036 .target, .prereq => |bytes| {
1036 try buffer.appendSlice(@tagName(token));1037 try buffer.appendSlice(arena, @tagName(token));
1037 try buffer.appendSlice(" = {");1038 try buffer.appendSlice(arena, " = {");
1038 for (bytes) |b| {1039 for (bytes) |b| {
1039 try buffer.append(printable_char_tab[b]);1040 try buffer.append(arena, printable_char_tab[b]);
1040 }1041 }
1041 try buffer.appendSlice("}");1042 try buffer.appendSlice(arena, "}");
1042 },1043 },
1043 .target_must_resolve => {1044 .target_must_resolve => {
1044 try buffer.appendSlice("target = {");1045 try buffer.appendSlice(arena, "target = {");
1045 try token.resolve(resolve_buf.writer());1046 try token.resolve(arena, &resolve_buf);
1046 for (resolve_buf.items) |b| {1047 for (resolve_buf.items) |b| {
1047 try buffer.append(printable_char_tab[b]);1048 try buffer.append(arena, printable_char_tab[b]);
1048 }1049 }
1049 resolve_buf.items.len = 0;1050 resolve_buf.items.len = 0;
1050 try buffer.appendSlice("}");1051 try buffer.appendSlice(arena, "}");
1051 },1052 },
1052 .prereq_must_resolve => {1053 .prereq_must_resolve => {
1053 try buffer.appendSlice("prereq = {");1054 try buffer.appendSlice(arena, "prereq = {");
1054 try token.resolve(resolve_buf.writer());1055 try token.resolve(arena, &resolve_buf);
1055 for (resolve_buf.items) |b| {1056 for (resolve_buf.items) |b| {
1056 try buffer.append(printable_char_tab[b]);1057 try buffer.append(arena, printable_char_tab[b]);
1057 }1058 }
1058 resolve_buf.items.len = 0;1059 resolve_buf.items.len = 0;
1059 try buffer.appendSlice("}");1060 try buffer.appendSlice(arena, "}");
1060 },1061 },
1061 else => {1062 else => {
1062 try buffer.appendSlice("ERROR: ");1063 try buffer.appendSlice(arena, "ERROR: ");
1063 try token.printError(buffer.writer());1064 try token.printError(arena, &buffer);
1064 break;1065 break;
1065 },1066 },
1066 }1067 }
...@@ -1072,121 +1073,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {...@@ -1072,121 +1073,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
1072 return;1073 return;
1073 }1074 }
10741075
1075 const out = std.io.getStdErr().writer();1076 try testing.expectEqualStrings(expect, buffer.items);
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]);
1190}1077}
11911078
1192fn printCharValues(out: anytype, bytes: []const u8) !void {1079fn 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...@@ -976,37 +976,12 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
976 @memcpy(self.items[old_len..][0..items.len], items);976 @memcpy(self.items[old_len..][0..items.len], items);
977 }977 }
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
1004 pub fn print(self: *Self, gpa: Allocator, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {979 pub fn print(self: *Self, gpa: Allocator, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {
1005 comptime assert(T == u8);980 comptime assert(T == u8);
1006 try self.ensureUnusedCapacity(gpa, fmt.len);981 try self.ensureUnusedCapacity(gpa, fmt.len);
1007 var alw: std.io.ArrayListWriter = undefined;982 var aw: std.io.AllocatingWriter = undefined;
1008 const bw = alw.fromOwned(gpa, self);983 const bw = aw.fromArrayList(gpa, self);
1009 defer self.* = alw.toOwned();984 defer self.* = aw.toArrayList();
1010 bw.print(fmt, args) catch return error.OutOfMemory;985 bw.print(fmt, args) catch return error.OutOfMemory;
1011 }986 }
1012987
lib/std/io.zig+2-2
...@@ -301,7 +301,7 @@ pub const AnyWriter = Writer;...@@ -301,7 +301,7 @@ pub const AnyWriter = Writer;
301pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;301pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
302302
303pub const BufferedWriter = @import("io/BufferedWriter.zig");303pub const BufferedWriter = @import("io/BufferedWriter.zig");
304pub const ArrayListWriter = @import("io/ArrayListWriter.zig");304pub const AllocatingWriter = @import("io/AllocatingWriter.zig");
305305
306pub const BufferedReader = @import("io/buffered_reader.zig").BufferedReader;306pub const BufferedReader = @import("io/buffered_reader.zig").BufferedReader;
307pub const bufferedReader = @import("io/buffered_reader.zig").bufferedReader;307pub const bufferedReader = @import("io/buffered_reader.zig").bufferedReader;
...@@ -784,7 +784,7 @@ test {...@@ -784,7 +784,7 @@ test {
784 _ = Writer;784 _ = Writer;
785 _ = CountingWriter;785 _ = CountingWriter;
786 _ = FixedBufferStream;786 _ = FixedBufferStream;
787 _ = ArrayListWriter;787 _ = AllocatingWriter;
788 _ = @import("io/bit_reader.zig");788 _ = @import("io/bit_reader.zig");
789 _ = @import("io/bit_writer.zig");789 _ = @import("io/bit_writer.zig");
790 _ = @import("io/buffered_atomic_file.zig");790 _ = @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,...@@ -19,6 +19,9 @@ end: usize = 0,
19/// vectors through the underlying write calls as possible.19/// vectors through the underlying write calls as possible.
20pub const max_buffers_len = 8;20pub 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.
22pub fn writer(bw: *BufferedWriter) Writer {25pub fn writer(bw: *BufferedWriter) Writer {
23 return .{26 return .{
24 .context = bw,27 .context = bw,
...@@ -212,6 +215,7 @@ pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize {...@@ -212,6 +215,7 @@ pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize {
212215
213 const new_end = end + n;216 const new_end = end + n;
214 if (new_end <= buffer.len) {217 if (new_end <= buffer.len) {
218 @branchHint(.likely);
215 @memset(buffer[end..][0..n], byte);219 @memset(buffer[end..][0..n], byte);
216 bw.end = new_end;220 bw.end = new_end;
217 return n;221 return n;
...@@ -226,6 +230,7 @@ pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize {...@@ -226,6 +230,7 @@ pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize {
226 bw.end = remainder.len;230 bw.end = remainder.len;
227 return 0;231 return 0;
228 }232 }
233 assert(bw.buffer.ptr == buffer.ptr); // TODO this is not a valid assertion
229 @memset(buffer[0..n], byte);234 @memset(buffer[0..n], byte);
230 bw.end = n;235 bw.end = n;
231 return n;236 return n;
lib/std/io/tty.zig+2-7
...@@ -71,12 +71,7 @@ pub const Config = union(enum) {...@@ -71,12 +71,7 @@ pub const Config = union(enum) {
71 reset_attributes: u16,71 reset_attributes: u16,
72 };72 };
7373
74 pub fn setColor(74 pub fn setColor(conf: Config, bw: *std.io.BufferedWriter, color: Color) anyerror!void {
75 conf: Config,
76 writer: anytype,
77 color: Color,
78 ) (@typeInfo(@TypeOf(writer.writeAll(""))).error_union.error_set ||
79 windows.SetConsoleTextAttributeError)!void {
80 nosuspend switch (conf) {75 nosuspend switch (conf) {
81 .no_color => return,76 .no_color => return,
82 .escape_codes => {77 .escape_codes => {
...@@ -101,7 +96,7 @@ pub const Config = union(enum) {...@@ -101,7 +96,7 @@ pub const Config = union(enum) {
101 .dim => "\x1b[2m",96 .dim => "\x1b[2m",
102 .reset => "\x1b[0m",97 .reset => "\x1b[0m",
103 };98 };
104 try writer.writeAll(color_string);99 try bw.writeAll(color_string);
105 },100 },
106 .windows_api => |ctx| if (native_os == .windows) {101 .windows_api => |ctx| if (native_os == .windows) {
107 const attributes = switch (color) {102 const attributes = switch (color) {
lib/std/process/Child.zig+2-1
...@@ -1004,7 +1004,8 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {...@@ -1004,7 +1004,8 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
10041004
1005fn writeIntFd(fd: i32, value: ErrInt) !void {1005fn writeIntFd(fd: i32, value: ErrInt) !void {
1006 const file: File = .{ .handle = fd };1006 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;
1008}1009}
10091010
1010fn readIntFd(fd: i32) !ErrInt {1011fn readIntFd(fd: i32) !ErrInt {
lib/std/zig.zig+12-12
...@@ -475,37 +475,37 @@ pub fn stringEscape(...@@ -475,37 +475,37 @@ pub fn stringEscape(
475 bytes: []const u8,475 bytes: []const u8,
476 comptime f: []const u8,476 comptime f: []const u8,
477 options: std.fmt.FormatOptions,477 options: std.fmt.FormatOptions,
478 writer: anytype,478 bw: *std.io.BufferedWriter,
479) !void {479) !void {
480 _ = options;480 _ = options;
481 for (bytes) |byte| switch (byte) {481 for (bytes) |byte| switch (byte) {
482 '\n' => try writer.writeAll("\\n"),482 '\n' => try bw.writeAll("\\n"),
483 '\r' => try writer.writeAll("\\r"),483 '\r' => try bw.writeAll("\\r"),
484 '\t' => try writer.writeAll("\\t"),484 '\t' => try bw.writeAll("\\t"),
485 '\\' => try writer.writeAll("\\\\"),485 '\\' => try bw.writeAll("\\\\"),
486 '"' => {486 '"' => {
487 if (f.len == 1 and f[0] == '\'') {487 if (f.len == 1 and f[0] == '\'') {
488 try writer.writeByte('"');488 try bw.writeByte('"');
489 } else if (f.len == 0) {489 } else if (f.len == 0) {
490 try writer.writeAll("\\\"");490 try bw.writeAll("\\\"");
491 } else {491 } else {
492 @compileError("expected {} or {'}, found {" ++ f ++ "}");492 @compileError("expected {} or {'}, found {" ++ f ++ "}");
493 }493 }
494 },494 },
495 '\'' => {495 '\'' => {
496 if (f.len == 1 and f[0] == '\'') {496 if (f.len == 1 and f[0] == '\'') {
497 try writer.writeAll("\\'");497 try bw.writeAll("\\'");
498 } else if (f.len == 0) {498 } else if (f.len == 0) {
499 try writer.writeByte('\'');499 try bw.writeByte('\'');
500 } else {500 } else {
501 @compileError("expected {} or {'}, found {" ++ f ++ "}");501 @compileError("expected {} or {'}, found {" ++ f ++ "}");
502 }502 }
503 },503 },
504 ' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte),504 ' ', '!', '#'...'&', '('...'[', ']'...'~' => try bw.writeByte(byte),
505 // Use hex escapes for rest any unprintable characters.505 // Use hex escapes for rest any unprintable characters.
506 else => {506 else => {
507 try writer.writeAll("\\x");507 try bw.writeAll("\\x");
508 try std.fmt.formatInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }, writer);508 try bw.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' });
509 },509 },
510 };510 };
511}511}
lib/std/zon/stringify.zig+915-942
...@@ -40,15 +40,12 @@ pub const SerializeOptions = struct {...@@ -40,15 +40,12 @@ pub const SerializeOptions = struct {
40/// Serialize the given value as ZON.40/// Serialize the given value as ZON.
41///41///
42/// It is asserted at comptime that `@TypeOf(val)` is not a recursive type.42/// It is asserted at comptime that `@TypeOf(val)` is not a recursive type.
43pub fn serialize(43pub fn serialize(val: anytype, options: SerializeOptions, writer: *std.io.BufferedWriter) anyerror!void {
44 val: anytype,44 var s: Serializer = .{
45 options: SerializeOptions,45 .writer = writer,
46 writer: anytype,46 .options = .{ .whitespace = options.whitespace },
47) @TypeOf(writer).Error!void {47 };
48 var sz = serializer(writer, .{48 try s.value(val, .{
49 .whitespace = options.whitespace,
50 });
51 try sz.value(val, .{
52 .emit_codepoint_literals = options.emit_codepoint_literals,49 .emit_codepoint_literals = options.emit_codepoint_literals,
53 .emit_strings_as_containers = options.emit_strings_as_containers,50 .emit_strings_as_containers = options.emit_strings_as_containers,
54 .emit_default_optional_fields = options.emit_default_optional_fields,51 .emit_default_optional_fields = options.emit_default_optional_fields,
...@@ -62,13 +59,14 @@ pub fn serialize(...@@ -62,13 +59,14 @@ pub fn serialize(
62pub fn serializeMaxDepth(59pub fn serializeMaxDepth(
63 val: anytype,60 val: anytype,
64 options: SerializeOptions,61 options: SerializeOptions,
65 writer: anytype,62 writer: *std.io.BufferedWriter,
66 depth: usize,63 depth: usize,
67) (@TypeOf(writer).Error || error{ExceededMaxDepth})!void {64) anyerror!void {
68 var sz = serializer(writer, .{65 var s: Serializer = .{
69 .whitespace = options.whitespace,66 .writer = writer,
70 });67 .options = .{ .whitespace = options.whitespace },
71 try sz.valueMaxDepth(val, .{68 };
69 try s.valueMaxDepth(val, .{
72 .emit_codepoint_literals = options.emit_codepoint_literals,70 .emit_codepoint_literals = options.emit_codepoint_literals,
73 .emit_strings_as_containers = options.emit_strings_as_containers,71 .emit_strings_as_containers = options.emit_strings_as_containers,
74 .emit_default_optional_fields = options.emit_default_optional_fields,72 .emit_default_optional_fields = options.emit_default_optional_fields,
...@@ -81,44 +79,45 @@ pub fn serializeMaxDepth(...@@ -81,44 +79,45 @@ pub fn serializeMaxDepth(
81pub fn serializeArbitraryDepth(79pub fn serializeArbitraryDepth(
82 val: anytype,80 val: anytype,
83 options: SerializeOptions,81 options: SerializeOptions,
84 writer: anytype,82 writer: *std.io.BufferedWriter,
85) @TypeOf(writer).Error!void {83) anyerror!void {
86 var sz = serializer(writer, .{84 var s: Serializer = .{
87 .whitespace = options.whitespace,85 .writer = writer,
88 });86 .options = .{ .whitespace = options.whitespace },
89 try sz.valueArbitraryDepth(val, .{87 };
88 try s.valueArbitraryDepth(val, .{
90 .emit_codepoint_literals = options.emit_codepoint_literals,89 .emit_codepoint_literals = options.emit_codepoint_literals,
91 .emit_strings_as_containers = options.emit_strings_as_containers,90 .emit_strings_as_containers = options.emit_strings_as_containers,
92 .emit_default_optional_fields = options.emit_default_optional_fields,91 .emit_default_optional_fields = options.emit_default_optional_fields,
93 });92 });
94}93}
9594
96fn typeIsRecursive(comptime T: type) bool {95inline fn typeIsRecursive(comptime T: type) bool {
97 return comptime typeIsRecursiveImpl(T, &.{});96 return comptime typeIsRecursiveInner(T, &.{});
98}97}
9998
100fn typeIsRecursiveImpl(comptime T: type, comptime prev_visited: []const type) bool {99fn typeIsRecursiveInner(comptime T: type, comptime prev_visited: []const type) bool {
101 for (prev_visited) |V| {100 for (prev_visited) |V| {
102 if (V == T) return true;101 if (V == T) return true;
103 }102 }
104 const visited = prev_visited ++ .{T};103 const visited = prev_visited ++ .{T};
105104
106 return switch (@typeInfo(T)) {105 return switch (@typeInfo(T)) {
107 .pointer => |pointer| typeIsRecursiveImpl(pointer.child, visited),106 .pointer => |pointer| typeIsRecursiveInner(pointer.child, visited),
108 .optional => |optional| typeIsRecursiveImpl(optional.child, visited),107 .optional => |optional| typeIsRecursiveInner(optional.child, visited),
109 .array => |array| typeIsRecursiveImpl(array.child, visited),108 .array => |array| typeIsRecursiveInner(array.child, visited),
110 .vector => |vector| typeIsRecursiveImpl(vector.child, visited),109 .vector => |vector| typeIsRecursiveInner(vector.child, visited),
111 .@"struct" => |@"struct"| for (@"struct".fields) |field| {110 .@"struct" => |@"struct"| for (@"struct".fields) |field| {
112 if (typeIsRecursiveImpl(field.type, visited)) break true;111 if (typeIsRecursiveInner(field.type, visited)) break true;
113 } else false,112 } else false,
114 .@"union" => |@"union"| inline for (@"union".fields) |field| {113 .@"union" => |@"union"| inline for (@"union".fields) |field| {
115 if (typeIsRecursiveImpl(field.type, visited)) break true;114 if (typeIsRecursiveInner(field.type, visited)) break true;
116 } else false,115 } else false,
117 else => false,116 else => false,
118 };117 };
119}118}
120119
121fn canSerializeType(T: type) bool {120inline fn canSerializeType(T: type) bool {
122 comptime return canSerializeTypeInner(T, &.{}, false);121 comptime return canSerializeTypeInner(T, &.{}, false);
123}122}
124123
...@@ -343,12 +342,6 @@ test "std.zon checkValueDepth" {...@@ -343,12 +342,6 @@ test "std.zon checkValueDepth" {
343 try expectValueDepthEquals(3, @as([]const []const u8, &.{&.{ 1, 2, 3 }}));342 try expectValueDepthEquals(3, @as([]const []const u8, &.{&.{ 1, 2, 3 }}));
344}343}
345344
346/// Options for `Serializer`.
347pub const SerializerOptions = struct {
348 /// If false, only syntactically necessary whitespace is emitted.
349 whitespace: bool = true,
350};
351
352/// Determines when to emit Unicode code point literals as opposed to integer literals.345/// Determines when to emit Unicode code point literals as opposed to integer literals.
353pub const EmitCodepointLiterals = enum {346pub const EmitCodepointLiterals = enum {
354 /// Never emit Unicode code point literals.347 /// Never emit Unicode code point literals.
...@@ -440,633 +433,610 @@ pub const SerializeContainerOptions = struct {...@@ -440,633 +433,610 @@ pub const SerializeContainerOptions = struct {
440/// For manual serialization of containers, see:433/// For manual serialization of containers, see:
441/// * `beginStruct`434/// * `beginStruct`
442/// * `beginTuple`435/// * `beginTuple`
443///436pub const Serializer = struct {
444/// # Example437 options: Options,
445/// ```zig438 indent_level: u8 = 0,
446/// var sz = serializer(writer, .{});439 writer: *std.io.BufferedWriter,
447/// var vec2 = try sz.beginStruct(.{});440
448/// try vec2.field("x", 1.5, .{});441 pub const Options = struct {
449/// try vec2.fieldPrefix();442 /// If false, only syntactically necessary whitespace is emitted.
450/// try sz.value(2.5);443 whitespace: bool = true,
451/// try vec2.end();444 };
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 }
469445
470 /// Serialize a value, similar to `serialize`.446 /// Serialize a value, similar to `serialize`.
471 pub fn value(self: *Self, val: anytype, options: ValueOptions) Writer.Error!void {447 pub fn value(self: *Serializer, val: anytype, options: ValueOptions) anyerror!void {
472 comptime assert(!typeIsRecursive(@TypeOf(val)));448 comptime assert(!typeIsRecursive(@TypeOf(val)));
473 return self.valueArbitraryDepth(val, options);449 return self.valueArbitraryDepth(val, options);
474 }450 }
475451
476 /// Serialize a value, similar to `serializeMaxDepth`.452 /// Serialize a value, similar to `serializeMaxDepth`.
477 pub fn valueMaxDepth(453 /// Can return `error.ExceededMaxDepth`.
478 self: *Self,454 pub fn valueMaxDepth(self: *Serializer, val: anytype, options: ValueOptions, depth: usize) anyerror!void {
479 val: anytype,455 try checkValueDepth(val, depth);
480 options: ValueOptions,456 return self.valueArbitraryDepth(val, options);
481 depth: usize,457 }
482 ) (Writer.Error || error{ExceededMaxDepth})!void {
483 try checkValueDepth(val, depth);
484 return self.valueArbitraryDepth(val, options);
485 }
486458
487 /// Serialize a value, similar to `serializeArbitraryDepth`.459 /// Serialize a value, similar to `serializeArbitraryDepth`.
488 pub fn valueArbitraryDepth(460 pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOptions) anyerror!void {
489 self: *Self,461 comptime assert(canSerializeType(@TypeOf(val)));
490 val: anytype,462 switch (@typeInfo(@TypeOf(val))) {
491 options: ValueOptions,463 .int, .comptime_int => if (options.emit_codepoint_literals.emitAsCodepoint(val)) |c| {
492 ) Writer.Error!void {464 self.codePoint(c) catch |err| switch (err) {
493 comptime assert(canSerializeType(@TypeOf(val)));465 error.InvalidCodepoint => unreachable, // Already validated
494 switch (@typeInfo(@TypeOf(val))) {466 else => |e| return e,
495 .int, .comptime_int => if (options.emit_codepoint_literals.emitAsCodepoint(val)) |c| {467 };
496 self.codePoint(c) catch |err| switch (err) {468 } else {
497 error.InvalidCodepoint => unreachable, // Already validated469 try self.int(val);
498 else => |e| return e,470 },
499 };471 .float, .comptime_float => try self.float(val),
500 } else {472 .bool, .null => try std.fmt.format(self.writer, "{}", .{val}),
501 try self.int(val);473 .enum_literal => try self.ident(@tagName(val)),
502 },474 .@"enum" => try self.ident(@tagName(val)),
503 .float, .comptime_float => try self.float(val),475 .pointer => |pointer| {
504 .bool, .null => try std.fmt.format(self.writer, "{}", .{val}),476 // Try to serialize as a string
505 .enum_literal => try self.ident(@tagName(val)),477 const item: ?type = switch (@typeInfo(pointer.child)) {
506 .@"enum" => try self.ident(@tagName(val)),478 .array => |array| array.child,
507 .pointer => |pointer| {479 else => if (pointer.size == .slice) pointer.child else null,
508 // Try to serialize as a string480 };
509 const item: ?type = switch (@typeInfo(pointer.child)) {481 if (item == u8 and
510 .array => |array| array.child,482 (pointer.sentinel() == null or pointer.sentinel() == 0) and
511 else => if (pointer.size == .slice) pointer.child else null,483 !options.emit_strings_as_containers)
512 };484 {
513 if (item == u8 and485 return try self.string(val);
514 (pointer.sentinel() == null or pointer.sentinel() == 0) and486 }
515 !options.emit_strings_as_containers)
516 {
517 return try self.string(val);
518 }
519487
520 // Serialize as either a tuple or as the child type488 // Serialize as either a tuple or as the child type
521 switch (pointer.size) {489 switch (pointer.size) {
522 .slice => try self.tupleImpl(val, options),490 .slice => try self.tupleImpl(val, options),
523 .one => try self.valueArbitraryDepth(val.*, options),491 .one => try self.valueArbitraryDepth(val.*, options),
524 else => comptime unreachable,492 else => comptime unreachable,
525 }493 }
526 },494 },
527 .array => {495 .array => {
528 var container = try self.beginTuple(496 var container = try self.beginTuple(
529 .{ .whitespace_style = .{ .fields = val.len } },497 .{ .whitespace_style = .{ .fields = val.len } },
530 );498 );
531 for (val) |item_val| {499 for (val) |item_val| {
532 try container.fieldArbitraryDepth(item_val, options);500 try container.fieldArbitraryDepth(item_val, options);
533 }501 }
534 try container.end();502 try container.end();
535 },503 },
536 .@"struct" => |@"struct"| if (@"struct".is_tuple) {504 .@"struct" => |@"struct"| if (@"struct".is_tuple) {
537 var container = try self.beginTuple(505 var container = try self.beginTuple(
538 .{ .whitespace_style = .{ .fields = @"struct".fields.len } },506 .{ .whitespace_style = .{ .fields = @"struct".fields.len } },
539 );507 );
540 inline for (val) |field_value| {508 inline for (val) |field_value| {
541 try container.fieldArbitraryDepth(field_value, options);509 try container.fieldArbitraryDepth(field_value, options);
542 }510 }
543 try container.end();511 try container.end();
544 } else {512 } else {
545 // Decide which fields to emit513 // Decide which fields to emit
546 const fields, const skipped: [@"struct".fields.len]bool = if (options.emit_default_optional_fields) b: {514 const fields, const skipped: [@"struct".fields.len]bool = if (options.emit_default_optional_fields) b: {
547 break :b .{ @"struct".fields.len, @splat(false) };515 break :b .{ @"struct".fields.len, @splat(false) };
548 } else b: {516 } else b: {
549 var fields = @"struct".fields.len;517 var fields = @"struct".fields.len;
550 var skipped: [@"struct".fields.len]bool = @splat(false);518 var skipped: [@"struct".fields.len]bool = @splat(false);
551 inline for (@"struct".fields, &skipped) |field_info, *skip| {519 inline for (@"struct".fields, &skipped) |field_info, *skip| {
552 if (field_info.default_value_ptr) |ptr| {520 if (field_info.default_value_ptr) |ptr| {
553 const default: *const field_info.type = @ptrCast(@alignCast(ptr));521 const default: *const field_info.type = @ptrCast(@alignCast(ptr));
554 const field_value = @field(val, field_info.name);522 const field_value = @field(val, field_info.name);
555 if (std.meta.eql(field_value, default.*)) {523 if (std.meta.eql(field_value, default.*)) {
556 skip.* = true;524 skip.* = true;
557 fields -= 1;525 fields -= 1;
558 }
559 }526 }
560 }527 }
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 },
595 }528 }
596 },529 break :b .{ fields, skipped };
597 .optional => if (val) |inner| {530 };
598 try self.valueArbitraryDepth(inner, options);531
599 } else {532 // Emit those fields
600 try self.writer.writeAll("null");533 var container = try self.beginStruct(
601 },534 .{ .whitespace_style = .{ .fields = fields } },
602 .vector => |vector| {535 );
603 var container = try self.beginTuple(536 inline for (@"struct".fields, skipped) |field_info, skip| {
604 .{ .whitespace_style = .{ .fields = vector.len } },537 if (!skip) {
605 );538 try container.fieldArbitraryDepth(
606 for (0..vector.len) |i| {539 field_info.name,
607 try container.fieldArbitraryDepth(val[i], options);540 @field(val, field_info.name),
541 options,
542 );
608 }543 }
609 try container.end();544 }
610 },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,580 else => comptime unreachable,
613 }
614 }581 }
582 }
615583
616 /// Serialize an integer.584 /// Serialize an integer.
617 pub fn int(self: *Self, val: anytype) Writer.Error!void {585 pub fn int(self: *Serializer, val: anytype) anyerror!void {
618 try std.fmt.formatInt(val, 10, .lower, .{}, self.writer);586 try std.fmt.formatInt(val, 10, .lower, .{}, self.writer);
619 }587 }
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 }
643588
644 /// Serialize `name` as an identifier prefixed with `.`.589 /// Serialize a float.
645 ///590 pub fn float(self: *Serializer, val: anytype) anyerror!void {
646 /// Escapes the identifier if necessary.591 switch (@typeInfo(@TypeOf(val))) {
647 pub fn ident(self: *Self, name: []const u8) Writer.Error!void {592 .float => if (std.math.isNan(val)) {
648 try self.writer.print(".{p_}", .{std.zig.fmtId(name)});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,
649 }603 }
604 }
650605
651 /// Serialize `val` as a Unicode codepoint.606 /// Serialize `name` as an identifier prefixed with `.`.
652 ///607 ///
653 /// Returns `error.InvalidCodepoint` if `val` is not a valid Unicode codepoint.608 /// Escapes the identifier if necessary.
654 pub fn codePoint(609 pub fn ident(self: *Serializer, name: []const u8) anyerror!void {
655 self: *Self,610 try self.writer.print(".{p_}", .{std.zig.fmtId(name)});
656 val: u21,611 }
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 }
671612
672 /// Like `tuple`, but recursive types are allowed.613 /// Serialize `val` as a Unicode codepoint.
673 ///614 ///
674 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.615 /// Returns `error.InvalidCodepoint` if `val` is not a valid Unicode codepoint.
675 pub fn tupleMaxDepth(616 pub fn codePoint(
676 self: *Self,617 self: *Serializer,
677 val: anytype,618 val: u21,
678 options: ValueOptions,619 ) anyerror!void {
679 depth: usize,620 var buf: [8]u8 = undefined;
680 ) (Writer.Error || error{ExceededMaxDepth})!void {621 const len = std.unicode.utf8Encode(val, &buf) catch return error.InvalidCodepoint;
681 try checkValueDepth(val, depth);622 const str = buf[0..len];
682 try self.tupleArbitraryDepth(val, options);623 try std.fmt.format(self.writer, "'{'}'", .{std.zig.fmtEscapes(str)});
683 }624 }
684625
685 /// Like `tuple`, but recursive types are allowed.626 /// Like `value`, but always serializes `val` as a tuple.
686 ///627 ///
687 /// It is the caller's responsibility to ensure that `val` does not contain cycles.628 /// Will fail at comptime if `val` is not a tuple, array, pointer to an array, or slice.
688 pub fn tupleArbitraryDepth(629 pub fn tuple(self: *Serializer, val: anytype, options: ValueOptions) anyerror!void {
689 self: *Self,630 comptime assert(!typeIsRecursive(@TypeOf(val)));
690 val: anytype,631 try self.tupleArbitraryDepth(val, options);
691 options: ValueOptions,632 }
692 ) Writer.Error!void {
693 try self.tupleImpl(val, options);
694 }
695633
696 fn tupleImpl(self: *Self, val: anytype, options: ValueOptions) Writer.Error!void {634 /// Like `tuple`, but recursive types are allowed.
697 comptime assert(canSerializeType(@TypeOf(val)));635 ///
698 switch (@typeInfo(@TypeOf(val))) {636 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
699 .@"struct" => {637 pub fn tupleMaxDepth(
700 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });638 self: *Serializer,
701 inline for (val) |item_val| {639 val: anytype,
702 try container.fieldArbitraryDepth(item_val, options);640 options: ValueOptions,
703 }641 depth: usize,
704 try container.end();642 ) anyerror!void {
705 },643 try checkValueDepth(val, depth);
706 .pointer, .array => {644 try self.tupleArbitraryDepth(val, options);
707 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });645 }
708 for (val) |item_val| {646
709 try container.fieldArbitraryDepth(item_val, options);647 /// Like `tuple`, but recursive types are allowed.
710 }648 ///
711 try container.end();649 /// It is the caller's responsibility to ensure that `val` does not contain cycles.
712 },650 pub fn tupleArbitraryDepth(
713 else => comptime unreachable,651 self: *Serializer,
714 }652 val: anytype,
715 }653 options: ValueOptions,
654 ) anyerror!void {
655 try self.tupleImpl(val, options);
656 }
716657
717 /// Like `value`, but always serializes `val` as a string.658 fn tupleImpl(self: *Serializer, val: anytype, options: ValueOptions) anyerror!void {
718 pub fn string(self: *Self, val: []const u8) Writer.Error!void {659 comptime assert(canSerializeType(@TypeOf(val)));
719 try std.fmt.format(self.writer, "\"{}\"", .{std.zig.fmtEscapes(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,
720 }676 }
677 }
721678
722 /// Options for formatting multiline strings.679 /// Like `value`, but always serializes `val` as a string.
723 pub const MultilineStringOptions = struct {680 pub fn string(self: *Serializer, val: []const u8) anyerror!void {
724 /// If top level is true, whitespace before and after the multiline string is elided.681 try std.fmt.format(self.writer, "\"{}\"", .{std.zig.fmtEscapes(val)});
725 /// If it is true, a newline is printed, then the value, followed by a newline, and if682 }
726 /// whitespace is true any necessary indentation follows.
727 top_level: bool = false,
728 };
729683
730 /// Like `value`, but always serializes to a multiline string literal.684 /// Options for formatting multiline strings.
731 ///685 pub const MultilineStringOptions = struct {
732 /// Returns `error.InnerCarriageReturn` if `val` contains a CR not followed by a newline,686 /// If top level is true, whitespace before and after the multiline string is elided.
733 /// since multiline strings cannot represent CR without a following newline.687 /// If it is true, a newline is printed, then the value, followed by a newline, and if
734 pub fn multilineString(688 /// whitespace is true any necessary indentation follows.
735 self: *Self,689 top_level: bool = false,
736 val: []const u8,690 };
737 options: MultilineStringOptions,691
738 ) (Writer.Error || error{InnerCarriageReturn})!void {692 /// Like `value`, but always serializes to a multiline string literal.
739 // Make sure the string does not contain any carriage returns not followed by a newline693 ///
740 var i: usize = 0;694 /// Returns `error.InnerCarriageReturn` if `val` contains a CR not followed by a newline,
741 while (i < val.len) : (i += 1) {695 /// since multiline strings cannot represent CR without a following newline.
742 if (val[i] == '\r') {696 pub fn multilineString(
743 if (i + 1 < val.len) {697 self: *Serializer,
744 if (val[i + 1] == '\n') {698 val: []const u8,
745 i += 1;699 options: MultilineStringOptions,
746 continue;700 ) anyerror!void {
747 }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;
748 }709 }
749 return error.InnerCarriageReturn;
750 }710 }
711 return error.InnerCarriageReturn;
751 }712 }
713 }
752714
753 if (!options.top_level) {715 if (!options.top_level) {
754 try self.newline();716 try self.newline();
755 try self.indent();717 try self.indent();
756 }718 }
757719
758 try self.writer.writeAll("\\\\");720 try self.writer.writeAll("\\\\");
759 for (val) |c| {721 for (val) |c| {
760 if (c != '\r') {722 if (c != '\r') {
761 try self.writer.writeByte(c); // We write newlines here even if whitespace off723 try self.writer.writeByte(c); // We write newlines here even if whitespace off
762 if (c == '\n') {724 if (c == '\n') {
763 try self.indent();725 try self.indent();
764 try self.writer.writeAll("\\\\");726 try self.writer.writeAll("\\\\");
765 }
766 }727 }
767 }728 }
768
769 if (!options.top_level) {
770 try self.writer.writeByte('\n'); // Even if whitespace off
771 try self.indent();
772 }
773 }729 }
774730
775 /// Create a `Struct` for writing ZON structs field by field.731 if (!options.top_level) {
776 pub fn beginStruct(732 try self.writer.writeByte('\n'); // Even if whitespace off
777 self: *Self,733 try self.indent();
778 options: SerializeContainerOptions,
779 ) Writer.Error!Struct {
780 return Struct.begin(self, options);
781 }734 }
735 }
782736
783 /// Creates a `Tuple` for writing ZON tuples field by field.737 /// Create a `Struct` for writing ZON structs field by field.
784 pub fn beginTuple(738 pub fn beginStruct(
785 self: *Self,739 self: *Serializer,
786 options: SerializeContainerOptions,740 options: SerializeContainerOptions,
787 ) Writer.Error!Tuple {741 ) anyerror!Struct {
788 return Tuple.begin(self, options);742 return Struct.begin(self, options);
789 }743 }
790744
791 fn indent(self: *Self) Writer.Error!void {745 /// Creates a `Tuple` for writing ZON tuples field by field.
792 if (self.options.whitespace) {746 pub fn beginTuple(
793 try self.writer.writeByteNTimes(' ', 4 * self.indent_level);747 self: *Serializer,
794 }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);
795 }756 }
757 }
796758
797 fn newline(self: *Self) Writer.Error!void {759 fn newline(self: *Serializer) anyerror!void {
798 if (self.options.whitespace) {760 if (self.options.whitespace) {
799 try self.writer.writeByte('\n');761 try self.writer.writeByte('\n');
800 }
801 }762 }
763 }
802764
803 fn newlineOrSpace(self: *Self, len: usize) Writer.Error!void {765 fn newlineOrSpace(self: *Serializer, len: usize) anyerror!void {
804 if (self.containerShouldWrap(len)) {766 if (self.containerShouldWrap(len)) {
805 try self.newline();767 try self.newline();
806 } else {768 } else {
807 try self.space();769 try self.space();
808 }
809 }770 }
771 }
810772
811 fn space(self: *Self) Writer.Error!void {773 fn space(self: *Serializer) anyerror!void {
812 if (self.options.whitespace) {774 if (self.options.whitespace) {
813 try self.writer.writeByte(' ');775 try self.writer.writeByte(' ');
814 }
815 }776 }
777 }
816778
817 /// Writes ZON tuples field by field.779 /// Writes ZON tuples field by field.
818 pub const Tuple = struct {780 pub const Tuple = struct {
819 container: Container,781 container: Container,
820782
821 fn begin(parent: *Self, options: SerializeContainerOptions) Writer.Error!Tuple {783 fn begin(parent: *Serializer, options: SerializeContainerOptions) anyerror!Tuple {
822 return .{784 return .{
823 .container = try Container.begin(parent, .anon, options),785 .container = try Container.begin(parent, .anon, options),
824 };786 };
825 }787 }
826788
827 /// Finishes serializing the tuple.789 /// Finishes serializing the tuple.
828 ///790 ///
829 /// Prints a trailing comma as configured when appropriate, and the closing bracket.791 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
830 pub fn end(self: *Tuple) Writer.Error!void {792 pub fn end(self: *Tuple) anyerror!void {
831 try self.container.end();793 try self.container.end();
832 self.* = undefined;794 self.* = undefined;
833 }795 }
834796
835 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.797 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
836 pub fn field(798 pub fn field(
837 self: *Tuple,799 self: *Tuple,
838 val: anytype,800 val: anytype,
839 options: ValueOptions,801 options: ValueOptions,
840 ) Writer.Error!void {802 ) anyerror!void {
841 try self.container.field(null, val, options);803 try self.container.field(null, val, options);
842 }804 }
843805
844 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.806 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
845 pub fn fieldMaxDepth(807 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
846 self: *Tuple,808 pub fn fieldMaxDepth(
847 val: anytype,809 self: *Tuple,
848 options: ValueOptions,810 val: anytype,
849 depth: usize,811 options: ValueOptions,
850 ) (Writer.Error || error{ExceededMaxDepth})!void {812 depth: usize,
851 try self.container.fieldMaxDepth(null, val, options, depth);813 ) anyerror!void {
852 }814 try self.container.fieldMaxDepth(null, val, options, depth);
815 }
853816
854 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by817 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
855 /// `valueArbitraryDepth`.818 /// `valueArbitraryDepth`.
856 pub fn fieldArbitraryDepth(819 pub fn fieldArbitraryDepth(
857 self: *Tuple,820 self: *Tuple,
858 val: anytype,821 val: anytype,
859 options: ValueOptions,822 options: ValueOptions,
860 ) Writer.Error!void {823 ) anyerror!void {
861 try self.container.fieldArbitraryDepth(null, val, options);824 try self.container.fieldArbitraryDepth(null, val, options);
862 }825 }
863826
864 /// Starts a field with a struct as a value. Returns the struct.827 /// Starts a field with a struct as a value. Returns the struct.
865 pub fn beginStructField(828 pub fn beginStructField(
866 self: *Tuple,829 self: *Tuple,
867 options: SerializeContainerOptions,830 options: SerializeContainerOptions,
868 ) Writer.Error!Struct {831 ) anyerror!Struct {
869 try self.fieldPrefix();832 try self.fieldPrefix();
870 return self.container.serializer.beginStruct(options);833 return self.container.serializer.beginStruct(options);
871 }834 }
872835
873 /// Starts a field with a tuple as a value. Returns the tuple.836 /// Starts a field with a tuple as a value. Returns the tuple.
874 pub fn beginTupleField(837 pub fn beginTupleField(
875 self: *Tuple,838 self: *Tuple,
876 options: SerializeContainerOptions,839 options: SerializeContainerOptions,
877 ) Writer.Error!Tuple {840 ) anyerror!Tuple {
878 try self.fieldPrefix();841 try self.fieldPrefix();
879 return self.container.serializer.beginTuple(options);842 return self.container.serializer.beginTuple(options);
880 }843 }
881844
882 /// Print a field prefix. This prints any necessary commas, and whitespace as845 /// Print a field prefix. This prints any necessary commas, and whitespace as
883 /// configured. Useful if you want to serialize the field value yourself.846 /// configured. Useful if you want to serialize the field value yourself.
884 pub fn fieldPrefix(self: *Tuple) Writer.Error!void {847 pub fn fieldPrefix(self: *Tuple) anyerror!void {
885 try self.container.fieldPrefix(null);848 try self.container.fieldPrefix(null);
886 }849 }
887 };850 };
888851
889 /// Writes ZON structs field by field.852 /// Writes ZON structs field by field.
890 pub const Struct = struct {853 pub const Struct = struct {
891 container: Container,854 container: Container,
892855
893 fn begin(parent: *Self, options: SerializeContainerOptions) Writer.Error!Struct {856 fn begin(parent: *Serializer, options: SerializeContainerOptions) anyerror!Struct {
894 return .{857 return .{
895 .container = try Container.begin(parent, .named, options),858 .container = try Container.begin(parent, .named, options),
896 };859 };
897 }860 }
898861
899 /// Finishes serializing the struct.862 /// Finishes serializing the struct.
900 ///863 ///
901 /// Prints a trailing comma as configured when appropriate, and the closing bracket.864 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
902 pub fn end(self: *Struct) Writer.Error!void {865 pub fn end(self: *Struct) anyerror!void {
903 try self.container.end();866 try self.container.end();
904 self.* = undefined;867 self.* = undefined;
905 }868 }
906869
907 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.870 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
908 pub fn field(871 pub fn field(
909 self: *Struct,872 self: *Struct,
910 name: []const u8,873 name: []const u8,
911 val: anytype,874 val: anytype,
912 options: ValueOptions,875 options: ValueOptions,
913 ) Writer.Error!void {876 ) anyerror!void {
914 try self.container.field(name, val, options);877 try self.container.field(name, val, options);
915 }878 }
916879
917 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.880 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
918 pub fn fieldMaxDepth(881 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
919 self: *Struct,882 pub fn fieldMaxDepth(
920 name: []const u8,883 self: *Struct,
921 val: anytype,884 name: []const u8,
922 options: ValueOptions,885 val: anytype,
923 depth: usize,886 options: ValueOptions,
924 ) (Writer.Error || error{ExceededMaxDepth})!void {887 depth: usize,
925 try self.container.fieldMaxDepth(name, val, options, depth);888 ) anyerror!void {
926 }889 try self.container.fieldMaxDepth(name, val, options, depth);
890 }
927891
928 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by892 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
929 /// `valueArbitraryDepth`.893 /// `valueArbitraryDepth`.
930 pub fn fieldArbitraryDepth(894 pub fn fieldArbitraryDepth(
931 self: *Struct,895 self: *Struct,
932 name: []const u8,896 name: []const u8,
933 val: anytype,897 val: anytype,
934 options: ValueOptions,898 options: ValueOptions,
935 ) Writer.Error!void {899 ) anyerror!void {
936 try self.container.fieldArbitraryDepth(name, val, options);900 try self.container.fieldArbitraryDepth(name, val, options);
937 }901 }
938902
939 /// Starts a field with a struct as a value. Returns the struct.903 /// Starts a field with a struct as a value. Returns the struct.
940 pub fn beginStructField(904 pub fn beginStructField(
941 self: *Struct,905 self: *Struct,
942 name: []const u8,906 name: []const u8,
943 options: SerializeContainerOptions,907 options: SerializeContainerOptions,
944 ) Writer.Error!Struct {908 ) anyerror!Struct {
945 try self.fieldPrefix(name);909 try self.fieldPrefix(name);
946 return self.container.serializer.beginStruct(options);910 return self.container.serializer.beginStruct(options);
947 }911 }
948912
949 /// Starts a field with a tuple as a value. Returns the tuple.913 /// Starts a field with a tuple as a value. Returns the tuple.
950 pub fn beginTupleField(914 pub fn beginTupleField(
951 self: *Struct,915 self: *Struct,
952 name: []const u8,916 name: []const u8,
953 options: SerializeContainerOptions,917 options: SerializeContainerOptions,
954 ) Writer.Error!Tuple {918 ) anyerror!Tuple {
955 try self.fieldPrefix(name);919 try self.fieldPrefix(name);
956 return self.container.serializer.beginTuple(options);920 return self.container.serializer.beginTuple(options);
957 }921 }
958922
959 /// Print a field prefix. This prints any necessary commas, the field name (escaped if923 /// 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 field924 /// necessary) and whitespace as configured. Useful if you want to serialize the field
961 /// value yourself.925 /// value yourself.
962 pub fn fieldPrefix(self: *Struct, name: []const u8) Writer.Error!void {926 pub fn fieldPrefix(self: *Struct, name: []const u8) anyerror!void {
963 try self.container.fieldPrefix(name);927 try self.container.fieldPrefix(name);
964 }928 }
965 };929 };
966930
967 const Container = struct {931 const Container = struct {
968 const FieldStyle = enum { named, anon };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,
971 field_style: FieldStyle,941 field_style: FieldStyle,
972 options: SerializeContainerOptions,942 options: SerializeContainerOptions,
973 empty: bool,943 ) anyerror!Container {
974944 if (options.shouldWrap()) sz.indent_level +|= 1;
975 fn begin(945 try sz.writer.writeAll(".{");
976 sz: *Self,946 return .{
977 field_style: FieldStyle,947 .serializer = sz,
978 options: SerializeContainerOptions,948 .field_style = field_style,
979 ) Writer.Error!Container {949 .options = options,
980 if (options.shouldWrap()) sz.indent_level +|= 1;950 .empty = true,
981 try sz.writer.writeAll(".{");951 };
982 return .{952 }
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 }
1006953
1007 fn fieldPrefix(self: *Container, name: ?[]const u8) Writer.Error!void {954 fn end(self: *Container) anyerror!void {
1008 if (!self.empty) {955 if (self.options.shouldWrap()) self.serializer.indent_level -|= 1;
1009 try self.serializer.writer.writeByte(',');956 if (!self.empty) {
1010 }
1011 self.empty = false;
1012 if (self.options.shouldWrap()) {957 if (self.options.shouldWrap()) {
958 if (self.serializer.options.whitespace) {
959 try self.serializer.writer.writeByte(',');
960 }
1013 try self.serializer.newline();961 try self.serializer.newline();
962 try self.serializer.indent();
1014 } else if (!self.shouldElideSpaces()) {963 } else if (!self.shouldElideSpaces()) {
1015 try self.serializer.space();964 try self.serializer.space();
1016 }965 }
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 }
1024 }966 }
967 try self.serializer.writer.writeByte('}');
968 self.* = undefined;
969 }
1025970
1026 fn field(971 fn fieldPrefix(self: *Container, name: ?[]const u8) anyerror!void {
1027 self: *Container,972 if (!self.empty) {
1028 name: ?[]const u8,973 try self.serializer.writer.writeByte(',');
1029 val: anytype,
1030 options: ValueOptions,
1031 ) Writer.Error!void {
1032 comptime assert(!typeIsRecursive(@TypeOf(val)));
1033 try self.fieldArbitraryDepth(name, val, options);
1034 }974 }
1035975 self.empty = false;
1036 fn fieldMaxDepth(976 if (self.options.shouldWrap()) {
1037 self: *Container,977 try self.serializer.newline();
1038 name: ?[]const u8,978 } else if (!self.shouldElideSpaces()) {
1039 val: anytype,979 try self.serializer.space();
1040 options: ValueOptions,
1041 depth: usize,
1042 ) (Writer.Error || error{ExceededMaxDepth})!void {
1043 try checkValueDepth(val, depth);
1044 try self.fieldArbitraryDepth(name, val, options);
1045 }980 }
1046981 if (self.options.shouldWrap()) try self.serializer.indent();
1047 fn fieldArbitraryDepth(982 if (name) |n| {
1048 self: *Container,983 try self.serializer.ident(n);
1049 name: ?[]const u8,984 try self.serializer.space();
1050 val: anytype,985 try self.serializer.writer.writeByte('=');
1051 options: ValueOptions,986 try self.serializer.space();
1052 ) Writer.Error!void {
1053 try self.fieldPrefix(name);
1054 try self.serializer.valueArbitraryDepth(val, options);
1055 }987 }
988 }
1056989
1057 fn shouldElideSpaces(self: *const Container) bool {990 fn field(
1058 return switch (self.options.whitespace_style) {991 self: *Container,
1059 .fields => |fields| self.field_style != .named and fields == 1,992 name: ?[]const u8,
1060 else => false,993 val: anytype,
1061 };994 options: ValueOptions,
1062 }995 ) anyerror!void {
1063 };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 }
1064 };1028 };
1065}1029};
10661030
1067/// Creates a new `Serializer` with the given writer and options.1031test Serializer {
1068pub fn serializer(writer: anytype, options: SerializerOptions) Serializer(@TypeOf(writer)) {1032 var s: Serializer = .{
1069 return .init(writer, options);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();
1070}1040}
10711041
1072fn expectSerializeEqual(1042fn expectSerializeEqual(
...@@ -1074,10 +1044,12 @@ fn expectSerializeEqual(...@@ -1074,10 +1044,12 @@ fn expectSerializeEqual(
1074 value: anytype,1044 value: anytype,
1075 options: SerializeOptions,1045 options: SerializeOptions,
1076) !void {1046) !void {
1077 var buf = std.ArrayList(u8).init(std.testing.allocator);1047 var aw: std.io.AllocatingWriter = undefined;
1078 defer buf.deinit();1048 defer aw.deinit();
1079 try serialize(value, options, buf.writer());1049 const bw = aw.init(std.testing.allocator);
1080 try std.testing.expectEqualStrings(expected, buf.items);1050
1051 try serialize(value, options, bw);
1052 try std.testing.expectEqualStrings(expected, aw.getWritten());
1081}1053}
10821054
1083test "std.zon stringify whitespace, high level API" {1055test "std.zon stringify whitespace, high level API" {
...@@ -1174,59 +1146,59 @@ test "std.zon stringify whitespace, high level API" {...@@ -1174,59 +1146,59 @@ test "std.zon stringify whitespace, high level API" {
1174}1146}
11751147
1176test "std.zon stringify whitespace, low level API" {1148test "std.zon stringify whitespace, low level API" {
1177 var buf = std.ArrayList(u8).init(std.testing.allocator);1149 var aw: std.io.AllocatingWriter = undefined;
1178 defer buf.deinit();1150 defer aw.deinit();
1179 var sz = serializer(buf.writer(), .{});1151 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
11801152
1181 inline for (.{ true, false }) |whitespace| {1153 for ([2]bool{ true, false }) |whitespace| {
1182 sz.options = .{ .whitespace = whitespace };1154 s.options = .{ .whitespace = whitespace };
11831155
1184 // Empty containers1156 // Empty containers
1185 {1157 {
1186 var container = try sz.beginStruct(.{});1158 var container = try s.beginStruct(.{});
1187 try container.end();1159 try container.end();
1188 try std.testing.expectEqualStrings(".{}", buf.items);1160 try std.testing.expectEqualStrings(".{}", aw.getWritten());
1189 buf.clearRetainingCapacity();1161 aw.clearRetainingCapacity();
1190 }1162 }
11911163
1192 {1164 {
1193 var container = try sz.beginTuple(.{});1165 var container = try s.beginTuple(.{});
1194 try container.end();1166 try container.end();
1195 try std.testing.expectEqualStrings(".{}", buf.items);1167 try std.testing.expectEqualStrings(".{}", aw.getWritten());
1196 buf.clearRetainingCapacity();1168 aw.clearRetainingCapacity();
1197 }1169 }
11981170
1199 {1171 {
1200 var container = try sz.beginStruct(.{ .whitespace_style = .{ .wrap = false } });1172 var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
1201 try container.end();1173 try container.end();
1202 try std.testing.expectEqualStrings(".{}", buf.items);1174 try std.testing.expectEqualStrings(".{}", aw.getWritten());
1203 buf.clearRetainingCapacity();1175 aw.clearRetainingCapacity();
1204 }1176 }
12051177
1206 {1178 {
1207 var container = try sz.beginTuple(.{ .whitespace_style = .{ .wrap = false } });1179 var container = try s.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
1208 try container.end();1180 try container.end();
1209 try std.testing.expectEqualStrings(".{}", buf.items);1181 try std.testing.expectEqualStrings(".{}", aw.getWritten());
1210 buf.clearRetainingCapacity();1182 aw.clearRetainingCapacity();
1211 }1183 }
12121184
1213 {1185 {
1214 var container = try sz.beginStruct(.{ .whitespace_style = .{ .fields = 0 } });1186 var container = try s.beginStruct(.{ .whitespace_style = .{ .fields = 0 } });
1215 try container.end();1187 try container.end();
1216 try std.testing.expectEqualStrings(".{}", buf.items);1188 try std.testing.expectEqualStrings(".{}", aw.getWritten());
1217 buf.clearRetainingCapacity();1189 aw.clearRetainingCapacity();
1218 }1190 }
12191191
1220 {1192 {
1221 var container = try sz.beginTuple(.{ .whitespace_style = .{ .fields = 0 } });1193 var container = try s.beginTuple(.{ .whitespace_style = .{ .fields = 0 } });
1222 try container.end();1194 try container.end();
1223 try std.testing.expectEqualStrings(".{}", buf.items);1195 try std.testing.expectEqualStrings(".{}", aw.getWritten());
1224 buf.clearRetainingCapacity();1196 aw.clearRetainingCapacity();
1225 }1197 }
12261198
1227 // Size 11199 // Size 1
1228 {1200 {
1229 var container = try sz.beginStruct(.{});1201 var container = try s.beginStruct(.{});
1230 try container.field("a", 1, .{});1202 try container.field("a", 1, .{});
1231 try container.end();1203 try container.end();
1232 if (whitespace) {1204 if (whitespace) {
...@@ -1234,15 +1206,15 @@ test "std.zon stringify whitespace, low level API" {...@@ -1234,15 +1206,15 @@ test "std.zon stringify whitespace, low level API" {
1234 \\.{1206 \\.{
1235 \\ .a = 1,1207 \\ .a = 1,
1236 \\}1208 \\}
1237 , buf.items);1209 , aw.getWritten());
1238 } else {1210 } else {
1239 try std.testing.expectEqualStrings(".{.a=1}", buf.items);1211 try std.testing.expectEqualStrings(".{.a=1}", aw.getWritten());
1240 }1212 }
1241 buf.clearRetainingCapacity();1213 aw.clearRetainingCapacity();
1242 }1214 }
12431215
1244 {1216 {
1245 var container = try sz.beginTuple(.{});1217 var container = try s.beginTuple(.{});
1246 try container.field(1, .{});1218 try container.field(1, .{});
1247 try container.end();1219 try container.end();
1248 if (whitespace) {1220 if (whitespace) {
...@@ -1250,62 +1222,62 @@ test "std.zon stringify whitespace, low level API" {...@@ -1250,62 +1222,62 @@ test "std.zon stringify whitespace, low level API" {
1250 \\.{1222 \\.{
1251 \\ 1,1223 \\ 1,
1252 \\}1224 \\}
1253 , buf.items);1225 , aw.getWritten());
1254 } else {1226 } else {
1255 try std.testing.expectEqualStrings(".{1}", buf.items);1227 try std.testing.expectEqualStrings(".{1}", aw.getWritten());
1256 }1228 }
1257 buf.clearRetainingCapacity();1229 aw.clearRetainingCapacity();
1258 }1230 }
12591231
1260 {1232 {
1261 var container = try sz.beginStruct(.{ .whitespace_style = .{ .wrap = false } });1233 var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
1262 try container.field("a", 1, .{});1234 try container.field("a", 1, .{});
1263 try container.end();1235 try container.end();
1264 if (whitespace) {1236 if (whitespace) {
1265 try std.testing.expectEqualStrings(".{ .a = 1 }", buf.items);1237 try std.testing.expectEqualStrings(".{ .a = 1 }", aw.getWritten());
1266 } else {1238 } else {
1267 try std.testing.expectEqualStrings(".{.a=1}", buf.items);1239 try std.testing.expectEqualStrings(".{.a=1}", aw.getWritten());
1268 }1240 }
1269 buf.clearRetainingCapacity();1241 aw.clearRetainingCapacity();
1270 }1242 }
12711243
1272 {1244 {
1273 // We get extra spaces here, since we didn't know up front that there would only be one1245 // We get extra spaces here, since we didn't know up front that there would only be one
1274 // field.1246 // field.
1275 var container = try sz.beginTuple(.{ .whitespace_style = .{ .wrap = false } });1247 var container = try s.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
1276 try container.field(1, .{});1248 try container.field(1, .{});
1277 try container.end();1249 try container.end();
1278 if (whitespace) {1250 if (whitespace) {
1279 try std.testing.expectEqualStrings(".{ 1 }", buf.items);1251 try std.testing.expectEqualStrings(".{ 1 }", aw.getWritten());
1280 } else {1252 } else {
1281 try std.testing.expectEqualStrings(".{1}", buf.items);1253 try std.testing.expectEqualStrings(".{1}", aw.getWritten());
1282 }1254 }
1283 buf.clearRetainingCapacity();1255 aw.clearRetainingCapacity();
1284 }1256 }
12851257
1286 {1258 {
1287 var container = try sz.beginStruct(.{ .whitespace_style = .{ .fields = 1 } });1259 var container = try s.beginStruct(.{ .whitespace_style = .{ .fields = 1 } });
1288 try container.field("a", 1, .{});1260 try container.field("a", 1, .{});
1289 try container.end();1261 try container.end();
1290 if (whitespace) {1262 if (whitespace) {
1291 try std.testing.expectEqualStrings(".{ .a = 1 }", buf.items);1263 try std.testing.expectEqualStrings(".{ .a = 1 }", aw.getWritten());
1292 } else {1264 } else {
1293 try std.testing.expectEqualStrings(".{.a=1}", buf.items);1265 try std.testing.expectEqualStrings(".{.a=1}", aw.getWritten());
1294 }1266 }
1295 buf.clearRetainingCapacity();1267 aw.clearRetainingCapacity();
1296 }1268 }
12971269
1298 {1270 {
1299 var container = try sz.beginTuple(.{ .whitespace_style = .{ .fields = 1 } });1271 var container = try s.beginTuple(.{ .whitespace_style = .{ .fields = 1 } });
1300 try container.field(1, .{});1272 try container.field(1, .{});
1301 try container.end();1273 try container.end();
1302 try std.testing.expectEqualStrings(".{1}", buf.items);1274 try std.testing.expectEqualStrings(".{1}", aw.getWritten());
1303 buf.clearRetainingCapacity();1275 aw.clearRetainingCapacity();
1304 }1276 }
13051277
1306 // Size 21278 // Size 2
1307 {1279 {
1308 var container = try sz.beginStruct(.{});1280 var container = try s.beginStruct(.{});
1309 try container.field("a", 1, .{});1281 try container.field("a", 1, .{});
1310 try container.field("b", 2, .{});1282 try container.field("b", 2, .{});
1311 try container.end();1283 try container.end();
...@@ -1315,15 +1287,15 @@ test "std.zon stringify whitespace, low level API" {...@@ -1315,15 +1287,15 @@ test "std.zon stringify whitespace, low level API" {
1315 \\ .a = 1,1287 \\ .a = 1,
1316 \\ .b = 2,1288 \\ .b = 2,
1317 \\}1289 \\}
1318 , buf.items);1290 , aw.getWritten());
1319 } else {1291 } else {
1320 try std.testing.expectEqualStrings(".{.a=1,.b=2}", buf.items);1292 try std.testing.expectEqualStrings(".{.a=1,.b=2}", aw.getWritten());
1321 }1293 }
1322 buf.clearRetainingCapacity();1294 aw.clearRetainingCapacity();
1323 }1295 }
13241296
1325 {1297 {
1326 var container = try sz.beginTuple(.{});1298 var container = try s.beginTuple(.{});
1327 try container.field(1, .{});1299 try container.field(1, .{});
1328 try container.field(2, .{});1300 try container.field(2, .{});
1329 try container.end();1301 try container.end();
...@@ -1333,68 +1305,68 @@ test "std.zon stringify whitespace, low level API" {...@@ -1333,68 +1305,68 @@ test "std.zon stringify whitespace, low level API" {
1333 \\ 1,1305 \\ 1,
1334 \\ 2,1306 \\ 2,
1335 \\}1307 \\}
1336 , buf.items);1308 , aw.getWritten());
1337 } else {1309 } else {
1338 try std.testing.expectEqualStrings(".{1,2}", buf.items);1310 try std.testing.expectEqualStrings(".{1,2}", aw.getWritten());
1339 }1311 }
1340 buf.clearRetainingCapacity();1312 aw.clearRetainingCapacity();
1341 }1313 }
13421314
1343 {1315 {
1344 var container = try sz.beginStruct(.{ .whitespace_style = .{ .wrap = false } });1316 var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
1345 try container.field("a", 1, .{});1317 try container.field("a", 1, .{});
1346 try container.field("b", 2, .{});1318 try container.field("b", 2, .{});
1347 try container.end();1319 try container.end();
1348 if (whitespace) {1320 if (whitespace) {
1349 try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", buf.items);1321 try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", aw.getWritten());
1350 } else {1322 } else {
1351 try std.testing.expectEqualStrings(".{.a=1,.b=2}", buf.items);1323 try std.testing.expectEqualStrings(".{.a=1,.b=2}", aw.getWritten());
1352 }1324 }
1353 buf.clearRetainingCapacity();1325 aw.clearRetainingCapacity();
1354 }1326 }
13551327
1356 {1328 {
1357 var container = try sz.beginTuple(.{ .whitespace_style = .{ .wrap = false } });1329 var container = try s.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
1358 try container.field(1, .{});1330 try container.field(1, .{});
1359 try container.field(2, .{});1331 try container.field(2, .{});
1360 try container.end();1332 try container.end();
1361 if (whitespace) {1333 if (whitespace) {
1362 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);1334 try std.testing.expectEqualStrings(".{ 1, 2 }", aw.getWritten());
1363 } else {1335 } else {
1364 try std.testing.expectEqualStrings(".{1,2}", buf.items);1336 try std.testing.expectEqualStrings(".{1,2}", aw.getWritten());
1365 }1337 }
1366 buf.clearRetainingCapacity();1338 aw.clearRetainingCapacity();
1367 }1339 }
13681340
1369 {1341 {
1370 var container = try sz.beginStruct(.{ .whitespace_style = .{ .fields = 2 } });1342 var container = try s.beginStruct(.{ .whitespace_style = .{ .fields = 2 } });
1371 try container.field("a", 1, .{});1343 try container.field("a", 1, .{});
1372 try container.field("b", 2, .{});1344 try container.field("b", 2, .{});
1373 try container.end();1345 try container.end();
1374 if (whitespace) {1346 if (whitespace) {
1375 try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", buf.items);1347 try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", aw.getWritten());
1376 } else {1348 } else {
1377 try std.testing.expectEqualStrings(".{.a=1,.b=2}", buf.items);1349 try std.testing.expectEqualStrings(".{.a=1,.b=2}", aw.getWritten());
1378 }1350 }
1379 buf.clearRetainingCapacity();1351 aw.clearRetainingCapacity();
1380 }1352 }
13811353
1382 {1354 {
1383 var container = try sz.beginTuple(.{ .whitespace_style = .{ .fields = 2 } });1355 var container = try s.beginTuple(.{ .whitespace_style = .{ .fields = 2 } });
1384 try container.field(1, .{});1356 try container.field(1, .{});
1385 try container.field(2, .{});1357 try container.field(2, .{});
1386 try container.end();1358 try container.end();
1387 if (whitespace) {1359 if (whitespace) {
1388 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);1360 try std.testing.expectEqualStrings(".{ 1, 2 }", aw.getWritten());
1389 } else {1361 } else {
1390 try std.testing.expectEqualStrings(".{1,2}", buf.items);1362 try std.testing.expectEqualStrings(".{1,2}", aw.getWritten());
1391 }1363 }
1392 buf.clearRetainingCapacity();1364 aw.clearRetainingCapacity();
1393 }1365 }
13941366
1395 // Size 31367 // Size 3
1396 {1368 {
1397 var container = try sz.beginStruct(.{});1369 var container = try s.beginStruct(.{});
1398 try container.field("a", 1, .{});1370 try container.field("a", 1, .{});
1399 try container.field("b", 2, .{});1371 try container.field("b", 2, .{});
1400 try container.field("c", 3, .{});1372 try container.field("c", 3, .{});
...@@ -1406,15 +1378,15 @@ test "std.zon stringify whitespace, low level API" {...@@ -1406,15 +1378,15 @@ test "std.zon stringify whitespace, low level API" {
1406 \\ .b = 2,1378 \\ .b = 2,
1407 \\ .c = 3,1379 \\ .c = 3,
1408 \\}1380 \\}
1409 , buf.items);1381 , aw.getWritten());
1410 } else {1382 } 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());
1412 }1384 }
1413 buf.clearRetainingCapacity();1385 aw.clearRetainingCapacity();
1414 }1386 }
14151387
1416 {1388 {
1417 var container = try sz.beginTuple(.{});1389 var container = try s.beginTuple(.{});
1418 try container.field(1, .{});1390 try container.field(1, .{});
1419 try container.field(2, .{});1391 try container.field(2, .{});
1420 try container.field(3, .{});1392 try container.field(3, .{});
...@@ -1426,43 +1398,43 @@ test "std.zon stringify whitespace, low level API" {...@@ -1426,43 +1398,43 @@ test "std.zon stringify whitespace, low level API" {
1426 \\ 2,1398 \\ 2,
1427 \\ 3,1399 \\ 3,
1428 \\}1400 \\}
1429 , buf.items);1401 , aw.getWritten());
1430 } else {1402 } else {
1431 try std.testing.expectEqualStrings(".{1,2,3}", buf.items);1403 try std.testing.expectEqualStrings(".{1,2,3}", aw.getWritten());
1432 }1404 }
1433 buf.clearRetainingCapacity();1405 aw.clearRetainingCapacity();
1434 }1406 }
14351407
1436 {1408 {
1437 var container = try sz.beginStruct(.{ .whitespace_style = .{ .wrap = false } });1409 var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
1438 try container.field("a", 1, .{});1410 try container.field("a", 1, .{});
1439 try container.field("b", 2, .{});1411 try container.field("b", 2, .{});
1440 try container.field("c", 3, .{});1412 try container.field("c", 3, .{});
1441 try container.end();1413 try container.end();
1442 if (whitespace) {1414 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());
1444 } else {1416 } 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());
1446 }1418 }
1447 buf.clearRetainingCapacity();1419 aw.clearRetainingCapacity();
1448 }1420 }
14491421
1450 {1422 {
1451 var container = try sz.beginTuple(.{ .whitespace_style = .{ .wrap = false } });1423 var container = try s.beginTuple(.{ .whitespace_style = .{ .wrap = false } });
1452 try container.field(1, .{});1424 try container.field(1, .{});
1453 try container.field(2, .{});1425 try container.field(2, .{});
1454 try container.field(3, .{});1426 try container.field(3, .{});
1455 try container.end();1427 try container.end();
1456 if (whitespace) {1428 if (whitespace) {
1457 try std.testing.expectEqualStrings(".{ 1, 2, 3 }", buf.items);1429 try std.testing.expectEqualStrings(".{ 1, 2, 3 }", aw.getWritten());
1458 } else {1430 } else {
1459 try std.testing.expectEqualStrings(".{1,2,3}", buf.items);1431 try std.testing.expectEqualStrings(".{1,2,3}", aw.getWritten());
1460 }1432 }
1461 buf.clearRetainingCapacity();1433 aw.clearRetainingCapacity();
1462 }1434 }
14631435
1464 {1436 {
1465 var container = try sz.beginStruct(.{ .whitespace_style = .{ .fields = 3 } });1437 var container = try s.beginStruct(.{ .whitespace_style = .{ .fields = 3 } });
1466 try container.field("a", 1, .{});1438 try container.field("a", 1, .{});
1467 try container.field("b", 2, .{});1439 try container.field("b", 2, .{});
1468 try container.field("c", 3, .{});1440 try container.field("c", 3, .{});
...@@ -1474,15 +1446,15 @@ test "std.zon stringify whitespace, low level API" {...@@ -1474,15 +1446,15 @@ test "std.zon stringify whitespace, low level API" {
1474 \\ .b = 2,1446 \\ .b = 2,
1475 \\ .c = 3,1447 \\ .c = 3,
1476 \\}1448 \\}
1477 , buf.items);1449 , aw.getWritten());
1478 } else {1450 } 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());
1480 }1452 }
1481 buf.clearRetainingCapacity();1453 aw.clearRetainingCapacity();
1482 }1454 }
14831455
1484 {1456 {
1485 var container = try sz.beginTuple(.{ .whitespace_style = .{ .fields = 3 } });1457 var container = try s.beginTuple(.{ .whitespace_style = .{ .fields = 3 } });
1486 try container.field(1, .{});1458 try container.field(1, .{});
1487 try container.field(2, .{});1459 try container.field(2, .{});
1488 try container.field(3, .{});1460 try container.field(3, .{});
...@@ -1494,16 +1466,16 @@ test "std.zon stringify whitespace, low level API" {...@@ -1494,16 +1466,16 @@ test "std.zon stringify whitespace, low level API" {
1494 \\ 2,1466 \\ 2,
1495 \\ 3,1467 \\ 3,
1496 \\}1468 \\}
1497 , buf.items);1469 , aw.getWritten());
1498 } else {1470 } else {
1499 try std.testing.expectEqualStrings(".{1,2,3}", buf.items);1471 try std.testing.expectEqualStrings(".{1,2,3}", aw.getWritten());
1500 }1472 }
1501 buf.clearRetainingCapacity();1473 aw.clearRetainingCapacity();
1502 }1474 }
15031475
1504 // Nested objects where the outer container doesn't wrap but the inner containers do1476 // Nested objects where the outer container doesn't wrap but the inner containers do
1505 {1477 {
1506 var container = try sz.beginStruct(.{ .whitespace_style = .{ .wrap = false } });1478 var container = try s.beginStruct(.{ .whitespace_style = .{ .wrap = false } });
1507 try container.field("first", .{ 1, 2, 3 }, .{});1479 try container.field("first", .{ 1, 2, 3 }, .{});
1508 try container.field("second", .{ 4, 5, 6 }, .{});1480 try container.field("second", .{ 4, 5, 6 }, .{});
1509 try container.end();1481 try container.end();
...@@ -1518,139 +1490,139 @@ test "std.zon stringify whitespace, low level API" {...@@ -1518,139 +1490,139 @@ test "std.zon stringify whitespace, low level API" {
1518 \\ 5,1490 \\ 5,
1519 \\ 6,1491 \\ 6,
1520 \\} }1492 \\} }
1521 , buf.items);1493 , aw.getWritten());
1522 } else {1494 } else {
1523 try std.testing.expectEqualStrings(1495 try std.testing.expectEqualStrings(
1524 ".{.first=.{1,2,3},.second=.{4,5,6}}",1496 ".{.first=.{1,2,3},.second=.{4,5,6}}",
1525 buf.items,1497 aw.getWritten(),
1526 );1498 );
1527 }1499 }
1528 buf.clearRetainingCapacity();1500 aw.clearRetainingCapacity();
1529 }1501 }
1530 }1502 }
1531}1503}
15321504
1533test "std.zon stringify utf8 codepoints" {1505test "std.zon stringify utf8 codepoints" {
1534 var buf = std.ArrayList(u8).init(std.testing.allocator);1506 var aw: std.io.AllocatingWriter = undefined;
1535 defer buf.deinit();1507 defer aw.deinit();
1536 var sz = serializer(buf.writer(), .{});1508 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
15371509
1538 // Printable ASCII1510 // Printable ASCII
1539 try sz.int('a');1511 try s.int('a');
1540 try std.testing.expectEqualStrings("97", buf.items);1512 try std.testing.expectEqualStrings("97", aw.getWritten());
1541 buf.clearRetainingCapacity();1513 aw.clearRetainingCapacity();
15421514
1543 try sz.codePoint('a');1515 try s.codePoint('a');
1544 try std.testing.expectEqualStrings("'a'", buf.items);1516 try std.testing.expectEqualStrings("'a'", aw.getWritten());
1545 buf.clearRetainingCapacity();1517 aw.clearRetainingCapacity();
15461518
1547 try sz.value('a', .{ .emit_codepoint_literals = .always });1519 try s.value('a', .{ .emit_codepoint_literals = .always });
1548 try std.testing.expectEqualStrings("'a'", buf.items);1520 try std.testing.expectEqualStrings("'a'", aw.getWritten());
1549 buf.clearRetainingCapacity();1521 aw.clearRetainingCapacity();
15501522
1551 try sz.value('a', .{ .emit_codepoint_literals = .printable_ascii });1523 try s.value('a', .{ .emit_codepoint_literals = .printable_ascii });
1552 try std.testing.expectEqualStrings("'a'", buf.items);1524 try std.testing.expectEqualStrings("'a'", aw.getWritten());
1553 buf.clearRetainingCapacity();1525 aw.clearRetainingCapacity();
15541526
1555 try sz.value('a', .{ .emit_codepoint_literals = .never });1527 try s.value('a', .{ .emit_codepoint_literals = .never });
1556 try std.testing.expectEqualStrings("97", buf.items);1528 try std.testing.expectEqualStrings("97", aw.getWritten());
1557 buf.clearRetainingCapacity();1529 aw.clearRetainingCapacity();
15581530
1559 // Short escaped codepoint1531 // Short escaped codepoint
1560 try sz.int('\n');1532 try s.int('\n');
1561 try std.testing.expectEqualStrings("10", buf.items);1533 try std.testing.expectEqualStrings("10", aw.getWritten());
1562 buf.clearRetainingCapacity();1534 aw.clearRetainingCapacity();
15631535
1564 try sz.codePoint('\n');1536 try s.codePoint('\n');
1565 try std.testing.expectEqualStrings("'\\n'", buf.items);1537 try std.testing.expectEqualStrings("'\\n'", aw.getWritten());
1566 buf.clearRetainingCapacity();1538 aw.clearRetainingCapacity();
15671539
1568 try sz.value('\n', .{ .emit_codepoint_literals = .always });1540 try s.value('\n', .{ .emit_codepoint_literals = .always });
1569 try std.testing.expectEqualStrings("'\\n'", buf.items);1541 try std.testing.expectEqualStrings("'\\n'", aw.getWritten());
1570 buf.clearRetainingCapacity();1542 aw.clearRetainingCapacity();
15711543
1572 try sz.value('\n', .{ .emit_codepoint_literals = .printable_ascii });1544 try s.value('\n', .{ .emit_codepoint_literals = .printable_ascii });
1573 try std.testing.expectEqualStrings("10", buf.items);1545 try std.testing.expectEqualStrings("10", aw.getWritten());
1574 buf.clearRetainingCapacity();1546 aw.clearRetainingCapacity();
15751547
1576 try sz.value('\n', .{ .emit_codepoint_literals = .never });1548 try s.value('\n', .{ .emit_codepoint_literals = .never });
1577 try std.testing.expectEqualStrings("10", buf.items);1549 try std.testing.expectEqualStrings("10", aw.getWritten());
1578 buf.clearRetainingCapacity();1550 aw.clearRetainingCapacity();
15791551
1580 // Large codepoint1552 // Large codepoint
1581 try sz.int('⚡');1553 try s.int('⚡');
1582 try std.testing.expectEqualStrings("9889", buf.items);1554 try std.testing.expectEqualStrings("9889", aw.getWritten());
1583 buf.clearRetainingCapacity();1555 aw.clearRetainingCapacity();
15841556
1585 try sz.codePoint('⚡');1557 try s.codePoint('⚡');
1586 try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", buf.items);1558 try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", aw.getWritten());
1587 buf.clearRetainingCapacity();1559 aw.clearRetainingCapacity();
15881560
1589 try sz.value('⚡', .{ .emit_codepoint_literals = .always });1561 try s.value('⚡', .{ .emit_codepoint_literals = .always });
1590 try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", buf.items);1562 try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", aw.getWritten());
1591 buf.clearRetainingCapacity();1563 aw.clearRetainingCapacity();
15921564
1593 try sz.value('⚡', .{ .emit_codepoint_literals = .printable_ascii });1565 try s.value('⚡', .{ .emit_codepoint_literals = .printable_ascii });
1594 try std.testing.expectEqualStrings("9889", buf.items);1566 try std.testing.expectEqualStrings("9889", aw.getWritten());
1595 buf.clearRetainingCapacity();1567 aw.clearRetainingCapacity();
15961568
1597 try sz.value('⚡', .{ .emit_codepoint_literals = .never });1569 try s.value('⚡', .{ .emit_codepoint_literals = .never });
1598 try std.testing.expectEqualStrings("9889", buf.items);1570 try std.testing.expectEqualStrings("9889", aw.getWritten());
1599 buf.clearRetainingCapacity();1571 aw.clearRetainingCapacity();
16001572
1601 // Invalid codepoint1573 // 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);1576 try s.int(0x110000 + 1);
1605 try std.testing.expectEqualStrings("1114113", buf.items);1577 try std.testing.expectEqualStrings("1114113", aw.getWritten());
1606 buf.clearRetainingCapacity();1578 aw.clearRetainingCapacity();
16071579
1608 try sz.value(0x110000 + 1, .{ .emit_codepoint_literals = .always });1580 try s.value(0x110000 + 1, .{ .emit_codepoint_literals = .always });
1609 try std.testing.expectEqualStrings("1114113", buf.items);1581 try std.testing.expectEqualStrings("1114113", aw.getWritten());
1610 buf.clearRetainingCapacity();1582 aw.clearRetainingCapacity();
16111583
1612 try sz.value(0x110000 + 1, .{ .emit_codepoint_literals = .printable_ascii });1584 try s.value(0x110000 + 1, .{ .emit_codepoint_literals = .printable_ascii });
1613 try std.testing.expectEqualStrings("1114113", buf.items);1585 try std.testing.expectEqualStrings("1114113", aw.getWritten());
1614 buf.clearRetainingCapacity();1586 aw.clearRetainingCapacity();
16151587
1616 try sz.value(0x110000 + 1, .{ .emit_codepoint_literals = .never });1588 try s.value(0x110000 + 1, .{ .emit_codepoint_literals = .never });
1617 try std.testing.expectEqualStrings("1114113", buf.items);1589 try std.testing.expectEqualStrings("1114113", aw.getWritten());
1618 buf.clearRetainingCapacity();1590 aw.clearRetainingCapacity();
16191591
1620 // Valid codepoint, not a codepoint type1592 // Valid codepoint, not a codepoint type
1621 try sz.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .always });1593 try s.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .always });
1622 try std.testing.expectEqualStrings("97", buf.items);1594 try std.testing.expectEqualStrings("97", aw.getWritten());
1623 buf.clearRetainingCapacity();1595 aw.clearRetainingCapacity();
16241596
1625 try sz.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .printable_ascii });1597 try s.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .printable_ascii });
1626 try std.testing.expectEqualStrings("97", buf.items);1598 try std.testing.expectEqualStrings("97", aw.getWritten());
1627 buf.clearRetainingCapacity();1599 aw.clearRetainingCapacity();
16281600
1629 try sz.value(@as(i32, 'a'), .{ .emit_codepoint_literals = .never });1601 try s.value(@as(i32, 'a'), .{ .emit_codepoint_literals = .never });
1630 try std.testing.expectEqualStrings("97", buf.items);1602 try std.testing.expectEqualStrings("97", aw.getWritten());
1631 buf.clearRetainingCapacity();1603 aw.clearRetainingCapacity();
16321604
1633 // Make sure value options are passed to children1605 // Make sure value options are passed to children
1634 try sz.value(.{ .c = '⚡' }, .{ .emit_codepoint_literals = .always });1606 try s.value(.{ .c = '⚡' }, .{ .emit_codepoint_literals = .always });
1635 try std.testing.expectEqualStrings(".{ .c = '\\xe2\\x9a\\xa1' }", buf.items);1607 try std.testing.expectEqualStrings(".{ .c = '\\xe2\\x9a\\xa1' }", aw.getWritten());
1636 buf.clearRetainingCapacity();1608 aw.clearRetainingCapacity();
16371609
1638 try sz.value(.{ .c = '⚡' }, .{ .emit_codepoint_literals = .never });1610 try s.value(.{ .c = '⚡' }, .{ .emit_codepoint_literals = .never });
1639 try std.testing.expectEqualStrings(".{ .c = 9889 }", buf.items);1611 try std.testing.expectEqualStrings(".{ .c = 9889 }", aw.getWritten());
1640 buf.clearRetainingCapacity();1612 aw.clearRetainingCapacity();
1641}1613}
16421614
1643test "std.zon stringify strings" {1615test "std.zon stringify strings" {
1644 var buf = std.ArrayList(u8).init(std.testing.allocator);1616 var aw: std.io.AllocatingWriter = undefined;
1645 defer buf.deinit();1617 defer aw.deinit();
1646 var sz = serializer(buf.writer(), .{});1618 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
16471619
1648 // Minimal case1620 // Minimal case
1649 try sz.string("abc⚡\n");1621 try s.string("abc⚡\n");
1650 try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", buf.items);1622 try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", aw.getWritten());
1651 buf.clearRetainingCapacity();1623 aw.clearRetainingCapacity();
16521624
1653 try sz.tuple("abc⚡\n", .{});1625 try s.tuple("abc⚡\n", .{});
1654 try std.testing.expectEqualStrings(1626 try std.testing.expectEqualStrings(
1655 \\.{1627 \\.{
1656 \\ 97,1628 \\ 97,
...@@ -1661,14 +1633,14 @@ test "std.zon stringify strings" {...@@ -1661,14 +1633,14 @@ test "std.zon stringify strings" {
1661 \\ 161,1633 \\ 161,
1662 \\ 10,1634 \\ 10,
1663 \\}1635 \\}
1664 , buf.items);1636 , aw.getWritten());
1665 buf.clearRetainingCapacity();1637 aw.clearRetainingCapacity();
16661638
1667 try sz.value("abc⚡\n", .{});1639 try s.value("abc⚡\n", .{});
1668 try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", buf.items);1640 try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", aw.getWritten());
1669 buf.clearRetainingCapacity();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 });
1672 try std.testing.expectEqualStrings(1644 try std.testing.expectEqualStrings(
1673 \\.{1645 \\.{
1674 \\ 97,1646 \\ 97,
...@@ -1679,113 +1651,113 @@ test "std.zon stringify strings" {...@@ -1679,113 +1651,113 @@ test "std.zon stringify strings" {
1679 \\ 161,1651 \\ 161,
1680 \\ 10,1652 \\ 10,
1681 \\}1653 \\}
1682 , buf.items);1654 , aw.getWritten());
1683 buf.clearRetainingCapacity();1655 aw.clearRetainingCapacity();
16841656
1685 // Value options are inherited by children1657 // Value options are inherited by children
1686 try sz.value(.{ .str = "abc" }, .{});1658 try s.value(.{ .str = "abc" }, .{});
1687 try std.testing.expectEqualStrings(".{ .str = \"abc\" }", buf.items);1659 try std.testing.expectEqualStrings(".{ .str = \"abc\" }", aw.getWritten());
1688 buf.clearRetainingCapacity();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 });
1691 try std.testing.expectEqualStrings(1663 try std.testing.expectEqualStrings(
1692 \\.{ .str = .{1664 \\.{ .str = .{
1693 \\ 97,1665 \\ 97,
1694 \\ 98,1666 \\ 98,
1695 \\ 99,1667 \\ 99,
1696 \\} }1668 \\} }
1697 , buf.items);1669 , aw.getWritten());
1698 buf.clearRetainingCapacity();1670 aw.clearRetainingCapacity();
16991671
1700 // Arrays (rather than pointers to arrays) of u8s are not considered strings, so that data can1672 // Arrays (rather than pointers to arrays) of u8s are not considered strings, so that data can
1701 // round trip correctly.1673 // round trip correctly.
1702 try sz.value("abc".*, .{});1674 try s.value("abc".*, .{});
1703 try std.testing.expectEqualStrings(1675 try std.testing.expectEqualStrings(
1704 \\.{1676 \\.{
1705 \\ 97,1677 \\ 97,
1706 \\ 98,1678 \\ 98,
1707 \\ 99,1679 \\ 99,
1708 \\}1680 \\}
1709 , buf.items);1681 , aw.getWritten());
1710 buf.clearRetainingCapacity();1682 aw.clearRetainingCapacity();
1711}1683}
17121684
1713test "std.zon stringify multiline strings" {1685test "std.zon stringify multiline strings" {
1714 var buf = std.ArrayList(u8).init(std.testing.allocator);1686 var aw: std.io.AllocatingWriter = undefined;
1715 defer buf.deinit();1687 defer aw.deinit();
1716 var sz = serializer(buf.writer(), .{});1688 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
17171689
1718 inline for (.{ true, false }) |whitespace| {1690 inline for (.{ true, false }) |whitespace| {
1719 sz.options.whitespace = whitespace;1691 s.options.whitespace = whitespace;
17201692
1721 {1693 {
1722 try sz.multilineString("", .{ .top_level = true });1694 try s.multilineString("", .{ .top_level = true });
1723 try std.testing.expectEqualStrings("\\\\", buf.items);1695 try std.testing.expectEqualStrings("\\\\", aw.getWritten());
1724 buf.clearRetainingCapacity();1696 aw.clearRetainingCapacity();
1725 }1697 }
17261698
1727 {1699 {
1728 try sz.multilineString("abc⚡", .{ .top_level = true });1700 try s.multilineString("abc⚡", .{ .top_level = true });
1729 try std.testing.expectEqualStrings("\\\\abc⚡", buf.items);1701 try std.testing.expectEqualStrings("\\\\abc⚡", aw.getWritten());
1730 buf.clearRetainingCapacity();1702 aw.clearRetainingCapacity();
1731 }1703 }
17321704
1733 {1705 {
1734 try sz.multilineString("abc⚡\ndef", .{ .top_level = true });1706 try s.multilineString("abc⚡\ndef", .{ .top_level = true });
1735 try std.testing.expectEqualStrings("\\\\abc⚡\n\\\\def", buf.items);1707 try std.testing.expectEqualStrings("\\\\abc⚡\n\\\\def", aw.getWritten());
1736 buf.clearRetainingCapacity();1708 aw.clearRetainingCapacity();
1737 }1709 }
17381710
1739 {1711 {
1740 try sz.multilineString("abc⚡\r\ndef", .{ .top_level = true });1712 try s.multilineString("abc⚡\r\ndef", .{ .top_level = true });
1741 try std.testing.expectEqualStrings("\\\\abc⚡\n\\\\def", buf.items);1713 try std.testing.expectEqualStrings("\\\\abc⚡\n\\\\def", aw.getWritten());
1742 buf.clearRetainingCapacity();1714 aw.clearRetainingCapacity();
1743 }1715 }
17441716
1745 {1717 {
1746 try sz.multilineString("\nabc⚡", .{ .top_level = true });1718 try s.multilineString("\nabc⚡", .{ .top_level = true });
1747 try std.testing.expectEqualStrings("\\\\\n\\\\abc⚡", buf.items);1719 try std.testing.expectEqualStrings("\\\\\n\\\\abc⚡", aw.getWritten());
1748 buf.clearRetainingCapacity();1720 aw.clearRetainingCapacity();
1749 }1721 }
17501722
1751 {1723 {
1752 try sz.multilineString("\r\nabc⚡", .{ .top_level = true });1724 try s.multilineString("\r\nabc⚡", .{ .top_level = true });
1753 try std.testing.expectEqualStrings("\\\\\n\\\\abc⚡", buf.items);1725 try std.testing.expectEqualStrings("\\\\\n\\\\abc⚡", aw.getWritten());
1754 buf.clearRetainingCapacity();1726 aw.clearRetainingCapacity();
1755 }1727 }
17561728
1757 {1729 {
1758 try sz.multilineString("abc\ndef", .{});1730 try s.multilineString("abc\ndef", .{});
1759 if (whitespace) {1731 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());
1761 } else {1733 } else {
1762 try std.testing.expectEqualStrings("\\\\abc\n\\\\def\n", buf.items);1734 try std.testing.expectEqualStrings("\\\\abc\n\\\\def\n", aw.getWritten());
1763 }1735 }
1764 buf.clearRetainingCapacity();1736 aw.clearRetainingCapacity();
1765 }1737 }
17661738
1767 {1739 {
1768 const str: []const u8 = &.{ 'a', '\r', 'c' };1740 const str: []const u8 = &.{ 'a', '\r', 'c' };
1769 try sz.string(str);1741 try s.string(str);
1770 try std.testing.expectEqualStrings("\"a\\rc\"", buf.items);1742 try std.testing.expectEqualStrings("\"a\\rc\"", aw.getWritten());
1771 buf.clearRetainingCapacity();1743 aw.clearRetainingCapacity();
1772 }1744 }
17731745
1774 {1746 {
1775 try std.testing.expectError(1747 try std.testing.expectError(
1776 error.InnerCarriageReturn,1748 error.InnerCarriageReturn,
1777 sz.multilineString(@as([]const u8, &.{ 'a', '\r', 'c' }), .{}),1749 s.multilineString(@as([]const u8, &.{ 'a', '\r', 'c' }), .{}),
1778 );1750 );
1779 try std.testing.expectError(1751 try std.testing.expectError(
1780 error.InnerCarriageReturn,1752 error.InnerCarriageReturn,
1781 sz.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\n' }), .{}),1753 s.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\n' }), .{}),
1782 );1754 );
1783 try std.testing.expectError(1755 try std.testing.expectError(
1784 error.InnerCarriageReturn,1756 error.InnerCarriageReturn,
1785 sz.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\r', '\n' }), .{}),1757 s.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\r', '\n' }), .{}),
1786 );1758 );
1787 try std.testing.expectEqualStrings("", buf.items);1759 try std.testing.expectEqualStrings("", aw.getWritten());
1788 buf.clearRetainingCapacity();1760 aw.clearRetainingCapacity();
1789 }1761 }
1790 }1762 }
1791}1763}
...@@ -1931,42 +1903,43 @@ test "std.zon stringify skip default fields" {...@@ -1931,42 +1903,43 @@ test "std.zon stringify skip default fields" {
1931}1903}
19321904
1933test "std.zon depth limits" {1905test "std.zon depth limits" {
1934 var buf = std.ArrayList(u8).init(std.testing.allocator);1906 var aw: std.io.AllocatingWriter = undefined;
1935 defer buf.deinit();1907 defer aw.deinit();
1908 const bw = aw.init(std.testing.allocator);
19361909
1937 const Recurse = struct { r: []const @This() };1910 const Recurse = struct { r: []const @This() };
19381911
1939 // Normal operation1912 // Normal operation
1940 try serializeMaxDepth(.{ 1, .{ 2, 3 } }, .{}, buf.writer(), 16);1913 try serializeMaxDepth(.{ 1, .{ 2, 3 } }, .{}, bw, 16);
1941 try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", buf.items);1914 try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", aw.getWritten());
1942 buf.clearRetainingCapacity();1915 aw.clearRetainingCapacity();
19431916
1944 try serializeArbitraryDepth(.{ 1, .{ 2, 3 } }, .{}, buf.writer());1917 try serializeArbitraryDepth(.{ 1, .{ 2, 3 } }, .{}, bw);
1945 try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", buf.items);1918 try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", aw.getWritten());
1946 buf.clearRetainingCapacity();1919 aw.clearRetainingCapacity();
19471920
1948 // Max depth failing on non recursive type1921 // Max depth failing on non recursive type
1949 try std.testing.expectError(1922 try std.testing.expectError(
1950 error.ExceededMaxDepth,1923 error.ExceededMaxDepth,
1951 serializeMaxDepth(.{ 1, .{ 2, .{ 3, 4 } } }, .{}, buf.writer(), 3),1924 serializeMaxDepth(.{ 1, .{ 2, .{ 3, 4 } } }, .{}, bw, 3),
1952 );1925 );
1953 try std.testing.expectEqualStrings("", buf.items);1926 try std.testing.expectEqualStrings("", aw.getWritten());
1954 buf.clearRetainingCapacity();1927 aw.clearRetainingCapacity();
19551928
1956 // Max depth passing on recursive type1929 // Max depth passing on recursive type
1957 {1930 {
1958 const maybe_recurse = Recurse{ .r = &.{} };1931 const maybe_recurse = Recurse{ .r = &.{} };
1959 try serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 2);1932 try serializeMaxDepth(maybe_recurse, .{}, bw, 2);
1960 try std.testing.expectEqualStrings(".{ .r = .{} }", buf.items);1933 try std.testing.expectEqualStrings(".{ .r = .{} }", aw.getWritten());
1961 buf.clearRetainingCapacity();1934 aw.clearRetainingCapacity();
1962 }1935 }
19631936
1964 // Unchecked passing on recursive type1937 // Unchecked passing on recursive type
1965 {1938 {
1966 const maybe_recurse = Recurse{ .r = &.{} };1939 const maybe_recurse = Recurse{ .r = &.{} };
1967 try serializeArbitraryDepth(maybe_recurse, .{}, buf.writer());1940 try serializeArbitraryDepth(maybe_recurse, .{}, bw);
1968 try std.testing.expectEqualStrings(".{ .r = .{} }", buf.items);1941 try std.testing.expectEqualStrings(".{ .r = .{} }", aw.getWritten());
1969 buf.clearRetainingCapacity();1942 aw.clearRetainingCapacity();
1970 }1943 }
19711944
1972 // Max depth failing on recursive type due to depth1945 // Max depth failing on recursive type due to depth
...@@ -1975,10 +1948,10 @@ test "std.zon depth limits" {...@@ -1975,10 +1948,10 @@ test "std.zon depth limits" {
1975 maybe_recurse.r = &.{.{ .r = &.{} }};1948 maybe_recurse.r = &.{.{ .r = &.{} }};
1976 try std.testing.expectError(1949 try std.testing.expectError(
1977 error.ExceededMaxDepth,1950 error.ExceededMaxDepth,
1978 serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 2),1951 serializeMaxDepth(maybe_recurse, .{}, bw, 2),
1979 );1952 );
1980 try std.testing.expectEqualStrings("", buf.items);1953 try std.testing.expectEqualStrings("", aw.getWritten());
1981 buf.clearRetainingCapacity();1954 aw.clearRetainingCapacity();
1982 }1955 }
19831956
1984 // Same but for a slice1957 // Same but for a slice
...@@ -1988,23 +1961,23 @@ test "std.zon depth limits" {...@@ -1988,23 +1961,23 @@ test "std.zon depth limits" {
19881961
1989 try std.testing.expectError(1962 try std.testing.expectError(
1990 error.ExceededMaxDepth,1963 error.ExceededMaxDepth,
1991 serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 2),1964 serializeMaxDepth(maybe_recurse, .{}, bw, 2),
1992 );1965 );
1993 try std.testing.expectEqualStrings("", buf.items);1966 try std.testing.expectEqualStrings("", aw.getWritten());
1994 buf.clearRetainingCapacity();1967 aw.clearRetainingCapacity();
19951968
1996 var sz = serializer(buf.writer(), .{});1969 var s: Serializer = .{ .writer = bw };
19971970
1998 try std.testing.expectError(1971 try std.testing.expectError(
1999 error.ExceededMaxDepth,1972 error.ExceededMaxDepth,
2000 sz.tupleMaxDepth(maybe_recurse, .{}, 2),1973 s.tupleMaxDepth(maybe_recurse, .{}, 2),
2001 );1974 );
2002 try std.testing.expectEqualStrings("", buf.items);1975 try std.testing.expectEqualStrings("", aw.getWritten());
2003 buf.clearRetainingCapacity();1976 aw.clearRetainingCapacity();
20041977
2005 try sz.tupleArbitraryDepth(maybe_recurse, .{});1978 try s.tupleArbitraryDepth(maybe_recurse, .{});
2006 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items);1979 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", aw.getWritten());
2007 buf.clearRetainingCapacity();1980 aw.clearRetainingCapacity();
2008 }1981 }
20091982
2010 // A slice succeeding1983 // A slice succeeding
...@@ -2012,19 +1985,19 @@ test "std.zon depth limits" {...@@ -2012,19 +1985,19 @@ test "std.zon depth limits" {
2012 var temp: [1]Recurse = .{.{ .r = &.{} }};1985 var temp: [1]Recurse = .{.{ .r = &.{} }};
2013 const maybe_recurse: []const Recurse = &temp;1986 const maybe_recurse: []const Recurse = &temp;
20141987
2015 try serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 3);1988 try serializeMaxDepth(maybe_recurse, .{}, bw, 3);
2016 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items);1989 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", aw.getWritten());
2017 buf.clearRetainingCapacity();1990 aw.clearRetainingCapacity();
20181991
2019 var sz = serializer(buf.writer(), .{});1992 var s: Serializer = .{ .writer = bw };
20201993
2021 try sz.tupleMaxDepth(maybe_recurse, .{}, 3);1994 try s.tupleMaxDepth(maybe_recurse, .{}, 3);
2022 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items);1995 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", aw.getWritten());
2023 buf.clearRetainingCapacity();1996 aw.clearRetainingCapacity();
20241997
2025 try sz.tupleArbitraryDepth(maybe_recurse, .{});1998 try s.tupleArbitraryDepth(maybe_recurse, .{});
2026 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items);1999 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", aw.getWritten());
2027 buf.clearRetainingCapacity();2000 aw.clearRetainingCapacity();
2028 }2001 }
20292002
2030 // Max depth failing on recursive type due to recursion2003 // Max depth failing on recursive type due to recursion
...@@ -2035,46 +2008,46 @@ test "std.zon depth limits" {...@@ -2035,46 +2008,46 @@ test "std.zon depth limits" {
20352008
2036 try std.testing.expectError(2009 try std.testing.expectError(
2037 error.ExceededMaxDepth,2010 error.ExceededMaxDepth,
2038 serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 128),2011 serializeMaxDepth(maybe_recurse, .{}, bw, 128),
2039 );2012 );
2040 try std.testing.expectEqualStrings("", buf.items);2013 try std.testing.expectEqualStrings("", aw.getWritten());
2041 buf.clearRetainingCapacity();2014 aw.clearRetainingCapacity();
20422015
2043 var sz = serializer(buf.writer(), .{});2016 var s: Serializer = .{ .writer = bw };
2044 try std.testing.expectError(2017 try std.testing.expectError(
2045 error.ExceededMaxDepth,2018 error.ExceededMaxDepth,
2046 sz.tupleMaxDepth(maybe_recurse, .{}, 128),2019 s.tupleMaxDepth(maybe_recurse, .{}, 128),
2047 );2020 );
2048 try std.testing.expectEqualStrings("", buf.items);2021 try std.testing.expectEqualStrings("", aw.getWritten());
2049 buf.clearRetainingCapacity();2022 aw.clearRetainingCapacity();
2050 }2023 }
20512024
2052 // Max depth on other parts of the lower level API2025 // Max depth on other parts of the lower level API
2053 {2026 {
2054 var sz = serializer(buf.writer(), .{});2027 var s: Serializer = .{ .writer = bw };
20552028
2056 const maybe_recurse: []const Recurse = &.{};2029 const maybe_recurse: []const Recurse = &.{};
20572030
2058 try std.testing.expectError(error.ExceededMaxDepth, sz.valueMaxDepth(1, .{}, 0));2031 try std.testing.expectError(error.ExceededMaxDepth, s.valueMaxDepth(1, .{}, 0));
2059 try sz.valueMaxDepth(2, .{}, 1);2032 try s.valueMaxDepth(2, .{}, 1);
2060 try sz.value(3, .{});2033 try s.value(3, .{});
2061 try sz.valueArbitraryDepth(maybe_recurse, .{});2034 try s.valueArbitraryDepth(maybe_recurse, .{});
20622035
2063 var s = try sz.beginStruct(.{});2036 var wip_struct = try s.beginStruct(.{});
2064 try std.testing.expectError(error.ExceededMaxDepth, s.fieldMaxDepth("a", 1, .{}, 0));2037 try std.testing.expectError(error.ExceededMaxDepth, wip_struct.fieldMaxDepth("a", 1, .{}, 0));
2065 try s.fieldMaxDepth("b", 4, .{}, 1);2038 try wip_struct.fieldMaxDepth("b", 4, .{}, 1);
2066 try s.field("c", 5, .{});2039 try wip_struct.field("c", 5, .{});
2067 try s.fieldArbitraryDepth("d", maybe_recurse, .{});2040 try wip_struct.fieldArbitraryDepth("d", maybe_recurse, .{});
2068 try s.end();2041 try wip_struct.end();
20692042
2070 var t = try sz.beginTuple(.{});2043 var t = try s.beginTuple(.{});
2071 try std.testing.expectError(error.ExceededMaxDepth, t.fieldMaxDepth(1, .{}, 0));2044 try std.testing.expectError(error.ExceededMaxDepth, t.fieldMaxDepth(1, .{}, 0));
2072 try t.fieldMaxDepth(6, .{}, 1);2045 try t.fieldMaxDepth(6, .{}, 1);
2073 try t.field(7, .{});2046 try t.field(7, .{});
2074 try t.fieldArbitraryDepth(maybe_recurse, .{});2047 try t.fieldArbitraryDepth(maybe_recurse, .{});
2075 try t.end();2048 try t.end();
20762049
2077 var a = try sz.beginTuple(.{});2050 var a = try s.beginTuple(.{});
2078 try std.testing.expectError(error.ExceededMaxDepth, a.fieldMaxDepth(1, .{}, 0));2051 try std.testing.expectError(error.ExceededMaxDepth, a.fieldMaxDepth(1, .{}, 0));
2079 try a.fieldMaxDepth(8, .{}, 1);2052 try a.fieldMaxDepth(8, .{}, 1);
2080 try a.field(9, .{});2053 try a.field(9, .{});
...@@ -2095,7 +2068,7 @@ test "std.zon depth limits" {...@@ -2095,7 +2068,7 @@ test "std.zon depth limits" {
2095 \\ 9,2068 \\ 9,
2096 \\ .{},2069 \\ .{},
2097 \\}2070 \\}
2098 , buf.items);2071 , aw.getWritten());
2099 }2072 }
2100}2073}
21012074
...@@ -2191,42 +2164,42 @@ test "std.zon stringify primitives" {...@@ -2191,42 +2164,42 @@ test "std.zon stringify primitives" {
2191}2164}
21922165
2193test "std.zon stringify ident" {2166test "std.zon stringify ident" {
2194 var buf = std.ArrayList(u8).init(std.testing.allocator);2167 var aw: std.io.AllocatingWriter = undefined;
2195 defer buf.deinit();2168 defer aw.deinit();
2196 var sz = serializer(buf.writer(), .{});2169 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
21972170
2198 try expectSerializeEqual(".{ .a = 0 }", .{ .a = 0 }, .{});2171 try expectSerializeEqual(".{ .a = 0 }", .{ .a = 0 }, .{});
2199 try sz.ident("a");2172 try s.ident("a");
2200 try std.testing.expectEqualStrings(".a", buf.items);2173 try std.testing.expectEqualStrings(".a", aw.getWritten());
2201 buf.clearRetainingCapacity();2174 aw.clearRetainingCapacity();
22022175
2203 try sz.ident("foo_1");2176 try s.ident("foo_1");
2204 try std.testing.expectEqualStrings(".foo_1", buf.items);2177 try std.testing.expectEqualStrings(".foo_1", aw.getWritten());
2205 buf.clearRetainingCapacity();2178 aw.clearRetainingCapacity();
22062179
2207 try sz.ident("_foo_1");2180 try s.ident("_foo_1");
2208 try std.testing.expectEqualStrings("._foo_1", buf.items);2181 try std.testing.expectEqualStrings("._foo_1", aw.getWritten());
2209 buf.clearRetainingCapacity();2182 aw.clearRetainingCapacity();
22102183
2211 try sz.ident("foo bar");2184 try s.ident("foo bar");
2212 try std.testing.expectEqualStrings(".@\"foo bar\"", buf.items);2185 try std.testing.expectEqualStrings(".@\"foo bar\"", aw.getWritten());
2213 buf.clearRetainingCapacity();2186 aw.clearRetainingCapacity();
22142187
2215 try sz.ident("1foo");2188 try s.ident("1foo");
2216 try std.testing.expectEqualStrings(".@\"1foo\"", buf.items);2189 try std.testing.expectEqualStrings(".@\"1foo\"", aw.getWritten());
2217 buf.clearRetainingCapacity();2190 aw.clearRetainingCapacity();
22182191
2219 try sz.ident("var");2192 try s.ident("var");
2220 try std.testing.expectEqualStrings(".@\"var\"", buf.items);2193 try std.testing.expectEqualStrings(".@\"var\"", aw.getWritten());
2221 buf.clearRetainingCapacity();2194 aw.clearRetainingCapacity();
22222195
2223 try sz.ident("true");2196 try s.ident("true");
2224 try std.testing.expectEqualStrings(".true", buf.items);2197 try std.testing.expectEqualStrings(".true", aw.getWritten());
2225 buf.clearRetainingCapacity();2198 aw.clearRetainingCapacity();
22262199
2227 try sz.ident("_");2200 try s.ident("_");
2228 try std.testing.expectEqualStrings("._", buf.items);2201 try std.testing.expectEqualStrings("._", aw.getWritten());
2229 buf.clearRetainingCapacity();2202 aw.clearRetainingCapacity();
22302203
2231 const Enum = enum {2204 const Enum = enum {
2232 @"foo bar",2205 @"foo bar",
...@@ -2238,40 +2211,40 @@ test "std.zon stringify ident" {...@@ -2238,40 +2211,40 @@ test "std.zon stringify ident" {
2238}2211}
22392212
2240test "std.zon stringify as tuple" {2213test "std.zon stringify as tuple" {
2241 var buf = std.ArrayList(u8).init(std.testing.allocator);2214 var aw: std.io.AllocatingWriter = undefined;
2242 defer buf.deinit();2215 defer aw.deinit();
2243 var sz = serializer(buf.writer(), .{});2216 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
22442217
2245 // Tuples2218 // Tuples
2246 try sz.tuple(.{ 1, 2 }, .{});2219 try s.tuple(.{ 1, 2 }, .{});
2247 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);2220 try std.testing.expectEqualStrings(".{ 1, 2 }", aw.getWritten());
2248 buf.clearRetainingCapacity();2221 aw.clearRetainingCapacity();
22492222
2250 // Slice2223 // Slice
2251 try sz.tuple(@as([]const u8, &.{ 1, 2 }), .{});2224 try s.tuple(@as([]const u8, &.{ 1, 2 }), .{});
2252 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);2225 try std.testing.expectEqualStrings(".{ 1, 2 }", aw.getWritten());
2253 buf.clearRetainingCapacity();2226 aw.clearRetainingCapacity();
22542227
2255 // Array2228 // Array
2256 try sz.tuple([2]u8{ 1, 2 }, .{});2229 try s.tuple([2]u8{ 1, 2 }, .{});
2257 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);2230 try std.testing.expectEqualStrings(".{ 1, 2 }", aw.getWritten());
2258 buf.clearRetainingCapacity();2231 aw.clearRetainingCapacity();
2259}2232}
22602233
2261test "std.zon stringify as float" {2234test "std.zon stringify as float" {
2262 var buf = std.ArrayList(u8).init(std.testing.allocator);2235 var aw: std.io.AllocatingWriter = undefined;
2263 defer buf.deinit();2236 defer aw.deinit();
2264 var sz = serializer(buf.writer(), .{});2237 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
22652238
2266 // Comptime float2239 // Comptime float
2267 try sz.float(2.5);2240 try s.float(2.5);
2268 try std.testing.expectEqualStrings("2.5", buf.items);2241 try std.testing.expectEqualStrings("2.5", aw.getWritten());
2269 buf.clearRetainingCapacity();2242 aw.clearRetainingCapacity();
22702243
2271 // Sized float2244 // Sized float
2272 try sz.float(@as(f32, 2.5));2245 try s.float(@as(f32, 2.5));
2273 try std.testing.expectEqualStrings("2.5", buf.items);2246 try std.testing.expectEqualStrings("2.5", aw.getWritten());
2274 buf.clearRetainingCapacity();2247 aw.clearRetainingCapacity();
2275}2248}
22762249
2277test "std.zon stringify vector" {2250test "std.zon stringify vector" {
...@@ -2363,13 +2336,13 @@ test "std.zon pointers" {...@@ -2363,13 +2336,13 @@ test "std.zon pointers" {
2363}2336}
23642337
2365test "std.zon tuple/struct field" {2338test "std.zon tuple/struct field" {
2366 var buf = std.ArrayList(u8).init(std.testing.allocator);2339 var aw: std.io.AllocatingWriter = undefined;
2367 defer buf.deinit();2340 defer aw.deinit();
2368 var sz = serializer(buf.writer(), .{});2341 var s: Serializer = .{ .writer = aw.init(std.testing.allocator) };
23692342
2370 // Test on structs2343 // Test on structs
2371 {2344 {
2372 var root = try sz.beginStruct(.{});2345 var root = try s.beginStruct(.{});
2373 {2346 {
2374 var tuple = try root.beginTupleField("foo", .{});2347 var tuple = try root.beginTupleField("foo", .{});
2375 try tuple.field(0, .{});2348 try tuple.field(0, .{});
...@@ -2395,13 +2368,13 @@ test "std.zon tuple/struct field" {...@@ -2395,13 +2368,13 @@ test "std.zon tuple/struct field" {
2395 \\ .b = 1,2368 \\ .b = 1,
2396 \\ },2369 \\ },
2397 \\}2370 \\}
2398 , buf.items);2371 , aw.getWritten());
2399 buf.clearRetainingCapacity();2372 aw.clearRetainingCapacity();
2400 }2373 }
24012374
2402 // Test on tuples2375 // Test on tuples
2403 {2376 {
2404 var root = try sz.beginTuple(.{});2377 var root = try s.beginTuple(.{});
2405 {2378 {
2406 var tuple = try root.beginTupleField(.{});2379 var tuple = try root.beginTupleField(.{});
2407 try tuple.field(0, .{});2380 try tuple.field(0, .{});
...@@ -2427,7 +2400,7 @@ test "std.zon tuple/struct field" {...@@ -2427,7 +2400,7 @@ test "std.zon tuple/struct field" {
2427 \\ .b = 1,2400 \\ .b = 1,
2428 \\ },2401 \\ },
2429 \\}2402 \\}
2430 , buf.items);2403 , aw.getWritten());
2431 buf.clearRetainingCapacity();2404 aw.clearRetainingCapacity();
2432 }2405 }
2433}2406}