authorgravatar for benjamin.feng@glassdoor.comBenjamin Feng <benjamin.feng@glassdoor.com> 2020-02-29 18:59:16-06:00
committergravatar for benjamin.feng@glassdoor.comBenjamin Feng <benjamin.feng@glassdoor.com> 2020-03-12 10:41:09-05:00
log2429fdd73bc2405034805893f169ae58c1c34c56
tree09df1497ae2da819d83e14f45b90725b00f5b79b
parent0fbccec000efcfe23188027e474530641daf67cd

Convert JSON to fmtstream


1 files changed, 72 insertions(+), 62 deletions(-)

lib/std/json.zig+72-62
......@@ -2252,45 +2252,43 @@ pub const StringifyOptions = struct {
22522252pub fn stringify(
22532253 value: var,
22542254 options: StringifyOptions,
2255 context: var,
2256 comptime Errors: type,
2257 comptime output: fn (@TypeOf(context), []const u8) Errors!void,
2258) Errors!void {
2255 out_stream: var,
2256) !void {
22592257 const T = @TypeOf(value);
22602258 switch (@typeInfo(T)) {
22612259 .Float, .ComptimeFloat => {
2262 return std.fmt.formatFloatScientific(value, std.fmt.FormatOptions{}, context, Errors, output);
2260 return std.fmtstream.formatFloatScientific(value, std.fmtstream.FormatOptions{}, out_stream);
22632261 },
22642262 .Int, .ComptimeInt => {
2265 return std.fmt.formatIntValue(value, "", std.fmt.FormatOptions{}, context, Errors, output);
2263 return std.fmtstream.formatIntValue(value, "", std.fmtstream.FormatOptions{}, out_stream);
22662264 },
22672265 .Bool => {
2268 return output(context, if (value) "true" else "false");
2266 return out_stream.writeAll(if (value) "true" else "false");
22692267 },
22702268 .Optional => {
22712269 if (value) |payload| {
2272 return try stringify(payload, options, context, Errors, output);
2270 return try stringify(payload, options, out_stream);
22732271 } else {
2274 return output(context, "null");
2272 return out_stream.writeAll("null");
22752273 }
22762274 },
22772275 .Enum => {
22782276 if (comptime std.meta.trait.hasFn("jsonStringify")(T)) {
2279 return value.jsonStringify(options, context, Errors, output);
2277 return value.jsonStringify(options, out_stream);
22802278 }
22812279
22822280 @compileError("Unable to stringify enum '" ++ @typeName(T) ++ "'");
22832281 },
22842282 .Union => {
22852283 if (comptime std.meta.trait.hasFn("jsonStringify")(T)) {
2286 return value.jsonStringify(options, context, Errors, output);
2284 return value.jsonStringify(options, out_stream);
22872285 }
22882286
22892287 const info = @typeInfo(T).Union;
22902288 if (info.tag_type) |UnionTagType| {
22912289 inline for (info.fields) |u_field| {
22922290 if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) {
2293 return try stringify(@field(value, u_field.name), options, context, Errors, output);
2291 return try stringify(@field(value, u_field.name), options, out_stream);
22942292 }
22952293 }
22962294 } else {
......@@ -2299,10 +2297,10 @@ pub fn stringify(
22992297 },
23002298 .Struct => |S| {
23012299 if (comptime std.meta.trait.hasFn("jsonStringify")(T)) {
2302 return value.jsonStringify(options, context, Errors, output);
2300 return value.jsonStringify(options, out_stream);
23032301 }
23042302
2305 try output(context, "{");
2303 try out_stream.writeAll("{");
23062304 comptime var field_output = false;
23072305 inline for (S.fields) |Field, field_i| {
23082306 // don't include void fields
......@@ -2311,39 +2309,39 @@ pub fn stringify(
23112309 if (!field_output) {
23122310 field_output = true;
23132311 } else {
2314 try output(context, ",");
2312 try out_stream.writeAll(",");
23152313 }
23162314
2317 try stringify(Field.name, options, context, Errors, output);
2318 try output(context, ":");
2319 try stringify(@field(value, Field.name), options, context, Errors, output);
2315 try stringify(Field.name, options, out_stream);
2316 try out_stream.writeAll(":");
2317 try stringify(@field(value, Field.name), options, out_stream);
23202318 }
2321 try output(context, "}");
2319 try out_stream.writeAll("}");
23222320 return;
23232321 },
23242322 .Pointer => |ptr_info| switch (ptr_info.size) {
23252323 .One => {
23262324 // TODO: avoid loops?
2327 return try stringify(value.*, options, context, Errors, output);
2325 return try stringify(value.*, options, out_stream);
23282326 },
23292327 // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972)
23302328 .Slice => {
23312329 if (ptr_info.child == u8 and std.unicode.utf8ValidateSlice(value)) {
2332 try output(context, "\"");
2330 try out_stream.writeAll("\"");
23332331 var i: usize = 0;
23342332 while (i < value.len) : (i += 1) {
23352333 switch (value[i]) {
23362334 // normal ascii characters
2337 0x20...0x21, 0x23...0x2E, 0x30...0x5B, 0x5D...0x7F => try output(context, value[i .. i + 1]),
2335 0x20...0x21, 0x23...0x2E, 0x30...0x5B, 0x5D...0x7F => try out_stream.writeAll(value[i .. i + 1]),
23382336 // control characters with short escapes
2339 '\\' => try output(context, "\\\\"),
2340 '\"' => try output(context, "\\\""),
2341 '/' => try output(context, "\\/"),
2342 0x8 => try output(context, "\\b"),
2343 0xC => try output(context, "\\f"),
2344 '\n' => try output(context, "\\n"),
2345 '\r' => try output(context, "\\r"),
2346 '\t' => try output(context, "\\t"),
2337 '\\' => try out_stream.writeAll("\\\\"),
2338 '\"' => try out_stream.writeAll("\\\""),
2339 '/' => try out_stream.writeAll("\\/"),
2340 0x8 => try out_stream.writeAll("\\b"),
2341 0xC => try out_stream.writeAll("\\f"),
2342 '\n' => try out_stream.writeAll("\\n"),
2343 '\r' => try out_stream.writeAll("\\r"),
2344 '\t' => try out_stream.writeAll("\\t"),
23472345 else => {
23482346 const ulen = std.unicode.utf8ByteSequenceLength(value[i]) catch unreachable;
23492347 const codepoint = std.unicode.utf8Decode(value[i .. i + ulen]) catch unreachable;
......@@ -2351,40 +2349,40 @@ pub fn stringify(
23512349 // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF),
23522350 // then it may be represented as a six-character sequence: a reverse solidus, followed
23532351 // by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point.
2354 try output(context, "\\u");
2355 try std.fmt.formatIntValue(codepoint, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, context, Errors, output);
2352 try out_stream.writeAll("\\u");
2353 try std.fmtstream.formatIntValue(codepoint, "x", std.fmtstream.FormatOptions{ .width = 4, .fill = '0' }, out_stream);
23562354 } else {
23572355 // To escape an extended character that is not in the Basic Multilingual Plane,
23582356 // the character is represented as a 12-character sequence, encoding the UTF-16 surrogate pair.
23592357 const high = @intCast(u16, (codepoint - 0x10000) >> 10) + 0xD800;
23602358 const low = @intCast(u16, codepoint & 0x3FF) + 0xDC00;
2361 try output(context, "\\u");
2362 try std.fmt.formatIntValue(high, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, context, Errors, output);
2363 try output(context, "\\u");
2364 try std.fmt.formatIntValue(low, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, context, Errors, output);
2359 try out_stream.writeAll("\\u");
2360 try std.fmtstream.formatIntValue(high, "x", std.fmtstream.FormatOptions{ .width = 4, .fill = '0' }, out_stream);
2361 try out_stream.writeAll("\\u");
2362 try std.fmtstream.formatIntValue(low, "x", std.fmtstream.FormatOptions{ .width = 4, .fill = '0' }, out_stream);
23652363 }
23662364 i += ulen - 1;
23672365 },
23682366 }
23692367 }
2370 try output(context, "\"");
2368 try out_stream.writeAll("\"");
23712369 return;
23722370 }
23732371
2374 try output(context, "[");
2372 try out_stream.writeAll("[");
23752373 for (value) |x, i| {
23762374 if (i != 0) {
2377 try output(context, ",");
2375 try out_stream.writeAll(",");
23782376 }
2379 try stringify(x, options, context, Errors, output);
2377 try stringify(x, options, out_stream);
23802378 }
2381 try output(context, "]");
2379 try out_stream.writeAll("]");
23822380 return;
23832381 },
23842382 else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
23852383 },
23862384 .Array => |info| {
2387 return try stringify(value[0..], options, context, Errors, output);
2385 return try stringify(value[0..], options, out_stream);
23882386 },
23892387 else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
23902388 }
......@@ -2392,10 +2390,26 @@ pub fn stringify(
23922390}
23932391
23942392fn teststringify(expected: []const u8, value: var) !void {
2395 const TestStringifyContext = struct {
2393 const ValidationOutStream = struct {
2394 const Self = @This();
2395 pub const OutStream = std.io.OutStream(*Self, Error, write);
2396 pub const Error = error{
2397 TooMuchData,
2398 DifferentData,
2399 };
2400
23962401 expected_remaining: []const u8,
2397 fn testStringifyWrite(context: *@This(), bytes: []const u8) !void {
2398 if (context.expected_remaining.len < bytes.len) {
2402
2403 fn init(exp: []const u8) Self {
2404 return .{ .expected_remaining = exp };
2405 }
2406
2407 pub fn outStream(self: *Self) OutStream {
2408 return .{ .context = self };
2409 }
2410
2411 fn write(self: *Self, bytes: []const u8) Error!usize {
2412 if (self.expected_remaining.len < bytes.len) {
23992413 std.debug.warn(
24002414 \\====== expected this output: =========
24012415 \\{}
......@@ -2403,12 +2417,12 @@ fn teststringify(expected: []const u8, value: var) !void {
24032417 \\{}
24042418 \\======================================
24052419 , .{
2406 context.expected_remaining,
2420 self.expected_remaining,
24072421 bytes,
24082422 });
24092423 return error.TooMuchData;
24102424 }
2411 if (!mem.eql(u8, context.expected_remaining[0..bytes.len], bytes)) {
2425 if (!mem.eql(u8, self.expected_remaining[0..bytes.len], bytes)) {
24122426 std.debug.warn(
24132427 \\====== expected this output: =========
24142428 \\{}
......@@ -2416,21 +2430,19 @@ fn teststringify(expected: []const u8, value: var) !void {
24162430 \\{}
24172431 \\======================================
24182432 , .{
2419 context.expected_remaining[0..bytes.len],
2433 self.expected_remaining[0..bytes.len],
24202434 bytes,
24212435 });
24222436 return error.DifferentData;
24232437 }
2424 context.expected_remaining = context.expected_remaining[bytes.len..];
2438 self.expected_remaining = self.expected_remaining[bytes.len..];
2439 return bytes.len;
24252440 }
24262441 };
2427 var buf: [100]u8 = undefined;
2428 var context = TestStringifyContext{ .expected_remaining = expected };
2429 try stringify(value, StringifyOptions{}, &context, error{
2430 TooMuchData,
2431 DifferentData,
2432 }, TestStringifyContext.testStringifyWrite);
2433 if (context.expected_remaining.len > 0) return error.NotEnoughData;
2442
2443 var vos = ValidationOutStream.init(expected);
2444 try stringify(value, StringifyOptions{}, vos.outStream());
2445 if (vos.expected_remaining.len > 0) return error.NotEnoughData;
24342446}
24352447
24362448test "stringify basic types" {
......@@ -2498,13 +2510,11 @@ test "stringify struct with custom stringifier" {
24982510 pub fn jsonStringify(
24992511 value: Self,
25002512 options: StringifyOptions,
2501 context: var,
2502 comptime Errors: type,
2503 comptime output: fn (@TypeOf(context), []const u8) Errors!void,
2513 out_stream: var,
25042514 ) !void {
2505 try output(context, "[\"something special\",");
2506 try stringify(42, options, context, Errors, output);
2507 try output(context, "]");
2515 try out_stream.writeAll("[\"something special\",");
2516 try stringify(42, options, out_stream);
2517 try out_stream.writeAll("]");
25082518 }
25092519 }{ .foo = 42 });
25102520}