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

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

Issue 1349373002: Improve the style of code in lib/. (Closed) Base URL: git@github.com:dart-lang/crypto.git@master
Patch Set: Code review changes Created 5 years, 3 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 | « lib/crypto.dart ('k') | lib/src/base64/decoder.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 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 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 library crypto.base64; 5 library crypto.base64;
6 6
7 import 'dart:convert'; 7 import 'dart:convert';
8 import 'dart:typed_data'; 8
9 import 'base64/decoder.dart';
10 import 'base64/encoder.dart';
9 11
10 /// An instance of the default implementation of [Base64Codec]. 12 /// An instance of the default implementation of [Base64Codec].
11 /// 13 ///
12 /// This provides convenient access to most common Base64 use-cases. 14 /// This provides convenient access to most common Base64 use-cases.
13 const Base64Codec BASE64 = const Base64Codec(); 15 const Base64Codec BASE64 = const Base64Codec();
14 16
15 /// A mapping from ASCII character codes to their corresponding Base64 values.
16 ///
17 /// Characters with a value of -2 are invalid. Characters with a value of -1
18 /// should be ignored. The padding character, "=", is represented as 0.
19 const List<int> _decodeTable = const [
20 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -1, -2, -2,
21 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
22 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63,
23 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, 0, -2, -2,
24 -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
25 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, 63,
26 -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
27 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
28 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
29 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
30 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
31 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
32 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
33 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
34 -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, -2, -2, -2, -2, -2
36 ];
37
38 /// A String representing a mapping from numbers between 0 and 63, inclusive, to
39 /// their corresponding encoded character.
40 ///
41 /// This is the table for URL-safe encodings.
42 const String _encodeTableUrlSafe =
43 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
44
45 /// A String representing a mapping from numbers between 0 and 63, inclusive, to
46 /// their corresponding encoded character.
47 ///
48 /// This is the table for URL-unsafe encodings.
49 const String _encodeTable =
50 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
51
52 /// The line length for Base64 strings with line separators.
53 const int _LINE_LENGTH = 76;
54
55 /// A carriage return.
56 const int _CR = 13; // '\r'
57
58 /// A line feed.
59 const int _LF = 10; // '\n'
60
61 /// The byte sequence representing non-URL-encoded padding.
62 const List<int> _PAD_BYTES = const [61]; // '='
63
64 /// The byte sequence representing URL-encoded padding.
65 const List<int> _ENCODED_PAD_BYTES = const [37, 51, 68]; // '%3D'
66
67 /// The string representing non-URL-encoded padding.
68 const String _PAD = "=";
69
70 /// The string representing URL-encoded padding.
71 const String _ENCODED_PAD = "%3D";
72
73 /// A codec that converts between binary data and [Base64][rfc]-encoded strings. 17 /// A codec that converts between binary data and [Base64][rfc]-encoded strings.
74 /// 18 ///
75 /// [rfc]: https://tools.ietf.org/html/rfc4648 19 /// [rfc]: https://tools.ietf.org/html/rfc4648
76 class Base64Codec extends Codec<List<int>, String> { 20 class Base64Codec extends Codec<List<int>, String> {
77 final bool _urlSafe; 21 final bool _urlSafe;
78 final bool _addLineSeparator; 22 final bool _addLineSeparator;
79 final bool _encodePaddingCharacter; 23 final bool _encodePaddingCharacter;
80 24
81 /// Creates a new [Base64Codec]. 25 /// Creates a new [Base64Codec].
82 /// 26 ///
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
117 /// 61 ///
118 /// Any flags passed to this method take precedence over the flags passed to 62 /// Any flags passed to this method take precedence over the flags passed to
119 /// the codec itself. 63 /// the codec itself.
120 String encode(List<int> bytes, 64 String encode(List<int> bytes,
121 {bool urlSafe, bool addLineSeparator, bool encodePaddingCharacter}) { 65 {bool urlSafe, bool addLineSeparator, bool encodePaddingCharacter}) {
122 if (urlSafe == null) urlSafe = _urlSafe; 66 if (urlSafe == null) urlSafe = _urlSafe;
123 if (addLineSeparator == null) addLineSeparator = _addLineSeparator; 67 if (addLineSeparator == null) addLineSeparator = _addLineSeparator;
124 if (encodePaddingCharacter == null) { 68 if (encodePaddingCharacter == null) {
125 encodePaddingCharacter = _encodePaddingCharacter; 69 encodePaddingCharacter = _encodePaddingCharacter;
126 } 70 }
71
127 return new Base64Encoder( 72 return new Base64Encoder(
128 urlSafe: urlSafe, 73 urlSafe: urlSafe,
129 addLineSeparator: addLineSeparator, 74 addLineSeparator: addLineSeparator,
130 encodePaddingCharacter: encodePaddingCharacter).convert(bytes); 75 encodePaddingCharacter: encodePaddingCharacter).convert(bytes);
131 } 76 }
132 77
133 Base64Encoder get encoder => new Base64Encoder( 78 Base64Encoder get encoder => new Base64Encoder(
134 urlSafe: _urlSafe, 79 urlSafe: _urlSafe,
135 addLineSeparator: _addLineSeparator, 80 addLineSeparator: _addLineSeparator,
136 encodePaddingCharacter: _encodePaddingCharacter); 81 encodePaddingCharacter: _encodePaddingCharacter);
137 82
138 Base64Decoder get decoder => new Base64Decoder(); 83 Base64Decoder get decoder => const Base64Decoder();
139 } 84 }
140
141 /// An encoder that converts sequences of bytes to strings using [Base64][rfc].
142 ///
143 /// [rfc]: https://tools.ietf.org/html/rfc4648
144 class Base64Encoder extends Converter<List<int>, String> {
145 /// Whether this encoder generates URL-safe strings.
146 final bool _urlSafe;
147
148 /// Whether this encoder adds line breaks to the output.
149 final bool _addLineSeparator;
150
151 /// Whether this encoder URL-encodes trailing padding characters.
152 final bool _encodePaddingCharacter;
153
154 /// The sequence of bytes to use as a padding character.
155 final List<int> _pad;
156
157 /// Creates a new [Base64Encoder].
158 ///
159 /// The default [BASE64.encoder] will be good enough for most cases. A new
160 /// codec only needs to be instantiated when you want to do multiple
161 /// conversions with the same configuration.
162 ///
163 /// If [urlSafe] is `true`, a URL-safe alphabet will be used. Specifically,
164 /// the characters `-` and `_` will be used instead of `+` and `/`.
165 ///
166 /// If [addLineSeparator] is `true`, `\r\n` line separators will be added
167 /// every 76 characters.
168 ///
169 /// If [encodePaddingCharacter] is `true`, the padding character `=` will be
170 /// written as `%3D`.
171 const Base64Encoder(
172 {bool urlSafe: false,
173 bool addLineSeparator: false,
174 bool encodePaddingCharacter: false})
175 : _urlSafe = urlSafe,
176 _addLineSeparator = addLineSeparator,
177 _encodePaddingCharacter = encodePaddingCharacter,
178 _pad = encodePaddingCharacter == true ? _ENCODED_PAD_BYTES : _PAD_BYTES;
179
180 /// Converts [bytes] to Base64.
181 ///
182 /// If [start] and [end] are provided, only the sublist `bytes.sublist(start,
183 /// end)` is converted.
184 String convert(List<int> bytes, [int start = 0, int end]) {
185 int bytes_length = bytes.length;
186 RangeError.checkValidRange(start, end, bytes_length);
187 if (end == null) end = bytes_length;
188 int length = end - start;
189 if (length == 0) {
190 return "";
191 }
192 final String lookup = _urlSafe ? _encodeTableUrlSafe : _encodeTable;
193 // Size of 24 bit chunks.
194 final int remainderLength = length.remainder(3);
195 final int chunkLength = length - remainderLength;
196 // Size of base output.
197 int baseOutputLength = ((length ~/ 3) * 4);
198 int remainderOutputLength;
199 if (_encodePaddingCharacter) {
200 remainderOutputLength = ((remainderLength > 0) ? 6 : 0);
201 } else {
202 remainderOutputLength = ((remainderLength > 0) ? 4 : 0);
203 }
204
205 int outputLength = baseOutputLength + remainderOutputLength;
206 // Add extra for line separators.
207 if (_addLineSeparator) {
208 outputLength += ((outputLength - 1) ~/ _LINE_LENGTH) << 1;
209 }
210 List<int> out = new List<int>(outputLength);
211
212 // Encode 24 bit chunks.
213 int j = 0, i = start, c = 0;
214 while (i < chunkLength) {
215 int x = ((bytes[i++] << 16) & 0x00FFFFFF) |
216 ((bytes[i++] << 8) & 0x00FFFFFF) |
217 bytes[i++];
218 out[j++] = lookup.codeUnitAt(x >> 18);
219 out[j++] = lookup.codeUnitAt((x >> 12) & 0x3F);
220 out[j++] = lookup.codeUnitAt((x >> 6) & 0x3F);
221 out[j++] = lookup.codeUnitAt(x & 0x3F);
222 // Add optional line separator for each 76 char output.
223 if (_addLineSeparator && ++c == 19 && j < outputLength - 2) {
224 out[j++] = _CR;
225 out[j++] = _LF;
226 c = 0;
227 }
228 }
229
230 // If input length if not a multiple of 3, encode remaining bytes and
231 // add padding.
232 if (remainderLength == 1) {
233 int x = bytes[i];
234 out[j++] = lookup.codeUnitAt(x >> 2);
235 out[j++] = lookup.codeUnitAt((x << 4) & 0x3F);
236 out.setRange(j, j + _pad.length, _pad);
237 out.setRange(j + _pad.length, j + 2 * _pad.length, _pad);
238 } else if (remainderLength == 2) {
239 int x = bytes[i];
240 int y = bytes[i + 1];
241 out[j++] = lookup.codeUnitAt(x >> 2);
242 out[j++] = lookup.codeUnitAt(((x << 4) | (y >> 4)) & 0x3F);
243 out[j++] = lookup.codeUnitAt((y << 2) & 0x3F);
244 out.setRange(j, j + _pad.length, _pad);
245 }
246
247 return new String.fromCharCodes(out);
248 }
249
250 _Base64EncoderSink startChunkedConversion(Sink<String> sink) {
251 StringConversionSink stringSink;
252 if (sink is StringConversionSink) {
253 stringSink = sink;
254 } else {
255 stringSink = new StringConversionSink.from(sink);
256 }
257 return new _Base64EncoderSink(stringSink, _urlSafe, _addLineSeparator);
258 }
259 }
260
261 /// A [ChunkedConversionSink] for encoding chunks of data to Base64.
262 class _Base64EncoderSink extends ChunkedConversionSink<List<int>> {
263 /// The encoder used to encode each chunk.
264 final Base64Encoder _encoder;
265
266 /// The underlying sink to which to emit the encoded strings.
267 final ChunkedConversionSink<String> _outSink;
268
269 /// The buffer of as-yet-unconverted bytes.
270 ///
271 /// This is used to ensure that we don't generate interstitial padding
272 /// characters.
273 final List<int> _buffer = new List<int>();
274
275 /// The length of [_buffer]; that is, the number of unconverted bytes.
276 int _bufferCount = 0;
277
278 _Base64EncoderSink(this._outSink, urlSafe, addLineSeparator)
279 : _encoder = new Base64Encoder(
280 urlSafe: urlSafe, addLineSeparator: addLineSeparator);
281
282 void add(List<int> chunk) {
283 var nextBufferCount = (chunk.length + _bufferCount) % 3;
284
285 int decodableLength = _bufferCount + chunk.length - nextBufferCount;
286
287 if (_bufferCount + chunk.length > _buffer.length) {
288 _buffer.replaceRange(_bufferCount, _buffer.length,
289 chunk.sublist(0, _buffer.length - _bufferCount));
290 _buffer.addAll(chunk.sublist(_buffer.length - _bufferCount));
291 } else {
292 _buffer.replaceRange(_bufferCount, _bufferCount + chunk.length, chunk);
293 }
294
295 _outSink.add(_encoder.convert(_buffer, 0, decodableLength));
296 _buffer.removeRange(0, decodableLength);
297 _bufferCount = nextBufferCount;
298 }
299
300 void close() {
301 if (_bufferCount > 0) {
302 _outSink.add(_encoder.convert(_buffer.sublist(0, _bufferCount)));
303 }
304 _outSink.close();
305 }
306 }
307
308 /// An encoder that converts [Base64][rfc] strings to sequences of bytes.
309 ///
310 /// [rfc]: https://tools.ietf.org/html/rfc4648
311 class Base64Decoder extends Converter<String, List<int>> {
312 const Base64Decoder();
313
314 List<int> convert(String input) {
315 int length = input.length;
316 if (length == 0) {
317 return new Uint8List(0);
318 }
319
320 int normalLength = 0;
321 int i = 0;
322 // Count '\r', '\n' and illegal characters, check if
323 // '/', '+' / '-', '_' are used consistently, for illegal characters,
324 // throw an exception.
325
326 while (i < length) {
327 int codeUnit = input.codeUnitAt(i);
328 int c = _decodeTable[codeUnit];
329 if (c == -2) {
330 if (codeUnit == _ENCODED_PAD_BYTES[0] &&
331 i < length - 2 &&
332 input.codeUnitAt(i + 1) == _ENCODED_PAD_BYTES[1] &&
333 input.codeUnitAt(i + 2) == _ENCODED_PAD_BYTES[2]) {
334 normalLength++;
335 i += 2;
336 } else {
337 throw new FormatException('Invalid character', input, i);
338 }
339 }
340 if (c >= 0) normalLength++;
341 i++;
342 }
343
344 if (normalLength % 4 != 0) {
345 throw new FormatException(
346 '''Size of Base 64 characters in Input
347 must be a multiple of 4''',
348 input,
349 normalLength);
350 }
351
352 // Count pad characters.
353 int padLength = 0;
354 i = length - 1;
355 while (i >= 0) {
356 int currentCodeUnit = input.codeUnitAt(i);
357 if (currentCodeUnit == _ENCODED_PAD_BYTES[2] &&
358 i >= 2 &&
359 input.codeUnitAt(i - 1) == _ENCODED_PAD_BYTES[1] &&
360 input.codeUnitAt(i - 2) == _ENCODED_PAD_BYTES[0]) {
361 padLength++;
362 i -= 2;
363 } else if (_decodeTable[currentCodeUnit] > 0) {
364 break;
365 } else if (currentCodeUnit == _PAD_BYTES[0]) {
366 padLength++;
367 }
368 i--;
369 }
370 int outputLength = ((normalLength * 6) >> 3) - padLength;
371 List<int> out = new Uint8List(outputLength);
372
373 for (int i = 0, o = 0; o < outputLength;) {
374 // Accumulate 4 valid 6 bit Base 64 characters into an int.
375 int x = 0;
376 for (int j = 4; j > 0;) {
377 int c = _decodeTable[input.codeUnitAt(i++)];
378 if (c >= 0) {
379 x = ((x << 6) & 0x00FFFFFF) | c;
380 j--;
381 }
382 }
383 out[o++] = x >> 16;
384 if (o < outputLength) {
385 out[o++] = (x >> 8) & 0xFF;
386 if (o < outputLength) out[o++] = x & 0xFF;
387 }
388 }
389
390 return out;
391 }
392
393 _Base64DecoderSink startChunkedConversion(Sink<List<int>> sink) {
394 if (sink is! ByteConversionSink) {
395 sink = new ByteConversionSink.from(sink);
396 }
397 return new _Base64DecoderSink(sink);
398 }
399 }
400
401 /// A [ChunkedConversionSink] for decoding chunks of Base64 strings to data.
402 class _Base64DecoderSink extends ChunkedConversionSink<String> {
403 /// The encoder used to decode each chunk.
404 final Base64Decoder _decoder = new Base64Decoder();
405
406 /// The underlying sink to which to emit the decoded strings.
407 final ChunkedConversionSink<List<int>> _outSink;
408
409 /// The as-yet-unconverted text.
410 ///
411 /// This is used to handle a chunk stopping partway in the middle of a
412 /// URL-encoded `=` character.
413 String _unconverted = "";
414
415 _Base64DecoderSink(this._outSink);
416
417 void add(String chunk) {
418 if (chunk.isEmpty) return;
419 if (_unconverted.isNotEmpty) {
420 chunk = _unconverted + chunk;
421 }
422 chunk = chunk.replaceAll(_ENCODED_PAD, _PAD);
423 int decodableLength = chunk.length;
424 // If chunk ends in "%" or "%3", it may be a partial encoded pad.
425 // If chunk is smaller than 4 characters, don't bother checking.
426 if (chunk.length > 3 && chunk.contains(_ENCODED_PAD[0], chunk.length - 2)) {
427 decodableLength = chunk.lastIndexOf(_ENCODED_PAD[0]);
428 }
429 decodableLength -= decodableLength % 4;
430 _unconverted = chunk.substring(decodableLength);
431 if (decodableLength > 0) {
432 _outSink.add(_decoder.convert(chunk.substring(0, decodableLength)));
433 }
434 }
435
436 void close() {
437 if (_unconverted.isNotEmpty) {
438 _outSink.add(_decoder.convert(_unconverted));
439 }
440 _outSink.close();
441 }
442 }
OLDNEW
« no previous file with comments | « lib/crypto.dart ('k') | lib/src/base64/decoder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698