1const std = @import("std");
2const nio = @import("./nio.zig");
3const extras = @import("extras");
4
5pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) type {
6 return struct {
7 unbuffered_reader: ReaderType,
8 buf: [buffer_size]u8,
9 start: usize,
10 end: usize,
11
12 const Self = @This();
13
14 pub fn init(unbuffered_reader: ReaderType) Self {
15 return .{
16 .unbuffered_reader = unbuffered_reader,
17 .buf = undefined,
18 .start = 0,
19 .end = 0,
20 };
21 }
22
23 const R = nio.Readable(@This(), ._var);
24 pub const readAll = R.readAll;
25 pub const readAtLeast = R.readAtLeast;
26 pub const readNoEof = R.readNoEof;
27 pub const readAllAlloc = R.readAllAlloc;
28 pub const readArray = R.readArray;
29 pub const readByte = R.readByte;
30 pub const readUntilDelimiterArrayList = R.readUntilDelimiterArrayList;
31 pub const readUntilDelimiterAlloc = R.readUntilDelimiterAlloc;
32 pub const readUntilDelimiterOrEofAlloc = R.readUntilDelimiterOrEofAlloc;
33 pub const readUntilDelimitersBuf = R.readUntilDelimitersBuf;
34 pub const readUntilDelimitersArrayList = R.readUntilDelimitersArrayList;
35 pub const readAlloc = R.readAlloc;
36 pub const readInt = R.readInt;
37 pub const readUntilDelimitersAlloc = R.readUntilDelimitersAlloc;
38 pub const readUntilDelimiter = R.readUntilDelimiter;
39 pub const readUntilDelimiterOrEof = R.readUntilDelimiterOrEof;
40 pub const readExpected = R.readExpected;
41 pub const readType = R.readType;
42 pub const skipBytes = R.skipBytes;
43 pub const skipUntilDelimiterOrEof = R.skipUntilDelimiterOrEof;
44 pub const pipeTo = R.pipeTo;
45
46 pub const ReadError = extras.Pointee(ReaderType).ReadError;
47 pub fn read(self: *Self, dest: []u8) ReadError!usize {
48 // First try reading from the already buffered data onto the destination.
49 const current = self.buf[self.start..self.end];
50 if (current.len != 0) {
51 const to_transfer = @min(current.len, dest.len);
52 @memcpy(dest[0..to_transfer], current[0..to_transfer]);
53 self.start += to_transfer;
54 return to_transfer;
55 }
56
57 // If dest is large, read from the unbuffered reader directly into the destination.
58 if (dest.len >= buffer_size) {
59 return self.unbuffered_reader.read(dest);
60 }
61
62 // If dest is small, read from the unbuffered reader into our own internal buffer,
63 // and then transfer to destination.
64 self.end = try self.unbuffered_reader.read(&self.buf);
65 const to_transfer = @min(self.end, dest.len);
66 @memcpy(dest[0..to_transfer], self.buf[0..to_transfer]);
67 self.start = to_transfer;
68 return to_transfer;
69 }
70
71 pub fn anyReadable(self: *Self) nio.AnyReadable {
72 const S = struct {
73 fn read(s: *allowzero anyopaque, buffer: []u8) anyerror!usize {
74 const br: *Self = @ptrCast(@alignCast(s));
75 return br.read(buffer);
76 }
77 };
78 return .{
79 .vtable = &.{ .read = S.read },
80 .state = @ptrCast(self),
81 };
82 }
83 };
84}