diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-03-13 15:17:53 -0400 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-03-13 15:17:53 -0400 |
| commit | 656ba530d80e67bc7bb9c40e5c2db26a40743a15 (patch) | |
| tree | 767f4d57000922cf122ae965dc825f87c62ec64e /lib/std/json.zig | |
| parent | 96c07674fc2293fa040212ab797c05436dc515b1 (diff) | |
| parent | 3eff77bfb52accbc16eb831753ff4917fc2b4873 (diff) | |
| download | zig-656ba530d80e67bc7bb9c40e5c2db26a40743a15.tar.gz zig-656ba530d80e67bc7bb9c40e5c2db26a40743a15.zip | |
Merge remote-tracking branch 'origin/master' into llvm10
Diffstat (limited to 'lib/std/json.zig')
| -rw-r--r-- | lib/std/json.zig | 143 |
1 files changed, 77 insertions, 66 deletions
diff --git a/lib/std/json.zig b/lib/std/json.zig index bb59b4e0f3..f5a72d86da 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -10,6 +10,7 @@ const mem = std.mem; const maxInt = std.math.maxInt; pub const WriteStream = @import("json/write_stream.zig").WriteStream; +pub const writeStream = @import("json/write_stream.zig").writeStream; const StringEscapes = union(enum) { None, @@ -2107,9 +2108,9 @@ test "import more json tests" { test "write json then parse it" { var out_buffer: [1000]u8 = undefined; - var slice_out_stream = std.io.SliceOutStream.init(&out_buffer); - const out_stream = &slice_out_stream.stream; - var jw = WriteStream(@TypeOf(out_stream).Child, 4).init(out_stream); + var fixed_buffer_stream = std.io.fixedBufferStream(&out_buffer); + const out_stream = fixed_buffer_stream.outStream(); + var jw = writeStream(out_stream, 4); try jw.beginObject(); @@ -2140,7 +2141,7 @@ test "write json then parse it" { var parser = Parser.init(testing.allocator, false); defer parser.deinit(); - var tree = try parser.parse(slice_out_stream.getWritten()); + var tree = try parser.parse(fixed_buffer_stream.getWritten()); defer tree.deinit(); testing.expect(tree.root.Object.get("f").?.value.Bool == false); @@ -2251,45 +2252,43 @@ pub const StringifyOptions = struct { pub fn stringify( value: var, options: StringifyOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, -) Errors!void { + out_stream: var, +) !void { const T = @TypeOf(value); switch (@typeInfo(T)) { .Float, .ComptimeFloat => { - return std.fmt.formatFloatScientific(value, std.fmt.FormatOptions{}, context, Errors, output); + return std.fmt.formatFloatScientific(value, std.fmt.FormatOptions{}, out_stream); }, .Int, .ComptimeInt => { - return std.fmt.formatIntValue(value, "", std.fmt.FormatOptions{}, context, Errors, output); + return std.fmt.formatIntValue(value, "", std.fmt.FormatOptions{}, out_stream); }, .Bool => { - return output(context, if (value) "true" else "false"); + return out_stream.writeAll(if (value) "true" else "false"); }, .Optional => { if (value) |payload| { - return try stringify(payload, options, context, Errors, output); + return try stringify(payload, options, out_stream); } else { - return output(context, "null"); + return out_stream.writeAll("null"); } }, .Enum => { if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { - return value.jsonStringify(options, context, Errors, output); + return value.jsonStringify(options, out_stream); } @compileError("Unable to stringify enum '" ++ @typeName(T) ++ "'"); }, .Union => { if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { - return value.jsonStringify(options, context, Errors, output); + return value.jsonStringify(options, out_stream); } const info = @typeInfo(T).Union; if (info.tag_type) |UnionTagType| { inline for (info.fields) |u_field| { if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) { - return try stringify(@field(value, u_field.name), options, context, Errors, output); + return try stringify(@field(value, u_field.name), options, out_stream); } } } else { @@ -2298,10 +2297,10 @@ pub fn stringify( }, .Struct => |S| { if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { - return value.jsonStringify(options, context, Errors, output); + return value.jsonStringify(options, out_stream); } - try output(context, "{"); + try out_stream.writeAll("{"); comptime var field_output = false; inline for (S.fields) |Field, field_i| { // don't include void fields @@ -2310,39 +2309,39 @@ pub fn stringify( if (!field_output) { field_output = true; } else { - try output(context, ","); + try out_stream.writeAll(","); } - try stringify(Field.name, options, context, Errors, output); - try output(context, ":"); - try stringify(@field(value, Field.name), options, context, Errors, output); + try stringify(Field.name, options, out_stream); + try out_stream.writeAll(":"); + try stringify(@field(value, Field.name), options, out_stream); } - try output(context, "}"); + try out_stream.writeAll("}"); return; }, .Pointer => |ptr_info| switch (ptr_info.size) { .One => { // TODO: avoid loops? - return try stringify(value.*, options, context, Errors, output); + return try stringify(value.*, options, out_stream); }, // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972) .Slice => { if (ptr_info.child == u8 and std.unicode.utf8ValidateSlice(value)) { - try output(context, "\""); + try out_stream.writeAll("\""); var i: usize = 0; while (i < value.len) : (i += 1) { switch (value[i]) { // normal ascii characters - 0x20...0x21, 0x23...0x2E, 0x30...0x5B, 0x5D...0x7F => try output(context, value[i .. i + 1]), + 0x20...0x21, 0x23...0x2E, 0x30...0x5B, 0x5D...0x7F => try out_stream.writeAll(value[i .. i + 1]), // control characters with short escapes - '\\' => try output(context, "\\\\"), - '\"' => try output(context, "\\\""), - '/' => try output(context, "\\/"), - 0x8 => try output(context, "\\b"), - 0xC => try output(context, "\\f"), - '\n' => try output(context, "\\n"), - '\r' => try output(context, "\\r"), - '\t' => try output(context, "\\t"), + '\\' => try out_stream.writeAll("\\\\"), + '\"' => try out_stream.writeAll("\\\""), + '/' => try out_stream.writeAll("\\/"), + 0x8 => try out_stream.writeAll("\\b"), + 0xC => try out_stream.writeAll("\\f"), + '\n' => try out_stream.writeAll("\\n"), + '\r' => try out_stream.writeAll("\\r"), + '\t' => try out_stream.writeAll("\\t"), else => { const ulen = std.unicode.utf8ByteSequenceLength(value[i]) catch unreachable; const codepoint = std.unicode.utf8Decode(value[i .. i + ulen]) catch unreachable; @@ -2350,40 +2349,40 @@ pub fn stringify( // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF), // then it may be represented as a six-character sequence: a reverse solidus, followed // by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point. - try output(context, "\\u"); - try std.fmt.formatIntValue(codepoint, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, context, Errors, output); + try out_stream.writeAll("\\u"); + try std.fmt.formatIntValue(codepoint, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); } else { // To escape an extended character that is not in the Basic Multilingual Plane, // the character is represented as a 12-character sequence, encoding the UTF-16 surrogate pair. const high = @intCast(u16, (codepoint - 0x10000) >> 10) + 0xD800; const low = @intCast(u16, codepoint & 0x3FF) + 0xDC00; - try output(context, "\\u"); - try std.fmt.formatIntValue(high, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, context, Errors, output); - try output(context, "\\u"); - try std.fmt.formatIntValue(low, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, context, Errors, output); + try out_stream.writeAll("\\u"); + try std.fmt.formatIntValue(high, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); + try out_stream.writeAll("\\u"); + try std.fmt.formatIntValue(low, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); } i += ulen - 1; }, } } - try output(context, "\""); + try out_stream.writeAll("\""); return; } - try output(context, "["); + try out_stream.writeAll("["); for (value) |x, i| { if (i != 0) { - try output(context, ","); + try out_stream.writeAll(","); } - try stringify(x, options, context, Errors, output); + try stringify(x, options, out_stream); } - try output(context, "]"); + try out_stream.writeAll("]"); return; }, else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"), }, .Array => |info| { - return try stringify(value[0..], options, context, Errors, output); + return try stringify(value[0..], options, out_stream); }, else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"), } @@ -2391,10 +2390,26 @@ pub fn stringify( } fn teststringify(expected: []const u8, value: var) !void { - const TestStringifyContext = struct { + const ValidationOutStream = struct { + const Self = @This(); + pub const OutStream = std.io.OutStream(*Self, Error, write); + pub const Error = error{ + TooMuchData, + DifferentData, + }; + expected_remaining: []const u8, - fn testStringifyWrite(context: *@This(), bytes: []const u8) !void { - if (context.expected_remaining.len < bytes.len) { + + fn init(exp: []const u8) Self { + return .{ .expected_remaining = exp }; + } + + pub fn outStream(self: *Self) OutStream { + return .{ .context = self }; + } + + fn write(self: *Self, bytes: []const u8) Error!usize { + if (self.expected_remaining.len < bytes.len) { std.debug.warn( \\====== expected this output: ========= \\{} @@ -2402,12 +2417,12 @@ fn teststringify(expected: []const u8, value: var) !void { \\{} \\====================================== , .{ - context.expected_remaining, + self.expected_remaining, bytes, }); return error.TooMuchData; } - if (!mem.eql(u8, context.expected_remaining[0..bytes.len], bytes)) { + if (!mem.eql(u8, self.expected_remaining[0..bytes.len], bytes)) { std.debug.warn( \\====== expected this output: ========= \\{} @@ -2415,21 +2430,19 @@ fn teststringify(expected: []const u8, value: var) !void { \\{} \\====================================== , .{ - context.expected_remaining[0..bytes.len], + self.expected_remaining[0..bytes.len], bytes, }); return error.DifferentData; } - context.expected_remaining = context.expected_remaining[bytes.len..]; + self.expected_remaining = self.expected_remaining[bytes.len..]; + return bytes.len; } }; - var buf: [100]u8 = undefined; - var context = TestStringifyContext{ .expected_remaining = expected }; - try stringify(value, StringifyOptions{}, &context, error{ - TooMuchData, - DifferentData, - }, TestStringifyContext.testStringifyWrite); - if (context.expected_remaining.len > 0) return error.NotEnoughData; + + var vos = ValidationOutStream.init(expected); + try stringify(value, StringifyOptions{}, vos.outStream()); + if (vos.expected_remaining.len > 0) return error.NotEnoughData; } test "stringify basic types" { @@ -2497,13 +2510,11 @@ test "stringify struct with custom stringifier" { pub fn jsonStringify( value: Self, options: StringifyOptions, - context: var, - comptime Errors: type, - comptime output: fn (@TypeOf(context), []const u8) Errors!void, + out_stream: var, ) !void { - try output(context, "[\"something special\","); - try stringify(42, options, context, Errors, output); - try output(context, "]"); + try out_stream.writeAll("[\"something special\","); + try stringify(42, options, out_stream); + try out_stream.writeAll("]"); } }{ .foo = 42 }); } |
