authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-20 11:38:16-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-20 11:38:16-07:00
log70994b13df94ac4a3392decef498724d0e0a0a28
tree98f0565fa62409dc61bd9a22abb81bbdb153a7fc
parent5d2faeb8f3acbcf28e08f1bd126e1cd8191afd07
parente4abdf5a133ac4822644da1dabd5437ac751d78d

Merge remote-tracking branch 'origin/master' into wrangle-writer-buffering


48 files changed, 3011 insertions(+), 3307 deletions(-)

doc/langref.html.in+1-1
......@@ -7987,7 +7987,7 @@ AsmInput <- COLON AsmInputList AsmClobbers?
79877987
79887988AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN
79897989
7990AsmClobbers <- COLON StringList
7990AsmClobbers <- COLON Expr
79917991
79927992# *** Helper grammar ***
79937993BreakLabel <- COLON IDENTIFIER
lib/compiler/resinator/main.zig+7-5
......@@ -292,12 +292,14 @@ pub fn main() !void {
292292 };
293293 defer depfile.close();
294294
295 const depfile_writer = depfile.deprecatedWriter();
296 var depfile_buffered_writer = std.io.bufferedWriter(depfile_writer);
295 var depfile_buffer: [1024]u8 = undefined;
296 var depfile_writer = depfile.writer(&depfile_buffer);
297297 switch (options.depfile_fmt) {
298298 .json => {
299 var write_stream = std.json.writeStream(depfile_buffered_writer.writer(), .{ .whitespace = .indent_2 });
300 defer write_stream.deinit();
299 var write_stream: std.json.Stringify = .{
300 .writer = &depfile_writer.interface,
301 .options = .{ .whitespace = .indent_2 },
302 };
301303
302304 try write_stream.beginArray();
303305 for (dependencies_list.items) |dep_path| {
......@@ -306,7 +308,7 @@ pub fn main() !void {
306308 try write_stream.endArray();
307309 },
308310 }
309 try depfile_buffered_writer.flush();
311 try depfile_writer.interface.flush();
310312 }
311313 }
312314
lib/compiler/test_runner.zig+6-6
......@@ -10,10 +10,10 @@ pub const std_options: std.Options = .{
1010};
1111
1212var log_err_count: usize = 0;
13var fba_buffer: [8192]u8 = undefined;
1413var fba = std.heap.FixedBufferAllocator.init(&fba_buffer);
15var stdin_buffer: [std.heap.page_size_min]u8 align(std.heap.page_size_min) = undefined;
16var stdout_buffer: [std.heap.page_size_min]u8 align(std.heap.page_size_min) = undefined;
14var fba_buffer: [8192]u8 = undefined;
15var stdin_buffer: [4096]u8 = undefined;
16var stdout_buffer: [4096]u8 = undefined;
1717
1818const crippled = switch (builtin.zig_backend) {
1919 .stage2_powerpc,
......@@ -68,8 +68,8 @@ pub fn main() void {
6868
6969fn mainServer() !void {
7070 @disableInstrumentation();
71 var stdin_reader = std.fs.File.stdin().reader(&stdin_buffer);
72 var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
71 var stdin_reader = std.fs.File.stdin().readerStreaming(&stdin_buffer);
72 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
7373 var server = try std.zig.Server.init(.{
7474 .in = &stdin_reader.interface,
7575 .out = &stdout_writer.interface,
......@@ -104,7 +104,7 @@ fn mainServer() !void {
104104 defer testing.allocator.free(expected_panic_msgs);
105105
106106 for (test_fns, names, expected_panic_msgs) |test_fn, *name, *expected_panic_msg| {
107 name.* = @as(u32, @intCast(string_bytes.items.len));
107 name.* = @intCast(string_bytes.items.len);
108108 try string_bytes.ensureUnusedCapacity(testing.allocator, test_fn.name.len + 1);
109109 string_bytes.appendSliceAssumeCapacity(test_fn.name);
110110 string_bytes.appendAssumeCapacity(0);
lib/std/Build/Cache/Path.zig+5-3
......@@ -161,17 +161,19 @@ pub fn formatEscapeString(path: Path, writer: *std.io.Writer) std.io.Writer.Erro
161161 }
162162}
163163
164/// Deprecated, use double quoted escape to print paths.
164165pub fn fmtEscapeChar(path: Path) std.fmt.Formatter(Path, formatEscapeChar) {
165166 return .{ .data = path };
166167}
167168
169/// Deprecated, use double quoted escape to print paths.
168170pub fn formatEscapeChar(path: Path, writer: *std.io.Writer) std.io.Writer.Error!void {
169171 if (path.root_dir.path) |p| {
170 try std.zig.charEscape(p, writer);
171 if (path.sub_path.len > 0) try std.zig.charEscape(fs.path.sep_str, writer);
172 for (p) |byte| try std.zig.charEscape(byte, writer);
173 if (path.sub_path.len > 0) try writer.writeByte(fs.path.sep);
172174 }
173175 if (path.sub_path.len > 0) {
174 try std.zig.charEscape(path.sub_path, writer);
176 for (path.sub_path) |byte| try std.zig.charEscape(byte, writer);
175177 }
176178}
177179
lib/std/Build/Step/Run.zig+7-5
......@@ -1122,10 +1122,12 @@ fn runCommand(
11221122 // Wine's excessive stderr logging is only situationally helpful. Disable it by default, but
11231123 // allow the user to override it (e.g. with `WINEDEBUG=err+all`) if desired.
11241124 if (env_map.get("WINEDEBUG") == null) {
1125 // We don't own `env_map` at this point, so turn it into a copy before modifying it.
1126 env_map = arena.create(EnvMap) catch @panic("OOM");
1127 env_map.hash_map = try env_map.hash_map.cloneWithAllocator(arena);
1128 try env_map.put("WINEDEBUG", "-all");
1125 // We don't own `env_map` at this point, so create a copy in order to modify it.
1126 const new_env_map = arena.create(EnvMap) catch @panic("OOM");
1127 new_env_map.hash_map = try env_map.hash_map.cloneWithAllocator(arena);
1128 try new_env_map.put("WINEDEBUG", "-all");
1129
1130 env_map = new_env_map;
11291131 }
11301132 } else {
11311133 return failForeign(run, "-fwine", argv[0], exe);
......@@ -1737,7 +1739,7 @@ fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void {
17371739 .tag = tag,
17381740 .bytes_len = 0,
17391741 };
1740 try file.writeAll(std.mem.asBytes(&header));
1742 try file.writeAll(@ptrCast(&header));
17411743}
17421744
17431745fn sendRunTestMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag, index: u32) !void {
lib/std/Io/Reader.zig+42-21
......@@ -990,9 +990,9 @@ pub fn discardDelimiterLimit(r: *Reader, delimiter: u8, limit: Limit) DiscardDel
990990/// Returns `error.EndOfStream` if and only if there are fewer than `n` bytes
991991/// remaining.
992992///
993/// Asserts buffer capacity is at least `n`.
993/// If the end of stream is not encountered, asserts buffer capacity is at
994/// least `n`.
994995pub fn fill(r: *Reader, n: usize) Error!void {
995 assert(n <= r.buffer.len);
996996 if (r.seek + n <= r.end) {
997997 @branchHint(.likely);
998998 return;
......@@ -1108,9 +1108,9 @@ pub fn takeVarInt(r: *Reader, comptime Int: type, endian: std.builtin.Endian, n:
11081108/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
11091109///
11101110/// See also:
1111/// * `peekStructReference`
1111/// * `peekStructPointer`
11121112/// * `takeStruct`
1113pub fn takeStructReference(r: *Reader, comptime T: type) Error!*align(1) T {
1113pub fn takeStructPointer(r: *Reader, comptime T: type) Error!*align(1) T {
11141114 // Only extern and packed structs have defined in-memory layout.
11151115 comptime assert(@typeInfo(T).@"struct".layout != .auto);
11161116 return @ptrCast(try r.takeArray(@sizeOf(T)));
......@@ -1122,9 +1122,9 @@ pub fn takeStructReference(r: *Reader, comptime T: type) Error!*align(1) T {
11221122/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
11231123///
11241124/// See also:
1125/// * `takeStructReference`
1125/// * `takeStructPointer`
11261126/// * `peekStruct`
1127pub fn peekStructReference(r: *Reader, comptime T: type) Error!*align(1) T {
1127pub fn peekStructPointer(r: *Reader, comptime T: type) Error!*align(1) T {
11281128 // Only extern and packed structs have defined in-memory layout.
11291129 comptime assert(@typeInfo(T).@"struct".layout != .auto);
11301130 return @ptrCast(try r.peekArray(@sizeOf(T)));
......@@ -1136,19 +1136,19 @@ pub fn peekStructReference(r: *Reader, comptime T: type) Error!*align(1) T {
11361136/// when `endian` is comptime-known and matches the host endianness.
11371137///
11381138/// See also:
1139/// * `takeStructReference`
1139/// * `takeStructPointer`
11401140/// * `peekStruct`
11411141pub inline fn takeStruct(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
11421142 switch (@typeInfo(T)) {
11431143 .@"struct" => |info| switch (info.layout) {
11441144 .auto => @compileError("ill-defined memory layout"),
11451145 .@"extern" => {
1146 var res = (try r.takeStructReference(T)).*;
1146 var res = (try r.takeStructPointer(T)).*;
11471147 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
11481148 return res;
11491149 },
11501150 .@"packed" => {
1151 return takeInt(r, info.backing_integer.?, endian);
1151 return @bitCast(try takeInt(r, info.backing_integer.?, endian));
11521152 },
11531153 },
11541154 else => @compileError("not a struct"),
......@@ -1162,18 +1162,18 @@ pub inline fn takeStruct(r: *Reader, comptime T: type, endian: std.builtin.Endia
11621162///
11631163/// See also:
11641164/// * `takeStruct`
1165/// * `peekStructReference`
1165/// * `peekStructPointer`
11661166pub inline fn peekStruct(r: *Reader, comptime T: type, endian: std.builtin.Endian) Error!T {
11671167 switch (@typeInfo(T)) {
11681168 .@"struct" => |info| switch (info.layout) {
11691169 .auto => @compileError("ill-defined memory layout"),
11701170 .@"extern" => {
1171 var res = (try r.peekStructReference(T)).*;
1171 var res = (try r.peekStructPointer(T)).*;
11721172 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
11731173 return res;
11741174 },
11751175 .@"packed" => {
1176 return peekInt(r, info.backing_integer.?, endian);
1176 return @bitCast(try peekInt(r, info.backing_integer.?, endian));
11771177 },
11781178 },
11791179 else => @compileError("not a struct"),
......@@ -1557,27 +1557,27 @@ test takeVarInt {
15571557 try testing.expectError(error.EndOfStream, r.takeVarInt(u16, .little, 1));
15581558}
15591559
1560test takeStructReference {
1560test takeStructPointer {
15611561 var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
15621562 const S = extern struct { a: u8, b: u16 };
15631563 switch (native_endian) {
1564 .little => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.takeStructReference(S)).*),
1565 .big => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.takeStructReference(S)).*),
1564 .little => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.takeStructPointer(S)).*),
1565 .big => try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.takeStructPointer(S)).*),
15661566 }
1567 try testing.expectError(error.EndOfStream, r.takeStructReference(S));
1567 try testing.expectError(error.EndOfStream, r.takeStructPointer(S));
15681568}
15691569
1570test peekStructReference {
1570test peekStructPointer {
15711571 var r: Reader = .fixed(&.{ 0x12, 0x00, 0x34, 0x56 });
15721572 const S = extern struct { a: u8, b: u16 };
15731573 switch (native_endian) {
15741574 .little => {
1575 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStructReference(S)).*);
1576 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStructReference(S)).*);
1575 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStructPointer(S)).*);
1576 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x5634 }), (try r.peekStructPointer(S)).*);
15771577 },
15781578 .big => {
1579 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStructReference(S)).*);
1580 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStructReference(S)).*);
1579 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStructPointer(S)).*);
1580 try testing.expectEqual(@as(S, .{ .a = 0x12, .b = 0x3456 }), (try r.peekStructPointer(S)).*);
15811581 },
15821582 }
15831583}
......@@ -1724,6 +1724,27 @@ test "takeDelimiterInclusive when it rebases" {
17241724 }
17251725}
17261726
1727test "takeStruct and peekStruct packed" {
1728 var r: Reader = .fixed(&.{ 0b11110000, 0b00110011 });
1729 const S = packed struct(u16) { a: u2, b: u6, c: u7, d: u1 };
1730
1731 try testing.expectEqual(@as(S, .{
1732 .a = 0b11,
1733 .b = 0b001100,
1734 .c = 0b1110000,
1735 .d = 0b1,
1736 }), try r.peekStruct(S, .big));
1737
1738 try testing.expectEqual(@as(S, .{
1739 .a = 0b11,
1740 .b = 0b001100,
1741 .c = 0b1110000,
1742 .d = 0b1,
1743 }), try r.takeStruct(S, .big));
1744
1745 try testing.expectError(error.EndOfStream, r.takeStruct(S, .little));
1746}
1747
17271748/// Provides a `Reader` implementation by passing data from an underlying
17281749/// reader through `Hasher.update`.
17291750///
lib/std/Io/Writer.zig+17-15
......@@ -867,18 +867,11 @@ pub inline fn writeSliceEndian(
867867 }
868868}
869869
870/// Asserts that the buffer storage capacity is at least enough to store `@sizeOf(Elem)`
871///
872/// Asserts that the buffer is aligned enough for `@alignOf(Elem)`.
873870pub fn writeSliceSwap(w: *Writer, Elem: type, slice: []const Elem) Error!void {
874 var i: usize = 0;
875 while (i < slice.len) {
876 const dest_bytes = try w.writableSliceGreedy(@sizeOf(Elem));
877 const dest: []Elem = @alignCast(@ptrCast(dest_bytes[0 .. dest_bytes.len - dest_bytes.len % @sizeOf(Elem)]));
878 const copy_len = @min(dest.len, slice.len - i);
879 @memcpy(dest[0..copy_len], slice[i..][0..copy_len]);
880 i += copy_len;
881 std.mem.byteSwapAllElements(Elem, dest);
871 for (slice) |elem| {
872 var tmp = elem;
873 std.mem.byteSwapAllFields(Elem, &tmp);
874 try w.writeAll(@ptrCast(&tmp));
882875 }
883876}
884877
......@@ -1141,8 +1134,8 @@ pub fn printValue(
11411134 else => invalidFmtError(fmt, value),
11421135 },
11431136 't' => switch (@typeInfo(T)) {
1144 .error_set => return w.writeAll(@errorName(value)),
1145 .@"enum", .@"union" => return w.writeAll(@tagName(value)),
1137 .error_set => return w.alignBufferOptions(@errorName(value), options),
1138 .@"enum", .@"union" => return w.alignBufferOptions(@tagName(value), options),
11461139 else => invalidFmtError(fmt, value),
11471140 },
11481141 else => {},
......@@ -2152,6 +2145,14 @@ test "bytes.hex" {
21522145 try testing.expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros});
21532146}
21542147
2148test "padding" {
2149 const foo: enum { foo } = .foo;
2150 try testing.expectFmt("tag: |foo |\n", "tag: |{t:<4}|\n", .{foo});
2151
2152 const bar: error{bar} = error.bar;
2153 try testing.expectFmt("error: |bar |\n", "error: |{t:<4}|\n", .{bar});
2154}
2155
21552156test fixed {
21562157 {
21572158 var buf: [255]u8 = undefined;
......@@ -2650,9 +2651,10 @@ test writeStruct {
26502651}
26512652
26522653test writeSliceEndian {
2653 var buffer: [4]u8 align(2) = undefined;
2654 var buffer: [5]u8 align(2) = undefined;
26542655 var w: Writer = .fixed(&buffer);
2656 try w.writeByte('x');
26552657 const array: [2]u16 = .{ 0x1234, 0x5678 };
26562658 try writeSliceEndian(&w, u16, &array, .big);
2657 try testing.expectEqualSlices(u8, &.{ 0x12, 0x34, 0x56, 0x78 }, &buffer);
2659 try testing.expectEqualSlices(u8, &.{ 'x', 0x12, 0x34, 0x56, 0x78 }, &buffer);
26582660}
lib/std/hash.zig-2
......@@ -31,8 +31,6 @@ pub const CityHash64 = cityhash.CityHash64;
3131const wyhash = @import("hash/wyhash.zig");
3232pub const Wyhash = wyhash.Wyhash;
3333
34pub const RapidHash = @import("hash/RapidHash.zig");
35
3634const xxhash = @import("hash/xxhash.zig");
3735pub const XxHash3 = xxhash.XxHash3;
3836pub const XxHash64 = xxhash.XxHash64;
lib/std/hash/RapidHash.zig deleted-125
......@@ -1,125 +0,0 @@
1const std = @import("std");
2
3const readInt = std.mem.readInt;
4const assert = std.debug.assert;
5const expect = std.testing.expect;
6const expectEqual = std.testing.expectEqual;
7
8const RAPID_SEED: u64 = 0xbdd89aa982704029;
9const RAPID_SECRET: [3]u64 = .{ 0x2d358dccaa6c78a5, 0x8bb84b93962eacc9, 0x4b33a62ed433d4a3 };
10
11pub fn hash(seed: u64, input: []const u8) u64 {
12 const sc = RAPID_SECRET;
13 const len = input.len;
14 var a: u64 = 0;
15 var b: u64 = 0;
16 var k = input;
17 var is: [3]u64 = .{ seed, 0, 0 };
18
19 is[0] ^= mix(seed ^ sc[0], sc[1]) ^ len;
20
21 if (len <= 16) {
22 if (len >= 4) {
23 const d: u64 = ((len & 24) >> @intCast(len >> 3));
24 const e = len - 4;
25 a = (r32(k) << 32) | r32(k[e..]);
26 b = ((r32(k[d..]) << 32) | r32(k[(e - d)..]));
27 } else if (len > 0)
28 a = (@as(u64, k[0]) << 56) | (@as(u64, k[len >> 1]) << 32) | @as(u64, k[len - 1]);
29 } else {
30 var remain = len;
31 if (len > 48) {
32 is[1] = is[0];
33 is[2] = is[0];
34 while (remain >= 96) {
35 inline for (0..6) |i| {
36 const m1 = r64(k[8 * i * 2 ..]);
37 const m2 = r64(k[8 * (i * 2 + 1) ..]);
38 is[i % 3] = mix(m1 ^ sc[i % 3], m2 ^ is[i % 3]);
39 }
40 k = k[96..];
41 remain -= 96;
42 }
43 if (remain >= 48) {
44 inline for (0..3) |i| {
45 const m1 = r64(k[8 * i * 2 ..]);
46 const m2 = r64(k[8 * (i * 2 + 1) ..]);
47 is[i] = mix(m1 ^ sc[i], m2 ^ is[i]);
48 }
49 k = k[48..];
50 remain -= 48;
51 }
52
53 is[0] ^= is[1] ^ is[2];
54 }
55
56 if (remain > 16) {
57 is[0] = mix(r64(k) ^ sc[2], r64(k[8..]) ^ is[0] ^ sc[1]);
58 if (remain > 32) {
59 is[0] = mix(r64(k[16..]) ^ sc[2], r64(k[24..]) ^ is[0]);
60 }
61 }
62
63 a = r64(input[len - 16 ..]);
64 b = r64(input[len - 8 ..]);
65 }
66
67 a ^= sc[1];
68 b ^= is[0];
69 mum(&a, &b);
70 return mix(a ^ sc[0] ^ len, b ^ sc[1]);
71}
72
73test "RapidHash.hash" {
74 const bytes: []const u8 = "abcdefgh" ** 128;
75
76 const sizes: [13]u64 = .{ 0, 1, 2, 3, 4, 8, 16, 32, 64, 128, 256, 512, 1024 };
77
78 const outcomes: [13]u64 = .{
79 0x5a6ef77074ebc84b,
80 0xc11328477bc0f5d1,
81 0x5644ac035e40d569,
82 0x347080fbf5fcd81,
83 0x56b66b8dc802bcc,
84 0xb6bf9055973aac7c,
85 0xed56d62eead1e402,
86 0xc19072d767da8ffb,
87 0x89bb40a9928a4f0d,
88 0xe0af7c5e7b6e29fd,
89 0x9a3ed35fbedfa11a,
90 0x4c684b2119ca19fb,
91 0x4b575f5bf25600d6,
92 };
93
94 var success: bool = true;
95 for (sizes, outcomes) |s, e| {
96 const r = hash(RAPID_SEED, bytes[0..s]);
97
98 expectEqual(e, r) catch |err| {
99 std.debug.print("Failed on {d}: {!}\n", .{ s, err });
100 success = false;
101 };
102 }
103 try expect(success);
104}
105
106inline fn mum(a: *u64, b: *u64) void {
107 const r = @as(u128, a.*) * b.*;
108 a.* = @truncate(r);
109 b.* = @truncate(r >> 64);
110}
111
112inline fn mix(a: u64, b: u64) u64 {
113 var copy_a = a;
114 var copy_b = b;
115 mum(&copy_a, &copy_b);
116 return copy_a ^ copy_b;
117}
118
119inline fn r64(p: []const u8) u64 {
120 return readInt(u64, p[0..8], .little);
121}
122
123inline fn r32(p: []const u8) u64 {
124 return readInt(u32, p[0..4], .little);
125}
lib/std/hash/benchmark.zig-6
......@@ -59,12 +59,6 @@ const hashes = [_]Hash{
5959 .ty = hash.crc.Crc32,
6060 .name = "crc32",
6161 },
62 Hash{
63 .ty = hash.RapidHash,
64 .name = "rapidhash",
65 .has_iterative_api = false,
66 .init_u64 = 0,
67 },
6862 Hash{
6963 .ty = hash.CityHash32,
7064 .name = "cityhash-32",
lib/std/json.zig+17-24
......@@ -44,7 +44,7 @@ test Value {
4444test Stringify {
4545 var out: std.io.Writer.Allocating = .init(testing.allocator);
4646 var write_stream: Stringify = .{
47 .writer = &out.interface,
47 .writer = &out.writer,
4848 .options = .{ .whitespace = .indent_2 },
4949 };
5050 defer out.deinit();
......@@ -66,18 +66,18 @@ pub const Value = @import("json/dynamic.zig").Value;
6666
6767pub const ArrayHashMap = @import("json/hashmap.zig").ArrayHashMap;
6868
69pub const validate = @import("json/scanner.zig").validate;
70pub const Error = @import("json/scanner.zig").Error;
71pub const reader = @import("json/scanner.zig").reader;
72pub const default_buffer_size = @import("json/scanner.zig").default_buffer_size;
73pub const Token = @import("json/scanner.zig").Token;
74pub const TokenType = @import("json/scanner.zig").TokenType;
75pub const Diagnostics = @import("json/scanner.zig").Diagnostics;
76pub const AllocWhen = @import("json/scanner.zig").AllocWhen;
77pub const default_max_value_len = @import("json/scanner.zig").default_max_value_len;
78pub const Reader = @import("json/scanner.zig").Reader;
79pub const Scanner = @import("json/scanner.zig").Scanner;
80pub const isNumberFormattedLikeAnInteger = @import("json/scanner.zig").isNumberFormattedLikeAnInteger;
69pub const Scanner = @import("json/Scanner.zig");
70pub const validate = Scanner.validate;
71pub const Error = Scanner.Error;
72pub const reader = Scanner.reader;
73pub const default_buffer_size = Scanner.default_buffer_size;
74pub const Token = Scanner.Token;
75pub const TokenType = Scanner.TokenType;
76pub const Diagnostics = Scanner.Diagnostics;
77pub const AllocWhen = Scanner.AllocWhen;
78pub const default_max_value_len = Scanner.default_max_value_len;
79pub const Reader = Scanner.Reader;
80pub const isNumberFormattedLikeAnInteger = Scanner.isNumberFormattedLikeAnInteger;
8181
8282pub const ParseOptions = @import("json/static.zig").ParseOptions;
8383pub const Parsed = @import("json/static.zig").Parsed;
......@@ -101,10 +101,10 @@ pub fn fmt(value: anytype, options: Stringify.Options) Formatter(@TypeOf(value))
101101
102102test fmt {
103103 const expectFmt = std.testing.expectFmt;
104 try expectFmt("123", "{}", .{fmt(@as(u32, 123), .{})});
104 try expectFmt("123", "{f}", .{fmt(@as(u32, 123), .{})});
105105 try expectFmt(
106106 \\{"num":927,"msg":"hello","sub":{"mybool":true}}
107 , "{}", .{fmt(struct {
107 , "{f}", .{fmt(struct {
108108 num: u32,
109109 msg: []const u8,
110110 sub: struct {
......@@ -123,14 +123,7 @@ pub fn Formatter(comptime T: type) type {
123123 value: T,
124124 options: Stringify.Options,
125125
126 pub fn format(
127 self: @This(),
128 comptime fmt_spec: []const u8,
129 options: std.fmt.FormatOptions,
130 writer: *std.io.Writer,
131 ) !void {
132 comptime std.debug.assert(fmt_spec.len == 0);
133 _ = options;
126 pub fn format(self: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void {
134127 try Stringify.value(self.value, self.options, writer);
135128 }
136129 };
......@@ -138,7 +131,7 @@ pub fn Formatter(comptime T: type) type {
138131
139132test {
140133 _ = @import("json/test.zig");
141 _ = @import("json/scanner.zig");
134 _ = Scanner;
142135 _ = @import("json/dynamic.zig");
143136 _ = @import("json/hashmap.zig");
144137 _ = @import("json/static.zig");
lib/std/json/Scanner.zig created+1767
......@@ -0,0 +1,1767 @@
1//! The lowest level parsing API in this package;
2//! supports streaming input with a low memory footprint.
3//! The memory requirement is `O(d)` where d is the nesting depth of `[]` or `{}` containers in the input.
4//! Specifically `d/8` bytes are required for this purpose,
5//! with some extra buffer according to the implementation of `std.ArrayList`.
6//!
7//! This scanner can emit partial tokens; see `std.json.Token`.
8//! The input to this class is a sequence of input buffers that you must supply one at a time.
9//! Call `feedInput()` with the first buffer, then call `next()` repeatedly until `error.BufferUnderrun` is returned.
10//! Then call `feedInput()` again and so forth.
11//! Call `endInput()` when the last input buffer has been given to `feedInput()`, either immediately after calling `feedInput()`,
12//! or when `error.BufferUnderrun` requests more data and there is no more.
13//! Be sure to call `next()` after calling `endInput()` until `Token.end_of_document` has been returned.
14//!
15//! Notes on standards compliance: https://datatracker.ietf.org/doc/html/rfc8259
16//! * RFC 8259 requires JSON documents be valid UTF-8,
17//! but makes an allowance for systems that are "part of a closed ecosystem".
18//! I have no idea what that's supposed to mean in the context of a standard specification.
19//! This implementation requires inputs to be valid UTF-8.
20//! * RFC 8259 contradicts itself regarding whether lowercase is allowed in \u hex digits,
21//! but this is probably a bug in the spec, and it's clear that lowercase is meant to be allowed.
22//! (RFC 5234 defines HEXDIG to only allow uppercase.)
23//! * When RFC 8259 refers to a "character", I assume they really mean a "Unicode scalar value".
24//! See http://www.unicode.org/glossary/#unicode_scalar_value .
25//! * RFC 8259 doesn't explicitly disallow unpaired surrogate halves in \u escape sequences,
26//! but vaguely implies that \u escapes are for encoding Unicode "characters" (i.e. Unicode scalar values?),
27//! which would mean that unpaired surrogate halves are forbidden.
28//! By contrast ECMA-404 (a competing(/compatible?) JSON standard, which JavaScript's JSON.parse() conforms to)
29//! explicitly allows unpaired surrogate halves.
30//! This implementation forbids unpaired surrogate halves in \u sequences.
31//! If a high surrogate half appears in a \u sequence,
32//! then a low surrogate half must immediately follow in \u notation.
33//! * RFC 8259 allows implementations to "accept non-JSON forms or extensions".
34//! This implementation does not accept any of that.
35//! * RFC 8259 allows implementations to put limits on "the size of texts",
36//! "the maximum depth of nesting", "the range and precision of numbers",
37//! and "the length and character contents of strings".
38//! This low-level implementation does not limit these,
39//! except where noted above, and except that nesting depth requires memory allocation.
40//! Note that this low-level API does not interpret numbers numerically,
41//! but simply emits their source form for some higher level code to make sense of.
42//! * This low-level implementation allows duplicate object keys,
43//! and key/value pairs are emitted in the order they appear in the input.
44
45const Scanner = @This();
46const std = @import("std");
47
48const Allocator = std.mem.Allocator;
49const ArrayList = std.ArrayList;
50const assert = std.debug.assert;
51const BitStack = std.BitStack;
52
53state: State = .value,
54string_is_object_key: bool = false,
55stack: BitStack,
56value_start: usize = undefined,
57utf16_code_units: [2]u16 = undefined,
58
59input: []const u8 = "",
60cursor: usize = 0,
61is_end_of_input: bool = false,
62diagnostics: ?*Diagnostics = null,
63
64/// The allocator is only used to track `[]` and `{}` nesting levels.
65pub fn initStreaming(allocator: Allocator) @This() {
66 return .{
67 .stack = BitStack.init(allocator),
68 };
69}
70/// Use this if your input is a single slice.
71/// This is effectively equivalent to:
72/// ```
73/// initStreaming(allocator);
74/// feedInput(complete_input);
75/// endInput();
76/// ```
77pub fn initCompleteInput(allocator: Allocator, complete_input: []const u8) @This() {
78 return .{
79 .stack = BitStack.init(allocator),
80 .input = complete_input,
81 .is_end_of_input = true,
82 };
83}
84pub fn deinit(self: *@This()) void {
85 self.stack.deinit();
86 self.* = undefined;
87}
88
89pub fn enableDiagnostics(self: *@This(), diagnostics: *Diagnostics) void {
90 diagnostics.cursor_pointer = &self.cursor;
91 self.diagnostics = diagnostics;
92}
93
94/// Call this whenever you get `error.BufferUnderrun` from `next()`.
95/// When there is no more input to provide, call `endInput()`.
96pub fn feedInput(self: *@This(), input: []const u8) void {
97 assert(self.cursor == self.input.len); // Not done with the last input slice.
98 if (self.diagnostics) |diag| {
99 diag.total_bytes_before_current_input += self.input.len;
100 // This usually goes "negative" to measure how far before the beginning
101 // of the new buffer the current line started.
102 diag.line_start_cursor -%= self.cursor;
103 }
104 self.input = input;
105 self.cursor = 0;
106 self.value_start = 0;
107}
108/// Call this when you will no longer call `feedInput()` anymore.
109/// This can be called either immediately after the last `feedInput()`,
110/// or at any time afterward, such as when getting `error.BufferUnderrun` from `next()`.
111/// Don't forget to call `next*()` after `endInput()` until you get `.end_of_document`.
112pub fn endInput(self: *@This()) void {
113 self.is_end_of_input = true;
114}
115
116pub const NextError = Error || Allocator.Error || error{BufferUnderrun};
117pub const AllocError = Error || Allocator.Error || error{ValueTooLong};
118pub const PeekError = Error || error{BufferUnderrun};
119pub const SkipError = Error || Allocator.Error;
120pub const AllocIntoArrayListError = AllocError || error{BufferUnderrun};
121
122/// Equivalent to `nextAllocMax(allocator, when, default_max_value_len);`
123/// This function is only available after `endInput()` (or `initCompleteInput()`) has been called.
124/// See also `std.json.Token` for documentation of `nextAlloc*()` function behavior.
125pub fn nextAlloc(self: *@This(), allocator: Allocator, when: AllocWhen) AllocError!Token {
126 return self.nextAllocMax(allocator, when, default_max_value_len);
127}
128
129/// This function is only available after `endInput()` (or `initCompleteInput()`) has been called.
130/// See also `std.json.Token` for documentation of `nextAlloc*()` function behavior.
131pub fn nextAllocMax(self: *@This(), allocator: Allocator, when: AllocWhen, max_value_len: usize) AllocError!Token {
132 assert(self.is_end_of_input); // This function is not available in streaming mode.
133 const token_type = self.peekNextTokenType() catch |e| switch (e) {
134 error.BufferUnderrun => unreachable,
135 else => |err| return err,
136 };
137 switch (token_type) {
138 .number, .string => {
139 var value_list = ArrayList(u8).init(allocator);
140 errdefer {
141 value_list.deinit();
142 }
143 if (self.allocNextIntoArrayListMax(&value_list, when, max_value_len) catch |e| switch (e) {
144 error.BufferUnderrun => unreachable,
145 else => |err| return err,
146 }) |slice| {
147 return if (token_type == .number)
148 Token{ .number = slice }
149 else
150 Token{ .string = slice };
151 } else {
152 return if (token_type == .number)
153 Token{ .allocated_number = try value_list.toOwnedSlice() }
154 else
155 Token{ .allocated_string = try value_list.toOwnedSlice() };
156 }
157 },
158
159 // Simple tokens never alloc.
160 .object_begin,
161 .object_end,
162 .array_begin,
163 .array_end,
164 .true,
165 .false,
166 .null,
167 .end_of_document,
168 => return self.next() catch |e| switch (e) {
169 error.BufferUnderrun => unreachable,
170 else => |err| return err,
171 },
172 }
173}
174
175/// Equivalent to `allocNextIntoArrayListMax(value_list, when, default_max_value_len);`
176pub fn allocNextIntoArrayList(self: *@This(), value_list: *ArrayList(u8), when: AllocWhen) AllocIntoArrayListError!?[]const u8 {
177 return self.allocNextIntoArrayListMax(value_list, when, default_max_value_len);
178}
179/// The next token type must be either `.number` or `.string`. See `peekNextTokenType()`.
180/// When allocation is not necessary with `.alloc_if_needed`,
181/// this method returns the content slice from the input buffer, and `value_list` is not touched.
182/// When allocation is necessary or with `.alloc_always`, this method concatenates partial tokens into the given `value_list`,
183/// and returns `null` once the final `.number` or `.string` token has been written into it.
184/// In case of an `error.BufferUnderrun`, partial values will be left in the given value_list.
185/// The given `value_list` is never reset by this method, so an `error.BufferUnderrun` situation
186/// can be resumed by passing the same array list in again.
187/// This method does not indicate whether the token content being returned is for a `.number` or `.string` token type;
188/// the caller of this method is expected to know which type of token is being processed.
189pub fn allocNextIntoArrayListMax(self: *@This(), value_list: *ArrayList(u8), when: AllocWhen, max_value_len: usize) AllocIntoArrayListError!?[]const u8 {
190 while (true) {
191 const token = try self.next();
192 switch (token) {
193 // Accumulate partial values.
194 .partial_number, .partial_string => |slice| {
195 try appendSlice(value_list, slice, max_value_len);
196 },
197 .partial_string_escaped_1 => |buf| {
198 try appendSlice(value_list, buf[0..], max_value_len);
199 },
200 .partial_string_escaped_2 => |buf| {
201 try appendSlice(value_list, buf[0..], max_value_len);
202 },
203 .partial_string_escaped_3 => |buf| {
204 try appendSlice(value_list, buf[0..], max_value_len);
205 },
206 .partial_string_escaped_4 => |buf| {
207 try appendSlice(value_list, buf[0..], max_value_len);
208 },
209
210 // Return complete values.
211 .number => |slice| {
212 if (when == .alloc_if_needed and value_list.items.len == 0) {
213 // No alloc necessary.
214 return slice;
215 }
216 try appendSlice(value_list, slice, max_value_len);
217 // The token is complete.
218 return null;
219 },
220 .string => |slice| {
221 if (when == .alloc_if_needed and value_list.items.len == 0) {
222 // No alloc necessary.
223 return slice;
224 }
225 try appendSlice(value_list, slice, max_value_len);
226 // The token is complete.
227 return null;
228 },
229
230 .object_begin,
231 .object_end,
232 .array_begin,
233 .array_end,
234 .true,
235 .false,
236 .null,
237 .end_of_document,
238 => unreachable, // Only .number and .string token types are allowed here. Check peekNextTokenType() before calling this.
239
240 .allocated_number, .allocated_string => unreachable,
241 }
242 }
243}
244
245/// This function is only available after `endInput()` (or `initCompleteInput()`) has been called.
246/// If the next token type is `.object_begin` or `.array_begin`,
247/// this function calls `next()` repeatedly until the corresponding `.object_end` or `.array_end` is found.
248/// If the next token type is `.number` or `.string`,
249/// this function calls `next()` repeatedly until the (non `.partial_*`) `.number` or `.string` token is found.
250/// If the next token type is `.true`, `.false`, or `.null`, this function calls `next()` once.
251/// The next token type must not be `.object_end`, `.array_end`, or `.end_of_document`;
252/// see `peekNextTokenType()`.
253pub fn skipValue(self: *@This()) SkipError!void {
254 assert(self.is_end_of_input); // This function is not available in streaming mode.
255 switch (self.peekNextTokenType() catch |e| switch (e) {
256 error.BufferUnderrun => unreachable,
257 else => |err| return err,
258 }) {
259 .object_begin, .array_begin => {
260 self.skipUntilStackHeight(self.stackHeight()) catch |e| switch (e) {
261 error.BufferUnderrun => unreachable,
262 else => |err| return err,
263 };
264 },
265 .number, .string => {
266 while (true) {
267 switch (self.next() catch |e| switch (e) {
268 error.BufferUnderrun => unreachable,
269 else => |err| return err,
270 }) {
271 .partial_number,
272 .partial_string,
273 .partial_string_escaped_1,
274 .partial_string_escaped_2,
275 .partial_string_escaped_3,
276 .partial_string_escaped_4,
277 => continue,
278
279 .number, .string => break,
280
281 else => unreachable,
282 }
283 }
284 },
285 .true, .false, .null => {
286 _ = self.next() catch |e| switch (e) {
287 error.BufferUnderrun => unreachable,
288 else => |err| return err,
289 };
290 },
291
292 .object_end, .array_end, .end_of_document => unreachable, // Attempt to skip a non-value token.
293 }
294}
295
296/// Skip tokens until an `.object_end` or `.array_end` token results in a `stackHeight()` equal the given stack height.
297/// Unlike `skipValue()`, this function is available in streaming mode.
298pub fn skipUntilStackHeight(self: *@This(), terminal_stack_height: usize) NextError!void {
299 while (true) {
300 switch (try self.next()) {
301 .object_end, .array_end => {
302 if (self.stackHeight() == terminal_stack_height) break;
303 },
304 .end_of_document => unreachable,
305 else => continue,
306 }
307 }
308}
309
310/// The depth of `{}` or `[]` nesting levels at the current position.
311pub fn stackHeight(self: *const @This()) usize {
312 return self.stack.bit_len;
313}
314
315/// Pre allocate memory to hold the given number of nesting levels.
316/// `stackHeight()` up to the given number will not cause allocations.
317pub fn ensureTotalStackCapacity(self: *@This(), height: usize) Allocator.Error!void {
318 try self.stack.ensureTotalCapacity(height);
319}
320
321/// See `std.json.Token` for documentation of this function.
322pub fn next(self: *@This()) NextError!Token {
323 state_loop: while (true) {
324 switch (self.state) {
325 .value => {
326 switch (try self.skipWhitespaceExpectByte()) {
327 // Object, Array
328 '{' => {
329 try self.stack.push(OBJECT_MODE);
330 self.cursor += 1;
331 self.state = .object_start;
332 return .object_begin;
333 },
334 '[' => {
335 try self.stack.push(ARRAY_MODE);
336 self.cursor += 1;
337 self.state = .array_start;
338 return .array_begin;
339 },
340
341 // String
342 '"' => {
343 self.cursor += 1;
344 self.value_start = self.cursor;
345 self.state = .string;
346 continue :state_loop;
347 },
348
349 // Number
350 '1'...'9' => {
351 self.value_start = self.cursor;
352 self.cursor += 1;
353 self.state = .number_int;
354 continue :state_loop;
355 },
356 '0' => {
357 self.value_start = self.cursor;
358 self.cursor += 1;
359 self.state = .number_leading_zero;
360 continue :state_loop;
361 },
362 '-' => {
363 self.value_start = self.cursor;
364 self.cursor += 1;
365 self.state = .number_minus;
366 continue :state_loop;
367 },
368
369 // literal values
370 't' => {
371 self.cursor += 1;
372 self.state = .literal_t;
373 continue :state_loop;
374 },
375 'f' => {
376 self.cursor += 1;
377 self.state = .literal_f;
378 continue :state_loop;
379 },
380 'n' => {
381 self.cursor += 1;
382 self.state = .literal_n;
383 continue :state_loop;
384 },
385
386 else => return error.SyntaxError,
387 }
388 },
389
390 .post_value => {
391 if (try self.skipWhitespaceCheckEnd()) return .end_of_document;
392
393 const c = self.input[self.cursor];
394 if (self.string_is_object_key) {
395 self.string_is_object_key = false;
396 switch (c) {
397 ':' => {
398 self.cursor += 1;
399 self.state = .value;
400 continue :state_loop;
401 },
402 else => return error.SyntaxError,
403 }
404 }
405
406 switch (c) {
407 '}' => {
408 if (self.stack.pop() != OBJECT_MODE) return error.SyntaxError;
409 self.cursor += 1;
410 // stay in .post_value state.
411 return .object_end;
412 },
413 ']' => {
414 if (self.stack.pop() != ARRAY_MODE) return error.SyntaxError;
415 self.cursor += 1;
416 // stay in .post_value state.
417 return .array_end;
418 },
419 ',' => {
420 switch (self.stack.peek()) {
421 OBJECT_MODE => {
422 self.state = .object_post_comma;
423 },
424 ARRAY_MODE => {
425 self.state = .value;
426 },
427 }
428 self.cursor += 1;
429 continue :state_loop;
430 },
431 else => return error.SyntaxError,
432 }
433 },
434
435 .object_start => {
436 switch (try self.skipWhitespaceExpectByte()) {
437 '"' => {
438 self.cursor += 1;
439 self.value_start = self.cursor;
440 self.state = .string;
441 self.string_is_object_key = true;
442 continue :state_loop;
443 },
444 '}' => {
445 self.cursor += 1;
446 _ = self.stack.pop();
447 self.state = .post_value;
448 return .object_end;
449 },
450 else => return error.SyntaxError,
451 }
452 },
453 .object_post_comma => {
454 switch (try self.skipWhitespaceExpectByte()) {
455 '"' => {
456 self.cursor += 1;
457 self.value_start = self.cursor;
458 self.state = .string;
459 self.string_is_object_key = true;
460 continue :state_loop;
461 },
462 else => return error.SyntaxError,
463 }
464 },
465
466 .array_start => {
467 switch (try self.skipWhitespaceExpectByte()) {
468 ']' => {
469 self.cursor += 1;
470 _ = self.stack.pop();
471 self.state = .post_value;
472 return .array_end;
473 },
474 else => {
475 self.state = .value;
476 continue :state_loop;
477 },
478 }
479 },
480
481 .number_minus => {
482 if (self.cursor >= self.input.len) return self.endOfBufferInNumber(false);
483 switch (self.input[self.cursor]) {
484 '0' => {
485 self.cursor += 1;
486 self.state = .number_leading_zero;
487 continue :state_loop;
488 },
489 '1'...'9' => {
490 self.cursor += 1;
491 self.state = .number_int;
492 continue :state_loop;
493 },
494 else => return error.SyntaxError,
495 }
496 },
497 .number_leading_zero => {
498 if (self.cursor >= self.input.len) return self.endOfBufferInNumber(true);
499 switch (self.input[self.cursor]) {
500 '.' => {
501 self.cursor += 1;
502 self.state = .number_post_dot;
503 continue :state_loop;
504 },
505 'e', 'E' => {
506 self.cursor += 1;
507 self.state = .number_post_e;
508 continue :state_loop;
509 },
510 else => {
511 self.state = .post_value;
512 return Token{ .number = self.takeValueSlice() };
513 },
514 }
515 },
516 .number_int => {
517 while (self.cursor < self.input.len) : (self.cursor += 1) {
518 switch (self.input[self.cursor]) {
519 '0'...'9' => continue,
520 '.' => {
521 self.cursor += 1;
522 self.state = .number_post_dot;
523 continue :state_loop;
524 },
525 'e', 'E' => {
526 self.cursor += 1;
527 self.state = .number_post_e;
528 continue :state_loop;
529 },
530 else => {
531 self.state = .post_value;
532 return Token{ .number = self.takeValueSlice() };
533 },
534 }
535 }
536 return self.endOfBufferInNumber(true);
537 },
538 .number_post_dot => {
539 if (self.cursor >= self.input.len) return self.endOfBufferInNumber(false);
540 switch (self.input[self.cursor]) {
541 '0'...'9' => {
542 self.cursor += 1;
543 self.state = .number_frac;
544 continue :state_loop;
545 },
546 else => return error.SyntaxError,
547 }
548 },
549 .number_frac => {
550 while (self.cursor < self.input.len) : (self.cursor += 1) {
551 switch (self.input[self.cursor]) {
552 '0'...'9' => continue,
553 'e', 'E' => {
554 self.cursor += 1;
555 self.state = .number_post_e;
556 continue :state_loop;
557 },
558 else => {
559 self.state = .post_value;
560 return Token{ .number = self.takeValueSlice() };
561 },
562 }
563 }
564 return self.endOfBufferInNumber(true);
565 },
566 .number_post_e => {
567 if (self.cursor >= self.input.len) return self.endOfBufferInNumber(false);
568 switch (self.input[self.cursor]) {
569 '0'...'9' => {
570 self.cursor += 1;
571 self.state = .number_exp;
572 continue :state_loop;
573 },
574 '+', '-' => {
575 self.cursor += 1;
576 self.state = .number_post_e_sign;
577 continue :state_loop;
578 },
579 else => return error.SyntaxError,
580 }
581 },
582 .number_post_e_sign => {
583 if (self.cursor >= self.input.len) return self.endOfBufferInNumber(false);
584 switch (self.input[self.cursor]) {
585 '0'...'9' => {
586 self.cursor += 1;
587 self.state = .number_exp;
588 continue :state_loop;
589 },
590 else => return error.SyntaxError,
591 }
592 },
593 .number_exp => {
594 while (self.cursor < self.input.len) : (self.cursor += 1) {
595 switch (self.input[self.cursor]) {
596 '0'...'9' => continue,
597 else => {
598 self.state = .post_value;
599 return Token{ .number = self.takeValueSlice() };
600 },
601 }
602 }
603 return self.endOfBufferInNumber(true);
604 },
605
606 .string => {
607 while (self.cursor < self.input.len) : (self.cursor += 1) {
608 switch (self.input[self.cursor]) {
609 0...0x1f => return error.SyntaxError, // Bare ASCII control code in string.
610
611 // ASCII plain text.
612 0x20...('"' - 1), ('"' + 1)...('\\' - 1), ('\\' + 1)...0x7F => continue,
613
614 // Special characters.
615 '"' => {
616 const result = Token{ .string = self.takeValueSlice() };
617 self.cursor += 1;
618 self.state = .post_value;
619 return result;
620 },
621 '\\' => {
622 const slice = self.takeValueSlice();
623 self.cursor += 1;
624 self.state = .string_backslash;
625 if (slice.len > 0) return Token{ .partial_string = slice };
626 continue :state_loop;
627 },
628
629 // UTF-8 validation.
630 // See http://unicode.org/mail-arch/unicode-ml/y2003-m02/att-0467/01-The_Algorithm_to_Valide_an_UTF-8_String
631 0xC2...0xDF => {
632 self.cursor += 1;
633 self.state = .string_utf8_last_byte;
634 continue :state_loop;
635 },
636 0xE0 => {
637 self.cursor += 1;
638 self.state = .string_utf8_second_to_last_byte_guard_against_overlong;
639 continue :state_loop;
640 },
641 0xE1...0xEC, 0xEE...0xEF => {
642 self.cursor += 1;
643 self.state = .string_utf8_second_to_last_byte;
644 continue :state_loop;
645 },
646 0xED => {
647 self.cursor += 1;
648 self.state = .string_utf8_second_to_last_byte_guard_against_surrogate_half;
649 continue :state_loop;
650 },
651 0xF0 => {
652 self.cursor += 1;
653 self.state = .string_utf8_third_to_last_byte_guard_against_overlong;
654 continue :state_loop;
655 },
656 0xF1...0xF3 => {
657 self.cursor += 1;
658 self.state = .string_utf8_third_to_last_byte;
659 continue :state_loop;
660 },
661 0xF4 => {
662 self.cursor += 1;
663 self.state = .string_utf8_third_to_last_byte_guard_against_too_large;
664 continue :state_loop;
665 },
666 0x80...0xC1, 0xF5...0xFF => return error.SyntaxError, // Invalid UTF-8.
667 }
668 }
669 if (self.is_end_of_input) return error.UnexpectedEndOfInput;
670 const slice = self.takeValueSlice();
671 if (slice.len > 0) return Token{ .partial_string = slice };
672 return error.BufferUnderrun;
673 },
674 .string_backslash => {
675 if (self.cursor >= self.input.len) return self.endOfBufferInString();
676 switch (self.input[self.cursor]) {
677 '"', '\\', '/' => {
678 // Since these characters now represent themselves literally,
679 // we can simply begin the next plaintext slice here.
680 self.value_start = self.cursor;
681 self.cursor += 1;
682 self.state = .string;
683 continue :state_loop;
684 },
685 'b' => {
686 self.cursor += 1;
687 self.value_start = self.cursor;
688 self.state = .string;
689 return Token{ .partial_string_escaped_1 = [_]u8{0x08} };
690 },
691 'f' => {
692 self.cursor += 1;
693 self.value_start = self.cursor;
694 self.state = .string;
695 return Token{ .partial_string_escaped_1 = [_]u8{0x0c} };
696 },
697 'n' => {
698 self.cursor += 1;
699 self.value_start = self.cursor;
700 self.state = .string;
701 return Token{ .partial_string_escaped_1 = [_]u8{'\n'} };
702 },
703 'r' => {
704 self.cursor += 1;
705 self.value_start = self.cursor;
706 self.state = .string;
707 return Token{ .partial_string_escaped_1 = [_]u8{'\r'} };
708 },
709 't' => {
710 self.cursor += 1;
711 self.value_start = self.cursor;
712 self.state = .string;
713 return Token{ .partial_string_escaped_1 = [_]u8{'\t'} };
714 },
715 'u' => {
716 self.cursor += 1;
717 self.state = .string_backslash_u;
718 continue :state_loop;
719 },
720 else => return error.SyntaxError,
721 }
722 },
723 .string_backslash_u => {
724 if (self.cursor >= self.input.len) return self.endOfBufferInString();
725 const c = self.input[self.cursor];
726 switch (c) {
727 '0'...'9' => {
728 self.utf16_code_units[0] = @as(u16, c - '0') << 12;
729 },
730 'A'...'F' => {
731 self.utf16_code_units[0] = @as(u16, c - 'A' + 10) << 12;
732 },
733 'a'...'f' => {
734 self.utf16_code_units[0] = @as(u16, c - 'a' + 10) << 12;
735 },
736 else => return error.SyntaxError,
737 }
738 self.cursor += 1;
739 self.state = .string_backslash_u_1;
740 continue :state_loop;
741 },
742 .string_backslash_u_1 => {
743 if (self.cursor >= self.input.len) return self.endOfBufferInString();
744 const c = self.input[self.cursor];
745 switch (c) {
746 '0'...'9' => {
747 self.utf16_code_units[0] |= @as(u16, c - '0') << 8;
748 },
749 'A'...'F' => {
750 self.utf16_code_units[0] |= @as(u16, c - 'A' + 10) << 8;
751 },
752 'a'...'f' => {
753 self.utf16_code_units[0] |= @as(u16, c - 'a' + 10) << 8;
754 },
755 else => return error.SyntaxError,
756 }
757 self.cursor += 1;
758 self.state = .string_backslash_u_2;
759 continue :state_loop;
760 },
761 .string_backslash_u_2 => {
762 if (self.cursor >= self.input.len) return self.endOfBufferInString();
763 const c = self.input[self.cursor];
764 switch (c) {
765 '0'...'9' => {
766 self.utf16_code_units[0] |= @as(u16, c - '0') << 4;
767 },
768 'A'...'F' => {
769 self.utf16_code_units[0] |= @as(u16, c - 'A' + 10) << 4;
770 },
771 'a'...'f' => {
772 self.utf16_code_units[0] |= @as(u16, c - 'a' + 10) << 4;
773 },
774 else => return error.SyntaxError,
775 }
776 self.cursor += 1;
777 self.state = .string_backslash_u_3;
778 continue :state_loop;
779 },
780 .string_backslash_u_3 => {
781 if (self.cursor >= self.input.len) return self.endOfBufferInString();
782 const c = self.input[self.cursor];
783 switch (c) {
784 '0'...'9' => {
785 self.utf16_code_units[0] |= c - '0';
786 },
787 'A'...'F' => {
788 self.utf16_code_units[0] |= c - 'A' + 10;
789 },
790 'a'...'f' => {
791 self.utf16_code_units[0] |= c - 'a' + 10;
792 },
793 else => return error.SyntaxError,
794 }
795 self.cursor += 1;
796 if (std.unicode.utf16IsHighSurrogate(self.utf16_code_units[0])) {
797 self.state = .string_surrogate_half;
798 continue :state_loop;
799 } else if (std.unicode.utf16IsLowSurrogate(self.utf16_code_units[0])) {
800 return error.SyntaxError; // Unexpected low surrogate half.
801 } else {
802 self.value_start = self.cursor;
803 self.state = .string;
804 return partialStringCodepoint(self.utf16_code_units[0]);
805 }
806 },
807 .string_surrogate_half => {
808 if (self.cursor >= self.input.len) return self.endOfBufferInString();
809 switch (self.input[self.cursor]) {
810 '\\' => {
811 self.cursor += 1;
812 self.state = .string_surrogate_half_backslash;
813 continue :state_loop;
814 },
815 else => return error.SyntaxError, // Expected low surrogate half.
816 }
817 },
818 .string_surrogate_half_backslash => {
819 if (self.cursor >= self.input.len) return self.endOfBufferInString();
820 switch (self.input[self.cursor]) {
821 'u' => {
822 self.cursor += 1;
823 self.state = .string_surrogate_half_backslash_u;
824 continue :state_loop;
825 },
826 else => return error.SyntaxError, // Expected low surrogate half.
827 }
828 },
829 .string_surrogate_half_backslash_u => {
830 if (self.cursor >= self.input.len) return self.endOfBufferInString();
831 switch (self.input[self.cursor]) {
832 'D', 'd' => {
833 self.cursor += 1;
834 self.utf16_code_units[1] = 0xD << 12;
835 self.state = .string_surrogate_half_backslash_u_1;
836 continue :state_loop;
837 },
838 else => return error.SyntaxError, // Expected low surrogate half.
839 }
840 },
841 .string_surrogate_half_backslash_u_1 => {
842 if (self.cursor >= self.input.len) return self.endOfBufferInString();
843 const c = self.input[self.cursor];
844 switch (c) {
845 'C'...'F' => {
846 self.cursor += 1;
847 self.utf16_code_units[1] |= @as(u16, c - 'A' + 10) << 8;
848 self.state = .string_surrogate_half_backslash_u_2;
849 continue :state_loop;
850 },
851 'c'...'f' => {
852 self.cursor += 1;
853 self.utf16_code_units[1] |= @as(u16, c - 'a' + 10) << 8;
854 self.state = .string_surrogate_half_backslash_u_2;
855 continue :state_loop;
856 },
857 else => return error.SyntaxError, // Expected low surrogate half.
858 }
859 },
860 .string_surrogate_half_backslash_u_2 => {
861 if (self.cursor >= self.input.len) return self.endOfBufferInString();
862 const c = self.input[self.cursor];
863 switch (c) {
864 '0'...'9' => {
865 self.cursor += 1;
866 self.utf16_code_units[1] |= @as(u16, c - '0') << 4;
867 self.state = .string_surrogate_half_backslash_u_3;
868 continue :state_loop;
869 },
870 'A'...'F' => {
871 self.cursor += 1;
872 self.utf16_code_units[1] |= @as(u16, c - 'A' + 10) << 4;
873 self.state = .string_surrogate_half_backslash_u_3;
874 continue :state_loop;
875 },
876 'a'...'f' => {
877 self.cursor += 1;
878 self.utf16_code_units[1] |= @as(u16, c - 'a' + 10) << 4;
879 self.state = .string_surrogate_half_backslash_u_3;
880 continue :state_loop;
881 },
882 else => return error.SyntaxError,
883 }
884 },
885 .string_surrogate_half_backslash_u_3 => {
886 if (self.cursor >= self.input.len) return self.endOfBufferInString();
887 const c = self.input[self.cursor];
888 switch (c) {
889 '0'...'9' => {
890 self.utf16_code_units[1] |= c - '0';
891 },
892 'A'...'F' => {
893 self.utf16_code_units[1] |= c - 'A' + 10;
894 },
895 'a'...'f' => {
896 self.utf16_code_units[1] |= c - 'a' + 10;
897 },
898 else => return error.SyntaxError,
899 }
900 self.cursor += 1;
901 self.value_start = self.cursor;
902 self.state = .string;
903 const code_point = std.unicode.utf16DecodeSurrogatePair(&self.utf16_code_units) catch unreachable;
904 return partialStringCodepoint(code_point);
905 },
906
907 .string_utf8_last_byte => {
908 if (self.cursor >= self.input.len) return self.endOfBufferInString();
909 switch (self.input[self.cursor]) {
910 0x80...0xBF => {
911 self.cursor += 1;
912 self.state = .string;
913 continue :state_loop;
914 },
915 else => return error.SyntaxError, // Invalid UTF-8.
916 }
917 },
918 .string_utf8_second_to_last_byte => {
919 if (self.cursor >= self.input.len) return self.endOfBufferInString();
920 switch (self.input[self.cursor]) {
921 0x80...0xBF => {
922 self.cursor += 1;
923 self.state = .string_utf8_last_byte;
924 continue :state_loop;
925 },
926 else => return error.SyntaxError, // Invalid UTF-8.
927 }
928 },
929 .string_utf8_second_to_last_byte_guard_against_overlong => {
930 if (self.cursor >= self.input.len) return self.endOfBufferInString();
931 switch (self.input[self.cursor]) {
932 0xA0...0xBF => {
933 self.cursor += 1;
934 self.state = .string_utf8_last_byte;
935 continue :state_loop;
936 },
937 else => return error.SyntaxError, // Invalid UTF-8.
938 }
939 },
940 .string_utf8_second_to_last_byte_guard_against_surrogate_half => {
941 if (self.cursor >= self.input.len) return self.endOfBufferInString();
942 switch (self.input[self.cursor]) {
943 0x80...0x9F => {
944 self.cursor += 1;
945 self.state = .string_utf8_last_byte;
946 continue :state_loop;
947 },
948 else => return error.SyntaxError, // Invalid UTF-8.
949 }
950 },
951 .string_utf8_third_to_last_byte => {
952 if (self.cursor >= self.input.len) return self.endOfBufferInString();
953 switch (self.input[self.cursor]) {
954 0x80...0xBF => {
955 self.cursor += 1;
956 self.state = .string_utf8_second_to_last_byte;
957 continue :state_loop;
958 },
959 else => return error.SyntaxError, // Invalid UTF-8.
960 }
961 },
962 .string_utf8_third_to_last_byte_guard_against_overlong => {
963 if (self.cursor >= self.input.len) return self.endOfBufferInString();
964 switch (self.input[self.cursor]) {
965 0x90...0xBF => {
966 self.cursor += 1;
967 self.state = .string_utf8_second_to_last_byte;
968 continue :state_loop;
969 },
970 else => return error.SyntaxError, // Invalid UTF-8.
971 }
972 },
973 .string_utf8_third_to_last_byte_guard_against_too_large => {
974 if (self.cursor >= self.input.len) return self.endOfBufferInString();
975 switch (self.input[self.cursor]) {
976 0x80...0x8F => {
977 self.cursor += 1;
978 self.state = .string_utf8_second_to_last_byte;
979 continue :state_loop;
980 },
981 else => return error.SyntaxError, // Invalid UTF-8.
982 }
983 },
984
985 .literal_t => {
986 switch (try self.expectByte()) {
987 'r' => {
988 self.cursor += 1;
989 self.state = .literal_tr;
990 continue :state_loop;
991 },
992 else => return error.SyntaxError,
993 }
994 },
995 .literal_tr => {
996 switch (try self.expectByte()) {
997 'u' => {
998 self.cursor += 1;
999 self.state = .literal_tru;
1000 continue :state_loop;
1001 },
1002 else => return error.SyntaxError,
1003 }
1004 },
1005 .literal_tru => {
1006 switch (try self.expectByte()) {
1007 'e' => {
1008 self.cursor += 1;
1009 self.state = .post_value;
1010 return .true;
1011 },
1012 else => return error.SyntaxError,
1013 }
1014 },
1015 .literal_f => {
1016 switch (try self.expectByte()) {
1017 'a' => {
1018 self.cursor += 1;
1019 self.state = .literal_fa;
1020 continue :state_loop;
1021 },
1022 else => return error.SyntaxError,
1023 }
1024 },
1025 .literal_fa => {
1026 switch (try self.expectByte()) {
1027 'l' => {
1028 self.cursor += 1;
1029 self.state = .literal_fal;
1030 continue :state_loop;
1031 },
1032 else => return error.SyntaxError,
1033 }
1034 },
1035 .literal_fal => {
1036 switch (try self.expectByte()) {
1037 's' => {
1038 self.cursor += 1;
1039 self.state = .literal_fals;
1040 continue :state_loop;
1041 },
1042 else => return error.SyntaxError,
1043 }
1044 },
1045 .literal_fals => {
1046 switch (try self.expectByte()) {
1047 'e' => {
1048 self.cursor += 1;
1049 self.state = .post_value;
1050 return .false;
1051 },
1052 else => return error.SyntaxError,
1053 }
1054 },
1055 .literal_n => {
1056 switch (try self.expectByte()) {
1057 'u' => {
1058 self.cursor += 1;
1059 self.state = .literal_nu;
1060 continue :state_loop;
1061 },
1062 else => return error.SyntaxError,
1063 }
1064 },
1065 .literal_nu => {
1066 switch (try self.expectByte()) {
1067 'l' => {
1068 self.cursor += 1;
1069 self.state = .literal_nul;
1070 continue :state_loop;
1071 },
1072 else => return error.SyntaxError,
1073 }
1074 },
1075 .literal_nul => {
1076 switch (try self.expectByte()) {
1077 'l' => {
1078 self.cursor += 1;
1079 self.state = .post_value;
1080 return .null;
1081 },
1082 else => return error.SyntaxError,
1083 }
1084 },
1085 }
1086 unreachable;
1087 }
1088}
1089
1090/// Seeks ahead in the input until the first byte of the next token (or the end of the input)
1091/// determines which type of token will be returned from the next `next*()` call.
1092/// This function is idempotent, only advancing past commas, colons, and inter-token whitespace.
1093pub fn peekNextTokenType(self: *@This()) PeekError!TokenType {
1094 state_loop: while (true) {
1095 switch (self.state) {
1096 .value => {
1097 switch (try self.skipWhitespaceExpectByte()) {
1098 '{' => return .object_begin,
1099 '[' => return .array_begin,
1100 '"' => return .string,
1101 '-', '0'...'9' => return .number,
1102 't' => return .true,
1103 'f' => return .false,
1104 'n' => return .null,
1105 else => return error.SyntaxError,
1106 }
1107 },
1108
1109 .post_value => {
1110 if (try self.skipWhitespaceCheckEnd()) return .end_of_document;
1111
1112 const c = self.input[self.cursor];
1113 if (self.string_is_object_key) {
1114 self.string_is_object_key = false;
1115 switch (c) {
1116 ':' => {
1117 self.cursor += 1;
1118 self.state = .value;
1119 continue :state_loop;
1120 },
1121 else => return error.SyntaxError,
1122 }
1123 }
1124
1125 switch (c) {
1126 '}' => return .object_end,
1127 ']' => return .array_end,
1128 ',' => {
1129 switch (self.stack.peek()) {
1130 OBJECT_MODE => {
1131 self.state = .object_post_comma;
1132 },
1133 ARRAY_MODE => {
1134 self.state = .value;
1135 },
1136 }
1137 self.cursor += 1;
1138 continue :state_loop;
1139 },
1140 else => return error.SyntaxError,
1141 }
1142 },
1143
1144 .object_start => {
1145 switch (try self.skipWhitespaceExpectByte()) {
1146 '"' => return .string,
1147 '}' => return .object_end,
1148 else => return error.SyntaxError,
1149 }
1150 },
1151 .object_post_comma => {
1152 switch (try self.skipWhitespaceExpectByte()) {
1153 '"' => return .string,
1154 else => return error.SyntaxError,
1155 }
1156 },
1157
1158 .array_start => {
1159 switch (try self.skipWhitespaceExpectByte()) {
1160 ']' => return .array_end,
1161 else => {
1162 self.state = .value;
1163 continue :state_loop;
1164 },
1165 }
1166 },
1167
1168 .number_minus,
1169 .number_leading_zero,
1170 .number_int,
1171 .number_post_dot,
1172 .number_frac,
1173 .number_post_e,
1174 .number_post_e_sign,
1175 .number_exp,
1176 => return .number,
1177
1178 .string,
1179 .string_backslash,
1180 .string_backslash_u,
1181 .string_backslash_u_1,
1182 .string_backslash_u_2,
1183 .string_backslash_u_3,
1184 .string_surrogate_half,
1185 .string_surrogate_half_backslash,
1186 .string_surrogate_half_backslash_u,
1187 .string_surrogate_half_backslash_u_1,
1188 .string_surrogate_half_backslash_u_2,
1189 .string_surrogate_half_backslash_u_3,
1190 => return .string,
1191
1192 .string_utf8_last_byte,
1193 .string_utf8_second_to_last_byte,
1194 .string_utf8_second_to_last_byte_guard_against_overlong,
1195 .string_utf8_second_to_last_byte_guard_against_surrogate_half,
1196 .string_utf8_third_to_last_byte,
1197 .string_utf8_third_to_last_byte_guard_against_overlong,
1198 .string_utf8_third_to_last_byte_guard_against_too_large,
1199 => return .string,
1200
1201 .literal_t,
1202 .literal_tr,
1203 .literal_tru,
1204 => return .true,
1205 .literal_f,
1206 .literal_fa,
1207 .literal_fal,
1208 .literal_fals,
1209 => return .false,
1210 .literal_n,
1211 .literal_nu,
1212 .literal_nul,
1213 => return .null,
1214 }
1215 unreachable;
1216 }
1217}
1218
1219const State = enum {
1220 value,
1221 post_value,
1222
1223 object_start,
1224 object_post_comma,
1225
1226 array_start,
1227
1228 number_minus,
1229 number_leading_zero,
1230 number_int,
1231 number_post_dot,
1232 number_frac,
1233 number_post_e,
1234 number_post_e_sign,
1235 number_exp,
1236
1237 string,
1238 string_backslash,
1239 string_backslash_u,
1240 string_backslash_u_1,
1241 string_backslash_u_2,
1242 string_backslash_u_3,
1243 string_surrogate_half,
1244 string_surrogate_half_backslash,
1245 string_surrogate_half_backslash_u,
1246 string_surrogate_half_backslash_u_1,
1247 string_surrogate_half_backslash_u_2,
1248 string_surrogate_half_backslash_u_3,
1249
1250 // From http://unicode.org/mail-arch/unicode-ml/y2003-m02/att-0467/01-The_Algorithm_to_Valide_an_UTF-8_String
1251 string_utf8_last_byte, // State A
1252 string_utf8_second_to_last_byte, // State B
1253 string_utf8_second_to_last_byte_guard_against_overlong, // State C
1254 string_utf8_second_to_last_byte_guard_against_surrogate_half, // State D
1255 string_utf8_third_to_last_byte, // State E
1256 string_utf8_third_to_last_byte_guard_against_overlong, // State F
1257 string_utf8_third_to_last_byte_guard_against_too_large, // State G
1258
1259 literal_t,
1260 literal_tr,
1261 literal_tru,
1262 literal_f,
1263 literal_fa,
1264 literal_fal,
1265 literal_fals,
1266 literal_n,
1267 literal_nu,
1268 literal_nul,
1269};
1270
1271fn expectByte(self: *const @This()) !u8 {
1272 if (self.cursor < self.input.len) {
1273 return self.input[self.cursor];
1274 }
1275 // No byte.
1276 if (self.is_end_of_input) return error.UnexpectedEndOfInput;
1277 return error.BufferUnderrun;
1278}
1279
1280fn skipWhitespace(self: *@This()) void {
1281 while (self.cursor < self.input.len) : (self.cursor += 1) {
1282 switch (self.input[self.cursor]) {
1283 // Whitespace
1284 ' ', '\t', '\r' => continue,
1285 '\n' => {
1286 if (self.diagnostics) |diag| {
1287 diag.line_number += 1;
1288 // This will count the newline itself,
1289 // which means a straight-forward subtraction will give a 1-based column number.
1290 diag.line_start_cursor = self.cursor;
1291 }
1292 continue;
1293 },
1294 else => return,
1295 }
1296 }
1297}
1298
1299fn skipWhitespaceExpectByte(self: *@This()) !u8 {
1300 self.skipWhitespace();
1301 return self.expectByte();
1302}
1303
1304fn skipWhitespaceCheckEnd(self: *@This()) !bool {
1305 self.skipWhitespace();
1306 if (self.cursor >= self.input.len) {
1307 // End of buffer.
1308 if (self.is_end_of_input) {
1309 // End of everything.
1310 if (self.stackHeight() == 0) {
1311 // We did it!
1312 return true;
1313 }
1314 return error.UnexpectedEndOfInput;
1315 }
1316 return error.BufferUnderrun;
1317 }
1318 if (self.stackHeight() == 0) return error.SyntaxError;
1319 return false;
1320}
1321
1322fn takeValueSlice(self: *@This()) []const u8 {
1323 const slice = self.input[self.value_start..self.cursor];
1324 self.value_start = self.cursor;
1325 return slice;
1326}
1327fn takeValueSliceMinusTrailingOffset(self: *@This(), trailing_negative_offset: usize) []const u8 {
1328 // Check if the escape sequence started before the current input buffer.
1329 // (The algebra here is awkward to avoid unsigned underflow,
1330 // but it's just making sure the slice on the next line isn't UB.)
1331 if (self.cursor <= self.value_start + trailing_negative_offset) return "";
1332 const slice = self.input[self.value_start .. self.cursor - trailing_negative_offset];
1333 // When trailing_negative_offset is non-zero, setting self.value_start doesn't matter,
1334 // because we always set it again while emitting the .partial_string_escaped_*.
1335 self.value_start = self.cursor;
1336 return slice;
1337}
1338
1339fn endOfBufferInNumber(self: *@This(), allow_end: bool) !Token {
1340 const slice = self.takeValueSlice();
1341 if (self.is_end_of_input) {
1342 if (!allow_end) return error.UnexpectedEndOfInput;
1343 self.state = .post_value;
1344 return Token{ .number = slice };
1345 }
1346 if (slice.len == 0) return error.BufferUnderrun;
1347 return Token{ .partial_number = slice };
1348}
1349
1350fn endOfBufferInString(self: *@This()) !Token {
1351 if (self.is_end_of_input) return error.UnexpectedEndOfInput;
1352 const slice = self.takeValueSliceMinusTrailingOffset(switch (self.state) {
1353 // Don't include the escape sequence in the partial string.
1354 .string_backslash => 1,
1355 .string_backslash_u => 2,
1356 .string_backslash_u_1 => 3,
1357 .string_backslash_u_2 => 4,
1358 .string_backslash_u_3 => 5,
1359 .string_surrogate_half => 6,
1360 .string_surrogate_half_backslash => 7,
1361 .string_surrogate_half_backslash_u => 8,
1362 .string_surrogate_half_backslash_u_1 => 9,
1363 .string_surrogate_half_backslash_u_2 => 10,
1364 .string_surrogate_half_backslash_u_3 => 11,
1365
1366 // Include everything up to the cursor otherwise.
1367 .string,
1368 .string_utf8_last_byte,
1369 .string_utf8_second_to_last_byte,
1370 .string_utf8_second_to_last_byte_guard_against_overlong,
1371 .string_utf8_second_to_last_byte_guard_against_surrogate_half,
1372 .string_utf8_third_to_last_byte,
1373 .string_utf8_third_to_last_byte_guard_against_overlong,
1374 .string_utf8_third_to_last_byte_guard_against_too_large,
1375 => 0,
1376
1377 else => unreachable,
1378 });
1379 if (slice.len == 0) return error.BufferUnderrun;
1380 return Token{ .partial_string = slice };
1381}
1382
1383fn partialStringCodepoint(code_point: u21) Token {
1384 var buf: [4]u8 = undefined;
1385 switch (std.unicode.utf8Encode(code_point, &buf) catch unreachable) {
1386 1 => return Token{ .partial_string_escaped_1 = buf[0..1].* },
1387 2 => return Token{ .partial_string_escaped_2 = buf[0..2].* },
1388 3 => return Token{ .partial_string_escaped_3 = buf[0..3].* },
1389 4 => return Token{ .partial_string_escaped_4 = buf[0..4].* },
1390 else => unreachable,
1391 }
1392}
1393
1394/// Scan the input and check for malformed JSON.
1395/// On `SyntaxError` or `UnexpectedEndOfInput`, returns `false`.
1396/// Returns any errors from the allocator as-is, which is unlikely,
1397/// but can be caused by extreme nesting depth in the input.
1398pub fn validate(allocator: Allocator, s: []const u8) Allocator.Error!bool {
1399 var scanner = Scanner.initCompleteInput(allocator, s);
1400 defer scanner.deinit();
1401
1402 while (true) {
1403 const token = scanner.next() catch |err| switch (err) {
1404 error.SyntaxError, error.UnexpectedEndOfInput => return false,
1405 error.OutOfMemory => return error.OutOfMemory,
1406 error.BufferUnderrun => unreachable,
1407 };
1408 if (token == .end_of_document) break;
1409 }
1410
1411 return true;
1412}
1413
1414/// The parsing errors are divided into two categories:
1415/// * `SyntaxError` is for clearly malformed JSON documents,
1416/// such as giving an input document that isn't JSON at all.
1417/// * `UnexpectedEndOfInput` is for signaling that everything's been
1418/// valid so far, but the input appears to be truncated for some reason.
1419/// Note that a completely empty (or whitespace-only) input will give `UnexpectedEndOfInput`.
1420pub const Error = error{ SyntaxError, UnexpectedEndOfInput };
1421
1422/// Used by `json.reader`.
1423pub const default_buffer_size = 0x1000;
1424
1425/// The tokens emitted by `std.json.Scanner` and `std.json.Reader` `.next*()` functions follow this grammar:
1426/// ```
1427/// <document> = <value> .end_of_document
1428/// <value> =
1429/// | <object>
1430/// | <array>
1431/// | <number>
1432/// | <string>
1433/// | .true
1434/// | .false
1435/// | .null
1436/// <object> = .object_begin ( <string> <value> )* .object_end
1437/// <array> = .array_begin ( <value> )* .array_end
1438/// <number> = <It depends. See below.>
1439/// <string> = <It depends. See below.>
1440/// ```
1441///
1442/// What you get for `<number>` and `<string>` values depends on which `next*()` method you call:
1443///
1444/// ```
1445/// next():
1446/// <number> = ( .partial_number )* .number
1447/// <string> = ( <partial_string> )* .string
1448/// <partial_string> =
1449/// | .partial_string
1450/// | .partial_string_escaped_1
1451/// | .partial_string_escaped_2
1452/// | .partial_string_escaped_3
1453/// | .partial_string_escaped_4
1454///
1455/// nextAlloc*(..., .alloc_always):
1456/// <number> = .allocated_number
1457/// <string> = .allocated_string
1458///
1459/// nextAlloc*(..., .alloc_if_needed):
1460/// <number> =
1461/// | .number
1462/// | .allocated_number
1463/// <string> =
1464/// | .string
1465/// | .allocated_string
1466/// ```
1467///
1468/// For all tokens with a `[]const u8`, `[]u8`, or `[n]u8` payload, the payload represents the content of the value.
1469/// For number values, this is the representation of the number exactly as it appears in the input.
1470/// For strings, this is the content of the string after resolving escape sequences.
1471///
1472/// For `.allocated_number` and `.allocated_string`, the `[]u8` payloads are allocations made with the given allocator.
1473/// You are responsible for managing that memory. `json.Reader.deinit()` does *not* free those allocations.
1474///
1475/// The `.partial_*` tokens indicate that a value spans multiple input buffers or that a string contains escape sequences.
1476/// To get a complete value in memory, you need to concatenate the values yourself.
1477/// Calling `nextAlloc*()` does this for you, and returns an `.allocated_*` token with the result.
1478///
1479/// For tokens with a `[]const u8` payload, the payload is a slice into the current input buffer.
1480/// The memory may become undefined during the next call to `json.Scanner.feedInput()`
1481/// or any `json.Reader` method whose return error set includes `json.Error`.
1482/// To keep the value persistently, it recommended to make a copy or to use `.alloc_always`,
1483/// which makes a copy for you.
1484///
1485/// Note that `.number` and `.string` tokens that follow `.partial_*` tokens may have `0` length to indicate that
1486/// the previously partial value is completed with no additional bytes.
1487/// (This can happen when the break between input buffers happens to land on the exact end of a value. E.g. `"[1234"`, `"]"`.)
1488/// `.partial_*` tokens never have `0` length.
1489///
1490/// The recommended strategy for using the different `next*()` methods is something like this:
1491///
1492/// When you're expecting an object key, use `.alloc_if_needed`.
1493/// You often don't need a copy of the key string to persist; you might just check which field it is.
1494/// In the case that the key happens to require an allocation, free it immediately after checking it.
1495///
1496/// When you're expecting a meaningful string value (such as on the right of a `:`),
1497/// use `.alloc_always` in order to keep the value valid throughout parsing the rest of the document.
1498///
1499/// When you're expecting a number value, use `.alloc_if_needed`.
1500/// You're probably going to be parsing the string representation of the number into a numeric representation,
1501/// so you need the complete string representation only temporarily.
1502///
1503/// When you're skipping an unrecognized value, use `skipValue()`.
1504pub const Token = union(enum) {
1505 object_begin,
1506 object_end,
1507 array_begin,
1508 array_end,
1509
1510 true,
1511 false,
1512 null,
1513
1514 number: []const u8,
1515 partial_number: []const u8,
1516 allocated_number: []u8,
1517
1518 string: []const u8,
1519 partial_string: []const u8,
1520 partial_string_escaped_1: [1]u8,
1521 partial_string_escaped_2: [2]u8,
1522 partial_string_escaped_3: [3]u8,
1523 partial_string_escaped_4: [4]u8,
1524 allocated_string: []u8,
1525
1526 end_of_document,
1527};
1528
1529/// This is only used in `peekNextTokenType()` and gives a categorization based on the first byte of the next token that will be emitted from a `next*()` call.
1530pub const TokenType = enum {
1531 object_begin,
1532 object_end,
1533 array_begin,
1534 array_end,
1535 true,
1536 false,
1537 null,
1538 number,
1539 string,
1540 end_of_document,
1541};
1542
1543/// To enable diagnostics, declare `var diagnostics = Diagnostics{};` then call `source.enableDiagnostics(&diagnostics);`
1544/// where `source` is either a `std.json.Reader` or a `std.json.Scanner` that has just been initialized.
1545/// At any time, notably just after an error, call `getLine()`, `getColumn()`, and/or `getByteOffset()`
1546/// to get meaningful information from this.
1547pub const Diagnostics = struct {
1548 line_number: u64 = 1,
1549 line_start_cursor: usize = @as(usize, @bitCast(@as(isize, -1))), // Start just "before" the input buffer to get a 1-based column for line 1.
1550 total_bytes_before_current_input: u64 = 0,
1551 cursor_pointer: *const usize = undefined,
1552
1553 /// Starts at 1.
1554 pub fn getLine(self: *const @This()) u64 {
1555 return self.line_number;
1556 }
1557 /// Starts at 1.
1558 pub fn getColumn(self: *const @This()) u64 {
1559 return self.cursor_pointer.* -% self.line_start_cursor;
1560 }
1561 /// Starts at 0. Measures the byte offset since the start of the input.
1562 pub fn getByteOffset(self: *const @This()) u64 {
1563 return self.total_bytes_before_current_input + self.cursor_pointer.*;
1564 }
1565};
1566
1567/// See the documentation for `std.json.Token`.
1568pub const AllocWhen = enum { alloc_if_needed, alloc_always };
1569
1570/// For security, the maximum size allocated to store a single string or number value is limited to 4MiB by default.
1571/// This limit can be specified by calling `nextAllocMax()` instead of `nextAlloc()`.
1572pub const default_max_value_len = 4 * 1024 * 1024;
1573
1574/// All `next*()` methods here handle `error.BufferUnderrun` from `std.json.Scanner`, and then read from the reader.
1575pub const Reader = struct {
1576 scanner: Scanner,
1577 reader: *std.Io.Reader,
1578
1579 /// The allocator is only used to track `[]` and `{}` nesting levels.
1580 pub fn init(allocator: Allocator, io_reader: *std.Io.Reader) @This() {
1581 return .{
1582 .scanner = Scanner.initStreaming(allocator),
1583 .reader = io_reader,
1584 };
1585 }
1586 pub fn deinit(self: *@This()) void {
1587 self.scanner.deinit();
1588 self.* = undefined;
1589 }
1590
1591 /// Calls `std.json.Scanner.enableDiagnostics`.
1592 pub fn enableDiagnostics(self: *@This(), diagnostics: *Diagnostics) void {
1593 self.scanner.enableDiagnostics(diagnostics);
1594 }
1595
1596 pub const NextError = std.Io.Reader.Error || Error || Allocator.Error;
1597 pub const SkipError = Reader.NextError;
1598 pub const AllocError = Reader.NextError || error{ValueTooLong};
1599 pub const PeekError = std.Io.Reader.Error || Error;
1600
1601 /// Equivalent to `nextAllocMax(allocator, when, default_max_value_len);`
1602 /// See also `std.json.Token` for documentation of `nextAlloc*()` function behavior.
1603 pub fn nextAlloc(self: *@This(), allocator: Allocator, when: AllocWhen) Reader.AllocError!Token {
1604 return self.nextAllocMax(allocator, when, default_max_value_len);
1605 }
1606 /// See also `std.json.Token` for documentation of `nextAlloc*()` function behavior.
1607 pub fn nextAllocMax(self: *@This(), allocator: Allocator, when: AllocWhen, max_value_len: usize) Reader.AllocError!Token {
1608 const token_type = try self.peekNextTokenType();
1609 switch (token_type) {
1610 .number, .string => {
1611 var value_list = ArrayList(u8).init(allocator);
1612 errdefer {
1613 value_list.deinit();
1614 }
1615 if (try self.allocNextIntoArrayListMax(&value_list, when, max_value_len)) |slice| {
1616 return if (token_type == .number)
1617 Token{ .number = slice }
1618 else
1619 Token{ .string = slice };
1620 } else {
1621 return if (token_type == .number)
1622 Token{ .allocated_number = try value_list.toOwnedSlice() }
1623 else
1624 Token{ .allocated_string = try value_list.toOwnedSlice() };
1625 }
1626 },
1627
1628 // Simple tokens never alloc.
1629 .object_begin,
1630 .object_end,
1631 .array_begin,
1632 .array_end,
1633 .true,
1634 .false,
1635 .null,
1636 .end_of_document,
1637 => return try self.next(),
1638 }
1639 }
1640
1641 /// Equivalent to `allocNextIntoArrayListMax(value_list, when, default_max_value_len);`
1642 pub fn allocNextIntoArrayList(self: *@This(), value_list: *ArrayList(u8), when: AllocWhen) Reader.AllocError!?[]const u8 {
1643 return self.allocNextIntoArrayListMax(value_list, when, default_max_value_len);
1644 }
1645 /// Calls `std.json.Scanner.allocNextIntoArrayListMax` and handles `error.BufferUnderrun`.
1646 pub fn allocNextIntoArrayListMax(self: *@This(), value_list: *ArrayList(u8), when: AllocWhen, max_value_len: usize) Reader.AllocError!?[]const u8 {
1647 while (true) {
1648 return self.scanner.allocNextIntoArrayListMax(value_list, when, max_value_len) catch |err| switch (err) {
1649 error.BufferUnderrun => {
1650 try self.refillBuffer();
1651 continue;
1652 },
1653 else => |other_err| return other_err,
1654 };
1655 }
1656 }
1657
1658 /// Like `std.json.Scanner.skipValue`, but handles `error.BufferUnderrun`.
1659 pub fn skipValue(self: *@This()) Reader.SkipError!void {
1660 switch (try self.peekNextTokenType()) {
1661 .object_begin, .array_begin => {
1662 try self.skipUntilStackHeight(self.stackHeight());
1663 },
1664 .number, .string => {
1665 while (true) {
1666 switch (try self.next()) {
1667 .partial_number,
1668 .partial_string,
1669 .partial_string_escaped_1,
1670 .partial_string_escaped_2,
1671 .partial_string_escaped_3,
1672 .partial_string_escaped_4,
1673 => continue,
1674
1675 .number, .string => break,
1676
1677 else => unreachable,
1678 }
1679 }
1680 },
1681 .true, .false, .null => {
1682 _ = try self.next();
1683 },
1684
1685 .object_end, .array_end, .end_of_document => unreachable, // Attempt to skip a non-value token.
1686 }
1687 }
1688 /// Like `std.json.Scanner.skipUntilStackHeight()` but handles `error.BufferUnderrun`.
1689 pub fn skipUntilStackHeight(self: *@This(), terminal_stack_height: usize) Reader.NextError!void {
1690 while (true) {
1691 return self.scanner.skipUntilStackHeight(terminal_stack_height) catch |err| switch (err) {
1692 error.BufferUnderrun => {
1693 try self.refillBuffer();
1694 continue;
1695 },
1696 else => |other_err| return other_err,
1697 };
1698 }
1699 }
1700
1701 /// Calls `std.json.Scanner.stackHeight`.
1702 pub fn stackHeight(self: *const @This()) usize {
1703 return self.scanner.stackHeight();
1704 }
1705 /// Calls `std.json.Scanner.ensureTotalStackCapacity`.
1706 pub fn ensureTotalStackCapacity(self: *@This(), height: usize) Allocator.Error!void {
1707 try self.scanner.ensureTotalStackCapacity(height);
1708 }
1709
1710 /// See `std.json.Token` for documentation of this function.
1711 pub fn next(self: *@This()) Reader.NextError!Token {
1712 while (true) {
1713 return self.scanner.next() catch |err| switch (err) {
1714 error.BufferUnderrun => {
1715 try self.refillBuffer();
1716 continue;
1717 },
1718 else => |other_err| return other_err,
1719 };
1720 }
1721 }
1722
1723 /// See `std.json.Scanner.peekNextTokenType()`.
1724 pub fn peekNextTokenType(self: *@This()) Reader.PeekError!TokenType {
1725 while (true) {
1726 return self.scanner.peekNextTokenType() catch |err| switch (err) {
1727 error.BufferUnderrun => {
1728 try self.refillBuffer();
1729 continue;
1730 },
1731 else => |other_err| return other_err,
1732 };
1733 }
1734 }
1735
1736 fn refillBuffer(self: *@This()) std.Io.Reader.Error!void {
1737 const input = self.reader.peekGreedy(1) catch |err| switch (err) {
1738 error.ReadFailed => return error.ReadFailed,
1739 error.EndOfStream => return self.scanner.endInput(),
1740 };
1741 self.reader.toss(input.len);
1742 self.scanner.feedInput(input);
1743 }
1744};
1745
1746const OBJECT_MODE = 0;
1747const ARRAY_MODE = 1;
1748
1749fn appendSlice(list: *std.ArrayList(u8), buf: []const u8, max_value_len: usize) !void {
1750 const new_len = std.math.add(usize, list.items.len, buf.len) catch return error.ValueTooLong;
1751 if (new_len > max_value_len) return error.ValueTooLong;
1752 try list.appendSlice(buf);
1753}
1754
1755/// For the slice you get from a `Token.number` or `Token.allocated_number`,
1756/// this function returns true if the number doesn't contain any fraction or exponent components, and is not `-0`.
1757/// Note, the numeric value encoded by the value may still be an integer, such as `1.0`.
1758/// This function is meant to give a hint about whether integer parsing or float parsing should be used on the value.
1759/// This function will not give meaningful results on non-numeric input.
1760pub fn isNumberFormattedLikeAnInteger(value: []const u8) bool {
1761 if (std.mem.eql(u8, value, "-0")) return false;
1762 return std.mem.indexOfAny(u8, value, ".eE") == null;
1763}
1764
1765test {
1766 _ = @import("./scanner_test.zig");
1767}
lib/std/json/Stringify.zig+23-24
......@@ -248,7 +248,7 @@ test print {
248248 \\ ]
249249 \\}
250250 ;
251 try std.testing.expectEqualStrings(expected, out.getWritten());
251 try std.testing.expectEqualStrings(expected, out.buffered());
252252}
253253
254254/// An alternative to calling `write` that allows you to write directly to the `.writer` field, e.g. with `.writer.writeAll()`.
......@@ -577,7 +577,7 @@ pub fn value(v: anytype, options: Options, writer: *Writer) Error!void {
577577
578578test value {
579579 var out: std.io.Writer.Allocating = .init(std.testing.allocator);
580 const writer = &out.interface;
580 const writer = &out.writer;
581581 defer out.deinit();
582582
583583 const T = struct { a: i32, b: []const u8 };
......@@ -617,9 +617,8 @@ test value {
617617/// Caller owns returned memory.
618618pub fn valueAlloc(gpa: Allocator, v: anytype, options: Options) error{OutOfMemory}![]u8 {
619619 var aw: std.io.Writer.Allocating = .init(gpa);
620 const writer = &aw.interface;
621620 defer aw.deinit();
622 value(v, options, writer) catch return error.OutOfMemory;
621 value(v, options, &aw.writer) catch return error.OutOfMemory;
623622 return aw.toOwnedSlice();
624623}
625624
......@@ -634,23 +633,23 @@ test valueAlloc {
634633 try std.testing.expectEqualStrings(expected, actual);
635634}
636635
637fn outputUnicodeEscape(codepoint: u21, bw: *Writer) Error!void {
636fn outputUnicodeEscape(codepoint: u21, w: *Writer) Error!void {
638637 if (codepoint <= 0xFFFF) {
639638 // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF),
640639 // then it may be represented as a six-character sequence: a reverse solidus, followed
641640 // by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point.
642 try bw.writeAll("\\u");
643 try bw.printInt("x", .{ .width = 4, .fill = '0' }, codepoint);
641 try w.writeAll("\\u");
642 try w.printInt(codepoint, 16, .lower, .{ .width = 4, .fill = '0' });
644643 } else {
645644 assert(codepoint <= 0x10FFFF);
646645 // To escape an extended character that is not in the Basic Multilingual Plane,
647646 // the character is represented as a 12-character sequence, encoding the UTF-16 surrogate pair.
648647 const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800;
649648 const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00;
650 try bw.writeAll("\\u");
651 try bw.printInt("x", .{ .width = 4, .fill = '0' }, high);
652 try bw.writeAll("\\u");
653 try bw.printInt("x", .{ .width = 4, .fill = '0' }, low);
649 try w.writeAll("\\u");
650 try w.printInt(high, 16, .lower, .{ .width = 4, .fill = '0' });
651 try w.writeAll("\\u");
652 try w.printInt(low, 16, .lower, .{ .width = 4, .fill = '0' });
654653 }
655654}
656655
......@@ -723,8 +722,8 @@ test "json write stream" {
723722 try testBasicWriteStream(&w);
724723}
725724
726fn testBasicWriteStream(w: *Stringify) Error!void {
727 w.writer.reset();
725fn testBasicWriteStream(w: *Stringify) !void {
726 w.writer.end = 0;
728727
729728 try w.beginObject();
730729
......@@ -755,19 +754,19 @@ fn testBasicWriteStream(w: *Stringify) Error!void {
755754 \\{
756755 \\ "object": {
757756 \\ "one": 1,
758 \\ "two": 2e0
757 \\ "two": 2
759758 \\ },
760759 \\ "string": "This is a string",
761760 \\ "array": [
762761 \\ "Another string",
763762 \\ 1,
764 \\ 3.5e0
763 \\ 3.5
765764 \\ ],
766765 \\ "int": 10,
767 \\ "float": 3.5e0
766 \\ "float": 3.5
768767 \\}
769768 ;
770 try std.testing.expectEqualStrings(expected, w.writer.getWritten());
769 try std.testing.expectEqualStrings(expected, w.writer.buffered());
771770}
772771
773772fn getJsonObject(allocator: std.mem.Allocator) !std.json.Value {
......@@ -804,12 +803,12 @@ test "stringify basic types" {
804803 try testStringify("null", @as(?u8, null), .{});
805804 try testStringify("null", @as(?*u32, null), .{});
806805 try testStringify("42", 42, .{});
807 try testStringify("4.2e1", 42.0, .{});
806 try testStringify("42", 42.0, .{});
808807 try testStringify("42", @as(u8, 42), .{});
809808 try testStringify("42", @as(u128, 42), .{});
810809 try testStringify("9999999999999999", 9999999999999999, .{});
811 try testStringify("4.2e1", @as(f32, 42), .{});
812 try testStringify("4.2e1", @as(f64, 42), .{});
810 try testStringify("42", @as(f32, 42), .{});
811 try testStringify("42", @as(f64, 42), .{});
813812 try testStringify("\"ItBroke\"", @as(anyerror, error.ItBroke), .{});
814813 try testStringify("\"ItBroke\"", error.ItBroke, .{});
815814}
......@@ -970,9 +969,9 @@ test "stringify struct with custom stringifier" {
970969
971970fn testStringify(expected: []const u8, v: anytype, options: Options) !void {
972971 var buffer: [4096]u8 = undefined;
973 var bw: Writer = .fixed(&buffer);
974 try value(v, options, &bw);
975 try std.testing.expectEqualStrings(expected, bw.getWritten());
972 var w: Writer = .fixed(&buffer);
973 try value(v, options, &w);
974 try std.testing.expectEqualStrings(expected, w.buffered());
976975}
977976
978977test "raw streaming" {
......@@ -996,5 +995,5 @@ test "raw streaming" {
996995 \\ "long key": "long value"
997996 \\}
998997 ;
999 try std.testing.expectEqualStrings(expected, w.writer.getWritten());
998 try std.testing.expectEqualStrings(expected, w.writer.buffered());
1000999}
lib/std/json/dynamic.zig+1-4
......@@ -9,10 +9,7 @@ const json = std.json;
99const ParseOptions = @import("./static.zig").ParseOptions;
1010const ParseError = @import("./static.zig").ParseError;
1111
12const JsonScanner = @import("./scanner.zig").Scanner;
13const AllocWhen = @import("./scanner.zig").AllocWhen;
14const Token = @import("./scanner.zig").Token;
15const isNumberFormattedLikeAnInteger = @import("./scanner.zig").isNumberFormattedLikeAnInteger;
12const isNumberFormattedLikeAnInteger = @import("Scanner.zig").isNumberFormattedLikeAnInteger;
1613
1714pub const ObjectMap = StringArrayHashMap(Value);
1815pub const Array = ArrayList(Value);
lib/std/json/dynamic_test.zig+12-13
......@@ -16,8 +16,7 @@ const parseFromTokenSource = @import("static.zig").parseFromTokenSource;
1616const parseFromValueLeaky = @import("static.zig").parseFromValueLeaky;
1717const ParseOptions = @import("static.zig").ParseOptions;
1818
19const jsonReader = @import("scanner.zig").reader;
20const JsonReader = @import("scanner.zig").Reader;
19const Scanner = @import("Scanner.zig");
2120
2221test "json.parser.dynamic" {
2322 const s =
......@@ -99,8 +98,8 @@ test "write json then parse it" {
9998
10099 try jw.endObject();
101100
102 var fbs: std.io.FixedBufferStream = .{ .buffer = fixed_writer.getWritten() };
103 var json_reader = jsonReader(testing.allocator, fbs.reader());
101 var fbs: std.Io.Reader = .fixed(fixed_writer.buffered());
102 var json_reader: Scanner.Reader = .init(testing.allocator, &fbs);
104103 defer json_reader.deinit();
105104 var parsed = try parseFromTokenSource(Value, testing.allocator, &json_reader, .{});
106105 defer parsed.deinit();
......@@ -263,7 +262,7 @@ test "Value.jsonStringify" {
263262 \\ }
264263 \\]
265264 ;
266 try testing.expectEqualStrings(expected, fixed_writer.getWritten());
265 try testing.expectEqualStrings(expected, fixed_writer.buffered());
267266}
268267
269268test "parseFromValue(std.json.Value,...)" {
......@@ -331,8 +330,8 @@ test "polymorphic parsing" {
331330test "long object value" {
332331 const value = "01234567890123456789";
333332 const doc = "{\"key\":\"" ++ value ++ "\"}";
334 var fbs: std.io.FixedBufferStream = .{ .buffer = doc };
335 var reader = smallBufferJsonReader(testing.allocator, fbs.reader());
333 var fbs: std.Io.Reader = .fixed(doc);
334 var reader = smallBufferJsonReader(testing.allocator, &fbs);
336335 defer reader.deinit();
337336 var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{});
338337 defer parsed.deinit();
......@@ -364,8 +363,8 @@ test "many object keys" {
364363 \\ "k5": "v5"
365364 \\}
366365 ;
367 var fbs: std.io.FixedBufferStream = .{ .buffer = doc };
368 var reader = smallBufferJsonReader(testing.allocator, fbs.reader());
366 var fbs: std.Io.Reader = .fixed(doc);
367 var reader = smallBufferJsonReader(testing.allocator, &fbs);
369368 defer reader.deinit();
370369 var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{});
371370 defer parsed.deinit();
......@@ -379,8 +378,8 @@ test "many object keys" {
379378
380379test "negative zero" {
381380 const doc = "-0";
382 var fbs: std.io.FixedBufferStream = .{ .buffer = doc };
383 var reader = smallBufferJsonReader(testing.allocator, fbs.reader());
381 var fbs: std.Io.Reader = .fixed(doc);
382 var reader = smallBufferJsonReader(testing.allocator, &fbs);
384383 defer reader.deinit();
385384 var parsed = try parseFromTokenSource(Value, testing.allocator, &reader, .{});
386385 defer parsed.deinit();
......@@ -388,6 +387,6 @@ test "negative zero" {
388387 try testing.expect(std.math.isNegativeZero(parsed.value.float));
389388}
390389
391fn smallBufferJsonReader(allocator: Allocator, io_reader: anytype) JsonReader(16, @TypeOf(io_reader)) {
392 return JsonReader(16, @TypeOf(io_reader)).init(allocator, io_reader);
390fn smallBufferJsonReader(allocator: Allocator, io_reader: anytype) Scanner.Reader {
391 return .init(allocator, io_reader);
393392}
lib/std/json/hashmap_test.zig+3-3
......@@ -10,7 +10,7 @@ const parseFromTokenSource = @import("static.zig").parseFromTokenSource;
1010const parseFromValue = @import("static.zig").parseFromValue;
1111const Value = @import("dynamic.zig").Value;
1212
13const jsonReader = @import("./scanner.zig").reader;
13const Scanner = @import("Scanner.zig");
1414
1515const T = struct {
1616 i: i32,
......@@ -39,8 +39,8 @@ test "parse json hashmap while streaming" {
3939 \\ "xyz": {"i": 1, "s": "w"}
4040 \\}
4141 ;
42 var stream: std.io.FixedBufferStream = .{ .buffer = doc };
43 var json_reader = jsonReader(testing.allocator, stream.reader());
42 var stream: std.Io.Reader = .fixed(doc);
43 var json_reader: Scanner.Reader = .init(testing.allocator, &stream);
4444
4545 var parsed = try parseFromTokenSource(
4646 ArrayHashMap(T),
lib/std/json/scanner.zig deleted-1776
......@@ -1,1776 +0,0 @@
1// Notes on standards compliance: https://datatracker.ietf.org/doc/html/rfc8259
2// * RFC 8259 requires JSON documents be valid UTF-8,
3// but makes an allowance for systems that are "part of a closed ecosystem".
4// I have no idea what that's supposed to mean in the context of a standard specification.
5// This implementation requires inputs to be valid UTF-8.
6// * RFC 8259 contradicts itself regarding whether lowercase is allowed in \u hex digits,
7// but this is probably a bug in the spec, and it's clear that lowercase is meant to be allowed.
8// (RFC 5234 defines HEXDIG to only allow uppercase.)
9// * When RFC 8259 refers to a "character", I assume they really mean a "Unicode scalar value".
10// See http://www.unicode.org/glossary/#unicode_scalar_value .
11// * RFC 8259 doesn't explicitly disallow unpaired surrogate halves in \u escape sequences,
12// but vaguely implies that \u escapes are for encoding Unicode "characters" (i.e. Unicode scalar values?),
13// which would mean that unpaired surrogate halves are forbidden.
14// By contrast ECMA-404 (a competing(/compatible?) JSON standard, which JavaScript's JSON.parse() conforms to)
15// explicitly allows unpaired surrogate halves.
16// This implementation forbids unpaired surrogate halves in \u sequences.
17// If a high surrogate half appears in a \u sequence,
18// then a low surrogate half must immediately follow in \u notation.
19// * RFC 8259 allows implementations to "accept non-JSON forms or extensions".
20// This implementation does not accept any of that.
21// * RFC 8259 allows implementations to put limits on "the size of texts",
22// "the maximum depth of nesting", "the range and precision of numbers",
23// and "the length and character contents of strings".
24// This low-level implementation does not limit these,
25// except where noted above, and except that nesting depth requires memory allocation.
26// Note that this low-level API does not interpret numbers numerically,
27// but simply emits their source form for some higher level code to make sense of.
28// * This low-level implementation allows duplicate object keys,
29// and key/value pairs are emitted in the order they appear in the input.
30
31const std = @import("std");
32
33const Allocator = std.mem.Allocator;
34const ArrayList = std.ArrayList;
35const assert = std.debug.assert;
36const BitStack = std.BitStack;
37
38/// Scan the input and check for malformed JSON.
39/// On `SyntaxError` or `UnexpectedEndOfInput`, returns `false`.
40/// Returns any errors from the allocator as-is, which is unlikely,
41/// but can be caused by extreme nesting depth in the input.
42pub fn validate(allocator: Allocator, s: []const u8) Allocator.Error!bool {
43 var scanner = Scanner.initCompleteInput(allocator, s);
44 defer scanner.deinit();
45
46 while (true) {
47 const token = scanner.next() catch |err| switch (err) {
48 error.SyntaxError, error.UnexpectedEndOfInput => return false,
49 error.OutOfMemory => return error.OutOfMemory,
50 error.BufferUnderrun => unreachable,
51 };
52 if (token == .end_of_document) break;
53 }
54
55 return true;
56}
57
58/// The parsing errors are divided into two categories:
59/// * `SyntaxError` is for clearly malformed JSON documents,
60/// such as giving an input document that isn't JSON at all.
61/// * `UnexpectedEndOfInput` is for signaling that everything's been
62/// valid so far, but the input appears to be truncated for some reason.
63/// Note that a completely empty (or whitespace-only) input will give `UnexpectedEndOfInput`.
64pub const Error = error{ SyntaxError, UnexpectedEndOfInput };
65
66/// Calls `std.json.Reader` with `std.json.default_buffer_size`.
67pub fn reader(allocator: Allocator, io_reader: anytype) Reader(default_buffer_size, @TypeOf(io_reader)) {
68 return Reader(default_buffer_size, @TypeOf(io_reader)).init(allocator, io_reader);
69}
70/// Used by `json.reader`.
71pub const default_buffer_size = 0x1000;
72
73/// The tokens emitted by `std.json.Scanner` and `std.json.Reader` `.next*()` functions follow this grammar:
74/// ```
75/// <document> = <value> .end_of_document
76/// <value> =
77/// | <object>
78/// | <array>
79/// | <number>
80/// | <string>
81/// | .true
82/// | .false
83/// | .null
84/// <object> = .object_begin ( <string> <value> )* .object_end
85/// <array> = .array_begin ( <value> )* .array_end
86/// <number> = <It depends. See below.>
87/// <string> = <It depends. See below.>
88/// ```
89///
90/// What you get for `<number>` and `<string>` values depends on which `next*()` method you call:
91///
92/// ```
93/// next():
94/// <number> = ( .partial_number )* .number
95/// <string> = ( <partial_string> )* .string
96/// <partial_string> =
97/// | .partial_string
98/// | .partial_string_escaped_1
99/// | .partial_string_escaped_2
100/// | .partial_string_escaped_3
101/// | .partial_string_escaped_4
102///
103/// nextAlloc*(..., .alloc_always):
104/// <number> = .allocated_number
105/// <string> = .allocated_string
106///
107/// nextAlloc*(..., .alloc_if_needed):
108/// <number> =
109/// | .number
110/// | .allocated_number
111/// <string> =
112/// | .string
113/// | .allocated_string
114/// ```
115///
116/// For all tokens with a `[]const u8`, `[]u8`, or `[n]u8` payload, the payload represents the content of the value.
117/// For number values, this is the representation of the number exactly as it appears in the input.
118/// For strings, this is the content of the string after resolving escape sequences.
119///
120/// For `.allocated_number` and `.allocated_string`, the `[]u8` payloads are allocations made with the given allocator.
121/// You are responsible for managing that memory. `json.Reader.deinit()` does *not* free those allocations.
122///
123/// The `.partial_*` tokens indicate that a value spans multiple input buffers or that a string contains escape sequences.
124/// To get a complete value in memory, you need to concatenate the values yourself.
125/// Calling `nextAlloc*()` does this for you, and returns an `.allocated_*` token with the result.
126///
127/// For tokens with a `[]const u8` payload, the payload is a slice into the current input buffer.
128/// The memory may become undefined during the next call to `json.Scanner.feedInput()`
129/// or any `json.Reader` method whose return error set includes `json.Error`.
130/// To keep the value persistently, it recommended to make a copy or to use `.alloc_always`,
131/// which makes a copy for you.
132///
133/// Note that `.number` and `.string` tokens that follow `.partial_*` tokens may have `0` length to indicate that
134/// the previously partial value is completed with no additional bytes.
135/// (This can happen when the break between input buffers happens to land on the exact end of a value. E.g. `"[1234"`, `"]"`.)
136/// `.partial_*` tokens never have `0` length.
137///
138/// The recommended strategy for using the different `next*()` methods is something like this:
139///
140/// When you're expecting an object key, use `.alloc_if_needed`.
141/// You often don't need a copy of the key string to persist; you might just check which field it is.
142/// In the case that the key happens to require an allocation, free it immediately after checking it.
143///
144/// When you're expecting a meaningful string value (such as on the right of a `:`),
145/// use `.alloc_always` in order to keep the value valid throughout parsing the rest of the document.
146///
147/// When you're expecting a number value, use `.alloc_if_needed`.
148/// You're probably going to be parsing the string representation of the number into a numeric representation,
149/// so you need the complete string representation only temporarily.
150///
151/// When you're skipping an unrecognized value, use `skipValue()`.
152pub const Token = union(enum) {
153 object_begin,
154 object_end,
155 array_begin,
156 array_end,
157
158 true,
159 false,
160 null,
161
162 number: []const u8,
163 partial_number: []const u8,
164 allocated_number: []u8,
165
166 string: []const u8,
167 partial_string: []const u8,
168 partial_string_escaped_1: [1]u8,
169 partial_string_escaped_2: [2]u8,
170 partial_string_escaped_3: [3]u8,
171 partial_string_escaped_4: [4]u8,
172 allocated_string: []u8,
173
174 end_of_document,
175};
176
177/// This is only used in `peekNextTokenType()` and gives a categorization based on the first byte of the next token that will be emitted from a `next*()` call.
178pub const TokenType = enum {
179 object_begin,
180 object_end,
181 array_begin,
182 array_end,
183 true,
184 false,
185 null,
186 number,
187 string,
188 end_of_document,
189};
190
191/// To enable diagnostics, declare `var diagnostics = Diagnostics{};` then call `source.enableDiagnostics(&diagnostics);`
192/// where `source` is either a `std.json.Reader` or a `std.json.Scanner` that has just been initialized.
193/// At any time, notably just after an error, call `getLine()`, `getColumn()`, and/or `getByteOffset()`
194/// to get meaningful information from this.
195pub const Diagnostics = struct {
196 line_number: u64 = 1,
197 line_start_cursor: usize = @as(usize, @bitCast(@as(isize, -1))), // Start just "before" the input buffer to get a 1-based column for line 1.
198 total_bytes_before_current_input: u64 = 0,
199 cursor_pointer: *const usize = undefined,
200
201 /// Starts at 1.
202 pub fn getLine(self: *const @This()) u64 {
203 return self.line_number;
204 }
205 /// Starts at 1.
206 pub fn getColumn(self: *const @This()) u64 {
207 return self.cursor_pointer.* -% self.line_start_cursor;
208 }
209 /// Starts at 0. Measures the byte offset since the start of the input.
210 pub fn getByteOffset(self: *const @This()) u64 {
211 return self.total_bytes_before_current_input + self.cursor_pointer.*;
212 }
213};
214
215/// See the documentation for `std.json.Token`.
216pub const AllocWhen = enum { alloc_if_needed, alloc_always };
217
218/// For security, the maximum size allocated to store a single string or number value is limited to 4MiB by default.
219/// This limit can be specified by calling `nextAllocMax()` instead of `nextAlloc()`.
220pub const default_max_value_len = 4 * 1024 * 1024;
221
222/// Connects a `std.io.GenericReader` to a `std.json.Scanner`.
223/// All `next*()` methods here handle `error.BufferUnderrun` from `std.json.Scanner`, and then read from the reader.
224pub fn Reader(comptime buffer_size: usize, comptime ReaderType: type) type {
225 return struct {
226 scanner: Scanner,
227 reader: ReaderType,
228
229 buffer: [buffer_size]u8 = undefined,
230
231 /// The allocator is only used to track `[]` and `{}` nesting levels.
232 pub fn init(allocator: Allocator, io_reader: ReaderType) @This() {
233 return .{
234 .scanner = Scanner.initStreaming(allocator),
235 .reader = io_reader,
236 };
237 }
238 pub fn deinit(self: *@This()) void {
239 self.scanner.deinit();
240 self.* = undefined;
241 }
242
243 /// Calls `std.json.Scanner.enableDiagnostics`.
244 pub fn enableDiagnostics(self: *@This(), diagnostics: *Diagnostics) void {
245 self.scanner.enableDiagnostics(diagnostics);
246 }
247
248 pub const NextError = ReaderType.Error || Error || Allocator.Error;
249 pub const SkipError = NextError;
250 pub const AllocError = NextError || error{ValueTooLong};
251 pub const PeekError = ReaderType.Error || Error;
252
253 /// Equivalent to `nextAllocMax(allocator, when, default_max_value_len);`
254 /// See also `std.json.Token` for documentation of `nextAlloc*()` function behavior.
255 pub fn nextAlloc(self: *@This(), allocator: Allocator, when: AllocWhen) AllocError!Token {
256 return self.nextAllocMax(allocator, when, default_max_value_len);
257 }
258 /// See also `std.json.Token` for documentation of `nextAlloc*()` function behavior.
259 pub fn nextAllocMax(self: *@This(), allocator: Allocator, when: AllocWhen, max_value_len: usize) AllocError!Token {
260 const token_type = try self.peekNextTokenType();
261 switch (token_type) {
262 .number, .string => {
263 var value_list = ArrayList(u8).init(allocator);
264 errdefer {
265 value_list.deinit();
266 }
267 if (try self.allocNextIntoArrayListMax(&value_list, when, max_value_len)) |slice| {
268 return if (token_type == .number)
269 Token{ .number = slice }
270 else
271 Token{ .string = slice };
272 } else {
273 return if (token_type == .number)
274 Token{ .allocated_number = try value_list.toOwnedSlice() }
275 else
276 Token{ .allocated_string = try value_list.toOwnedSlice() };
277 }
278 },
279
280 // Simple tokens never alloc.
281 .object_begin,
282 .object_end,
283 .array_begin,
284 .array_end,
285 .true,
286 .false,
287 .null,
288 .end_of_document,
289 => return try self.next(),
290 }
291 }
292
293 /// Equivalent to `allocNextIntoArrayListMax(value_list, when, default_max_value_len);`
294 pub fn allocNextIntoArrayList(self: *@This(), value_list: *ArrayList(u8), when: AllocWhen) AllocError!?[]const u8 {
295 return self.allocNextIntoArrayListMax(value_list, when, default_max_value_len);
296 }
297 /// Calls `std.json.Scanner.allocNextIntoArrayListMax` and handles `error.BufferUnderrun`.
298 pub fn allocNextIntoArrayListMax(self: *@This(), value_list: *ArrayList(u8), when: AllocWhen, max_value_len: usize) AllocError!?[]const u8 {
299 while (true) {
300 return self.scanner.allocNextIntoArrayListMax(value_list, when, max_value_len) catch |err| switch (err) {
301 error.BufferUnderrun => {
302 try self.refillBuffer();
303 continue;
304 },
305 else => |other_err| return other_err,
306 };
307 }
308 }
309
310 /// Like `std.json.Scanner.skipValue`, but handles `error.BufferUnderrun`.
311 pub fn skipValue(self: *@This()) SkipError!void {
312 switch (try self.peekNextTokenType()) {
313 .object_begin, .array_begin => {
314 try self.skipUntilStackHeight(self.stackHeight());
315 },
316 .number, .string => {
317 while (true) {
318 switch (try self.next()) {
319 .partial_number,
320 .partial_string,
321 .partial_string_escaped_1,
322 .partial_string_escaped_2,
323 .partial_string_escaped_3,
324 .partial_string_escaped_4,
325 => continue,
326
327 .number, .string => break,
328
329 else => unreachable,
330 }
331 }
332 },
333 .true, .false, .null => {
334 _ = try self.next();
335 },
336
337 .object_end, .array_end, .end_of_document => unreachable, // Attempt to skip a non-value token.
338 }
339 }
340 /// Like `std.json.Scanner.skipUntilStackHeight()` but handles `error.BufferUnderrun`.
341 pub fn skipUntilStackHeight(self: *@This(), terminal_stack_height: usize) NextError!void {
342 while (true) {
343 return self.scanner.skipUntilStackHeight(terminal_stack_height) catch |err| switch (err) {
344 error.BufferUnderrun => {
345 try self.refillBuffer();
346 continue;
347 },
348 else => |other_err| return other_err,
349 };
350 }
351 }
352
353 /// Calls `std.json.Scanner.stackHeight`.
354 pub fn stackHeight(self: *const @This()) usize {
355 return self.scanner.stackHeight();
356 }
357 /// Calls `std.json.Scanner.ensureTotalStackCapacity`.
358 pub fn ensureTotalStackCapacity(self: *@This(), height: usize) Allocator.Error!void {
359 try self.scanner.ensureTotalStackCapacity(height);
360 }
361
362 /// See `std.json.Token` for documentation of this function.
363 pub fn next(self: *@This()) NextError!Token {
364 while (true) {
365 return self.scanner.next() catch |err| switch (err) {
366 error.BufferUnderrun => {
367 try self.refillBuffer();
368 continue;
369 },
370 else => |other_err| return other_err,
371 };
372 }
373 }
374
375 /// See `std.json.Scanner.peekNextTokenType()`.
376 pub fn peekNextTokenType(self: *@This()) PeekError!TokenType {
377 while (true) {
378 return self.scanner.peekNextTokenType() catch |err| switch (err) {
379 error.BufferUnderrun => {
380 try self.refillBuffer();
381 continue;
382 },
383 else => |other_err| return other_err,
384 };
385 }
386 }
387
388 fn refillBuffer(self: *@This()) ReaderType.Error!void {
389 const input = self.buffer[0..try self.reader.read(self.buffer[0..])];
390 if (input.len > 0) {
391 self.scanner.feedInput(input);
392 } else {
393 self.scanner.endInput();
394 }
395 }
396 };
397}
398
399/// The lowest level parsing API in this package;
400/// supports streaming input with a low memory footprint.
401/// The memory requirement is `O(d)` where d is the nesting depth of `[]` or `{}` containers in the input.
402/// Specifically `d/8` bytes are required for this purpose,
403/// with some extra buffer according to the implementation of `std.ArrayList`.
404///
405/// This scanner can emit partial tokens; see `std.json.Token`.
406/// The input to this class is a sequence of input buffers that you must supply one at a time.
407/// Call `feedInput()` with the first buffer, then call `next()` repeatedly until `error.BufferUnderrun` is returned.
408/// Then call `feedInput()` again and so forth.
409/// Call `endInput()` when the last input buffer has been given to `feedInput()`, either immediately after calling `feedInput()`,
410/// or when `error.BufferUnderrun` requests more data and there is no more.
411/// Be sure to call `next()` after calling `endInput()` until `Token.end_of_document` has been returned.
412pub const Scanner = struct {
413 state: State = .value,
414 string_is_object_key: bool = false,
415 stack: BitStack,
416 value_start: usize = undefined,
417 utf16_code_units: [2]u16 = undefined,
418
419 input: []const u8 = "",
420 cursor: usize = 0,
421 is_end_of_input: bool = false,
422 diagnostics: ?*Diagnostics = null,
423
424 /// The allocator is only used to track `[]` and `{}` nesting levels.
425 pub fn initStreaming(allocator: Allocator) @This() {
426 return .{
427 .stack = BitStack.init(allocator),
428 };
429 }
430 /// Use this if your input is a single slice.
431 /// This is effectively equivalent to:
432 /// ```
433 /// initStreaming(allocator);
434 /// feedInput(complete_input);
435 /// endInput();
436 /// ```
437 pub fn initCompleteInput(allocator: Allocator, complete_input: []const u8) @This() {
438 return .{
439 .stack = BitStack.init(allocator),
440 .input = complete_input,
441 .is_end_of_input = true,
442 };
443 }
444 pub fn deinit(self: *@This()) void {
445 self.stack.deinit();
446 self.* = undefined;
447 }
448
449 pub fn enableDiagnostics(self: *@This(), diagnostics: *Diagnostics) void {
450 diagnostics.cursor_pointer = &self.cursor;
451 self.diagnostics = diagnostics;
452 }
453
454 /// Call this whenever you get `error.BufferUnderrun` from `next()`.
455 /// When there is no more input to provide, call `endInput()`.
456 pub fn feedInput(self: *@This(), input: []const u8) void {
457 assert(self.cursor == self.input.len); // Not done with the last input slice.
458 if (self.diagnostics) |diag| {
459 diag.total_bytes_before_current_input += self.input.len;
460 // This usually goes "negative" to measure how far before the beginning
461 // of the new buffer the current line started.
462 diag.line_start_cursor -%= self.cursor;
463 }
464 self.input = input;
465 self.cursor = 0;
466 self.value_start = 0;
467 }
468 /// Call this when you will no longer call `feedInput()` anymore.
469 /// This can be called either immediately after the last `feedInput()`,
470 /// or at any time afterward, such as when getting `error.BufferUnderrun` from `next()`.
471 /// Don't forget to call `next*()` after `endInput()` until you get `.end_of_document`.
472 pub fn endInput(self: *@This()) void {
473 self.is_end_of_input = true;
474 }
475
476 pub const NextError = Error || Allocator.Error || error{BufferUnderrun};
477 pub const AllocError = Error || Allocator.Error || error{ValueTooLong};
478 pub const PeekError = Error || error{BufferUnderrun};
479 pub const SkipError = Error || Allocator.Error;
480 pub const AllocIntoArrayListError = AllocError || error{BufferUnderrun};
481
482 /// Equivalent to `nextAllocMax(allocator, when, default_max_value_len);`
483 /// This function is only available after `endInput()` (or `initCompleteInput()`) has been called.
484 /// See also `std.json.Token` for documentation of `nextAlloc*()` function behavior.
485 pub fn nextAlloc(self: *@This(), allocator: Allocator, when: AllocWhen) AllocError!Token {
486 return self.nextAllocMax(allocator, when, default_max_value_len);
487 }
488
489 /// This function is only available after `endInput()` (or `initCompleteInput()`) has been called.
490 /// See also `std.json.Token` for documentation of `nextAlloc*()` function behavior.
491 pub fn nextAllocMax(self: *@This(), allocator: Allocator, when: AllocWhen, max_value_len: usize) AllocError!Token {
492 assert(self.is_end_of_input); // This function is not available in streaming mode.
493 const token_type = self.peekNextTokenType() catch |e| switch (e) {
494 error.BufferUnderrun => unreachable,
495 else => |err| return err,
496 };
497 switch (token_type) {
498 .number, .string => {
499 var value_list = ArrayList(u8).init(allocator);
500 errdefer {
501 value_list.deinit();
502 }
503 if (self.allocNextIntoArrayListMax(&value_list, when, max_value_len) catch |e| switch (e) {
504 error.BufferUnderrun => unreachable,
505 else => |err| return err,
506 }) |slice| {
507 return if (token_type == .number)
508 Token{ .number = slice }
509 else
510 Token{ .string = slice };
511 } else {
512 return if (token_type == .number)
513 Token{ .allocated_number = try value_list.toOwnedSlice() }
514 else
515 Token{ .allocated_string = try value_list.toOwnedSlice() };
516 }
517 },
518
519 // Simple tokens never alloc.
520 .object_begin,
521 .object_end,
522 .array_begin,
523 .array_end,
524 .true,
525 .false,
526 .null,
527 .end_of_document,
528 => return self.next() catch |e| switch (e) {
529 error.BufferUnderrun => unreachable,
530 else => |err| return err,
531 },
532 }
533 }
534
535 /// Equivalent to `allocNextIntoArrayListMax(value_list, when, default_max_value_len);`
536 pub fn allocNextIntoArrayList(self: *@This(), value_list: *ArrayList(u8), when: AllocWhen) AllocIntoArrayListError!?[]const u8 {
537 return self.allocNextIntoArrayListMax(value_list, when, default_max_value_len);
538 }
539 /// The next token type must be either `.number` or `.string`. See `peekNextTokenType()`.
540 /// When allocation is not necessary with `.alloc_if_needed`,
541 /// this method returns the content slice from the input buffer, and `value_list` is not touched.
542 /// When allocation is necessary or with `.alloc_always`, this method concatenates partial tokens into the given `value_list`,
543 /// and returns `null` once the final `.number` or `.string` token has been written into it.
544 /// In case of an `error.BufferUnderrun`, partial values will be left in the given value_list.
545 /// The given `value_list` is never reset by this method, so an `error.BufferUnderrun` situation
546 /// can be resumed by passing the same array list in again.
547 /// This method does not indicate whether the token content being returned is for a `.number` or `.string` token type;
548 /// the caller of this method is expected to know which type of token is being processed.
549 pub fn allocNextIntoArrayListMax(self: *@This(), value_list: *ArrayList(u8), when: AllocWhen, max_value_len: usize) AllocIntoArrayListError!?[]const u8 {
550 while (true) {
551 const token = try self.next();
552 switch (token) {
553 // Accumulate partial values.
554 .partial_number, .partial_string => |slice| {
555 try appendSlice(value_list, slice, max_value_len);
556 },
557 .partial_string_escaped_1 => |buf| {
558 try appendSlice(value_list, buf[0..], max_value_len);
559 },
560 .partial_string_escaped_2 => |buf| {
561 try appendSlice(value_list, buf[0..], max_value_len);
562 },
563 .partial_string_escaped_3 => |buf| {
564 try appendSlice(value_list, buf[0..], max_value_len);
565 },
566 .partial_string_escaped_4 => |buf| {
567 try appendSlice(value_list, buf[0..], max_value_len);
568 },
569
570 // Return complete values.
571 .number => |slice| {
572 if (when == .alloc_if_needed and value_list.items.len == 0) {
573 // No alloc necessary.
574 return slice;
575 }
576 try appendSlice(value_list, slice, max_value_len);
577 // The token is complete.
578 return null;
579 },
580 .string => |slice| {
581 if (when == .alloc_if_needed and value_list.items.len == 0) {
582 // No alloc necessary.
583 return slice;
584 }
585 try appendSlice(value_list, slice, max_value_len);
586 // The token is complete.
587 return null;
588 },
589
590 .object_begin,
591 .object_end,
592 .array_begin,
593 .array_end,
594 .true,
595 .false,
596 .null,
597 .end_of_document,
598 => unreachable, // Only .number and .string token types are allowed here. Check peekNextTokenType() before calling this.
599
600 .allocated_number, .allocated_string => unreachable,
601 }
602 }
603 }
604
605 /// This function is only available after `endInput()` (or `initCompleteInput()`) has been called.
606 /// If the next token type is `.object_begin` or `.array_begin`,
607 /// this function calls `next()` repeatedly until the corresponding `.object_end` or `.array_end` is found.
608 /// If the next token type is `.number` or `.string`,
609 /// this function calls `next()` repeatedly until the (non `.partial_*`) `.number` or `.string` token is found.
610 /// If the next token type is `.true`, `.false`, or `.null`, this function calls `next()` once.
611 /// The next token type must not be `.object_end`, `.array_end`, or `.end_of_document`;
612 /// see `peekNextTokenType()`.
613 pub fn skipValue(self: *@This()) SkipError!void {
614 assert(self.is_end_of_input); // This function is not available in streaming mode.
615 switch (self.peekNextTokenType() catch |e| switch (e) {
616 error.BufferUnderrun => unreachable,
617 else => |err| return err,
618 }) {
619 .object_begin, .array_begin => {
620 self.skipUntilStackHeight(self.stackHeight()) catch |e| switch (e) {
621 error.BufferUnderrun => unreachable,
622 else => |err| return err,
623 };
624 },
625 .number, .string => {
626 while (true) {
627 switch (self.next() catch |e| switch (e) {
628 error.BufferUnderrun => unreachable,
629 else => |err| return err,
630 }) {
631 .partial_number,
632 .partial_string,
633 .partial_string_escaped_1,
634 .partial_string_escaped_2,
635 .partial_string_escaped_3,
636 .partial_string_escaped_4,
637 => continue,
638
639 .number, .string => break,
640
641 else => unreachable,
642 }
643 }
644 },
645 .true, .false, .null => {
646 _ = self.next() catch |e| switch (e) {
647 error.BufferUnderrun => unreachable,
648 else => |err| return err,
649 };
650 },
651
652 .object_end, .array_end, .end_of_document => unreachable, // Attempt to skip a non-value token.
653 }
654 }
655
656 /// Skip tokens until an `.object_end` or `.array_end` token results in a `stackHeight()` equal the given stack height.
657 /// Unlike `skipValue()`, this function is available in streaming mode.
658 pub fn skipUntilStackHeight(self: *@This(), terminal_stack_height: usize) NextError!void {
659 while (true) {
660 switch (try self.next()) {
661 .object_end, .array_end => {
662 if (self.stackHeight() == terminal_stack_height) break;
663 },
664 .end_of_document => unreachable,
665 else => continue,
666 }
667 }
668 }
669
670 /// The depth of `{}` or `[]` nesting levels at the current position.
671 pub fn stackHeight(self: *const @This()) usize {
672 return self.stack.bit_len;
673 }
674
675 /// Pre allocate memory to hold the given number of nesting levels.
676 /// `stackHeight()` up to the given number will not cause allocations.
677 pub fn ensureTotalStackCapacity(self: *@This(), height: usize) Allocator.Error!void {
678 try self.stack.ensureTotalCapacity(height);
679 }
680
681 /// See `std.json.Token` for documentation of this function.
682 pub fn next(self: *@This()) NextError!Token {
683 state_loop: while (true) {
684 switch (self.state) {
685 .value => {
686 switch (try self.skipWhitespaceExpectByte()) {
687 // Object, Array
688 '{' => {
689 try self.stack.push(OBJECT_MODE);
690 self.cursor += 1;
691 self.state = .object_start;
692 return .object_begin;
693 },
694 '[' => {
695 try self.stack.push(ARRAY_MODE);
696 self.cursor += 1;
697 self.state = .array_start;
698 return .array_begin;
699 },
700
701 // String
702 '"' => {
703 self.cursor += 1;
704 self.value_start = self.cursor;
705 self.state = .string;
706 continue :state_loop;
707 },
708
709 // Number
710 '1'...'9' => {
711 self.value_start = self.cursor;
712 self.cursor += 1;
713 self.state = .number_int;
714 continue :state_loop;
715 },
716 '0' => {
717 self.value_start = self.cursor;
718 self.cursor += 1;
719 self.state = .number_leading_zero;
720 continue :state_loop;
721 },
722 '-' => {
723 self.value_start = self.cursor;
724 self.cursor += 1;
725 self.state = .number_minus;
726 continue :state_loop;
727 },
728
729 // literal values
730 't' => {
731 self.cursor += 1;
732 self.state = .literal_t;
733 continue :state_loop;
734 },
735 'f' => {
736 self.cursor += 1;
737 self.state = .literal_f;
738 continue :state_loop;
739 },
740 'n' => {
741 self.cursor += 1;
742 self.state = .literal_n;
743 continue :state_loop;
744 },
745
746 else => return error.SyntaxError,
747 }
748 },
749
750 .post_value => {
751 if (try self.skipWhitespaceCheckEnd()) return .end_of_document;
752
753 const c = self.input[self.cursor];
754 if (self.string_is_object_key) {
755 self.string_is_object_key = false;
756 switch (c) {
757 ':' => {
758 self.cursor += 1;
759 self.state = .value;
760 continue :state_loop;
761 },
762 else => return error.SyntaxError,
763 }
764 }
765
766 switch (c) {
767 '}' => {
768 if (self.stack.pop() != OBJECT_MODE) return error.SyntaxError;
769 self.cursor += 1;
770 // stay in .post_value state.
771 return .object_end;
772 },
773 ']' => {
774 if (self.stack.pop() != ARRAY_MODE) return error.SyntaxError;
775 self.cursor += 1;
776 // stay in .post_value state.
777 return .array_end;
778 },
779 ',' => {
780 switch (self.stack.peek()) {
781 OBJECT_MODE => {
782 self.state = .object_post_comma;
783 },
784 ARRAY_MODE => {
785 self.state = .value;
786 },
787 }
788 self.cursor += 1;
789 continue :state_loop;
790 },
791 else => return error.SyntaxError,
792 }
793 },
794
795 .object_start => {
796 switch (try self.skipWhitespaceExpectByte()) {
797 '"' => {
798 self.cursor += 1;
799 self.value_start = self.cursor;
800 self.state = .string;
801 self.string_is_object_key = true;
802 continue :state_loop;
803 },
804 '}' => {
805 self.cursor += 1;
806 _ = self.stack.pop();
807 self.state = .post_value;
808 return .object_end;
809 },
810 else => return error.SyntaxError,
811 }
812 },
813 .object_post_comma => {
814 switch (try self.skipWhitespaceExpectByte()) {
815 '"' => {
816 self.cursor += 1;
817 self.value_start = self.cursor;
818 self.state = .string;
819 self.string_is_object_key = true;
820 continue :state_loop;
821 },
822 else => return error.SyntaxError,
823 }
824 },
825
826 .array_start => {
827 switch (try self.skipWhitespaceExpectByte()) {
828 ']' => {
829 self.cursor += 1;
830 _ = self.stack.pop();
831 self.state = .post_value;
832 return .array_end;
833 },
834 else => {
835 self.state = .value;
836 continue :state_loop;
837 },
838 }
839 },
840
841 .number_minus => {
842 if (self.cursor >= self.input.len) return self.endOfBufferInNumber(false);
843 switch (self.input[self.cursor]) {
844 '0' => {
845 self.cursor += 1;
846 self.state = .number_leading_zero;
847 continue :state_loop;
848 },
849 '1'...'9' => {
850 self.cursor += 1;
851 self.state = .number_int;
852 continue :state_loop;
853 },
854 else => return error.SyntaxError,
855 }
856 },
857 .number_leading_zero => {
858 if (self.cursor >= self.input.len) return self.endOfBufferInNumber(true);
859 switch (self.input[self.cursor]) {
860 '.' => {
861 self.cursor += 1;
862 self.state = .number_post_dot;
863 continue :state_loop;
864 },
865 'e', 'E' => {
866 self.cursor += 1;
867 self.state = .number_post_e;
868 continue :state_loop;
869 },
870 else => {
871 self.state = .post_value;
872 return Token{ .number = self.takeValueSlice() };
873 },
874 }
875 },
876 .number_int => {
877 while (self.cursor < self.input.len) : (self.cursor += 1) {
878 switch (self.input[self.cursor]) {
879 '0'...'9' => continue,
880 '.' => {
881 self.cursor += 1;
882 self.state = .number_post_dot;
883 continue :state_loop;
884 },
885 'e', 'E' => {
886 self.cursor += 1;
887 self.state = .number_post_e;
888 continue :state_loop;
889 },
890 else => {
891 self.state = .post_value;
892 return Token{ .number = self.takeValueSlice() };
893 },
894 }
895 }
896 return self.endOfBufferInNumber(true);
897 },
898 .number_post_dot => {
899 if (self.cursor >= self.input.len) return self.endOfBufferInNumber(false);
900 switch (self.input[self.cursor]) {
901 '0'...'9' => {
902 self.cursor += 1;
903 self.state = .number_frac;
904 continue :state_loop;
905 },
906 else => return error.SyntaxError,
907 }
908 },
909 .number_frac => {
910 while (self.cursor < self.input.len) : (self.cursor += 1) {
911 switch (self.input[self.cursor]) {
912 '0'...'9' => continue,
913 'e', 'E' => {
914 self.cursor += 1;
915 self.state = .number_post_e;
916 continue :state_loop;
917 },
918 else => {
919 self.state = .post_value;
920 return Token{ .number = self.takeValueSlice() };
921 },
922 }
923 }
924 return self.endOfBufferInNumber(true);
925 },
926 .number_post_e => {
927 if (self.cursor >= self.input.len) return self.endOfBufferInNumber(false);
928 switch (self.input[self.cursor]) {
929 '0'...'9' => {
930 self.cursor += 1;
931 self.state = .number_exp;
932 continue :state_loop;
933 },
934 '+', '-' => {
935 self.cursor += 1;
936 self.state = .number_post_e_sign;
937 continue :state_loop;
938 },
939 else => return error.SyntaxError,
940 }
941 },
942 .number_post_e_sign => {
943 if (self.cursor >= self.input.len) return self.endOfBufferInNumber(false);
944 switch (self.input[self.cursor]) {
945 '0'...'9' => {
946 self.cursor += 1;
947 self.state = .number_exp;
948 continue :state_loop;
949 },
950 else => return error.SyntaxError,
951 }
952 },
953 .number_exp => {
954 while (self.cursor < self.input.len) : (self.cursor += 1) {
955 switch (self.input[self.cursor]) {
956 '0'...'9' => continue,
957 else => {
958 self.state = .post_value;
959 return Token{ .number = self.takeValueSlice() };
960 },
961 }
962 }
963 return self.endOfBufferInNumber(true);
964 },
965
966 .string => {
967 while (self.cursor < self.input.len) : (self.cursor += 1) {
968 switch (self.input[self.cursor]) {
969 0...0x1f => return error.SyntaxError, // Bare ASCII control code in string.
970
971 // ASCII plain text.
972 0x20...('"' - 1), ('"' + 1)...('\\' - 1), ('\\' + 1)...0x7F => continue,
973
974 // Special characters.
975 '"' => {
976 const result = Token{ .string = self.takeValueSlice() };
977 self.cursor += 1;
978 self.state = .post_value;
979 return result;
980 },
981 '\\' => {
982 const slice = self.takeValueSlice();
983 self.cursor += 1;
984 self.state = .string_backslash;
985 if (slice.len > 0) return Token{ .partial_string = slice };
986 continue :state_loop;
987 },
988
989 // UTF-8 validation.
990 // See http://unicode.org/mail-arch/unicode-ml/y2003-m02/att-0467/01-The_Algorithm_to_Valide_an_UTF-8_String
991 0xC2...0xDF => {
992 self.cursor += 1;
993 self.state = .string_utf8_last_byte;
994 continue :state_loop;
995 },
996 0xE0 => {
997 self.cursor += 1;
998 self.state = .string_utf8_second_to_last_byte_guard_against_overlong;
999 continue :state_loop;
1000 },
1001 0xE1...0xEC, 0xEE...0xEF => {
1002 self.cursor += 1;
1003 self.state = .string_utf8_second_to_last_byte;
1004 continue :state_loop;
1005 },
1006 0xED => {
1007 self.cursor += 1;
1008 self.state = .string_utf8_second_to_last_byte_guard_against_surrogate_half;
1009 continue :state_loop;
1010 },
1011 0xF0 => {
1012 self.cursor += 1;
1013 self.state = .string_utf8_third_to_last_byte_guard_against_overlong;
1014 continue :state_loop;
1015 },
1016 0xF1...0xF3 => {
1017 self.cursor += 1;
1018 self.state = .string_utf8_third_to_last_byte;
1019 continue :state_loop;
1020 },
1021 0xF4 => {
1022 self.cursor += 1;
1023 self.state = .string_utf8_third_to_last_byte_guard_against_too_large;
1024 continue :state_loop;
1025 },
1026 0x80...0xC1, 0xF5...0xFF => return error.SyntaxError, // Invalid UTF-8.
1027 }
1028 }
1029 if (self.is_end_of_input) return error.UnexpectedEndOfInput;
1030 const slice = self.takeValueSlice();
1031 if (slice.len > 0) return Token{ .partial_string = slice };
1032 return error.BufferUnderrun;
1033 },
1034 .string_backslash => {
1035 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1036 switch (self.input[self.cursor]) {
1037 '"', '\\', '/' => {
1038 // Since these characters now represent themselves literally,
1039 // we can simply begin the next plaintext slice here.
1040 self.value_start = self.cursor;
1041 self.cursor += 1;
1042 self.state = .string;
1043 continue :state_loop;
1044 },
1045 'b' => {
1046 self.cursor += 1;
1047 self.value_start = self.cursor;
1048 self.state = .string;
1049 return Token{ .partial_string_escaped_1 = [_]u8{0x08} };
1050 },
1051 'f' => {
1052 self.cursor += 1;
1053 self.value_start = self.cursor;
1054 self.state = .string;
1055 return Token{ .partial_string_escaped_1 = [_]u8{0x0c} };
1056 },
1057 'n' => {
1058 self.cursor += 1;
1059 self.value_start = self.cursor;
1060 self.state = .string;
1061 return Token{ .partial_string_escaped_1 = [_]u8{'\n'} };
1062 },
1063 'r' => {
1064 self.cursor += 1;
1065 self.value_start = self.cursor;
1066 self.state = .string;
1067 return Token{ .partial_string_escaped_1 = [_]u8{'\r'} };
1068 },
1069 't' => {
1070 self.cursor += 1;
1071 self.value_start = self.cursor;
1072 self.state = .string;
1073 return Token{ .partial_string_escaped_1 = [_]u8{'\t'} };
1074 },
1075 'u' => {
1076 self.cursor += 1;
1077 self.state = .string_backslash_u;
1078 continue :state_loop;
1079 },
1080 else => return error.SyntaxError,
1081 }
1082 },
1083 .string_backslash_u => {
1084 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1085 const c = self.input[self.cursor];
1086 switch (c) {
1087 '0'...'9' => {
1088 self.utf16_code_units[0] = @as(u16, c - '0') << 12;
1089 },
1090 'A'...'F' => {
1091 self.utf16_code_units[0] = @as(u16, c - 'A' + 10) << 12;
1092 },
1093 'a'...'f' => {
1094 self.utf16_code_units[0] = @as(u16, c - 'a' + 10) << 12;
1095 },
1096 else => return error.SyntaxError,
1097 }
1098 self.cursor += 1;
1099 self.state = .string_backslash_u_1;
1100 continue :state_loop;
1101 },
1102 .string_backslash_u_1 => {
1103 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1104 const c = self.input[self.cursor];
1105 switch (c) {
1106 '0'...'9' => {
1107 self.utf16_code_units[0] |= @as(u16, c - '0') << 8;
1108 },
1109 'A'...'F' => {
1110 self.utf16_code_units[0] |= @as(u16, c - 'A' + 10) << 8;
1111 },
1112 'a'...'f' => {
1113 self.utf16_code_units[0] |= @as(u16, c - 'a' + 10) << 8;
1114 },
1115 else => return error.SyntaxError,
1116 }
1117 self.cursor += 1;
1118 self.state = .string_backslash_u_2;
1119 continue :state_loop;
1120 },
1121 .string_backslash_u_2 => {
1122 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1123 const c = self.input[self.cursor];
1124 switch (c) {
1125 '0'...'9' => {
1126 self.utf16_code_units[0] |= @as(u16, c - '0') << 4;
1127 },
1128 'A'...'F' => {
1129 self.utf16_code_units[0] |= @as(u16, c - 'A' + 10) << 4;
1130 },
1131 'a'...'f' => {
1132 self.utf16_code_units[0] |= @as(u16, c - 'a' + 10) << 4;
1133 },
1134 else => return error.SyntaxError,
1135 }
1136 self.cursor += 1;
1137 self.state = .string_backslash_u_3;
1138 continue :state_loop;
1139 },
1140 .string_backslash_u_3 => {
1141 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1142 const c = self.input[self.cursor];
1143 switch (c) {
1144 '0'...'9' => {
1145 self.utf16_code_units[0] |= c - '0';
1146 },
1147 'A'...'F' => {
1148 self.utf16_code_units[0] |= c - 'A' + 10;
1149 },
1150 'a'...'f' => {
1151 self.utf16_code_units[0] |= c - 'a' + 10;
1152 },
1153 else => return error.SyntaxError,
1154 }
1155 self.cursor += 1;
1156 if (std.unicode.utf16IsHighSurrogate(self.utf16_code_units[0])) {
1157 self.state = .string_surrogate_half;
1158 continue :state_loop;
1159 } else if (std.unicode.utf16IsLowSurrogate(self.utf16_code_units[0])) {
1160 return error.SyntaxError; // Unexpected low surrogate half.
1161 } else {
1162 self.value_start = self.cursor;
1163 self.state = .string;
1164 return partialStringCodepoint(self.utf16_code_units[0]);
1165 }
1166 },
1167 .string_surrogate_half => {
1168 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1169 switch (self.input[self.cursor]) {
1170 '\\' => {
1171 self.cursor += 1;
1172 self.state = .string_surrogate_half_backslash;
1173 continue :state_loop;
1174 },
1175 else => return error.SyntaxError, // Expected low surrogate half.
1176 }
1177 },
1178 .string_surrogate_half_backslash => {
1179 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1180 switch (self.input[self.cursor]) {
1181 'u' => {
1182 self.cursor += 1;
1183 self.state = .string_surrogate_half_backslash_u;
1184 continue :state_loop;
1185 },
1186 else => return error.SyntaxError, // Expected low surrogate half.
1187 }
1188 },
1189 .string_surrogate_half_backslash_u => {
1190 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1191 switch (self.input[self.cursor]) {
1192 'D', 'd' => {
1193 self.cursor += 1;
1194 self.utf16_code_units[1] = 0xD << 12;
1195 self.state = .string_surrogate_half_backslash_u_1;
1196 continue :state_loop;
1197 },
1198 else => return error.SyntaxError, // Expected low surrogate half.
1199 }
1200 },
1201 .string_surrogate_half_backslash_u_1 => {
1202 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1203 const c = self.input[self.cursor];
1204 switch (c) {
1205 'C'...'F' => {
1206 self.cursor += 1;
1207 self.utf16_code_units[1] |= @as(u16, c - 'A' + 10) << 8;
1208 self.state = .string_surrogate_half_backslash_u_2;
1209 continue :state_loop;
1210 },
1211 'c'...'f' => {
1212 self.cursor += 1;
1213 self.utf16_code_units[1] |= @as(u16, c - 'a' + 10) << 8;
1214 self.state = .string_surrogate_half_backslash_u_2;
1215 continue :state_loop;
1216 },
1217 else => return error.SyntaxError, // Expected low surrogate half.
1218 }
1219 },
1220 .string_surrogate_half_backslash_u_2 => {
1221 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1222 const c = self.input[self.cursor];
1223 switch (c) {
1224 '0'...'9' => {
1225 self.cursor += 1;
1226 self.utf16_code_units[1] |= @as(u16, c - '0') << 4;
1227 self.state = .string_surrogate_half_backslash_u_3;
1228 continue :state_loop;
1229 },
1230 'A'...'F' => {
1231 self.cursor += 1;
1232 self.utf16_code_units[1] |= @as(u16, c - 'A' + 10) << 4;
1233 self.state = .string_surrogate_half_backslash_u_3;
1234 continue :state_loop;
1235 },
1236 'a'...'f' => {
1237 self.cursor += 1;
1238 self.utf16_code_units[1] |= @as(u16, c - 'a' + 10) << 4;
1239 self.state = .string_surrogate_half_backslash_u_3;
1240 continue :state_loop;
1241 },
1242 else => return error.SyntaxError,
1243 }
1244 },
1245 .string_surrogate_half_backslash_u_3 => {
1246 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1247 const c = self.input[self.cursor];
1248 switch (c) {
1249 '0'...'9' => {
1250 self.utf16_code_units[1] |= c - '0';
1251 },
1252 'A'...'F' => {
1253 self.utf16_code_units[1] |= c - 'A' + 10;
1254 },
1255 'a'...'f' => {
1256 self.utf16_code_units[1] |= c - 'a' + 10;
1257 },
1258 else => return error.SyntaxError,
1259 }
1260 self.cursor += 1;
1261 self.value_start = self.cursor;
1262 self.state = .string;
1263 const code_point = std.unicode.utf16DecodeSurrogatePair(&self.utf16_code_units) catch unreachable;
1264 return partialStringCodepoint(code_point);
1265 },
1266
1267 .string_utf8_last_byte => {
1268 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1269 switch (self.input[self.cursor]) {
1270 0x80...0xBF => {
1271 self.cursor += 1;
1272 self.state = .string;
1273 continue :state_loop;
1274 },
1275 else => return error.SyntaxError, // Invalid UTF-8.
1276 }
1277 },
1278 .string_utf8_second_to_last_byte => {
1279 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1280 switch (self.input[self.cursor]) {
1281 0x80...0xBF => {
1282 self.cursor += 1;
1283 self.state = .string_utf8_last_byte;
1284 continue :state_loop;
1285 },
1286 else => return error.SyntaxError, // Invalid UTF-8.
1287 }
1288 },
1289 .string_utf8_second_to_last_byte_guard_against_overlong => {
1290 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1291 switch (self.input[self.cursor]) {
1292 0xA0...0xBF => {
1293 self.cursor += 1;
1294 self.state = .string_utf8_last_byte;
1295 continue :state_loop;
1296 },
1297 else => return error.SyntaxError, // Invalid UTF-8.
1298 }
1299 },
1300 .string_utf8_second_to_last_byte_guard_against_surrogate_half => {
1301 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1302 switch (self.input[self.cursor]) {
1303 0x80...0x9F => {
1304 self.cursor += 1;
1305 self.state = .string_utf8_last_byte;
1306 continue :state_loop;
1307 },
1308 else => return error.SyntaxError, // Invalid UTF-8.
1309 }
1310 },
1311 .string_utf8_third_to_last_byte => {
1312 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1313 switch (self.input[self.cursor]) {
1314 0x80...0xBF => {
1315 self.cursor += 1;
1316 self.state = .string_utf8_second_to_last_byte;
1317 continue :state_loop;
1318 },
1319 else => return error.SyntaxError, // Invalid UTF-8.
1320 }
1321 },
1322 .string_utf8_third_to_last_byte_guard_against_overlong => {
1323 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1324 switch (self.input[self.cursor]) {
1325 0x90...0xBF => {
1326 self.cursor += 1;
1327 self.state = .string_utf8_second_to_last_byte;
1328 continue :state_loop;
1329 },
1330 else => return error.SyntaxError, // Invalid UTF-8.
1331 }
1332 },
1333 .string_utf8_third_to_last_byte_guard_against_too_large => {
1334 if (self.cursor >= self.input.len) return self.endOfBufferInString();
1335 switch (self.input[self.cursor]) {
1336 0x80...0x8F => {
1337 self.cursor += 1;
1338 self.state = .string_utf8_second_to_last_byte;
1339 continue :state_loop;
1340 },
1341 else => return error.SyntaxError, // Invalid UTF-8.
1342 }
1343 },
1344
1345 .literal_t => {
1346 switch (try self.expectByte()) {
1347 'r' => {
1348 self.cursor += 1;
1349 self.state = .literal_tr;
1350 continue :state_loop;
1351 },
1352 else => return error.SyntaxError,
1353 }
1354 },
1355 .literal_tr => {
1356 switch (try self.expectByte()) {
1357 'u' => {
1358 self.cursor += 1;
1359 self.state = .literal_tru;
1360 continue :state_loop;
1361 },
1362 else => return error.SyntaxError,
1363 }
1364 },
1365 .literal_tru => {
1366 switch (try self.expectByte()) {
1367 'e' => {
1368 self.cursor += 1;
1369 self.state = .post_value;
1370 return .true;
1371 },
1372 else => return error.SyntaxError,
1373 }
1374 },
1375 .literal_f => {
1376 switch (try self.expectByte()) {
1377 'a' => {
1378 self.cursor += 1;
1379 self.state = .literal_fa;
1380 continue :state_loop;
1381 },
1382 else => return error.SyntaxError,
1383 }
1384 },
1385 .literal_fa => {
1386 switch (try self.expectByte()) {
1387 'l' => {
1388 self.cursor += 1;
1389 self.state = .literal_fal;
1390 continue :state_loop;
1391 },
1392 else => return error.SyntaxError,
1393 }
1394 },
1395 .literal_fal => {
1396 switch (try self.expectByte()) {
1397 's' => {
1398 self.cursor += 1;
1399 self.state = .literal_fals;
1400 continue :state_loop;
1401 },
1402 else => return error.SyntaxError,
1403 }
1404 },
1405 .literal_fals => {
1406 switch (try self.expectByte()) {
1407 'e' => {
1408 self.cursor += 1;
1409 self.state = .post_value;
1410 return .false;
1411 },
1412 else => return error.SyntaxError,
1413 }
1414 },
1415 .literal_n => {
1416 switch (try self.expectByte()) {
1417 'u' => {
1418 self.cursor += 1;
1419 self.state = .literal_nu;
1420 continue :state_loop;
1421 },
1422 else => return error.SyntaxError,
1423 }
1424 },
1425 .literal_nu => {
1426 switch (try self.expectByte()) {
1427 'l' => {
1428 self.cursor += 1;
1429 self.state = .literal_nul;
1430 continue :state_loop;
1431 },
1432 else => return error.SyntaxError,
1433 }
1434 },
1435 .literal_nul => {
1436 switch (try self.expectByte()) {
1437 'l' => {
1438 self.cursor += 1;
1439 self.state = .post_value;
1440 return .null;
1441 },
1442 else => return error.SyntaxError,
1443 }
1444 },
1445 }
1446 unreachable;
1447 }
1448 }
1449
1450 /// Seeks ahead in the input until the first byte of the next token (or the end of the input)
1451 /// determines which type of token will be returned from the next `next*()` call.
1452 /// This function is idempotent, only advancing past commas, colons, and inter-token whitespace.
1453 pub fn peekNextTokenType(self: *@This()) PeekError!TokenType {
1454 state_loop: while (true) {
1455 switch (self.state) {
1456 .value => {
1457 switch (try self.skipWhitespaceExpectByte()) {
1458 '{' => return .object_begin,
1459 '[' => return .array_begin,
1460 '"' => return .string,
1461 '-', '0'...'9' => return .number,
1462 't' => return .true,
1463 'f' => return .false,
1464 'n' => return .null,
1465 else => return error.SyntaxError,
1466 }
1467 },
1468
1469 .post_value => {
1470 if (try self.skipWhitespaceCheckEnd()) return .end_of_document;
1471
1472 const c = self.input[self.cursor];
1473 if (self.string_is_object_key) {
1474 self.string_is_object_key = false;
1475 switch (c) {
1476 ':' => {
1477 self.cursor += 1;
1478 self.state = .value;
1479 continue :state_loop;
1480 },
1481 else => return error.SyntaxError,
1482 }
1483 }
1484
1485 switch (c) {
1486 '}' => return .object_end,
1487 ']' => return .array_end,
1488 ',' => {
1489 switch (self.stack.peek()) {
1490 OBJECT_MODE => {
1491 self.state = .object_post_comma;
1492 },
1493 ARRAY_MODE => {
1494 self.state = .value;
1495 },
1496 }
1497 self.cursor += 1;
1498 continue :state_loop;
1499 },
1500 else => return error.SyntaxError,
1501 }
1502 },
1503
1504 .object_start => {
1505 switch (try self.skipWhitespaceExpectByte()) {
1506 '"' => return .string,
1507 '}' => return .object_end,
1508 else => return error.SyntaxError,
1509 }
1510 },
1511 .object_post_comma => {
1512 switch (try self.skipWhitespaceExpectByte()) {
1513 '"' => return .string,
1514 else => return error.SyntaxError,
1515 }
1516 },
1517
1518 .array_start => {
1519 switch (try self.skipWhitespaceExpectByte()) {
1520 ']' => return .array_end,
1521 else => {
1522 self.state = .value;
1523 continue :state_loop;
1524 },
1525 }
1526 },
1527
1528 .number_minus,
1529 .number_leading_zero,
1530 .number_int,
1531 .number_post_dot,
1532 .number_frac,
1533 .number_post_e,
1534 .number_post_e_sign,
1535 .number_exp,
1536 => return .number,
1537
1538 .string,
1539 .string_backslash,
1540 .string_backslash_u,
1541 .string_backslash_u_1,
1542 .string_backslash_u_2,
1543 .string_backslash_u_3,
1544 .string_surrogate_half,
1545 .string_surrogate_half_backslash,
1546 .string_surrogate_half_backslash_u,
1547 .string_surrogate_half_backslash_u_1,
1548 .string_surrogate_half_backslash_u_2,
1549 .string_surrogate_half_backslash_u_3,
1550 => return .string,
1551
1552 .string_utf8_last_byte,
1553 .string_utf8_second_to_last_byte,
1554 .string_utf8_second_to_last_byte_guard_against_overlong,
1555 .string_utf8_second_to_last_byte_guard_against_surrogate_half,
1556 .string_utf8_third_to_last_byte,
1557 .string_utf8_third_to_last_byte_guard_against_overlong,
1558 .string_utf8_third_to_last_byte_guard_against_too_large,
1559 => return .string,
1560
1561 .literal_t,
1562 .literal_tr,
1563 .literal_tru,
1564 => return .true,
1565 .literal_f,
1566 .literal_fa,
1567 .literal_fal,
1568 .literal_fals,
1569 => return .false,
1570 .literal_n,
1571 .literal_nu,
1572 .literal_nul,
1573 => return .null,
1574 }
1575 unreachable;
1576 }
1577 }
1578
1579 const State = enum {
1580 value,
1581 post_value,
1582
1583 object_start,
1584 object_post_comma,
1585
1586 array_start,
1587
1588 number_minus,
1589 number_leading_zero,
1590 number_int,
1591 number_post_dot,
1592 number_frac,
1593 number_post_e,
1594 number_post_e_sign,
1595 number_exp,
1596
1597 string,
1598 string_backslash,
1599 string_backslash_u,
1600 string_backslash_u_1,
1601 string_backslash_u_2,
1602 string_backslash_u_3,
1603 string_surrogate_half,
1604 string_surrogate_half_backslash,
1605 string_surrogate_half_backslash_u,
1606 string_surrogate_half_backslash_u_1,
1607 string_surrogate_half_backslash_u_2,
1608 string_surrogate_half_backslash_u_3,
1609
1610 // From http://unicode.org/mail-arch/unicode-ml/y2003-m02/att-0467/01-The_Algorithm_to_Valide_an_UTF-8_String
1611 string_utf8_last_byte, // State A
1612 string_utf8_second_to_last_byte, // State B
1613 string_utf8_second_to_last_byte_guard_against_overlong, // State C
1614 string_utf8_second_to_last_byte_guard_against_surrogate_half, // State D
1615 string_utf8_third_to_last_byte, // State E
1616 string_utf8_third_to_last_byte_guard_against_overlong, // State F
1617 string_utf8_third_to_last_byte_guard_against_too_large, // State G
1618
1619 literal_t,
1620 literal_tr,
1621 literal_tru,
1622 literal_f,
1623 literal_fa,
1624 literal_fal,
1625 literal_fals,
1626 literal_n,
1627 literal_nu,
1628 literal_nul,
1629 };
1630
1631 fn expectByte(self: *const @This()) !u8 {
1632 if (self.cursor < self.input.len) {
1633 return self.input[self.cursor];
1634 }
1635 // No byte.
1636 if (self.is_end_of_input) return error.UnexpectedEndOfInput;
1637 return error.BufferUnderrun;
1638 }
1639
1640 fn skipWhitespace(self: *@This()) void {
1641 while (self.cursor < self.input.len) : (self.cursor += 1) {
1642 switch (self.input[self.cursor]) {
1643 // Whitespace
1644 ' ', '\t', '\r' => continue,
1645 '\n' => {
1646 if (self.diagnostics) |diag| {
1647 diag.line_number += 1;
1648 // This will count the newline itself,
1649 // which means a straight-forward subtraction will give a 1-based column number.
1650 diag.line_start_cursor = self.cursor;
1651 }
1652 continue;
1653 },
1654 else => return,
1655 }
1656 }
1657 }
1658
1659 fn skipWhitespaceExpectByte(self: *@This()) !u8 {
1660 self.skipWhitespace();
1661 return self.expectByte();
1662 }
1663
1664 fn skipWhitespaceCheckEnd(self: *@This()) !bool {
1665 self.skipWhitespace();
1666 if (self.cursor >= self.input.len) {
1667 // End of buffer.
1668 if (self.is_end_of_input) {
1669 // End of everything.
1670 if (self.stackHeight() == 0) {
1671 // We did it!
1672 return true;
1673 }
1674 return error.UnexpectedEndOfInput;
1675 }
1676 return error.BufferUnderrun;
1677 }
1678 if (self.stackHeight() == 0) return error.SyntaxError;
1679 return false;
1680 }
1681
1682 fn takeValueSlice(self: *@This()) []const u8 {
1683 const slice = self.input[self.value_start..self.cursor];
1684 self.value_start = self.cursor;
1685 return slice;
1686 }
1687 fn takeValueSliceMinusTrailingOffset(self: *@This(), trailing_negative_offset: usize) []const u8 {
1688 // Check if the escape sequence started before the current input buffer.
1689 // (The algebra here is awkward to avoid unsigned underflow,
1690 // but it's just making sure the slice on the next line isn't UB.)
1691 if (self.cursor <= self.value_start + trailing_negative_offset) return "";
1692 const slice = self.input[self.value_start .. self.cursor - trailing_negative_offset];
1693 // When trailing_negative_offset is non-zero, setting self.value_start doesn't matter,
1694 // because we always set it again while emitting the .partial_string_escaped_*.
1695 self.value_start = self.cursor;
1696 return slice;
1697 }
1698
1699 fn endOfBufferInNumber(self: *@This(), allow_end: bool) !Token {
1700 const slice = self.takeValueSlice();
1701 if (self.is_end_of_input) {
1702 if (!allow_end) return error.UnexpectedEndOfInput;
1703 self.state = .post_value;
1704 return Token{ .number = slice };
1705 }
1706 if (slice.len == 0) return error.BufferUnderrun;
1707 return Token{ .partial_number = slice };
1708 }
1709
1710 fn endOfBufferInString(self: *@This()) !Token {
1711 if (self.is_end_of_input) return error.UnexpectedEndOfInput;
1712 const slice = self.takeValueSliceMinusTrailingOffset(switch (self.state) {
1713 // Don't include the escape sequence in the partial string.
1714 .string_backslash => 1,
1715 .string_backslash_u => 2,
1716 .string_backslash_u_1 => 3,
1717 .string_backslash_u_2 => 4,
1718 .string_backslash_u_3 => 5,
1719 .string_surrogate_half => 6,
1720 .string_surrogate_half_backslash => 7,
1721 .string_surrogate_half_backslash_u => 8,
1722 .string_surrogate_half_backslash_u_1 => 9,
1723 .string_surrogate_half_backslash_u_2 => 10,
1724 .string_surrogate_half_backslash_u_3 => 11,
1725
1726 // Include everything up to the cursor otherwise.
1727 .string,
1728 .string_utf8_last_byte,
1729 .string_utf8_second_to_last_byte,
1730 .string_utf8_second_to_last_byte_guard_against_overlong,
1731 .string_utf8_second_to_last_byte_guard_against_surrogate_half,
1732 .string_utf8_third_to_last_byte,
1733 .string_utf8_third_to_last_byte_guard_against_overlong,
1734 .string_utf8_third_to_last_byte_guard_against_too_large,
1735 => 0,
1736
1737 else => unreachable,
1738 });
1739 if (slice.len == 0) return error.BufferUnderrun;
1740 return Token{ .partial_string = slice };
1741 }
1742
1743 fn partialStringCodepoint(code_point: u21) Token {
1744 var buf: [4]u8 = undefined;
1745 switch (std.unicode.utf8Encode(code_point, &buf) catch unreachable) {
1746 1 => return Token{ .partial_string_escaped_1 = buf[0..1].* },
1747 2 => return Token{ .partial_string_escaped_2 = buf[0..2].* },
1748 3 => return Token{ .partial_string_escaped_3 = buf[0..3].* },
1749 4 => return Token{ .partial_string_escaped_4 = buf[0..4].* },
1750 else => unreachable,
1751 }
1752 }
1753};
1754
1755const OBJECT_MODE = 0;
1756const ARRAY_MODE = 1;
1757
1758fn appendSlice(list: *std.ArrayList(u8), buf: []const u8, max_value_len: usize) !void {
1759 const new_len = std.math.add(usize, list.items.len, buf.len) catch return error.ValueTooLong;
1760 if (new_len > max_value_len) return error.ValueTooLong;
1761 try list.appendSlice(buf);
1762}
1763
1764/// For the slice you get from a `Token.number` or `Token.allocated_number`,
1765/// this function returns true if the number doesn't contain any fraction or exponent components, and is not `-0`.
1766/// Note, the numeric value encoded by the value may still be an integer, such as `1.0`.
1767/// This function is meant to give a hint about whether integer parsing or float parsing should be used on the value.
1768/// This function will not give meaningful results on non-numeric input.
1769pub fn isNumberFormattedLikeAnInteger(value: []const u8) bool {
1770 if (std.mem.eql(u8, value, "-0")) return false;
1771 return std.mem.indexOfAny(u8, value, ".eE") == null;
1772}
1773
1774test {
1775 _ = @import("./scanner_test.zig");
1776}
lib/std/json/scanner_test.zig+39-39
......@@ -1,13 +1,11 @@
11const std = @import("std");
2const JsonScanner = @import("./scanner.zig").Scanner;
3const jsonReader = @import("./scanner.zig").reader;
4const JsonReader = @import("./scanner.zig").Reader;
5const Token = @import("./scanner.zig").Token;
6const TokenType = @import("./scanner.zig").TokenType;
7const Diagnostics = @import("./scanner.zig").Diagnostics;
8const Error = @import("./scanner.zig").Error;
9const validate = @import("./scanner.zig").validate;
10const isNumberFormattedLikeAnInteger = @import("./scanner.zig").isNumberFormattedLikeAnInteger;
2const Scanner = @import("Scanner.zig");
3const Token = Scanner.Token;
4const TokenType = Scanner.TokenType;
5const Diagnostics = Scanner.Diagnostics;
6const Error = Scanner.Error;
7const validate = Scanner.validate;
8const isNumberFormattedLikeAnInteger = Scanner.isNumberFormattedLikeAnInteger;
119
1210const example_document_str =
1311 \\{
......@@ -36,7 +34,7 @@ fn expectPeekNext(scanner_or_reader: anytype, expected_token_type: TokenType, ex
3634}
3735
3836test "token" {
39 var scanner = JsonScanner.initCompleteInput(std.testing.allocator, example_document_str);
37 var scanner = Scanner.initCompleteInput(std.testing.allocator, example_document_str);
4038 defer scanner.deinit();
4139
4240 try expectNext(&scanner, .object_begin);
......@@ -138,23 +136,25 @@ fn testAllTypes(source: anytype, large_buffer: bool) !void {
138136}
139137
140138test "peek all types" {
141 var scanner = JsonScanner.initCompleteInput(std.testing.allocator, all_types_test_case);
139 var scanner = Scanner.initCompleteInput(std.testing.allocator, all_types_test_case);
142140 defer scanner.deinit();
143141 try testAllTypes(&scanner, true);
144142
145 var stream: std.io.FixedBufferStream = .{ .buffer = all_types_test_case };
146 var json_reader = jsonReader(std.testing.allocator, stream.reader());
143 var stream: std.Io.Reader = .fixed(all_types_test_case);
144 var json_reader: Scanner.Reader = .init(std.testing.allocator, &stream);
147145 defer json_reader.deinit();
148146 try testAllTypes(&json_reader, true);
149147
150 var tiny_stream: std.io.FixedBufferStream = .{ .buffer = all_types_test_case };
151 var tiny_json_reader = JsonReader(1, @TypeOf(tiny_stream.reader())).init(std.testing.allocator, tiny_stream.reader());
148 var tiny_buffer: [1]u8 = undefined;
149 var tiny_stream: std.testing.Reader = .init(&tiny_buffer, &.{.{ .buffer = all_types_test_case }});
150 tiny_stream.artificial_limit = .limited(1);
151 var tiny_json_reader: Scanner.Reader = .init(std.testing.allocator, &tiny_stream.interface);
152152 defer tiny_json_reader.deinit();
153153 try testAllTypes(&tiny_json_reader, false);
154154}
155155
156156test "token mismatched close" {
157 var scanner = JsonScanner.initCompleteInput(std.testing.allocator, "[102, 111, 111 }");
157 var scanner = Scanner.initCompleteInput(std.testing.allocator, "[102, 111, 111 }");
158158 defer scanner.deinit();
159159 try expectNext(&scanner, .array_begin);
160160 try expectNext(&scanner, Token{ .number = "102" });
......@@ -164,15 +164,15 @@ test "token mismatched close" {
164164}
165165
166166test "token premature object close" {
167 var scanner = JsonScanner.initCompleteInput(std.testing.allocator, "{ \"key\": }");
167 var scanner = Scanner.initCompleteInput(std.testing.allocator, "{ \"key\": }");
168168 defer scanner.deinit();
169169 try expectNext(&scanner, .object_begin);
170170 try expectNext(&scanner, Token{ .string = "key" });
171171 try std.testing.expectError(error.SyntaxError, scanner.next());
172172}
173173
174test "JsonScanner basic" {
175 var scanner = JsonScanner.initCompleteInput(std.testing.allocator, example_document_str);
174test "Scanner basic" {
175 var scanner = Scanner.initCompleteInput(std.testing.allocator, example_document_str);
176176 defer scanner.deinit();
177177
178178 while (true) {
......@@ -181,10 +181,10 @@ test "JsonScanner basic" {
181181 }
182182}
183183
184test "JsonReader basic" {
185 var stream: std.io.FixedBufferStream = .{ .buffer = example_document_str };
184test "Scanner.Reader basic" {
185 var stream: std.Io.Reader = .fixed(example_document_str);
186186
187 var json_reader = jsonReader(std.testing.allocator, stream.reader());
187 var json_reader: Scanner.Reader = .init(std.testing.allocator, &stream);
188188 defer json_reader.deinit();
189189
190190 while (true) {
......@@ -215,7 +215,7 @@ const number_test_items = blk: {
215215
216216test "numbers" {
217217 for (number_test_items) |number_str| {
218 var scanner = JsonScanner.initCompleteInput(std.testing.allocator, number_str);
218 var scanner = Scanner.initCompleteInput(std.testing.allocator, number_str);
219219 defer scanner.deinit();
220220
221221 const token = try scanner.next();
......@@ -243,10 +243,10 @@ const string_test_cases = .{
243243
244244test "strings" {
245245 inline for (string_test_cases) |tuple| {
246 var stream: std.io.FixedBufferStream = .{ .buffer = "\"" ++ tuple[0] ++ "\"" };
246 var stream: std.Io.Reader = .fixed("\"" ++ tuple[0] ++ "\"");
247247 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
248248 defer arena.deinit();
249 var json_reader = jsonReader(std.testing.allocator, stream.reader());
249 var json_reader: Scanner.Reader = .init(std.testing.allocator, &stream);
250250 defer json_reader.deinit();
251251
252252 const token = try json_reader.nextAlloc(arena.allocator(), .alloc_if_needed);
......@@ -289,7 +289,7 @@ test "nesting" {
289289}
290290
291291fn expectMaybeError(document_str: []const u8, maybe_error: ?Error) !void {
292 var scanner = JsonScanner.initCompleteInput(std.testing.allocator, document_str);
292 var scanner = Scanner.initCompleteInput(std.testing.allocator, document_str);
293293 defer scanner.deinit();
294294
295295 while (true) {
......@@ -352,12 +352,12 @@ fn expectEqualTokens(expected_token: Token, actual_token: Token) !void {
352352}
353353
354354fn testTinyBufferSize(document_str: []const u8) !void {
355 var tiny_stream: std.io.FixedBufferStream = .{ .buffer = document_str };
356 var normal_stream: std.io.FixedBufferStream = .{ .buffer = document_str };
355 var tiny_stream: std.Io.Reader = .fixed(document_str);
356 var normal_stream: std.Io.Reader = .fixed(document_str);
357357
358 var tiny_json_reader = JsonReader(1, @TypeOf(tiny_stream.reader())).init(std.testing.allocator, tiny_stream.reader());
358 var tiny_json_reader: Scanner.Reader = .init(std.testing.allocator, &tiny_stream);
359359 defer tiny_json_reader.deinit();
360 var normal_json_reader = JsonReader(0x1000, @TypeOf(normal_stream.reader())).init(std.testing.allocator, normal_stream.reader());
360 var normal_json_reader: Scanner.Reader = .init(std.testing.allocator, &normal_stream);
361361 defer normal_json_reader.deinit();
362362
363363 expectEqualStreamOfTokens(&normal_json_reader, &tiny_json_reader) catch |err| {
......@@ -397,13 +397,13 @@ test "validate" {
397397}
398398
399399fn testSkipValue(s: []const u8) !void {
400 var scanner = JsonScanner.initCompleteInput(std.testing.allocator, s);
400 var scanner = Scanner.initCompleteInput(std.testing.allocator, s);
401401 defer scanner.deinit();
402402 try scanner.skipValue();
403403 try expectEqualTokens(.end_of_document, try scanner.next());
404404
405 var stream: std.io.FixedBufferStream = .{ .buffer = s };
406 var json_reader = jsonReader(std.testing.allocator, stream.reader());
405 var stream: std.Io.Reader = .fixed(s);
406 var json_reader: Scanner.Reader = .init(std.testing.allocator, &stream);
407407 defer json_reader.deinit();
408408 try json_reader.skipValue();
409409 try expectEqualTokens(.end_of_document, try json_reader.next());
......@@ -441,7 +441,7 @@ fn testEnsureStackCapacity(do_ensure: bool) !void {
441441 try input_string.appendNTimes(std.testing.allocator, ']', nestings);
442442 defer input_string.deinit(std.testing.allocator);
443443
444 var scanner = JsonScanner.initCompleteInput(failing_allocator, input_string.items);
444 var scanner = Scanner.initCompleteInput(failing_allocator, input_string.items);
445445 defer scanner.deinit();
446446
447447 if (do_ensure) {
......@@ -473,17 +473,17 @@ fn testDiagnosticsFromSource(expected_error: ?anyerror, line: u64, col: u64, byt
473473 try std.testing.expectEqual(byte_offset, diagnostics.getByteOffset());
474474}
475475fn testDiagnostics(expected_error: ?anyerror, line: u64, col: u64, byte_offset: u64, s: []const u8) !void {
476 var scanner = JsonScanner.initCompleteInput(std.testing.allocator, s);
476 var scanner = Scanner.initCompleteInput(std.testing.allocator, s);
477477 defer scanner.deinit();
478478 try testDiagnosticsFromSource(expected_error, line, col, byte_offset, &scanner);
479479
480 var tiny_stream: std.io.FixedBufferStream = .{ .buffer = s };
481 var tiny_json_reader = JsonReader(1, @TypeOf(tiny_stream.reader())).init(std.testing.allocator, tiny_stream.reader());
480 var tiny_stream: std.Io.Reader = .fixed(s);
481 var tiny_json_reader: Scanner.Reader = .init(std.testing.allocator, &tiny_stream);
482482 defer tiny_json_reader.deinit();
483483 try testDiagnosticsFromSource(expected_error, line, col, byte_offset, &tiny_json_reader);
484484
485 var medium_stream: std.io.FixedBufferStream = .{ .buffer = s };
486 var medium_json_reader = JsonReader(5, @TypeOf(medium_stream.reader())).init(std.testing.allocator, medium_stream.reader());
485 var medium_stream: std.Io.Reader = .fixed(s);
486 var medium_json_reader: Scanner.Reader = .init(std.testing.allocator, &medium_stream);
487487 defer medium_json_reader.deinit();
488488 try testDiagnosticsFromSource(expected_error, line, col, byte_offset, &medium_json_reader);
489489}
lib/std/json/static.zig+5-5
......@@ -4,11 +4,11 @@ const Allocator = std.mem.Allocator;
44const ArenaAllocator = std.heap.ArenaAllocator;
55const ArrayList = std.ArrayList;
66
7const Scanner = @import("./scanner.zig").Scanner;
8const Token = @import("./scanner.zig").Token;
9const AllocWhen = @import("./scanner.zig").AllocWhen;
10const default_max_value_len = @import("./scanner.zig").default_max_value_len;
11const isNumberFormattedLikeAnInteger = @import("./scanner.zig").isNumberFormattedLikeAnInteger;
7const Scanner = @import("Scanner.zig");
8const Token = Scanner.Token;
9const AllocWhen = Scanner.AllocWhen;
10const default_max_value_len = Scanner.default_max_value_len;
11const isNumberFormattedLikeAnInteger = Scanner.isNumberFormattedLikeAnInteger;
1212
1313const Value = @import("./dynamic.zig").Value;
1414const Array = @import("./dynamic.zig").Array;
lib/std/json/static_test.zig+14-16
......@@ -12,9 +12,7 @@ const parseFromValue = @import("./static.zig").parseFromValue;
1212const parseFromValueLeaky = @import("./static.zig").parseFromValueLeaky;
1313const ParseOptions = @import("./static.zig").ParseOptions;
1414
15const JsonScanner = @import("./scanner.zig").Scanner;
16const jsonReader = @import("./scanner.zig").reader;
17const Diagnostics = @import("./scanner.zig").Diagnostics;
15const Scanner = @import("Scanner.zig");
1816
1917const Value = @import("./dynamic.zig").Value;
2018
......@@ -300,9 +298,9 @@ const subnamespaces_0_doc =
300298fn testAllParseFunctions(comptime T: type, expected: T, doc: []const u8) !void {
301299 // First do the one with the debug info in case we get a SyntaxError or something.
302300 {
303 var scanner = JsonScanner.initCompleteInput(testing.allocator, doc);
301 var scanner = Scanner.initCompleteInput(testing.allocator, doc);
304302 defer scanner.deinit();
305 var diagnostics = Diagnostics{};
303 var diagnostics = Scanner.Diagnostics{};
306304 scanner.enableDiagnostics(&diagnostics);
307305 var parsed = parseFromTokenSource(T, testing.allocator, &scanner, .{}) catch |e| {
308306 std.debug.print("at line,col: {}:{}\n", .{ diagnostics.getLine(), diagnostics.getColumn() });
......@@ -317,8 +315,8 @@ fn testAllParseFunctions(comptime T: type, expected: T, doc: []const u8) !void {
317315 try testing.expectEqualDeep(expected, parsed.value);
318316 }
319317 {
320 var stream: std.io.FixedBufferStream = .{ .buffer = doc };
321 var json_reader = jsonReader(std.testing.allocator, stream.reader());
318 var stream: std.Io.Reader = .fixed(doc);
319 var json_reader: Scanner.Reader = .init(std.testing.allocator, &stream);
322320 defer json_reader.deinit();
323321 var parsed = try parseFromTokenSource(T, testing.allocator, &json_reader, .{});
324322 defer parsed.deinit();
......@@ -331,13 +329,13 @@ fn testAllParseFunctions(comptime T: type, expected: T, doc: []const u8) !void {
331329 try testing.expectEqualDeep(expected, try parseFromSliceLeaky(T, arena.allocator(), doc, .{}));
332330 }
333331 {
334 var scanner = JsonScanner.initCompleteInput(testing.allocator, doc);
332 var scanner = Scanner.initCompleteInput(testing.allocator, doc);
335333 defer scanner.deinit();
336334 try testing.expectEqualDeep(expected, try parseFromTokenSourceLeaky(T, arena.allocator(), &scanner, .{}));
337335 }
338336 {
339 var stream: std.io.FixedBufferStream = .{ .buffer = doc };
340 var json_reader = jsonReader(std.testing.allocator, stream.reader());
337 var stream: std.Io.Reader = .fixed(doc);
338 var json_reader: Scanner.Reader = .init(std.testing.allocator, &stream);
341339 defer json_reader.deinit();
342340 try testing.expectEqualDeep(expected, try parseFromTokenSourceLeaky(T, arena.allocator(), &json_reader, .{}));
343341 }
......@@ -763,7 +761,7 @@ test "parse exponential into int" {
763761
764762test "parseFromTokenSource" {
765763 {
766 var scanner = JsonScanner.initCompleteInput(testing.allocator, "123");
764 var scanner = Scanner.initCompleteInput(testing.allocator, "123");
767765 defer scanner.deinit();
768766 var parsed = try parseFromTokenSource(u32, testing.allocator, &scanner, .{});
769767 defer parsed.deinit();
......@@ -771,8 +769,8 @@ test "parseFromTokenSource" {
771769 }
772770
773771 {
774 var stream: std.io.FixedBufferStream = .{ .buffer = "123" };
775 var json_reader = jsonReader(std.testing.allocator, stream.reader());
772 var stream: std.Io.Reader = .fixed("123");
773 var json_reader: Scanner.Reader = .init(std.testing.allocator, &stream);
776774 defer json_reader.deinit();
777775 var parsed = try parseFromTokenSource(u32, testing.allocator, &json_reader, .{});
778776 defer parsed.deinit();
......@@ -836,7 +834,7 @@ test "json parse partial" {
836834 \\}
837835 ;
838836 const allocator = testing.allocator;
839 var scanner = JsonScanner.initCompleteInput(allocator, str);
837 var scanner = Scanner.initCompleteInput(allocator, str);
840838 defer scanner.deinit();
841839
842840 var arena = ArenaAllocator.init(allocator);
......@@ -886,8 +884,8 @@ test "json parse allocate when streaming" {
886884 var arena = ArenaAllocator.init(allocator);
887885 defer arena.deinit();
888886
889 var stream: std.io.FixedBufferStream = .{ .buffer = str };
890 var json_reader = jsonReader(std.testing.allocator, stream.reader());
887 var stream: std.Io.Reader = .fixed(str);
888 var json_reader: Scanner.Reader = .init(std.testing.allocator, &stream);
891889
892890 const parsed = parseFromTokenSourceLeaky(T, arena.allocator(), &json_reader, .{}) catch |err| {
893891 json_reader.deinit();
lib/std/json/test.zig+3-4
......@@ -2,8 +2,7 @@ const std = @import("std");
22const json = std.json;
33const testing = std.testing;
44const parseFromSlice = @import("./static.zig").parseFromSlice;
5const validate = @import("./scanner.zig").validate;
6const JsonScanner = @import("./scanner.zig").Scanner;
5const Scanner = @import("./Scanner.zig");
76const Value = @import("./dynamic.zig").Value;
87
98// Support for JSONTestSuite.zig
......@@ -20,7 +19,7 @@ pub fn any(s: []const u8) !void {
2019 testHighLevelDynamicParser(s) catch {};
2120}
2221fn testLowLevelScanner(s: []const u8) !void {
23 var scanner = JsonScanner.initCompleteInput(testing.allocator, s);
22 var scanner = Scanner.initCompleteInput(testing.allocator, s);
2423 defer scanner.deinit();
2524 while (true) {
2625 const token = try scanner.next();
......@@ -47,7 +46,7 @@ test "n_object_closed_missing_value" {
4746}
4847
4948fn roundTrip(s: []const u8) !void {
50 try testing.expect(try validate(testing.allocator, s));
49 try testing.expect(try Scanner.validate(testing.allocator, s));
5150
5251 var parsed = try parseFromSlice(Value, testing.allocator, s, .{});
5352 defer parsed.deinit();
lib/std/zig.zig+16-13
......@@ -446,8 +446,8 @@ pub fn fmtString(bytes: []const u8) std.fmt.Formatter([]const u8, stringEscape)
446446}
447447
448448/// Return a formatter for escaping a single quoted Zig string.
449pub fn fmtChar(bytes: []const u8) std.fmt.Formatter([]const u8, charEscape) {
450 return .{ .data = bytes };
449pub fn fmtChar(c: u21) std.fmt.Formatter(u21, charEscape) {
450 return .{ .data = c };
451451}
452452
453453test fmtString {
......@@ -458,9 +458,7 @@ test fmtString {
458458}
459459
460460test fmtChar {
461 try std.testing.expectFmt(
462 \\" \\ hi \x07 \x11 " derp \'"
463 , "\"{f}\"", .{fmtChar(" \\ hi \x07 \x11 \" derp '")});
461 try std.testing.expectFmt("c \\u{26a1}", "{f} {f}", .{ fmtChar('c'), fmtChar('⚡') });
464462}
465463
466464/// Print the string as escaped contents of a double quoted string.
......@@ -480,21 +478,26 @@ pub fn stringEscape(bytes: []const u8, w: *Writer) Writer.Error!void {
480478 };
481479}
482480
483/// Print the string as escaped contents of a single-quoted string.
484pub fn charEscape(bytes: []const u8, w: *Writer) Writer.Error!void {
485 for (bytes) |byte| switch (byte) {
481/// Print as escaped contents of a single-quoted string.
482pub fn charEscape(codepoint: u21, w: *Writer) Writer.Error!void {
483 switch (codepoint) {
486484 '\n' => try w.writeAll("\\n"),
487485 '\r' => try w.writeAll("\\r"),
488486 '\t' => try w.writeAll("\\t"),
489487 '\\' => try w.writeAll("\\\\"),
490 '"' => try w.writeByte('"'),
491488 '\'' => try w.writeAll("\\'"),
492 ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte),
489 '"', ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(@intCast(codepoint)),
493490 else => {
494 try w.writeAll("\\x");
495 try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' });
491 if (std.math.cast(u8, codepoint)) |byte| {
492 try w.writeAll("\\x");
493 try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' });
494 } else {
495 try w.writeAll("\\u{");
496 try w.printInt(codepoint, 16, .lower, .{});
497 try w.writeByte('}');
498 }
496499 },
497 };
500 }
498501}
499502
500503pub fn isValidId(bytes: []const u8) bool {
lib/std/zig/Ast.zig+1-1
......@@ -574,7 +574,7 @@ pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void
574574 '/' => "comment",
575575 else => unreachable,
576576 },
577 std.zig.fmtChar(tok_slice[parse_error.extra.offset..][0..1]),
577 std.zig.fmtChar(tok_slice[parse_error.extra.offset]),
578578 });
579579 },
580580
lib/std/zig/Server.zig+2-2
......@@ -203,8 +203,8 @@ pub const TestMetadata = struct {
203203
204204pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void {
205205 const header: OutMessage.TestMetadata = .{
206 .tests_len = @as(u32, @intCast(test_metadata.names.len)),
207 .string_bytes_len = @as(u32, @intCast(test_metadata.string_bytes.len)),
206 .tests_len = @intCast(test_metadata.names.len),
207 .string_bytes_len = @intCast(test_metadata.string_bytes.len),
208208 };
209209 const trailing = 2;
210210 const bytes_len = @sizeOf(OutMessage.TestMetadata) +
lib/std/zig/llvm/BitcodeReader.zig+1-1
......@@ -177,7 +177,7 @@ pub fn next(bc: *BitcodeReader) !?Item {
177177
178178pub fn skipBlock(bc: *BitcodeReader, block: Block) !void {
179179 assert(bc.bit_offset == 0);
180 try bc.reader.discardAll(4 * @as(u34, block.len));
180 try bc.reader.discardAll(4 * @as(usize, block.len));
181181 try bc.endBlock();
182182}
183183
lib/std/zon.zig+1
......@@ -38,6 +38,7 @@
3838
3939pub const parse = @import("zon/parse.zig");
4040pub const stringify = @import("zon/stringify.zig");
41pub const Serializer = @import("zon/Serializer.zig");
4142
4243test {
4344 _ = parse;
lib/std/zon/Serializer.zig created+929
......@@ -0,0 +1,929 @@
1//! Lower level control over serialization, you can create a new instance with `serializer`.
2//!
3//! Useful when you want control over which fields are serialized, how they're represented,
4//! or want to write a ZON object that does not exist in memory.
5//!
6//! You can serialize values with `value`. To serialize recursive types, the following are provided:
7//! * `valueMaxDepth`
8//! * `valueArbitraryDepth`
9//!
10//! You can also serialize values using specific notations:
11//! * `int`
12//! * `float`
13//! * `codePoint`
14//! * `tuple`
15//! * `tupleMaxDepth`
16//! * `tupleArbitraryDepth`
17//! * `string`
18//! * `multilineString`
19//!
20//! For manual serialization of containers, see:
21//! * `beginStruct`
22//! * `beginTuple`
23
24options: Options = .{},
25indent_level: u8 = 0,
26writer: *Writer,
27
28const Serializer = @This();
29const std = @import("std");
30const assert = std.debug.assert;
31const Writer = std.Io.Writer;
32
33pub const Error = Writer.Error;
34pub const DepthError = Error || error{ExceededMaxDepth};
35
36pub const Options = struct {
37 /// If false, only syntactically necessary whitespace is emitted.
38 whitespace: bool = true,
39};
40
41/// Options for manual serialization of container types.
42pub const ContainerOptions = struct {
43 /// The whitespace style that should be used for this container. Ignored if whitespace is off.
44 whitespace_style: union(enum) {
45 /// If true, wrap every field. If false do not.
46 wrap: bool,
47 /// Automatically decide whether to wrap or not based on the number of fields. Following
48 /// the standard rule of thumb, containers with more than two fields are wrapped.
49 fields: usize,
50 } = .{ .wrap = true },
51
52 fn shouldWrap(self: ContainerOptions) bool {
53 return switch (self.whitespace_style) {
54 .wrap => |wrap| wrap,
55 .fields => |fields| fields > 2,
56 };
57 }
58};
59
60/// Options for serialization of an individual value.
61///
62/// See `SerializeOptions` for more information on these options.
63pub const ValueOptions = struct {
64 emit_codepoint_literals: EmitCodepointLiterals = .never,
65 emit_strings_as_containers: bool = false,
66 emit_default_optional_fields: bool = true,
67};
68
69/// Determines when to emit Unicode code point literals as opposed to integer literals.
70pub const EmitCodepointLiterals = enum {
71 /// Never emit Unicode code point literals.
72 never,
73 /// Emit Unicode code point literals for any `u8` in the printable ASCII range.
74 printable_ascii,
75 /// Emit Unicode code point literals for any unsigned integer with 21 bits or fewer
76 /// whose value is a valid non-surrogate code point.
77 always,
78
79 /// If the value should be emitted as a Unicode codepoint, return it as a u21.
80 fn emitAsCodepoint(self: @This(), val: anytype) ?u21 {
81 // Rule out incompatible integer types
82 switch (@typeInfo(@TypeOf(val))) {
83 .int => |int_info| if (int_info.signedness == .signed or int_info.bits > 21) {
84 return null;
85 },
86 .comptime_int => {},
87 else => comptime unreachable,
88 }
89
90 // Return null if the value shouldn't be printed as a Unicode codepoint, or the value casted
91 // to a u21 if it should.
92 switch (self) {
93 .always => {
94 const c = std.math.cast(u21, val) orelse return null;
95 if (!std.unicode.utf8ValidCodepoint(c)) return null;
96 return c;
97 },
98 .printable_ascii => {
99 const c = std.math.cast(u8, val) orelse return null;
100 if (!std.ascii.isPrint(c)) return null;
101 return c;
102 },
103 .never => {
104 return null;
105 },
106 }
107 }
108};
109
110/// Serialize a value, similar to `serialize`.
111pub fn value(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
112 comptime assert(!typeIsRecursive(@TypeOf(val)));
113 return self.valueArbitraryDepth(val, options);
114}
115
116/// Serialize a value, similar to `serializeMaxDepth`.
117/// Can return `error.ExceededMaxDepth`.
118pub fn valueMaxDepth(self: *Serializer, val: anytype, options: ValueOptions, depth: usize) DepthError!void {
119 try checkValueDepth(val, depth);
120 return self.valueArbitraryDepth(val, options);
121}
122
123/// Serialize a value, similar to `serializeArbitraryDepth`.
124pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
125 comptime assert(canSerializeType(@TypeOf(val)));
126 switch (@typeInfo(@TypeOf(val))) {
127 .int, .comptime_int => if (options.emit_codepoint_literals.emitAsCodepoint(val)) |c| {
128 self.codePoint(c) catch |err| switch (err) {
129 error.InvalidCodepoint => unreachable, // Already validated
130 else => |e| return e,
131 };
132 } else {
133 try self.int(val);
134 },
135 .float, .comptime_float => try self.float(val),
136 .bool, .null => try self.writer.print("{}", .{val}),
137 .enum_literal => try self.ident(@tagName(val)),
138 .@"enum" => try self.ident(@tagName(val)),
139 .pointer => |pointer| {
140 // Try to serialize as a string
141 const item: ?type = switch (@typeInfo(pointer.child)) {
142 .array => |array| array.child,
143 else => if (pointer.size == .slice) pointer.child else null,
144 };
145 if (item == u8 and
146 (pointer.sentinel() == null or pointer.sentinel() == 0) and
147 !options.emit_strings_as_containers)
148 {
149 return try self.string(val);
150 }
151
152 // Serialize as either a tuple or as the child type
153 switch (pointer.size) {
154 .slice => try self.tupleImpl(val, options),
155 .one => try self.valueArbitraryDepth(val.*, options),
156 else => comptime unreachable,
157 }
158 },
159 .array => {
160 var container = try self.beginTuple(
161 .{ .whitespace_style = .{ .fields = val.len } },
162 );
163 for (val) |item_val| {
164 try container.fieldArbitraryDepth(item_val, options);
165 }
166 try container.end();
167 },
168 .@"struct" => |@"struct"| if (@"struct".is_tuple) {
169 var container = try self.beginTuple(
170 .{ .whitespace_style = .{ .fields = @"struct".fields.len } },
171 );
172 inline for (val) |field_value| {
173 try container.fieldArbitraryDepth(field_value, options);
174 }
175 try container.end();
176 } else {
177 // Decide which fields to emit
178 const fields, const skipped: [@"struct".fields.len]bool = if (options.emit_default_optional_fields) b: {
179 break :b .{ @"struct".fields.len, @splat(false) };
180 } else b: {
181 var fields = @"struct".fields.len;
182 var skipped: [@"struct".fields.len]bool = @splat(false);
183 inline for (@"struct".fields, &skipped) |field_info, *skip| {
184 if (field_info.default_value_ptr) |ptr| {
185 const default: *const field_info.type = @ptrCast(@alignCast(ptr));
186 const field_value = @field(val, field_info.name);
187 if (std.meta.eql(field_value, default.*)) {
188 skip.* = true;
189 fields -= 1;
190 }
191 }
192 }
193 break :b .{ fields, skipped };
194 };
195
196 // Emit those fields
197 var container = try self.beginStruct(
198 .{ .whitespace_style = .{ .fields = fields } },
199 );
200 inline for (@"struct".fields, skipped) |field_info, skip| {
201 if (!skip) {
202 try container.fieldArbitraryDepth(
203 field_info.name,
204 @field(val, field_info.name),
205 options,
206 );
207 }
208 }
209 try container.end();
210 },
211 .@"union" => |@"union"| {
212 comptime assert(@"union".tag_type != null);
213 switch (val) {
214 inline else => |pl, tag| if (@TypeOf(pl) == void)
215 try self.writer.print(".{s}", .{@tagName(tag)})
216 else {
217 var container = try self.beginStruct(.{ .whitespace_style = .{ .fields = 1 } });
218
219 try container.fieldArbitraryDepth(
220 @tagName(tag),
221 pl,
222 options,
223 );
224
225 try container.end();
226 },
227 }
228 },
229 .optional => if (val) |inner| {
230 try self.valueArbitraryDepth(inner, options);
231 } else {
232 try self.writer.writeAll("null");
233 },
234 .vector => |vector| {
235 var container = try self.beginTuple(
236 .{ .whitespace_style = .{ .fields = vector.len } },
237 );
238 for (0..vector.len) |i| {
239 try container.fieldArbitraryDepth(val[i], options);
240 }
241 try container.end();
242 },
243
244 else => comptime unreachable,
245 }
246}
247
248/// Serialize an integer.
249pub fn int(self: *Serializer, val: anytype) Error!void {
250 try self.writer.printInt(val, 10, .lower, .{});
251}
252
253/// Serialize a float.
254pub fn float(self: *Serializer, val: anytype) Error!void {
255 switch (@typeInfo(@TypeOf(val))) {
256 .float => if (std.math.isNan(val)) {
257 return self.writer.writeAll("nan");
258 } else if (std.math.isPositiveInf(val)) {
259 return self.writer.writeAll("inf");
260 } else if (std.math.isNegativeInf(val)) {
261 return self.writer.writeAll("-inf");
262 } else if (std.math.isNegativeZero(val)) {
263 return self.writer.writeAll("-0.0");
264 } else {
265 try self.writer.print("{d}", .{val});
266 },
267 .comptime_float => if (val == 0) {
268 return self.writer.writeAll("0");
269 } else {
270 try self.writer.print("{d}", .{val});
271 },
272 else => comptime unreachable,
273 }
274}
275
276/// Serialize `name` as an identifier prefixed with `.`.
277///
278/// Escapes the identifier if necessary.
279pub fn ident(self: *Serializer, name: []const u8) Error!void {
280 try self.writer.print(".{f}", .{std.zig.fmtIdPU(name)});
281}
282
283pub const CodePointError = Error || error{InvalidCodepoint};
284
285/// Serialize `val` as a Unicode codepoint.
286///
287/// Returns `error.InvalidCodepoint` if `val` is not a valid Unicode codepoint.
288pub fn codePoint(self: *Serializer, val: u21) CodePointError!void {
289 try self.writer.print("'{f}'", .{std.zig.fmtChar(val)});
290}
291
292/// Like `value`, but always serializes `val` as a tuple.
293///
294/// Will fail at comptime if `val` is not a tuple, array, pointer to an array, or slice.
295pub fn tuple(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
296 comptime assert(!typeIsRecursive(@TypeOf(val)));
297 try self.tupleArbitraryDepth(val, options);
298}
299
300/// Like `tuple`, but recursive types are allowed.
301///
302/// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
303pub fn tupleMaxDepth(
304 self: *Serializer,
305 val: anytype,
306 options: ValueOptions,
307 depth: usize,
308) DepthError!void {
309 try checkValueDepth(val, depth);
310 try self.tupleArbitraryDepth(val, options);
311}
312
313/// Like `tuple`, but recursive types are allowed.
314///
315/// It is the caller's responsibility to ensure that `val` does not contain cycles.
316pub fn tupleArbitraryDepth(
317 self: *Serializer,
318 val: anytype,
319 options: ValueOptions,
320) Error!void {
321 try self.tupleImpl(val, options);
322}
323
324fn tupleImpl(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
325 comptime assert(canSerializeType(@TypeOf(val)));
326 switch (@typeInfo(@TypeOf(val))) {
327 .@"struct" => {
328 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });
329 inline for (val) |item_val| {
330 try container.fieldArbitraryDepth(item_val, options);
331 }
332 try container.end();
333 },
334 .pointer, .array => {
335 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });
336 for (val) |item_val| {
337 try container.fieldArbitraryDepth(item_val, options);
338 }
339 try container.end();
340 },
341 else => comptime unreachable,
342 }
343}
344
345/// Like `value`, but always serializes `val` as a string.
346pub fn string(self: *Serializer, val: []const u8) Error!void {
347 try self.writer.print("\"{f}\"", .{std.zig.fmtString(val)});
348}
349
350/// Options for formatting multiline strings.
351pub const MultilineStringOptions = struct {
352 /// If top level is true, whitespace before and after the multiline string is elided.
353 /// If it is true, a newline is printed, then the value, followed by a newline, and if
354 /// whitespace is true any necessary indentation follows.
355 top_level: bool = false,
356};
357
358pub const MultilineStringError = Error || error{InnerCarriageReturn};
359
360/// Like `value`, but always serializes to a multiline string literal.
361///
362/// Returns `error.InnerCarriageReturn` if `val` contains a CR not followed by a newline,
363/// since multiline strings cannot represent CR without a following newline.
364pub fn multilineString(
365 self: *Serializer,
366 val: []const u8,
367 options: MultilineStringOptions,
368) MultilineStringError!void {
369 // Make sure the string does not contain any carriage returns not followed by a newline
370 var i: usize = 0;
371 while (i < val.len) : (i += 1) {
372 if (val[i] == '\r') {
373 if (i + 1 < val.len) {
374 if (val[i + 1] == '\n') {
375 i += 1;
376 continue;
377 }
378 }
379 return error.InnerCarriageReturn;
380 }
381 }
382
383 if (!options.top_level) {
384 try self.newline();
385 try self.indent();
386 }
387
388 try self.writer.writeAll("\\\\");
389 for (val) |c| {
390 if (c != '\r') {
391 try self.writer.writeByte(c); // We write newlines here even if whitespace off
392 if (c == '\n') {
393 try self.indent();
394 try self.writer.writeAll("\\\\");
395 }
396 }
397 }
398
399 if (!options.top_level) {
400 try self.writer.writeByte('\n'); // Even if whitespace off
401 try self.indent();
402 }
403}
404
405/// Create a `Struct` for writing ZON structs field by field.
406pub fn beginStruct(self: *Serializer, options: ContainerOptions) Error!Struct {
407 return Struct.begin(self, options);
408}
409
410/// Creates a `Tuple` for writing ZON tuples field by field.
411pub fn beginTuple(self: *Serializer, options: ContainerOptions) Error!Tuple {
412 return Tuple.begin(self, options);
413}
414
415fn indent(self: *Serializer) Error!void {
416 if (self.options.whitespace) {
417 try self.writer.splatByteAll(' ', 4 * self.indent_level);
418 }
419}
420
421fn newline(self: *Serializer) Error!void {
422 if (self.options.whitespace) {
423 try self.writer.writeByte('\n');
424 }
425}
426
427fn newlineOrSpace(self: *Serializer, len: usize) Error!void {
428 if (self.containerShouldWrap(len)) {
429 try self.newline();
430 } else {
431 try self.space();
432 }
433}
434
435fn space(self: *Serializer) Error!void {
436 if (self.options.whitespace) {
437 try self.writer.writeByte(' ');
438 }
439}
440
441/// Writes ZON tuples field by field.
442pub const Tuple = struct {
443 container: Container,
444
445 fn begin(parent: *Serializer, options: ContainerOptions) Error!Tuple {
446 return .{
447 .container = try Container.begin(parent, .anon, options),
448 };
449 }
450
451 /// Finishes serializing the tuple.
452 ///
453 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
454 pub fn end(self: *Tuple) Error!void {
455 try self.container.end();
456 self.* = undefined;
457 }
458
459 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
460 pub fn field(
461 self: *Tuple,
462 val: anytype,
463 options: ValueOptions,
464 ) Error!void {
465 try self.container.field(null, val, options);
466 }
467
468 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
469 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
470 pub fn fieldMaxDepth(
471 self: *Tuple,
472 val: anytype,
473 options: ValueOptions,
474 depth: usize,
475 ) DepthError!void {
476 try self.container.fieldMaxDepth(null, val, options, depth);
477 }
478
479 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
480 /// `valueArbitraryDepth`.
481 pub fn fieldArbitraryDepth(
482 self: *Tuple,
483 val: anytype,
484 options: ValueOptions,
485 ) Error!void {
486 try self.container.fieldArbitraryDepth(null, val, options);
487 }
488
489 /// Starts a field with a struct as a value. Returns the struct.
490 pub fn beginStructField(
491 self: *Tuple,
492 options: ContainerOptions,
493 ) Error!Struct {
494 try self.fieldPrefix();
495 return self.container.serializer.beginStruct(options);
496 }
497
498 /// Starts a field with a tuple as a value. Returns the tuple.
499 pub fn beginTupleField(
500 self: *Tuple,
501 options: ContainerOptions,
502 ) Error!Tuple {
503 try self.fieldPrefix();
504 return self.container.serializer.beginTuple(options);
505 }
506
507 /// Print a field prefix. This prints any necessary commas, and whitespace as
508 /// configured. Useful if you want to serialize the field value yourself.
509 pub fn fieldPrefix(self: *Tuple) Error!void {
510 try self.container.fieldPrefix(null);
511 }
512};
513
514/// Writes ZON structs field by field.
515pub const Struct = struct {
516 container: Container,
517
518 fn begin(parent: *Serializer, options: ContainerOptions) Error!Struct {
519 return .{
520 .container = try Container.begin(parent, .named, options),
521 };
522 }
523
524 /// Finishes serializing the struct.
525 ///
526 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
527 pub fn end(self: *Struct) Error!void {
528 try self.container.end();
529 self.* = undefined;
530 }
531
532 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
533 pub fn field(
534 self: *Struct,
535 name: []const u8,
536 val: anytype,
537 options: ValueOptions,
538 ) Error!void {
539 try self.container.field(name, val, options);
540 }
541
542 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
543 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
544 pub fn fieldMaxDepth(
545 self: *Struct,
546 name: []const u8,
547 val: anytype,
548 options: ValueOptions,
549 depth: usize,
550 ) DepthError!void {
551 try self.container.fieldMaxDepth(name, val, options, depth);
552 }
553
554 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
555 /// `valueArbitraryDepth`.
556 pub fn fieldArbitraryDepth(
557 self: *Struct,
558 name: []const u8,
559 val: anytype,
560 options: ValueOptions,
561 ) Error!void {
562 try self.container.fieldArbitraryDepth(name, val, options);
563 }
564
565 /// Starts a field with a struct as a value. Returns the struct.
566 pub fn beginStructField(
567 self: *Struct,
568 name: []const u8,
569 options: ContainerOptions,
570 ) Error!Struct {
571 try self.fieldPrefix(name);
572 return self.container.serializer.beginStruct(options);
573 }
574
575 /// Starts a field with a tuple as a value. Returns the tuple.
576 pub fn beginTupleField(
577 self: *Struct,
578 name: []const u8,
579 options: ContainerOptions,
580 ) Error!Tuple {
581 try self.fieldPrefix(name);
582 return self.container.serializer.beginTuple(options);
583 }
584
585 /// Print a field prefix. This prints any necessary commas, the field name (escaped if
586 /// necessary) and whitespace as configured. Useful if you want to serialize the field
587 /// value yourself.
588 pub fn fieldPrefix(self: *Struct, name: []const u8) Error!void {
589 try self.container.fieldPrefix(name);
590 }
591};
592
593const Container = struct {
594 const FieldStyle = enum { named, anon };
595
596 serializer: *Serializer,
597 field_style: FieldStyle,
598 options: ContainerOptions,
599 empty: bool,
600
601 fn begin(
602 sz: *Serializer,
603 field_style: FieldStyle,
604 options: ContainerOptions,
605 ) Error!Container {
606 if (options.shouldWrap()) sz.indent_level +|= 1;
607 try sz.writer.writeAll(".{");
608 return .{
609 .serializer = sz,
610 .field_style = field_style,
611 .options = options,
612 .empty = true,
613 };
614 }
615
616 fn end(self: *Container) Error!void {
617 if (self.options.shouldWrap()) self.serializer.indent_level -|= 1;
618 if (!self.empty) {
619 if (self.options.shouldWrap()) {
620 if (self.serializer.options.whitespace) {
621 try self.serializer.writer.writeByte(',');
622 }
623 try self.serializer.newline();
624 try self.serializer.indent();
625 } else if (!self.shouldElideSpaces()) {
626 try self.serializer.space();
627 }
628 }
629 try self.serializer.writer.writeByte('}');
630 self.* = undefined;
631 }
632
633 fn fieldPrefix(self: *Container, name: ?[]const u8) Error!void {
634 if (!self.empty) {
635 try self.serializer.writer.writeByte(',');
636 }
637 self.empty = false;
638 if (self.options.shouldWrap()) {
639 try self.serializer.newline();
640 } else if (!self.shouldElideSpaces()) {
641 try self.serializer.space();
642 }
643 if (self.options.shouldWrap()) try self.serializer.indent();
644 if (name) |n| {
645 try self.serializer.ident(n);
646 try self.serializer.space();
647 try self.serializer.writer.writeByte('=');
648 try self.serializer.space();
649 }
650 }
651
652 fn field(
653 self: *Container,
654 name: ?[]const u8,
655 val: anytype,
656 options: ValueOptions,
657 ) Error!void {
658 comptime assert(!typeIsRecursive(@TypeOf(val)));
659 try self.fieldArbitraryDepth(name, val, options);
660 }
661
662 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
663 fn fieldMaxDepth(
664 self: *Container,
665 name: ?[]const u8,
666 val: anytype,
667 options: ValueOptions,
668 depth: usize,
669 ) DepthError!void {
670 try checkValueDepth(val, depth);
671 try self.fieldArbitraryDepth(name, val, options);
672 }
673
674 fn fieldArbitraryDepth(
675 self: *Container,
676 name: ?[]const u8,
677 val: anytype,
678 options: ValueOptions,
679 ) Error!void {
680 try self.fieldPrefix(name);
681 try self.serializer.valueArbitraryDepth(val, options);
682 }
683
684 fn shouldElideSpaces(self: *const Container) bool {
685 return switch (self.options.whitespace_style) {
686 .fields => |fields| self.field_style != .named and fields == 1,
687 else => false,
688 };
689 }
690};
691
692test Serializer {
693 var discarding: Writer.Discarding = .init(&.{});
694 var s: Serializer = .{ .writer = &discarding.writer };
695 var vec2 = try s.beginStruct(.{});
696 try vec2.field("x", 1.5, .{});
697 try vec2.fieldPrefix("prefix");
698 try s.value(2.5, .{});
699 try vec2.end();
700}
701
702inline fn typeIsRecursive(comptime T: type) bool {
703 return comptime typeIsRecursiveInner(T, &.{});
704}
705
706fn typeIsRecursiveInner(comptime T: type, comptime prev_visited: []const type) bool {
707 for (prev_visited) |V| {
708 if (V == T) return true;
709 }
710 const visited = prev_visited ++ .{T};
711
712 return switch (@typeInfo(T)) {
713 .pointer => |pointer| typeIsRecursiveInner(pointer.child, visited),
714 .optional => |optional| typeIsRecursiveInner(optional.child, visited),
715 .array => |array| typeIsRecursiveInner(array.child, visited),
716 .vector => |vector| typeIsRecursiveInner(vector.child, visited),
717 .@"struct" => |@"struct"| for (@"struct".fields) |field| {
718 if (typeIsRecursiveInner(field.type, visited)) break true;
719 } else false,
720 .@"union" => |@"union"| inline for (@"union".fields) |field| {
721 if (typeIsRecursiveInner(field.type, visited)) break true;
722 } else false,
723 else => false,
724 };
725}
726
727test typeIsRecursive {
728 try std.testing.expect(!typeIsRecursive(bool));
729 try std.testing.expect(!typeIsRecursive(struct { x: i32, y: i32 }));
730 try std.testing.expect(!typeIsRecursive(struct { i32, i32 }));
731 try std.testing.expect(typeIsRecursive(struct { x: i32, y: i32, z: *@This() }));
732 try std.testing.expect(typeIsRecursive(struct {
733 a: struct {
734 const A = @This();
735 b: struct {
736 c: *struct {
737 a: ?A,
738 },
739 },
740 },
741 }));
742 try std.testing.expect(typeIsRecursive(struct {
743 a: [3]*@This(),
744 }));
745 try std.testing.expect(typeIsRecursive(struct {
746 a: union { a: i32, b: *@This() },
747 }));
748}
749
750fn checkValueDepth(val: anytype, depth: usize) error{ExceededMaxDepth}!void {
751 if (depth == 0) return error.ExceededMaxDepth;
752 const child_depth = depth - 1;
753
754 switch (@typeInfo(@TypeOf(val))) {
755 .pointer => |pointer| switch (pointer.size) {
756 .one => try checkValueDepth(val.*, child_depth),
757 .slice => for (val) |item| {
758 try checkValueDepth(item, child_depth);
759 },
760 .c, .many => {},
761 },
762 .array => for (val) |item| {
763 try checkValueDepth(item, child_depth);
764 },
765 .@"struct" => |@"struct"| inline for (@"struct".fields) |field_info| {
766 try checkValueDepth(@field(val, field_info.name), child_depth);
767 },
768 .@"union" => |@"union"| if (@"union".tag_type == null) {
769 return;
770 } else switch (val) {
771 inline else => |payload| {
772 return checkValueDepth(payload, child_depth);
773 },
774 },
775 .optional => if (val) |inner| try checkValueDepth(inner, child_depth),
776 else => {},
777 }
778}
779
780fn expectValueDepthEquals(expected: usize, v: anytype) !void {
781 try checkValueDepth(v, expected);
782 try std.testing.expectError(error.ExceededMaxDepth, checkValueDepth(v, expected - 1));
783}
784
785test checkValueDepth {
786 try expectValueDepthEquals(1, 10);
787 try expectValueDepthEquals(2, .{ .x = 1, .y = 2 });
788 try expectValueDepthEquals(2, .{ 1, 2 });
789 try expectValueDepthEquals(3, .{ 1, .{ 2, 3 } });
790 try expectValueDepthEquals(3, .{ .{ 1, 2 }, 3 });
791 try expectValueDepthEquals(3, .{ .x = 0, .y = 1, .z = .{ .x = 3 } });
792 try expectValueDepthEquals(3, .{ .x = 0, .y = .{ .x = 1 }, .z = 2 });
793 try expectValueDepthEquals(3, .{ .x = .{ .x = 0 }, .y = 1, .z = 2 });
794 try expectValueDepthEquals(2, @as(?u32, 1));
795 try expectValueDepthEquals(1, @as(?u32, null));
796 try expectValueDepthEquals(1, null);
797 try expectValueDepthEquals(2, &1);
798 try expectValueDepthEquals(3, &@as(?u32, 1));
799
800 const Union = union(enum) {
801 x: u32,
802 y: struct { x: u32 },
803 };
804 try expectValueDepthEquals(2, Union{ .x = 1 });
805 try expectValueDepthEquals(3, Union{ .y = .{ .x = 1 } });
806
807 const Recurse = struct { r: ?*const @This() };
808 try expectValueDepthEquals(2, Recurse{ .r = null });
809 try expectValueDepthEquals(5, Recurse{ .r = &Recurse{ .r = null } });
810 try expectValueDepthEquals(8, Recurse{ .r = &Recurse{ .r = &Recurse{ .r = null } } });
811
812 try expectValueDepthEquals(2, @as([]const u8, &.{ 1, 2, 3 }));
813 try expectValueDepthEquals(3, @as([]const []const u8, &.{&.{ 1, 2, 3 }}));
814}
815
816inline fn canSerializeType(T: type) bool {
817 comptime return canSerializeTypeInner(T, &.{}, false);
818}
819
820fn canSerializeTypeInner(
821 T: type,
822 /// Visited structs and unions, to avoid infinite recursion.
823 /// Tracking more types is unnecessary, and a little complex due to optional nesting.
824 visited: []const type,
825 parent_is_optional: bool,
826) bool {
827 return switch (@typeInfo(T)) {
828 .bool,
829 .int,
830 .float,
831 .comptime_float,
832 .comptime_int,
833 .null,
834 .enum_literal,
835 => true,
836
837 .noreturn,
838 .void,
839 .type,
840 .undefined,
841 .error_union,
842 .error_set,
843 .@"fn",
844 .frame,
845 .@"anyframe",
846 .@"opaque",
847 => false,
848
849 .@"enum" => |@"enum"| @"enum".is_exhaustive,
850
851 .pointer => |pointer| switch (pointer.size) {
852 .one => canSerializeTypeInner(pointer.child, visited, parent_is_optional),
853 .slice => canSerializeTypeInner(pointer.child, visited, false),
854 .many, .c => false,
855 },
856
857 .optional => |optional| if (parent_is_optional)
858 false
859 else
860 canSerializeTypeInner(optional.child, visited, true),
861
862 .array => |array| canSerializeTypeInner(array.child, visited, false),
863 .vector => |vector| canSerializeTypeInner(vector.child, visited, false),
864
865 .@"struct" => |@"struct"| {
866 for (visited) |V| if (T == V) return true;
867 const new_visited = visited ++ .{T};
868 for (@"struct".fields) |field| {
869 if (!canSerializeTypeInner(field.type, new_visited, false)) return false;
870 }
871 return true;
872 },
873 .@"union" => |@"union"| {
874 for (visited) |V| if (T == V) return true;
875 const new_visited = visited ++ .{T};
876 if (@"union".tag_type == null) return false;
877 for (@"union".fields) |field| {
878 if (field.type != void and !canSerializeTypeInner(field.type, new_visited, false)) {
879 return false;
880 }
881 }
882 return true;
883 },
884 };
885}
886
887test canSerializeType {
888 try std.testing.expect(!comptime canSerializeType(void));
889 try std.testing.expect(!comptime canSerializeType(struct { f: [*]u8 }));
890 try std.testing.expect(!comptime canSerializeType(struct { error{foo} }));
891 try std.testing.expect(!comptime canSerializeType(union(enum) { a: void, f: [*c]u8 }));
892 try std.testing.expect(!comptime canSerializeType(@Vector(0, [*c]u8)));
893 try std.testing.expect(!comptime canSerializeType(*?[*c]u8));
894 try std.testing.expect(!comptime canSerializeType(enum(u8) { _ }));
895 try std.testing.expect(!comptime canSerializeType(union { foo: void }));
896 try std.testing.expect(comptime canSerializeType(union(enum) { foo: void }));
897 try std.testing.expect(comptime canSerializeType(comptime_float));
898 try std.testing.expect(comptime canSerializeType(comptime_int));
899 try std.testing.expect(!comptime canSerializeType(struct { comptime foo: ??u8 = null }));
900 try std.testing.expect(comptime canSerializeType(@TypeOf(.foo)));
901 try std.testing.expect(comptime canSerializeType(?u8));
902 try std.testing.expect(comptime canSerializeType(*?*u8));
903 try std.testing.expect(comptime canSerializeType(?struct {
904 foo: ?struct {
905 ?union(enum) {
906 a: ?@Vector(0, ?*u8),
907 },
908 ?struct {
909 f: ?[]?u8,
910 },
911 },
912 }));
913 try std.testing.expect(!comptime canSerializeType(??u8));
914 try std.testing.expect(!comptime canSerializeType(?*?u8));
915 try std.testing.expect(!comptime canSerializeType(*?*?*u8));
916 try std.testing.expect(comptime canSerializeType(struct { x: comptime_int = 2 }));
917 try std.testing.expect(comptime canSerializeType(struct { x: comptime_float = 2 }));
918 try std.testing.expect(comptime canSerializeType(struct { comptime_int }));
919 try std.testing.expect(comptime canSerializeType(struct { comptime x: @TypeOf(.foo) = .foo }));
920 const Recursive = struct { foo: ?*@This() };
921 try std.testing.expect(comptime canSerializeType(Recursive));
922
923 // Make sure we validate nested optional before we early out due to already having seen
924 // a type recursion!
925 try std.testing.expect(!comptime canSerializeType(struct {
926 add_to_visited: ?u8,
927 retrieve_from_visited: ??u8,
928 }));
929}
lib/std/zon/parse.zig+5-5
......@@ -64,14 +64,14 @@ pub const Error = union(enum) {
6464 }
6565 };
6666
67 fn formatMessage(self: []const u8, w: *std.io.Writer) std.io.Writer.Error!void {
67 fn formatMessage(self: []const u8, w: *std.Io.Writer) std.Io.Writer.Error!void {
6868 // Just writes the string for now, but we're keeping this behind a formatter so we have
6969 // the option to extend it in the future to print more advanced messages (like `Error`
7070 // does) without breaking the API.
7171 try w.writeAll(self);
7272 }
7373
74 pub fn fmtMessage(self: Note, diag: *const Diagnostics) std.fmt.Formatter([]const u8, Note.formatMessage) {
74 pub fn fmtMessage(self: Note, diag: *const Diagnostics) std.fmt.Alt([]const u8, Note.formatMessage) {
7575 return .{ .data = switch (self) {
7676 .zoir => |note| note.msg.get(diag.zoir),
7777 .type_check => |note| note.msg,
......@@ -147,14 +147,14 @@ pub const Error = union(enum) {
147147 diag: *const Diagnostics,
148148 };
149149
150 fn formatMessage(self: FormatMessage, w: *std.io.Writer) std.io.Writer.Error!void {
150 fn formatMessage(self: FormatMessage, w: *std.Io.Writer) std.Io.Writer.Error!void {
151151 switch (self.err) {
152152 .zoir => |err| try w.writeAll(err.msg.get(self.diag.zoir)),
153153 .type_check => |tc| try w.writeAll(tc.message),
154154 }
155155 }
156156
157 pub fn fmtMessage(self: @This(), diag: *const Diagnostics) std.fmt.Formatter(FormatMessage, formatMessage) {
157 pub fn fmtMessage(self: @This(), diag: *const Diagnostics) std.fmt.Alt(FormatMessage, formatMessage) {
158158 return .{ .data = .{
159159 .err = self,
160160 .diag = diag,
......@@ -226,7 +226,7 @@ pub const Diagnostics = struct {
226226 return .{ .diag = self };
227227 }
228228
229 pub fn format(self: *const @This(), w: *std.io.Writer) std.io.Writer.Error!void {
229 pub fn format(self: *const @This(), w: *std.Io.Writer) std.Io.Writer.Error!void {
230230 var errors = self.iterateErrors();
231231 while (errors.next()) |err| {
232232 const loc = err.getLocation(self);
lib/std/zon/stringify.zig+29-962
......@@ -22,14 +22,14 @@
2222
2323const std = @import("std");
2424const assert = std.debug.assert;
25const Writer = std.io.Writer;
25const Writer = std.Io.Writer;
26const Serializer = std.zon.Serializer;
2627
27/// Options for `serialize`.
2828pub const SerializeOptions = struct {
2929 /// If false, whitespace is omitted. Otherwise whitespace is emitted in standard Zig style.
3030 whitespace: bool = true,
3131 /// Determines when to emit Unicode code point literals as opposed to integer literals.
32 emit_codepoint_literals: EmitCodepointLiterals = .never,
32 emit_codepoint_literals: Serializer.EmitCodepointLiterals = .never,
3333 /// If true, slices of `u8`s, and pointers to arrays of `u8` are serialized as containers.
3434 /// Otherwise they are serialized as string literals.
3535 emit_strings_as_containers: bool = false,
......@@ -93,102 +93,6 @@ pub fn serializeArbitraryDepth(
9393 });
9494}
9595
96inline fn typeIsRecursive(comptime T: type) bool {
97 return comptime typeIsRecursiveInner(T, &.{});
98}
99
100fn typeIsRecursiveInner(comptime T: type, comptime prev_visited: []const type) bool {
101 for (prev_visited) |V| {
102 if (V == T) return true;
103 }
104 const visited = prev_visited ++ .{T};
105
106 return switch (@typeInfo(T)) {
107 .pointer => |pointer| typeIsRecursiveInner(pointer.child, visited),
108 .optional => |optional| typeIsRecursiveInner(optional.child, visited),
109 .array => |array| typeIsRecursiveInner(array.child, visited),
110 .vector => |vector| typeIsRecursiveInner(vector.child, visited),
111 .@"struct" => |@"struct"| for (@"struct".fields) |field| {
112 if (typeIsRecursiveInner(field.type, visited)) break true;
113 } else false,
114 .@"union" => |@"union"| inline for (@"union".fields) |field| {
115 if (typeIsRecursiveInner(field.type, visited)) break true;
116 } else false,
117 else => false,
118 };
119}
120
121inline fn canSerializeType(T: type) bool {
122 comptime return canSerializeTypeInner(T, &.{}, false);
123}
124
125fn canSerializeTypeInner(
126 T: type,
127 /// Visited structs and unions, to avoid infinite recursion.
128 /// Tracking more types is unnecessary, and a little complex due to optional nesting.
129 visited: []const type,
130 parent_is_optional: bool,
131) bool {
132 return switch (@typeInfo(T)) {
133 .bool,
134 .int,
135 .float,
136 .comptime_float,
137 .comptime_int,
138 .null,
139 .enum_literal,
140 => true,
141
142 .noreturn,
143 .void,
144 .type,
145 .undefined,
146 .error_union,
147 .error_set,
148 .@"fn",
149 .frame,
150 .@"anyframe",
151 .@"opaque",
152 => false,
153
154 .@"enum" => |@"enum"| @"enum".is_exhaustive,
155
156 .pointer => |pointer| switch (pointer.size) {
157 .one => canSerializeTypeInner(pointer.child, visited, parent_is_optional),
158 .slice => canSerializeTypeInner(pointer.child, visited, false),
159 .many, .c => false,
160 },
161
162 .optional => |optional| if (parent_is_optional)
163 false
164 else
165 canSerializeTypeInner(optional.child, visited, true),
166
167 .array => |array| canSerializeTypeInner(array.child, visited, false),
168 .vector => |vector| canSerializeTypeInner(vector.child, visited, false),
169
170 .@"struct" => |@"struct"| {
171 for (visited) |V| if (T == V) return true;
172 const new_visited = visited ++ .{T};
173 for (@"struct".fields) |field| {
174 if (!canSerializeTypeInner(field.type, new_visited, false)) return false;
175 }
176 return true;
177 },
178 .@"union" => |@"union"| {
179 for (visited) |V| if (T == V) return true;
180 const new_visited = visited ++ .{T};
181 if (@"union".tag_type == null) return false;
182 for (@"union".fields) |field| {
183 if (field.type != void and !canSerializeTypeInner(field.type, new_visited, false)) {
184 return false;
185 }
186 }
187 return true;
188 },
189 };
190}
191
19296fn isNestedOptional(T: type) bool {
19397 comptime switch (@typeInfo(T)) {
19498 .optional => |optional| return isNestedOptionalInner(optional.child),
......@@ -210,852 +114,13 @@ fn isNestedOptionalInner(T: type) bool {
210114 }
211115}
212116
213test "std.zon stringify canSerializeType" {
214 try std.testing.expect(!comptime canSerializeType(void));
215 try std.testing.expect(!comptime canSerializeType(struct { f: [*]u8 }));
216 try std.testing.expect(!comptime canSerializeType(struct { error{foo} }));
217 try std.testing.expect(!comptime canSerializeType(union(enum) { a: void, f: [*c]u8 }));
218 try std.testing.expect(!comptime canSerializeType(@Vector(0, [*c]u8)));
219 try std.testing.expect(!comptime canSerializeType(*?[*c]u8));
220 try std.testing.expect(!comptime canSerializeType(enum(u8) { _ }));
221 try std.testing.expect(!comptime canSerializeType(union { foo: void }));
222 try std.testing.expect(comptime canSerializeType(union(enum) { foo: void }));
223 try std.testing.expect(comptime canSerializeType(comptime_float));
224 try std.testing.expect(comptime canSerializeType(comptime_int));
225 try std.testing.expect(!comptime canSerializeType(struct { comptime foo: ??u8 = null }));
226 try std.testing.expect(comptime canSerializeType(@TypeOf(.foo)));
227 try std.testing.expect(comptime canSerializeType(?u8));
228 try std.testing.expect(comptime canSerializeType(*?*u8));
229 try std.testing.expect(comptime canSerializeType(?struct {
230 foo: ?struct {
231 ?union(enum) {
232 a: ?@Vector(0, ?*u8),
233 },
234 ?struct {
235 f: ?[]?u8,
236 },
237 },
238 }));
239 try std.testing.expect(!comptime canSerializeType(??u8));
240 try std.testing.expect(!comptime canSerializeType(?*?u8));
241 try std.testing.expect(!comptime canSerializeType(*?*?*u8));
242 try std.testing.expect(comptime canSerializeType(struct { x: comptime_int = 2 }));
243 try std.testing.expect(comptime canSerializeType(struct { x: comptime_float = 2 }));
244 try std.testing.expect(comptime canSerializeType(struct { comptime_int }));
245 try std.testing.expect(comptime canSerializeType(struct { comptime x: @TypeOf(.foo) = .foo }));
246 const Recursive = struct { foo: ?*@This() };
247 try std.testing.expect(comptime canSerializeType(Recursive));
248
249 // Make sure we validate nested optional before we early out due to already having seen
250 // a type recursion!
251 try std.testing.expect(!comptime canSerializeType(struct {
252 add_to_visited: ?u8,
253 retrieve_from_visited: ??u8,
254 }));
255}
256
257test "std.zon typeIsRecursive" {
258 try std.testing.expect(!typeIsRecursive(bool));
259 try std.testing.expect(!typeIsRecursive(struct { x: i32, y: i32 }));
260 try std.testing.expect(!typeIsRecursive(struct { i32, i32 }));
261 try std.testing.expect(typeIsRecursive(struct { x: i32, y: i32, z: *@This() }));
262 try std.testing.expect(typeIsRecursive(struct {
263 a: struct {
264 const A = @This();
265 b: struct {
266 c: *struct {
267 a: ?A,
268 },
269 },
270 },
271 }));
272 try std.testing.expect(typeIsRecursive(struct {
273 a: [3]*@This(),
274 }));
275 try std.testing.expect(typeIsRecursive(struct {
276 a: union { a: i32, b: *@This() },
277 }));
278}
279
280fn checkValueDepth(val: anytype, depth: usize) error{ExceededMaxDepth}!void {
281 if (depth == 0) return error.ExceededMaxDepth;
282 const child_depth = depth - 1;
283
284 switch (@typeInfo(@TypeOf(val))) {
285 .pointer => |pointer| switch (pointer.size) {
286 .one => try checkValueDepth(val.*, child_depth),
287 .slice => for (val) |item| {
288 try checkValueDepth(item, child_depth);
289 },
290 .c, .many => {},
291 },
292 .array => for (val) |item| {
293 try checkValueDepth(item, child_depth);
294 },
295 .@"struct" => |@"struct"| inline for (@"struct".fields) |field_info| {
296 try checkValueDepth(@field(val, field_info.name), child_depth);
297 },
298 .@"union" => |@"union"| if (@"union".tag_type == null) {
299 return;
300 } else switch (val) {
301 inline else => |payload| {
302 return checkValueDepth(payload, child_depth);
303 },
304 },
305 .optional => if (val) |inner| try checkValueDepth(inner, child_depth),
306 else => {},
307 }
308}
309
310fn expectValueDepthEquals(expected: usize, value: anytype) !void {
311 try checkValueDepth(value, expected);
312 try std.testing.expectError(error.ExceededMaxDepth, checkValueDepth(value, expected - 1));
313}
314
315test "std.zon checkValueDepth" {
316 try expectValueDepthEquals(1, 10);
317 try expectValueDepthEquals(2, .{ .x = 1, .y = 2 });
318 try expectValueDepthEquals(2, .{ 1, 2 });
319 try expectValueDepthEquals(3, .{ 1, .{ 2, 3 } });
320 try expectValueDepthEquals(3, .{ .{ 1, 2 }, 3 });
321 try expectValueDepthEquals(3, .{ .x = 0, .y = 1, .z = .{ .x = 3 } });
322 try expectValueDepthEquals(3, .{ .x = 0, .y = .{ .x = 1 }, .z = 2 });
323 try expectValueDepthEquals(3, .{ .x = .{ .x = 0 }, .y = 1, .z = 2 });
324 try expectValueDepthEquals(2, @as(?u32, 1));
325 try expectValueDepthEquals(1, @as(?u32, null));
326 try expectValueDepthEquals(1, null);
327 try expectValueDepthEquals(2, &1);
328 try expectValueDepthEquals(3, &@as(?u32, 1));
329
330 const Union = union(enum) {
331 x: u32,
332 y: struct { x: u32 },
333 };
334 try expectValueDepthEquals(2, Union{ .x = 1 });
335 try expectValueDepthEquals(3, Union{ .y = .{ .x = 1 } });
336
337 const Recurse = struct { r: ?*const @This() };
338 try expectValueDepthEquals(2, Recurse{ .r = null });
339 try expectValueDepthEquals(5, Recurse{ .r = &Recurse{ .r = null } });
340 try expectValueDepthEquals(8, Recurse{ .r = &Recurse{ .r = &Recurse{ .r = null } } });
341
342 try expectValueDepthEquals(2, @as([]const u8, &.{ 1, 2, 3 }));
343 try expectValueDepthEquals(3, @as([]const []const u8, &.{&.{ 1, 2, 3 }}));
344}
345
346/// Determines when to emit Unicode code point literals as opposed to integer literals.
347pub const EmitCodepointLiterals = enum {
348 /// Never emit Unicode code point literals.
349 never,
350 /// Emit Unicode code point literals for any `u8` in the printable ASCII range.
351 printable_ascii,
352 /// Emit Unicode code point literals for any unsigned integer with 21 bits or fewer
353 /// whose value is a valid non-surrogate code point.
354 always,
355
356 /// If the value should be emitted as a Unicode codepoint, return it as a u21.
357 fn emitAsCodepoint(self: @This(), val: anytype) ?u21 {
358 // Rule out incompatible integer types
359 switch (@typeInfo(@TypeOf(val))) {
360 .int => |int_info| if (int_info.signedness == .signed or int_info.bits > 21) {
361 return null;
362 },
363 .comptime_int => {},
364 else => comptime unreachable,
365 }
366
367 // Return null if the value shouldn't be printed as a Unicode codepoint, or the value casted
368 // to a u21 if it should.
369 switch (self) {
370 .always => {
371 const c = std.math.cast(u21, val) orelse return null;
372 if (!std.unicode.utf8ValidCodepoint(c)) return null;
373 return c;
374 },
375 .printable_ascii => {
376 const c = std.math.cast(u8, val) orelse return null;
377 if (!std.ascii.isPrint(c)) return null;
378 return c;
379 },
380 .never => {
381 return null;
382 },
383 }
384 }
385};
386
387/// Options for serialization of an individual value.
388///
389/// See `SerializeOptions` for more information on these options.
390pub const ValueOptions = struct {
391 emit_codepoint_literals: EmitCodepointLiterals = .never,
392 emit_strings_as_containers: bool = false,
393 emit_default_optional_fields: bool = true,
394};
395
396/// Options for manual serialization of container types.
397pub const SerializeContainerOptions = struct {
398 /// The whitespace style that should be used for this container. Ignored if whitespace is off.
399 whitespace_style: union(enum) {
400 /// If true, wrap every field. If false do not.
401 wrap: bool,
402 /// Automatically decide whether to wrap or not based on the number of fields. Following
403 /// the standard rule of thumb, containers with more than two fields are wrapped.
404 fields: usize,
405 } = .{ .wrap = true },
406
407 fn shouldWrap(self: SerializeContainerOptions) bool {
408 return switch (self.whitespace_style) {
409 .wrap => |wrap| wrap,
410 .fields => |fields| fields > 2,
411 };
412 }
413};
414
415/// Lower level control over serialization, you can create a new instance with `serializer`.
416///
417/// Useful when you want control over which fields are serialized, how they're represented,
418/// or want to write a ZON object that does not exist in memory.
419///
420/// You can serialize values with `value`. To serialize recursive types, the following are provided:
421/// * `valueMaxDepth`
422/// * `valueArbitraryDepth`
423///
424/// You can also serialize values using specific notations:
425/// * `int`
426/// * `float`
427/// * `codePoint`
428/// * `tuple`
429/// * `tupleMaxDepth`
430/// * `tupleArbitraryDepth`
431/// * `string`
432/// * `multilineString`
433///
434/// For manual serialization of containers, see:
435/// * `beginStruct`
436/// * `beginTuple`
437pub const Serializer = struct {
438 options: Options = .{},
439 indent_level: u8 = 0,
440 writer: *Writer,
441
442 pub const Error = Writer.Error;
443 pub const DepthError = Error || error{ExceededMaxDepth};
444
445 pub const Options = struct {
446 /// If false, only syntactically necessary whitespace is emitted.
447 whitespace: bool = true,
448 };
449
450 /// Serialize a value, similar to `serialize`.
451 pub fn value(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
452 comptime assert(!typeIsRecursive(@TypeOf(val)));
453 return self.valueArbitraryDepth(val, options);
454 }
455
456 /// Serialize a value, similar to `serializeMaxDepth`.
457 /// Can return `error.ExceededMaxDepth`.
458 pub fn valueMaxDepth(self: *Serializer, val: anytype, options: ValueOptions, depth: usize) DepthError!void {
459 try checkValueDepth(val, depth);
460 return self.valueArbitraryDepth(val, options);
461 }
462
463 /// Serialize a value, similar to `serializeArbitraryDepth`.
464 pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
465 comptime assert(canSerializeType(@TypeOf(val)));
466 switch (@typeInfo(@TypeOf(val))) {
467 .int, .comptime_int => if (options.emit_codepoint_literals.emitAsCodepoint(val)) |c| {
468 self.codePoint(c) catch |err| switch (err) {
469 error.InvalidCodepoint => unreachable, // Already validated
470 else => |e| return e,
471 };
472 } else {
473 try self.int(val);
474 },
475 .float, .comptime_float => try self.float(val),
476 .bool, .null => try std.fmt.format(self.writer, "{}", .{val}),
477 .enum_literal => try self.ident(@tagName(val)),
478 .@"enum" => try self.ident(@tagName(val)),
479 .pointer => |pointer| {
480 // Try to serialize as a string
481 const item: ?type = switch (@typeInfo(pointer.child)) {
482 .array => |array| array.child,
483 else => if (pointer.size == .slice) pointer.child else null,
484 };
485 if (item == u8 and
486 (pointer.sentinel() == null or pointer.sentinel() == 0) and
487 !options.emit_strings_as_containers)
488 {
489 return try self.string(val);
490 }
491
492 // Serialize as either a tuple or as the child type
493 switch (pointer.size) {
494 .slice => try self.tupleImpl(val, options),
495 .one => try self.valueArbitraryDepth(val.*, options),
496 else => comptime unreachable,
497 }
498 },
499 .array => {
500 var container = try self.beginTuple(
501 .{ .whitespace_style = .{ .fields = val.len } },
502 );
503 for (val) |item_val| {
504 try container.fieldArbitraryDepth(item_val, options);
505 }
506 try container.end();
507 },
508 .@"struct" => |@"struct"| if (@"struct".is_tuple) {
509 var container = try self.beginTuple(
510 .{ .whitespace_style = .{ .fields = @"struct".fields.len } },
511 );
512 inline for (val) |field_value| {
513 try container.fieldArbitraryDepth(field_value, options);
514 }
515 try container.end();
516 } else {
517 // Decide which fields to emit
518 const fields, const skipped: [@"struct".fields.len]bool = if (options.emit_default_optional_fields) b: {
519 break :b .{ @"struct".fields.len, @splat(false) };
520 } else b: {
521 var fields = @"struct".fields.len;
522 var skipped: [@"struct".fields.len]bool = @splat(false);
523 inline for (@"struct".fields, &skipped) |field_info, *skip| {
524 if (field_info.default_value_ptr) |ptr| {
525 const default: *const field_info.type = @ptrCast(@alignCast(ptr));
526 const field_value = @field(val, field_info.name);
527 if (std.meta.eql(field_value, default.*)) {
528 skip.* = true;
529 fields -= 1;
530 }
531 }
532 }
533 break :b .{ fields, skipped };
534 };
535
536 // Emit those fields
537 var container = try self.beginStruct(
538 .{ .whitespace_style = .{ .fields = fields } },
539 );
540 inline for (@"struct".fields, skipped) |field_info, skip| {
541 if (!skip) {
542 try container.fieldArbitraryDepth(
543 field_info.name,
544 @field(val, field_info.name),
545 options,
546 );
547 }
548 }
549 try container.end();
550 },
551 .@"union" => |@"union"| {
552 comptime assert(@"union".tag_type != null);
553 switch (val) {
554 inline else => |pl, tag| if (@TypeOf(pl) == void)
555 try self.writer.print(".{s}", .{@tagName(tag)})
556 else {
557 var container = try self.beginStruct(.{ .whitespace_style = .{ .fields = 1 } });
558
559 try container.fieldArbitraryDepth(
560 @tagName(tag),
561 pl,
562 options,
563 );
564
565 try container.end();
566 },
567 }
568 },
569 .optional => if (val) |inner| {
570 try self.valueArbitraryDepth(inner, options);
571 } else {
572 try self.writer.writeAll("null");
573 },
574 .vector => |vector| {
575 var container = try self.beginTuple(
576 .{ .whitespace_style = .{ .fields = vector.len } },
577 );
578 for (0..vector.len) |i| {
579 try container.fieldArbitraryDepth(val[i], options);
580 }
581 try container.end();
582 },
583
584 else => comptime unreachable,
585 }
586 }
587
588 /// Serialize an integer.
589 pub fn int(self: *Serializer, val: anytype) Error!void {
590 try self.writer.printIntOptions(val, 10, .lower, .{});
591 }
592
593 /// Serialize a float.
594 pub fn float(self: *Serializer, val: anytype) Error!void {
595 switch (@typeInfo(@TypeOf(val))) {
596 .float => if (std.math.isNan(val)) {
597 return self.writer.writeAll("nan");
598 } else if (std.math.isPositiveInf(val)) {
599 return self.writer.writeAll("inf");
600 } else if (std.math.isNegativeInf(val)) {
601 return self.writer.writeAll("-inf");
602 } else if (std.math.isNegativeZero(val)) {
603 return self.writer.writeAll("-0.0");
604 } else {
605 try std.fmt.format(self.writer, "{d}", .{val});
606 },
607 .comptime_float => if (val == 0) {
608 return self.writer.writeAll("0");
609 } else {
610 try std.fmt.format(self.writer, "{d}", .{val});
611 },
612 else => comptime unreachable,
613 }
614 }
615
616 /// Serialize `name` as an identifier prefixed with `.`.
617 ///
618 /// Escapes the identifier if necessary.
619 pub fn ident(self: *Serializer, name: []const u8) Error!void {
620 try self.writer.print(".{fp_}", .{std.zig.fmtId(name)});
621 }
622
623 pub const CodePointError = Error || error{InvalidCodepoint};
624
625 /// Serialize `val` as a Unicode codepoint.
626 ///
627 /// Returns `error.InvalidCodepoint` if `val` is not a valid Unicode codepoint.
628 pub fn codePoint(self: *Serializer, val: u21) CodePointError!void {
629 var buf: [8]u8 = undefined;
630 const len = std.unicode.utf8Encode(val, &buf) catch return error.InvalidCodepoint;
631 const str = buf[0..len];
632 try std.fmt.format(self.writer, "'{f'}'", .{std.zig.fmtEscapes(str)});
633 }
634
635 /// Like `value`, but always serializes `val` as a tuple.
636 ///
637 /// Will fail at comptime if `val` is not a tuple, array, pointer to an array, or slice.
638 pub fn tuple(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
639 comptime assert(!typeIsRecursive(@TypeOf(val)));
640 try self.tupleArbitraryDepth(val, options);
641 }
642
643 /// Like `tuple`, but recursive types are allowed.
644 ///
645 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
646 pub fn tupleMaxDepth(
647 self: *Serializer,
648 val: anytype,
649 options: ValueOptions,
650 depth: usize,
651 ) DepthError!void {
652 try checkValueDepth(val, depth);
653 try self.tupleArbitraryDepth(val, options);
654 }
655
656 /// Like `tuple`, but recursive types are allowed.
657 ///
658 /// It is the caller's responsibility to ensure that `val` does not contain cycles.
659 pub fn tupleArbitraryDepth(
660 self: *Serializer,
661 val: anytype,
662 options: ValueOptions,
663 ) Error!void {
664 try self.tupleImpl(val, options);
665 }
666
667 fn tupleImpl(self: *Serializer, val: anytype, options: ValueOptions) Error!void {
668 comptime assert(canSerializeType(@TypeOf(val)));
669 switch (@typeInfo(@TypeOf(val))) {
670 .@"struct" => {
671 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });
672 inline for (val) |item_val| {
673 try container.fieldArbitraryDepth(item_val, options);
674 }
675 try container.end();
676 },
677 .pointer, .array => {
678 var container = try self.beginTuple(.{ .whitespace_style = .{ .fields = val.len } });
679 for (val) |item_val| {
680 try container.fieldArbitraryDepth(item_val, options);
681 }
682 try container.end();
683 },
684 else => comptime unreachable,
685 }
686 }
687
688 /// Like `value`, but always serializes `val` as a string.
689 pub fn string(self: *Serializer, val: []const u8) Error!void {
690 try std.fmt.format(self.writer, "\"{f}\"", .{std.zig.fmtEscapes(val)});
691 }
692
693 /// Options for formatting multiline strings.
694 pub const MultilineStringOptions = struct {
695 /// If top level is true, whitespace before and after the multiline string is elided.
696 /// If it is true, a newline is printed, then the value, followed by a newline, and if
697 /// whitespace is true any necessary indentation follows.
698 top_level: bool = false,
699 };
700
701 pub const MultilineStringError = Error || error{InnerCarriageReturn};
702
703 /// Like `value`, but always serializes to a multiline string literal.
704 ///
705 /// Returns `error.InnerCarriageReturn` if `val` contains a CR not followed by a newline,
706 /// since multiline strings cannot represent CR without a following newline.
707 pub fn multilineString(
708 self: *Serializer,
709 val: []const u8,
710 options: MultilineStringOptions,
711 ) MultilineStringError!void {
712 // Make sure the string does not contain any carriage returns not followed by a newline
713 var i: usize = 0;
714 while (i < val.len) : (i += 1) {
715 if (val[i] == '\r') {
716 if (i + 1 < val.len) {
717 if (val[i + 1] == '\n') {
718 i += 1;
719 continue;
720 }
721 }
722 return error.InnerCarriageReturn;
723 }
724 }
725
726 if (!options.top_level) {
727 try self.newline();
728 try self.indent();
729 }
730
731 try self.writer.writeAll("\\\\");
732 for (val) |c| {
733 if (c != '\r') {
734 try self.writer.writeByte(c); // We write newlines here even if whitespace off
735 if (c == '\n') {
736 try self.indent();
737 try self.writer.writeAll("\\\\");
738 }
739 }
740 }
741
742 if (!options.top_level) {
743 try self.writer.writeByte('\n'); // Even if whitespace off
744 try self.indent();
745 }
746 }
747
748 /// Create a `Struct` for writing ZON structs field by field.
749 pub fn beginStruct(
750 self: *Serializer,
751 options: SerializeContainerOptions,
752 ) Error!Struct {
753 return Struct.begin(self, options);
754 }
755
756 /// Creates a `Tuple` for writing ZON tuples field by field.
757 pub fn beginTuple(
758 self: *Serializer,
759 options: SerializeContainerOptions,
760 ) Error!Tuple {
761 return Tuple.begin(self, options);
762 }
763
764 fn indent(self: *Serializer) Error!void {
765 if (self.options.whitespace) {
766 try self.writer.splatByteAll(' ', 4 * self.indent_level);
767 }
768 }
769
770 fn newline(self: *Serializer) Error!void {
771 if (self.options.whitespace) {
772 try self.writer.writeByte('\n');
773 }
774 }
775
776 fn newlineOrSpace(self: *Serializer, len: usize) Error!void {
777 if (self.containerShouldWrap(len)) {
778 try self.newline();
779 } else {
780 try self.space();
781 }
782 }
783
784 fn space(self: *Serializer) Error!void {
785 if (self.options.whitespace) {
786 try self.writer.writeByte(' ');
787 }
788 }
789
790 /// Writes ZON tuples field by field.
791 pub const Tuple = struct {
792 container: Container,
793
794 fn begin(parent: *Serializer, options: SerializeContainerOptions) Error!Tuple {
795 return .{
796 .container = try Container.begin(parent, .anon, options),
797 };
798 }
799
800 /// Finishes serializing the tuple.
801 ///
802 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
803 pub fn end(self: *Tuple) Error!void {
804 try self.container.end();
805 self.* = undefined;
806 }
807
808 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
809 pub fn field(
810 self: *Tuple,
811 val: anytype,
812 options: ValueOptions,
813 ) Error!void {
814 try self.container.field(null, val, options);
815 }
816
817 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
818 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
819 pub fn fieldMaxDepth(
820 self: *Tuple,
821 val: anytype,
822 options: ValueOptions,
823 depth: usize,
824 ) DepthError!void {
825 try self.container.fieldMaxDepth(null, val, options, depth);
826 }
827
828 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
829 /// `valueArbitraryDepth`.
830 pub fn fieldArbitraryDepth(
831 self: *Tuple,
832 val: anytype,
833 options: ValueOptions,
834 ) Error!void {
835 try self.container.fieldArbitraryDepth(null, val, options);
836 }
837
838 /// Starts a field with a struct as a value. Returns the struct.
839 pub fn beginStructField(
840 self: *Tuple,
841 options: SerializeContainerOptions,
842 ) Error!Struct {
843 try self.fieldPrefix();
844 return self.container.serializer.beginStruct(options);
845 }
846
847 /// Starts a field with a tuple as a value. Returns the tuple.
848 pub fn beginTupleField(
849 self: *Tuple,
850 options: SerializeContainerOptions,
851 ) Error!Tuple {
852 try self.fieldPrefix();
853 return self.container.serializer.beginTuple(options);
854 }
855
856 /// Print a field prefix. This prints any necessary commas, and whitespace as
857 /// configured. Useful if you want to serialize the field value yourself.
858 pub fn fieldPrefix(self: *Tuple) Error!void {
859 try self.container.fieldPrefix(null);
860 }
861 };
862
863 /// Writes ZON structs field by field.
864 pub const Struct = struct {
865 container: Container,
866
867 fn begin(parent: *Serializer, options: SerializeContainerOptions) Error!Struct {
868 return .{
869 .container = try Container.begin(parent, .named, options),
870 };
871 }
872
873 /// Finishes serializing the struct.
874 ///
875 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
876 pub fn end(self: *Struct) Error!void {
877 try self.container.end();
878 self.* = undefined;
879 }
880
881 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
882 pub fn field(
883 self: *Struct,
884 name: []const u8,
885 val: anytype,
886 options: ValueOptions,
887 ) Error!void {
888 try self.container.field(name, val, options);
889 }
890
891 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
892 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
893 pub fn fieldMaxDepth(
894 self: *Struct,
895 name: []const u8,
896 val: anytype,
897 options: ValueOptions,
898 depth: usize,
899 ) DepthError!void {
900 try self.container.fieldMaxDepth(name, val, options, depth);
901 }
902
903 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
904 /// `valueArbitraryDepth`.
905 pub fn fieldArbitraryDepth(
906 self: *Struct,
907 name: []const u8,
908 val: anytype,
909 options: ValueOptions,
910 ) Error!void {
911 try self.container.fieldArbitraryDepth(name, val, options);
912 }
913
914 /// Starts a field with a struct as a value. Returns the struct.
915 pub fn beginStructField(
916 self: *Struct,
917 name: []const u8,
918 options: SerializeContainerOptions,
919 ) Error!Struct {
920 try self.fieldPrefix(name);
921 return self.container.serializer.beginStruct(options);
922 }
923
924 /// Starts a field with a tuple as a value. Returns the tuple.
925 pub fn beginTupleField(
926 self: *Struct,
927 name: []const u8,
928 options: SerializeContainerOptions,
929 ) Error!Tuple {
930 try self.fieldPrefix(name);
931 return self.container.serializer.beginTuple(options);
932 }
933
934 /// Print a field prefix. This prints any necessary commas, the field name (escaped if
935 /// necessary) and whitespace as configured. Useful if you want to serialize the field
936 /// value yourself.
937 pub fn fieldPrefix(self: *Struct, name: []const u8) Error!void {
938 try self.container.fieldPrefix(name);
939 }
940 };
941
942 const Container = struct {
943 const FieldStyle = enum { named, anon };
944
945 serializer: *Serializer,
946 field_style: FieldStyle,
947 options: SerializeContainerOptions,
948 empty: bool,
949
950 fn begin(
951 sz: *Serializer,
952 field_style: FieldStyle,
953 options: SerializeContainerOptions,
954 ) Error!Container {
955 if (options.shouldWrap()) sz.indent_level +|= 1;
956 try sz.writer.writeAll(".{");
957 return .{
958 .serializer = sz,
959 .field_style = field_style,
960 .options = options,
961 .empty = true,
962 };
963 }
964
965 fn end(self: *Container) Error!void {
966 if (self.options.shouldWrap()) self.serializer.indent_level -|= 1;
967 if (!self.empty) {
968 if (self.options.shouldWrap()) {
969 if (self.serializer.options.whitespace) {
970 try self.serializer.writer.writeByte(',');
971 }
972 try self.serializer.newline();
973 try self.serializer.indent();
974 } else if (!self.shouldElideSpaces()) {
975 try self.serializer.space();
976 }
977 }
978 try self.serializer.writer.writeByte('}');
979 self.* = undefined;
980 }
981
982 fn fieldPrefix(self: *Container, name: ?[]const u8) Error!void {
983 if (!self.empty) {
984 try self.serializer.writer.writeByte(',');
985 }
986 self.empty = false;
987 if (self.options.shouldWrap()) {
988 try self.serializer.newline();
989 } else if (!self.shouldElideSpaces()) {
990 try self.serializer.space();
991 }
992 if (self.options.shouldWrap()) try self.serializer.indent();
993 if (name) |n| {
994 try self.serializer.ident(n);
995 try self.serializer.space();
996 try self.serializer.writer.writeByte('=');
997 try self.serializer.space();
998 }
999 }
1000
1001 fn field(
1002 self: *Container,
1003 name: ?[]const u8,
1004 val: anytype,
1005 options: ValueOptions,
1006 ) Error!void {
1007 comptime assert(!typeIsRecursive(@TypeOf(val)));
1008 try self.fieldArbitraryDepth(name, val, options);
1009 }
1010
1011 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
1012 fn fieldMaxDepth(
1013 self: *Container,
1014 name: ?[]const u8,
1015 val: anytype,
1016 options: ValueOptions,
1017 depth: usize,
1018 ) DepthError!void {
1019 try checkValueDepth(val, depth);
1020 try self.fieldArbitraryDepth(name, val, options);
1021 }
1022
1023 fn fieldArbitraryDepth(
1024 self: *Container,
1025 name: ?[]const u8,
1026 val: anytype,
1027 options: ValueOptions,
1028 ) Error!void {
1029 try self.fieldPrefix(name);
1030 try self.serializer.valueArbitraryDepth(val, options);
1031 }
1032
1033 fn shouldElideSpaces(self: *const Container) bool {
1034 return switch (self.options.whitespace_style) {
1035 .fields => |fields| self.field_style != .named and fields == 1,
1036 else => false,
1037 };
1038 }
1039 };
1040};
1041
1042test Serializer {
1043 var w: Writer = .discarding(&.{});
1044 var s: Serializer = .{ .writer = &w };
1045 var vec2 = try s.beginStruct(.{});
1046 try vec2.field("x", 1.5, .{});
1047 try vec2.fieldPrefix("prefix");
1048 try s.value(2.5, .{});
1049 try vec2.end();
1050}
1051
1052117fn expectSerializeEqual(
1053118 expected: []const u8,
1054119 value: anytype,
1055120 options: SerializeOptions,
1056121) !void {
1057 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
1058 const bw = &aw.interface;
122 var aw: Writer.Allocating = .init(std.testing.allocator);
123 const bw = &aw.writer;
1059124 defer aw.deinit();
1060125
1061126 try serialize(value, options, bw);
......@@ -1156,8 +221,8 @@ test "std.zon stringify whitespace, high level API" {
1156221}
1157222
1158223test "std.zon stringify whitespace, low level API" {
1159 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
1160 var s: Serializer = .{ .writer = &aw.interface };
224 var aw: Writer.Allocating = .init(std.testing.allocator);
225 var s: Serializer = .{ .writer = &aw.writer };
1161226 defer aw.deinit();
1162227
1163228 for ([2]bool{ true, false }) |whitespace| {
......@@ -1513,8 +578,8 @@ test "std.zon stringify whitespace, low level API" {
1513578}
1514579
1515580test "std.zon stringify utf8 codepoints" {
1516 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
1517 var s: Serializer = .{ .writer = &aw.interface };
581 var aw: Writer.Allocating = .init(std.testing.allocator);
582 var s: Serializer = .{ .writer = &aw.writer };
1518583 defer aw.deinit();
1519584
1520585 // Printable ASCII
......@@ -1565,11 +630,11 @@ test "std.zon stringify utf8 codepoints" {
1565630 aw.clearRetainingCapacity();
1566631
1567632 try s.codePoint('⚡');
1568 try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", aw.getWritten());
633 try std.testing.expectEqualStrings("'\\u{26a1}'", aw.getWritten());
1569634 aw.clearRetainingCapacity();
1570635
1571636 try s.value('⚡', .{ .emit_codepoint_literals = .always });
1572 try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", aw.getWritten());
637 try std.testing.expectEqualStrings("'\\u{26a1}'", aw.getWritten());
1573638 aw.clearRetainingCapacity();
1574639
1575640 try s.value('⚡', .{ .emit_codepoint_literals = .printable_ascii });
......@@ -1581,7 +646,9 @@ test "std.zon stringify utf8 codepoints" {
1581646 aw.clearRetainingCapacity();
1582647
1583648 // Invalid codepoint
1584 try std.testing.expectError(error.InvalidCodepoint, s.codePoint(0x110000 + 1));
649 try s.codePoint(0x110000 + 1);
650 try std.testing.expectEqualStrings("'\\u{110001}'", aw.getWritten());
651 aw.clearRetainingCapacity();
1585652
1586653 try s.int(0x110000 + 1);
1587654 try std.testing.expectEqualStrings("1114113", aw.getWritten());
......@@ -1614,7 +681,7 @@ test "std.zon stringify utf8 codepoints" {
1614681
1615682 // Make sure value options are passed to children
1616683 try s.value(.{ .c = '⚡' }, .{ .emit_codepoint_literals = .always });
1617 try std.testing.expectEqualStrings(".{ .c = '\\xe2\\x9a\\xa1' }", aw.getWritten());
684 try std.testing.expectEqualStrings(".{ .c = '\\u{26a1}' }", aw.getWritten());
1618685 aw.clearRetainingCapacity();
1619686
1620687 try s.value(.{ .c = '⚡' }, .{ .emit_codepoint_literals = .never });
......@@ -1623,8 +690,8 @@ test "std.zon stringify utf8 codepoints" {
1623690}
1624691
1625692test "std.zon stringify strings" {
1626 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
1627 var s: Serializer = .{ .writer = &aw.interface };
693 var aw: Writer.Allocating = .init(std.testing.allocator);
694 var s: Serializer = .{ .writer = &aw.writer };
1628695 defer aw.deinit();
1629696
1630697 // Minimal case
......@@ -1693,8 +760,8 @@ test "std.zon stringify strings" {
1693760}
1694761
1695762test "std.zon stringify multiline strings" {
1696 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
1697 var s: Serializer = .{ .writer = &aw.interface };
763 var aw: Writer.Allocating = .init(std.testing.allocator);
764 var s: Serializer = .{ .writer = &aw.writer };
1698765 defer aw.deinit();
1699766
1700767 inline for (.{ true, false }) |whitespace| {
......@@ -1913,8 +980,8 @@ test "std.zon stringify skip default fields" {
1913980}
1914981
1915982test "std.zon depth limits" {
1916 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
1917 const bw = &aw.interface;
983 var aw: Writer.Allocating = .init(std.testing.allocator);
984 const bw = &aw.writer;
1918985 defer aw.deinit();
1919986
1920987 const Recurse = struct { r: []const @This() };
......@@ -2174,8 +1241,8 @@ test "std.zon stringify primitives" {
21741241}
21751242
21761243test "std.zon stringify ident" {
2177 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
2178 var s: Serializer = .{ .writer = &aw.interface };
1244 var aw: Writer.Allocating = .init(std.testing.allocator);
1245 var s: Serializer = .{ .writer = &aw.writer };
21791246 defer aw.deinit();
21801247
21811248 try expectSerializeEqual(".{ .a = 0 }", .{ .a = 0 }, .{});
......@@ -2221,8 +1288,8 @@ test "std.zon stringify ident" {
22211288}
22221289
22231290test "std.zon stringify as tuple" {
2224 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
2225 var s: Serializer = .{ .writer = &aw.interface };
1291 var aw: Writer.Allocating = .init(std.testing.allocator);
1292 var s: Serializer = .{ .writer = &aw.writer };
22261293 defer aw.deinit();
22271294
22281295 // Tuples
......@@ -2242,8 +1309,8 @@ test "std.zon stringify as tuple" {
22421309}
22431310
22441311test "std.zon stringify as float" {
2245 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
2246 var s: Serializer = .{ .writer = &aw.interface };
1312 var aw: Writer.Allocating = .init(std.testing.allocator);
1313 var s: Serializer = .{ .writer = &aw.writer };
22471314 defer aw.deinit();
22481315
22491316 // Comptime float
......@@ -2346,8 +1413,8 @@ test "std.zon pointers" {
23461413}
23471414
23481415test "std.zon tuple/struct field" {
2349 var aw: std.io.Writer.Allocating = .init(std.testing.allocator);
2350 var s: Serializer = .{ .writer = &aw.interface };
1416 var aw: Writer.Allocating = .init(std.testing.allocator);
1417 var s: Serializer = .{ .writer = &aw.writer };
23511418 defer aw.deinit();
23521419
23531420 // Test on structs
src/Zcu.zig+1-1
......@@ -2821,7 +2821,7 @@ pub fn loadZirCache(gpa: Allocator, cache_file: std.fs.File) !Zir {
28212821 var buffer: [2000]u8 = undefined;
28222822 var file_reader = cache_file.reader(&buffer);
28232823 return result: {
2824 const header = file_reader.interface.takeStructReference(Zir.Header) catch |err| break :result err;
2824 const header = file_reader.interface.takeStructPointer(Zir.Header) catch |err| break :result err;
28252825 break :result loadZirCacheBody(gpa, header.*, &file_reader.interface);
28262826 } catch |err| switch (err) {
28272827 error.ReadFailed => return file_reader.err.?,
src/Zcu/PerThread.zig+1-1
......@@ -349,7 +349,7 @@ fn loadZirZoirCache(
349349 const cache_br = &cache_fr.interface;
350350
351351 // First we read the header to determine the lengths of arrays.
352 const header = (cache_br.takeStructReference(Header) catch |err| switch (err) {
352 const header = (cache_br.takeStructPointer(Header) catch |err| switch (err) {
353353 error.ReadFailed => return cache_fr.err.?,
354354 // This can happen if Zig bails out of this function between creating
355355 // the cached file and writing it.
src/codegen/c.zig+4-1
......@@ -2438,7 +2438,10 @@ pub const DeclGen = struct {
24382438 const ty = val.typeOf(zcu);
24392439 return .{ .data = .{
24402440 .dg = dg,
2441 .int_info = ty.intInfo(zcu),
2441 .int_info = if (ty.zigTypeTag(zcu) == .@"union" and ty.containerLayout(zcu) == .@"packed")
2442 .{ .signedness = .unsigned, .bits = @intCast(ty.bitSize(zcu)) }
2443 else
2444 ty.intInfo(zcu),
24422445 .kind = kind,
24432446 .ctype = try dg.ctypeFromType(ty, kind),
24442447 .val = val,
src/codegen/llvm.zig+3
......@@ -6385,6 +6385,9 @@ pub const FuncGen = struct {
63856385 // * https://github.com/llvm/llvm-project/blob/56905dab7da50bccfcceaeb496b206ff476127e1/llvm/test/MC/WebAssembly/blockaddress.ll
63866386 if (zcu.comp.getTarget().cpu.arch.isWasm()) break :jmp_table null;
63876387
6388 // Workaround for https://github.com/ziglang/zig/issues/24383:
6389 if (self.ng.ownerModule().optimize_mode == .ReleaseSafe) break :jmp_table null;
6390
63886391 // On a 64-bit target, 1024 pointers in our jump table is about 8K of pointers. This seems just
63896392 // about acceptable - it won't fill L1d cache on most CPUs.
63906393 const max_table_len = 1024;
src/main.zig+6-3
......@@ -346,8 +346,9 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
346346 } else if (mem.eql(u8, cmd, "targets")) {
347347 dev.check(.targets_command);
348348 const host = std.zig.resolveTargetQueryOrFatal(.{});
349 const stdout = fs.File.stdout().deprecatedWriter();
350 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, &host);
349 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
350 try @import("print_targets.zig").cmdTargets(arena, cmd_args, &stdout_writer.interface, &host);
351 return stdout_writer.interface.flush();
351352 } else if (mem.eql(u8, cmd, "version")) {
352353 dev.check(.version_command);
353354 try fs.File.stdout().writeAll(build_options.version ++ "\n");
......@@ -358,7 +359,9 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
358359 } else if (mem.eql(u8, cmd, "env")) {
359360 dev.check(.env_command);
360361 verifyLibcxxCorrectlyLinked();
361 return @import("print_env.zig").cmdEnv(arena, cmd_args);
362 var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
363 try @import("print_env.zig").cmdEnv(arena, &stdout_writer.interface);
364 return stdout_writer.interface.flush();
362365 } else if (mem.eql(u8, cmd, "reduce")) {
363366 return jitCmd(gpa, arena, cmd_args, .{
364367 .cmd_name = "reduce",
src/print_env.zig+14-35
......@@ -4,8 +4,7 @@ const introspect = @import("introspect.zig");
44const Allocator = std.mem.Allocator;
55const fatal = std.process.fatal;
66
7pub fn cmdEnv(arena: Allocator, args: []const []const u8) !void {
8 _ = args;
7pub fn cmdEnv(arena: Allocator, out: *std.Io.Writer) !void {
98 const cwd_path = try introspect.getResolvedCwd(arena);
109 const self_exe_path = try std.fs.selfExePathAlloc(arena);
1110
......@@ -21,41 +20,21 @@ pub fn cmdEnv(arena: Allocator, args: []const []const u8) !void {
2120 const host = try std.zig.system.resolveTargetQuery(.{});
2221 const triple = try host.zigTriple(arena);
2322
24 var buffer: [1024]u8 = undefined;
25 var stdout_writer = std.fs.File.stdout().writer(&buffer);
26 const w = &stdout_writer.interface();
27 var jws: std.json.Stringify = .{ .writer = w, .options = .{ .whitespace = .indent_1 } };
23 var serializer: std.zon.Serializer = .{ .writer = out };
24 var root = try serializer.beginStruct(.{});
2825
29 try jws.beginObject();
30
31 try jws.objectField("zig_exe");
32 try jws.write(self_exe_path);
33
34 try jws.objectField("lib_dir");
35 try jws.write(zig_lib_directory.path.?);
36
37 try jws.objectField("std_dir");
38 try jws.write(zig_std_dir);
39
40 try jws.objectField("global_cache_dir");
41 try jws.write(global_cache_dir);
42
43 try jws.objectField("version");
44 try jws.write(build_options.version);
45
46 try jws.objectField("target");
47 try jws.write(triple);
48
49 try jws.objectField("env");
50 try jws.beginObject();
26 try root.field("zig_exe", self_exe_path, .{});
27 try root.field("lib_dir", zig_lib_directory.path.?, .{});
28 try root.field("std_dir", zig_std_dir, .{});
29 try root.field("global_cache_dir", global_cache_dir, .{});
30 try root.field("version", build_options.version, .{});
31 try root.field("target", triple, .{});
32 var env = try root.beginStructField("env", .{});
5133 inline for (@typeInfo(std.zig.EnvVar).@"enum".fields) |field| {
52 try jws.objectField(field.name);
53 try jws.write(try @field(std.zig.EnvVar, field.name).get(arena));
34 try env.field(field.name, try @field(std.zig.EnvVar, field.name).get(arena), .{});
5435 }
55 try jws.endObject();
56
57 try jws.endObject();
58 try w.writeByte('\n');
36 try env.end();
37 try root.end();
5938
60 try w.flush();
39 try out.writeByte('\n');
6140}
src/print_targets.zig+28-26
......@@ -10,38 +10,37 @@ const target = @import("target.zig");
1010const assert = std.debug.assert;
1111const glibc = @import("libs/glibc.zig");
1212const introspect = @import("introspect.zig");
13const Writer = std.io.Writer;
1413
15pub fn cmdTargets(arena: Allocator, args: []const []const u8) !void {
14pub fn cmdTargets(
15 allocator: Allocator,
16 args: []const []const u8,
17 out: *std.Io.Writer,
18 native_target: *const Target,
19) !void {
1620 _ = args;
17 const host = std.zig.resolveTargetQueryOrFatal(.{});
18 var buffer: [1024]u8 = undefined;
19 var bw = fs.File.stdout().writer().buffered(&buffer);
20 try print(arena, &bw, host);
21 try bw.flush();
22}
23
24fn print(arena: Allocator, output: *Writer, host: *const Target) Writer.Error!void {
25 var zig_lib_directory = introspect.findZigLibDir(arena) catch |err| {
21 var zig_lib_directory = introspect.findZigLibDir(allocator) catch |err| {
2622 fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)});
2723 };
2824 defer zig_lib_directory.handle.close();
25 defer allocator.free(zig_lib_directory.path.?);
2926
3027 const abilists_contents = zig_lib_directory.handle.readFileAlloc(
28 allocator,
3129 glibc.abilists_path,
32 arena,
33 .limited(glibc.abilists_max_size),
30 glibc.abilists_max_size,
3431 ) catch |err| switch (err) {
3532 error.OutOfMemory => return error.OutOfMemory,
3633 else => fatal("unable to read " ++ glibc.abilists_path ++ ": {s}", .{@errorName(err)}),
3734 };
35 defer allocator.free(abilists_contents);
3836
39 const glibc_abi = try glibc.loadMetaData(arena, abilists_contents);
37 const glibc_abi = try glibc.loadMetaData(allocator, abilists_contents);
38 defer glibc_abi.destroy(allocator);
4039
41 var sz: std.zon.stringify.Serializer = .{ .writer = output };
40 var serializer: std.zon.Serializer = .{ .writer = out };
4241
4342 {
44 var root_obj = try sz.beginStruct(.{});
43 var root_obj = try serializer.beginStruct(.{});
4544
4645 try root_obj.field("arch", meta.fieldNames(Target.Cpu.Arch), .{});
4746 try root_obj.field("os", meta.fieldNames(Target.Os.Tag), .{});
......@@ -50,9 +49,10 @@ fn print(arena: Allocator, output: *Writer, host: *const Target) Writer.Error!vo
5049 {
5150 var libc_obj = try root_obj.beginTupleField("libc", .{});
5251 for (std.zig.target.available_libcs) |libc| {
53 const tmp = try std.fmt.allocPrint(arena, "{s}-{s}-{s}", .{
52 const tmp = try std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{
5453 @tagName(libc.arch), @tagName(libc.os), @tagName(libc.abi),
5554 });
55 defer allocator.free(tmp);
5656 try libc_obj.field(tmp, .{});
5757 }
5858 try libc_obj.end();
......@@ -61,7 +61,8 @@ fn print(arena: Allocator, output: *Writer, host: *const Target) Writer.Error!vo
6161 {
6262 var glibc_obj = try root_obj.beginTupleField("glibc", .{});
6363 for (glibc_abi.all_versions) |ver| {
64 const tmp = try std.fmt.allocPrint(arena, "{f}", .{ver});
64 const tmp = try std.fmt.allocPrint(allocator, "{f}", .{ver});
65 defer allocator.free(tmp);
6566 try glibc_obj.field(tmp, .{});
6667 }
6768 try glibc_obj.end();
......@@ -101,20 +102,21 @@ fn print(arena: Allocator, output: *Writer, host: *const Target) Writer.Error!vo
101102 {
102103 var native_obj = try root_obj.beginStructField("native", .{});
103104 {
104 const triple = try host.zigTriple(arena);
105 const triple = try native_target.zigTriple(allocator);
106 defer allocator.free(triple);
105107 try native_obj.field("triple", triple, .{});
106108 }
107109 {
108110 var cpu_obj = try native_obj.beginStructField("cpu", .{});
109 try cpu_obj.field("arch", @tagName(host.cpu.arch), .{});
111 try cpu_obj.field("arch", @tagName(native_target.cpu.arch), .{});
110112
111 try cpu_obj.field("name", host.cpu.model.name, .{});
113 try cpu_obj.field("name", native_target.cpu.model.name, .{});
112114
113115 {
114116 var features = try native_obj.beginTupleField("features", .{});
115 for (host.cpu.arch.allFeaturesList(), 0..) |feature, i_usize| {
117 for (native_target.cpu.arch.allFeaturesList(), 0..) |feature, i_usize| {
116118 const index = @as(Target.Cpu.Feature.Set.Index, @intCast(i_usize));
117 if (host.cpu.features.isEnabled(index)) {
119 if (native_target.cpu.features.isEnabled(index)) {
118120 try features.field(feature.name, .{});
119121 }
120122 }
......@@ -123,13 +125,13 @@ fn print(arena: Allocator, output: *Writer, host: *const Target) Writer.Error!vo
123125 try cpu_obj.end();
124126 }
125127
126 try native_obj.field("os", @tagName(host.os.tag), .{});
127 try native_obj.field("abi", @tagName(host.abi), .{});
128 try native_obj.field("os", @tagName(native_target.os.tag), .{});
129 try native_obj.field("abi", @tagName(native_target.abi), .{});
128130 try native_obj.end();
129131 }
130132
131133 try root_obj.end();
132134 }
133135
134 try output.writeByte('\n');
136 try out.writeByte('\n');
135137}
src/translate_c.zig+1-1
......@@ -3338,7 +3338,7 @@ fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.Predefined
33383338
33393339fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node {
33403340 return Tag.char_literal.create(c.arena, if (narrow)
3341 try std.fmt.allocPrint(c.arena, "'{f}'", .{std.zig.fmtChar(&.{@as(u8, @intCast(val))})})
3341 try std.fmt.allocPrint(c.arena, "'{f}'", .{std.zig.fmtChar(@intCast(val))})
33423342 else
33433343 try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val}));
33443344}
test/behavior/type_info.zig-14
......@@ -539,20 +539,6 @@ fn add(a: i32, b: i32) i32 {
539539 return a + b;
540540}
541541
542test "type info for async frames" {
543 if (true) {
544 // https://github.com/ziglang/zig/issues/6025
545 return error.SkipZigTest;
546 }
547
548 switch (@typeInfo(@Frame(add))) {
549 .frame => |frame| {
550 try expect(@as(@TypeOf(add), @ptrCast(frame.function)) == add);
551 },
552 else => unreachable,
553 }
554}
555
556542test "Declarations are returned in declaration order" {
557543 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
558544 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
test/cases/compile_errors/async/Frame_of_generic_function.zig deleted-14
......@@ -1,14 +0,0 @@
1export fn entry() void {
2 var frame: @Frame(func) = undefined;
3 _ = &frame;
4}
5fn func(comptime T: type) void {
6 var x: T = undefined;
7 _ = &x;
8}
9
10// error
11// backend=stage1
12// target=native
13//
14// tmp.zig:2:16: error: @Frame() of generic function
test/cases/compile_errors/async/bad_alignment_in_asynccall.zig deleted-13
......@@ -1,13 +0,0 @@
1export fn entry() void {
2 var ptr: fn () callconv(.@"async") void = func;
3 var bytes: [64]u8 = undefined;
4 _ = @asyncCall(&bytes, {}, ptr, .{});
5 _ = &ptr;
6}
7fn func() callconv(.@"async") void {}
8
9// error
10// backend=stage1
11// target=aarch64-linux-none
12//
13// tmp.zig:4:21: error: expected type '[]align(8) u8', found '*[64]u8'
test/cases/compile_errors/async/exported_async_function.zig deleted-7
......@@ -1,7 +0,0 @@
1export fn foo() callconv(.@"async") void {}
2
3// error
4// backend=stage1
5// target=native
6//
7// tmp.zig:1:1: error: exported function cannot be async
test/cases/compile_errors/async/frame_called_outside_of_function_definition.zig deleted-11
......@@ -1,11 +0,0 @@
1var handle_undef: anyframe = undefined;
2var handle_dummy: anyframe = @frame();
3export fn entry() bool {
4 return handle_undef == handle_dummy;
5}
6
7// error
8// backend=stage1
9// target=native
10//
11// tmp.zig:2:30: error: @frame() called outside of function definition
test/cases/compile_errors/async/frame_causes_function_to_be_async.zig deleted-13
......@@ -1,13 +0,0 @@
1export fn entry() void {
2 func();
3}
4fn func() void {
5 _ = @frame();
6}
7
8// error
9// backend=stage1
10// target=native
11//
12// tmp.zig:1:1: error: function with calling convention 'C' cannot be async
13// tmp.zig:5:9: note: @frame() causes function to be async
test/cases/compile_errors/async/non-async_function_pointer_eventually_is_inferred_to_become_async.zig deleted-15
......@@ -1,15 +0,0 @@
1export fn a() void {
2 var non_async_fn: fn () void = undefined;
3 non_async_fn = func;
4}
5fn func() void {
6 suspend {}
7}
8
9// error
10// backend=stage1
11// target=native
12//
13// tmp.zig:5:1: error: 'func' cannot be async
14// tmp.zig:3:20: note: required to be non-async here
15// tmp.zig:6:5: note: suspends here
test/cases/compile_errors/async/non_async_function_pointer_passed_to_asyncCall.zig deleted-13
......@@ -1,13 +0,0 @@
1export fn entry() void {
2 var ptr = afunc;
3 var bytes: [100]u8 align(16) = undefined;
4 _ = @asyncCall(&bytes, {}, ptr, .{});
5 _ = &ptr;
6}
7fn afunc() void {}
8
9// error
10// backend=stage1
11// target=native
12//
13// tmp.zig:4:32: error: expected async function, found 'fn () void'
test/cases/compile_errors/async/prevent_bad_implicit_casting_of_anyframe_types.zig deleted-24
......@@ -1,24 +0,0 @@
1export fn a() void {
2 var x: anyframe = undefined;
3 var y: anyframe->i32 = x;
4 _ = .{ &x, &y };
5}
6export fn b() void {
7 var x: i32 = undefined;
8 var y: anyframe->i32 = x;
9 _ = .{ &x, &y };
10}
11export fn c() void {
12 var x: @Frame(func) = undefined;
13 var y: anyframe->i32 = &x;
14 _ = .{ &x, &y };
15}
16fn func() void {}
17
18// error
19// backend=stage1
20// target=native
21//
22// :3:28: error: expected type 'anyframe->i32', found 'anyframe'
23// :8:28: error: expected type 'anyframe->i32', found 'i32'
24// tmp.zig:13:29: error: expected type 'anyframe->i32', found '*@Frame(func)'
test/cases/compile_errors/async/wrong_type_for_argument_tuple_to_asyncCall.zig deleted-14
......@@ -1,14 +0,0 @@
1export fn entry1() void {
2 var frame: @Frame(foo) = undefined;
3 @asyncCall(&frame, {}, foo, {});
4}
5
6fn foo() i32 {
7 return 0;
8}
9
10// error
11// backend=stage1
12// target=native
13//
14// tmp.zig:3:33: error: expected tuple or struct, found 'void'
test/cases/safety/nosuspend function call, callee suspends.zig deleted-20
......@@ -1,20 +0,0 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = message;
5 _ = stack_trace;
6 std.process.exit(0);
7}
8pub fn main() !void {
9 _ = nosuspend add(101, 100);
10 return error.TestFailed;
11}
12fn add(a: i32, b: i32) i32 {
13 if (a > 100) {
14 suspend {}
15 }
16 return a + b;
17}
18// run
19// backend=stage1
20// target=native