| 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 | .macos => @import("sys-darwin"), |
| 8 | .freebsd => @import("sys-freebsd"), |
| 9 | .netbsd => @import("sys-netbsd"), |
| 10 | .openbsd => @import("sys-openbsd"), |
| 11 | else => unreachable, |
| 12 | }; |
| 13 | |
| 14 | pub const AllocatingWriter = struct { |
| 15 | allocator: std.mem.Allocator, |
| 16 | items: []u8, |
| 17 | capacity: usize, |
| 18 | |
| 19 | pub fn init(allocator: std.mem.Allocator) AllocatingWriter { |
| 20 | return .{ |
| 21 | .allocator = allocator, |
| 22 | .items = "", |
| 23 | .capacity = 0, |
| 24 | }; |
| 25 | } |
| 26 | |
| 27 | pub fn deinit(self: *AllocatingWriter) void { |
| 28 | self.allocator.free(self.allocatedSlice()); |
| 29 | } |
| 30 | |
| 31 | pub fn allocatedSlice(self: *AllocatingWriter) []u8 { |
| 32 | return self.items.ptr[0..self.capacity]; |
| 33 | } |
| 34 | |
| 35 | pub fn toOwnedSlice(self: *AllocatingWriter) ![]u8 { |
| 36 | if (self.allocator.resize(self.allocatedSlice(), self.items.len)) { |
| 37 | defer self.capacity = 0; |
| 38 | defer self.items = ""; |
| 39 | return self.items; |
| 40 | } |
| 41 | const new_slice = try self.allocator.dupe(u8, self.items); |
| 42 | self.allocator.free(self.allocatedSlice()); |
| 43 | return new_slice; |
| 44 | } |
| 45 | |
| 46 | pub fn ensureUnusedCapacity(self: *AllocatingWriter, capacity: usize) !void { |
| 47 | if (self.capacity - self.items.len >= capacity) return; |
| 48 | const len = self.items.len; |
| 49 | const new_capacity = std.math.ceilPowerOfTwo(usize, @max(len + capacity, self.capacity)) catch return error.OutOfMemory; |
| 50 | const new_slice = try self.allocator.alloc(u8, new_capacity); |
| 51 | @memcpy(new_slice[0..len], self.items); |
| 52 | self.allocator.free(self.allocatedSlice()); |
| 53 | self.items = new_slice; |
| 54 | self.items.len = len; |
| 55 | self.capacity = new_capacity; |
| 56 | } |
| 57 | |
| 58 | pub fn unusedSlice(self: *AllocatingWriter) []u8 { |
| 59 | return self.allocatedSlice()[self.items.len..]; |
| 60 | } |
| 61 | |
| 62 | pub fn appendAssumeCapacity(self: *AllocatingWriter, bytes: []const u8) void { |
| 63 | @memcpy(self.unusedSlice()[0..bytes.len], bytes); |
| 64 | self.items.len += bytes.len; |
| 65 | } |
| 66 | |
| 67 | pub fn orderedRemove(self: *AllocatingWriter, index: usize) u8 { |
| 68 | const old_item = self.items[index]; |
| 69 | std.mem.copyForwards(u8, self.items[index .. self.items.len - 1], self.items[index + 1 .. self.items.len]); |
| 70 | self.items.len -= 1; |
| 71 | return old_item; |
| 72 | } |
| 73 | |
| 74 | pub fn last(self: *AllocatingWriter) u8 { |
| 75 | return self.items[self.items.len - 1]; |
| 76 | } |
| 77 | |
| 78 | pub fn insertAt(self: *AllocatingWriter, index: usize, bytes: []const u8) !void { |
| 79 | try self.ensureUnusedCapacity(bytes.len); |
| 80 | self.items.len += bytes.len; |
| 81 | std.mem.copyBackwards(u8, self.items[index + bytes.len .. self.items.len], self.items[index .. self.items.len - bytes.len]); |
| 82 | @memcpy(self.items[index..][0..bytes.len], bytes); |
| 83 | } |
| 84 | |
| 85 | pub fn clearAndFree(self: *AllocatingWriter) void { |
| 86 | self.allocator.free(self.allocatedSlice()); |
| 87 | self.items.len = 0; |
| 88 | self.capacity = 0; |
| 89 | } |
| 90 | |
| 91 | pub fn replaceRangeAssumeCapacity(self: *AllocatingWriter, start: usize, len: usize, new_items: []const u8) void { |
| 92 | std.debug.assert(self.capacity - self.items.len >= new_items.len -| len); |
| 93 | const tail = self.items[start + len ..]; |
| 94 | const vacated = self.items[self.items.len - (len -| new_items.len) ..]; |
| 95 | self.items.len = self.items.len - len + new_items.len; |
| 96 | @memmove(self.items[start + new_items.len ..], tail); |
| 97 | @memcpy(self.items[start..][0..new_items.len], new_items); |
| 98 | @memset(vacated, undefined); |
| 99 | } |
| 100 | |
| 101 | const W = nio.Writable(@This(), ._var); |
| 102 | pub const writeAll = W.writeAll; |
| 103 | pub const writevAll = W.writevAll; |
| 104 | pub const writeByteNTimes = W.writeByteNTimes; |
| 105 | pub const writeNTimes = W.writeNTimes; |
| 106 | pub const writeInt = W.writeInt; |
| 107 | pub const writeStruct = W.writeStruct; |
| 108 | pub const writeIntPretty = W.writeIntPretty; |
| 109 | pub const print = W.print; |
| 110 | |
| 111 | pub const WriteError = std.mem.Allocator.Error; |
| 112 | |
| 113 | pub fn write(self: *AllocatingWriter, bytes: []const u8) WriteError!usize { |
| 114 | try self.ensureUnusedCapacity(bytes.len); |
| 115 | self.appendAssumeCapacity(bytes); |
| 116 | return bytes.len; |
| 117 | } |
| 118 | pub fn writev(self: *AllocatingWriter, iovec: []const sys.struct_iovec) WriteError!usize { |
| 119 | var len: usize = 0; |
| 120 | for (iovec) |vec| len += vec.len; |
| 121 | try self.ensureUnusedCapacity(len); |
| 122 | for (iovec) |vec| self.appendAssumeCapacity(vec.base[0..vec.len]); |
| 123 | return len; |
| 124 | } |
| 125 | |
| 126 | pub fn anyWritable(self: *AllocatingWriter) nio.AnyWritable { |
| 127 | const S = struct { |
| 128 | fn write(s: *allowzero anyopaque, buffer: []const u8) anyerror!usize { |
| 129 | const bw: *AllocatingWriter = @ptrCast(@alignCast(s)); |
| 130 | return bw.write(buffer); |
| 131 | } |
| 132 | }; |
| 133 | return .{ |
| 134 | .vtable = &.{ .write = S.write }, |
| 135 | .state = @ptrCast(self), |
| 136 | }; |
| 137 | } |
| 138 | }; |