OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |