From 7e3591bedd41bde8bcca892885376e2f4a8163a3 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Wed, 8 Mar 2023 19:25:53 +0100 Subject: [PATCH 1/2] std.json.parseInternal: use switches instead of ifs --- lib/std/json.zig | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/lib/std/json.zig b/lib/std/json.zig index 0cce71b1e6..9d24ebaff4 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1534,30 +1534,25 @@ fn parseInternal( child_options.allow_trailing_data = true; var found = false; inline for (structInfo.fields, 0..) |field, i| { - // TODO: using switches here segfault the compiler (#2727?) - if ((stringToken.escapes == .None and mem.eql(u8, field.name, key_source_slice)) or (stringToken.escapes == .Some and (field.name.len == stringToken.decodedLength() and encodesTo(field.name, key_source_slice)))) { - // if (switch (stringToken.escapes) { - // .None => mem.eql(u8, field.name, key_source_slice), - // .Some => (field.name.len == stringToken.decodedLength() and encodesTo(field.name, key_source_slice)), - // }) { + if (switch (stringToken.escapes) { + .None => mem.eql(u8, field.name, key_source_slice), + .Some => (field.name.len == stringToken.decodedLength() and encodesTo(field.name, key_source_slice)), + }) { if (fields_seen[i]) { - // switch (options.duplicate_field_behavior) { - // .UseFirst => {}, - // .Error => {}, - // .UseLast => {}, - // } - if (options.duplicate_field_behavior == .UseFirst) { - // unconditonally ignore value. for comptime fields, this skips check against default_value - parseFree(field.type, try parse(field.type, tokens, child_options), child_options); - found = true; - break; - } else if (options.duplicate_field_behavior == .Error) { - return error.DuplicateJSONField; - } else if (options.duplicate_field_behavior == .UseLast) { - if (!field.is_comptime) { - parseFree(field.type, @field(r, field.name), child_options); - } - fields_seen[i] = false; + switch (options.duplicate_field_behavior) { + .UseFirst => { + // unconditonally ignore value. for comptime fields, this skips check against default_value + parseFree(field.type, try parse(field.type, tokens, child_options), child_options); + found = true; + break; + }, + .Error => return error.DuplicateJSONField, + .UseLast => { + if (!field.is_comptime) { + parseFree(field.type, @field(r, field.name), child_options); + } + fields_seen[i] = false; + }, } } if (field.is_comptime) { From c0789b4814989f2d91d3d2145d227dcdec3e2770 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Wed, 8 Mar 2023 19:26:03 +0100 Subject: [PATCH 2/2] std.json.stringify: support [*:0]const u8 --- lib/std/json.zig | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/std/json.zig b/lib/std/json.zig index 9d24ebaff4..590040efba 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -2350,10 +2350,13 @@ pub fn stringify( return 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 options.string == .String and std.unicode.utf8ValidateSlice(value)) { - try encodeJsonString(value, options, out_stream); + .Many, .Slice => { + if (ptr_info.size == .Many and ptr_info.sentinel == null) + @compileError("unable to stringify type '" ++ @typeName(T) ++ "' without sentinel"); + const slice = if (ptr_info.size == .Many) mem.span(value) else value; + + if (ptr_info.child == u8 and options.string == .String and std.unicode.utf8ValidateSlice(slice)) { + try encodeJsonString(slice, options, out_stream); return; } @@ -2362,7 +2365,7 @@ pub fn stringify( if (child_options.whitespace) |*whitespace| { whitespace.indent_level += 1; } - for (value, 0..) |x, i| { + for (slice, 0..) |x, i| { if (i != 0) { try out_stream.writeByte(','); } @@ -2371,7 +2374,7 @@ pub fn stringify( } try stringify(x, child_options, out_stream); } - if (value.len != 0) { + if (slice.len != 0) { if (options.whitespace) |whitespace| { try whitespace.outputIndent(out_stream); } @@ -2526,6 +2529,12 @@ test "stringify string" { try teststringify("\"\\/\"", "/", StringifyOptions{ .string = .{ .String = .{ .escape_solidus = true } } }); } +test "stringify many-item sentinel-terminated string" { + try teststringify("\"hello\"", @as([*:0]const u8, "hello"), StringifyOptions{}); + try teststringify("\"with\\nescapes\\r\"", @as([*:0]const u8, "with\nescapes\r"), StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); + try teststringify("\"with unicode\\u0001\"", @as([*:0]const u8, "with unicode\u{1}"), StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); +} + test "stringify tagged unions" { try teststringify("42", union(enum) { Foo: u32, @@ -2712,7 +2721,7 @@ test "encodesTo" { try testing.expectEqual(true, encodesTo("withąunicode😂", "with\\u0105unicode\\ud83d\\ude02")); } -test "issue 14600" { +test "deserializing string with escape sequence into sentinel slice" { const json = "\"\\n\""; var token_stream = std.json.TokenStream.init(json); const options = ParseOptions{ .allocator = std.testing.allocator };