diff options
Diffstat (limited to 'src/base32.zig')
-rw-r--r-- | src/base32.zig | 120 |
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 @@ | |||
1 | const std = @import("std"); | ||
2 | |||
3 | // TODO(rutgerbrf): simplify the code of the encoder & decoder? | ||
4 | |||
5 | pub 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 | |||
38 | pub const DecodeError = error{CorruptInputError}; | ||
39 | |||
40 | pub 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 | |||
81 | pub 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 | |||
86 | pub fn decodedLen(enc_len: usize) usize { | ||
87 | const enc_len_bits = enc_len * 5; | ||
88 | return enc_len_bits / 8; | ||
89 | } | ||
90 | |||
91 | pub 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 | |||
105 | pub 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 | } | ||