diff options
Diffstat (limited to 'src')
| -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; |