aboutsummaryrefslogtreecommitdiffstats
path: root/src/base32.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/base32.zig')
-rw-r--r--src/base32.zig120
1 files changed, 120 insertions, 0 deletions
diff --git a/src/base32.zig b/src/base32.zig
new file mode 100644
index 0000000..b1400a5
--- /dev/null
+++ b/src/base32.zig
@@ -0,0 +1,120 @@
1const std = @import("std");
2
3// TODO(rutgerbrf): simplify the code of the encoder & decoder?
4
5pub const Encoder = struct {
6 const Self = @This();
7
8 out_off: u4 = 0,
9 buf: u5 = 0,
10
11 pub fn write(self: *Self, b: u8, out: []u8) usize {
12 var i: usize = 0;
13 var bits_left: u4 = 8;
14 while (bits_left > 0) {
15 var space_avail = @truncate(u3, 5 - self.out_off);
16 var write_bits: u3 = if (bits_left < space_avail) @truncate(u3, bits_left) else space_avail;
17 bits_left -= write_bits;
18 var mask: u8 = (@as(u8, 0x01) << write_bits) - 1;
19 var want: u8 = (b >> @truncate(u3, bits_left)) & mask;
20 self.buf |= @truncate(u5, want << (space_avail - write_bits));
21 self.out_off += write_bits;
22 if (self.out_off == 5) {
23 if (i >= out.len) break;
24 out[i] = self.char();
25 i += 1;
26 self.out_off = 0;
27 self.buf = 0;
28 }
29 }
30 return i;
31 }
32
33 fn char(self: *const Self) u8 {
34 return self.buf + (if (self.buf < 26) @as(u8, 'A') else '2' - 26);
35 }
36};
37
38pub const DecodeError = error{CorruptInputError};
39
40pub const Decoder = struct {
41 const Self = @This();
42
43 out_off: u4 = 0,
44 buf: u8 = 0,
45
46 pub fn read(self: *Self, c: u8) DecodeError!?u8 {
47 var ret: ?u8 = null;
48 var decoded_c = try decodeChar(c);
49 var bits_left: u3 = 5;
50 while (bits_left > 0) {
51 var space_avail: u4 = 8 - self.out_off;
52 var write_bits: u3 = if (bits_left < space_avail) bits_left else @truncate(u3, space_avail);
53 bits_left -= write_bits;
54 var mask: u8 = (@as(u8, 0x01) << write_bits) - 1;
55 var want: u8 = (decoded_c >> bits_left) & mask;
56 self.buf |= want << @truncate(u3, space_avail - write_bits);
57 self.out_off += write_bits;
58 if (self.out_off == 8) {
59 ret = self.buf;
60 self.out_off = 0;
61 self.buf = 0;
62 }
63 }
64 return ret;
65 }
66
67 fn decodeChar(p: u8) DecodeError!u5 {
68 var value: u5 = 0;
69 if (p >= 'A' and p <= 'Z') {
70 value = @truncate(u5, p - @as(u8, 'A'));
71 } else if (p >= '2' and p <= '9') {
72 // '2' -> 26
73 value = @truncate(u5, p - @as(u8, '2') + 26);
74 } else {
75 return error.CorruptInputError;
76 }
77 return value;
78 }
79};
80
81pub fn encodedLen(src_len: usize) usize {
82 const src_len_bits = src_len * 8;
83 return src_len_bits / 5 + (if (src_len_bits % 5 > 0) @as(usize, 1) else 0);
84}
85
86pub fn decodedLen(enc_len: usize) usize {
87 const enc_len_bits = enc_len * 5;
88 return enc_len_bits / 8;
89}
90
91pub fn encode(bs: []const u8, out: []u8) usize {
92 var e = Encoder{};
93 var i: usize = 0;
94 for (bs) |b| {
95 if (i >= out.len) break;
96 i += e.write(b, out[i..]);
97 }
98 if (e.out_off != 0 and i < out.len) {
99 out[i] = e.char();
100 i += 1;
101 }
102 return i; // amount of bytes processed
103}
104
105pub fn decode(ps: []const u8, out: []u8) DecodeError!usize {
106 var d = Decoder{};
107 var i: usize = 0;
108 for (ps) |p| {
109 if (i >= out.len) break;
110 if (try d.read(p)) |b| {
111 out[i] = b;
112 i += 1;
113 }
114 }
115 if (d.out_off != 0 and i < out.len) {
116 out[i] = d.buf;
117 i += 1;
118 }
119 return i; // amount of bytes processed
120}