diff options
author | Rutger Broekhoff | 2021-05-22 17:11:33 +0200 |
---|---|---|
committer | Rutger Broekhoff | 2021-05-22 17:11:33 +0200 |
commit | dd5e276c483852afd9b294b6d3559af46ef819a0 (patch) | |
tree | b1cb76aa47c03b305587d9751417047783af2479 | |
parent | d52705d2587a7cc96e535b9d9730ea8cab5594c8 (diff) | |
download | zig-nkeys-dd5e276c483852afd9b294b6d3559af46ef819a0.tar.gz zig-nkeys-dd5e276c483852afd9b294b6d3559af46ef819a0.zip |
Document the Base32 decoder
-rw-r--r-- | src/base32.zig | 110 |
1 files changed, 67 insertions, 43 deletions
diff --git a/src/base32.zig b/src/base32.zig index 559427b..c92545b 100644 --- a/src/base32.zig +++ b/src/base32.zig | |||
@@ -8,6 +8,7 @@ pub const Encoder = struct { | |||
8 | index: ?usize, | 8 | index: ?usize, |
9 | bit_off: u3, | 9 | bit_off: u3, |
10 | 10 | ||
11 | /// Init the encoder. | ||
11 | pub fn init(buffer: []const u8) Encoder { | 12 | pub fn init(buffer: []const u8) Encoder { |
12 | return .{ | 13 | return .{ |
13 | .buffer = buffer, | 14 | .buffer = buffer, |
@@ -16,11 +17,14 @@ pub const Encoder = struct { | |||
16 | }; | 17 | }; |
17 | } | 18 | } |
18 | 19 | ||
20 | /// Calculate the Base32-encoded size of an array of bytes. | ||
19 | pub fn calcSize(source_len: usize) usize { | 21 | pub fn calcSize(source_len: usize) usize { |
20 | const source_len_bits = source_len * 8; | 22 | const source_len_bits = source_len * 8; |
21 | return source_len_bits / 5 + (if (source_len_bits % 5 > 0) @as(usize, 1) else 0); | 23 | return source_len_bits / 5 + (if (source_len_bits % 5 > 0) @as(usize, 1) else 0); |
22 | } | 24 | } |
23 | 25 | ||
26 | /// Encode some data as Base32. | ||
27 | /// Note that `dest.len` must at least be as big as `Encoder.calcSize(source.len)`. | ||
24 | pub fn encode(dest: []u8, source: []const u8) []const u8 { | 28 | pub fn encode(dest: []u8, source: []const u8) []const u8 { |
25 | const out_len = calcSize(source.len); | 29 | const out_len = calcSize(source.len); |
26 | std.debug.assert(dest.len >= out_len); | 30 | std.debug.assert(dest.len >= out_len); |
@@ -30,8 +34,8 @@ pub const Encoder = struct { | |||
30 | return dest[0..out_len]; | 34 | return dest[0..out_len]; |
31 | } | 35 | } |
32 | 36 | ||
33 | // Calculates the amount of bits can be read from `self.buffer[self.index]`, | 37 | /// Calculate the amount of bits can be read from `self.buffer[self.index]`, |
34 | // with a maximum of 5 and an offset of `self.bit_off`. | 38 | /// with a maximum of 5 and an offset of `self.bit_off`. |
35 | fn frontBitsLen(self: *const Self) u3 { | 39 | fn frontBitsLen(self: *const Self) u3 { |
36 | // bit_off frontBitsLen | 40 | // bit_off frontBitsLen |
37 | // 0 5 | 41 | // 0 5 |
@@ -45,20 +49,20 @@ pub const Encoder = struct { | |||
45 | return if (self.bit_off <= 3) 5 else 7 - self.bit_off + 1; | 49 | return if (self.bit_off <= 3) 5 else 7 - self.bit_off + 1; |
46 | } | 50 | } |
47 | 51 | ||
48 | // Returns the bits of `self.buffer[self.index]`, read with an offset of `self.bit_off`, | 52 | /// Get the bits of `self.buffer[self.index]`, read with an offset of `self.bit_off`, |
49 | // aligned to the left of the 5-bit unsigned integer. | 53 | /// aligned to the left of the 5-bit unsigned integer. |
50 | // Returns null if `self.index` is null. | 54 | /// Returns null if `self.index` is null. |
51 | // An illustration of its behaviour, with `self.buffer[self.index]` being 0b10010111: | 55 | /// An illustration of its behaviour, with `self.buffer[self.index]` being 0b10010111: |
52 | // | `self.bit_off` | `frontBits` | | 56 | /// | `self.bit_off` | `frontBits` | |
53 | // |----------------|-------------| | 57 | /// |----------------|-------------| |
54 | // | 0 | 0b10010 | | 58 | /// | 0 | 0b10010 | |
55 | // | 1 | 0b00101 | | 59 | /// | 1 | 0b00101 | |
56 | // | 2 | 0b01011 | | 60 | /// | 2 | 0b01011 | |
57 | // | 3 | 0b10111 | | 61 | /// | 3 | 0b10111 | |
58 | // | 4 | 0b01110 | | 62 | /// | 4 | 0b01110 | |
59 | // | 5 | 0b11100 | | 63 | /// | 5 | 0b11100 | |
60 | // | 6 | 0b11000 | | 64 | /// | 6 | 0b11000 | |
61 | // | 7 | 0b10000 | | 65 | /// | 7 | 0b10000 | |
62 | fn frontBits(self: *const Self) ?u5 { | 66 | fn frontBits(self: *const Self) ?u5 { |
63 | // bit_off bitmask shl shr frontBits | 67 | // bit_off bitmask shl shr frontBits |
64 | // 0 0b11111000 3 0b11111 | 68 | // 0 0b11111000 3 0b11111 |
@@ -76,25 +80,25 @@ pub const Encoder = struct { | |||
76 | return @truncate(u5, bits >> (3 - self.bit_off)); | 80 | return @truncate(u5, bits >> (3 - self.bit_off)); |
77 | } | 81 | } |
78 | 82 | ||
79 | // Returns the `self.buffer[self.index]` with the maximum amount specified by the `bits` parameter, | 83 | /// Get the bits of `self.buffer[self.index]` with the maximum amount specified by the `bits` parameter, |
80 | // aligned to the right of the 5-bit unsigned integer. | 84 | /// aligned to the right of the 5-bit unsigned integer. |
81 | // Because a 5-bit integer is returned, not more than 5 bits can be read. `bits` must not be greater than 5. | 85 | /// Because a 5-bit integer is returned, not more than 5 bits can be read. `bits` must not be greater than 5. |
82 | // An illustration of its behaviour, with `self.buffer[self.index]` being 0b11101001: | 86 | /// An illustration of its behaviour, with `self.buffer[self.index]` being 0b11101001: |
83 | // | `bits` | `backBits` | | 87 | /// | `bits` | `backBits` | |
84 | // |--------|------------| | 88 | /// |--------|------------| |
85 | // | 0 | 0b00000 | | 89 | /// | 0 | 0b00000 | |
86 | // | 1 | 0b10000 | | 90 | /// | 1 | 0b10000 | |
87 | // | 2 | 0b11000 | | 91 | /// | 2 | 0b11000 | |
88 | // | 3 | 0b11100 | | 92 | /// | 3 | 0b11100 | |
89 | // | 4 | 0b11100 | | 93 | /// | 4 | 0b11100 | |
90 | // | 5 | 0b11101 | | 94 | /// | 5 | 0b11101 | |
91 | fn backBits(self: *const Self, bits: u3) u5 { | 95 | fn backBits(self: *const Self, bits: u3) u5 { |
92 | std.debug.assert(bits <= 5); | 96 | std.debug.assert(bits <= 5); |
93 | if (bits == 0 or self.index == null) return 0; | 97 | if (bits == 0 or self.index == null) return 0; |
94 | return @truncate(u5, self.buffer[self.index.?] >> (7 - bits + 1)); | 98 | return @truncate(u5, self.buffer[self.index.?] >> (7 - bits + 1)); |
95 | } | 99 | } |
96 | 100 | ||
97 | // Returns the next 5-bit integer, read from `self.buffer`. | 101 | /// Get the next 5-bit integer, read from `self.buffer`. |
98 | fn nextU5(self: *Self) ?u5 { | 102 | fn nextU5(self: *Self) ?u5 { |
99 | // `self.buffer` is read 5 bits at a time by `nextU5`. | 103 | // `self.buffer` is read 5 bits at a time by `nextU5`. |
100 | // Because of the elements of `self.buffer` being 8 bits each, we need to | 104 | // Because of the elements of `self.buffer` being 8 bits each, we need to |
@@ -126,12 +130,12 @@ pub const Encoder = struct { | |||
126 | return front_bits | back_bits; | 130 | return front_bits | back_bits; |
127 | } | 131 | } |
128 | 132 | ||
129 | // Returns the corresponding ASCII character for 5 bits of the input. | 133 | /// Get the corresponding ASCII character for 5 bits of the input. |
130 | fn char(unencoded: u5) u8 { | 134 | fn char(unencoded: u5) u8 { |
131 | return unencoded + (if (unencoded < 26) @as(u8, 'A') else '2' - 26); | 135 | return unencoded + (if (unencoded < 26) @as(u8, 'A') else '2' - 26); |
132 | } | 136 | } |
133 | 137 | ||
134 | // Returns the next byte of the encoded buffer. | 138 | /// Get the next byte of the encoded buffer. |
135 | pub fn next(self: *Self) ?u8 { | 139 | pub fn next(self: *Self) ?u8 { |
136 | const unencoded = self.nextU5() orelse return null; | 140 | const unencoded = self.nextU5() orelse return null; |
137 | return char(unencoded); | 141 | return char(unencoded); |
@@ -149,6 +153,7 @@ pub const Decoder = struct { | |||
149 | buf: u8, | 153 | buf: u8, |
150 | buf_len: u4, | 154 | buf_len: u4, |
151 | 155 | ||
156 | /// Init the decoder. | ||
152 | pub fn init(buffer: []const u8) Self { | 157 | pub fn init(buffer: []const u8) Self { |
153 | return .{ | 158 | return .{ |
154 | .buffer = buffer, | 159 | .buffer = buffer, |
@@ -158,11 +163,14 @@ pub const Decoder = struct { | |||
158 | }; | 163 | }; |
159 | } | 164 | } |
160 | 165 | ||
166 | /// Calculate the size of a Base32-encoded array of bytes. | ||
161 | pub fn calcSize(source_len: usize) usize { | 167 | pub fn calcSize(source_len: usize) usize { |
162 | const source_len_bits = source_len * 5; | 168 | const source_len_bits = source_len * 5; |
163 | return source_len_bits / 8; | 169 | return source_len_bits / 8; |
164 | } | 170 | } |
165 | 171 | ||
172 | /// Decode a slice of Base32-encoded data. | ||
173 | /// Note that `dest.len` must at least be as big as `Decoder.calcSize(source.len)`. | ||
166 | pub fn decode(dest: []u8, source: []const u8) DecodeError![]const u8 { | 174 | pub fn decode(dest: []u8, source: []const u8) DecodeError![]const u8 { |
167 | const out_len = calcSize(source.len); | 175 | const out_len = calcSize(source.len); |
168 | std.debug.assert(dest.len >= out_len); | 176 | std.debug.assert(dest.len >= out_len); |
@@ -172,44 +180,60 @@ pub const Decoder = struct { | |||
172 | return dest[0..out_len]; | 180 | return dest[0..out_len]; |
173 | } | 181 | } |
174 | 182 | ||
175 | fn decodeChar(p: u8) DecodeError!u5 { | 183 | /// Get a character from the buffer. |
184 | fn decodeChar(c: u8) DecodeError!u5 { | ||
176 | var value: u5 = 0; | 185 | var value: u5 = 0; |
177 | if (p >= 'A' and p <= 'Z') { | 186 | if (c >= 'A' and c <= 'Z') { |
178 | value = @truncate(u5, p - @as(u8, 'A')); | 187 | value = @truncate(u5, c - @as(u8, 'A')); |
179 | } else if (p >= '2' and p <= '9') { | 188 | } else if (c >= '2' and c <= '9') { |
180 | // '2' -> 26 | 189 | // '2' -> 26 |
181 | value = @truncate(u5, p - @as(u8, '2') + 26); | 190 | value = @truncate(u5, c - @as(u8, '2') + 26); |
182 | } else { | 191 | } else { |
183 | return error.CorruptInputError; | 192 | return error.CorruptInputError; |
184 | } | 193 | } |
185 | return value; | 194 | return value; |
186 | } | 195 | } |
187 | 196 | ||
197 | /// Get the next 5-bit decoded character, read from `self.buffer`. | ||
188 | fn nextU5(self: *Self) DecodeError!?u5 { | 198 | fn nextU5(self: *Self) DecodeError!?u5 { |
189 | const index = self.index orelse return null; | 199 | const index = self.index orelse return null; |
190 | self.index = if (index + 1 < self.buffer.len) index + 1 else null; | 200 | self.index = if (index + 1 < self.buffer.len) index + 1 else null; |
191 | return try decodeChar(self.buffer[index]); | 201 | return try decodeChar(self.buffer[index]); |
192 | } | 202 | } |
193 | 203 | ||
204 | /// Get the next byte of the decoded buffer. | ||
194 | pub fn next(self: *Self) DecodeError!?u8 { | 205 | pub fn next(self: *Self) DecodeError!?u8 { |
195 | var read_any = false; | ||
196 | while (true) { | 206 | while (true) { |
207 | // Read a character and decode it. | ||
197 | const c = (try self.nextU5()) orelse break; | 208 | const c = (try self.nextU5()) orelse break; |
209 | // Check how many bits we can write to the buffer. | ||
198 | const buf_remaining_len = 8 - self.buf_len; | 210 | const buf_remaining_len = 8 - self.buf_len; |
199 | const write_len = if (buf_remaining_len > 5) 5 else buf_remaining_len; | 211 | // Calculate how many bytes we will write to the buffer (the decoded character represents 5 bits). |
200 | const c_remaining_len = 5 - write_len; | 212 | const buf_write_len = if (buf_remaining_len > 5) 5 else buf_remaining_len; |
213 | // Calculate how many bits of the decoded remain when we've written part of it to the buffer. | ||
214 | const c_remaining_len = 5 - buf_write_len; | ||
215 | // Write (the first) part of the decoded character to the buffer. | ||
201 | self.buf |= (@as(u8, c) << 3) >> @truncate(u3, self.buf_len); | 216 | self.buf |= (@as(u8, c) << 3) >> @truncate(u3, self.buf_len); |
202 | self.buf_len += write_len; | 217 | self.buf_len += buf_write_len; |
203 | read_any = true; | ||
204 | if (self.buf_len == 8) { | 218 | if (self.buf_len == 8) { |
219 | // The buffer is full, we can return a byte. | ||
205 | const ret = self.buf; | 220 | const ret = self.buf; |
206 | self.buf_len = c_remaining_len; | 221 | self.buf_len = c_remaining_len; |
207 | self.buf = 0; | 222 | self.buf = 0; |
208 | if (write_len != 5) self.buf = @as(u8, c) << @truncate(u3, write_len + 3); | 223 | if (buf_write_len != 5) { |
224 | // We didn't write the entire decoded character to the buffer. | ||
225 | // Write the remaining part to the beginning of the buffer. | ||
226 | self.buf = @as(u8, c) << @truncate(u3, buf_write_len + 3); | ||
227 | } | ||
209 | return ret; | 228 | return ret; |
210 | } | 229 | } |
211 | } | 230 | } |
212 | if ((self.buf_len == 0 or self.buf == 0) and !read_any) return null; | 231 | |
232 | // We aren't able to read any characters anymore. | ||
233 | // If the buffer doesn't contain any (actual) data we can stop decoding. | ||
234 | // Otherwise, we can return what remains in the buffer, and stop decoding | ||
235 | // after having done that. | ||
236 | if (self.buf == 0 and self.buf_len < 5) return null; | ||
213 | 237 | ||
214 | const ret = self.buf; | 238 | const ret = self.buf; |
215 | self.buf_len = 0; | 239 | self.buf_len = 0; |