authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-17 19:49:36-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:26-07:00
log051c7af49ad3e3d4575fd63c68cf96eff144bed7
treeaaa629b7e8c38bdbd94fcd1a47e020a22a28224a
parente154b62d63539cdd0f281dd4eaadc5ab229d2e24

get std lib json tests passing


7 files changed, 95 insertions(+), 127 deletions(-)

lib/std/io/BufferedWriter.zig+2-2
...@@ -757,9 +757,9 @@ pub fn printValue(...@@ -757,9 +757,9 @@ pub fn printValue(
757 return printValue(bw, actual_fmt, options, value.*, max_depth);757 return printValue(bw, actual_fmt, options, value.*, max_depth);
758 },758 },
759 else => {759 else => {
760 const buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" };760 var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" };
761 try writevAll(bw, &buffers);761 try writevAll(bw, &buffers);
762 try printIntOptions(bw, @intFromPtr(value), 16, .lower);762 try printIntOptions(bw, @intFromPtr(value), 16, .lower, options);
763 },763 },
764 },764 },
765 .many, .c => {765 .many, .c => {
lib/std/io/Writer.zig+17
...@@ -76,6 +76,23 @@ pub fn writeFile(...@@ -76,6 +76,23 @@ pub fn writeFile(
76 return w.vtable.writeFile(w.context, file, offset, len, headers_and_trailers, headers_len);76 return w.vtable.writeFile(w.context, file, offset, len, headers_and_trailers, headers_len);
77}77}
7878
79pub fn unimplemented_writeFile(
80 context: *anyopaque,
81 file: std.fs.File,
82 offset: u64,
83 len: VTable.FileLen,
84 headers_and_trailers: []const []const u8,
85 headers_len: usize,
86) anyerror!usize {
87 _ = context;
88 _ = file;
89 _ = offset;
90 _ = len;
91 _ = headers_and_trailers;
92 _ = headers_len;
93 return error.Unimplemented;
94}
95
79pub fn write(w: Writer, bytes: []const u8) anyerror!usize {96pub fn write(w: Writer, bytes: []const u8) anyerror!usize {
80 const single: [1][]const u8 = .{bytes};97 const single: [1][]const u8 = .{bytes};
81 return w.vtable.writeSplat(w.context, &single, 1);98 return w.vtable.writeSplat(w.context, &single, 1);
lib/std/json/Stringify.zig+59-106
...@@ -24,8 +24,10 @@ const ArrayList = std.ArrayList;...@@ -24,8 +24,10 @@ const ArrayList = std.ArrayList;
24const BitStack = std.BitStack;24const BitStack = std.BitStack;
25const Stringify = @This();25const Stringify = @This();
2626
27const OBJECT_MODE = 0;27const IndentationMode = enum(u1) {
28const ARRAY_MODE = 1;28 object = 0,
29 array = 1,
30};
2931
30writer: *std.io.BufferedWriter,32writer: *std.io.BufferedWriter,
31options: Options = .{},33options: Options = .{},
...@@ -78,22 +80,22 @@ else...@@ -78,22 +80,22 @@ else
78pub fn beginArray(self: *Stringify) anyerror!void {80pub fn beginArray(self: *Stringify) anyerror!void {
79 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);81 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
80 try self.valueStart();82 try self.valueStart();
81 try self.stream.writeByte('[');83 try self.writer.writeByte('[');
82 try self.pushIndentation(ARRAY_MODE);84 try self.pushIndentation(.array);
83 self.next_punctuation = .none;85 self.next_punctuation = .none;
84}86}
8587
86pub fn beginObject(self: *Stringify) anyerror!void {88pub fn beginObject(self: *Stringify) anyerror!void {
87 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);89 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
88 try self.valueStart();90 try self.valueStart();
89 try self.stream.writeByte('{');91 try self.writer.writeByte('{');
90 try self.pushIndentation(OBJECT_MODE);92 try self.pushIndentation(.object);
91 self.next_punctuation = .none;93 self.next_punctuation = .none;
92}94}
9395
94pub fn endArray(self: *Stringify) anyerror!void {96pub fn endArray(self: *Stringify) anyerror!void {
95 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);97 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
96 self.popIndentation(ARRAY_MODE);98 self.popIndentation(.array);
97 switch (self.next_punctuation) {99 switch (self.next_punctuation) {
98 .none => {},100 .none => {},
99 .comma => {101 .comma => {
...@@ -101,13 +103,13 @@ pub fn endArray(self: *Stringify) anyerror!void {...@@ -101,13 +103,13 @@ pub fn endArray(self: *Stringify) anyerror!void {
101 },103 },
102 .the_beginning, .colon => unreachable,104 .the_beginning, .colon => unreachable,
103 }105 }
104 try self.stream.writeByte(']');106 try self.writer.writeByte(']');
105 self.valueDone();107 self.valueDone();
106}108}
107109
108pub fn endObject(self: *Stringify) anyerror!void {110pub fn endObject(self: *Stringify) anyerror!void {
109 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);111 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
110 self.popIndentation(OBJECT_MODE);112 self.popIndentation(.object);
111 switch (self.next_punctuation) {113 switch (self.next_punctuation) {
112 .none => {},114 .none => {},
113 .comma => {115 .comma => {
...@@ -115,24 +117,24 @@ pub fn endObject(self: *Stringify) anyerror!void {...@@ -115,24 +117,24 @@ pub fn endObject(self: *Stringify) anyerror!void {
115 },117 },
116 .the_beginning, .colon => unreachable,118 .the_beginning, .colon => unreachable,
117 }119 }
118 try self.stream.writeByte('}');120 try self.writer.writeByte('}');
119 self.valueDone();121 self.valueDone();
120}122}
121123
122fn pushIndentation(self: *Stringify, mode: u1) !void {124fn pushIndentation(self: *Stringify, mode: IndentationMode) !void {
123 switch (safety_checks) {125 switch (safety_checks) {
124 .checked_to_fixed_depth => {126 .checked_to_fixed_depth => {
125 BitStack.pushWithStateAssumeCapacity(&self.nesting_stack, &self.indent_level, mode);127 BitStack.pushWithStateAssumeCapacity(&self.nesting_stack, &self.indent_level, @intFromEnum(mode));
126 },128 },
127 .assumed_correct => {129 .assumed_correct => {
128 self.indent_level += 1;130 self.indent_level += 1;
129 },131 },
130 }132 }
131}133}
132fn popIndentation(self: *Stringify, assert_its_this_one: u1) void {134fn popIndentation(self: *Stringify, expected_mode: IndentationMode) void {
133 switch (safety_checks) {135 switch (safety_checks) {
134 .checked_to_fixed_depth => {136 .checked_to_fixed_depth => {
135 assert(BitStack.popWithState(&self.nesting_stack, &self.indent_level) == assert_its_this_one);137 assert(BitStack.popWithState(&self.nesting_stack, &self.indent_level) == @intFromEnum(expected_mode));
136 },138 },
137 .assumed_correct => {139 .assumed_correct => {
138 self.indent_level -= 1;140 self.indent_level -= 1;
...@@ -154,8 +156,8 @@ fn indent(self: *Stringify) !void {...@@ -154,8 +156,8 @@ fn indent(self: *Stringify) !void {
154 break :blk self.indent_level;156 break :blk self.indent_level;
155 },157 },
156 };158 };
157 try self.stream.writeByte('\n');159 try self.writer.writeByte('\n');
158 try self.stream.writeByteNTimes(char, n_chars);160 try self.writer.splatByteAll(char, n_chars);
159}161}
160162
161fn valueStart(self: *Stringify) !void {163fn valueStart(self: *Stringify) !void {
...@@ -178,13 +180,13 @@ fn valueStartAssumeTypeOk(self: *Stringify) !void {...@@ -178,13 +180,13 @@ fn valueStartAssumeTypeOk(self: *Stringify) !void {
178 },180 },
179 .comma => {181 .comma => {
180 // Subsequent item in a container.182 // Subsequent item in a container.
181 try self.stream.writeByte(',');183 try self.writer.writeByte(',');
182 try self.indent();184 try self.indent();
183 },185 },
184 .colon => {186 .colon => {
185 try self.stream.writeByte(':');187 try self.writer.writeByte(':');
186 if (self.options.whitespace != .minified) {188 if (self.options.whitespace != .minified) {
187 try self.stream.writeByte(' ');189 try self.writer.writeByte(' ');
188 }190 }
189 },191 },
190 }192 }
...@@ -197,7 +199,7 @@ fn valueDone(self: *Stringify) void {...@@ -197,7 +199,7 @@ fn valueDone(self: *Stringify) void {
197fn isObjectKeyExpected(self: *const Stringify) ?bool {199fn isObjectKeyExpected(self: *const Stringify) ?bool {
198 switch (safety_checks) {200 switch (safety_checks) {
199 .checked_to_fixed_depth => return self.indent_level > 0 and201 .checked_to_fixed_depth => return self.indent_level > 0 and
200 BitStack.peekWithState(&self.nesting_stack, self.indent_level) == OBJECT_MODE and202 BitStack.peekWithState(&self.nesting_stack, self.indent_level) == @intFromEnum(IndentationMode.object) and
201 self.next_punctuation != .colon,203 self.next_punctuation != .colon,
202 .assumed_correct => return null,204 .assumed_correct => return null,
203 }205 }
...@@ -214,7 +216,7 @@ fn isComplete(self: *const Stringify) bool {...@@ -214,7 +216,7 @@ fn isComplete(self: *const Stringify) bool {
214pub fn print(self: *Stringify, comptime fmt: []const u8, args: anytype) anyerror!void {216pub fn print(self: *Stringify, comptime fmt: []const u8, args: anytype) anyerror!void {
215 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);217 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
216 try self.valueStart();218 try self.valueStart();
217 try self.stream.print(fmt, args);219 try self.writer.print(fmt, args);
218 self.valueDone();220 self.valueDone();
219}221}
220222
...@@ -224,7 +226,6 @@ test print {...@@ -224,7 +226,6 @@ test print {
224 out.initFixed(&out_buf);226 out.initFixed(&out_buf);
225227
226 var w: Stringify = .{ .writer = &out, .options = .{ .whitespace = .indent_2 } };228 var w: Stringify = .{ .writer = &out, .options = .{ .whitespace = .indent_2 } };
227 defer w.deinit();
228229
229 try w.beginObject();230 try w.beginObject();
230 try w.objectField("a");231 try w.objectField("a");
...@@ -248,8 +249,8 @@ test print {...@@ -248,8 +249,8 @@ test print {
248 try std.testing.expectEqualStrings(expected, out.getWritten());249 try std.testing.expectEqualStrings(expected, out.getWritten());
249}250}
250251
251/// An alternative to calling `write` that allows you to write directly to the `.stream` field, e.g. with `.stream.writeAll()`.252/// An alternative to calling `write` that allows you to write directly to the `.writer` field, e.g. with `.writer.writeAll()`.
252/// Call `beginWriteRaw()`, then write a complete value (including any quotes if necessary) directly to the `.stream` field,253/// Call `beginWriteRaw()`, then write a complete value (including any quotes if necessary) directly to the `.writer` field,
253/// then call `endWriteRaw()`.254/// then call `endWriteRaw()`.
254/// This can be useful for streaming very long strings into the output without needing it all buffered in memory.255/// This can be useful for streaming very long strings into the output without needing it all buffered in memory.
255pub fn beginWriteRaw(self: *Stringify) !void {256pub fn beginWriteRaw(self: *Stringify) !void {
...@@ -276,7 +277,7 @@ pub fn endWriteRaw(self: *Stringify) void {...@@ -276,7 +277,7 @@ pub fn endWriteRaw(self: *Stringify) void {
276pub fn objectField(self: *Stringify, key: []const u8) anyerror!void {277pub fn objectField(self: *Stringify, key: []const u8) anyerror!void {
277 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);278 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
278 try self.objectFieldStart();279 try self.objectFieldStart();
279 try encodeJsonString(key, self.options, self.stream);280 try encodeJsonString(key, self.options, self.writer);
280 self.next_punctuation = .colon;281 self.next_punctuation = .colon;
281}282}
282/// See `Stringify` for when to call this method.283/// See `Stringify` for when to call this method.
...@@ -287,12 +288,12 @@ pub fn objectFieldRaw(self: *Stringify, quoted_key: []const u8) anyerror!void {...@@ -287,12 +288,12 @@ pub fn objectFieldRaw(self: *Stringify, quoted_key: []const u8) anyerror!void {
287 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);288 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
288 assert(quoted_key.len >= 2 and quoted_key[0] == '"' and quoted_key[quoted_key.len - 1] == '"'); // quoted_key should be "quoted".289 assert(quoted_key.len >= 2 and quoted_key[0] == '"' and quoted_key[quoted_key.len - 1] == '"'); // quoted_key should be "quoted".
289 try self.objectFieldStart();290 try self.objectFieldStart();
290 try self.stream.writeAll(quoted_key);291 try self.writer.writeAll(quoted_key);
291 self.next_punctuation = .colon;292 self.next_punctuation = .colon;
292}293}
293294
294/// In the rare case that you need to write very long object field names,295/// In the rare case that you need to write very long object field names,
295/// this is an alternative to `objectField` and `objectFieldRaw` that allows you to write directly to the `.stream` field296/// this is an alternative to `objectField` and `objectFieldRaw` that allows you to write directly to the `.writer` field
296/// similar to `beginWriteRaw`.297/// similar to `beginWriteRaw`.
297/// Call `endObjectFieldRaw()` when you're done.298/// Call `endObjectFieldRaw()` when you're done.
298pub fn beginObjectFieldRaw(self: *Stringify) !void {299pub fn beginObjectFieldRaw(self: *Stringify) !void {
...@@ -351,9 +352,9 @@ pub fn write(self: *Stringify, v: anytype) anyerror!void {...@@ -351,9 +352,9 @@ pub fn write(self: *Stringify, v: anytype) anyerror!void {
351 if (self.options.emit_nonportable_numbers_as_strings and352 if (self.options.emit_nonportable_numbers_as_strings and
352 (v <= -(1 << 53) or v >= (1 << 53)))353 (v <= -(1 << 53) or v >= (1 << 53)))
353 {354 {
354 try self.stream.print("\"{}\"", .{v});355 try self.writer.print("\"{}\"", .{v});
355 } else {356 } else {
356 try self.stream.print("{}", .{v});357 try self.writer.print("{}", .{v});
357 }358 }
358 self.valueDone();359 self.valueDone();
359 return;360 return;
...@@ -364,25 +365,25 @@ pub fn write(self: *Stringify, v: anytype) anyerror!void {...@@ -364,25 +365,25 @@ pub fn write(self: *Stringify, v: anytype) anyerror!void {
364 .float, .comptime_float => {365 .float, .comptime_float => {
365 if (@as(f64, @floatCast(v)) == v) {366 if (@as(f64, @floatCast(v)) == v) {
366 try self.valueStart();367 try self.valueStart();
367 try self.stream.print("{}", .{@as(f64, @floatCast(v))});368 try self.writer.print("{}", .{@as(f64, @floatCast(v))});
368 self.valueDone();369 self.valueDone();
369 return;370 return;
370 }371 }
371 try self.valueStart();372 try self.valueStart();
372 try self.stream.print("\"{}\"", .{v});373 try self.writer.print("\"{}\"", .{v});
373 self.valueDone();374 self.valueDone();
374 return;375 return;
375 },376 },
376377
377 .bool => {378 .bool => {
378 try self.valueStart();379 try self.valueStart();
379 try self.stream.writeAll(if (v) "true" else "false");380 try self.writer.writeAll(if (v) "true" else "false");
380 self.valueDone();381 self.valueDone();
381 return;382 return;
382 },383 },
383 .null => {384 .null => {
384 try self.valueStart();385 try self.valueStart();
385 try self.stream.writeAll("null");386 try self.writer.writeAll("null");
386 self.valueDone();387 self.valueDone();
387 return;388 return;
388 },389 },
...@@ -529,7 +530,7 @@ pub fn write(self: *Stringify, v: anytype) anyerror!void {...@@ -529,7 +530,7 @@ pub fn write(self: *Stringify, v: anytype) anyerror!void {
529530
530fn stringValue(self: *Stringify, s: []const u8) !void {531fn stringValue(self: *Stringify, s: []const u8) !void {
531 try self.valueStart();532 try self.valueStart();
532 try encodeJsonString(s, self.options, self.stream);533 try encodeJsonString(s, self.options, self.writer);
533 self.valueDone();534 self.valueDone();
534}535}
535536
...@@ -564,7 +565,7 @@ pub const Options = struct {...@@ -564,7 +565,7 @@ pub const Options = struct {
564 emit_nonportable_numbers_as_strings: bool = false,565 emit_nonportable_numbers_as_strings: bool = false,
565};566};
566567
567/// Writes the given value to the `std.io.Writer` stream.568/// Writes the given value to the `std.io.Writer` writer.
568/// See `Stringify` for how the given value is serialized into JSON.569/// See `Stringify` for how the given value is serialized into JSON.
569/// The maximum nesting depth of the output JSON document is 256.570/// The maximum nesting depth of the output JSON document is 256.
570pub fn value(v: anytype, options: Options, writer: *std.io.BufferedWriter) anyerror!void {571pub fn value(v: anytype, options: Options, writer: *std.io.BufferedWriter) anyerror!void {
...@@ -616,7 +617,7 @@ pub fn valueAlloc(gpa: Allocator, v: anytype, options: Options) error{OutOfMemor...@@ -616,7 +617,7 @@ pub fn valueAlloc(gpa: Allocator, v: anytype, options: Options) error{OutOfMemor
616 var aw: std.io.AllocatingWriter = undefined;617 var aw: std.io.AllocatingWriter = undefined;
617 const writer = aw.init(gpa);618 const writer = aw.init(gpa);
618 defer aw.deinit();619 defer aw.deinit();
619 try value(v, options, writer);620 value(v, options, writer) catch return error.OutOfMemory; // TODO: try @errorCast(...)
620 return aw.toOwnedSlice();621 return aw.toOwnedSlice();
621}622}
622623
...@@ -631,27 +632,27 @@ test valueAlloc {...@@ -631,27 +632,27 @@ test valueAlloc {
631 try std.testing.expectEqualStrings(expected, actual);632 try std.testing.expectEqualStrings(expected, actual);
632}633}
633634
634fn outputUnicodeEscape(codepoint: u21, out_stream: *std.io.BufferedWriter) !void {635fn outputUnicodeEscape(codepoint: u21, bw: *std.io.BufferedWriter) anyerror!void {
635 if (codepoint <= 0xFFFF) {636 if (codepoint <= 0xFFFF) {
636 // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF),637 // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF),
637 // then it may be represented as a six-character sequence: a reverse solidus, followed638 // then it may be represented as a six-character sequence: a reverse solidus, followed
638 // by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point.639 // by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point.
639 try out_stream.writeAll("\\u");640 try bw.writeAll("\\u");
640 try std.fmt.formatIntValue(codepoint, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream);641 try bw.printInt("x", .{ .width = 4, .fill = '0' }, codepoint);
641 } else {642 } else {
642 assert(codepoint <= 0x10FFFF);643 assert(codepoint <= 0x10FFFF);
643 // To escape an extended character that is not in the Basic Multilingual Plane,644 // To escape an extended character that is not in the Basic Multilingual Plane,
644 // the character is represented as a 12-character sequence, encoding the UTF-16 surrogate pair.645 // the character is represented as a 12-character sequence, encoding the UTF-16 surrogate pair.
645 const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800;646 const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800;
646 const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00;647 const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00;
647 try out_stream.writeAll("\\u");648 try bw.writeAll("\\u");
648 try std.fmt.formatIntValue(high, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream);649 try bw.printInt("x", .{ .width = 4, .fill = '0' }, high);
649 try out_stream.writeAll("\\u");650 try bw.writeAll("\\u");
650 try std.fmt.formatIntValue(low, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream);651 try bw.printInt("x", .{ .width = 4, .fill = '0' }, low);
651 }652 }
652}653}
653654
654fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) !void {655fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) anyerror!void {
655 switch (c) {656 switch (c) {
656 '\\' => try writer.writeAll("\\\\"),657 '\\' => try writer.writeAll("\\\\"),
657 '\"' => try writer.writeAll("\\\""),658 '\"' => try writer.writeAll("\\\""),
...@@ -665,14 +666,14 @@ fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) !void {...@@ -665,14 +666,14 @@ fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) !void {
665}666}
666667
667/// Write `string` to `writer` as a JSON encoded string.668/// Write `string` to `writer` as a JSON encoded string.
668pub fn encodeJsonString(string: []const u8, options: Options, writer: *std.io.BufferedWriter) !void {669pub fn encodeJsonString(string: []const u8, options: Options, writer: *std.io.BufferedWriter) anyerror!void {
669 try writer.writeByte('\"');670 try writer.writeByte('\"');
670 try encodeJsonStringChars(string, options, writer);671 try encodeJsonStringChars(string, options, writer);
671 try writer.writeByte('\"');672 try writer.writeByte('\"');
672}673}
673674
674/// Write `chars` to `writer` as JSON encoded string characters.675/// Write `chars` to `writer` as JSON encoded string characters.
675pub fn encodeJsonStringChars(chars: []const u8, options: Options, writer: *std.io.BufferedWriter) !void {676pub fn encodeJsonStringChars(chars: []const u8, options: Options, writer: *std.io.BufferedWriter) anyerror!void {
676 var write_cursor: usize = 0;677 var write_cursor: usize = 0;
677 var i: usize = 0;678 var i: usize = 0;
678 if (options.escape_unicode) {679 if (options.escape_unicode) {
...@@ -721,8 +722,8 @@ test "json write stream" {...@@ -721,8 +722,8 @@ test "json write stream" {
721 try testBasicWriteStream(&w);722 try testBasicWriteStream(&w);
722}723}
723724
724fn testBasicWriteStream(w: *Stringify, out: *std.io.BufferedWriter) !void {725fn testBasicWriteStream(w: *Stringify) anyerror!void {
725 out.reset();726 w.writer.reset();
726727
727 try w.beginObject();728 try w.beginObject();
728729
...@@ -765,7 +766,7 @@ fn testBasicWriteStream(w: *Stringify, out: *std.io.BufferedWriter) !void {...@@ -765,7 +766,7 @@ fn testBasicWriteStream(w: *Stringify, out: *std.io.BufferedWriter) !void {
765 \\ "float": 3.5e0766 \\ "float": 3.5e0
766 \\}767 \\}
767 ;768 ;
768 try std.testing.expectEqualStrings(expected, out.getWritten());769 try std.testing.expectEqualStrings(expected, w.writer.getWritten());
769}770}
770771
771fn getJsonObject(allocator: std.mem.Allocator) !std.json.Value {772fn getJsonObject(allocator: std.mem.Allocator) !std.json.Value {
...@@ -967,59 +968,11 @@ test "stringify struct with custom stringifier" {...@@ -967,59 +968,11 @@ test "stringify struct with custom stringifier" {
967}968}
968969
969fn testStringify(expected: []const u8, v: anytype, options: Options) !void {970fn testStringify(expected: []const u8, v: anytype, options: Options) !void {
970 const ValidationWriter = struct {971 var buffer: [4096]u8 = undefined;
971 const Self = @This();972 var bw: std.io.BufferedWriter = undefined;
972 pub const Writer = std.io.Writer(*Self, Error, Self.write);973 bw.initFixed(&buffer);
973 pub const Error = error{974 try value(v, options, &bw);
974 TooMuchData,975 try std.testing.expectEqualStrings(expected, bw.getWritten());
975 DifferentData,
976 };
977
978 expected_remaining: []const u8,
979
980 fn init(exp: []const u8) Self {
981 return .{ .expected_remaining = exp };
982 }
983
984 pub fn writer(self: *Self) Writer {
985 return .{ .context = self };
986 }
987
988 fn write(self: *Self, bytes: []const u8) Error!usize {
989 if (self.expected_remaining.len < bytes.len) {
990 std.debug.print(
991 \\====== expected this output: =========
992 \\{s}
993 \\======== instead found this: =========
994 \\{s}
995 \\======================================
996 , .{
997 self.expected_remaining,
998 bytes,
999 });
1000 return error.TooMuchData;
1001 }
1002 if (!std.mem.eql(u8, self.expected_remaining[0..bytes.len], bytes)) {
1003 std.debug.print(
1004 \\====== expected this output: =========
1005 \\{s}
1006 \\======== instead found this: =========
1007 \\{s}
1008 \\======================================
1009 , .{
1010 self.expected_remaining[0..bytes.len],
1011 bytes,
1012 });
1013 return error.DifferentData;
1014 }
1015 self.expected_remaining = self.expected_remaining[bytes.len..];
1016 return bytes.len;
1017 }
1018 };
1019
1020 var vos = ValidationWriter.init(expected);
1021 try value(v, options, vos.writer());
1022 if (vos.expected_remaining.len > 0) return error.NotEnoughData;
1023}976}
1024977
1025test "raw streaming" {978test "raw streaming" {
...@@ -1030,12 +983,12 @@ test "raw streaming" {...@@ -1030,12 +983,12 @@ test "raw streaming" {
1030 var w: Stringify = .{ .writer = &out, .options = .{ .whitespace = .indent_2 } };983 var w: Stringify = .{ .writer = &out, .options = .{ .whitespace = .indent_2 } };
1031 try w.beginObject();984 try w.beginObject();
1032 try w.beginObjectFieldRaw();985 try w.beginObjectFieldRaw();
1033 try w.stream.writeAll("\"long");986 try w.writer.writeAll("\"long");
1034 try w.stream.writeAll(" key\"");987 try w.writer.writeAll(" key\"");
1035 w.endObjectFieldRaw();988 w.endObjectFieldRaw();
1036 try w.beginWriteRaw();989 try w.beginWriteRaw();
1037 try w.stream.writeAll("\"long");990 try w.writer.writeAll("\"long");
1038 try w.stream.writeAll(" value\"");991 try w.writer.writeAll(" value\"");
1039 w.endWriteRaw();992 w.endWriteRaw();
1040 try w.endObject();993 try w.endObject();
1041994
lib/std/json/dynamic_test.zig+3-5
...@@ -76,7 +76,6 @@ test "write json then parse it" {...@@ -76,7 +76,6 @@ test "write json then parse it" {
76 var fixed_writer: std.io.BufferedWriter = undefined;76 var fixed_writer: std.io.BufferedWriter = undefined;
77 fixed_writer.initFixed(&out_buffer);77 fixed_writer.initFixed(&out_buffer);
78 var jw: json.Stringify = .{ .writer = &fixed_writer, .options = .{} };78 var jw: json.Stringify = .{ .writer = &fixed_writer, .options = .{} };
79 defer jw.deinit();
8079
81 try jw.beginObject();80 try jw.beginObject();
8281
...@@ -245,7 +244,6 @@ test "Value.jsonStringify" {...@@ -245,7 +244,6 @@ test "Value.jsonStringify" {
245 fixed_writer.initFixed(&buffer);244 fixed_writer.initFixed(&buffer);
246245
247 var jw: json.Stringify = .{ .writer = &fixed_writer, .options = .{ .whitespace = .indent_1 } };246 var jw: json.Stringify = .{ .writer = &fixed_writer, .options = .{ .whitespace = .indent_1 } };
248 defer jw.deinit();
249 try jw.write(array);247 try jw.write(array);
250248
251 const expected =249 const expected =
...@@ -334,7 +332,7 @@ test "polymorphic parsing" {...@@ -334,7 +332,7 @@ test "polymorphic parsing" {
334test "long object value" {332test "long object value" {
335 const value = "01234567890123456789";333 const value = "01234567890123456789";
336 const doc = "{\"key\":\"" ++ value ++ "\"}";334 const doc = "{\"key\":\"" ++ value ++ "\"}";
337 var fbs = std.io.fixedBufferStream(doc);335 var fbs: std.io.FixedBufferStream = .{ .buffer = doc };
338 var reader = smallBufferJsonReader(testing.allocator, fbs.reader());336 var reader = smallBufferJsonReader(testing.allocator, fbs.reader());
339 defer reader.deinit();337 defer reader.deinit();
340 var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{});338 var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{});
...@@ -367,7 +365,7 @@ test "many object keys" {...@@ -367,7 +365,7 @@ test "many object keys" {
367 \\ "k5": "v5"365 \\ "k5": "v5"
368 \\}366 \\}
369 ;367 ;
370 var fbs = std.io.fixedBufferStream(doc);368 var fbs: std.io.FixedBufferStream = .{ .buffer = doc };
371 var reader = smallBufferJsonReader(testing.allocator, fbs.reader());369 var reader = smallBufferJsonReader(testing.allocator, fbs.reader());
372 defer reader.deinit();370 defer reader.deinit();
373 var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{});371 var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{});
...@@ -382,7 +380,7 @@ test "many object keys" {...@@ -382,7 +380,7 @@ test "many object keys" {
382380
383test "negative zero" {381test "negative zero" {
384 const doc = "-0";382 const doc = "-0";
385 var fbs = std.io.fixedBufferStream(doc);383 var fbs: std.io.FixedBufferStream = .{ .buffer = doc };
386 var reader = smallBufferJsonReader(testing.allocator, fbs.reader());384 var reader = smallBufferJsonReader(testing.allocator, fbs.reader());
387 defer reader.deinit();385 defer reader.deinit();
388 var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{});386 var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{});
lib/std/json/hashmap_test.zig+1-1
...@@ -39,7 +39,7 @@ test "parse json hashmap while streaming" {...@@ -39,7 +39,7 @@ test "parse json hashmap while streaming" {
39 \\ "xyz": {"i": 1, "s": "w"}39 \\ "xyz": {"i": 1, "s": "w"}
40 \\}40 \\}
41 ;41 ;
42 var stream = std.io.fixedBufferStream(doc);42 var stream: std.io.FixedBufferStream = .{ .buffer = doc };
43 var json_reader = jsonReader(testing.allocator, stream.reader());43 var json_reader = jsonReader(testing.allocator, stream.reader());
4444
45 var parsed = try parseFromTokenSource(45 var parsed = try parseFromTokenSource(
lib/std/json/scanner_test.zig+9-9
...@@ -142,12 +142,12 @@ test "peek all types" {...@@ -142,12 +142,12 @@ test "peek all types" {
142 defer scanner.deinit();142 defer scanner.deinit();
143 try testAllTypes(&scanner, true);143 try testAllTypes(&scanner, true);
144144
145 var stream = std.io.fixedBufferStream(all_types_test_case);145 var stream: std.io.FixedBufferStream = .{ .buffer = all_types_test_case };
146 var json_reader = jsonReader(std.testing.allocator, stream.reader());146 var json_reader = jsonReader(std.testing.allocator, stream.reader());
147 defer json_reader.deinit();147 defer json_reader.deinit();
148 try testAllTypes(&json_reader, true);148 try testAllTypes(&json_reader, true);
149149
150 var tiny_stream = std.io.fixedBufferStream(all_types_test_case);150 var tiny_stream: std.io.FixedBufferStream = .{ .buffer = all_types_test_case };
151 var tiny_json_reader = JsonReader(1, @TypeOf(tiny_stream.reader())).init(std.testing.allocator, tiny_stream.reader());151 var tiny_json_reader = JsonReader(1, @TypeOf(tiny_stream.reader())).init(std.testing.allocator, tiny_stream.reader());
152 defer tiny_json_reader.deinit();152 defer tiny_json_reader.deinit();
153 try testAllTypes(&tiny_json_reader, false);153 try testAllTypes(&tiny_json_reader, false);
...@@ -182,7 +182,7 @@ test "JsonScanner basic" {...@@ -182,7 +182,7 @@ test "JsonScanner basic" {
182}182}
183183
184test "JsonReader basic" {184test "JsonReader basic" {
185 var stream = std.io.fixedBufferStream(example_document_str);185 var stream: std.io.FixedBufferStream = .{ .buffer = example_document_str };
186186
187 var json_reader = jsonReader(std.testing.allocator, stream.reader());187 var json_reader = jsonReader(std.testing.allocator, stream.reader());
188 defer json_reader.deinit();188 defer json_reader.deinit();
...@@ -243,7 +243,7 @@ const string_test_cases = .{...@@ -243,7 +243,7 @@ const string_test_cases = .{
243243
244test "strings" {244test "strings" {
245 inline for (string_test_cases) |tuple| {245 inline for (string_test_cases) |tuple| {
246 var stream = std.io.fixedBufferStream("\"" ++ tuple[0] ++ "\"");246 var stream: std.io.FixedBufferStream = .{ .buffer = "\"" ++ tuple[0] ++ "\"" };
247 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);247 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
248 defer arena.deinit();248 defer arena.deinit();
249 var json_reader = jsonReader(std.testing.allocator, stream.reader());249 var json_reader = jsonReader(std.testing.allocator, stream.reader());
...@@ -352,8 +352,8 @@ fn expectEqualTokens(expected_token: Token, actual_token: Token) !void {...@@ -352,8 +352,8 @@ fn expectEqualTokens(expected_token: Token, actual_token: Token) !void {
352}352}
353353
354fn testTinyBufferSize(document_str: []const u8) !void {354fn testTinyBufferSize(document_str: []const u8) !void {
355 var tiny_stream = std.io.fixedBufferStream(document_str);355 var tiny_stream: std.io.FixedBufferStream = .{ .buffer = document_str };
356 var normal_stream = std.io.fixedBufferStream(document_str);356 var normal_stream: std.io.FixedBufferStream = .{ .buffer = document_str };
357357
358 var tiny_json_reader = JsonReader(1, @TypeOf(tiny_stream.reader())).init(std.testing.allocator, tiny_stream.reader());358 var tiny_json_reader = JsonReader(1, @TypeOf(tiny_stream.reader())).init(std.testing.allocator, tiny_stream.reader());
359 defer tiny_json_reader.deinit();359 defer tiny_json_reader.deinit();
...@@ -402,7 +402,7 @@ fn testSkipValue(s: []const u8) !void {...@@ -402,7 +402,7 @@ fn testSkipValue(s: []const u8) !void {
402 try scanner.skipValue();402 try scanner.skipValue();
403 try expectEqualTokens(.end_of_document, try scanner.next());403 try expectEqualTokens(.end_of_document, try scanner.next());
404404
405 var stream = std.io.fixedBufferStream(s);405 var stream: std.io.FixedBufferStream = .{ .buffer = s };
406 var json_reader = jsonReader(std.testing.allocator, stream.reader());406 var json_reader = jsonReader(std.testing.allocator, stream.reader());
407 defer json_reader.deinit();407 defer json_reader.deinit();
408 try json_reader.skipValue();408 try json_reader.skipValue();
...@@ -477,12 +477,12 @@ fn testDiagnostics(expected_error: ?anyerror, line: u64, col: u64, byte_offset:...@@ -477,12 +477,12 @@ fn testDiagnostics(expected_error: ?anyerror, line: u64, col: u64, byte_offset:
477 defer scanner.deinit();477 defer scanner.deinit();
478 try testDiagnosticsFromSource(expected_error, line, col, byte_offset, &scanner);478 try testDiagnosticsFromSource(expected_error, line, col, byte_offset, &scanner);
479479
480 var tiny_stream = std.io.fixedBufferStream(s);480 var tiny_stream: std.io.FixedBufferStream = .{ .buffer = s };
481 var tiny_json_reader = JsonReader(1, @TypeOf(tiny_stream.reader())).init(std.testing.allocator, tiny_stream.reader());481 var tiny_json_reader = JsonReader(1, @TypeOf(tiny_stream.reader())).init(std.testing.allocator, tiny_stream.reader());
482 defer tiny_json_reader.deinit();482 defer tiny_json_reader.deinit();
483 try testDiagnosticsFromSource(expected_error, line, col, byte_offset, &tiny_json_reader);483 try testDiagnosticsFromSource(expected_error, line, col, byte_offset, &tiny_json_reader);
484484
485 var medium_stream = std.io.fixedBufferStream(s);485 var medium_stream: std.io.FixedBufferStream = .{ .buffer = s };
486 var medium_json_reader = JsonReader(5, @TypeOf(medium_stream.reader())).init(std.testing.allocator, medium_stream.reader());486 var medium_json_reader = JsonReader(5, @TypeOf(medium_stream.reader())).init(std.testing.allocator, medium_stream.reader());
487 defer medium_json_reader.deinit();487 defer medium_json_reader.deinit();
488 try testDiagnosticsFromSource(expected_error, line, col, byte_offset, &medium_json_reader);488 try testDiagnosticsFromSource(expected_error, line, col, byte_offset, &medium_json_reader);
lib/std/json/static_test.zig+4-4
...@@ -317,7 +317,7 @@ fn testAllParseFunctions(comptime T: type, expected: T, doc: []const u8) !void {...@@ -317,7 +317,7 @@ fn testAllParseFunctions(comptime T: type, expected: T, doc: []const u8) !void {
317 try testing.expectEqualDeep(expected, parsed.value);317 try testing.expectEqualDeep(expected, parsed.value);
318 }318 }
319 {319 {
320 var stream = std.io.fixedBufferStream(doc);320 var stream: std.io.FixedBufferStream = .{ .buffer = doc };
321 var json_reader = jsonReader(std.testing.allocator, stream.reader());321 var json_reader = jsonReader(std.testing.allocator, stream.reader());
322 defer json_reader.deinit();322 defer json_reader.deinit();
323 var parsed = try parseFromTokenSource(T, testing.allocator, &json_reader, .{});323 var parsed = try parseFromTokenSource(T, testing.allocator, &json_reader, .{});
...@@ -336,7 +336,7 @@ fn testAllParseFunctions(comptime T: type, expected: T, doc: []const u8) !void {...@@ -336,7 +336,7 @@ fn testAllParseFunctions(comptime T: type, expected: T, doc: []const u8) !void {
336 try testing.expectEqualDeep(expected, try parseFromTokenSourceLeaky(T, arena.allocator(), &scanner, .{}));336 try testing.expectEqualDeep(expected, try parseFromTokenSourceLeaky(T, arena.allocator(), &scanner, .{}));
337 }337 }
338 {338 {
339 var stream = std.io.fixedBufferStream(doc);339 var stream: std.io.FixedBufferStream = .{ .buffer = doc };
340 var json_reader = jsonReader(std.testing.allocator, stream.reader());340 var json_reader = jsonReader(std.testing.allocator, stream.reader());
341 defer json_reader.deinit();341 defer json_reader.deinit();
342 try testing.expectEqualDeep(expected, try parseFromTokenSourceLeaky(T, arena.allocator(), &json_reader, .{}));342 try testing.expectEqualDeep(expected, try parseFromTokenSourceLeaky(T, arena.allocator(), &json_reader, .{}));
...@@ -771,7 +771,7 @@ test "parseFromTokenSource" {...@@ -771,7 +771,7 @@ test "parseFromTokenSource" {
771 }771 }
772772
773 {773 {
774 var stream = std.io.fixedBufferStream("123");774 var stream: std.io.FixedBufferStream = .{ .buffer = "123" };
775 var json_reader = jsonReader(std.testing.allocator, stream.reader());775 var json_reader = jsonReader(std.testing.allocator, stream.reader());
776 defer json_reader.deinit();776 defer json_reader.deinit();
777 var parsed = try parseFromTokenSource(u32, testing.allocator, &json_reader, .{});777 var parsed = try parseFromTokenSource(u32, testing.allocator, &json_reader, .{});
...@@ -886,7 +886,7 @@ test "json parse allocate when streaming" {...@@ -886,7 +886,7 @@ test "json parse allocate when streaming" {
886 var arena = ArenaAllocator.init(allocator);886 var arena = ArenaAllocator.init(allocator);
887 defer arena.deinit();887 defer arena.deinit();
888888
889 var stream = std.io.fixedBufferStream(str);889 var stream: std.io.FixedBufferStream = .{ .buffer = str };
890 var json_reader = jsonReader(std.testing.allocator, stream.reader());890 var json_reader = jsonReader(std.testing.allocator, stream.reader());
891891
892 const parsed = parseFromTokenSourceLeaky(T, arena.allocator(), &json_reader, .{}) catch |err| {892 const parsed = parseFromTokenSourceLeaky(T, arena.allocator(), &json_reader, .{}) catch |err| {