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

Side by Side Diff: lib/src/base64.dart

Issue 1169453002: Add an option for percent-encoding of the padding character to Base64Codec (Closed) Base URL: https://github.com/dart-lang/crypto.git@master
Patch Set: Fix formatting according to issue comments Created 5 years, 6 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 | « no previous file | test/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) 2012, the Dart project authors. Please see the AUTHORS file
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.
4
1 part of crypto; 5 part of crypto;
2 6
3 const Base64Codec BASE64 = const Base64Codec(); 7 const Base64Codec BASE64 = const Base64Codec();
4 8
5 const List<int> _decodeTable = 9 const List<int> _decodeTable =
6 const [ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -1, -2, -2, 10 const [ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -1, -2, -2,
7 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 11 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
8 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63, 12 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63,
9 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, 0, -2, -2, 13 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, 0, -2, -2,
10 -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14 -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
(...skipping 12 matching lines...) Expand all
23 const String _encodeTableUrlSafe = 27 const String _encodeTableUrlSafe =
24 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; 28 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
25 29
26 const String _encodeTable = 30 const String _encodeTable =
27 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 31 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
28 32
29 const List<String> _URL_SAFE_CHARACTERS = const ['+', '/']; 33 const List<String> _URL_SAFE_CHARACTERS = const ['+', '/'];
30 const List<String> _URL_UNSAFE_CHARACTERS = const ['-', '_']; 34 const List<String> _URL_UNSAFE_CHARACTERS = const ['-', '_'];
31 35
32 const int _LINE_LENGTH = 76; 36 const int _LINE_LENGTH = 76;
33 const int _PAD = 61; // '='
34 const int _CR = 13; // '\r' 37 const int _CR = 13; // '\r'
35 const int _LF = 10; // '\n' 38 const int _LF = 10; // '\n'
39 const List<int> _PAD_BYTES = const [61]; // '='
40 const List<int> _ENCODED_PAD_BYTES = const [37, 51, 68]; // '%3D'
41 const String _PAD = "=";
42 const String _ENCODED_PAD = "%3D";
36 43
37 class Base64Codec extends Codec<List<int>, String> { 44 class Base64Codec extends Codec<List<int>, String> {
38 45
39 final bool _urlSafe; 46 final bool _urlSafe;
40 final bool _addLineSeparator; 47 final bool _addLineSeparator;
48 final bool _encodePaddingCharacter;
41 49
42 /** 50 /**
43 * Instantiates a new [Base64Codec]. 51 * Instantiates a new [Base64Codec].
44 * 52 *
45 * The optional [urlSafe] argument specifies if [encoder] and [encode] 53 * The optional [urlSafe] argument specifies if [encoder] and [encode]
46 * should generate a string, that is safe to use in an URL. 54 * should generate a string, that is safe to use in an URL.
47 * 55 *
48 * If [urlSafe] is `true` (and not overriden at the method invocation) 56 * If [urlSafe] is `true` (and not overriden at the method invocation)
49 * the [encoder] and [encode] use '-' instead of '+' and '_' instead of '/'. 57 * the [encoder] and [encode] use '-' instead of '+' and '_' instead of '/'.
50 * 58 *
51 * The default value of [urlSafe] is `false`. 59 * The default value of [urlSafe] is `false`.
52 * 60 *
53 * The optional [addLineSeparator] argument specifies if the [encoder] and 61 * The optional [addLineSeparator] argument specifies if the [encoder] and
54 * [encode] should add line separators. 62 * [encode] should add line separators.
55 * 63 *
56 * If `addLineSeparator` is `true` [encode] adds an 64 * If `addLineSeparator` is `true` [encode] adds an
57 * optional line separator (CR + LF) for each 76 char output. 65 * optional line separator (CR + LF) for each 76 char output.
58 * 66 *
59 * The default value of [addLineSeparator] if `false`. 67 * The default value of [addLineSeparator] if `false`.
68 *
69 * If [encodePaddingCharacter] is `true` `encode` converts `=` to `%3D`.
70 * The default value of [encodePaddingCharacter] is `false`.
60 */ 71 */
61 const Base64Codec({bool urlSafe: false, bool addLineSeparator: false}) 72 const Base64Codec({bool urlSafe: false,
73 bool addLineSeparator: false,
74 bool encodePaddingCharacter: false})
62 : _urlSafe = urlSafe, 75 : _urlSafe = urlSafe,
63 _addLineSeparator = addLineSeparator; 76 _addLineSeparator = addLineSeparator,
77 _encodePaddingCharacter = encodePaddingCharacter;
64 78
65 String get name => "base64"; 79 String get name => "base64";
66 80
67 String encode(List<int> bytes, 81 String encode(List<int> bytes,
68 {bool urlSafe, 82 {bool urlSafe,
69 bool addLineSeparator}) { 83 bool addLineSeparator,
84 bool encodePaddingCharacter}) {
70 if (urlSafe == null) urlSafe = _urlSafe; 85 if (urlSafe == null) urlSafe = _urlSafe;
71 if (addLineSeparator == null) addLineSeparator = _addLineSeparator; 86 if (addLineSeparator == null) addLineSeparator = _addLineSeparator;
72 return new Base64Encoder(urlSafe: urlSafe, 87 if (encodePaddingCharacter == null) {
73 addLineSeparator: addLineSeparator).convert(bytes); 88 encodePaddingCharacter = _encodePaddingCharacter;
89 }
90 return new Base64Encoder(
91 urlSafe: urlSafe,
92 addLineSeparator: addLineSeparator,
93 encodePaddingCharacter: encodePaddingCharacter)
94 .convert(bytes);
74 95
75 96
76 } 97 }
77 98
78 Base64Encoder get encoder => new Base64Encoder( 99 Base64Encoder get encoder =>
79 urlSafe: _urlSafe, 100 new Base64Encoder(urlSafe: _urlSafe,
80 addLineSeparator: _addLineSeparator); 101 addLineSeparator: _addLineSeparator,
102 encodePaddingCharacter: _encodePaddingCharacter);
81 103
82 Base64Decoder get decoder => new Base64Decoder(); 104 Base64Decoder get decoder => new Base64Decoder();
83 105
84 } 106 }
85 107
86 /** 108 /**
87 * This class encodes byte strings (lists of unsigned 109 * This class encodes byte strings (lists of unsigned
88 * 8-bit integers) to strings according to Base64. 110 * 8-bit integers) to strings according to Base64.
89 */ 111 */
90 class Base64Encoder extends Converter<List<int>, String> { 112 class Base64Encoder extends Converter<List<int>, String> {
91 final bool _urlSafe; 113 final bool _urlSafe;
92 final bool _addLineSeparator; 114 final bool _addLineSeparator;
115 final bool _encodePaddingCharacter;
116 final List<int> _pad;
93 117
94 /** 118 /**
95 * Instantiates a new [Base64Encoder]. 119 * Instantiates a new [Base64Encoder].
96 * 120 *
97 * The optional [urlSafe] argument specifies if [convert] 121 * The optional [urlSafe] argument specifies if [convert]
98 * should generate a string, that is safe to use in an URL. 122 * should generate a string, that is safe to use in an URL.
99 * 123 *
100 * If it is `true` the [convert] use 124 * If it is `true` the [convert] use
101 * '-' instead of '+' and '_' instead of '/'. 125 * '-' instead of '+' and '_' instead of '/'.
102 * 126 *
103 * The default value of [urlSafe] is `false`. 127 * The default value of [urlSafe] is `false`.
104 * 128 *
105 * The optional [addLineSeparator] argument specifies if [convert] 129 * The optional [addLineSeparator] argument specifies if [convert]
106 * should add line separators. 130 * should add line separators.
107 * 131 *
108 * If it is `true` [convert] adds an optional line separator(CR + LF) 132 * If it is `true` [convert] adds an optional line separator(CR + LF)
109 * for each 76 char output. 133 * for each 76 char output.
110 * 134 *
111 * The default value of [addLineSeparator] if `false`. 135 * The default value of [addLineSeparator] if `false`.
136 *
137 * If [encodePaddingCharacter] is `true` `encode` converts `=` to `%3D`.
138 * The default value of [encodePaddingCharacter] is `false`.
112 */ 139 */
113 const Base64Encoder({bool urlSafe: false, bool addLineSeparator: false}) 140 const Base64Encoder({bool urlSafe: false,
141 bool addLineSeparator: false,
142 bool encodePaddingCharacter: false})
114 : _urlSafe = urlSafe, 143 : _urlSafe = urlSafe,
115 _addLineSeparator = addLineSeparator; 144 _addLineSeparator = addLineSeparator,
145 _encodePaddingCharacter = encodePaddingCharacter,
146 _pad = encodePaddingCharacter == true ? _ENCODED_PAD_BYTES : _PAD_BYTES;
116 147
117 /** 148 /**
118 * Converts [bytes] to its Base64 representation as a string. 149 * Converts [bytes] to its Base64 representation as a string.
119 * 150 *
120 * if [start] and [end] are provided, only the sublist 151 * if [start] and [end] are provided, only the sublist
121 * `bytes.sublist(start, end)` is converted. 152 * `bytes.sublist(start, end)` is converted.
122 */ 153 */
123
124 String convert(List<int> bytes, [int start = 0, int end]) { 154 String convert(List<int> bytes, [int start = 0, int end]) {
125 int bytes_length = bytes.length; 155 int bytes_length = bytes.length;
126 RangeError.checkValidRange(start, end, bytes_length); 156 RangeError.checkValidRange(start, end, bytes_length);
127 if (end == null) end = bytes_length; 157 if (end == null) end = bytes_length;
128 int length = end - start; 158 int length = end - start;
129 if (length == 0) { 159 if (length == 0) {
130 return ""; 160 return "";
131 } 161 }
132 final String lookup = _urlSafe ? _encodeTableUrlSafe : _encodeTable; 162 final String lookup = _urlSafe ? _encodeTableUrlSafe : _encodeTable;
133 // Size of 24 bit chunks. 163 // Size of 24 bit chunks.
134 final int remainderLength = length.remainder(3); 164 final int remainderLength = length.remainder(3);
135 final int chunkLength = length - remainderLength; 165 final int chunkLength = length - remainderLength;
136 // Size of base output. 166 // Size of base output.
137 int baseOutputLength = ((length ~/ 3) * 4); 167 int baseOutputLength = ((length ~/ 3) * 4);
138 int remainderOutputLength = ((remainderLength > 0) ? 4 : 0); 168 int remainderOutputLength;
169 if(_encodePaddingCharacter) {
170 remainderOutputLength = ((remainderLength > 0) ? 6 : 0);
171 } else {
172 remainderOutputLength = ((remainderLength > 0) ? 4 : 0);
173 }
174
139 int outputLength = baseOutputLength + remainderOutputLength; 175 int outputLength = baseOutputLength + remainderOutputLength;
140 // Add extra for line separators. 176 // Add extra for line separators.
141 if (_addLineSeparator) { 177 if (_addLineSeparator) {
142 outputLength += ((outputLength - 1) ~/ _LINE_LENGTH) << 1; 178 outputLength += ((outputLength - 1) ~/ _LINE_LENGTH) << 1;
143 } 179 }
144 List<int> out = new List<int>(outputLength); 180 List<int> out = new List<int>(outputLength);
145 181
146 // Encode 24 bit chunks. 182 // Encode 24 bit chunks.
147 int j = 0, i = start, c = 0; 183 int j = 0, i = start, c = 0;
148 while (i < chunkLength) { 184 while (i < chunkLength) {
(...skipping 11 matching lines...) Expand all
160 c = 0; 196 c = 0;
161 } 197 }
162 } 198 }
163 199
164 // If input length if not a multiple of 3, encode remaining bytes and 200 // If input length if not a multiple of 3, encode remaining bytes and
165 // add padding. 201 // add padding.
166 if (remainderLength == 1) { 202 if (remainderLength == 1) {
167 int x = bytes[i]; 203 int x = bytes[i];
168 out[j++] = lookup.codeUnitAt(x >> 2); 204 out[j++] = lookup.codeUnitAt(x >> 2);
169 out[j++] = lookup.codeUnitAt((x << 4) & 0x3F); 205 out[j++] = lookup.codeUnitAt((x << 4) & 0x3F);
170 out[j++] = _PAD; 206 out.setRange(j, j + _pad.length, _pad);
171 out[j++] = _PAD; 207 out.setRange(j + _pad.length, j + 2 * _pad.length, _pad);
172 } else if (remainderLength == 2) { 208 } else if (remainderLength == 2) {
173 int x = bytes[i]; 209 int x = bytes[i];
174 int y = bytes[i + 1]; 210 int y = bytes[i + 1];
175 out[j++] = lookup.codeUnitAt(x >> 2); 211 out[j++] = lookup.codeUnitAt(x >> 2);
176 out[j++] = lookup.codeUnitAt(((x << 4) | (y >> 4)) & 0x3F); 212 out[j++] = lookup.codeUnitAt(((x << 4) | (y >> 4)) & 0x3F);
177 out[j++] = lookup.codeUnitAt((y << 2) & 0x3F); 213 out[j++] = lookup.codeUnitAt((y << 2) & 0x3F);
178 out[j++] = _PAD; 214 out.setRange(j, j + _pad.length, _pad);
179 } 215 }
180 216
181 return new String.fromCharCodes(out); 217 return new String.fromCharCodes(out);
182 } 218 }
183 219
184 _Base64EncoderSink startChunkedConversion(Sink<String> sink) { 220 _Base64EncoderSink startChunkedConversion(Sink<String> sink) {
185 StringConversionSink stringSink; 221 StringConversionSink stringSink;
186 if (sink is StringConversionSink) { 222 if (sink is StringConversionSink) {
187 stringSink = sink; 223 stringSink = sink;
188 } else { 224 } else {
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
235 * This class decodes strings to lists of bytes(lists of 271 * This class decodes strings to lists of bytes(lists of
236 * unsigned 8-bit integers) according to Base64. 272 * unsigned 8-bit integers) according to Base64.
237 */ 273 */
238 class Base64Decoder extends Converter<String, List<int>> { 274 class Base64Decoder extends Converter<String, List<int>> {
239 275
240 /** 276 /**
241 * Instantiates a new [Base64Decoder] 277 * Instantiates a new [Base64Decoder]
242 */ 278 */
243 const Base64Decoder(); 279 const Base64Decoder();
244 280
245 List<int> convert(String input, {bool alwaysPadding: false}) { 281 List<int> convert(String input) {
246 int length = input.length; 282 int length = input.length;
247 if (length == 0) { 283 if (length == 0) {
248 return new List<int>(0); 284 return new List<int>(0);
249 } 285 }
250 286
287 bool expectedSafe = false;
288 bool expectedUnsafe = false;
289
290 int normalLength = 0;
291 int i = 0;
251 // Count '\r', '\n' and illegal characters, check if 292 // Count '\r', '\n' and illegal characters, check if
252 // '/', '+' / '-', '_' are used consistently, for illegal characters, 293 // '/', '+' / '-', '_' are used consistently, for illegal characters,
253 // throw an exception. 294 // throw an exception.
254 int extrasLength = 0;
255 bool expectedSafe = false;
256 bool expectedUnsafe = false;
257 295
258 for (int i = 0; i < length; i++) { 296 while (i < length) {
259 int c = _decodeTable[input.codeUnitAt(i)]; 297 int codeUnit = input.codeUnitAt(i);
260 if (c < 0) { 298 int c = _decodeTable[codeUnit];
261 extrasLength++; 299 if (c == -2) {
262 if (c == -2) { 300 if (codeUnit == _ENCODED_PAD_BYTES[0] &&
301 i < length - 2 &&
302 input.codeUnitAt(i + 1) == _ENCODED_PAD_BYTES[1] &&
303 input.codeUnitAt(i + 2) == _ENCODED_PAD_BYTES[2]) {
304 normalLength++;
305 i += 2;
306 } else {
263 throw new FormatException('Invalid character', input, i); 307 throw new FormatException('Invalid character', input, i);
264 } 308 }
265 } else if (input[i] == _URL_UNSAFE_CHARACTERS[0] || 309 } else if (input[i] == _URL_UNSAFE_CHARACTERS[0] ||
266 input[i] == _URL_UNSAFE_CHARACTERS[1]) { 310 input[i] == _URL_UNSAFE_CHARACTERS[1]) {
267 311
268 if (expectedSafe) { 312 if (expectedSafe) {
269 throw new FormatException('Unsafe character in URL-safe string', 313 throw new FormatException('Unsafe character in URL-safe string',
270 input, i); 314 input, i);
271 } 315 }
272 expectedUnsafe = true; 316 expectedUnsafe = true;
273 } else if (input[i] == _URL_SAFE_CHARACTERS[0] || 317 } else if (input[i] == _URL_SAFE_CHARACTERS[0] ||
274 input[i] == _URL_SAFE_CHARACTERS[1]) { 318 input[i] == _URL_SAFE_CHARACTERS[1]) {
275 if (expectedUnsafe) { 319 if (expectedUnsafe) {
276 throw new FormatException('Invalid character', input, i); 320 throw new FormatException('Invalid character', input, i);
277 } 321 }
278 expectedSafe = true; 322 expectedSafe = true;
279 } 323 }
324 if (c >= 0) normalLength++;
325 i++;
280 } 326 }
281 327
282 if ((length - extrasLength) % 4 != 0) { 328 if (normalLength % 4 != 0) {
283 throw new FormatException('''Size of Base 64 characters in Input 329 throw new FormatException('''Size of Base 64 characters in Input
284 must be a multiple of 4''', input, length - extrasLength); 330 must be a multiple of 4''', input, normalLength);
285 } 331 }
286 332
287 // Count pad characters. 333 // Count pad characters.
288 int padLength = 0; 334 int padLength = 0;
289 for (int i = length - 1; i >= 0; i--) { 335 i = length - 1;
336 while(i >= 0) {
290 int currentCodeUnit = input.codeUnitAt(i); 337 int currentCodeUnit = input.codeUnitAt(i);
291 if (_decodeTable[currentCodeUnit] > 0) break; 338 if (currentCodeUnit == _ENCODED_PAD_BYTES[2] &&
292 if (currentCodeUnit == _PAD) padLength++; 339 i >= 2 &&
340 input.codeUnitAt(i - 1) == _ENCODED_PAD_BYTES[1] &&
341 input.codeUnitAt(i - 2) == _ENCODED_PAD_BYTES[0]) {
342 padLength++;
343 i -= 2;
344 } else if (_decodeTable[currentCodeUnit] > 0) {
345 break;
346 } else if (currentCodeUnit == _PAD_BYTES[0]) {
347 padLength++;
348 }
349 i--;
293 } 350 }
294 int outputLength = (((length - extrasLength) * 6) >> 3) - padLength; 351 int outputLength = ((normalLength * 6) >> 3) - padLength;
295 List<int> out = new List<int>(outputLength); 352 List<int> out = new List<int>(outputLength);
296 353
297 for (int i = 0, o = 0; o < outputLength; ) { 354 for (int i = 0, o = 0; o < outputLength; ) {
298 // Accumulate 4 valid 6 bit Base 64 characters into an int. 355 // Accumulate 4 valid 6 bit Base 64 characters into an int.
299 int x = 0; 356 int x = 0;
300 for (int j = 4; j > 0; ) { 357 for (int j = 4; j > 0; ) {
301 int c = _decodeTable[input.codeUnitAt(i++)]; 358 int c = _decodeTable[input.codeUnitAt(i++)];
302 if (c >= 0) { 359 if (c >= 0) {
303 x = ((x << 6) & 0x00FFFFFF) | c; 360 x = ((x << 6) & 0x00FFFFFF) | c;
304 j--; 361 j--;
(...skipping 18 matching lines...) Expand all
323 } 380 }
324 381
325 382
326 class _Base64DecoderSink extends ChunkedConversionSink<String> { 383 class _Base64DecoderSink extends ChunkedConversionSink<String> {
327 384
328 final Base64Decoder _decoder = new Base64Decoder(); 385 final Base64Decoder _decoder = new Base64Decoder();
329 final ChunkedConversionSink<List<int>> _outSink; 386 final ChunkedConversionSink<List<int>> _outSink;
330 String _buffer = ""; 387 String _buffer = "";
331 bool _isSafe = false; 388 bool _isSafe = false;
332 bool _isUnsafe = false; 389 bool _isUnsafe = false;
390 int _expectPaddingCount = 3;
333 391
334 _Base64DecoderSink(this._outSink); 392 _Base64DecoderSink(this._outSink);
335 393
336 void add(String chunk) { 394 void add(String chunk) {
395 if (chunk.isEmpty) return;
396
337 int nextBufferLength = (chunk.length + _buffer.length) % 4; 397 int nextBufferLength = (chunk.length + _buffer.length) % 4;
338 398
399 if (chunk.length >= _expectPaddingCount &&
400 chunk.substring(0, _expectPaddingCount) ==
401 _ENCODED_PAD.substring(3 - _expectPaddingCount, 3)) {
402 chunk = _PAD + chunk.substring(_expectPaddingCount);
403 _expectPaddingCount = 3;
404 } else if(chunk.length < _expectPaddingCount &&
405 chunk == _ENCODED_PAD.substring(
406 3 - _expectPaddingCount,
407 3 - _expectPaddingCount + chunk.length)) {
408 _expectPaddingCount -= chunk.length;
409 chunk = "";
410 }
411
412 if (chunk.length > 1 &&
413 chunk[chunk.length - 2] == _ENCODED_PAD[0] &&
414 chunk[chunk.length - 1] == _ENCODED_PAD[1]) {
415 _expectPaddingCount = 1;
416 chunk = chunk.substring(0, chunk.length - 2);
417 } else if (!chunk.isEmpty && chunk[chunk.length - 1] == _ENCODED_PAD[0]) {
418 _expectPaddingCount = 2;
419 chunk = chunk.substring(0, chunk.length - 1);
420 }
421
422 chunk = chunk.replaceAll(_ENCODED_PAD, _PAD);
423
339 if (chunk.length + _buffer.length >= 4) { 424 if (chunk.length + _buffer.length >= 4) {
340 int remainder = chunk.length - nextBufferLength; 425 int remainder = chunk.length - nextBufferLength;
341 String decodable = _buffer + chunk.substring(0, remainder); 426 String decodable = _buffer + chunk.substring(0, remainder);
342 _buffer = chunk.substring(remainder); 427 _buffer = chunk.substring(remainder);
343 428
344 for (int i = 0;i < decodable.length; i++) { 429 for (int i = 0;i < decodable.length; i++) {
345 if (decodable[i] == _URL_UNSAFE_CHARACTERS[0] || 430 if (decodable[i] == _URL_UNSAFE_CHARACTERS[0] ||
346 decodable[i] == _URL_UNSAFE_CHARACTERS[1]) { 431 decodable[i] == _URL_UNSAFE_CHARACTERS[1]) {
347 if (_isSafe) { 432 if (_isSafe) {
348 throw new FormatException('Unsafe character in URL-safe string', 433 throw new FormatException('Unsafe character in URL-safe string',
349 decodable, i); 434 decodable, i);
350 } 435 }
351 _isUnsafe = true; 436 _isUnsafe = true;
352 } else if (decodable[i] == _URL_SAFE_CHARACTERS[0] || 437 } else if (decodable[i] == _URL_SAFE_CHARACTERS[0] ||
353 decodable[i] == _URL_SAFE_CHARACTERS[1]) { 438 decodable[i] == _URL_SAFE_CHARACTERS[1]) {
354 if (_isUnsafe) { 439 if (_isUnsafe) {
355 throw new FormatException('Invalid character', decodable, i); 440 throw new FormatException('Invalid character', decodable, i);
356 } 441 }
357 _isSafe = true; 442 _isSafe = true;
358 } 443 }
359 } 444 }
360 445
361 _outSink.add(_decoder.convert(decodable)); 446 _outSink.add(_decoder.convert(decodable));
362 } else { 447 } else {
363 _buffer += chunk; 448 _buffer += chunk;
364 } 449 }
365 } 450 }
366 451
367 void close() { 452 void close() {
368 if (!_buffer.isEmpty) { 453 if (_expectPaddingCount == 0 &&
454 _buffer.length == 3) {
455 _outSink.add(_buffer + _PAD);
456 } else if (_expectPaddingCount < 3 &&
457 _buffer.length + 3 - _expectPaddingCount == 4) {
458 _outSink.add(_buffer + _ENCODED_PAD.substring(0, 3 - _expectPaddingCount)) ;
459 } else if (_expectPaddingCount != 3 || !_buffer.isEmpty) {
369 throw new FormatException( 460 throw new FormatException(
370 "Size of Base 64 input must be a multiple of 4", 461 "Size of Base 64 input must be a multiple of 4",
371 _buffer, 462 _buffer + _PAD.substring(0, 3 - _expectPaddingCount),
372 _buffer.length); 463 _buffer.length + 3 - _expectPaddingCount);
373 } 464 }
374 _outSink.close(); 465 _outSink.close();
375 } 466 }
376 } 467 }
377 468
469
OLDNEW
« no previous file with comments | « no previous file | test/base64_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698