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

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

Issue 1159093002: Implement a Base64 codec (Closed) Base URL: https://github.com/dart-lang/crypto.git@master
Patch Set: 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
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
5 part of crypto; 1 part of crypto;
6 2
7 abstract class _CryptoUtils { 3 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.
8 static String bytesToHex(List<int> bytes) {
9 var result = new StringBuffer();
10 for (var part in bytes) {
11 result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}');
12 }
13 return result.toString();
14 }
15 4
16 static const int PAD = 61; // '=' 5 const List<int> _decodeTable =
17 static const int CR = 13; // '\r'
18 static const int LF = 10; // '\n'
19 static const int LINE_LENGTH = 76;
20
21 static const String _encodeTable =
22 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
23
24 static const String _encodeTableUrlSafe =
25 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
26
27 // Lookup table used for finding Base 64 alphabet index of a given byte.
28 // -2 : Outside Base 64 alphabet.
29 // -1 : '\r' or '\n'
30 // 0 : = (Padding character).
31 // >0 : Base 64 alphabet index of given byte.
32 static const List<int> _decodeTable =
33 const [ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -1, -2, -2, 6 const [ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -1, -2, -2,
34 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 7 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
35 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63, 8 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63,
36 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, 0, -2, -2, 9 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, 0, -2, -2,
37 -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 10 -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
38 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, 63, 11 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, 63,
39 -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 12 -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
40 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, 13 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
41 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 14 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
42 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 15 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
43 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 16 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
44 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 17 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
45 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 18 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
46 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 19 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
47 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 20 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
48 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 ]; 21 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 ];
49 22
50 static String bytesToBase64(List<int> bytes, 23 const String _encodeTableUrlSafe =
51 [bool urlSafe = false, 24 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
52 bool addLineSeparator = false]) { 25
26 const String _encodeTable =
27 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
28
29 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.
30 const int PAD = 61; // '='
31 const int CR = 13; // '\r'
32 const int LF = 10; // '\n'
33
34 class Base64Codec extends Codec<List<int>, String> {
35
36 final bool _urlSafe;
37 final bool _addLineSeparator;
38
39 /**
40 * 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.
41 *
42 */
43
Lasse Reichstein Nielsen 2015/05/29 06:48:42 No empty line here.
Alexander Ivanov 2015/05/29 13:12:05 Done.
44 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.
45 : _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.
46 _addLineSeparator = addLineSeparator;
47
48 String get name => "base64";
49
50 String encode(List<int> bytes,
51 { 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.
52 bool addLineSeparator }) {
53 if (urlSafe == null) urlSafe = _urlSafe;
54 if (addLineSeparator == null) addLineSeparator = _addLineSeparator;
55 return new Base64Encoder(urlSafe: urlSafe, addLineSeparator: addLineSeparato r)
Lasse Reichstein Nielsen 2015/05/29 06:48:42 Long line, break it somewhere.
Alexander Ivanov 2015/05/29 13:12:05 Done.
56 .convert(bytes);
57 }
58
59 Base64Encoder get encoder => new Base64Encoder(urlSafe: _urlSafe, addLineSepar ator: _addLineSeparator);
Lasse Reichstein Nielsen 2015/05/29 06:48:42 Long line.
Alexander Ivanov 2015/05/29 13:12:04 Done.
60 Base64Decoder get decoder => new Base64Decoder();
61
62 }
63
64
65 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.
66 final bool _urlSafe;
67 final bool _addLineSeparator;
68
69 const Base64Encoder({ bool urlSafe: false, bool addLineSeparator: false})
70 : _urlSafe = urlSafe,
71 _addLineSeparator = addLineSeparator;
72
73 String convert(List<int> bytes) {
53 int len = bytes.length; 74 int len = bytes.length;
54 if (len == 0) { 75 if (len == 0) {
55 return ""; 76 return "";
56 } 77 }
57 final String lookup = urlSafe ? _encodeTableUrlSafe : _encodeTable; 78 final String lookup = _urlSafe ? _encodeTableUrlSafe : _encodeTable;
58 // Size of 24 bit chunks. 79 // Size of 24 bit chunks.
59 final int remainderLength = len.remainder(3); 80 final int remainderLength = len.remainder(3);
60 final int chunkLength = len - remainderLength; 81 final int chunkLength = len - remainderLength;
61 // Size of base output. 82 // Size of base output.
62 int outputLen = ((len ~/ 3) * 4) + ((remainderLength > 0) ? 4 : 0); 83 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.
63 // Add extra for line separators. 84 // Add extra for line separators.
64 if (addLineSeparator) { 85 if (_addLineSeparator) {
65 outputLen += ((outputLen - 1) ~/ LINE_LENGTH) << 1; 86 outputLen += ((outputLen - 1) ~/ LINE_LENGTH) << 1;
66 } 87 }
67 List<int> out = new List<int>(outputLen); 88 List<int> out = new List<int>(outputLen);
68 89
69 // Encode 24 bit chunks. 90 // Encode 24 bit chunks.
70 int j = 0, i = 0, c = 0; 91 int j = 0, i = 0, c = 0;
71 while (i < chunkLength) { 92 while (i < chunkLength) {
72 int x = ((bytes[i++] << 16) & 0xFFFFFF) | 93 int x = ((bytes[i++] << 16) & 0xFFFFFF) |
73 ((bytes[i++] << 8) & 0xFFFFFF) | 94 ((bytes[i++] << 8) & 0xFFFFFF) |
74 bytes[i++]; 95 bytes[i++];
75 out[j++] = lookup.codeUnitAt(x >> 18); 96 out[j++] = lookup.codeUnitAt(x >> 18);
76 out[j++] = lookup.codeUnitAt((x >> 12) & 0x3F); 97 out[j++] = lookup.codeUnitAt((x >> 12) & 0x3F);
77 out[j++] = lookup.codeUnitAt((x >> 6) & 0x3F); 98 out[j++] = lookup.codeUnitAt((x >> 6) & 0x3F);
78 out[j++] = lookup.codeUnitAt(x & 0x3f); 99 out[j++] = lookup.codeUnitAt(x & 0x3f);
79 // Add optional line separator for each 76 char output. 100 // Add optional line separator for each 76 char output.
80 if (addLineSeparator && ++c == 19 && j < outputLen - 2) { 101 if (_addLineSeparator && ++c == 19 && j < outputLen - 2) {
81 out[j++] = CR; 102 out[j++] = CR;
82 out[j++] = LF; 103 out[j++] = LF;
83 c = 0; 104 c = 0;
84 } 105 }
85 } 106 }
86 107
87 // If input length if not a multiple of 3, encode remaining bytes and 108 // If input length if not a multiple of 3, encode remaining bytes and
88 // add padding. 109 // add padding.
89 if (remainderLength == 1) { 110 if (remainderLength == 1) {
90 int x = bytes[i]; 111 int x = bytes[i];
91 out[j++] = lookup.codeUnitAt(x >> 2); 112 out[j++] = lookup.codeUnitAt(x >> 2);
92 out[j++] = lookup.codeUnitAt((x << 4) & 0x3F); 113 out[j++] = lookup.codeUnitAt((x << 4) & 0x3F);
93 out[j++] = PAD; 114 out[j++] = PAD;
94 out[j++] = PAD; 115 out[j++] = PAD;
95 } else if (remainderLength == 2) { 116 } else if (remainderLength == 2) {
96 int x = bytes[i]; 117 int x = bytes[i];
97 int y = bytes[i + 1]; 118 int y = bytes[i + 1];
98 out[j++] = lookup.codeUnitAt(x >> 2); 119 out[j++] = lookup.codeUnitAt(x >> 2);
99 out[j++] = lookup.codeUnitAt(((x << 4) | (y >> 4)) & 0x3F); 120 out[j++] = lookup.codeUnitAt(((x << 4) | (y >> 4)) & 0x3F);
100 out[j++] = lookup.codeUnitAt((y << 2) & 0x3F); 121 out[j++] = lookup.codeUnitAt((y << 2) & 0x3F);
101 out[j++] = PAD; 122 out[j++] = PAD;
102 } 123 }
103 124
104 return new String.fromCharCodes(out); 125 return new String.fromCharCodes(out);
105 } 126 }
106 127
107 static List<int> base64StringToBytes(String input) { 128 _Base64EncoderSink startChunkedConversion(Sink<String> sink) {
129 StringConversionSink stringSink;
130 if (sink is StringConversionSink) {
131 stringSink = sink;
132 } else {
133 stringSink = new StringConversionSink.from(sink);
134 }
135 return new _Base64EncoderSink(stringSink, _urlSafe, _addLineSeparator);
136 }
137 }
138
139 class _Base64EncoderSink extends ChunkedConversionSink<List<int>> {
140
141 final Base64Encoder _encoder;
142 final ChunkedConversionSink<String> _outSink;
143 final List<int> _buffer = new List<int>(3);
144 int _buffer_count = 0;
Lasse Reichstein Nielsen 2015/05/29 07:15:16 _bufferCount.
Alexander Ivanov 2015/05/29 13:12:05 Done.
145
146 _Base64EncoderSink(this._outSink, urlSafe, addLineSeparator)
147 : _encoder = new Base64Encoder(urlSafe: urlSafe, addLineSeparator: addLineSe parator);
Lasse Reichstein Nielsen 2015/05/29 06:48:42 long line, indent : by four.
Alexander Ivanov 2015/05/29 13:12:05 Done.
148
149 void add(List<int> chunk) {
150
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.
151 var next_buffer_length = (chunk.length + _buffer_count) % 3;
152
153 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.
154 int remainder = chunk.length - next_buffer_length;
155 var decodable = new List.from(_buffer.sublist(0, _buffer_count))..addAll(c hunk.sublist(0, remainder));
Lasse Reichstein Nielsen 2015/05/29 06:48:42 long line
Alexander Ivanov 2015/05/29 13:12:05 Done.
156 _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.
157
158 _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.
159 }
160 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.
161 _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.
162 }
163 _buffer_count = next_buffer_length;
164 }
165
166 void close() {
167 if(_buffer_count > 0) {
168 _outSink.add(_encoder.convert(_buffer.sublist(0, _buffer_count)));
169 }
170 _outSink.close();
171 }
172 }
173
174 class Base64Decoder extends Converter<String, List<int>> {
175 const Base64Decoder();
176
177 List<int> convert(String input, {bool alwaysPadding: false}) {
108 int len = input.length; 178 int len = input.length;
109 if (len == 0) { 179 if (len == 0) {
110 return new List<int>(0); 180 return new List<int>(0);
111 } 181 }
112 182
113 // Count '\r', '\n' and illegal characters, For illegal characters, 183 // Count '\r', '\n' and illegal characters, For illegal characters,
114 // throw an exception. 184 // throw an exception.
115 int extrasLen = 0; 185 int extrasLen = 0;
116 for (int i = 0; i < len; i++) { 186 for (int i = 0; i < len; i++) {
117 int c = _decodeTable[input.codeUnitAt(i)]; 187 int c = _decodeTable[input.codeUnitAt(i)];
(...skipping 29 matching lines...) Expand all
147 x = ((x << 6) & 0xFFFFFF) | c; 217 x = ((x << 6) & 0xFFFFFF) | c;
148 j--; 218 j--;
149 } 219 }
150 } 220 }
151 out[o++] = x >> 16; 221 out[o++] = x >> 16;
152 if (o < outputLen) { 222 if (o < outputLen) {
153 out[o++] = (x >> 8) & 0xFF; 223 out[o++] = (x >> 8) & 0xFF;
154 if (o < outputLen) out[o++] = x & 0xFF; 224 if (o < outputLen) out[o++] = x & 0xFF;
155 } 225 }
156 } 226 }
227
157 return out; 228 return out;
158 } 229 }
159 230
231 _Base64DecoderSink startChunkedConversion(Sink<List<int>> sink) {
232 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.
233 sink = new ByteConversionSink.from(sink);
234 }
235 return new _Base64DecoderSink(sink);
236 }
160 } 237 }
238
239
240 class _Base64DecoderSink extends ChunkedConversionSink<String> {
241
242 final Base64Decoder _decoder = new Base64Decoder();
243 final ChunkedConversionSink<List<int>> _outSink;
244 String _buffer = "";
245
246 _Base64DecoderSink(this._outSink);
247
248 void add(String chunk) {
249 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.
250
251 if(chunk.length + _buffer.length >= 4) {
252 int remainder = chunk.length - next_buffer_length;
253 String decodable = _buffer + chunk.substring(0, remainder);
254 _buffer = chunk.substring(remainder);
255
256 _outSink.add(_decoder.convert(decodable));
257 }
258 else {
259 _buffer += chunk;
260 }
261 }
262
263 void close() {
264 if (!_buffer.isEmpty) {
265 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.
266 }
267 _outSink.close();
268 }
269 }
270
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698