| ... | @@ -1534,30 +1534,25 @@ fn parseInternal( | ... | @@ -1534,30 +1534,25 @@ fn parseInternal( |
| 1534 | child_options.allow_trailing_data = true; | 1534 | child_options.allow_trailing_data = true; |
| 1535 | var found = false; | 1535 | var found = false; |
| 1536 | inline for (structInfo.fields, 0..) |field, i| { | 1536 | inline for (structInfo.fields, 0..) |field, i| { |
| 1537 | // TODO: using switches here segfault the compiler (#2727?) | 1537 | if (switch (stringToken.escapes) { |
| 1538 | 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)))) { | 1538 | .None => mem.eql(u8, field.name, key_source_slice), |
| 1539 | // if (switch (stringToken.escapes) { | 1539 | .Some => (field.name.len == stringToken.decodedLength() and encodesTo(field.name, key_source_slice)), |
| 1540 | // .None => mem.eql(u8, field.name, key_source_slice), | 1540 | }) { |
| 1541 | // .Some => (field.name.len == stringToken.decodedLength() and encodesTo(field.name, key_source_slice)), | | |
| 1542 | // }) { | | |
| 1543 | if (fields_seen[i]) { | 1541 | if (fields_seen[i]) { |
| 1544 | // switch (options.duplicate_field_behavior) { | 1542 | switch (options.duplicate_field_behavior) { |
| 1545 | // .UseFirst => {}, | 1543 | .UseFirst => { |
| 1546 | // .Error => {}, | 1544 | // unconditonally ignore value. for comptime fields, this skips check against default_value |
| 1547 | // .UseLast => {}, | 1545 | parseFree(field.type, try parse(field.type, tokens, child_options), child_options); |
| 1548 | // } | 1546 | found = true; |
| 1549 | if (options.duplicate_field_behavior == .UseFirst) { | 1547 | break; |
| 1550 | // unconditonally ignore value. for comptime fields, this skips check against default_value | 1548 | }, |
| 1551 | parseFree(field.type, try parse(field.type, tokens, child_options), child_options); | 1549 | .Error => return error.DuplicateJSONField, |
| 1552 | found = true; | 1550 | .UseLast => { |
| 1553 | break; | 1551 | if (!field.is_comptime) { |
| 1554 | } else if (options.duplicate_field_behavior == .Error) { | 1552 | parseFree(field.type, @field(r, field.name), child_options); |
| 1555 | return error.DuplicateJSONField; | 1553 | } |
| 1556 | } else if (options.duplicate_field_behavior == .UseLast) { | 1554 | fields_seen[i] = false; |
| 1557 | if (!field.is_comptime) { | 1555 | }, |
| 1558 | parseFree(field.type, @field(r, field.name), child_options); | | |
| 1559 | } | | |
| 1560 | fields_seen[i] = false; | | |
| 1561 | } | 1556 | } |
| 1562 | } | 1557 | } |
| 1563 | if (field.is_comptime) { | 1558 | if (field.is_comptime) { |
| ... | @@ -2355,10 +2350,13 @@ pub fn stringify( | ... | @@ -2355,10 +2350,13 @@ pub fn stringify( |
| 2355 | return stringify(value.*, options, out_stream); | 2350 | return stringify(value.*, options, out_stream); |
| 2356 | }, | 2351 | }, |
| 2357 | }, | 2352 | }, |
| 2358 | // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972) | 2353 | .Many, .Slice => { |
| 2359 | .Slice => { | 2354 | if (ptr_info.size == .Many and ptr_info.sentinel == null) |
| 2360 | if (ptr_info.child == u8 and options.string == .String and std.unicode.utf8ValidateSlice(value)) { | 2355 | @compileError("unable to stringify type '" ++ @typeName(T) ++ "' without sentinel"); |
| 2361 | try encodeJsonString(value, options, out_stream); | 2356 | const slice = if (ptr_info.size == .Many) mem.span(value) else value; |
| | 2357 | |
| | 2358 | if (ptr_info.child == u8 and options.string == .String and std.unicode.utf8ValidateSlice(slice)) { |
| | 2359 | try encodeJsonString(slice, options, out_stream); |
| 2362 | return; | 2360 | return; |
| 2363 | } | 2361 | } |
| 2364 | | 2362 | |
| ... | @@ -2367,7 +2365,7 @@ pub fn stringify( | ... | @@ -2367,7 +2365,7 @@ pub fn stringify( |
| 2367 | if (child_options.whitespace) |*whitespace| { | 2365 | if (child_options.whitespace) |*whitespace| { |
| 2368 | whitespace.indent_level += 1; | 2366 | whitespace.indent_level += 1; |
| 2369 | } | 2367 | } |
| 2370 | for (value, 0..) |x, i| { | 2368 | for (slice, 0..) |x, i| { |
| 2371 | if (i != 0) { | 2369 | if (i != 0) { |
| 2372 | try out_stream.writeByte(','); | 2370 | try out_stream.writeByte(','); |
| 2373 | } | 2371 | } |
| ... | @@ -2376,7 +2374,7 @@ pub fn stringify( | ... | @@ -2376,7 +2374,7 @@ pub fn stringify( |
| 2376 | } | 2374 | } |
| 2377 | try stringify(x, child_options, out_stream); | 2375 | try stringify(x, child_options, out_stream); |
| 2378 | } | 2376 | } |
| 2379 | if (value.len != 0) { | 2377 | if (slice.len != 0) { |
| 2380 | if (options.whitespace) |whitespace| { | 2378 | if (options.whitespace) |whitespace| { |
| 2381 | try whitespace.outputIndent(out_stream); | 2379 | try whitespace.outputIndent(out_stream); |
| 2382 | } | 2380 | } |
| ... | @@ -2531,6 +2529,12 @@ test "stringify string" { | ... | @@ -2531,6 +2529,12 @@ test "stringify string" { |
| 2531 | try teststringify("\"\\/\"", "/", StringifyOptions{ .string = .{ .String = .{ .escape_solidus = true } } }); | 2529 | try teststringify("\"\\/\"", "/", StringifyOptions{ .string = .{ .String = .{ .escape_solidus = true } } }); |
| 2532 | } | 2530 | } |
| 2533 | | 2531 | |
| | 2532 | test "stringify many-item sentinel-terminated string" { |
| | 2533 | try teststringify("\"hello\"", @as([*:0]const u8, "hello"), StringifyOptions{}); |
| | 2534 | try teststringify("\"with\\nescapes\\r\"", @as([*:0]const u8, "with\nescapes\r"), StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); |
| | 2535 | try teststringify("\"with unicode\\u0001\"", @as([*:0]const u8, "with unicode\u{1}"), StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); |
| | 2536 | } |
| | 2537 | |
| 2534 | test "stringify tagged unions" { | 2538 | test "stringify tagged unions" { |
| 2535 | try teststringify("42", union(enum) { | 2539 | try teststringify("42", union(enum) { |
| 2536 | Foo: u32, | 2540 | Foo: u32, |
| ... | @@ -2717,7 +2721,7 @@ test "encodesTo" { | ... | @@ -2717,7 +2721,7 @@ test "encodesTo" { |
| 2717 | try testing.expectEqual(true, encodesTo("withąunicode😂", "with\\u0105unicode\\ud83d\\ude02")); | 2721 | try testing.expectEqual(true, encodesTo("withąunicode😂", "with\\u0105unicode\\ud83d\\ude02")); |
| 2718 | } | 2722 | } |
| 2719 | | 2723 | |
| 2720 | test "issue 14600" { | 2724 | test "deserializing string with escape sequence into sentinel slice" { |
| 2721 | const json = "\"\\n\""; | 2725 | const json = "\"\\n\""; |
| 2722 | var token_stream = std.json.TokenStream.init(json); | 2726 | var token_stream = std.json.TokenStream.init(json); |
| 2723 | const options = ParseOptions{ .allocator = std.testing.allocator }; | 2727 | const options = ParseOptions{ .allocator = std.testing.allocator }; |