Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: sdk/lib/convert/base64.dart

Issue 1858113003: Add "url-safe" encoding to base64 in dart:convert. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Fix typo Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « CHANGELOG.md ('k') | tests/lib/convert/base64_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart.convert; 5 part of dart.convert;
6 6
7 /** 7 /**
8 * An instance of [Base64Codec]. 8 * A [base64](https://tools.ietf.org/html/rfc4648) encoder and decoder.
9 * 9 *
10 * This instance provides a convenient access to 10 * It encodes using the default base64 alphabet,
11 * [base64](https://tools.ietf.org/html/rfc4648) encoding and decoding. 11 * decodes using both the base64 and base64url alphabets,
12 * 12 * does not allow invalid characters and requires padding.
13 * It encodes and decodes using the default base64 alphabet, does not allow
14 * any invalid characters when decoding, and requires padding.
15 * 13 *
16 * Examples: 14 * Examples:
17 * 15 *
18 * var encoded = BASE64.encode([0x62, 0x6c, 0xc3, 0xa5, 0x62, 0xc3, 0xa6, 16 * var encoded = BASE64.encode([0x62, 0x6c, 0xc3, 0xa5, 0x62, 0xc3, 0xa6,
19 * 0x72, 0x67, 0x72, 0xc3, 0xb8, 0x64]); 17 * 0x72, 0x67, 0x72, 0xc3, 0xb8, 0x64]);
20 * var decoded = BASE64.decode("YmzDpWLDpnJncsO4ZAo="); 18 * var decoded = BASE64.decode("YmzDpWLDpnJncsO4ZAo=");
21 */ 19 */
22 const Base64Codec BASE64 = const Base64Codec(); 20 const Base64Codec BASE64 = const Base64Codec();
23 21
22 /**
23 * A [base64url](https://tools.ietf.org/html/rfc4648) encoder and decoder.
24 *
25 * It encodes and decodes using the base64url alphabet,
26 * decodes using both the base64 and base64url alphabets,
27 * does not allow invalid characters and requires padding.
28 *
29 * Examples:
30 *
31 * var encoded = BASE64.encode([0x62, 0x6c, 0xc3, 0xa5, 0x62, 0xc3, 0xa6,
32 * 0x72, 0x67, 0x72, 0xc3, 0xb8, 0x64]);
33 * var decoded = BASE64.decode("YmzDpWLDpnJncsO4ZAo=");
34 */
35 const Base64Codec BASE64URL = const Base64Codec.urlSafe();
36
24 // Constants used in more than one class. 37 // Constants used in more than one class.
25 const int _paddingChar = 0x3d; // '='. 38 const int _paddingChar = 0x3d; // '='.
26 39
27 /** 40 /**
28 * A [base64](https://tools.ietf.org/html/rfc4648) encoder and decoder. 41 * A [base64](https://tools.ietf.org/html/rfc4648) encoder and decoder.
29 * 42 *
30 * A [Base64Codec] allows base64 encoding bytes into ASCII strings and 43 * A [Base64Codec] allows base64 encoding bytes into ASCII strings and
31 * decoding valid encodings back to bytes. 44 * decoding valid encodings back to bytes.
32 * 45 *
33 * This implementation only handles the simplest RFC 4648 base-64 encoding. 46 * This implementation only handles the simplest RFC 4648 base64 and base64url
47 * encodings.
34 * It does not allow invalid characters when decoding and it requires, 48 * It does not allow invalid characters when decoding and it requires,
35 * and generates, padding so that the input is always a multiple of four 49 * and generates, padding so that the input is always a multiple of four
36 * characters. 50 * characters.
37 */ 51 */
38 class Base64Codec extends Codec<List<int>, String> { 52 class Base64Codec extends Codec<List<int>, String> {
39 const Base64Codec(); 53 final Base64Encoder _encoder;
54 const Base64Codec() : _encoder = const Base64Encoder();
55 const Base64Codec.urlSafe() : _encoder = const Base64Encoder.urlSafe();
40 56
41 Base64Encoder get encoder => const Base64Encoder(); 57 Base64Encoder get encoder => _encoder;
42 58
43 Base64Decoder get decoder => const Base64Decoder(); 59 Base64Decoder get decoder => const Base64Decoder();
44 } 60 }
45 61
46 // ------------------------------------------------------------------------ 62 // ------------------------------------------------------------------------
47 // Encoder 63 // Encoder
48 // ------------------------------------------------------------------------ 64 // ------------------------------------------------------------------------
49 65
50 /** 66 /**
51 * Base-64 encoding converter. 67 * Base64 and base64url encoding converter.
52 * 68 *
53 * Encodes lists of bytes using base64 encoding. 69 * Encodes lists of bytes using base64 or base64url encoding.
54 * The result are ASCII strings using a restricted alphabet. 70 *
71 * The results are ASCII strings using a restricted alphabet.
55 */ 72 */
56 class Base64Encoder extends Converter<List<int>, String> { 73 class Base64Encoder extends Converter<List<int>, String> {
57 const Base64Encoder(); 74 final bool _urlSafe;
75
76 const Base64Encoder() : _urlSafe = false;
77 const Base64Encoder.urlSafe() : _urlSafe = true;
58 78
59 String convert(List<int> input) { 79 String convert(List<int> input) {
60 if (input.isEmpty) return ""; 80 if (input.isEmpty) return "";
61 var encoder = new _Base64Encoder(); 81 var encoder = new _Base64Encoder(_urlSafe);
62 Uint8List buffer = encoder.encode(input, 0, input.length, true); 82 Uint8List buffer = encoder.encode(input, 0, input.length, true);
63 return new String.fromCharCodes(buffer); 83 return new String.fromCharCodes(buffer);
64 } 84 }
65 85
66 ByteConversionSink startChunkedConversion(Sink<String> sink) { 86 ByteConversionSink startChunkedConversion(Sink<String> sink) {
67 if (sink is StringConversionSink) { 87 if (sink is StringConversionSink) {
68 return new _Utf8Base64EncoderSink(sink.asUtf8Sink(false)); 88 return new _Utf8Base64EncoderSink(sink.asUtf8Sink(false), _urlSafe);
69 } 89 }
70 return new _AsciiBase64EncoderSink(sink); 90 return new _AsciiBase64EncoderSink(sink, _urlSafe);
71 } 91 }
72 92
73 Stream<String> bind(Stream<List<int>> stream) { 93 Stream<String> bind(Stream<List<int>> stream) {
74 return new Stream<String>.eventTransformed( 94 return new Stream<String>.eventTransformed(
75 stream, 95 stream,
76 (EventSink sink) => 96 (EventSink sink) =>
77 new _ConverterStreamEventSink<List<int>, String>(this, sink)); 97 new _ConverterStreamEventSink<List<int>, String>(
98 this, sink));
78 } 99 }
79 } 100 }
80 101
81 /** 102 /**
82 * Helper class for encoding bytes to BASE-64. 103 * Helper class for encoding bytes to base64.
83 */ 104 */
84 class _Base64Encoder { 105 class _Base64Encoder {
85 /** The RFC 4648 base64 encoding alphabet. */ 106 /** The RFC 4648 base64 encoding alphabet. */
86 static const String _base64Alphabet = 107 static const String _base64Alphabet =
87 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 108 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
88 109
110 /** The RFC 4648 base64url encoding alphabet. */
111 static const String _base64urlAlphabet =
112 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
113
89 /** Shift-count to extract the values stored in [_state]. */ 114 /** Shift-count to extract the values stored in [_state]. */
90 static const int _valueShift = 2; 115 static const int _valueShift = 2;
91 116
92 /** Mask to extract the count value stored in [_state]. */ 117 /** Mask to extract the count value stored in [_state]. */
93 static const int _countMask = 3; 118 static const int _countMask = 3;
94 119
95 static const int _sixBitMask = 0x3F; 120 static const int _sixBitMask = 0x3F;
96 121
97 /** 122 /**
98 * Intermediate state between chunks. 123 * Intermediate state between chunks.
99 * 124 *
100 * Encoding handles three bytes at a time. 125 * Encoding handles three bytes at a time.
101 * If fewer than three bytes has been seen, this value encodes 126 * If fewer than three bytes has been seen, this value encodes
102 * the number of bytes seen (0, 1 or 2) and their values. 127 * the number of bytes seen (0, 1 or 2) and their values.
103 */ 128 */
104 int _state = 0; 129 int _state = 0;
105 130
131 /** Alphabet used for encoding. */
132 final String _alphabet;
133
134 _Base64Encoder(bool urlSafe)
135 : _alphabet = urlSafe ? _base64urlAlphabet : _base64Alphabet;
136
106 /** Encode count and bits into a value to be stored in [_state]. */ 137 /** Encode count and bits into a value to be stored in [_state]. */
107 static int _encodeState(int count, int bits) { 138 static int _encodeState(int count, int bits) {
108 assert(count <= _countMask); 139 assert(count <= _countMask);
109 return bits << _valueShift | count; 140 return bits << _valueShift | count;
110 } 141 }
111 142
112 /** Extract bits from encoded state. */ 143 /** Extract bits from encoded state. */
113 static int _stateBits(int state) => state >> _valueShift; 144 static int _stateBits(int state) => state >> _valueShift;
114 145
115 /** Extract count from encoded state. */ 146 /** Extract count from encoded state. */
(...skipping 25 matching lines...) Expand all
141 172
142 int count = _stateCount(_state); 173 int count = _stateCount(_state);
143 int byteCount = (count + length); 174 int byteCount = (count + length);
144 int fullChunks = byteCount ~/ 3; 175 int fullChunks = byteCount ~/ 3;
145 int partialChunkLength = byteCount - fullChunks * 3; 176 int partialChunkLength = byteCount - fullChunks * 3;
146 int bufferLength = fullChunks * 4; 177 int bufferLength = fullChunks * 4;
147 if (isLast && partialChunkLength > 0) { 178 if (isLast && partialChunkLength > 0) {
148 bufferLength += 4; // Room for padding. 179 bufferLength += 4; // Room for padding.
149 } 180 }
150 var output = createBuffer(bufferLength); 181 var output = createBuffer(bufferLength);
151 _state = encodeChunk(bytes, start, end, isLast, output, 0, _state); 182 _state = encodeChunk(_alphabet, bytes, start, end, isLast,
183 output, 0, _state);
152 if (bufferLength > 0) return output; 184 if (bufferLength > 0) return output;
153 // If the input plus the data in state is still less than three bytes, 185 // If the input plus the data in state is still less than three bytes,
154 // there may not be any output. 186 // there may not be any output.
155 return null; 187 return null;
156 } 188 }
157 189
158 static int encodeChunk(List<int> bytes, int start, int end, bool isLast, 190 static int encodeChunk(String alphabet,
159 Uint8List output, int outputIndex, int state) { 191 List<int> bytes, int start, int end, bool isLast,
192 Uint8List output, int outputIndex, int state) {
160 int bits = _stateBits(state); 193 int bits = _stateBits(state);
161 // Count number of missing bytes in three-byte chunk. 194 // Count number of missing bytes in three-byte chunk.
162 int expectedChars = 3 - _stateCount(state); 195 int expectedChars = 3 - _stateCount(state);
163 196
164 // The input must be a list of bytes (integers in the range 0..255). 197 // The input must be a list of bytes (integers in the range 0..255).
165 // The value of `byteOr` will be the bitwise or of all the values in 198 // The value of `byteOr` will be the bitwise or of all the values in
166 // `bytes` and a later check will validate that they were all valid bytes. 199 // `bytes` and a later check will validate that they were all valid bytes.
167 int byteOr = 0; 200 int byteOr = 0;
168 for (int i = start; i < end; i++) { 201 for (int i = start; i < end; i++) {
169 int byte = bytes[i]; 202 int byte = bytes[i];
170 byteOr |= byte; 203 byteOr |= byte;
171 bits = ((bits << 8) | byte) & 0xFFFFFF; // Never store more than 24 bits. 204 bits = ((bits << 8) | byte) & 0xFFFFFF; // Never store more than 24 bits.
172 expectedChars--; 205 expectedChars--;
173 if (expectedChars == 0) { 206 if (expectedChars == 0) {
174 output[outputIndex++] = 207 output[outputIndex++] =
175 _base64Alphabet.codeUnitAt((bits >> 18) & _sixBitMask); 208 alphabet.codeUnitAt((bits >> 18) & _sixBitMask);
176 output[outputIndex++] = 209 output[outputIndex++] =
177 _base64Alphabet.codeUnitAt((bits >> 12) & _sixBitMask); 210 alphabet.codeUnitAt((bits >> 12) & _sixBitMask);
178 output[outputIndex++] = 211 output[outputIndex++] =
179 _base64Alphabet.codeUnitAt((bits >> 6) & _sixBitMask); 212 alphabet.codeUnitAt((bits >> 6) & _sixBitMask);
180 output[outputIndex++] = 213 output[outputIndex++] =
181 _base64Alphabet.codeUnitAt(bits & _sixBitMask); 214 alphabet.codeUnitAt(bits & _sixBitMask);
182 expectedChars = 3; 215 expectedChars = 3;
183 bits = 0; 216 bits = 0;
184 } 217 }
185 } 218 }
186 if (byteOr >= 0 && byteOr <= 255) { 219 if (byteOr >= 0 && byteOr <= 255) {
187 if (isLast && expectedChars < 3) { 220 if (isLast && expectedChars < 3) {
188 writeFinalChunk(output, outputIndex, 3 - expectedChars, bits); 221 writeFinalChunk(alphabet, output, outputIndex, 3 - expectedChars, bits);
189 return 0; 222 return 0;
190 } 223 }
191 return _encodeState(3 - expectedChars, bits); 224 return _encodeState(3 - expectedChars, bits);
192 } 225 }
193 226
194 // There was an invalid byte value somewhere in the input - find it! 227 // There was an invalid byte value somewhere in the input - find it!
195 int i = start; 228 int i = start;
196 while (i < end) { 229 while (i < end) {
197 int byte = bytes[i]; 230 int byte = bytes[i];
198 if (byte < 0 || byte > 255) break; 231 if (byte < 0 || byte > 255) break;
199 i++; 232 i++;
200 } 233 }
201 throw new ArgumentError.value(bytes, 234 throw new ArgumentError.value(bytes,
202 "Not a byte value at index $i: 0x${bytes[i].toRadixString(16)}"); 235 "Not a byte value at index $i: 0x${bytes[i].toRadixString(16)}");
203 } 236 }
204 237
205 /** 238 /**
206 * Writes a final encoded four-character chunk. 239 * Writes a final encoded four-character chunk.
207 * 240 *
208 * Only used when the [_state] contains a partial (1 or 2 byte) 241 * Only used when the [_state] contains a partial (1 or 2 byte)
209 * input. 242 * input.
210 */ 243 */
211 static void writeFinalChunk(Uint8List output, int outputIndex, 244 static void writeFinalChunk(String alphabet,
245 Uint8List output, int outputIndex,
212 int count, int bits) { 246 int count, int bits) {
213 assert(count > 0); 247 assert(count > 0);
214 if (count == 1) { 248 if (count == 1) {
215 output[outputIndex++] = 249 output[outputIndex++] =
216 _base64Alphabet.codeUnitAt((bits >> 2) & _sixBitMask); 250 alphabet.codeUnitAt((bits >> 2) & _sixBitMask);
217 output[outputIndex++] = 251 output[outputIndex++] =
218 _base64Alphabet.codeUnitAt((bits << 4) & _sixBitMask); 252 alphabet.codeUnitAt((bits << 4) & _sixBitMask);
219 output[outputIndex++] = _paddingChar; 253 output[outputIndex++] = _paddingChar;
220 output[outputIndex++] = _paddingChar; 254 output[outputIndex++] = _paddingChar;
221 } else { 255 } else {
222 assert(count == 2); 256 assert(count == 2);
223 output[outputIndex++] = 257 output[outputIndex++] =
224 _base64Alphabet.codeUnitAt((bits >> 10) & _sixBitMask); 258 alphabet.codeUnitAt((bits >> 10) & _sixBitMask);
225 output[outputIndex++] = 259 output[outputIndex++] =
226 _base64Alphabet.codeUnitAt((bits >> 4) & _sixBitMask); 260 alphabet.codeUnitAt((bits >> 4) & _sixBitMask);
227 output[outputIndex++] = 261 output[outputIndex++] =
228 _base64Alphabet.codeUnitAt((bits << 2) & _sixBitMask); 262 alphabet.codeUnitAt((bits << 2) & _sixBitMask);
229 output[outputIndex++] = _paddingChar; 263 output[outputIndex++] = _paddingChar;
230 } 264 }
231 } 265 }
232 } 266 }
233 267
234 class _BufferCachingBase64Encoder extends _Base64Encoder { 268 class _BufferCachingBase64Encoder extends _Base64Encoder {
235 /** 269 /**
236 * Reused buffer. 270 * Reused buffer.
237 * 271 *
238 * When the buffer isn't released to the sink, only used to create another 272 * When the buffer isn't released to the sink, only used to create another
239 * value (a string), the buffer can be reused between chunks. 273 * value (a string), the buffer can be reused between chunks.
240 */ 274 */
241 Uint8List bufferCache; 275 Uint8List bufferCache;
242 276
277 _BufferCachingBase64Encoder(bool urlSafe) : super(urlSafe);
278
243 Uint8List createBuffer(int bufferLength) { 279 Uint8List createBuffer(int bufferLength) {
244 if (bufferCache == null || bufferCache.length < bufferLength) { 280 if (bufferCache == null || bufferCache.length < bufferLength) {
245 bufferCache = new Uint8List(bufferLength); 281 bufferCache = new Uint8List(bufferLength);
246 } 282 }
247 // Return a view of the buffer, so it has the reuested length. 283 // Return a view of the buffer, so it has the reuested length.
248 return new Uint8List.view(bufferCache.buffer, 0, bufferLength); 284 return new Uint8List.view(bufferCache.buffer, 0, bufferLength);
249 } 285 }
250 } 286 }
251 287
252 abstract class _Base64EncoderSink extends ByteConversionSinkBase { 288 abstract class _Base64EncoderSink extends ByteConversionSinkBase {
253 void add(List<int> source) { 289 void add(List<int> source) {
254 _add(source, 0, source.length, false); 290 _add(source, 0, source.length, false);
255 } 291 }
256 292
257 void close() { 293 void close() {
258 _add(null, 0, 0, true); 294 _add(null, 0, 0, true);
259 } 295 }
260 296
261 void addSlice(List<int> source, int start, int end, bool isLast) { 297 void addSlice(List<int> source, int start, int end, bool isLast) {
262 if (end == null) throw new ArgumentError.notNull("end"); 298 if (end == null) throw new ArgumentError.notNull("end");
263 RangeError.checkValidRange(start, end, source.length); 299 RangeError.checkValidRange(start, end, source.length);
264 _add(source, start, end, isLast); 300 _add(source, start, end, isLast);
265 } 301 }
266 302
267 void _add(List<int> source, int start, int end, bool isLast); 303 void _add(List<int> source, int start, int end, bool isLast);
268 } 304 }
269 305
270 class _AsciiBase64EncoderSink extends _Base64EncoderSink { 306 class _AsciiBase64EncoderSink extends _Base64EncoderSink {
271 final _Base64Encoder _encoder = new _BufferCachingBase64Encoder(); 307 final Sink<String> _sink;
308 final _Base64Encoder _encoder;
272 309
273 final Sink<String> _sink; 310 _AsciiBase64EncoderSink(this._sink, bool urlSafe)
274 311 : _encoder = new _BufferCachingBase64Encoder(urlSafe);
275 _AsciiBase64EncoderSink(this._sink);
276 312
277 void _add(List<int> source, int start, int end, bool isLast) { 313 void _add(List<int> source, int start, int end, bool isLast) {
278 Uint8List buffer = _encoder.encode(source, start, end, isLast); 314 Uint8List buffer = _encoder.encode(source, start, end, isLast);
279 if (buffer != null) { 315 if (buffer != null) {
280 String string = new String.fromCharCodes(buffer); 316 String string = new String.fromCharCodes(buffer);
281 _sink.add(string); 317 _sink.add(string);
282 } 318 }
283 if (isLast) { 319 if (isLast) {
284 _sink.close(); 320 _sink.close();
285 } 321 }
286 } 322 }
287 } 323 }
288 324
289 class _Utf8Base64EncoderSink extends _Base64EncoderSink { 325 class _Utf8Base64EncoderSink extends _Base64EncoderSink {
290 final ByteConversionSink _sink; 326 final ByteConversionSink _sink;
291 final _Base64Encoder _encoder = new _Base64Encoder(); 327 final _Base64Encoder _encoder;
292 328
293 _Utf8Base64EncoderSink(this._sink); 329 _Utf8Base64EncoderSink(this._sink, bool urlSafe)
330 : _encoder = new _Base64Encoder(urlSafe);
294 331
295 void _add(List<int> source, int start, int end, bool isLast) { 332 void _add(List<int> source, int start, int end, bool isLast) {
296 Uint8List buffer = _encoder.encode(source, start, end, isLast); 333 Uint8List buffer = _encoder.encode(source, start, end, isLast);
297 if (buffer != null) { 334 if (buffer != null) {
298 _sink.addSlice(buffer, 0, buffer.length, isLast); 335 _sink.addSlice(buffer, 0, buffer.length, isLast);
299 } 336 }
300 } 337 }
301 } 338 }
302 339
303 // ------------------------------------------------------------------------ 340 // ------------------------------------------------------------------------
304 // Decoder 341 // Decoder
305 // ------------------------------------------------------------------------ 342 // ------------------------------------------------------------------------
306 343
344 /**
345 * Decoder for base64 encoded data.
346 *
347 * This decoder accepts both base64 and base64url ("url-safe") encodings.
348 *
349 * The encoding is required to be properly padded.
350 */
307 class Base64Decoder extends Converter<String, List<int>> { 351 class Base64Decoder extends Converter<String, List<int>> {
308 const Base64Decoder(); 352 const Base64Decoder();
309 353
310 List<int> convert(String input, [int start = 0, int end]) { 354 List<int> convert(String input, [int start = 0, int end]) {
311 end = RangeError.checkValidRange(start, end, input.length); 355 end = RangeError.checkValidRange(start, end, input.length);
312 if (start == end) return new Uint8List(0); 356 if (start == end) return new Uint8List(0);
313 var decoder = new _Base64Decoder(); 357 var decoder = new _Base64Decoder();
314 Uint8List buffer = decoder.decode(input, start, end); 358 Uint8List buffer = decoder.decode(input, start, end);
315 decoder.close(input, end); 359 decoder.close(input, end);
316 return buffer; 360 return buffer;
(...skipping 25 matching lines...) Expand all
342 static const int _p = _padding; 386 static const int _p = _padding;
343 387
344 /** 388 /**
345 * Mapping from ASCII characters to their index in the base64 alphabet. 389 * Mapping from ASCII characters to their index in the base64 alphabet.
346 * 390 *
347 * Uses [_invalid] for invalid indices and [_padding] for the padding 391 * Uses [_invalid] for invalid indices and [_padding] for the padding
348 * character. 392 * character.
349 * 393 *
350 * Accepts the "URL-safe" alphabet as well (`-` and `_` are the 394 * Accepts the "URL-safe" alphabet as well (`-` and `_` are the
351 * 62nd and 63rd alphabet characters), and considers `%` a padding 395 * 62nd and 63rd alphabet characters), and considers `%` a padding
352 * character which mush be followed by `3D`, the percent-escape 396 * character, which mush then be followed by `3D`, the percent-escape
353 * for `=`. 397 * for `=`.
354 */ 398 */
355 static final List<int> _inverseAlphabet = new Int8List.fromList([ 399 static final List<int> _inverseAlphabet = new Int8List.fromList([
356 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 400 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
357 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 401 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
358 __, __, __, __, __, _p, __, __, __, __, __, 62, __, 62, __, 63, 402 __, __, __, __, __, _p, __, __, __, __, __, 62, __, 62, __, 63,
359 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, __, __, __, _p, __, __, 403 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, __, __, __, _p, __, __,
360 __, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 404 __, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
361 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, __, __, __, __, 63, 405 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, __, __, __, __, 63,
362 __, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 406 __, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
363 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, __, __, __, __, __, 407 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, __, __, __, __, __,
364 ]); 408 ]);
365 409
366 // Character constants. 410 // Character constants.
367 static const int _char_percent = 0x25; // '%'. 411 static const int _char_percent = 0x25; // '%'.
368 static const int _char_3 = 0x33; // '3'. 412 static const int _char_3 = 0x33; // '3'.
369 static const int _char_d = 0x64; // 'd'. 413 static const int _char_d = 0x64; // 'd'.
370 414
371 /** 415 /**
372 * Maintains the intermediate state of a partly-decoded input. 416 * Maintains the intermediate state of a partly-decoded input.
373 * 417 *
374 * BASE-64 is decoded in chunks of four characters. If a chunk does not 418 * Base64 is decoded in chunks of four characters. If a chunk does not
375 * contain a full block, the decoded bits (six per character) of the 419 * contain a full block, the decoded bits (six per character) of the
376 * available characters are stored in [_state] until the next call to 420 * available characters are stored in [_state] until the next call to
377 * [_decode] or [_close]. 421 * [_decode] or [_close].
378 * 422 *
379 * If no padding has been seen, the value is 423 * If no padding has been seen, the value is
380 * `numberOfCharactersSeen | (decodedBits << 2)` 424 * `numberOfCharactersSeen | (decodedBits << 2)`
381 * where `numberOfCharactersSeen` is between 0 and 3 and decoded bits 425 * where `numberOfCharactersSeen` is between 0 and 3 and decoded bits
382 * contains six bits per seen character. 426 * contains six bits per seen character.
383 * 427 *
384 * If padding has been seen the value is negative. It's the bitwise negation 428 * If padding has been seen the value is negative. It's the bitwise negation
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after
611 } 655 }
612 break; 656 break;
613 } 657 }
614 return newEnd; 658 return newEnd;
615 } 659 }
616 660
617 /** 661 /**
618 * Check that the remainder of the string is valid padding. 662 * Check that the remainder of the string is valid padding.
619 * 663 *
620 * Valid padding is a correct number (0, 1 or 2) of `=` characters 664 * Valid padding is a correct number (0, 1 or 2) of `=` characters
621 * or `%3D` sequences depending on the number of preceding BASE-64 characters. 665 * or `%3D` sequences depending on the number of preceding base64 characters.
622 * The [state] parameter encodes which padding continuations are allowed 666 * The [state] parameter encodes which padding continuations are allowed
623 * as the number of expected characters. That number is the number of 667 * as the number of expected characters. That number is the number of
624 * expected padding characters times 3 minus the number of padding characters 668 * expected padding characters times 3 minus the number of padding characters
625 * seen so far, where `=` counts as 3 counts as three characters, 669 * seen so far, where `=` counts as 3 counts as three characters,
626 * and the padding sequence `%3D` counts as one character per character. 670 * and the padding sequence `%3D` counts as one character per character.
627 * 671 *
628 * The number of missing characters is always between 0 and 5 because we 672 * The number of missing characters is always between 0 and 5 because we
629 * only call this function after having seen at least one `=` or `%` 673 * only call this function after having seen at least one `=` or `%`
630 * character. 674 * character.
631 * If the number of missing characters is not 3 or 0, we have seen (at least) 675 * If the number of missing characters is not 3 or 0, we have seen (at least)
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
704 end = RangeError.checkValidRange(start, end, string.length); 748 end = RangeError.checkValidRange(start, end, string.length);
705 if (start == end) return; 749 if (start == end) return;
706 Uint8List buffer = _decoder.decode(string, start, end); 750 Uint8List buffer = _decoder.decode(string, start, end);
707 if (buffer != null) _sink.add(buffer); 751 if (buffer != null) _sink.add(buffer);
708 if (isLast) { 752 if (isLast) {
709 _decoder.close(string, end); 753 _decoder.close(string, end);
710 _sink.close(); 754 _sink.close();
711 } 755 }
712 } 756 }
713 } 757 }
OLDNEW
« no previous file with comments | « CHANGELOG.md ('k') | tests/lib/convert/base64_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698