| ... | @@ -0,0 +1,83 @@ |
| 1 | const std = @import("std"); |
| 2 | const nio = @import("./nio.zig"); |
| 3 | const builtin = @import("builtin"); |
| 4 | |
| 5 | const sys = switch (builtin.target.os.tag) { |
| 6 | .linux => @import("sys-linux"), |
| 7 | else => unreachable, |
| 8 | }; |
| 9 | |
| 10 | pub const AllocatingWriter = struct { |
| 11 | allocator: std.mem.Allocator, |
| 12 | items: []u8, |
| 13 | capacity: usize, |
| 14 | |
| 15 | pub fn init(allocator: std.mem.Allocator) AllocatingWriter { |
| 16 | return .{ |
| 17 | .allocator = allocator, |
| 18 | .items = "", |
| 19 | .capacity = 0, |
| 20 | }; |
| 21 | } |
| 22 | |
| 23 | pub fn deinit(self: *AllocatingWriter) void { |
| 24 | self.allocator.free(self.allocatedSlice()); |
| 25 | } |
| 26 | |
| 27 | pub fn allocatedSlice(self: *AllocatingWriter) []u8 { |
| 28 | return self.items.ptr[0..self.capacity]; |
| 29 | } |
| 30 | |
| 31 | pub fn toOwnedSlice(self: *AllocatingWriter) ![]u8 { |
| 32 | if (self.allocator.resize(self.allocatedSlice(), self.items.len)) { |
| 33 | defer self.capacity = 0; |
| 34 | defer self.items = ""; |
| 35 | return self.items; |
| 36 | } |
| 37 | const new_slice = try self.allocator.dupe(u8, self.items); |
| 38 | self.allocator.free(self.allocatedSlice()); |
| 39 | return new_slice; |
| 40 | } |
| 41 | |
| 42 | pub fn ensureUnusedCapacity(self: *AllocatingWriter, capacity: usize) !void { |
| 43 | if (self.capacity - self.items.len >= capacity) return; |
| 44 | const len = self.items.len; |
| 45 | const new_capacity = std.math.ceilPowerOfTwo(usize, @max(len + capacity, self.capacity)) catch return error.OutOfMemory; |
| 46 | const new_slice = try self.allocator.alloc(u8, new_capacity); |
| 47 | @memcpy(new_slice[0..len], self.items); |
| 48 | self.allocator.free(self.allocatedSlice()); |
| 49 | self.items = new_slice; |
| 50 | self.items.len = len; |
| 51 | self.capacity = new_capacity; |
| 52 | } |
| 53 | |
| 54 | fn unusedSlice(self: *AllocatingWriter) []u8 { |
| 55 | return self.allocatedSlice()[self.items.len..]; |
| 56 | } |
| 57 | |
| 58 | const W = nio.Writable(@This(), ._var); |
| 59 | pub const writeAll = W.writeAll; |
| 60 | pub const writevAll = W.writevAll; |
| 61 | pub const writeByteNTimes = W.writeByteNTimes; |
| 62 | pub const writeNTimes = W.writeNTimes; |
| 63 | pub const writeInt = W.writeInt; |
| 64 | pub const writeStruct = W.writeStruct; |
| 65 | pub const writeIntPretty = W.writeIntPretty; |
| 66 | pub const print = W.print; |
| 67 | |
| 68 | pub const WriteError = std.mem.Allocator.Error; |
| 69 | |
| 70 | pub fn write(self: *AllocatingWriter, bytes: []const u8) WriteError!usize { |
| 71 | try self.ensureUnusedCapacity(bytes.len); |
| 72 | @memcpy(self.unusedSlice()[0..bytes.len], bytes); |
| 73 | self.items.len += bytes.len; |
| 74 | return bytes.len; |
| 75 | } |
| 76 | pub fn writev(self: *AllocatingWriter, iovec: []const sys.struct_iovec) WriteError!usize { |
| 77 | var len: usize = 0; |
| 78 | for (iovec) |vec| len += vec.len; |
| 79 | try self.ensureUnusedCapacity(len); |
| 80 | for (iovec) |vec| _ = self.write(vec.base[0..vec.len]) catch unreachable; |
| 81 | return len; |
| 82 | } |
| 83 | }; |