1const std = @import("std");
2const builtin = @import("builtin");
3const extras = @import("extras");
4const sys_linux = @import("sys-linux");
5
6pub const fmt = @import("./fmt.zig");
7
8const sys = switch (builtin.target.os.tag) {
9 .linux => sys_linux,
10 .macos => @import("sys-darwin"),
11 .freebsd => @import("sys-freebsd"),
12 .netbsd => @import("sys-netbsd"),
13 .openbsd => @import("sys-openbsd"),
14 else => unreachable,
15};
16
17pub fn Readable(T: type, this_kind: enum { _var, _const, _bare }) type {
18 return struct {
19 const Error = T.ReadError;
20
21 const Self = switch (this_kind) {
22 ._var => *T,
23 ._const => *const T,
24 ._bare => T,
25 };
26
27 /// Returns the number of bytes read. It may be less than buffer.len.
28 /// If the number of bytes read is 0, it means end of stream.
29 /// End of stream is not an error condition.
30 // pub fn read(self: Self, buffer: []u8) Error!usize {
31 // }
32
33 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
34 /// means the stream reached the end. Reaching the end of a stream is not an error
35 /// condition.
36 pub fn readAll(self: Self, buffer: []u8) Error!usize {
37 return readAtLeast(self, buffer, buffer.len);
38 }
39
40 /// Returns the number of bytes read, calling the underlying read
41 /// function the minimal number of times until the buffer has at least
42 /// `len` bytes filled. If the number read is less than `len` it means
43 /// the stream reached the end. Reaching the end of the stream is not
44 /// an error condition.
45 pub fn readAtLeast(self: Self, buffer: []u8, len: usize) Error!usize {
46 std.debug.assert(len <= buffer.len);
47 var index: usize = 0;
48 while (index < len) {
49 const amt = try self.read(buffer[index..]);
50 if (amt == 0) break;
51 index += amt;
52 }
53 return index;
54 }
55
56 /// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead.
57 pub fn readNoEof(self: Self, buf: []u8) (Error || error{EndOfStream})!void {
58 const amt_read = try readAll(self, buf);
59 if (amt_read < buf.len) return error.EndOfStream;
60 }
61
62 /// Appends to the `std.ArrayList` contents by reading from the stream until end of stream is found.
63 /// If the number of bytes appended would exceed `max_append_size`, `error.StreamTooLong` is returned and the `std.ArrayList` has exactly `max_append_size` bytes appended.
64 fn readAllArrayList(self: Self, array_list: *std.array_list.Managed(u8), max_append_size: usize) !void {
65 return readAllArrayListAligned(self, null, array_list, max_append_size);
66 }
67
68 fn readAllArrayListAligned(self: Self, comptime alignment: ?std.mem.Alignment, array_list: *std.array_list.AlignedManaged(u8, alignment), max_append_size: usize) !void {
69 try array_list.ensureTotalCapacity(@min(max_append_size, 4096));
70 const original_len = array_list.items.len;
71 var start_index: usize = original_len;
72 while (true) {
73 array_list.expandToCapacity();
74 const dest_slice = array_list.items[start_index..];
75 const bytes_read = try readAll(self, dest_slice);
76 start_index += bytes_read;
77
78 if (start_index - original_len > max_append_size) {
79 array_list.shrinkAndFree(original_len + max_append_size);
80 return error.StreamTooLong;
81 }
82
83 if (bytes_read != dest_slice.len) {
84 array_list.shrinkAndFree(start_index);
85 return;
86 }
87
88 // This will trigger ArrayList to expand superlinearly at whatever its growth rate is.
89 try array_list.ensureTotalCapacity(start_index + 1);
90 }
91 }
92
93 /// Allocates enough memory to hold all the contents of the stream.
94 /// If the allocated memory would be greater than `max_size`, returns `error.StreamTooLong`.
95 /// Caller owns returned memory.
96 /// If this function returns an error, the contents from the stream read so far are lost.
97 pub fn readAllAlloc(self: Self, allocator: std.mem.Allocator, max_size: usize) ![]u8 {
98 var array_list = std.array_list.Managed(u8).init(allocator);
99 defer array_list.deinit();
100 try readAllArrayList(self, &array_list, max_size);
101 return try array_list.toOwnedSlice();
102 }
103
104 pub fn readArray(self: Self, comptime N: usize) ![N]u8 {
105 var buffer: [N]u8 = undefined;
106 if (try readAll(self, &buffer) != N) return error.EndOfStream;
107 return buffer;
108 }
109
110 pub fn readByte(self: Self) !u8 {
111 return (try readArray(self, 1))[0];
112 }
113
114 /// Returned slice is not suffixed by needle but array_list will contain it.
115 pub fn readUntilDelimiterArrayList(self: Self, array_list: *std.array_list.Managed(u8), needle: u8, max_size: usize) ![]u8 {
116 const initial_len = array_list.items.len;
117 for (0..max_size) |i| {
118 try array_list.append(try readByte(self));
119 if (array_list.items[array_list.items.len - 1] == needle) return array_list.items[initial_len..][0..i];
120 }
121 return error.StreamTooLong;
122 }
123
124 /// Returned slice is suffixed by needle.
125 pub fn readUntilDelimiterAlloc(self: Self, allocator: std.mem.Allocator, needle: u8, max_size: usize) ![]u8 {
126 var list: std.array_list.Managed(u8) = .init(allocator);
127 errdefer list.deinit();
128 _ = try readUntilDelimiterArrayList(self, &list, needle, max_size);
129 return list.toOwnedSlice();
130 }
131
132 pub fn readUntilDelimiterOrEofAlloc(self: Self, allocator: std.mem.Allocator, needle: u8, max_size: usize) !?[]u8 {
133 var list: std.array_list.Managed(u8) = .init(allocator);
134 defer list.deinit();
135 _ = readUntilDelimiterArrayList(self, &list, needle, max_size) catch |err| switch (err) {
136 error.EndOfStream => return null,
137 else => |e| return e,
138 };
139 return try list.toOwnedSlice();
140 }
141
142 /// Returned slice is not suffixed by needle but buffer will contain it.
143 pub fn readUntilDelimitersBuf(self: Self, buffer: []u8, needle: []const u8) ![]u8 {
144 var real_len: usize = 0;
145 for (0..buffer.len) |_| {
146 buffer[real_len] = try readByte(self);
147 real_len += 1;
148 if (real_len < needle.len) continue;
149 if (std.mem.endsWith(u8, buffer[0..real_len], needle)) return buffer[0 .. real_len - needle.len];
150 }
151 return error.StreamTooLong;
152 }
153
154 /// Returned slice is not suffixed by needle but array_list will contain it.
155 pub fn readUntilDelimitersArrayList(self: Self, array_list: *std.array_list.Managed(u8), needle: []const u8, max_size: usize) ![]u8 {
156 const initial_len = array_list.items.len;
157 for (0..max_size) |i| {
158 try array_list.append(try readByte(self));
159 if (std.mem.endsWith(u8, array_list.items, needle)) return array_list.items[initial_len..][0 .. i + 1 - needle.len];
160 }
161 return error.StreamTooLong;
162 }
163
164 pub fn readAlloc(self: Self, allocator: std.mem.Allocator, size: usize) ![]u8 {
165 var array_list = try std.array_list.Managed(u8).initCapacity(allocator, size);
166 defer array_list.deinit();
167 try array_list.ensureUnusedCapacity(size);
168 const len = try readAll(self, array_list.allocatedSlice());
169 array_list.items.len += len;
170 if (len != size) return error.EndOfStream;
171 return array_list.toOwnedSlice();
172 }
173
174 pub fn readInt(self: Self, I: type, endian: std.builtin.Endian) !I {
175 comptime std.debug.assert(@bitSizeOf(I) % 8 == 0);
176 const array = try readArray(self, @sizeOf(I));
177 return std.mem.readInt(I, &array, endian);
178 }
179
180 /// Returned slice is suffixed by needle.
181 pub fn readUntilDelimitersAlloc(self: Self, allocator: std.mem.Allocator, needle: []const u8, max_size: usize) ![]u8 {
182 var list: std.array_list.Managed(u8) = .init(allocator);
183 errdefer list.deinit();
184 _ = try readUntilDelimitersArrayList(self, &list, needle, max_size);
185 return list.toOwnedSlice();
186 }
187
188 pub fn readUntilDelimiter(self: Self, buf: []u8, needle: u8) ![]u8 {
189 for (buf, 0..) |*c, i| {
190 const b = try readByte(self);
191 c.* = b;
192 if (b == needle) return buf[0..i];
193 }
194 return error.StreamTooLong;
195 }
196
197 /// Returned slice is not suffixed by needle but buf will contain it.
198 pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, needle: u8) !?[]u8 {
199 for (buf, 0..) |*c, i| {
200 const b = try readByte(self);
201 c.* = b;
202 if (b == needle) {
203 if (i == 0) return null;
204 return buf[0..i];
205 }
206 }
207 return error.StreamTooLong;
208 }
209
210 pub fn readExpected(self: Self, expected: []const u8) !bool {
211 for (expected) |item| {
212 const actual = try readByte(self);
213 if (actual != item) {
214 return false;
215 }
216 }
217 return true;
218 }
219
220 pub fn readType(self: Self, comptime C: type, endian: std.builtin.Endian) !C {
221 if (C == u8) return readByte(self); // single bytes dont have an endianness
222 return switch (@typeInfo(C)) {
223 .@"struct" => |t| {
224 switch (t.layout) {
225 .auto, .@"extern" => {
226 var s: C = undefined;
227 inline for (std.meta.fields(C)) |field| {
228 @field(s, field.name) = try readType(self, field.type, endian);
229 }
230 return s;
231 },
232 .@"packed" => return @bitCast(try readType(self, t.backing_integer.?, endian)),
233 }
234 },
235 .array => |t| {
236 var s: C = undefined;
237 for (0..t.len) |i| {
238 s[i] = try readType(self, t.child, endian);
239 }
240 return s;
241 },
242 .int => try self.readInt(C, endian),
243 .@"enum" => |t| @enumFromInt(try readType(self, t.tag_type, endian)),
244 else => unreachable,
245 };
246 }
247
248 pub fn skipBytes(self: Self, num_bytes: u64, comptime options: struct { buf_size: usize = 512 }) !void {
249 var buf: [options.buf_size]u8 = undefined;
250 var remaining = num_bytes;
251 while (remaining > 0) {
252 const amt = @min(remaining, options.buf_size);
253 try readNoEof(self, buf[0..amt]);
254 remaining -= amt;
255 }
256 }
257
258 pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) !void {
259 while (true) {
260 const byte = self.readByte() catch |err| switch (err) {
261 error.EndOfStream => return,
262 else => |e| return e,
263 };
264 if (byte == delimiter) return;
265 }
266 }
267
268 pub fn pipeTo(self: Self, writer: anytype) !void {
269 var buf: [4096]u8 = undefined;
270 while (true) {
271 const n = try self.read(&buf);
272 if (n == 0) break;
273 try writer.writeAll(buf[0..n]);
274 }
275 }
276 };
277}
278
279pub fn Writable(T: type, this_kind: enum { _var, _const, _bare }) type {
280 return struct {
281 const Error = T.WriteError;
282
283 const Self = switch (this_kind) {
284 ._var => *T,
285 ._const => *const T,
286 ._bare => T,
287 };
288
289 // pub fn write(self: Self, bytes: []const u8) WriteError!usize {
290 // }
291
292 // pub fn writev(self: Self, iovec: []const sys.struct_iovec) WriteError!usize {
293 // }
294
295 pub fn writeAll(self: Self, bytes: []const u8) Error!void {
296 var index: usize = 0;
297 while (index != bytes.len) {
298 index += try self.write(bytes[index..]);
299 }
300 }
301
302 pub fn writevAll(self: Self, bytes: []const []const u8) Error!void {
303 var iovec: [1024]sys.struct_iovec = undefined;
304 for (bytes, 0..) |slice, i| iovec[i] = .{ .base = @constCast(slice.ptr), .len = slice.len };
305 var left: usize = 0;
306 for (bytes) |item| left += item.len;
307
308 while (left > 0) {
309 var written: usize = try self.writev(iovec[0..bytes.len]);
310 left -= written;
311 for (iovec[0..bytes.len], 0..) |vec, i| {
312 switch (std.math.order(written, vec.len)) {
313 .gt => {
314 written -= iovec[i].len;
315 iovec[i].len = 0;
316 continue;
317 },
318 .eq => {
319 written -= iovec[i].len;
320 iovec[i].len = 0;
321 break;
322 },
323 .lt => {
324 iovec[i].base += written;
325 iovec[i].len -= written;
326 written -= written;
327 },
328 }
329 }
330 std.debug.assert(written == 0);
331 }
332 }
333
334 pub fn writeByteNTimes(self: Self, byte: u8, n: usize) Error!void {
335 var bytes: [4096]u8 = @splat(byte);
336 var remaining: usize = n;
337 while (remaining > 0) {
338 const to_write = @min(remaining, bytes.len);
339 try writeAll(self, bytes[0..to_write]);
340 remaining -= to_write;
341 }
342 }
343
344 pub fn writeNTimes(self: Self, input: []const u8, n: usize) Error!void {
345 var bytes: [1024][]const u8 = @splat(input);
346 var remaining: usize = n;
347 while (remaining > 0) {
348 const to_write = @min(remaining, bytes.len);
349 try writevAll(self, bytes[0..n]);
350 remaining -= to_write;
351 }
352 }
353
354 pub fn writeInt(self: Self, comptime I: type, value: I, endian: std.builtin.Endian) Error!void {
355 var bytes: [@as(u16, @intCast((@as(u17, @typeInfo(I).int.bits) + 7) / 8))]u8 = undefined;
356 std.mem.writeInt(std.math.ByteAlignedInt(I), &bytes, value, endian);
357 return writeAll(self, &bytes);
358 }
359
360 pub fn writeStruct(self: Self, value: anytype) Error!void {
361 // Only extern and packed structs have defined in-memory layout.
362 comptime std.debug.assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto);
363 return writeAll(self, std.mem.asBytes(&value));
364 }
365
366 pub fn writeIntPretty(self: Self, value: anytype, base: u8, case: fmt.Case) !void {
367 std.debug.assert(base >= 2);
368
369 const int_value = if (@TypeOf(value) == comptime_int) @as(std.math.IntFittingRange(value, value), value) else value;
370 const value_info = @typeInfo(@TypeOf(int_value)).int;
371
372 // The type must have the same size as `base` or be wider in order for the division to work
373 const min_int_bits = comptime @max(value_info.bits, 8);
374 const MinInt = std.meta.Int(.unsigned, min_int_bits);
375
376 const abs_value = @abs(int_value);
377 // The worst case in terms of space needed is base 2, plus 1 for the sign
378 var buf: [1 + @max(@as(comptime_int, value_info.bits), 1)]u8 = undefined;
379
380 var a: MinInt = abs_value;
381 var index: usize = buf.len;
382
383 if (base == 10) {
384 while (a >= 100) : (a = @divTrunc(a, 100)) {
385 index -= 2;
386 buf[index..][0..2].* = fmt.digits2(@intCast(a % 100));
387 }
388 if (a < 10) {
389 index -= 1;
390 buf[index] = '0' + @as(u8, @intCast(a));
391 } else {
392 index -= 2;
393 buf[index..][0..2].* = fmt.digits2(@intCast(a));
394 }
395 } else {
396 while (true) {
397 const digit = a % base;
398 index -= 1;
399 buf[index] = fmt.digitToChar(@intCast(digit), case);
400 a /= base;
401 if (a == 0) break;
402 }
403 }
404
405 if (value_info.signedness == .signed) {
406 if (value < 0) {
407 // Negative integer
408 index -= 1;
409 buf[index] = '-';
410 } else if (true) {
411 // Positive integer, omit the plus sign
412 // if (options.width == null or options.width.? == 0)
413 } else {
414 // Positive integer
415 index -= 1;
416 buf[index] = '+';
417 }
418 }
419
420 return writeAll(self, buf[index..]);
421 }
422
423 pub fn print(self: Self, comptime format: []const u8, args: anytype) Error!void {
424 return fmt.format(self, format, args);
425 }
426 };
427}
428
429pub const AnyReadable = @import("./AnyReadable.zig");
430
431pub const AnyWritable = @import("./AnyWritable.zig");
432
433pub const FixedBufferStream = @import("./fixed_buffer_stream.zig").FixedBufferStream;
434
435pub const BufferedReader = @import("./buffered_reader.zig").BufferedReader;
436
437pub const BufferedWriter = @import("./buffered_writer.zig").BufferedWriter;
438
439pub const CountingWriter = @import("./counting_writer.zig").CountingWriter;
440
441pub const NullWriter = @import("./null_writer.zig").NullWriter;
442
443pub const AllocatingWriter = @import("./allocating_writer.zig").AllocatingWriter;
444
445pub const CountingReader = @import("./counting_reader.zig").CountingReader;
446
447pub const LimitedReader = @import("./limited_reader.zig").LimitedReader;
448
449pub const HashWriter = @import("./hash_writer.zig").HashWriter;
450
451pub const SkipReader = @import("./skip_reader.zig").SkipReader;
452
453pub const Base64Reader = @import("./base64_reader.zig").Base64Reader;
454
455pub const Base64Writer = @import("./base64_writer.zig").Base64Writer;
456
457pub const crypto_random: std.Random = .{
458 .ptr = undefined,
459 .fillFn = getrandomFill,
460};
461fn getrandomFill(_: *anyopaque, buffer: []u8) void {
462 if (builtin.target.abi.isMusl()) {
463 _ = sys.getrandom(buffer, 0) catch unreachable;
464 return;
465 }
466 sys.libc.arc4random_buf(buffer.ptr, buffer.len);
467}
468
469pub fn randomBytes(comptime len: usize) [len]u8 {
470 var bytes: [len]u8 = undefined;
471 crypto_random.bytes(&bytes);
472 return bytes;
473}
474
475pub fn indexBufferT(bytes: [*]const u8, comptime T: type, endian: std.builtin.Endian, idx: usize, max_len: usize) T {
476 std.debug.assert(idx < max_len);
477 var fbs: FixedBufferStream([]const u8) = .init((bytes + (idx * @sizeOf(T)))[0..@sizeOf(T)]);
478 return fbs.readType(T, endian) catch |err| switch (err) {
479 error.EndOfStream => unreachable, // assert above has been violated
480 };
481}
482
483pub fn BufIndexer(comptime T: type, comptime endian: std.builtin.Endian) type {
484 return struct {
485 bytes: [*]const u8,
486 max_len: usize,
487
488 const Self = @This();
489
490 pub fn init(bytes: [*]const u8, max_len: usize) Self {
491 return .{
492 .bytes = bytes,
493 .max_len = max_len,
494 };
495 }
496
497 /// asserts 'idx' to be in bounds
498 pub fn at(self: *const Self, idx: usize) T {
499 return indexBufferT(self.bytes, T, endian, idx, self.max_len);
500 }
501 };
502}