From cfb10566fdb3093363fc39d21a8c5aa5c4deeeeb Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Fri, 21 May 2021 00:16:17 +0200 Subject: Initial commit --- src/base32.zig | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 src/base32.zig (limited to 'src/base32.zig') 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 @@ +const std = @import("std"); + +// TODO(rutgerbrf): simplify the code of the encoder & decoder? + +pub const Encoder = struct { + const Self = @This(); + + out_off: u4 = 0, + buf: u5 = 0, + + pub fn write(self: *Self, b: u8, out: []u8) usize { + var i: usize = 0; + var bits_left: u4 = 8; + while (bits_left > 0) { + var space_avail = @truncate(u3, 5 - self.out_off); + var write_bits: u3 = if (bits_left < space_avail) @truncate(u3, bits_left) else space_avail; + bits_left -= write_bits; + var mask: u8 = (@as(u8, 0x01) << write_bits) - 1; + var want: u8 = (b >> @truncate(u3, bits_left)) & mask; + self.buf |= @truncate(u5, want << (space_avail - write_bits)); + self.out_off += write_bits; + if (self.out_off == 5) { + if (i >= out.len) break; + out[i] = self.char(); + i += 1; + self.out_off = 0; + self.buf = 0; + } + } + return i; + } + + fn char(self: *const Self) u8 { + return self.buf + (if (self.buf < 26) @as(u8, 'A') else '2' - 26); + } +}; + +pub const DecodeError = error{CorruptInputError}; + +pub const Decoder = struct { + const Self = @This(); + + out_off: u4 = 0, + buf: u8 = 0, + + pub fn read(self: *Self, c: u8) DecodeError!?u8 { + var ret: ?u8 = null; + var decoded_c = try decodeChar(c); + var bits_left: u3 = 5; + while (bits_left > 0) { + var space_avail: u4 = 8 - self.out_off; + var write_bits: u3 = if (bits_left < space_avail) bits_left else @truncate(u3, space_avail); + bits_left -= write_bits; + var mask: u8 = (@as(u8, 0x01) << write_bits) - 1; + var want: u8 = (decoded_c >> bits_left) & mask; + self.buf |= want << @truncate(u3, space_avail - write_bits); + self.out_off += write_bits; + if (self.out_off == 8) { + ret = self.buf; + self.out_off = 0; + self.buf = 0; + } + } + return ret; + } + + fn decodeChar(p: u8) DecodeError!u5 { + var value: u5 = 0; + if (p >= 'A' and p <= 'Z') { + value = @truncate(u5, p - @as(u8, 'A')); + } else if (p >= '2' and p <= '9') { + // '2' -> 26 + value = @truncate(u5, p - @as(u8, '2') + 26); + } else { + return error.CorruptInputError; + } + return value; + } +}; + +pub fn encodedLen(src_len: usize) usize { + const src_len_bits = src_len * 8; + return src_len_bits / 5 + (if (src_len_bits % 5 > 0) @as(usize, 1) else 0); +} + +pub fn decodedLen(enc_len: usize) usize { + const enc_len_bits = enc_len * 5; + return enc_len_bits / 8; +} + +pub fn encode(bs: []const u8, out: []u8) usize { + var e = Encoder{}; + var i: usize = 0; + for (bs) |b| { + if (i >= out.len) break; + i += e.write(b, out[i..]); + } + if (e.out_off != 0 and i < out.len) { + out[i] = e.char(); + i += 1; + } + return i; // amount of bytes processed +} + +pub fn decode(ps: []const u8, out: []u8) DecodeError!usize { + var d = Decoder{}; + var i: usize = 0; + for (ps) |p| { + if (i >= out.len) break; + if (try d.read(p)) |b| { + out[i] = b; + i += 1; + } + } + if (d.out_off != 0 and i < out.len) { + out[i] = d.buf; + i += 1; + } + return i; // amount of bytes processed +} -- cgit v1.2.3