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

Side by Side Diff: sdk/lib/convert/base64.dart

Issue 2694373003: Normalize UriData.parse result. (Closed)
Patch Set: Merge to head. Created 3 years, 10 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) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, 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 part of dart.convert; 5 part of dart.convert;
6 6
7 /** 7 /**
8 * A [base64](https://tools.ietf.org/html/rfc4648) encoder and decoder. 8 * A [base64](https://tools.ietf.org/html/rfc4648) encoder and decoder.
9 * 9 *
10 * It encodes using the default base64 alphabet, 10 * It encodes using the default base64 alphabet,
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
50 * characters. 50 * characters.
51 */ 51 */
52 class Base64Codec extends Codec<List<int>, String> { 52 class Base64Codec extends Codec<List<int>, String> {
53 final Base64Encoder _encoder; 53 final Base64Encoder _encoder;
54 const Base64Codec() : _encoder = const Base64Encoder(); 54 const Base64Codec() : _encoder = const Base64Encoder();
55 const Base64Codec.urlSafe() : _encoder = const Base64Encoder.urlSafe(); 55 const Base64Codec.urlSafe() : _encoder = const Base64Encoder.urlSafe();
56 56
57 Base64Encoder get encoder => _encoder; 57 Base64Encoder get encoder => _encoder;
58 58
59 Base64Decoder get decoder => const Base64Decoder(); 59 Base64Decoder get decoder => const Base64Decoder();
60
61 /**
62 * Validates and normalizes the base64 encoded data in [source].
63 *
64 * Only acts on the substring from [start] to [end], with [end]
65 * defaulting to the end of the string.
66 *
67 * Normalization will:
68 * * Unescape any `%`-escapes.
69 * * Only allow valid characters (`A`-`Z`, `a`-`z`, `0`-`9`, `/` and `+`).
70 * * Normalize a `_` or `-` character to `/` or `+`.
71 * * Validate that existing padding (trailing `=` characters) is correct.
72 * * If no padding exists, add correct padding if necessary and possible.
73 * * Validate that the length is correct (a multiplum of four).
floitsch 2017/02/15 17:03:09 multiple ?
Lasse Reichstein Nielsen 2017/02/17 11:22:09 Done.
74 */
75 String normalize(String source, [int start = 0, int end]) {
76 end = RangeError.checkValidRange(start, end, source.length);
77 const int percent = 0x25;
78 const int equals = 0x3d;
79 StringBuffer buffer = null;
80 int sliceStart = start;
81 var alphabet = _Base64Encoder._base64Alphabet;
82 var inverseAlphabet = _Base64Decoder._inverseAlphabet;
83 int firstPadding = -1;
84 int firstPaddingSourceIndex = -1;
85 int paddingCount = 0;
86 for (int i = start; i < end;) {
87 int sliceEnd = i;
88 int char = source.codeUnitAt(i++);
89 int originalChar = char;
90 // Normalize char, keep originalChar to see if it matches the source.
91 if (char == percent) {
92 originalChar = -1;
floitsch 2017/02/15 17:03:09 Add comment explaining that you pick '-1' because
Lasse Reichstein Nielsen 2017/02/17 11:22:09 It's actually not necessary to set it at all - a '
93 if (i + 2 <= end) {
94 char = parseHexByte(source, i); // May be negative.
95 i += 2;
96 } else {
97 char = 0; // Invalid.
floitsch 2017/02/15 17:03:09 If this is invalid, why it is set to 0, and not `_
Lasse Reichstein Nielsen 2017/02/17 11:22:09 I'll set it to -1, so it'll just fall through to t
98 }
99 }
100 if (0 <= char && char <= 127) {
101 int value = inverseAlphabet[char];
102 if (value >= 0) {
103 char = alphabet.codeUnitAt(value);
104 if (char == originalChar) continue;
105 } else if (value == _Base64Decoder._padding) {
106 if (firstPadding < 0) {
107 // Mark position in normalized output where padding occurs.
108 firstPadding = (buffer?.length ?? 0) + (sliceEnd - sliceStart);
109 firstPaddingSourceIndex = sliceEnd;
110 }
111 paddingCount++;
112 if (originalChar == equals) continue;
113 }
114 if (value != _Base64Decoder._invalid) {
115 buffer ??= new StringBuffer();
116 buffer.write(source.substring(sliceStart, sliceEnd));
117 buffer.writeCharCode(char);
118 sliceStart = i;
119 continue;
120 }
121 }
122 throw new FormatException("Invalid base64 data", source, sliceEnd);
123 }
124 if (buffer != null) {
125 buffer.write(source.substring(sliceStart, end));
126 if (firstPadding >= 0) {
127 // There was padding in the source. Check that it is valid:
128 // * result length a multiple of four
129 // * one or two padding characters at the end.
130 _checkPadding(source, firstPaddingSourceIndex, end,
131 firstPadding, paddingCount, buffer.length);
132 } else {
133 // Length of last chunk (1-4 chars) in the encoding.
134 int endLength = ((buffer.length - 1) % 4) + 1;
135 if (endLength == 1) {
136 // The data must have length 0, 2 or 3 modulo 4.
137 throw new FormatException("Invalid base64 encoding length ",
138 source, end);
139 }
140 while (endLength < 4) {
141 buffer.write("=");
142 endLength++;
143 }
144 }
145 return source.replaceRange(start, end, buffer.toString());
146 }
147 // Original was already normalized, only check padding.
148 int length = end - start;
149 if (firstPadding >= 0) {
150 _checkPadding(source, firstPaddingSourceIndex, end,
151 firstPadding, paddingCount, length);
152 } else {
153 // No padding given, so add some if needed it.
154 int endLength = length % 4;
155 if (endLength == 1) {
156 // The data must have length 0, 2 or 3 modulo 4.
157 throw new FormatException("Invalid base64 encoding length ",
158 source, end);
159 }
160 if (endLength > 1) {
161 // There is no "insertAt" on String, but this works as well.
162 source = source.replaceRange(end, end, (endLength == 2) ? "==" : "=");
163 }
164 }
165 return source;
166 }
167
168 static int _checkPadding(String source, int sourceIndex, int sourceEnd,
169 int firstPadding, int paddingCount, int length) {
170 if (length % 4 != 0) {
171 throw new FormatException(
172 "Invalid base64 padding, padded length must be multiplum of four, "
floitsch 2017/02/15 17:03:09 multiple
Lasse Reichstein Nielsen 2017/02/17 11:22:09 Done.
173 "is $length",
174 source, sourceEnd);
175 }
176 if (firstPadding + paddingCount != length) {
177 throw new FormatException(
178 "Invalid base64 padding, '=' not at the end",
179 source, sourceIndex);
180 }
181 if (paddingCount > 2) {
182 throw new FormatException(
183 "Invalid base64 padding, more than two '=' characters",
184 source, sourceIndex);
185 }
186 }
60 } 187 }
61 188
62 // ------------------------------------------------------------------------ 189 // ------------------------------------------------------------------------
63 // Encoder 190 // Encoder
64 // ------------------------------------------------------------------------ 191 // ------------------------------------------------------------------------
65 192
66 /** 193 /**
67 * Base64 and base64url encoding converter. 194 * Base64 and base64url encoding converter.
68 * 195 *
69 * Encodes lists of bytes using base64 or base64url encoding. 196 * Encodes lists of bytes using base64 or base64url encoding.
(...skipping 312 matching lines...) Expand 10 before | Expand all | Expand 10 after
382 static const int _p = _padding; 509 static const int _p = _padding;
383 510
384 /** 511 /**
385 * Mapping from ASCII characters to their index in the base64 alphabet. 512 * Mapping from ASCII characters to their index in the base64 alphabet.
386 * 513 *
387 * Uses [_invalid] for invalid indices and [_padding] for the padding 514 * Uses [_invalid] for invalid indices and [_padding] for the padding
388 * character. 515 * character.
389 * 516 *
390 * Accepts the "URL-safe" alphabet as well (`-` and `_` are the 517 * Accepts the "URL-safe" alphabet as well (`-` and `_` are the
391 * 62nd and 63rd alphabet characters), and considers `%` a padding 518 * 62nd and 63rd alphabet characters), and considers `%` a padding
392 * character, which mush then be followed by `3D`, the percent-escape 519 * character, which must then be followed by `3D`, the percent-escape
393 * for `=`. 520 * for `=`.
394 */ 521 */
395 static final List<int> _inverseAlphabet = new Int8List.fromList([ 522 static final List<int> _inverseAlphabet = new Int8List.fromList([
396 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 523 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
397 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 524 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
398 __, __, __, __, __, _p, __, __, __, __, __, 62, __, 62, __, 63, 525 __, __, __, __, __, _p, __, __, __, __, __, 62, __, 62, __, 63,
399 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, __, __, __, _p, __, __, 526 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, __, __, __, _p, __, __,
400 __, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 527 __, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
401 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, __, __, __, __, 63, 528 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, __, __, __, __, 63,
402 __, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 529 __, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
(...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after
744 end = RangeError.checkValidRange(start, end, string.length); 871 end = RangeError.checkValidRange(start, end, string.length);
745 if (start == end) return; 872 if (start == end) return;
746 Uint8List buffer = _decoder.decode(string, start, end); 873 Uint8List buffer = _decoder.decode(string, start, end);
747 if (buffer != null) _sink.add(buffer); 874 if (buffer != null) _sink.add(buffer);
748 if (isLast) { 875 if (isLast) {
749 _decoder.close(string, end); 876 _decoder.close(string, end);
750 _sink.close(); 877 _sink.close();
751 } 878 }
752 } 879 }
753 } 880 }
OLDNEW
« no previous file with comments | « CHANGELOG.md ('k') | sdk/lib/convert/convert.dart » ('j') | tests/corelib/data_uri_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698