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 | } | ||