Commit e3c2cc1443
Changed files (2)
lib
lib/std/json.zig
@@ -1870,20 +1870,34 @@ fn parseInternal(
const v = try parseInternal(ptrInfo.child, tok, tokens, options);
arraylist.appendAssumeCapacity(v);
}
+
+ if (ptrInfo.sentinel) |some| {
+ const sentinel_value = @ptrCast(*const ptrInfo.child, some).*;
+ try arraylist.append(sentinel_value);
+ const output = arraylist.toOwnedSlice();
+ return output[0 .. output.len - 1 :sentinel_value];
+ }
+
return arraylist.toOwnedSlice();
},
.String => |stringToken| {
if (ptrInfo.child != u8) return error.UnexpectedToken;
const source_slice = stringToken.slice(tokens.slice, tokens.i - 1);
+ const len = stringToken.decodedLength();
+ const output = try allocator.alloc(u8, len + @boolToInt(ptrInfo.sentinel != null));
+ errdefer allocator.free(output);
switch (stringToken.escapes) {
- .None => return allocator.dupe(u8, source_slice),
- .Some => {
- const output = try allocator.alloc(u8, stringToken.decodedLength());
- errdefer allocator.free(output);
- try unescapeValidString(output, source_slice);
- return output;
- },
+ .None => mem.copy(u8, output, source_slice),
+ .Some => try unescapeValidString(output, source_slice),
}
+
+ if (ptrInfo.sentinel) |some| {
+ const char = @ptrCast(*const u8, some).*;
+ output[len] = char;
+ return output[0..len :char];
+ }
+
+ return output;
},
else => return error.UnexpectedToken,
}
@@ -2216,6 +2230,35 @@ test "parse into struct with misc fields" {
try testing.expectEqual(T.Union{ .float = 100000 }, r.a_union);
}
+test "parse into struct with strings and arrays with sentinels" {
+ @setEvalBranchQuota(10000);
+ const options = ParseOptions{ .allocator = testing.allocator };
+ const T = struct {
+ language: [:0]const u8,
+ language_without_sentinel: []const u8,
+ data: [:99]const i32,
+ simple_data: []const i32,
+ };
+ const r = try parse(T, &TokenStream.init(
+ \\{
+ \\ "language": "zig",
+ \\ "language_without_sentinel": "zig again!",
+ \\ "data": [1, 2, 3],
+ \\ "simple_data": [4, 5, 6]
+ \\}
+ ), options);
+ defer parseFree(T, r, options);
+
+ try testing.expectEqualSentinel(u8, 0, "zig", r.language);
+
+ const data = [_:99]i32{ 1, 2, 3 };
+ try testing.expectEqualSentinel(i32, 99, data[0..data.len], r.data);
+
+ // Make sure that arrays who aren't supposed to have a sentinel still parse without one.
+ try testing.expectEqual(@as(?i32, null), std.meta.sentinel(@TypeOf(r.simple_data)));
+ try testing.expectEqual(@as(?u8, null), std.meta.sentinel(@TypeOf(r.language_without_sentinel)));
+}
+
test "parse into struct with duplicate field" {
// allow allocator to detect double frees by keeping bucket in use
const ballast = try testing.allocator.alloc(u64, 1);
lib/std/testing.zig
@@ -299,6 +299,48 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
}
}
+/// This function is intended to be used only in tests. Checks that two slices or two arrays are equal,
+/// including that their sentinel (if any) are the same. Will error if given another type.
+pub fn expectEqualSentinel(comptime T: type, comptime sentinel: T, expected: [:sentinel]const T, actual: [:sentinel]const T) !void {
+ try expectEqualSlices(T, expected, actual);
+
+ const expected_value_sentinel = blk: {
+ switch (@typeInfo(@TypeOf(expected))) {
+ .Pointer => {
+ break :blk expected[expected.len];
+ },
+ .Array => |array_info| {
+ const indexable_outside_of_bounds = @as([]const array_info.child, &expected);
+ break :blk indexable_outside_of_bounds[indexable_outside_of_bounds.len];
+ },
+ else => {},
+ }
+ };
+
+ const actual_value_sentinel = blk: {
+ switch (@typeInfo(@TypeOf(actual))) {
+ .Pointer => {
+ break :blk actual[actual.len];
+ },
+ .Array => |array_info| {
+ const indexable_outside_of_bounds = @as([]const array_info.child, &actual);
+ break :blk indexable_outside_of_bounds[indexable_outside_of_bounds.len];
+ },
+ else => {},
+ }
+ };
+
+ if (!std.meta.eql(sentinel, expected_value_sentinel)) {
+ std.debug.print("expectEqualSentinel: 'expected' sentinel in memory is different from its type sentinel. type sentinel {}, in memory sentinel {}\n", .{ sentinel, expected_value_sentinel });
+ return error.TestExpectedEqual;
+ }
+
+ if (!std.meta.eql(sentinel, actual_value_sentinel)) {
+ std.debug.print("expectEqualSentinel: 'actual' sentinel in memory is different from its type sentinel. type sentinel {}, in memory sentinel {}\n", .{ sentinel, actual_value_sentinel });
+ return error.TestExpectedEqual;
+ }
+}
+
/// This function is intended to be used only in tests. When `ok` is false, the test fails.
/// A message is printed to stderr and then abort is called.
pub fn expect(ok: bool) !void {