authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-09-14 15:35:18-07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-09-14 15:35:18-07:00
log786d9e1b55cc75d6e11ad5077e96efc425316512
tree3e952c5482ad84db2c7ff295a3e92a8579bf3a68
parent61cf2b1af033a83ec2137e21824a8afa01318388
signature Signed by SSH key SHA256:4hHJbtBRU58AYXwjL7fkz2fnQHdiye8x1QpTCQ0sHNw

add Base64Writer


2 files changed, 141 insertions(+), 0 deletions(-)

base64_writer.zig created+139
......@@ -0,0 +1,139 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const extras = @import("extras");
4const nio = @import("./nio.zig");
5
6const sys = switch (builtin.target.os.tag) {
7 .linux => @import("sys-linux"),
8 .freebsd => @import("sys-freebsd"),
9 .netbsd => @import("sys-netbsd"),
10 .openbsd => @import("sys-openbsd"),
11 else => unreachable,
12};
13
14pub fn Base64Writer(comptime WriterType: type) type {
15 return struct {
16 backing_writer: WriterType,
17 alphabet: *const [64]u8,
18 buffer: extras.RingBuffer(u8, 3),
19
20 const Self = @This();
21
22 const Alphabet = union(enum) {
23 standard,
24 url_safe,
25 custom: *const [64]u8,
26 };
27
28 pub fn init(backing_writer: WriterType, alphabet: Alphabet) Self {
29 return .{
30 .backing_writer = backing_writer,
31 .alphabet = switch (alphabet) {
32 .standard => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
33 .url_safe => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
34 .custom => |a| a,
35 },
36 .buffer = .empty,
37 };
38 }
39
40 pub fn from(backing_writer: anytype, alphabet: Alphabet) Base64Writer(@TypeOf(backing_writer)) {
41 return .init(backing_writer, alphabet);
42 }
43
44 const W = nio.Writable(@This(), ._var);
45 pub const writeAll = W.writeAll;
46 pub const writevAll = W.writevAll;
47 pub const writeByteNTimes = W.writeByteNTimes;
48 pub const writeNTimes = W.writeNTimes;
49 pub const writeInt = W.writeInt;
50 pub const writeStruct = W.writeStruct;
51 pub const writeIntPretty = W.writeIntPretty;
52 pub const print = W.print;
53
54 pub const WriteError = extras.Pointee(WriterType).WriteError;
55
56 pub fn write(self: *Self, bytes: []const u8) WriteError!usize {
57 var count: usize = 0;
58 var fbs: nio.FixedBufferStream([]const u8) = .init(bytes);
59 while (true) {
60 const len = fbs.read(self.buffer.rest()) catch comptime unreachable;
61 self.buffer.len += len;
62 count += len;
63 if (len == 0) {
64 break;
65 }
66 if (self.buffer.len == 3) {
67 const i: u24 = @bitCast(self.buffer.items);
68 const P = packed struct(u24) { n3: u6, n2: u6, n1: u6, n0: u6 };
69 const p: P = @bitCast(@byteSwap(i));
70 try self.backing_writer.writeAll(&.{
71 self.alphabet[p.n0],
72 self.alphabet[p.n1],
73 self.alphabet[p.n2],
74 self.alphabet[p.n3],
75 });
76 self.buffer.len = 0;
77 }
78 }
79 return count;
80 }
81
82 pub fn writev(self: *Self, iovec: []const sys.struct_iovec) WriteError!usize {
83 var count: usize = 0;
84 for (iovec) |vec| {
85 const len = try write(self, vec.base[0..vec.len]);
86 if (len == 0) break;
87 count += len;
88 }
89 return count;
90 }
91
92 pub fn anyWritable(self: *Self) nio.AnyWritable {
93 const S = struct {
94 fn write(s: *allowzero anyopaque, buffer: []const u8) anyerror!usize {
95 const bw: *Self = @ptrCast(@alignCast(s));
96 return bw.write(buffer);
97 }
98 };
99 return .{
100 .vtable = &.{ .write = S.write },
101 .state = @ptrCast(self),
102 };
103 }
104
105 pub fn flush(self: *Self) !void {
106 switch (self.buffer.len) {
107 else => unreachable,
108 3 => unreachable,
109 2 => {
110 const n: u16 = @bitCast(self.buffer.items[0..2].*);
111 const i: u18 = @byteSwap(n);
112 const P = packed struct(u18) { n2: u6, n1: u6, n0: u6 };
113 const p: P = @bitCast(i << 2);
114 try self.backing_writer.writeAll(&.{
115 self.alphabet[p.n0],
116 self.alphabet[p.n1],
117 self.alphabet[p.n2],
118 '=',
119 });
120 self.buffer.len = 0;
121 },
122 1 => {
123 const n: u8 = @bitCast(self.buffer.items[0..1].*);
124 const i: u12 = @byteSwap(n);
125 const P = packed struct(u12) { n1: u6, n0: u6 };
126 const p: P = @bitCast(i << 4);
127 try self.backing_writer.writeAll(&.{
128 self.alphabet[p.n0],
129 self.alphabet[p.n1],
130 '=',
131 '=',
132 });
133 self.buffer.len = 0;
134 },
135 0 => {},
136 }
137 }
138 };
139}
nio.zig+2
......@@ -452,6 +452,8 @@ pub const SkipReader = @import("./skip_reader.zig").SkipReader;
452452
453453pub const Base64Reader = @import("./base64_reader.zig").Base64Reader;
454454
455pub const Base64Writer = @import("./base64_writer.zig").Base64Writer;
456
455457pub const crypto_random: std.Random = .{
456458 .ptr = undefined,
457459 .fillFn = getrandomFill,