authorgravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2017-11-20 21:36:18-07:00
committergravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2017-11-20 23:26:45-07:00
logafbbdb2c67127985cadae7244348665ece8b2f25
treeb1bcbbc82d1214171ec84978de9a6e4df8e4da2c
parenta44283b0b2e585d7e15d7c8e6574411b75c12a0a

move base64 functions into structs


4 files changed, 307 insertions(+), 282 deletions(-)

doc/langref.html.in+3-2
......@@ -5414,8 +5414,9 @@ export fn decode_base_64(dest_ptr: &amp;u8, dest_len: usize,
54145414{
54155415 const src = source_ptr[0..source_len];
54165416 const dest = dest_ptr[0..dest_len];
5417 const decoded_size = base64.calcDecodedSizeExactUnsafe(src, base64.standard_pad_char);
5418 base64.decodeExactUnsafe(dest[0..decoded_size], src, base64.standard_alphabet_unsafe);
5417 const base64_decoder = base64.standard_decoder_unsafe;
5418 const decoded_size = base64_decoder.calcSize(src);
5419 base64_decoder.decode(dest[0..decoded_size], src);
54195420 return decoded_size;
54205421}
54215422</code></pre>
example/mix_o_files/base64.zig+3-2
......@@ -3,7 +3,8 @@ const base64 = @import("std").base64;
33export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8, source_len: usize) -> usize {
44 const src = source_ptr[0..source_len];
55 const dest = dest_ptr[0..dest_len];
6 const decoded_size = base64.calcDecodedSizeExactUnsafe(src, base64.standard_pad_char);
7 base64.decodeExactUnsafe(dest[0..decoded_size], src, base64.standard_alphabet_unsafe);
6 const base64_decoder = base64.standard_decoder_unsafe;
7 const decoded_size = base64_decoder.calcSize(src);
8 base64_decoder.decode(dest[0..decoded_size], src);
89 return decoded_size;
910}
std/base64.zig+294-273
......@@ -3,64 +3,85 @@ const mem = @import("mem.zig");
33
44pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
55pub const standard_pad_char = '=';
6pub const standard_encoder = Base64Encoder.init(standard_alphabet_chars, standard_pad_char);
67
7/// ceil(source_len * 4/3)
8pub fn calcEncodedSize(source_len: usize) -> usize {
9 return @divTrunc(source_len + 2, 3) * 4;
10}
11
12/// dest.len must be what you get from ::calcEncodedSize.
13/// It is assumed that alphabet_chars and pad_char are all unique characters.
14pub fn encode(dest: []u8, source: []const u8, alphabet_chars: []const u8, pad_char: u8) {
15 assert(alphabet_chars.len == 64);
16 assert(dest.len == calcEncodedSize(source.len));
17
18 var i: usize = 0;
19 var out_index: usize = 0;
20 while (i + 2 < source.len) : (i += 3) {
21 dest[out_index] = alphabet_chars[(source[i] >> 2) & 0x3f];
22 out_index += 1;
8pub const Base64Encoder = struct {
9 alphabet_chars: []const u8,
10 pad_char: u8,
2311
24 dest[out_index] = alphabet_chars[((source[i] & 0x3) << 4) |
25 ((source[i + 1] & 0xf0) >> 4)];
26 out_index += 1;
12 /// a bunch of assertions, then simply pass the data right through.
13 pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64Encoder {
14 assert(alphabet_chars.len == 64);
15 var char_in_alphabet = []bool{false} ** 256;
16 for (alphabet_chars) |c| {
17 assert(!char_in_alphabet[c]);
18 assert(c != pad_char);
19 char_in_alphabet[c] = true;
20 }
2721
28 dest[out_index] = alphabet_chars[((source[i + 1] & 0xf) << 2) |
29 ((source[i + 2] & 0xc0) >> 6)];
30 out_index += 1;
22 return Base64Encoder{
23 .alphabet_chars = alphabet_chars,
24 .pad_char = pad_char,
25 };
26 }
3127
32 dest[out_index] = alphabet_chars[source[i + 2] & 0x3f];
33 out_index += 1;
28 /// ceil(source_len * 4/3)
29 pub fn calcSize(source_len: usize) -> usize {
30 return @divTrunc(source_len + 2, 3) * 4;
3431 }
3532
36 if (i < source.len) {
37 dest[out_index] = alphabet_chars[(source[i] >> 2) & 0x3f];
38 out_index += 1;
33 /// dest.len must be what you get from ::calcSize.
34 pub fn encode(encoder: &const Base64Encoder, dest: []u8, source: []const u8) {
35 assert(dest.len == Base64Encoder.calcSize(source.len));
3936
40 if (i + 1 == source.len) {
41 dest[out_index] = alphabet_chars[(source[i] & 0x3) << 4];
37 var i: usize = 0;
38 var out_index: usize = 0;
39 while (i + 2 < source.len) : (i += 3) {
40 dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];
4241 out_index += 1;
4342
44 dest[out_index] = pad_char;
45 out_index += 1;
46 } else {
47 dest[out_index] = alphabet_chars[((source[i] & 0x3) << 4) |
43 dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) |
4844 ((source[i + 1] & 0xf0) >> 4)];
4945 out_index += 1;
5046
51 dest[out_index] = alphabet_chars[(source[i + 1] & 0xf) << 2];
47 dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) |
48 ((source[i + 2] & 0xc0) >> 6)];
49 out_index += 1;
50
51 dest[out_index] = encoder.alphabet_chars[source[i + 2] & 0x3f];
5252 out_index += 1;
5353 }
5454
55 dest[out_index] = pad_char;
56 out_index += 1;
55 if (i < source.len) {
56 dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];
57 out_index += 1;
58
59 if (i + 1 == source.len) {
60 dest[out_index] = encoder.alphabet_chars[(source[i] & 0x3) << 4];
61 out_index += 1;
62
63 dest[out_index] = encoder.pad_char;
64 out_index += 1;
65 } else {
66 dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) |
67 ((source[i + 1] & 0xf0) >> 4)];
68 out_index += 1;
69
70 dest[out_index] = encoder.alphabet_chars[(source[i + 1] & 0xf) << 2];
71 out_index += 1;
72 }
73
74 dest[out_index] = encoder.pad_char;
75 out_index += 1;
76 }
5777 }
58}
78};
5979
60pub const standard_alphabet = Base64Alphabet.init(standard_alphabet_chars, standard_pad_char);
80pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char);
81error InvalidPadding;
82error InvalidCharacter;
6183
62/// For use with ::decodeExact.
63pub const Base64Alphabet = struct {
84pub const Base64Decoder = struct {
6485 /// e.g. 'A' => 0.
6586 /// undefined for any value not in the 64 alphabet chars.
6687 char_to_index: [256]u8,
......@@ -68,10 +89,10 @@ pub const Base64Alphabet = struct {
6889 char_in_alphabet: [256]bool,
6990 pad_char: u8,
7091
71 pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64Alphabet {
92 pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64Decoder {
7293 assert(alphabet_chars.len == 64);
7394
74 var result = Base64Alphabet{
95 var result = Base64Decoder{
7596 .char_to_index = undefined,
7697 .char_in_alphabet = []bool{false} ** 256,
7798 .pad_char = pad_char,
......@@ -87,197 +108,193 @@ pub const Base64Alphabet = struct {
87108
88109 return result;
89110 }
90};
91111
92error InvalidPadding;
93/// For use with ::decodeExact.
94/// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.
95pub fn calcDecodedSizeExact(encoded: []const u8, pad_char: u8) -> %usize {
96 if (encoded.len % 4 != 0) return error.InvalidPadding;
97 return calcDecodedSizeExactUnsafe(encoded, pad_char);
98}
112 /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.
113 pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) -> %usize {
114 if (source.len % 4 != 0) return error.InvalidPadding;
115 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
116 }
99117
100error InvalidCharacter;
101/// dest.len must be what you get from ::calcDecodedSizeExact.
102/// invalid characters result in error.InvalidCharacter.
103/// invalid padding results in error.InvalidPadding.
104pub fn decodeExact(dest: []u8, source: []const u8, alphabet: &const Base64Alphabet) -> %void {
105 assert(dest.len == %%calcDecodedSizeExact(source, alphabet.pad_char));
106 assert(source.len % 4 == 0);
107
108 var src_cursor: usize = 0;
109 var dest_cursor: usize = 0;
110
111 while (src_cursor < source.len) : (src_cursor += 4) {
112 if (!alphabet.char_in_alphabet[source[src_cursor + 0]]) return error.InvalidCharacter;
113 if (!alphabet.char_in_alphabet[source[src_cursor + 1]]) return error.InvalidCharacter;
114 if (src_cursor < source.len - 4 or source[src_cursor + 3] != alphabet.pad_char) {
115 // common case
116 if (!alphabet.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
117 if (!alphabet.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter;
118 dest[dest_cursor + 0] = alphabet.char_to_index[source[src_cursor + 0]] << 2 |
119 alphabet.char_to_index[source[src_cursor + 1]] >> 4;
120 dest[dest_cursor + 1] = alphabet.char_to_index[source[src_cursor + 1]] << 4 |
121 alphabet.char_to_index[source[src_cursor + 2]] >> 2;
122 dest[dest_cursor + 2] = alphabet.char_to_index[source[src_cursor + 2]] << 6 |
123 alphabet.char_to_index[source[src_cursor + 3]];
124 dest_cursor += 3;
125 } else if (source[src_cursor + 2] != alphabet.pad_char) {
126 // one pad char
127 if (!alphabet.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
128 dest[dest_cursor + 0] = alphabet.char_to_index[source[src_cursor + 0]] << 2 |
129 alphabet.char_to_index[source[src_cursor + 1]] >> 4;
130 dest[dest_cursor + 1] = alphabet.char_to_index[source[src_cursor + 1]] << 4 |
131 alphabet.char_to_index[source[src_cursor + 2]] >> 2;
132 if (alphabet.char_to_index[source[src_cursor + 2]] << 6 != 0) return error.InvalidPadding;
133 dest_cursor += 2;
134 } else {
135 // two pad chars
136 dest[dest_cursor + 0] = alphabet.char_to_index[source[src_cursor + 0]] << 2 |
137 alphabet.char_to_index[source[src_cursor + 1]] >> 4;
138 if (alphabet.char_to_index[source[src_cursor + 1]] << 4 != 0) return error.InvalidPadding;
139 dest_cursor += 1;
118 /// dest.len must be what you get from ::calcSize.
119 /// invalid characters result in error.InvalidCharacter.
120 /// invalid padding results in error.InvalidPadding.
121 pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) -> %void {
122 assert(dest.len == %%decoder.calcSize(source));
123 assert(source.len % 4 == 0);
124
125 var src_cursor: usize = 0;
126 var dest_cursor: usize = 0;
127
128 while (src_cursor < source.len) : (src_cursor += 4) {
129 if (!decoder.char_in_alphabet[source[src_cursor + 0]]) return error.InvalidCharacter;
130 if (!decoder.char_in_alphabet[source[src_cursor + 1]]) return error.InvalidCharacter;
131 if (src_cursor < source.len - 4 or source[src_cursor + 3] != decoder.pad_char) {
132 // common case
133 if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
134 if (!decoder.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter;
135 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
136 decoder.char_to_index[source[src_cursor + 1]] >> 4;
137 dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 |
138 decoder.char_to_index[source[src_cursor + 2]] >> 2;
139 dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 |
140 decoder.char_to_index[source[src_cursor + 3]];
141 dest_cursor += 3;
142 } else if (source[src_cursor + 2] != decoder.pad_char) {
143 // one pad char
144 if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
145 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
146 decoder.char_to_index[source[src_cursor + 1]] >> 4;
147 dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 |
148 decoder.char_to_index[source[src_cursor + 2]] >> 2;
149 if (decoder.char_to_index[source[src_cursor + 2]] << 6 != 0) return error.InvalidPadding;
150 dest_cursor += 2;
151 } else {
152 // two pad chars
153 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
154 decoder.char_to_index[source[src_cursor + 1]] >> 4;
155 if (decoder.char_to_index[source[src_cursor + 1]] << 4 != 0) return error.InvalidPadding;
156 dest_cursor += 1;
157 }
140158 }
159
160 assert(src_cursor == source.len);
161 assert(dest_cursor == dest.len);
141162 }
163};
142164
143 assert(src_cursor == source.len);
144 assert(dest_cursor == dest.len);
145}
165error OutputTooSmall;
146166
147/// For use with ::decodeWithIgnore.
148pub const Base64AlphabetWithIgnore = struct {
149 alphabet: Base64Alphabet,
167pub const Base64DecoderWithIgnore = struct {
168 decoder: Base64Decoder,
150169 char_is_ignored: [256]bool,
151 pub fn init(alphabet_chars: []const u8, pad_char: u8, ignore_chars: []const u8) -> Base64AlphabetWithIgnore {
152 var result = Base64AlphabetWithIgnore {
153 .alphabet = Base64Alphabet.init(alphabet_chars, pad_char),
170 pub fn init(alphabet_chars: []const u8, pad_char: u8, ignore_chars: []const u8) -> Base64DecoderWithIgnore {
171 var result = Base64DecoderWithIgnore {
172 .decoder = Base64Decoder.init(alphabet_chars, pad_char),
154173 .char_is_ignored = []bool{false} ** 256,
155174 };
156175
157176 for (ignore_chars) |c| {
158 assert(!result.alphabet.char_in_alphabet[c]);
177 assert(!result.decoder.char_in_alphabet[c]);
159178 assert(!result.char_is_ignored[c]);
160 assert(result.alphabet.pad_char != c);
179 assert(result.decoder.pad_char != c);
161180 result.char_is_ignored[c] = true;
162181 }
163182
164183 return result;
165184 }
166};
167185
168/// For use with ::decodeWithIgnore.
169/// If no characters end up being ignored, this will be the exact decoded size.
170pub fn calcDecodedSizeUpperBound(encoded_len: usize) -> %usize {
171 return @divTrunc(encoded_len, 4) * 3;
172}
186 /// If no characters end up being ignored or padding, this will be the exact decoded size.
187 pub fn calcSizeUpperBound(encoded_len: usize) -> %usize {
188 return @divTrunc(encoded_len, 4) * 3;
189 }
173190
174error OutputTooSmall;
175/// Invalid characters that are not ignored results in error.InvalidCharacter.
176/// Invalid padding results in error.InvalidPadding.
177/// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcDecodedSizeUpperBound.
178/// Returns the number of bytes writen to dest.
179pub fn decodeWithIgnore(dest: []u8, source: []const u8, alphabet_with_ignore: &const Base64AlphabetWithIgnore) -> %usize {
180 const alphabet = &const alphabet_with_ignore.alphabet;
181
182 var src_cursor: usize = 0;
183 var dest_cursor: usize = 0;
184
185 while (true) {
186 // get the next 4 chars, if available
187 var next_4_chars: [4]u8 = undefined;
188 var available_chars: usize = 0;
189 var pad_char_count: usize = 0;
190 while (available_chars < 4 and src_cursor < source.len) {
191 var c = source[src_cursor];
192 src_cursor += 1;
193
194 if (alphabet.char_in_alphabet[c]) {
195 // normal char
196 next_4_chars[available_chars] = c;
197 available_chars += 1;
198 } else if (alphabet_with_ignore.char_is_ignored[c]) {
199 // we're told to skip this one
200 continue;
201 } else if (c == alphabet.pad_char) {
202 // the padding has begun. count the pad chars.
203 pad_char_count += 1;
204 while (src_cursor < source.len) {
205 c = source[src_cursor];
206 src_cursor += 1;
207 if (c == alphabet.pad_char) {
208 pad_char_count += 1;
209 if (pad_char_count > 2) return error.InvalidCharacter;
210 } else if (alphabet_with_ignore.char_is_ignored[c]) {
211 // we can even ignore chars during the padding
212 continue;
213 } else return error.InvalidCharacter;
214 }
215 break;
216 } else return error.InvalidCharacter;
191 /// Invalid characters that are not ignored result in error.InvalidCharacter.
192 /// Invalid padding results in error.InvalidPadding.
193 /// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.
194 /// Returns the number of bytes writen to dest.
195 pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) -> %usize {
196 const decoder = &const decoder_with_ignore.decoder;
197
198 var src_cursor: usize = 0;
199 var dest_cursor: usize = 0;
200
201 while (true) {
202 // get the next 4 chars, if available
203 var next_4_chars: [4]u8 = undefined;
204 var available_chars: usize = 0;
205 var pad_char_count: usize = 0;
206 while (available_chars < 4 and src_cursor < source.len) {
207 var c = source[src_cursor];
208 src_cursor += 1;
209
210 if (decoder.char_in_alphabet[c]) {
211 // normal char
212 next_4_chars[available_chars] = c;
213 available_chars += 1;
214 } else if (decoder_with_ignore.char_is_ignored[c]) {
215 // we're told to skip this one
216 continue;
217 } else if (c == decoder.pad_char) {
218 // the padding has begun. count the pad chars.
219 pad_char_count += 1;
220 while (src_cursor < source.len) {
221 c = source[src_cursor];
222 src_cursor += 1;
223 if (c == decoder.pad_char) {
224 pad_char_count += 1;
225 if (pad_char_count > 2) return error.InvalidCharacter;
226 } else if (decoder_with_ignore.char_is_ignored[c]) {
227 // we can even ignore chars during the padding
228 continue;
229 } else return error.InvalidCharacter;
230 }
231 break;
232 } else return error.InvalidCharacter;
233 }
234
235 switch (available_chars) {
236 4 => {
237 // common case
238 if (dest_cursor + 3 > dest.len) return error.OutputTooSmall;
239 assert(pad_char_count == 0);
240 dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
241 decoder.char_to_index[next_4_chars[1]] >> 4;
242 dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 |
243 decoder.char_to_index[next_4_chars[2]] >> 2;
244 dest[dest_cursor + 2] = decoder.char_to_index[next_4_chars[2]] << 6 |
245 decoder.char_to_index[next_4_chars[3]];
246 dest_cursor += 3;
247 continue;
248 },
249 3 => {
250 if (dest_cursor + 2 > dest.len) return error.OutputTooSmall;
251 if (pad_char_count != 1) return error.InvalidPadding;
252 dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
253 decoder.char_to_index[next_4_chars[1]] >> 4;
254 dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 |
255 decoder.char_to_index[next_4_chars[2]] >> 2;
256 if (decoder.char_to_index[next_4_chars[2]] << 6 != 0) return error.InvalidPadding;
257 dest_cursor += 2;
258 break;
259 },
260 2 => {
261 if (dest_cursor + 1 > dest.len) return error.OutputTooSmall;
262 if (pad_char_count != 2) return error.InvalidPadding;
263 dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
264 decoder.char_to_index[next_4_chars[1]] >> 4;
265 if (decoder.char_to_index[next_4_chars[1]] << 4 != 0) return error.InvalidPadding;
266 dest_cursor += 1;
267 break;
268 },
269 1 => {
270 return error.InvalidPadding;
271 },
272 0 => {
273 if (pad_char_count != 0) return error.InvalidPadding;
274 break;
275 },
276 else => unreachable,
277 }
217278 }
218279
219 switch (available_chars) {
220 4 => {
221 // common case
222 if (dest_cursor + 3 > dest.len) return error.OutputTooSmall;
223 assert(pad_char_count == 0);
224 dest[dest_cursor + 0] = alphabet.char_to_index[next_4_chars[0]] << 2 |
225 alphabet.char_to_index[next_4_chars[1]] >> 4;
226 dest[dest_cursor + 1] = alphabet.char_to_index[next_4_chars[1]] << 4 |
227 alphabet.char_to_index[next_4_chars[2]] >> 2;
228 dest[dest_cursor + 2] = alphabet.char_to_index[next_4_chars[2]] << 6 |
229 alphabet.char_to_index[next_4_chars[3]];
230 dest_cursor += 3;
231 continue;
232 },
233 3 => {
234 if (dest_cursor + 2 > dest.len) return error.OutputTooSmall;
235 if (pad_char_count != 1) return error.InvalidPadding;
236 dest[dest_cursor + 0] = alphabet.char_to_index[next_4_chars[0]] << 2 |
237 alphabet.char_to_index[next_4_chars[1]] >> 4;
238 dest[dest_cursor + 1] = alphabet.char_to_index[next_4_chars[1]] << 4 |
239 alphabet.char_to_index[next_4_chars[2]] >> 2;
240 if (alphabet.char_to_index[next_4_chars[2]] << 6 != 0) return error.InvalidPadding;
241 dest_cursor += 2;
242 break;
243 },
244 2 => {
245 if (dest_cursor + 1 > dest.len) return error.OutputTooSmall;
246 if (pad_char_count != 2) return error.InvalidPadding;
247 dest[dest_cursor + 0] = alphabet.char_to_index[next_4_chars[0]] << 2 |
248 alphabet.char_to_index[next_4_chars[1]] >> 4;
249 if (alphabet.char_to_index[next_4_chars[1]] << 4 != 0) return error.InvalidPadding;
250 dest_cursor += 1;
251 break;
252 },
253 1 => {
254 return error.InvalidPadding;
255 },
256 0 => {
257 if (pad_char_count != 0) return error.InvalidPadding;
258 break;
259 },
260 else => unreachable,
261 }
280 assert(src_cursor == source.len);
281
282 return dest_cursor;
262283 }
284};
263285
264 assert(src_cursor == source.len);
265286
266 return dest_cursor;
267}
287pub const standard_decoder_unsafe = Base64DecoderUnsafe.init(standard_alphabet_chars, standard_pad_char);
268288
269pub const standard_alphabet_unsafe = Base64AlphabetUnsafe.init(standard_alphabet_chars, standard_pad_char);
270
271/// For use with ::decodeExactUnsafe.
272pub const Base64AlphabetUnsafe = struct {
289pub const Base64DecoderUnsafe = struct {
273290 /// e.g. 'A' => 0.
274291 /// undefined for any value not in the 64 alphabet chars.
275292 char_to_index: [256]u8,
276293 pad_char: u8,
277294
278 pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64AlphabetUnsafe {
295 pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64DecoderUnsafe {
279296 assert(alphabet_chars.len == 64);
280 var result = Base64AlphabetUnsafe {
297 var result = Base64DecoderUnsafe {
281298 .char_to_index = undefined,
282299 .pad_char = pad_char,
283300 };
......@@ -287,69 +304,73 @@ pub const Base64AlphabetUnsafe = struct {
287304 }
288305 return result;
289306 }
290};
291307
292/// For use with ::decodeExactUnsafe.
293/// The encoded buffer must be valid.
294pub fn calcDecodedSizeExactUnsafe(encoded: []const u8, pad_char: u8) -> usize {
295 if (encoded.len == 0) return 0;
296 var result = @divExact(encoded.len, 4) * 3;
297 if (encoded[encoded.len - 1] == pad_char) {
298 result -= 1;
299 if (encoded[encoded.len - 2] == pad_char) {
300 result -= 1;
301 }
308 /// The source buffer must be valid.
309 pub fn calcSize(decoder: &const Base64DecoderUnsafe, source: []const u8) -> usize {
310 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
302311 }
303 return result;
304}
305312
306/// dest.len must be what you get from ::calcDecodedSizeExactUnsafe.
307/// invalid characters or padding will result in undefined values.
308pub fn decodeExactUnsafe(dest: []u8, source: []const u8, alphabet: &const Base64AlphabetUnsafe) {
309 assert(dest.len == calcDecodedSizeExactUnsafe(source, alphabet.pad_char));
313 /// dest.len must be what you get from ::calcDecodedSizeExactUnsafe.
314 /// invalid characters or padding will result in undefined values.
315 pub fn decode(decoder: &const Base64DecoderUnsafe, dest: []u8, source: []const u8) {
316 assert(dest.len == decoder.calcSize(source));
310317
311 var src_index: usize = 0;
312 var dest_index: usize = 0;
313 var in_buf_len: usize = source.len;
318 var src_index: usize = 0;
319 var dest_index: usize = 0;
320 var in_buf_len: usize = source.len;
314321
315 while (in_buf_len > 0 and source[in_buf_len - 1] == alphabet.pad_char) {
316 in_buf_len -= 1;
317 }
322 while (in_buf_len > 0 and source[in_buf_len - 1] == decoder.pad_char) {
323 in_buf_len -= 1;
324 }
318325
319 while (in_buf_len > 4) {
320 dest[dest_index] = alphabet.char_to_index[source[src_index + 0]] << 2 |
321 alphabet.char_to_index[source[src_index + 1]] >> 4;
322 dest_index += 1;
326 while (in_buf_len > 4) {
327 dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 |
328 decoder.char_to_index[source[src_index + 1]] >> 4;
329 dest_index += 1;
323330
324 dest[dest_index] = alphabet.char_to_index[source[src_index + 1]] << 4 |
325 alphabet.char_to_index[source[src_index + 2]] >> 2;
326 dest_index += 1;
331 dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 |
332 decoder.char_to_index[source[src_index + 2]] >> 2;
333 dest_index += 1;
327334
328 dest[dest_index] = alphabet.char_to_index[source[src_index + 2]] << 6 |
329 alphabet.char_to_index[source[src_index + 3]];
330 dest_index += 1;
335 dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 |
336 decoder.char_to_index[source[src_index + 3]];
337 dest_index += 1;
331338
332 src_index += 4;
333 in_buf_len -= 4;
334 }
339 src_index += 4;
340 in_buf_len -= 4;
341 }
335342
336 if (in_buf_len > 1) {
337 dest[dest_index] = alphabet.char_to_index[source[src_index + 0]] << 2 |
338 alphabet.char_to_index[source[src_index + 1]] >> 4;
339 dest_index += 1;
340 }
341 if (in_buf_len > 2) {
342 dest[dest_index] = alphabet.char_to_index[source[src_index + 1]] << 4 |
343 alphabet.char_to_index[source[src_index + 2]] >> 2;
344 dest_index += 1;
343 if (in_buf_len > 1) {
344 dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 |
345 decoder.char_to_index[source[src_index + 1]] >> 4;
346 dest_index += 1;
347 }
348 if (in_buf_len > 2) {
349 dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 |
350 decoder.char_to_index[source[src_index + 2]] >> 2;
351 dest_index += 1;
352 }
353 if (in_buf_len > 3) {
354 dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 |
355 decoder.char_to_index[source[src_index + 3]];
356 dest_index += 1;
357 }
345358 }
346 if (in_buf_len > 3) {
347 dest[dest_index] = alphabet.char_to_index[source[src_index + 2]] << 6 |
348 alphabet.char_to_index[source[src_index + 3]];
349 dest_index += 1;
359};
360
361fn calcDecodedSizeExactUnsafe(source: []const u8, pad_char: u8) -> usize {
362 if (source.len == 0) return 0;
363 var result = @divExact(source.len, 4) * 3;
364 if (source[source.len - 1] == pad_char) {
365 result -= 1;
366 if (source[source.len - 2] == pad_char) {
367 result -= 1;
368 }
350369 }
370 return result;
351371}
352372
373
353374test "base64" {
354375 @setEvalBranchQuota(5000);
355376 %%testBase64();
......@@ -391,74 +412,74 @@ fn testBase64() -> %void {
391412}
392413
393414fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) -> %void {
394 // encode
415 // Base64Encoder
395416 {
396417 var buffer: [0x100]u8 = undefined;
397 var encoded = buffer[0..calcEncodedSize(expected_decoded.len)];
398 encode(encoded, expected_decoded, standard_alphabet_chars, standard_pad_char);
418 var encoded = buffer[0..Base64Encoder.calcSize(expected_decoded.len)];
419 standard_encoder.encode(encoded, expected_decoded);
399420 assert(mem.eql(u8, encoded, expected_encoded));
400421 }
401422
402 // decodeExact
423 // Base64Decoder
403424 {
404425 var buffer: [0x100]u8 = undefined;
405 var decoded = buffer[0..%return calcDecodedSizeExact(expected_encoded, standard_pad_char)];
406 %return decodeExact(decoded, expected_encoded, standard_alphabet);
426 var decoded = buffer[0..%return standard_decoder.calcSize(expected_encoded)];
427 %return standard_decoder.decode(decoded, expected_encoded);
407428 assert(mem.eql(u8, decoded, expected_decoded));
408429 }
409430
410 // decodeWithIgnore
431 // Base64DecoderWithIgnore
411432 {
412 const standard_alphabet_ignore_nothing = Base64AlphabetWithIgnore.init(
433 const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(
413434 standard_alphabet_chars, standard_pad_char, "");
414435 var buffer: [0x100]u8 = undefined;
415 var decoded = buffer[0..%return calcDecodedSizeUpperBound(expected_encoded.len)];
416 var written = %return decodeWithIgnore(decoded, expected_encoded, standard_alphabet_ignore_nothing);
436 var decoded = buffer[0..%return Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];
437 var written = %return standard_decoder_ignore_nothing.decode(decoded, expected_encoded);
417438 assert(written <= decoded.len);
418439 assert(mem.eql(u8, decoded[0..written], expected_decoded));
419440 }
420441
421 // decodeExactUnsafe
442 // Base64DecoderUnsafe
422443 {
423444 var buffer: [0x100]u8 = undefined;
424 var decoded = buffer[0..calcDecodedSizeExactUnsafe(expected_encoded, standard_pad_char)];
425 decodeExactUnsafe(decoded, expected_encoded, standard_alphabet_unsafe);
445 var decoded = buffer[0..standard_decoder_unsafe.calcSize(expected_encoded)];
446 standard_decoder_unsafe.decode(decoded, expected_encoded);
426447 assert(mem.eql(u8, decoded, expected_decoded));
427448 }
428449}
429450
430451fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) -> %void {
431 const standard_alphabet_ignore_space = Base64AlphabetWithIgnore.init(
452 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
432453 standard_alphabet_chars, standard_pad_char, " ");
433454 var buffer: [0x100]u8 = undefined;
434 var decoded = buffer[0..%return calcDecodedSizeUpperBound(encoded.len)];
435 var written = %return decodeWithIgnore(decoded, encoded, standard_alphabet_ignore_space);
455 var decoded = buffer[0..%return Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];
456 var written = %return standard_decoder_ignore_space.decode(decoded, encoded);
436457 assert(mem.eql(u8, decoded[0..written], expected_decoded));
437458}
438459
439460error ExpectedError;
440461fn testError(encoded: []const u8, expected_err: error) -> %void {
441 const standard_alphabet_ignore_space = Base64AlphabetWithIgnore.init(
462 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
442463 standard_alphabet_chars, standard_pad_char, " ");
443464 var buffer: [0x100]u8 = undefined;
444 if (calcDecodedSizeExact(encoded, standard_pad_char)) |decoded_size| {
465 if (standard_decoder.calcSize(encoded)) |decoded_size| {
445466 var decoded = buffer[0..decoded_size];
446 if (decodeExact(decoded, encoded, standard_alphabet)) |_| {
467 if (standard_decoder.decode(decoded, encoded)) |_| {
447468 return error.ExpectedError;
448469 } else |err| if (err != expected_err) return err;
449470 } else |err| if (err != expected_err) return err;
450471
451 if (decodeWithIgnore(buffer[0..], encoded, standard_alphabet_ignore_space)) |_| {
472 if (standard_decoder_ignore_space.decode(buffer[0..], encoded)) |_| {
452473 return error.ExpectedError;
453474 } else |err| if (err != expected_err) return err;
454475}
455476
456477fn testOutputTooSmallError(encoded: []const u8) -> %void {
457 const standard_alphabet_ignore_space = Base64AlphabetWithIgnore.init(
478 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
458479 standard_alphabet_chars, standard_pad_char, " ");
459480 var buffer: [0x100]u8 = undefined;
460481 var decoded = buffer[0..calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1];
461 if (decodeWithIgnore(decoded, encoded, standard_alphabet_ignore_space)) |_| {
482 if (standard_decoder_ignore_space.decode(decoded, encoded)) |_| {
462483 return error.ExpectedError;
463484 } else |err| if (err != error.OutputTooSmall) return err;
464485}
std/os/index.zig+7-5
......@@ -622,7 +622,9 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
622622}
623623
624624// here we replace the standard +/ with -_ so that it can be used in a file name
625const b64_fs_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
625const b64_fs_encoder = base64.Base64Encoder.init(
626 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
627 base64.standard_pad_char);
626628
627629pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) -> %void {
628630 if (symLink(allocator, existing_path, new_path)) {
......@@ -634,12 +636,12 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
634636 }
635637
636638 var rand_buf: [12]u8 = undefined;
637 const tmp_path = %return allocator.alloc(u8, new_path.len + base64.calcEncodedSize(rand_buf.len));
639 const tmp_path = %return allocator.alloc(u8, new_path.len + base64.Base64Encoder.calcSize(rand_buf.len));
638640 defer allocator.free(tmp_path);
639641 mem.copy(u8, tmp_path[0..], new_path);
640642 while (true) {
641643 %return getRandomBytes(rand_buf[0..]);
642 base64.encode(tmp_path[new_path.len..], rand_buf, b64_fs_alphabet_chars, base64.standard_pad_char);
644 b64_fs_encoder.encode(tmp_path[new_path.len..], rand_buf);
643645 if (symLink(allocator, existing_path, tmp_path)) {
644646 return rename(allocator, tmp_path, new_path);
645647 } else |err| {
......@@ -717,11 +719,11 @@ pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []con
717719/// Guaranteed to be atomic.
718720pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: usize) -> %void {
719721 var rand_buf: [12]u8 = undefined;
720 const tmp_path = %return allocator.alloc(u8, dest_path.len + base64.calcEncodedSize(rand_buf.len));
722 const tmp_path = %return allocator.alloc(u8, dest_path.len + base64.Base64Encoder.calcSize(rand_buf.len));
721723 defer allocator.free(tmp_path);
722724 mem.copy(u8, tmp_path[0..], dest_path);
723725 %return getRandomBytes(rand_buf[0..]);
724 base64.encode(tmp_path[dest_path.len..], rand_buf, b64_fs_alphabet_chars, base64.standard_pad_char);
726 b64_fs_encoder.encode(tmp_path[dest_path.len..], rand_buf);
725727
726728 var out_file = %return io.File.openWriteMode(tmp_path, mode, allocator);
727729 defer out_file.close();