aboutsummaryrefslogtreecommitdiffstats
path: root/src/base32.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/base32.zig')
-rw-r--r--src/base32.zig110
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;