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,...@@ -5414,8 +5414,9 @@ export fn decode_base_64(dest_ptr: &amp;u8, dest_len: usize,
5414{5414{
5415 const src = source_ptr[0..source_len];5415 const src = source_ptr[0..source_len];
5416 const dest = dest_ptr[0..dest_len];5416 const dest = dest_ptr[0..dest_len];
5417 const decoded_size = base64.calcDecodedSizeExactUnsafe(src, base64.standard_pad_char);5417 const base64_decoder = base64.standard_decoder_unsafe;
5418 base64.decodeExactUnsafe(dest[0..decoded_size], src, base64.standard_alphabet_unsafe);5418 const decoded_size = base64_decoder.calcSize(src);
5419 base64_decoder.decode(dest[0..decoded_size], src);
5419 return decoded_size;5420 return decoded_size;
5420}5421}
5421</code></pre>5422</code></pre>
example/mix_o_files/base64.zig+3-2
...@@ -3,7 +3,8 @@ const base64 = @import("std").base64;...@@ -3,7 +3,8 @@ const base64 = @import("std").base64;
3export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8, source_len: usize) -> usize {3export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8, source_len: usize) -> usize {
4 const src = source_ptr[0..source_len];4 const src = source_ptr[0..source_len];
5 const dest = dest_ptr[0..dest_len];5 const dest = dest_ptr[0..dest_len];
6 const decoded_size = base64.calcDecodedSizeExactUnsafe(src, base64.standard_pad_char);6 const base64_decoder = base64.standard_decoder_unsafe;
7 base64.decodeExactUnsafe(dest[0..decoded_size], src, base64.standard_alphabet_unsafe);7 const decoded_size = base64_decoder.calcSize(src);
8 base64_decoder.decode(dest[0..decoded_size], src);
8 return decoded_size;9 return decoded_size;
9}10}
std/base64.zig+294-273
...@@ -3,64 +3,85 @@ const mem = @import("mem.zig");...@@ -3,64 +3,85 @@ const mem = @import("mem.zig");
33
4pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";4pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
5pub const standard_pad_char = '=';5pub const standard_pad_char = '=';
6pub const standard_encoder = Base64Encoder.init(standard_alphabet_chars, standard_pad_char);
67
7/// ceil(source_len * 4/3)8pub const Base64Encoder = struct {
8pub fn calcEncodedSize(source_len: usize) -> usize {9 alphabet_chars: []const u8,
9 return @divTrunc(source_len + 2, 3) * 4;10 pad_char: u8,
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;
2311
24 dest[out_index] = alphabet_chars[((source[i] & 0x3) << 4) |12 /// a bunch of assertions, then simply pass the data right through.
25 ((source[i + 1] & 0xf0) >> 4)];13 pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64Encoder {
26 out_index += 1;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) |22 return Base64Encoder{
29 ((source[i + 2] & 0xc0) >> 6)];23 .alphabet_chars = alphabet_chars,
30 out_index += 1;24 .pad_char = pad_char,
25 };
26 }
3127
32 dest[out_index] = alphabet_chars[source[i + 2] & 0x3f];28 /// ceil(source_len * 4/3)
33 out_index += 1;29 pub fn calcSize(source_len: usize) -> usize {
30 return @divTrunc(source_len + 2, 3) * 4;
34 }31 }
3532
36 if (i < source.len) {33 /// dest.len must be what you get from ::calcSize.
37 dest[out_index] = alphabet_chars[(source[i] >> 2) & 0x3f];34 pub fn encode(encoder: &const Base64Encoder, dest: []u8, source: []const u8) {
38 out_index += 1;35 assert(dest.len == Base64Encoder.calcSize(source.len));
3936
40 if (i + 1 == source.len) {37 var i: usize = 0;
41 dest[out_index] = alphabet_chars[(source[i] & 0x3) << 4];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];
42 out_index += 1;41 out_index += 1;
4342
44 dest[out_index] = pad_char;43 dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) |
45 out_index += 1;
46 } else {
47 dest[out_index] = alphabet_chars[((source[i] & 0x3) << 4) |
48 ((source[i + 1] & 0xf0) >> 4)];44 ((source[i + 1] & 0xf0) >> 4)];
49 out_index += 1;45 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];
52 out_index += 1;52 out_index += 1;
53 }53 }
5454
55 dest[out_index] = pad_char;55 if (i < source.len) {
56 out_index += 1;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 }
57 }77 }
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.84pub const Base64Decoder = struct {
63pub const Base64Alphabet = struct {
64 /// e.g. 'A' => 0.85 /// e.g. 'A' => 0.
65 /// undefined for any value not in the 64 alphabet chars.86 /// undefined for any value not in the 64 alphabet chars.
66 char_to_index: [256]u8,87 char_to_index: [256]u8,
...@@ -68,10 +89,10 @@ pub const Base64Alphabet = struct {...@@ -68,10 +89,10 @@ pub const Base64Alphabet = struct {
68 char_in_alphabet: [256]bool,89 char_in_alphabet: [256]bool,
69 pad_char: u8,90 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 {
72 assert(alphabet_chars.len == 64);93 assert(alphabet_chars.len == 64);
7394
74 var result = Base64Alphabet{95 var result = Base64Decoder{
75 .char_to_index = undefined,96 .char_to_index = undefined,
76 .char_in_alphabet = []bool{false} ** 256,97 .char_in_alphabet = []bool{false} ** 256,
77 .pad_char = pad_char,98 .pad_char = pad_char,
...@@ -87,197 +108,193 @@ pub const Base64Alphabet = struct {...@@ -87,197 +108,193 @@ pub const Base64Alphabet = struct {
87108
88 return result;109 return result;
89 }110 }
90};
91111
92error InvalidPadding;112 /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.
93/// For use with ::decodeExact.113 pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) -> %usize {
94/// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.114 if (source.len % 4 != 0) return error.InvalidPadding;
95pub fn calcDecodedSizeExact(encoded: []const u8, pad_char: u8) -> %usize {115 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
96 if (encoded.len % 4 != 0) return error.InvalidPadding;116 }
97 return calcDecodedSizeExactUnsafe(encoded, pad_char);
98}
99117
100error InvalidCharacter;118 /// dest.len must be what you get from ::calcSize.
101/// dest.len must be what you get from ::calcDecodedSizeExact.119 /// invalid characters result in error.InvalidCharacter.
102/// invalid characters result in error.InvalidCharacter.120 /// invalid padding results in error.InvalidPadding.
103/// invalid padding results in error.InvalidPadding.121 pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) -> %void {
104pub fn decodeExact(dest: []u8, source: []const u8, alphabet: &const Base64Alphabet) -> %void {122 assert(dest.len == %%decoder.calcSize(source));
105 assert(dest.len == %%calcDecodedSizeExact(source, alphabet.pad_char));123 assert(source.len % 4 == 0);
106 assert(source.len % 4 == 0);124
107125 var src_cursor: usize = 0;
108 var src_cursor: usize = 0;126 var dest_cursor: usize = 0;
109 var dest_cursor: usize = 0;127
110128 while (src_cursor < source.len) : (src_cursor += 4) {
111 while (src_cursor < source.len) : (src_cursor += 4) {129 if (!decoder.char_in_alphabet[source[src_cursor + 0]]) return error.InvalidCharacter;
112 if (!alphabet.char_in_alphabet[source[src_cursor + 0]]) return error.InvalidCharacter;130 if (!decoder.char_in_alphabet[source[src_cursor + 1]]) return error.InvalidCharacter;
113 if (!alphabet.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) {
114 if (src_cursor < source.len - 4 or source[src_cursor + 3] != alphabet.pad_char) {132 // common case
115 // common case133 if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
116 if (!alphabet.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;134 if (!decoder.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter;
117 if (!alphabet.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter;135 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
118 dest[dest_cursor + 0] = alphabet.char_to_index[source[src_cursor + 0]] << 2 |136 decoder.char_to_index[source[src_cursor + 1]] >> 4;
119 alphabet.char_to_index[source[src_cursor + 1]] >> 4;137 dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 |
120 dest[dest_cursor + 1] = alphabet.char_to_index[source[src_cursor + 1]] << 4 |138 decoder.char_to_index[source[src_cursor + 2]] >> 2;
121 alphabet.char_to_index[source[src_cursor + 2]] >> 2;139 dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 |
122 dest[dest_cursor + 2] = alphabet.char_to_index[source[src_cursor + 2]] << 6 |140 decoder.char_to_index[source[src_cursor + 3]];
123 alphabet.char_to_index[source[src_cursor + 3]];141 dest_cursor += 3;
124 dest_cursor += 3;142 } else if (source[src_cursor + 2] != decoder.pad_char) {
125 } else if (source[src_cursor + 2] != alphabet.pad_char) {143 // one pad char
126 // one pad char144 if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
127 if (!alphabet.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;145 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
128 dest[dest_cursor + 0] = alphabet.char_to_index[source[src_cursor + 0]] << 2 |146 decoder.char_to_index[source[src_cursor + 1]] >> 4;
129 alphabet.char_to_index[source[src_cursor + 1]] >> 4;147 dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 |
130 dest[dest_cursor + 1] = alphabet.char_to_index[source[src_cursor + 1]] << 4 |148 decoder.char_to_index[source[src_cursor + 2]] >> 2;
131 alphabet.char_to_index[source[src_cursor + 2]] >> 2;149 if (decoder.char_to_index[source[src_cursor + 2]] << 6 != 0) return error.InvalidPadding;
132 if (alphabet.char_to_index[source[src_cursor + 2]] << 6 != 0) return error.InvalidPadding;150 dest_cursor += 2;
133 dest_cursor += 2;151 } else {
134 } else {152 // two pad chars
135 // two pad chars153 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
136 dest[dest_cursor + 0] = alphabet.char_to_index[source[src_cursor + 0]] << 2 |154 decoder.char_to_index[source[src_cursor + 1]] >> 4;
137 alphabet.char_to_index[source[src_cursor + 1]] >> 4;155 if (decoder.char_to_index[source[src_cursor + 1]] << 4 != 0) return error.InvalidPadding;
138 if (alphabet.char_to_index[source[src_cursor + 1]] << 4 != 0) return error.InvalidPadding;156 dest_cursor += 1;
139 dest_cursor += 1;157 }
140 }158 }
159
160 assert(src_cursor == source.len);
161 assert(dest_cursor == dest.len);
141 }162 }
163};
142164
143 assert(src_cursor == source.len);165error OutputTooSmall;
144 assert(dest_cursor == dest.len);
145}
146166
147/// For use with ::decodeWithIgnore.167pub const Base64DecoderWithIgnore = struct {
148pub const Base64AlphabetWithIgnore = struct {168 decoder: Base64Decoder,
149 alphabet: Base64Alphabet,
150 char_is_ignored: [256]bool,169 char_is_ignored: [256]bool,
151 pub fn init(alphabet_chars: []const u8, pad_char: u8, ignore_chars: []const u8) -> Base64AlphabetWithIgnore {170 pub fn init(alphabet_chars: []const u8, pad_char: u8, ignore_chars: []const u8) -> Base64DecoderWithIgnore {
152 var result = Base64AlphabetWithIgnore {171 var result = Base64DecoderWithIgnore {
153 .alphabet = Base64Alphabet.init(alphabet_chars, pad_char),172 .decoder = Base64Decoder.init(alphabet_chars, pad_char),
154 .char_is_ignored = []bool{false} ** 256,173 .char_is_ignored = []bool{false} ** 256,
155 };174 };
156175
157 for (ignore_chars) |c| {176 for (ignore_chars) |c| {
158 assert(!result.alphabet.char_in_alphabet[c]);177 assert(!result.decoder.char_in_alphabet[c]);
159 assert(!result.char_is_ignored[c]);178 assert(!result.char_is_ignored[c]);
160 assert(result.alphabet.pad_char != c);179 assert(result.decoder.pad_char != c);
161 result.char_is_ignored[c] = true;180 result.char_is_ignored[c] = true;
162 }181 }
163182
164 return result;183 return result;
165 }184 }
166};
167185
168/// For use with ::decodeWithIgnore.186 /// If no characters end up being ignored or padding, this will be the exact decoded size.
169/// If no characters end up being ignored, this will be the exact decoded size.187 pub fn calcSizeUpperBound(encoded_len: usize) -> %usize {
170pub fn calcDecodedSizeUpperBound(encoded_len: usize) -> %usize {188 return @divTrunc(encoded_len, 4) * 3;
171 return @divTrunc(encoded_len, 4) * 3;189 }
172}
173190
174error OutputTooSmall;191 /// Invalid characters that are not ignored result in error.InvalidCharacter.
175/// Invalid characters that are not ignored results in error.InvalidCharacter.192 /// Invalid padding results in error.InvalidPadding.
176/// Invalid padding results in error.InvalidPadding.193 /// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.
177/// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcDecodedSizeUpperBound.194 /// Returns the number of bytes writen to dest.
178/// Returns the number of bytes writen to dest.195 pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) -> %usize {
179pub fn decodeWithIgnore(dest: []u8, source: []const u8, alphabet_with_ignore: &const Base64AlphabetWithIgnore) -> %usize {196 const decoder = &const decoder_with_ignore.decoder;
180 const alphabet = &const alphabet_with_ignore.alphabet;197
181198 var src_cursor: usize = 0;
182 var src_cursor: usize = 0;199 var dest_cursor: usize = 0;
183 var dest_cursor: usize = 0;200
184201 while (true) {
185 while (true) {202 // get the next 4 chars, if available
186 // get the next 4 chars, if available203 var next_4_chars: [4]u8 = undefined;
187 var next_4_chars: [4]u8 = undefined;204 var available_chars: usize = 0;
188 var available_chars: usize = 0;205 var pad_char_count: usize = 0;
189 var pad_char_count: usize = 0;206 while (available_chars < 4 and src_cursor < source.len) {
190 while (available_chars < 4 and src_cursor < source.len) {207 var c = source[src_cursor];
191 var c = source[src_cursor];208 src_cursor += 1;
192 src_cursor += 1;209
193210 if (decoder.char_in_alphabet[c]) {
194 if (alphabet.char_in_alphabet[c]) {211 // normal char
195 // normal char212 next_4_chars[available_chars] = c;
196 next_4_chars[available_chars] = c;213 available_chars += 1;
197 available_chars += 1;214 } else if (decoder_with_ignore.char_is_ignored[c]) {
198 } else if (alphabet_with_ignore.char_is_ignored[c]) {215 // we're told to skip this one
199 // we're told to skip this one216 continue;
200 continue;217 } else if (c == decoder.pad_char) {
201 } else if (c == alphabet.pad_char) {218 // the padding has begun. count the pad chars.
202 // the padding has begun. count the pad chars.219 pad_char_count += 1;
203 pad_char_count += 1;220 while (src_cursor < source.len) {
204 while (src_cursor < source.len) {221 c = source[src_cursor];
205 c = source[src_cursor];222 src_cursor += 1;
206 src_cursor += 1;223 if (c == decoder.pad_char) {
207 if (c == alphabet.pad_char) {224 pad_char_count += 1;
208 pad_char_count += 1;225 if (pad_char_count > 2) return error.InvalidCharacter;
209 if (pad_char_count > 2) return error.InvalidCharacter;226 } else if (decoder_with_ignore.char_is_ignored[c]) {
210 } else if (alphabet_with_ignore.char_is_ignored[c]) {227 // we can even ignore chars during the padding
211 // we can even ignore chars during the padding228 continue;
212 continue;229 } else return error.InvalidCharacter;
213 } else return error.InvalidCharacter;230 }
214 }231 break;
215 break;232 } else return error.InvalidCharacter;
216 } 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 }
217 }278 }
218279
219 switch (available_chars) {280 assert(src_cursor == source.len);
220 4 => {281
221 // common case282 return dest_cursor;
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 }
262 }283 }
284};
263285
264 assert(src_cursor == source.len);
265286
266 return dest_cursor;287pub const standard_decoder_unsafe = Base64DecoderUnsafe.init(standard_alphabet_chars, standard_pad_char);
267}
268288
269pub const standard_alphabet_unsafe = Base64AlphabetUnsafe.init(standard_alphabet_chars, standard_pad_char);289pub const Base64DecoderUnsafe = struct {
270
271/// For use with ::decodeExactUnsafe.
272pub const Base64AlphabetUnsafe = struct {
273 /// e.g. 'A' => 0.290 /// e.g. 'A' => 0.
274 /// undefined for any value not in the 64 alphabet chars.291 /// undefined for any value not in the 64 alphabet chars.
275 char_to_index: [256]u8,292 char_to_index: [256]u8,
276 pad_char: u8,293 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 {
279 assert(alphabet_chars.len == 64);296 assert(alphabet_chars.len == 64);
280 var result = Base64AlphabetUnsafe {297 var result = Base64DecoderUnsafe {
281 .char_to_index = undefined,298 .char_to_index = undefined,
282 .pad_char = pad_char,299 .pad_char = pad_char,
283 };300 };
...@@ -287,69 +304,73 @@ pub const Base64AlphabetUnsafe = struct {...@@ -287,69 +304,73 @@ pub const Base64AlphabetUnsafe = struct {
287 }304 }
288 return result;305 return result;
289 }306 }
290};
291307
292/// For use with ::decodeExactUnsafe.308 /// The source buffer must be valid.
293/// The encoded buffer must be valid.309 pub fn calcSize(decoder: &const Base64DecoderUnsafe, source: []const u8) -> usize {
294pub fn calcDecodedSizeExactUnsafe(encoded: []const u8, pad_char: u8) -> usize {310 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
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 }
302 }311 }
303 return result;
304}
305312
306/// dest.len must be what you get from ::calcDecodedSizeExactUnsafe.313 /// dest.len must be what you get from ::calcDecodedSizeExactUnsafe.
307/// invalid characters or padding will result in undefined values.314 /// invalid characters or padding will result in undefined values.
308pub fn decodeExactUnsafe(dest: []u8, source: []const u8, alphabet: &const Base64AlphabetUnsafe) {315 pub fn decode(decoder: &const Base64DecoderUnsafe, dest: []u8, source: []const u8) {
309 assert(dest.len == calcDecodedSizeExactUnsafe(source, alphabet.pad_char));316 assert(dest.len == decoder.calcSize(source));
310317
311 var src_index: usize = 0;318 var src_index: usize = 0;
312 var dest_index: usize = 0;319 var dest_index: usize = 0;
313 var in_buf_len: usize = source.len;320 var in_buf_len: usize = source.len;
314321
315 while (in_buf_len > 0 and source[in_buf_len - 1] == alphabet.pad_char) {322 while (in_buf_len > 0 and source[in_buf_len - 1] == decoder.pad_char) {
316 in_buf_len -= 1;323 in_buf_len -= 1;
317 }324 }
318325
319 while (in_buf_len > 4) {326 while (in_buf_len > 4) {
320 dest[dest_index] = alphabet.char_to_index[source[src_index + 0]] << 2 |327 dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 |
321 alphabet.char_to_index[source[src_index + 1]] >> 4;328 decoder.char_to_index[source[src_index + 1]] >> 4;
322 dest_index += 1;329 dest_index += 1;
323330
324 dest[dest_index] = alphabet.char_to_index[source[src_index + 1]] << 4 |331 dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 |
325 alphabet.char_to_index[source[src_index + 2]] >> 2;332 decoder.char_to_index[source[src_index + 2]] >> 2;
326 dest_index += 1;333 dest_index += 1;
327334
328 dest[dest_index] = alphabet.char_to_index[source[src_index + 2]] << 6 |335 dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 |
329 alphabet.char_to_index[source[src_index + 3]];336 decoder.char_to_index[source[src_index + 3]];
330 dest_index += 1;337 dest_index += 1;
331338
332 src_index += 4;339 src_index += 4;
333 in_buf_len -= 4;340 in_buf_len -= 4;
334 }341 }
335342
336 if (in_buf_len > 1) {343 if (in_buf_len > 1) {
337 dest[dest_index] = alphabet.char_to_index[source[src_index + 0]] << 2 |344 dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 |
338 alphabet.char_to_index[source[src_index + 1]] >> 4;345 decoder.char_to_index[source[src_index + 1]] >> 4;
339 dest_index += 1;346 dest_index += 1;
340 }347 }
341 if (in_buf_len > 2) {348 if (in_buf_len > 2) {
342 dest[dest_index] = alphabet.char_to_index[source[src_index + 1]] << 4 |349 dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 |
343 alphabet.char_to_index[source[src_index + 2]] >> 2;350 decoder.char_to_index[source[src_index + 2]] >> 2;
344 dest_index += 1;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 }
345 }358 }
346 if (in_buf_len > 3) {359};
347 dest[dest_index] = alphabet.char_to_index[source[src_index + 2]] << 6 |360
348 alphabet.char_to_index[source[src_index + 3]];361fn calcDecodedSizeExactUnsafe(source: []const u8, pad_char: u8) -> usize {
349 dest_index += 1;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 }
350 }369 }
370 return result;
351}371}
352372
373
353test "base64" {374test "base64" {
354 @setEvalBranchQuota(5000);375 @setEvalBranchQuota(5000);
355 %%testBase64();376 %%testBase64();
...@@ -391,74 +412,74 @@ fn testBase64() -> %void {...@@ -391,74 +412,74 @@ fn testBase64() -> %void {
391}412}
392413
393fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) -> %void {414fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) -> %void {
394 // encode415 // Base64Encoder
395 {416 {
396 var buffer: [0x100]u8 = undefined;417 var buffer: [0x100]u8 = undefined;
397 var encoded = buffer[0..calcEncodedSize(expected_decoded.len)];418 var encoded = buffer[0..Base64Encoder.calcSize(expected_decoded.len)];
398 encode(encoded, expected_decoded, standard_alphabet_chars, standard_pad_char);419 standard_encoder.encode(encoded, expected_decoded);
399 assert(mem.eql(u8, encoded, expected_encoded));420 assert(mem.eql(u8, encoded, expected_encoded));
400 }421 }
401422
402 // decodeExact423 // Base64Decoder
403 {424 {
404 var buffer: [0x100]u8 = undefined;425 var buffer: [0x100]u8 = undefined;
405 var decoded = buffer[0..%return calcDecodedSizeExact(expected_encoded, standard_pad_char)];426 var decoded = buffer[0..%return standard_decoder.calcSize(expected_encoded)];
406 %return decodeExact(decoded, expected_encoded, standard_alphabet);427 %return standard_decoder.decode(decoded, expected_encoded);
407 assert(mem.eql(u8, decoded, expected_decoded));428 assert(mem.eql(u8, decoded, expected_decoded));
408 }429 }
409430
410 // decodeWithIgnore431 // Base64DecoderWithIgnore
411 {432 {
412 const standard_alphabet_ignore_nothing = Base64AlphabetWithIgnore.init(433 const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(
413 standard_alphabet_chars, standard_pad_char, "");434 standard_alphabet_chars, standard_pad_char, "");
414 var buffer: [0x100]u8 = undefined;435 var buffer: [0x100]u8 = undefined;
415 var decoded = buffer[0..%return calcDecodedSizeUpperBound(expected_encoded.len)];436 var decoded = buffer[0..%return Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];
416 var written = %return decodeWithIgnore(decoded, expected_encoded, standard_alphabet_ignore_nothing);437 var written = %return standard_decoder_ignore_nothing.decode(decoded, expected_encoded);
417 assert(written <= decoded.len);438 assert(written <= decoded.len);
418 assert(mem.eql(u8, decoded[0..written], expected_decoded));439 assert(mem.eql(u8, decoded[0..written], expected_decoded));
419 }440 }
420441
421 // decodeExactUnsafe442 // Base64DecoderUnsafe
422 {443 {
423 var buffer: [0x100]u8 = undefined;444 var buffer: [0x100]u8 = undefined;
424 var decoded = buffer[0..calcDecodedSizeExactUnsafe(expected_encoded, standard_pad_char)];445 var decoded = buffer[0..standard_decoder_unsafe.calcSize(expected_encoded)];
425 decodeExactUnsafe(decoded, expected_encoded, standard_alphabet_unsafe);446 standard_decoder_unsafe.decode(decoded, expected_encoded);
426 assert(mem.eql(u8, decoded, expected_decoded));447 assert(mem.eql(u8, decoded, expected_decoded));
427 }448 }
428}449}
429450
430fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) -> %void {451fn 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(
432 standard_alphabet_chars, standard_pad_char, " ");453 standard_alphabet_chars, standard_pad_char, " ");
433 var buffer: [0x100]u8 = undefined;454 var buffer: [0x100]u8 = undefined;
434 var decoded = buffer[0..%return calcDecodedSizeUpperBound(encoded.len)];455 var decoded = buffer[0..%return Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];
435 var written = %return decodeWithIgnore(decoded, encoded, standard_alphabet_ignore_space);456 var written = %return standard_decoder_ignore_space.decode(decoded, encoded);
436 assert(mem.eql(u8, decoded[0..written], expected_decoded));457 assert(mem.eql(u8, decoded[0..written], expected_decoded));
437}458}
438459
439error ExpectedError;460error ExpectedError;
440fn testError(encoded: []const u8, expected_err: error) -> %void {461fn testError(encoded: []const u8, expected_err: error) -> %void {
441 const standard_alphabet_ignore_space = Base64AlphabetWithIgnore.init(462 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
442 standard_alphabet_chars, standard_pad_char, " ");463 standard_alphabet_chars, standard_pad_char, " ");
443 var buffer: [0x100]u8 = undefined;464 var buffer: [0x100]u8 = undefined;
444 if (calcDecodedSizeExact(encoded, standard_pad_char)) |decoded_size| {465 if (standard_decoder.calcSize(encoded)) |decoded_size| {
445 var decoded = buffer[0..decoded_size];466 var decoded = buffer[0..decoded_size];
446 if (decodeExact(decoded, encoded, standard_alphabet)) |_| {467 if (standard_decoder.decode(decoded, encoded)) |_| {
447 return error.ExpectedError;468 return error.ExpectedError;
448 } else |err| if (err != expected_err) return err;469 } else |err| if (err != expected_err) return err;
449 } else |err| if (err != expected_err) return err;470 } 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)) |_| {
452 return error.ExpectedError;473 return error.ExpectedError;
453 } else |err| if (err != expected_err) return err;474 } else |err| if (err != expected_err) return err;
454}475}
455476
456fn testOutputTooSmallError(encoded: []const u8) -> %void {477fn testOutputTooSmallError(encoded: []const u8) -> %void {
457 const standard_alphabet_ignore_space = Base64AlphabetWithIgnore.init(478 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
458 standard_alphabet_chars, standard_pad_char, " ");479 standard_alphabet_chars, standard_pad_char, " ");
459 var buffer: [0x100]u8 = undefined;480 var buffer: [0x100]u8 = undefined;
460 var decoded = buffer[0..calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1];481 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)) |_| {
462 return error.ExpectedError;483 return error.ExpectedError;
463 } else |err| if (err != error.OutputTooSmall) return err;484 } else |err| if (err != error.OutputTooSmall) return err;
464}485}
std/os/index.zig+7-5
...@@ -622,7 +622,9 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:...@@ -622,7 +622,9 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
622}622}
623623
624// here we replace the standard +/ with -_ so that it can be used in a file name624// 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
627pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) -> %void {629pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) -> %void {
628 if (symLink(allocator, existing_path, new_path)) {630 if (symLink(allocator, existing_path, new_path)) {
...@@ -634,12 +636,12 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:...@@ -634,12 +636,12 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
634 }636 }
635637
636 var rand_buf: [12]u8 = undefined;638 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));
638 defer allocator.free(tmp_path);640 defer allocator.free(tmp_path);
639 mem.copy(u8, tmp_path[0..], new_path);641 mem.copy(u8, tmp_path[0..], new_path);
640 while (true) {642 while (true) {
641 %return getRandomBytes(rand_buf[0..]);643 %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);
643 if (symLink(allocator, existing_path, tmp_path)) {645 if (symLink(allocator, existing_path, tmp_path)) {
644 return rename(allocator, tmp_path, new_path);646 return rename(allocator, tmp_path, new_path);
645 } else |err| {647 } else |err| {
...@@ -717,11 +719,11 @@ pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []con...@@ -717,11 +719,11 @@ pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []con
717/// Guaranteed to be atomic.719/// Guaranteed to be atomic.
718pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: usize) -> %void {720pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: usize) -> %void {
719 var rand_buf: [12]u8 = undefined;721 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));
721 defer allocator.free(tmp_path);723 defer allocator.free(tmp_path);
722 mem.copy(u8, tmp_path[0..], dest_path);724 mem.copy(u8, tmp_path[0..], dest_path);
723 %return getRandomBytes(rand_buf[0..]);725 %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
726 var out_file = %return io.File.openWriteMode(tmp_path, mode, allocator);728 var out_file = %return io.File.openWriteMode(tmp_path, mode, allocator);
727 defer out_file.close();729 defer out_file.close();