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

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

Issue 2694373003: Normalize UriData.parse result. (Closed)
Patch Set: Address comments. Fix bug. 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
« no previous file with comments | « CHANGELOG.md ('k') | sdk/lib/convert/convert.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) 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 multiple of four).
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 if (i + 2 <= end) {
93 char = parseHexByte(source, i); // May be negative.
94 i += 2;
95 // We know that %25 isn't valid, but our table considers it
96 // a potential padding start, so skip the checks.
97 if (char == percent) char = -1;
98 } else {
99 // An invalid HEX escape (too short).
100 // Just skip past the handling and reach the throw below.
101 char = -1;
102 }
103 }
104 // If char is negative here, hex-decoding failed in some way.
105 if (0 <= char && char <= 127) {
106 int value = inverseAlphabet[char];
107 if (value >= 0) {
108 char = alphabet.codeUnitAt(value);
109 if (char == originalChar) continue;
110 } else if (value == _Base64Decoder._padding) {
111 // We have ruled out percent, so char is '='.
112 if (firstPadding < 0) {
113 // Mark position in normalized output where padding occurs.
114 firstPadding = (buffer?.length ?? 0) + (sliceEnd - sliceStart);
115 firstPaddingSourceIndex = sliceEnd;
116 }
117 paddingCount++;
118 // It could have been an escaped equals (%3D).
119 if (originalChar == equals) continue;
120 }
121 if (value != _Base64Decoder._invalid) {
122 buffer ??= new StringBuffer();
123 buffer.write(source.substring(sliceStart, sliceEnd));
124 buffer.writeCharCode(char);
125 sliceStart = i;
126 continue;
127 }
128 }
129 throw new FormatException("Invalid base64 data", source, sliceEnd);
130 }
131 if (buffer != null) {
132 buffer.write(source.substring(sliceStart, end));
133 if (firstPadding >= 0) {
134 // There was padding in the source. Check that it is valid:
135 // * result length a multiple of four
136 // * one or two padding characters at the end.
137 _checkPadding(source, firstPaddingSourceIndex, end,
138 firstPadding, paddingCount, buffer.length);
139 } else {
140 // Length of last chunk (1-4 chars) in the encoding.
141 int endLength = ((buffer.length - 1) % 4) + 1;
142 if (endLength == 1) {
143 // The data must have length 0, 2 or 3 modulo 4.
144 throw new FormatException("Invalid base64 encoding length ",
145 source, end);
146 }
147 while (endLength < 4) {
148 buffer.write("=");
149 endLength++;
150 }
151 }
152 return source.replaceRange(start, end, buffer.toString());
153 }
154 // Original was already normalized, only check padding.
155 int length = end - start;
156 if (firstPadding >= 0) {
157 _checkPadding(source, firstPaddingSourceIndex, end,
158 firstPadding, paddingCount, length);
159 } else {
160 // No padding given, so add some if needed it.
161 int endLength = length % 4;
162 if (endLength == 1) {
163 // The data must have length 0, 2 or 3 modulo 4.
164 throw new FormatException("Invalid base64 encoding length ",
165 source, end);
166 }
167 if (endLength > 1) {
168 // There is no "insertAt" on String, but this works as well.
169 source = source.replaceRange(end, end, (endLength == 2) ? "==" : "=");
170 }
171 }
172 return source;
173 }
174
175 static int _checkPadding(String source, int sourceIndex, int sourceEnd,
176 int firstPadding, int paddingCount, int length) {
177 if (length % 4 != 0) {
178 throw new FormatException(
179 "Invalid base64 padding, padded length must be multiple of four, "
180 "is $length",
181 source, sourceEnd);
182 }
183 if (firstPadding + paddingCount != length) {
184 throw new FormatException(
185 "Invalid base64 padding, '=' not at the end",
186 source, sourceIndex);
187 }
188 if (paddingCount > 2) {
189 throw new FormatException(
190 "Invalid base64 padding, more than two '=' characters",
191 source, sourceIndex);
192 }
193 }
60 } 194 }
61 195
62 // ------------------------------------------------------------------------ 196 // ------------------------------------------------------------------------
63 // Encoder 197 // Encoder
64 // ------------------------------------------------------------------------ 198 // ------------------------------------------------------------------------
65 199
66 /** 200 /**
67 * Base64 and base64url encoding converter. 201 * Base64 and base64url encoding converter.
68 * 202 *
69 * Encodes lists of bytes using base64 or base64url encoding. 203 * 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; 516 static const int _p = _padding;
383 517
384 /** 518 /**
385 * Mapping from ASCII characters to their index in the base64 alphabet. 519 * Mapping from ASCII characters to their index in the base64 alphabet.
386 * 520 *
387 * Uses [_invalid] for invalid indices and [_padding] for the padding 521 * Uses [_invalid] for invalid indices and [_padding] for the padding
388 * character. 522 * character.
389 * 523 *
390 * Accepts the "URL-safe" alphabet as well (`-` and `_` are the 524 * Accepts the "URL-safe" alphabet as well (`-` and `_` are the
391 * 62nd and 63rd alphabet characters), and considers `%` a padding 525 * 62nd and 63rd alphabet characters), and considers `%` a padding
392 * character, which mush then be followed by `3D`, the percent-escape 526 * character, which must then be followed by `3D`, the percent-escape
393 * for `=`. 527 * for `=`.
394 */ 528 */
395 static final List<int> _inverseAlphabet = new Int8List.fromList([ 529 static final List<int> _inverseAlphabet = new Int8List.fromList([
396 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 530 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
397 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 531 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
398 __, __, __, __, __, _p, __, __, __, __, __, 62, __, 62, __, 63, 532 __, __, __, __, __, _p, __, __, __, __, __, 62, __, 62, __, 63,
399 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, __, __, __, _p, __, __, 533 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, 534 __, 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, 535 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, 536 __, 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); 878 end = RangeError.checkValidRange(start, end, string.length);
745 if (start == end) return; 879 if (start == end) return;
746 Uint8List buffer = _decoder.decode(string, start, end); 880 Uint8List buffer = _decoder.decode(string, start, end);
747 if (buffer != null) _sink.add(buffer); 881 if (buffer != null) _sink.add(buffer);
748 if (isLast) { 882 if (isLast) {
749 _decoder.close(string, end); 883 _decoder.close(string, end);
750 _sink.close(); 884 _sink.close();
751 } 885 }
752 } 886 }
753 } 887 }
OLDNEW
« no previous file with comments | « CHANGELOG.md ('k') | sdk/lib/convert/convert.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698