| ... | ... | @@ -1414,11 +1414,9 @@ pub const Parser = struct { |
| 1414 | 1414 | } |
| 1415 | 1415 | |
| 1416 | 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 | 1417 | const slice = s.slice(input, i); |
| 1420 | 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 | 1420 | .Some => |some_escapes| { |
| 1423 | 1421 | const output = try allocator.alloc(u8, s.decodedLength()); |
| 1424 | 1422 | errdefer allocator.free(output); |
| ... | ... | @@ -1497,7 +1495,10 @@ fn unescapeString(output: []u8, input: []const u8) !void { |
| 1497 | 1495 | } |
| 1498 | 1496 | |
| 1499 | 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 | 1502 | defer p.deinit(); |
| 1502 | 1503 | |
| 1503 | 1504 | const s = |
| ... | ... | @@ -1600,17 +1601,21 @@ test "write json then parse it" { |
| 1600 | 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 | | var p = Parser.init(debug.global_allocator, false); |
| 1604 | fn test_parse(memory: []u8, json_str: []const u8) !Value { |
| 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 | 1608 | return (try p.parse(json_str)).root; |
| 1606 | 1609 | } |
| 1607 | 1610 | |
| 1608 | 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 | 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 | 1620 | \\ "float": 3.14, |
| 1616 | 1621 | \\ "ints": [1, 2, 3] |
| ... | ... | @@ -1620,6 +1625,7 @@ test "integer after float has proper type" { |
| 1620 | 1625 | } |
| 1621 | 1626 | |
| 1622 | 1627 | test "escaped characters" { |
| 1628 | var memory: [1024 * 16]u8 = undefined; |
| 1623 | 1629 | const input = |
| 1624 | 1630 | \\{ |
| 1625 | 1631 | \\ "backslash": "\\", |
| ... | ... | @@ -1635,10 +1641,7 @@ test "escaped characters" { |
| 1635 | 1641 | \\} |
| 1636 | 1642 | ; |
| 1637 | 1643 | |
| 1638 | | var p = Parser.init(debug.global_allocator, false); |
| 1639 | | const tree = try p.parse(input); |
| 1640 | | |
| 1641 | | const obj = tree.root.Object; |
| 1644 | const obj = (try test_parse(&memory, input)).Object; |
| 1642 | 1645 | |
| 1643 | 1646 | testing.expectEqualSlices(u8, obj.get("backslash").?.value.String, "\\"); |
| 1644 | 1647 | testing.expectEqualSlices(u8, obj.get("forwardslash").?.value.String, "/"); |
| ... | ... | @@ -1651,3 +1654,39 @@ test "escaped characters" { |
| 1651 | 1654 | testing.expectEqualSlices(u8, obj.get("unicode").?.value.String, "ą"); |
| 1652 | 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 | } |