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

Unified 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, 7 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | test/base64_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/base64.dart
diff --git a/lib/src/base64.dart b/lib/src/base64.dart
index 7ccf4aa4fb97544666fe61e1034c1b7571030b8a..00ff11e84bd6cde58b3b23403f3cdaa05b6e47d3 100644
--- a/lib/src/base64.dart
+++ b/lib/src/base64.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of crypto;
const Base64Codec BASE64 = const Base64Codec();
@@ -30,14 +34,18 @@ const List<String> _URL_SAFE_CHARACTERS = const ['+', '/'];
const List<String> _URL_UNSAFE_CHARACTERS = const ['-', '_'];
const int _LINE_LENGTH = 76;
-const int _PAD = 61; // '='
const int _CR = 13; // '\r'
const int _LF = 10; // '\n'
+const List<int> _PAD_BYTES = const [61]; // '='
+const List<int> _ENCODED_PAD_BYTES = const [37, 51, 68]; // '%3D'
+const String _PAD = "=";
+const String _ENCODED_PAD = "%3D";
class Base64Codec extends Codec<List<int>, String> {
final bool _urlSafe;
final bool _addLineSeparator;
+ final bool _encodePaddingCharacter;
/**
* Instantiates a new [Base64Codec].
@@ -57,27 +65,41 @@ class Base64Codec extends Codec<List<int>, String> {
* optional line separator (CR + LF) for each 76 char output.
*
* The default value of [addLineSeparator] if `false`.
+ *
+ * If [encodePaddingCharacter] is `true` `encode` converts `=` to `%3D`.
+ * The default value of [encodePaddingCharacter] is `false`.
*/
- const Base64Codec({bool urlSafe: false, bool addLineSeparator: false})
+ const Base64Codec({bool urlSafe: false,
+ bool addLineSeparator: false,
+ bool encodePaddingCharacter: false})
: _urlSafe = urlSafe,
- _addLineSeparator = addLineSeparator;
+ _addLineSeparator = addLineSeparator,
+ _encodePaddingCharacter = encodePaddingCharacter;
String get name => "base64";
String encode(List<int> bytes,
{bool urlSafe,
- bool addLineSeparator}) {
+ bool addLineSeparator,
+ bool encodePaddingCharacter}) {
if (urlSafe == null) urlSafe = _urlSafe;
if (addLineSeparator == null) addLineSeparator = _addLineSeparator;
- return new Base64Encoder(urlSafe: urlSafe,
- addLineSeparator: addLineSeparator).convert(bytes);
+ if (encodePaddingCharacter == null) {
+ encodePaddingCharacter = _encodePaddingCharacter;
+ }
+ return new Base64Encoder(
+ urlSafe: urlSafe,
+ addLineSeparator: addLineSeparator,
+ encodePaddingCharacter: encodePaddingCharacter)
+ .convert(bytes);
}
- Base64Encoder get encoder => new Base64Encoder(
- urlSafe: _urlSafe,
- addLineSeparator: _addLineSeparator);
+ Base64Encoder get encoder =>
+ new Base64Encoder(urlSafe: _urlSafe,
+ addLineSeparator: _addLineSeparator,
+ encodePaddingCharacter: _encodePaddingCharacter);
Base64Decoder get decoder => new Base64Decoder();
@@ -90,6 +112,8 @@ class Base64Codec extends Codec<List<int>, String> {
class Base64Encoder extends Converter<List<int>, String> {
final bool _urlSafe;
final bool _addLineSeparator;
+ final bool _encodePaddingCharacter;
+ final List<int> _pad;
/**
* Instantiates a new [Base64Encoder].
@@ -109,10 +133,17 @@ class Base64Encoder extends Converter<List<int>, String> {
* for each 76 char output.
*
* The default value of [addLineSeparator] if `false`.
+ *
+ * If [encodePaddingCharacter] is `true` `encode` converts `=` to `%3D`.
+ * The default value of [encodePaddingCharacter] is `false`.
*/
- const Base64Encoder({bool urlSafe: false, bool addLineSeparator: false})
+ const Base64Encoder({bool urlSafe: false,
+ bool addLineSeparator: false,
+ bool encodePaddingCharacter: false})
: _urlSafe = urlSafe,
- _addLineSeparator = addLineSeparator;
+ _addLineSeparator = addLineSeparator,
+ _encodePaddingCharacter = encodePaddingCharacter,
+ _pad = encodePaddingCharacter == true ? _ENCODED_PAD_BYTES : _PAD_BYTES;
/**
* Converts [bytes] to its Base64 representation as a string.
@@ -120,7 +151,6 @@ class Base64Encoder extends Converter<List<int>, String> {
* if [start] and [end] are provided, only the sublist
* `bytes.sublist(start, end)` is converted.
*/
-
String convert(List<int> bytes, [int start = 0, int end]) {
int bytes_length = bytes.length;
RangeError.checkValidRange(start, end, bytes_length);
@@ -135,7 +165,13 @@ class Base64Encoder extends Converter<List<int>, String> {
final int chunkLength = length - remainderLength;
// Size of base output.
int baseOutputLength = ((length ~/ 3) * 4);
- int remainderOutputLength = ((remainderLength > 0) ? 4 : 0);
+ int remainderOutputLength;
+ if(_encodePaddingCharacter) {
+ remainderOutputLength = ((remainderLength > 0) ? 6 : 0);
+ } else {
+ remainderOutputLength = ((remainderLength > 0) ? 4 : 0);
+ }
+
int outputLength = baseOutputLength + remainderOutputLength;
// Add extra for line separators.
if (_addLineSeparator) {
@@ -167,15 +203,15 @@ class Base64Encoder extends Converter<List<int>, String> {
int x = bytes[i];
out[j++] = lookup.codeUnitAt(x >> 2);
out[j++] = lookup.codeUnitAt((x << 4) & 0x3F);
- out[j++] = _PAD;
- out[j++] = _PAD;
+ out.setRange(j, j + _pad.length, _pad);
+ out.setRange(j + _pad.length, j + 2 * _pad.length, _pad);
} else if (remainderLength == 2) {
int x = bytes[i];
int y = bytes[i + 1];
out[j++] = lookup.codeUnitAt(x >> 2);
out[j++] = lookup.codeUnitAt(((x << 4) | (y >> 4)) & 0x3F);
out[j++] = lookup.codeUnitAt((y << 2) & 0x3F);
- out[j++] = _PAD;
+ out.setRange(j, j + _pad.length, _pad);
}
return new String.fromCharCodes(out);
@@ -242,24 +278,32 @@ class Base64Decoder extends Converter<String, List<int>> {
*/
const Base64Decoder();
- List<int> convert(String input, {bool alwaysPadding: false}) {
+ List<int> convert(String input) {
int length = input.length;
if (length == 0) {
return new List<int>(0);
}
+ bool expectedSafe = false;
+ bool expectedUnsafe = false;
+
+ int normalLength = 0;
+ int i = 0;
// Count '\r', '\n' and illegal characters, check if
// '/', '+' / '-', '_' are used consistently, for illegal characters,
// throw an exception.
- int extrasLength = 0;
- bool expectedSafe = false;
- bool expectedUnsafe = false;
- for (int i = 0; i < length; i++) {
- int c = _decodeTable[input.codeUnitAt(i)];
- if (c < 0) {
- extrasLength++;
- if (c == -2) {
+ while (i < length) {
+ int codeUnit = input.codeUnitAt(i);
+ int c = _decodeTable[codeUnit];
+ if (c == -2) {
+ if (codeUnit == _ENCODED_PAD_BYTES[0] &&
+ i < length - 2 &&
+ input.codeUnitAt(i + 1) == _ENCODED_PAD_BYTES[1] &&
+ input.codeUnitAt(i + 2) == _ENCODED_PAD_BYTES[2]) {
+ normalLength++;
+ i += 2;
+ } else {
throw new FormatException('Invalid character', input, i);
}
} else if (input[i] == _URL_UNSAFE_CHARACTERS[0] ||
@@ -277,21 +321,34 @@ class Base64Decoder extends Converter<String, List<int>> {
}
expectedSafe = true;
}
+ if (c >= 0) normalLength++;
+ i++;
}
- if ((length - extrasLength) % 4 != 0) {
+ if (normalLength % 4 != 0) {
throw new FormatException('''Size of Base 64 characters in Input
- must be a multiple of 4''', input, length - extrasLength);
+ must be a multiple of 4''', input, normalLength);
}
// Count pad characters.
int padLength = 0;
- for (int i = length - 1; i >= 0; i--) {
+ i = length - 1;
+ while(i >= 0) {
int currentCodeUnit = input.codeUnitAt(i);
- if (_decodeTable[currentCodeUnit] > 0) break;
- if (currentCodeUnit == _PAD) padLength++;
+ if (currentCodeUnit == _ENCODED_PAD_BYTES[2] &&
+ i >= 2 &&
+ input.codeUnitAt(i - 1) == _ENCODED_PAD_BYTES[1] &&
+ input.codeUnitAt(i - 2) == _ENCODED_PAD_BYTES[0]) {
+ padLength++;
+ i -= 2;
+ } else if (_decodeTable[currentCodeUnit] > 0) {
+ break;
+ } else if (currentCodeUnit == _PAD_BYTES[0]) {
+ padLength++;
+ }
+ i--;
}
- int outputLength = (((length - extrasLength) * 6) >> 3) - padLength;
+ int outputLength = ((normalLength * 6) >> 3) - padLength;
List<int> out = new List<int>(outputLength);
for (int i = 0, o = 0; o < outputLength; ) {
@@ -330,12 +387,40 @@ class _Base64DecoderSink extends ChunkedConversionSink<String> {
String _buffer = "";
bool _isSafe = false;
bool _isUnsafe = false;
+ int _expectPaddingCount = 3;
_Base64DecoderSink(this._outSink);
void add(String chunk) {
+ if (chunk.isEmpty) return;
+
int nextBufferLength = (chunk.length + _buffer.length) % 4;
+ if (chunk.length >= _expectPaddingCount &&
+ chunk.substring(0, _expectPaddingCount) ==
+ _ENCODED_PAD.substring(3 - _expectPaddingCount, 3)) {
+ chunk = _PAD + chunk.substring(_expectPaddingCount);
+ _expectPaddingCount = 3;
+ } else if(chunk.length < _expectPaddingCount &&
+ chunk == _ENCODED_PAD.substring(
+ 3 - _expectPaddingCount,
+ 3 - _expectPaddingCount + chunk.length)) {
+ _expectPaddingCount -= chunk.length;
+ chunk = "";
+ }
+
+ if (chunk.length > 1 &&
+ chunk[chunk.length - 2] == _ENCODED_PAD[0] &&
+ chunk[chunk.length - 1] == _ENCODED_PAD[1]) {
+ _expectPaddingCount = 1;
+ chunk = chunk.substring(0, chunk.length - 2);
+ } else if (!chunk.isEmpty && chunk[chunk.length - 1] == _ENCODED_PAD[0]) {
+ _expectPaddingCount = 2;
+ chunk = chunk.substring(0, chunk.length - 1);
+ }
+
+ chunk = chunk.replaceAll(_ENCODED_PAD, _PAD);
+
if (chunk.length + _buffer.length >= 4) {
int remainder = chunk.length - nextBufferLength;
String decodable = _buffer + chunk.substring(0, remainder);
@@ -365,13 +450,20 @@ class _Base64DecoderSink extends ChunkedConversionSink<String> {
}
void close() {
- if (!_buffer.isEmpty) {
+ if (_expectPaddingCount == 0 &&
+ _buffer.length == 3) {
+ _outSink.add(_buffer + _PAD);
+ } else if (_expectPaddingCount < 3 &&
+ _buffer.length + 3 - _expectPaddingCount == 4) {
+ _outSink.add(_buffer + _ENCODED_PAD.substring(0, 3 - _expectPaddingCount));
+ } else if (_expectPaddingCount != 3 || !_buffer.isEmpty) {
throw new FormatException(
- "Size of Base 64 input must be a multiple of 4",
- _buffer,
- _buffer.length);
+ "Size of Base 64 input must be a multiple of 4",
+ _buffer + _PAD.substring(0, 3 - _expectPaddingCount),
+ _buffer.length + 3 - _expectPaddingCount);
}
_outSink.close();
}
}
+
« 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