| 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 |