Index: lib/src/base64.dart |
diff --git a/lib/src/crypto_utils.dart b/lib/src/base64.dart |
similarity index 51% |
copy from lib/src/crypto_utils.dart |
copy to lib/src/base64.dart |
index 515c1e95eaffa121ac3482497adb86d3efc1acfd..9e52cea20b27cb245d04b04fac59213625b556ad 100644 |
--- a/lib/src/crypto_utils.dart |
+++ b/lib/src/base64.dart |
@@ -1,35 +1,8 @@ |
-// 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; |
-abstract class _CryptoUtils { |
- static String bytesToHex(List<int> bytes) { |
- var result = new StringBuffer(); |
- for (var part in bytes) { |
- result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}'); |
- } |
- return result.toString(); |
- } |
- |
- static const int PAD = 61; // '=' |
- static const int CR = 13; // '\r' |
- static const int LF = 10; // '\n' |
- static const int LINE_LENGTH = 76; |
- |
- static const String _encodeTable = |
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
- |
- static const String _encodeTableUrlSafe = |
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; |
+const Base64Codec Base64 = const Base64Codec(); |
Lasse Reichstein Nielsen
2015/05/29 06:48:41
Name it either "BASE64" or "base64" (style guide f
Alexander Ivanov
2015/05/29 13:12:06
Done.
|
- // Lookup table used for finding Base 64 alphabet index of a given byte. |
- // -2 : Outside Base 64 alphabet. |
- // -1 : '\r' or '\n' |
- // 0 : = (Padding character). |
- // >0 : Base 64 alphabet index of given byte. |
- static const List<int> _decodeTable = |
+const List<int> _decodeTable = |
const [ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -1, -2, -2, |
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63, |
@@ -47,21 +20,69 @@ abstract class _CryptoUtils { |
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, |
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 ]; |
- static String bytesToBase64(List<int> bytes, |
- [bool urlSafe = false, |
- bool addLineSeparator = false]) { |
+const String _encodeTableUrlSafe = |
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; |
+ |
+const String _encodeTable = |
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
+ |
+const int LINE_LENGTH = 76; |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
These should probably be private.
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+const int PAD = 61; // '=' |
+const int CR = 13; // '\r' |
+const int LF = 10; // '\n' |
+ |
+class Base64Codec extends Codec<List<int>, String> { |
+ |
+ final bool _urlSafe; |
+ final bool _addLineSeparator; |
+ |
+ /** |
+ * Instantiates a new [Base64Codec]. |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
Document the parameters, say what they do and what
Alexander Ivanov
2015/05/29 13:12:04
Done.
|
+ * |
+ */ |
+ |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
No empty line here.
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ const Base64Codec({ bool urlSafe: false, bool addLineSeparator: false }) |
Lasse Reichstein Nielsen
2015/05/29 06:48:41
No space between "{" and "bool" (or before "}").
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ : _urlSafe = urlSafe, |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
indent four spaces before :, then one space after.
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ _addLineSeparator = addLineSeparator; |
+ |
+ String get name => "base64"; |
+ |
+ String encode(List<int> bytes, |
+ { bool urlSafe, |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
Again no space after '{' or before '}' (becaue sty
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ bool addLineSeparator }) { |
+ if (urlSafe == null) urlSafe = _urlSafe; |
+ if (addLineSeparator == null) addLineSeparator = _addLineSeparator; |
+ return new Base64Encoder(urlSafe: urlSafe, addLineSeparator: addLineSeparator) |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
Long line, break it somewhere.
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ .convert(bytes); |
+ } |
+ |
+ Base64Encoder get encoder => new Base64Encoder(urlSafe: _urlSafe, addLineSeparator: _addLineSeparator); |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
Long line.
Alexander Ivanov
2015/05/29 13:12:04
Done.
|
+ Base64Decoder get decoder => new Base64Decoder(); |
+ |
+} |
+ |
+ |
+class Base64Encoder extends Converter<List<int>, String> { |
Lasse Reichstein Nielsen
2015/05/29 07:15:15
Could generally use more comments, but ok for now.
Alexander Ivanov
2015/05/29 13:12:06
Done.
|
+ final bool _urlSafe; |
+ final bool _addLineSeparator; |
+ |
+ const Base64Encoder({ bool urlSafe: false, bool addLineSeparator: false}) |
+ : _urlSafe = urlSafe, |
+ _addLineSeparator = addLineSeparator; |
+ |
+ String convert(List<int> bytes) { |
int len = bytes.length; |
if (len == 0) { |
return ""; |
} |
- final String lookup = urlSafe ? _encodeTableUrlSafe : _encodeTable; |
+ final String lookup = _urlSafe ? _encodeTableUrlSafe : _encodeTable; |
// Size of 24 bit chunks. |
final int remainderLength = len.remainder(3); |
final int chunkLength = len - remainderLength; |
// Size of base output. |
int outputLen = ((len ~/ 3) * 4) + ((remainderLength > 0) ? 4 : 0); |
Lasse Reichstein Nielsen
2015/05/29 06:48:41
This seems too complex. Let's look at that later :
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
// Add extra for line separators. |
- if (addLineSeparator) { |
+ if (_addLineSeparator) { |
outputLen += ((outputLen - 1) ~/ LINE_LENGTH) << 1; |
} |
List<int> out = new List<int>(outputLen); |
@@ -77,7 +98,7 @@ abstract class _CryptoUtils { |
out[j++] = lookup.codeUnitAt((x >> 6) & 0x3F); |
out[j++] = lookup.codeUnitAt(x & 0x3f); |
// Add optional line separator for each 76 char output. |
- if (addLineSeparator && ++c == 19 && j < outputLen - 2) { |
+ if (_addLineSeparator && ++c == 19 && j < outputLen - 2) { |
out[j++] = CR; |
out[j++] = LF; |
c = 0; |
@@ -104,7 +125,56 @@ abstract class _CryptoUtils { |
return new String.fromCharCodes(out); |
} |
- static List<int> base64StringToBytes(String input) { |
+ _Base64EncoderSink startChunkedConversion(Sink<String> sink) { |
+ StringConversionSink stringSink; |
+ if (sink is StringConversionSink) { |
+ stringSink = sink; |
+ } else { |
+ stringSink = new StringConversionSink.from(sink); |
+ } |
+ return new _Base64EncoderSink(stringSink, _urlSafe, _addLineSeparator); |
+ } |
+} |
+ |
+class _Base64EncoderSink extends ChunkedConversionSink<List<int>> { |
+ |
+ final Base64Encoder _encoder; |
+ final ChunkedConversionSink<String> _outSink; |
+ final List<int> _buffer = new List<int>(3); |
+ int _buffer_count = 0; |
Lasse Reichstein Nielsen
2015/05/29 07:15:16
_bufferCount.
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ |
+ _Base64EncoderSink(this._outSink, urlSafe, addLineSeparator) |
+ : _encoder = new Base64Encoder(urlSafe: urlSafe, addLineSeparator: addLineSeparator); |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
long line, indent : by four.
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ |
+ void add(List<int> chunk) { |
+ |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
We usually don't have an empty line at the beginni
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ var next_buffer_length = (chunk.length + _buffer_count) % 3; |
+ |
+ if(chunk.length + _buffer_count >= 3) { |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
space between "if" and "(". More cases below.
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ int remainder = chunk.length - next_buffer_length; |
+ var decodable = new List.from(_buffer.sublist(0, _buffer_count))..addAll(chunk.sublist(0, remainder)); |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
long line
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ _buffer.setRange(0, next_buffer_length, chunk.sublist(remainder)); |
Lasse Reichstein Nielsen
2015/05/29 07:15:16
Use the fourth argument to setRange instead of cre
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ |
+ _outSink.add(_encoder.convert(decodable)); |
Lasse Reichstein Nielsen
2015/05/29 07:15:16
The current thing works, which is great, so this i
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ } |
+ else { |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
Else goes on the same line as '}'.
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ _buffer.setRange(_buffer_count, _buffer_count + chunk.length, chunk); |
Lasse Reichstein Nielsen
2015/05/29 07:15:16
_buffer_cunt + chunk.length => nextBufferLength
Alexander Ivanov
2015/05/29 13:12:04
Done.
|
+ } |
+ _buffer_count = next_buffer_length; |
+ } |
+ |
+ void close() { |
+ if(_buffer_count > 0) { |
+ _outSink.add(_encoder.convert(_buffer.sublist(0, _buffer_count))); |
+ } |
+ _outSink.close(); |
+ } |
+} |
+ |
+class Base64Decoder extends Converter<String, List<int>> { |
+ const Base64Decoder(); |
+ |
+ List<int> convert(String input, {bool alwaysPadding: false}) { |
int len = input.length; |
if (len == 0) { |
return new List<int>(0); |
@@ -154,7 +224,47 @@ abstract class _CryptoUtils { |
if (o < outputLen) out[o++] = x & 0xFF; |
} |
} |
+ |
return out; |
} |
+ _Base64DecoderSink startChunkedConversion(Sink<List<int>> sink) { |
+ if(sink is! ByteConversionSink) { |
Lasse Reichstein Nielsen
2015/05/29 06:48:41
Indent by 2 only.
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ sink = new ByteConversionSink.from(sink); |
+ } |
+ return new _Base64DecoderSink(sink); |
+ } |
} |
+ |
+ |
+class _Base64DecoderSink extends ChunkedConversionSink<String> { |
+ |
+ final Base64Decoder _decoder = new Base64Decoder(); |
+ final ChunkedConversionSink<List<int>> _outSink; |
+ String _buffer = ""; |
+ |
+ _Base64DecoderSink(this._outSink); |
+ |
+ void add(String chunk) { |
+ int next_buffer_length = (chunk.length + _buffer.length) % 4; |
Lasse Reichstein Nielsen
2015/05/29 06:48:42
We use camelCase for variables, so "nextBufferLeng
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ |
+ if(chunk.length + _buffer.length >= 4) { |
+ int remainder = chunk.length - next_buffer_length; |
+ String decodable = _buffer + chunk.substring(0, remainder); |
+ _buffer = chunk.substring(remainder); |
+ |
+ _outSink.add(_decoder.convert(decodable)); |
+ } |
+ else { |
+ _buffer += chunk; |
+ } |
+ } |
+ |
+ void close() { |
+ if (!_buffer.isEmpty) { |
+ throw new FormatException("Size of Base 64 input must be a multiple of 4 : ${_buffer.toString()}"); |
Lasse Reichstein Nielsen
2015/05/29 06:48:41
Use other arguments to FormatException (source and
Alexander Ivanov
2015/05/29 13:12:05
Done.
|
+ } |
+ _outSink.close(); |
+ } |
+} |
+ |