authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-26 23:44:25-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-28 18:30:57-07:00
loge9a271cba3c79358cc8d24a3892fbbefc4e12d9d
treefe16372f3f789648a066bae301d045a5bf94f8ef
parent9860dd475ab754f85f2f8c96039255bda8721002

std.tz: update to new Reader API


1 files changed, 29 insertions(+), 27 deletions(-)

lib/std/tz.zig+29-27
...@@ -1,6 +1,9 @@...@@ -1,6 +1,9 @@
1const std = @import("std.zig");
2const builtin = @import("builtin");1const builtin = @import("builtin");
32
3const std = @import("std.zig");
4const Reader = std.Io.Reader;
5const Allocator = std.mem.Allocator;
6
4pub const Transition = struct {7pub const Transition = struct {
5 ts: i64,8 ts: i64,
6 timetype: *Timetype,9 timetype: *Timetype,
...@@ -34,7 +37,7 @@ pub const Leapsecond = struct {...@@ -34,7 +37,7 @@ pub const Leapsecond = struct {
34};37};
3538
36pub const Tz = struct {39pub const Tz = struct {
37 allocator: std.mem.Allocator,40 allocator: Allocator,
38 transitions: []const Transition,41 transitions: []const Transition,
39 timetypes: []const Timetype,42 timetypes: []const Timetype,
40 leapseconds: []const Leapsecond,43 leapseconds: []const Leapsecond,
...@@ -54,8 +57,8 @@ pub const Tz = struct {...@@ -54,8 +57,8 @@ pub const Tz = struct {
54 },57 },
55 };58 };
5659
57 pub fn parse(allocator: std.mem.Allocator, reader: anytype) !Tz {60 pub fn parse(allocator: Allocator, reader: *Reader) !Tz {
58 var legacy_header = try reader.readStruct(Header);61 var legacy_header = try reader.takeStruct(Header, .little);
59 if (!std.mem.eql(u8, &legacy_header.magic, "TZif")) return error.BadHeader;62 if (!std.mem.eql(u8, &legacy_header.magic, "TZif")) return error.BadHeader;
60 if (legacy_header.version != 0 and legacy_header.version != '2' and legacy_header.version != '3') return error.BadVersion;63 if (legacy_header.version != 0 and legacy_header.version != '2' and legacy_header.version != '3') return error.BadVersion;
6164
...@@ -68,9 +71,9 @@ pub const Tz = struct {...@@ -68,9 +71,9 @@ pub const Tz = struct {
68 } else {71 } else {
69 // If the format is modern, just skip over the legacy data72 // If the format is modern, just skip over the legacy data
70 const skipv = legacy_header.counts.timecnt * 5 + legacy_header.counts.typecnt * 6 + legacy_header.counts.charcnt + legacy_header.counts.leapcnt * 8 + legacy_header.counts.isstdcnt + legacy_header.counts.isutcnt;73 const skipv = legacy_header.counts.timecnt * 5 + legacy_header.counts.typecnt * 6 + legacy_header.counts.charcnt + legacy_header.counts.leapcnt * 8 + legacy_header.counts.isstdcnt + legacy_header.counts.isutcnt;
71 try reader.skipBytes(skipv, .{});74 try reader.discardAll(skipv);
7275
73 var header = try reader.readStruct(Header);76 var header = try reader.takeStruct(Header, .little);
74 if (!std.mem.eql(u8, &header.magic, "TZif")) return error.BadHeader;77 if (!std.mem.eql(u8, &header.magic, "TZif")) return error.BadHeader;
75 if (header.version != '2' and header.version != '3') return error.BadVersion;78 if (header.version != '2' and header.version != '3') return error.BadVersion;
76 if (builtin.target.cpu.arch.endian() != std.builtin.Endian.big) {79 if (builtin.target.cpu.arch.endian() != std.builtin.Endian.big) {
...@@ -81,7 +84,7 @@ pub const Tz = struct {...@@ -81,7 +84,7 @@ pub const Tz = struct {
81 }84 }
82 }85 }
8386
84 fn parseBlock(allocator: std.mem.Allocator, reader: anytype, header: Header, legacy: bool) !Tz {87 fn parseBlock(allocator: Allocator, reader: *Reader, header: Header, legacy: bool) !Tz {
85 if (header.counts.isstdcnt != 0 and header.counts.isstdcnt != header.counts.typecnt) return error.Malformed; // rfc8536: isstdcnt [...] MUST either be zero or equal to "typecnt"88 if (header.counts.isstdcnt != 0 and header.counts.isstdcnt != header.counts.typecnt) return error.Malformed; // rfc8536: isstdcnt [...] MUST either be zero or equal to "typecnt"
86 if (header.counts.isutcnt != 0 and header.counts.isutcnt != header.counts.typecnt) return error.Malformed; // rfc8536: isutcnt [...] MUST either be zero or equal to "typecnt"89 if (header.counts.isutcnt != 0 and header.counts.isutcnt != header.counts.typecnt) return error.Malformed; // rfc8536: isutcnt [...] MUST either be zero or equal to "typecnt"
87 if (header.counts.typecnt == 0) return error.Malformed; // rfc8536: typecnt [...] MUST NOT be zero90 if (header.counts.typecnt == 0) return error.Malformed; // rfc8536: typecnt [...] MUST NOT be zero
...@@ -98,12 +101,12 @@ pub const Tz = struct {...@@ -98,12 +101,12 @@ pub const Tz = struct {
98 // Parse transition types101 // Parse transition types
99 var i: usize = 0;102 var i: usize = 0;
100 while (i < header.counts.timecnt) : (i += 1) {103 while (i < header.counts.timecnt) : (i += 1) {
101 transitions[i].ts = if (legacy) try reader.readInt(i32, .big) else try reader.readInt(i64, .big);104 transitions[i].ts = if (legacy) try reader.takeInt(i32, .big) else try reader.takeInt(i64, .big);
102 }105 }
103106
104 i = 0;107 i = 0;
105 while (i < header.counts.timecnt) : (i += 1) {108 while (i < header.counts.timecnt) : (i += 1) {
106 const tt = try reader.readByte();109 const tt = try reader.takeByte();
107 if (tt >= timetypes.len) return error.Malformed; // rfc8536: Each type index MUST be in the range [0, "typecnt" - 1]110 if (tt >= timetypes.len) return error.Malformed; // rfc8536: Each type index MUST be in the range [0, "typecnt" - 1]
108 transitions[i].timetype = &timetypes[tt];111 transitions[i].timetype = &timetypes[tt];
109 }112 }
...@@ -111,11 +114,11 @@ pub const Tz = struct {...@@ -111,11 +114,11 @@ pub const Tz = struct {
111 // Parse time types114 // Parse time types
112 i = 0;115 i = 0;
113 while (i < header.counts.typecnt) : (i += 1) {116 while (i < header.counts.typecnt) : (i += 1) {
114 const offset = try reader.readInt(i32, .big);117 const offset = try reader.takeInt(i32, .big);
115 if (offset < -2147483648) return error.Malformed; // rfc8536: utoff [...] MUST NOT be -2**31118 if (offset < -2147483648) return error.Malformed; // rfc8536: utoff [...] MUST NOT be -2**31
116 const dst = try reader.readByte();119 const dst = try reader.takeByte();
117 if (dst != 0 and dst != 1) return error.Malformed; // rfc8536: (is)dst [...] The value MUST be 0 or 1.120 if (dst != 0 and dst != 1) return error.Malformed; // rfc8536: (is)dst [...] The value MUST be 0 or 1.
118 const idx = try reader.readByte();121 const idx = try reader.takeByte();
119 if (idx > header.counts.charcnt - 1) return error.Malformed; // rfc8536: (desig)idx [...] Each index MUST be in the range [0, "charcnt" - 1]122 if (idx > header.counts.charcnt - 1) return error.Malformed; // rfc8536: (desig)idx [...] Each index MUST be in the range [0, "charcnt" - 1]
120 timetypes[i] = .{123 timetypes[i] = .{
121 .offset = offset,124 .offset = offset,
...@@ -128,7 +131,7 @@ pub const Tz = struct {...@@ -128,7 +131,7 @@ pub const Tz = struct {
128 }131 }
129132
130 var designators_data: [256 + 6]u8 = undefined;133 var designators_data: [256 + 6]u8 = undefined;
131 try reader.readNoEof(designators_data[0..header.counts.charcnt]);134 try reader.readSliceAll(designators_data[0..header.counts.charcnt]);
132 const designators = designators_data[0..header.counts.charcnt];135 const designators = designators_data[0..header.counts.charcnt];
133 if (designators[designators.len - 1] != 0) return error.Malformed; // rfc8536: charcnt [...] includes the trailing NUL (0x00) octet136 if (designators[designators.len - 1] != 0) return error.Malformed; // rfc8536: charcnt [...] includes the trailing NUL (0x00) octet
134137
...@@ -144,12 +147,12 @@ pub const Tz = struct {...@@ -144,12 +147,12 @@ pub const Tz = struct {
144 // Parse leap seconds147 // Parse leap seconds
145 i = 0;148 i = 0;
146 while (i < header.counts.leapcnt) : (i += 1) {149 while (i < header.counts.leapcnt) : (i += 1) {
147 const occur: i64 = if (legacy) try reader.readInt(i32, .big) else try reader.readInt(i64, .big);150 const occur: i64 = if (legacy) try reader.takeInt(i32, .big) else try reader.takeInt(i64, .big);
148 if (occur < 0) return error.Malformed; // rfc8536: occur [...] MUST be nonnegative151 if (occur < 0) return error.Malformed; // rfc8536: occur [...] MUST be nonnegative
149 if (i > 0 and leapseconds[i - 1].occurrence + 2419199 > occur) return error.Malformed; // rfc8536: occur [...] each later value MUST be at least 2419199 greater than the previous value152 if (i > 0 and leapseconds[i - 1].occurrence + 2419199 > occur) return error.Malformed; // rfc8536: occur [...] each later value MUST be at least 2419199 greater than the previous value
150 if (occur > std.math.maxInt(i48)) return error.Malformed; // Unreasonably far into the future153 if (occur > std.math.maxInt(i48)) return error.Malformed; // Unreasonably far into the future
151154
152 const corr = try reader.readInt(i32, .big);155 const corr = try reader.takeInt(i32, .big);
153 if (i == 0 and corr != -1 and corr != 1) return error.Malformed; // rfc8536: The correction value in the first leap-second record, if present, MUST be either one (1) or minus one (-1)156 if (i == 0 and corr != -1 and corr != 1) return error.Malformed; // rfc8536: The correction value in the first leap-second record, if present, MUST be either one (1) or minus one (-1)
154 if (i > 0 and leapseconds[i - 1].correction != corr + 1 and leapseconds[i - 1].correction != corr - 1) return error.Malformed; // rfc8536: The correction values in adjacent leap-second records MUST differ by exactly one (1)157 if (i > 0 and leapseconds[i - 1].correction != corr + 1 and leapseconds[i - 1].correction != corr - 1) return error.Malformed; // rfc8536: The correction values in adjacent leap-second records MUST differ by exactly one (1)
155 if (corr > std.math.maxInt(i16)) return error.Malformed; // Unreasonably large correction158 if (corr > std.math.maxInt(i16)) return error.Malformed; // Unreasonably large correction
...@@ -163,7 +166,7 @@ pub const Tz = struct {...@@ -163,7 +166,7 @@ pub const Tz = struct {
163 // Parse standard/wall indicators166 // Parse standard/wall indicators
164 i = 0;167 i = 0;
165 while (i < header.counts.isstdcnt) : (i += 1) {168 while (i < header.counts.isstdcnt) : (i += 1) {
166 const stdtime = try reader.readByte();169 const stdtime = try reader.takeByte();
167 if (stdtime == 1) {170 if (stdtime == 1) {
168 timetypes[i].flags |= 0x02;171 timetypes[i].flags |= 0x02;
169 }172 }
...@@ -172,7 +175,7 @@ pub const Tz = struct {...@@ -172,7 +175,7 @@ pub const Tz = struct {
172 // Parse UT/local indicators175 // Parse UT/local indicators
173 i = 0;176 i = 0;
174 while (i < header.counts.isutcnt) : (i += 1) {177 while (i < header.counts.isutcnt) : (i += 1) {
175 const ut = try reader.readByte();178 const ut = try reader.takeByte();
176 if (ut == 1) {179 if (ut == 1) {
177 timetypes[i].flags |= 0x04;180 timetypes[i].flags |= 0x04;
178 if (!timetypes[i].standardTimeIndicator()) return error.Malformed; // rfc8536: standard/wall value MUST be one (1) if the UT/local value is one (1)181 if (!timetypes[i].standardTimeIndicator()) return error.Malformed; // rfc8536: standard/wall value MUST be one (1) if the UT/local value is one (1)
...@@ -182,9 +185,8 @@ pub const Tz = struct {...@@ -182,9 +185,8 @@ pub const Tz = struct {
182 // Footer185 // Footer
183 var footer: ?[]u8 = null;186 var footer: ?[]u8 = null;
184 if (!legacy) {187 if (!legacy) {
185 if ((try reader.readByte()) != '\n') return error.Malformed; // An rfc8536 footer must start with a newline188 if ((try reader.takeByte()) != '\n') return error.Malformed; // An rfc8536 footer must start with a newline
186 var footerdata_buf: [128]u8 = undefined;189 const footer_mem = reader.takeSentinel('\n') catch |err| switch (err) {
187 const footer_mem = reader.readUntilDelimiter(&footerdata_buf, '\n') catch |err| switch (err) {
188 error.StreamTooLong => return error.OverlargeFooter, // Read more than 128 bytes, much larger than any reasonable POSIX TZ string190 error.StreamTooLong => return error.OverlargeFooter, // Read more than 128 bytes, much larger than any reasonable POSIX TZ string
189 else => return err,191 else => return err,
190 };192 };
...@@ -194,7 +196,7 @@ pub const Tz = struct {...@@ -194,7 +196,7 @@ pub const Tz = struct {
194 }196 }
195 errdefer if (footer) |ft| allocator.free(ft);197 errdefer if (footer) |ft| allocator.free(ft);
196198
197 return Tz{199 return .{
198 .allocator = allocator,200 .allocator = allocator,
199 .transitions = transitions,201 .transitions = transitions,
200 .timetypes = timetypes,202 .timetypes = timetypes,
...@@ -215,9 +217,9 @@ pub const Tz = struct {...@@ -215,9 +217,9 @@ pub const Tz = struct {
215217
216test "slim" {218test "slim" {
217 const data = @embedFile("tz/asia_tokyo.tzif");219 const data = @embedFile("tz/asia_tokyo.tzif");
218 var in_stream = std.io.fixedBufferStream(data);220 var in_stream: Reader = .fixed(data);
219221
220 var tz = try std.Tz.parse(std.testing.allocator, in_stream.reader());222 var tz = try std.Tz.parse(std.testing.allocator, &in_stream);
221 defer tz.deinit();223 defer tz.deinit();
222224
223 try std.testing.expectEqual(tz.transitions.len, 9);225 try std.testing.expectEqual(tz.transitions.len, 9);
...@@ -228,9 +230,9 @@ test "slim" {...@@ -228,9 +230,9 @@ test "slim" {
228230
229test "fat" {231test "fat" {
230 const data = @embedFile("tz/antarctica_davis.tzif");232 const data = @embedFile("tz/antarctica_davis.tzif");
231 var in_stream = std.io.fixedBufferStream(data);233 var in_stream: Reader = .fixed(data);
232234
233 var tz = try std.Tz.parse(std.testing.allocator, in_stream.reader());235 var tz = try std.Tz.parse(std.testing.allocator, &in_stream);
234 defer tz.deinit();236 defer tz.deinit();
235237
236 try std.testing.expectEqual(tz.transitions.len, 8);238 try std.testing.expectEqual(tz.transitions.len, 8);
...@@ -241,9 +243,9 @@ test "fat" {...@@ -241,9 +243,9 @@ test "fat" {
241test "legacy" {243test "legacy" {
242 // Taken from Slackware 8.0, from 2001244 // Taken from Slackware 8.0, from 2001
243 const data = @embedFile("tz/europe_vatican.tzif");245 const data = @embedFile("tz/europe_vatican.tzif");
244 var in_stream = std.io.fixedBufferStream(data);246 var in_stream: Reader = .fixed(data);
245247
246 var tz = try std.Tz.parse(std.testing.allocator, in_stream.reader());248 var tz = try std.Tz.parse(std.testing.allocator, &in_stream);
247 defer tz.deinit();249 defer tz.deinit();
248250
249 try std.testing.expectEqual(tz.transitions.len, 170);251 try std.testing.expectEqual(tz.transitions.len, 170);