aboutsummaryrefslogtreecommitdiff
path: root/lib/std/json.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-03-13 15:17:53 -0400
committerAndrew Kelley <andrew@ziglang.org>2020-03-13 15:17:53 -0400
commit656ba530d80e67bc7bb9c40e5c2db26a40743a15 (patch)
tree767f4d57000922cf122ae965dc825f87c62ec64e /lib/std/json.zig
parent96c07674fc2293fa040212ab797c05436dc515b1 (diff)
parent3eff77bfb52accbc16eb831753ff4917fc2b4873 (diff)
downloadzig-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.zig143
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 });
}