| 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); |
| 3 | const extras = @import("extras"); |
| 4 | const sys_linux = @import("sys-linux"); |
| 5 | |
| 6 | pub const fmt = @import("./fmt.zig"); |
| 7 | |
| 8 | const 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 | |
| 17 | pub 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 | |
| 279 | pub 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 | |
| 429 | pub const AnyReadable = @import("./AnyReadable.zig"); |
| 430 | |
| 431 | pub const AnyWritable = @import("./AnyWritable.zig"); |
| 432 | |
| 433 | pub const FixedBufferStream = @import("./fixed_buffer_stream.zig").FixedBufferStream; |
| 434 | |
| 435 | pub const BufferedReader = @import("./buffered_reader.zig").BufferedReader; |
| 436 | |
| 437 | pub const BufferedWriter = @import("./buffered_writer.zig").BufferedWriter; |
| 438 | |
| 439 | pub const CountingWriter = @import("./counting_writer.zig").CountingWriter; |
| 440 | |
| 441 | pub const NullWriter = @import("./null_writer.zig").NullWriter; |
| 442 | |
| 443 | pub const AllocatingWriter = @import("./allocating_writer.zig").AllocatingWriter; |
| 444 | |
| 445 | pub const CountingReader = @import("./counting_reader.zig").CountingReader; |
| 446 | |
| 447 | pub const LimitedReader = @import("./limited_reader.zig").LimitedReader; |
| 448 | |
| 449 | pub const HashWriter = @import("./hash_writer.zig").HashWriter; |
| 450 | |
| 451 | pub const SkipReader = @import("./skip_reader.zig").SkipReader; |
| 452 | |
| 453 | pub const Base64Reader = @import("./base64_reader.zig").Base64Reader; |
| 454 | |
| 455 | pub const Base64Writer = @import("./base64_writer.zig").Base64Writer; |
| 456 | |
| 457 | pub const crypto_random: std.Random = .{ |
| 458 | .ptr = undefined, |
| 459 | .fillFn = getrandomFill, |
| 460 | }; |
| 461 | fn 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 | |
| 469 | pub fn randomBytes(comptime len: usize) [len]u8 { |
| 470 | var bytes: [len]u8 = undefined; |
| 471 | crypto_random.bytes(&bytes); |
| 472 | return bytes; |
| 473 | } |
| 474 | |
| 475 | pub 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 | |
| 483 | pub 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 | } |