1const std = @import("std");
2const nio = @import("./nio.zig");
3const builtin = @import("builtin");
4
5const 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
14pub 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};