diff options
author | Rutger Broekhoff | 2021-05-22 11:07:03 +0200 |
---|---|---|
committer | Rutger Broekhoff | 2021-05-22 11:07:13 +0200 |
commit | 39deabb251e81d3a4f4777cec3ce32f5356fe842 (patch) | |
tree | 798297cf93ba0b6a90a1f5e871ea9f8166957e73 /src/base32.zig | |
parent | 14bb72bf0176e395723f31ad071480a3160e0130 (diff) | |
download | zig-nkeys-39deabb251e81d3a4f4777cec3ce32f5356fe842.tar.gz zig-nkeys-39deabb251e81d3a4f4777cec3ce32f5356fe842.zip |
Make Base32 encoder an iterator
Diffstat (limited to 'src/base32.zig')
-rw-r--r-- | src/base32.zig | 105 |
1 files changed, 71 insertions, 34 deletions
diff --git a/src/base32.zig b/src/base32.zig index b1400a5..26622d8 100644 --- a/src/base32.zig +++ b/src/base32.zig | |||
@@ -1,40 +1,83 @@ | |||
1 | const std = @import("std"); | 1 | const std = @import("std"); |
2 | 2 | ||
3 | // TODO(rutgerbrf): simplify the code of the encoder & decoder? | ||
4 | |||
5 | pub const Encoder = struct { | 3 | pub const Encoder = struct { |
6 | const Self = @This(); | 4 | const Self = @This(); |
7 | 5 | ||
8 | out_off: u4 = 0, | 6 | buffer: []const u8, |
9 | buf: u5 = 0, | 7 | index: ?usize, |
8 | bit_off: u3, | ||
10 | 9 | ||
11 | pub fn write(self: *Self, b: u8, out: []u8) usize { | 10 | fn bitmask(self: *const Self) u8 { |
12 | var i: usize = 0; | 11 | return @as(u8, 0b11111000) >> self.bit_off; |
13 | var bits_left: u4 = 8; | 12 | } |
14 | while (bits_left > 0) { | 13 | |
15 | var space_avail = @truncate(u3, 5 - self.out_off); | 14 | fn n_front_bits(self: *const Self) u3 { |
16 | var write_bits: u3 = if (bits_left < space_avail) @truncate(u3, bits_left) else space_avail; | 15 | // bit_off n_front_bits |
17 | bits_left -= write_bits; | 16 | // 0 5 |
18 | var mask: u8 = (@as(u8, 0x01) << write_bits) - 1; | 17 | // 1 5 |
19 | var want: u8 = (b >> @truncate(u3, bits_left)) & mask; | 18 | // 2 5 |
20 | self.buf |= @truncate(u5, want << (space_avail - write_bits)); | 19 | // 3 5 |
21 | self.out_off += write_bits; | 20 | // 4 4 |
22 | if (self.out_off == 5) { | 21 | // 5 3 |
23 | if (i >= out.len) break; | 22 | // 6 2 |
24 | out[i] = self.char(); | 23 | // 7 1 |
25 | i += 1; | 24 | return if (self.bit_off <= 3) 5 else 7 - self.bit_off + 1; |
26 | self.out_off = 0; | 25 | } |
27 | self.buf = 0; | 26 | |
27 | fn front(self: *const Self, index: usize) u5 { | ||
28 | // bit_off bits shl shr front | ||
29 | // 0 0b11111000 3 0b11111 | ||
30 | // 1 0b01111100 2 0b11111 | ||
31 | // 2 0b00111110 1 0b11111 | ||
32 | // 3 0b00011111 0 0 0b11111 | ||
33 | // 4 0b00001111 1 0b11110 | ||
34 | // 5 0b00000111 2 0b11100 | ||
35 | // 6 0b00000011 3 0b11000 | ||
36 | // 7 0b00000001 4 0b10000 | ||
37 | const bits = self.buffer[index] & self.bitmask(); | ||
38 | if (self.bit_off >= 4) return @truncate(u5, bits << (self.bit_off - 3)); | ||
39 | return @truncate(u5, bits >> (3 - self.bit_off)); | ||
40 | } | ||
41 | |||
42 | fn back(self: *const Self, index: usize, bits: u3) u5 { | ||
43 | if (bits == 0) return 0; | ||
44 | return @truncate(u5, self.buffer[index] >> (7 - bits + 1)); | ||
45 | } | ||
46 | |||
47 | fn next_u5(self: *Self) ?u5 { | ||
48 | const front_index = self.index orelse return null; | ||
49 | const num_front_bits = self.n_front_bits(); | ||
50 | const front_bits = self.front(front_index); | ||
51 | |||
52 | var back_bits: u5 = 0; | ||
53 | if (self.bit_off >= 3) { | ||
54 | self.bit_off -= 3; | ||
55 | const new_index = front_index + 1; | ||
56 | if (self.buffer.len > new_index) { | ||
57 | self.index = new_index; | ||
58 | back_bits = self.back(new_index, 5 - num_front_bits); | ||
59 | } else { | ||
60 | self.index = null; | ||
28 | } | 61 | } |
62 | } else { | ||
63 | self.bit_off += 5; | ||
29 | } | 64 | } |
30 | return i; | 65 | |
66 | return front_bits | back_bits; | ||
67 | } | ||
68 | |||
69 | fn char(unencoded: u5) u8 { | ||
70 | return unencoded + (if (unencoded < 26) @as(u8, 'A') else '2' - 26); | ||
31 | } | 71 | } |
32 | 72 | ||
33 | fn char(self: *const Self) u8 { | 73 | pub fn next(self: *Self) ?u8 { |
34 | return self.buf + (if (self.buf < 26) @as(u8, 'A') else '2' - 26); | 74 | const unencoded = self.next_u5() orelse return null; |
75 | return char(unencoded); | ||
35 | } | 76 | } |
36 | }; | 77 | }; |
37 | 78 | ||
79 | // TODO(rutgerbrf): simplify the code of the decoder | ||
80 | |||
38 | pub const DecodeError = error{CorruptInputError}; | 81 | pub const DecodeError = error{CorruptInputError}; |
39 | 82 | ||
40 | pub const Decoder = struct { | 83 | pub const Decoder = struct { |
@@ -89,17 +132,11 @@ pub fn decodedLen(enc_len: usize) usize { | |||
89 | } | 132 | } |
90 | 133 | ||
91 | pub fn encode(bs: []const u8, out: []u8) usize { | 134 | pub fn encode(bs: []const u8, out: []u8) usize { |
92 | var e = Encoder{}; | 135 | var e = Encoder{ .buffer = bs, .index = 0, .bit_off = 0 }; |
93 | var i: usize = 0; | 136 | for (out) |*b, i| { |
94 | for (bs) |b| { | 137 | b.* = e.next() orelse return i; |
95 | if (i >= out.len) break; | ||
96 | i += e.write(b, out[i..]); | ||
97 | } | 138 | } |
98 | if (e.out_off != 0 and i < out.len) { | 139 | return out.len; |
99 | out[i] = e.char(); | ||
100 | i += 1; | ||
101 | } | ||
102 | return i; // amount of bytes processed | ||
103 | } | 140 | } |
104 | 141 | ||
105 | pub fn decode(ps: []const u8, out: []u8) DecodeError!usize { | 142 | pub fn decode(ps: []const u8, out: []u8) DecodeError!usize { |