| ... | @@ -1414,11 +1414,9 @@ pub const Parser = struct { | ... | @@ -1414,11 +1414,9 @@ pub const Parser = struct { |
| 1414 | } | 1414 | } |
| 1415 | | 1415 | |
| 1416 | fn parseString(p: *Parser, allocator: *Allocator, s: std.meta.TagPayloadType(Token, Token.String), input: []const u8, i: usize) !Value { | 1416 | fn parseString(p: *Parser, allocator: *Allocator, s: std.meta.TagPayloadType(Token, Token.String), input: []const u8, i: usize) !Value { |
| 1417 | // TODO: We don't strictly have to copy values which do not contain any escape | | |
| 1418 | // characters if flagged with the option. | | |
| 1419 | const slice = s.slice(input, i); | 1417 | const slice = s.slice(input, i); |
| 1420 | switch (s.escapes) { | 1418 | switch (s.escapes) { |
| 1421 | .None => return Value{ .String = try mem.dupe(allocator, u8, slice) }, | 1419 | .None => return Value{ .String = if (p.copy_strings) try mem.dupe(allocator, u8, slice) else slice }, |
| 1422 | .Some => |some_escapes| { | 1420 | .Some => |some_escapes| { |
| 1423 | const output = try allocator.alloc(u8, s.decodedLength()); | 1421 | const output = try allocator.alloc(u8, s.decodedLength()); |
| 1424 | errdefer allocator.free(output); | 1422 | errdefer allocator.free(output); |
| ... | @@ -1497,7 +1495,10 @@ fn unescapeString(output: []u8, input: []const u8) !void { | ... | @@ -1497,7 +1495,10 @@ fn unescapeString(output: []u8, input: []const u8) !void { |
| 1497 | } | 1495 | } |
| 1498 | | 1496 | |
| 1499 | test "json.parser.dynamic" { | 1497 | test "json.parser.dynamic" { |
| 1500 | var p = Parser.init(debug.global_allocator, false); | 1498 | var memory: [1024 * 16]u8 = undefined; |
| | 1499 | var buf_alloc = std.heap.FixedBufferAllocator.init(&memory); |
| | 1500 | |
| | 1501 | var p = Parser.init(&buf_alloc.allocator, false); |
| 1501 | defer p.deinit(); | 1502 | defer p.deinit(); |
| 1502 | | 1503 | |
| 1503 | const s = | 1504 | const s = |
| ... | @@ -1600,17 +1601,21 @@ test "write json then parse it" { | ... | @@ -1600,17 +1601,21 @@ test "write json then parse it" { |
| 1600 | testing.expect(mem.eql(u8, tree.root.Object.get("str").?.value.String, "hello")); | 1601 | testing.expect(mem.eql(u8, tree.root.Object.get("str").?.value.String, "hello")); |
| 1601 | } | 1602 | } |
| 1602 | | 1603 | |
| 1603 | fn test_parse(json_str: []const u8) !Value { | 1604 | fn test_parse(memory: []u8, json_str: []const u8) !Value { |
| 1604 | var p = Parser.init(debug.global_allocator, false); | 1605 | // buf_alloc goes out of scope, but we don't use it after parsing |
| | 1606 | var buf_alloc = std.heap.FixedBufferAllocator.init(memory); |
| | 1607 | var p = Parser.init(&buf_alloc.allocator, false); |
| 1605 | return (try p.parse(json_str)).root; | 1608 | return (try p.parse(json_str)).root; |
| 1606 | } | 1609 | } |
| 1607 | | 1610 | |
| 1608 | test "parsing empty string gives appropriate error" { | 1611 | test "parsing empty string gives appropriate error" { |
| 1609 | testing.expectError(error.UnexpectedEndOfJson, test_parse("")); | 1612 | var memory: [1024 * 4]u8 = undefined; |
| | 1613 | testing.expectError(error.UnexpectedEndOfJson, test_parse(&memory, "")); |
| 1610 | } | 1614 | } |
| 1611 | | 1615 | |
| 1612 | test "integer after float has proper type" { | 1616 | test "integer after float has proper type" { |
| 1613 | const json = try test_parse( | 1617 | var memory: [1024 * 8]u8 = undefined; |
| | 1618 | const json = try test_parse(&memory, |
| 1614 | \\{ | 1619 | \\{ |
| 1615 | \\ "float": 3.14, | 1620 | \\ "float": 3.14, |
| 1616 | \\ "ints": [1, 2, 3] | 1621 | \\ "ints": [1, 2, 3] |
| ... | @@ -1620,6 +1625,7 @@ test "integer after float has proper type" { | ... | @@ -1620,6 +1625,7 @@ test "integer after float has proper type" { |
| 1620 | } | 1625 | } |
| 1621 | | 1626 | |
| 1622 | test "escaped characters" { | 1627 | test "escaped characters" { |
| | 1628 | var memory: [1024 * 16]u8 = undefined; |
| 1623 | const input = | 1629 | const input = |
| 1624 | \\{ | 1630 | \\{ |
| 1625 | \\ "backslash": "\\", | 1631 | \\ "backslash": "\\", |
| ... | @@ -1635,10 +1641,7 @@ test "escaped characters" { | ... | @@ -1635,10 +1641,7 @@ test "escaped characters" { |
| 1635 | \\} | 1641 | \\} |
| 1636 | ; | 1642 | ; |
| 1637 | | 1643 | |
| 1638 | var p = Parser.init(debug.global_allocator, false); | 1644 | const obj = (try test_parse(&memory, input)).Object; |
| 1639 | const tree = try p.parse(input); | | |
| 1640 | | | |
| 1641 | const obj = tree.root.Object; | | |
| 1642 | | 1645 | |
| 1643 | testing.expectEqualSlices(u8, obj.get("backslash").?.value.String, "\\"); | 1646 | testing.expectEqualSlices(u8, obj.get("backslash").?.value.String, "\\"); |
| 1644 | testing.expectEqualSlices(u8, obj.get("forwardslash").?.value.String, "/"); | 1647 | testing.expectEqualSlices(u8, obj.get("forwardslash").?.value.String, "/"); |
| ... | @@ -1651,3 +1654,39 @@ test "escaped characters" { | ... | @@ -1651,3 +1654,39 @@ test "escaped characters" { |
| 1651 | testing.expectEqualSlices(u8, obj.get("unicode").?.value.String, "ą"); | 1654 | testing.expectEqualSlices(u8, obj.get("unicode").?.value.String, "ą"); |
| 1652 | testing.expectEqualSlices(u8, obj.get("surrogatepair").?.value.String, "😂"); | 1655 | testing.expectEqualSlices(u8, obj.get("surrogatepair").?.value.String, "😂"); |
| 1653 | } | 1656 | } |
| | 1657 | |
| | 1658 | test "string copy option" { |
| | 1659 | const input = |
| | 1660 | \\{ |
| | 1661 | \\ "noescape": "aąðŸ˜‚", |
| | 1662 | \\ "simple": "\\\/\n\r\t\f\b\"", |
| | 1663 | \\ "unicode": "\u0105", |
| | 1664 | \\ "surrogatepair": "\ud83d\ude02" |
| | 1665 | \\} |
| | 1666 | ; |
| | 1667 | |
| | 1668 | var mem_buffer: [1024 * 16]u8 = undefined; |
| | 1669 | var buf_alloc = std.heap.FixedBufferAllocator.init(&mem_buffer); |
| | 1670 | |
| | 1671 | const tree_nocopy = try Parser.init(&buf_alloc.allocator, false).parse(input); |
| | 1672 | const obj_nocopy = tree_nocopy.root.Object; |
| | 1673 | |
| | 1674 | const tree_copy = try Parser.init(&buf_alloc.allocator, true).parse(input); |
| | 1675 | const obj_copy = tree_copy.root.Object; |
| | 1676 | |
| | 1677 | for ([_][]const u8{ "noescape", "simple", "unicode", "surrogatepair" }) |field_name| { |
| | 1678 | testing.expectEqualSlices(u8, obj_nocopy.getValue(field_name).?.String, obj_copy.getValue(field_name).?.String); |
| | 1679 | } |
| | 1680 | |
| | 1681 | const nocopy_addr = &obj_nocopy.getValue("noescape").?.String[0]; |
| | 1682 | const copy_addr = &obj_copy.getValue("noescape").?.String[0]; |
| | 1683 | |
| | 1684 | var found_nocopy = false; |
| | 1685 | for (input) |_, index| { |
| | 1686 | testing.expect(copy_addr != &input[index]); |
| | 1687 | if (nocopy_addr == &input[index]) { |
| | 1688 | found_nocopy = true; |
| | 1689 | } |
| | 1690 | } |
| | 1691 | testing.expect(found_nocopy); |
| | 1692 | } |