1const std = @import("std");
2const builtin = @import("builtin");
3const extras = @import("extras");
4const sys_linux = @import("sys-linux");
5
6const sys = switch (builtin.target.os.tag) {
7 .linux => sys_linux,
8 .macos => @import("sys-darwin"),
9 .freebsd => @import("sys-freebsd"),
10 .netbsd => @import("sys-netbsd"),
11 .openbsd => @import("sys-openbsd"),
12 else => unreachable,
13};
14
15const nio = @import("./nio.zig");
16
17pub fn FixedBufferStream(comptime Buffer: type) type {
18 comptime std.debug.assert(Buffer == []u8 or Buffer == []const u8);
19 return struct {
20 buffer: Buffer,
21 pos: usize,
22
23 const is_const = Buffer == []const u8;
24
25 const Self = @This();
26
27 pub fn init(buffer: Buffer) Self {
28 return .{
29 .buffer = buffer,
30 .pos = 0,
31 };
32 }
33
34 const R = nio.Readable(@This(), ._var);
35 pub const readAll = R.readAll;
36 pub const readAtLeast = R.readAtLeast;
37 pub const readNoEof = R.readNoEof;
38 pub const readAllAlloc = R.readAllAlloc;
39 pub const readArray = R.readArray;
40 pub const readByte = R.readByte;
41 pub const readUntilDelimiterArrayList = R.readUntilDelimiterArrayList;
42 pub const readUntilDelimiterAlloc = R.readUntilDelimiterAlloc;
43 pub const readUntilDelimiterOrEofAlloc = R.readUntilDelimiterOrEofAlloc;
44 pub const readUntilDelimitersBuf = R.readUntilDelimitersBuf;
45 pub const readUntilDelimitersArrayList = R.readUntilDelimitersArrayList;
46 pub const readAlloc = R.readAlloc;
47 pub const readInt = R.readInt;
48 pub const readUntilDelimitersAlloc = R.readUntilDelimitersAlloc;
49 pub const readUntilDelimiter = R.readUntilDelimiter;
50 pub const readUntilDelimiterOrEof = R.readUntilDelimiterOrEof;
51 pub const readExpected = R.readExpected;
52 pub const readType = R.readType;
53 pub const skipBytes = R.skipBytes;
54 pub const skipUntilDelimiterOrEof = R.skipUntilDelimiterOrEof;
55 pub const pipeTo = R.pipeTo;
56
57 pub const ReadError = error{};
58 pub fn read(self: *Self, dest: []u8) ReadError!usize {
59 const size = @min(dest.len, self.buffer.len - self.pos);
60 const end = self.pos + size;
61 @memcpy(dest[0..size], self.buffer[self.pos..end]);
62 self.pos = end;
63 return size;
64 }
65
66 pub fn anyReadable(self: *Self) nio.AnyReadable {
67 const S = struct {
68 fn read(s: *allowzero anyopaque, buffer: []u8) anyerror!usize {
69 const fbs: *Self = @ptrCast(@alignCast(s));
70 return fbs.read(buffer);
71 }
72 };
73 return .{
74 .vtable = &.{ .read = S.read },
75 .state = @ptrCast(self),
76 };
77 }
78
79 const W = nio.Writable(@This(), ._var);
80 pub const writeAll = W.writeAll;
81 pub const writevAll = W.writevAll;
82 pub const writeByteNTimes = W.writeByteNTimes;
83 pub const writeNTimes = W.writeNTimes;
84 pub const writeInt = W.writeInt;
85 pub const writeStruct = W.writeStruct;
86 pub const writeIntPretty = W.writeIntPretty;
87 pub const print = W.print;
88
89 pub const WriteError = error{NoSpaceLeft};
90 /// If the returned number of bytes written is less than requested, the buffer is full.
91 /// Returns `error.NoSpaceLeft` when no bytes would be written.
92 pub fn write(self: *Self, bytes: []const u8) WriteError!usize {
93 if (bytes.len == 0) return 0;
94 if (self.pos >= self.buffer.len) return error.NoSpaceLeft;
95 const n = @min(self.buffer.len - self.pos, bytes.len);
96 @memcpy(self.buffer[self.pos..][0..n], bytes[0..n]);
97 self.pos += n;
98 if (n == 0) return error.NoSpaceLeft;
99 return n;
100 }
101 pub fn writev(self: *Self, iovec: []const sys.struct_iovec) WriteError!usize {
102 var count: usize = 0;
103 for (iovec) |vec| {
104 const actual = try write(self, vec.base[0..vec.len]);
105 count += actual;
106 if (actual < vec.len) break;
107 }
108 return count;
109 }
110
111 pub fn anyWritable(self: *Self) nio.AnyWritable {
112 const S = struct {
113 fn write(s: *allowzero anyopaque, buffer: []const u8) anyerror!usize {
114 const fbs: *Self = @ptrCast(@alignCast(s));
115 return fbs.write(buffer);
116 }
117 };
118 return .{
119 .vtable = &.{ .write = S.write },
120 .state = @ptrCast(self),
121 };
122 }
123
124 pub fn written(self: *Self) Buffer {
125 return self.buffer[0..self.pos];
126 }
127
128 pub fn rest(self: *Self) Buffer {
129 return self.buffer[self.pos..];
130 }
131
132 pub fn takeArray(self: *Self, comptime len: usize) (if (is_const) *const [len]u8 else *[len]u8) {
133 defer self.pos += len;
134 return self.rest()[0..len];
135 }
136
137 pub fn takeSlice(self: *Self, count: usize) Buffer {
138 defer self.pos += count;
139 return self.rest()[0..count];
140 }
141
142 pub fn takeInt(self: *Self, I: type, endian: std.builtin.Endian) I {
143 comptime std.debug.assert(@bitSizeOf(I) % 8 == 0);
144 return std.mem.readInt(I, self.takeArray(@sizeOf(I)), endian);
145 }
146
147 pub fn takeIntSlice(self: *Self, I: type, len: usize) (if (is_const) []align(1) const I else []align(1) I) {
148 comptime std.debug.assert(@bitSizeOf(I) % 8 == 0);
149 return @ptrCast(self.takeSlice(@sizeOf(I) * len));
150 }
151
152 pub fn readSlice(self: *Self, count: usize) !Buffer {
153 if (self.pos + count > self.buffer.len) return error.EndOfStream;
154 return self.takeSlice(count);
155 }
156 };
157}