authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-07-10 19:58:39 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-07-10 19:58:39 -07:00
log589cf70fd213e2f4dc5b76561d176e4c8a450c17
tree814780eb41e4b14f19254657690cedfd712ddce6
parent6299ba686339c2c7c8d83a4648bcd9f0eec3ff77
signaturebadge-check Signed by SSH key SHA256:4hHJbtBRU58AYXwjL7fkz2fnQHdiye8x1QpTCQ0sHNw

add Base64Reader


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

base64_reader.zig created+102
...@@ -0,0 +1,102 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const extras = @import("extras");
4const nio = @import("./nio.zig");
5const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
6const url_safe_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;
7
8pub fn Base64Reader(comptime ReaderType: type) type {
9 return struct {
10 source_reader: ReaderType,
11 alphabet: *const [64]u8,
12 bits: std.bit_set.IntegerBitSet(bit_max),
13 jdx: u8,
14
15 const Self = @This();
16 const bit_max = std.math.log2(64); // 6
17
18 pub fn init(source_reader: ReaderType) Self {
19 return .{
20 .source_reader = source_reader,
21 .alphabet = standard_alphabet_chars,
22 .bits = .empty,
23 .jdx = bit_max,
24 };
25 }
26
27 pub fn from(source_reader: anytype) Base64Reader(@TypeOf(source_reader)) {
28 return .init(source_reader);
29 }
30
31 const R = nio.Readable(@This(), ._var);
32 pub const readAll = R.readAll;
33 pub const readAtLeast = R.readAtLeast;
34 pub const readNoEof = R.readNoEof;
35 pub const readAllAlloc = R.readAllAlloc;
36 pub const readArray = R.readArray;
37 pub const readByte = R.readByte;
38 pub const readUntilDelimiterArrayList = R.readUntilDelimiterArrayList;
39 pub const readUntilDelimiterAlloc = R.readUntilDelimiterAlloc;
40 pub const readUntilDelimiterOrEofAlloc = R.readUntilDelimiterOrEofAlloc;
41 pub const readUntilDelimitersBuf = R.readUntilDelimitersBuf;
42 pub const readUntilDelimitersArrayList = R.readUntilDelimitersArrayList;
43 pub const readAlloc = R.readAlloc;
44 pub const readInt = R.readInt;
45 pub const readUntilDelimitersAlloc = R.readUntilDelimitersAlloc;
46 pub const readUntilDelimiter = R.readUntilDelimiter;
47 pub const readUntilDelimiterOrEof = R.readUntilDelimiterOrEof;
48 pub const readExpected = R.readExpected;
49 pub const readType = R.readType;
50 pub const skipBytes = R.skipBytes;
51 pub const skipUntilDelimiterOrEof = R.skipUntilDelimiterOrEof;
52 pub const pipeTo = R.pipeTo;
53
54 pub const ReadError = extras.Pointee(ReaderType).ReadError || error{InvalidCharacter};
55 pub fn read(self: *Self, dest: []u8) ReadError!usize {
56 var i: usize = 0;
57 while (i < dest.len) {
58 dest[i] = self.nextInt(u8) catch |err| switch (err) {
59 error.EndOfStream => break,
60 else => |e| return e,
61 };
62 i += 1;
63 }
64 return i;
65 }
66
67 pub fn anyReadable(self: *Self) nio.AnyReadable {
68 const S = struct {
69 fn read(s: *allowzero anyopaque, buffer: []u8) anyerror!usize {
70 const br: *Self = @ptrCast(@alignCast(s));
71 return br.read(buffer);
72 }
73 };
74 return .{
75 .vtable = &.{ .read = S.read },
76 .state = @ptrCast(self),
77 };
78 }
79
80 pub fn next(self: *Self) !u1 {
81 if (self.jdx == bit_max) {
82 const c = try self.source_reader.readByte();
83 self.bits.mask = @intCast(std.mem.findScalar(u8, self.alphabet, c) orelse if (c == '=') 0 else return error.InvalidCharacter);
84 self.jdx = 0;
85 return self.next();
86 }
87 defer self.jdx += 1;
88 return @intFromBool(self.bits.isSet(bit_max - 1 - self.jdx));
89 }
90
91 pub fn nextInt(self: *Self, T: type) !T {
92 const info = @typeInfo(T).int;
93 var result: T = 0;
94 for (0..info.bits) |_| {
95 result <<= 1;
96 const val = try self.next();
97 result += val;
98 }
99 return result;
100 }
101 };
102}
nio.zig+2
...@@ -448,6 +448,8 @@ pub const HashWriter = @import("./hash_writer.zig").HashWriter;...@@ -448,6 +448,8 @@ pub const HashWriter = @import("./hash_writer.zig").HashWriter;
448448
449pub const SkipReader = @import("./skip_reader.zig").SkipReader;449pub const SkipReader = @import("./skip_reader.zig").SkipReader;
450450
451pub const Base64Reader = @import("./base64_reader.zig").Base64Reader;
452
451pub const crypto_random: std.Random = .{453pub const crypto_random: std.Random = .{
452 .ptr = undefined,454 .ptr = undefined,
453 .fillFn = getrandomFill,455 .fillFn = getrandomFill,