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

Side by Side Diff: runtime/lib/string_patch.dart

Issue 2767533002: Revert "Fix observatory tests broken by running dartfmt." (Closed)
Patch Set: Created 3 years, 9 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 | « runtime/lib/string_buffer_patch.dart ('k') | runtime/lib/symbol_patch.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 const int _maxAscii = 0x7f; 5 const int _maxAscii = 0x7f;
6 const int _maxLatin1 = 0xff; 6 const int _maxLatin1 = 0xff;
7 const int _maxUtf16 = 0xffff; 7 const int _maxUtf16 = 0xffff;
8 const int _maxUnicode = 0x10ffff; 8 const int _maxUnicode = 0x10ffff;
9 9
10 @patch 10 @patch class String {
11 class String { 11 @patch factory String.fromCharCodes(Iterable<int> charCodes,
12 @patch 12 [int start = 0, int end]) {
13 factory String.fromCharCodes(Iterable<int> charCodes, 13 if (charCodes is! Iterable) throw new ArgumentError.value(charCodes, "charCo des");
14 [int start = 0, int end]) {
15 if (charCodes is! Iterable)
16 throw new ArgumentError.value(charCodes, "charCodes");
17 if (start is! int) throw new ArgumentError.value(start, "start"); 14 if (start is! int) throw new ArgumentError.value(start, "start");
18 if (end != null && end is! int) throw new ArgumentError.value(end, "end"); 15 if (end != null && end is! int) throw new ArgumentError.value(end, "end");
19 return _StringBase.createFromCharCodes(charCodes, start, end, null); 16 return _StringBase.createFromCharCodes(charCodes, start, end, null);
20 } 17 }
21 18
22 @patch 19 @patch factory String.fromCharCode(int charCode) {
23 factory String.fromCharCode(int charCode) {
24 if (charCode >= 0) { 20 if (charCode >= 0) {
25 if (charCode <= 0xff) { 21 if (charCode <= 0xff) {
26 return _OneByteString._allocate(1).._setAt(0, charCode); 22 return _OneByteString._allocate(1).._setAt(0, charCode);
27 } 23 }
28 if (charCode <= 0xffff) { 24 if (charCode <= 0xffff) {
29 return _StringBase._createFromCodePoints( 25 return _StringBase._createFromCodePoints(new _List(1)..[0] = charCode,
30 new _List(1)..[0] = charCode, 0, 1); 26 0, 1);
31 } 27 }
32 if (charCode <= 0x10ffff) { 28 if (charCode <= 0x10ffff) {
33 var low = 0xDC00 | (charCode & 0x3ff); 29 var low = 0xDC00 | (charCode & 0x3ff);
34 int bits = charCode - 0x10000; 30 int bits = charCode - 0x10000;
35 var high = 0xD800 | (bits >> 10); 31 var high = 0xD800 | (bits >> 10);
36 return _StringBase._createFromCodePoints( 32 return _StringBase._createFromCodePoints(new _List(2)..[0] = high
37 new _List(2) 33 ..[1] = low,
38 ..[0] = high 34 0, 2);
39 ..[1] = low,
40 0,
41 2);
42 } 35 }
43 } 36 }
44 throw new RangeError.range(charCode, 0, 0x10ffff); 37 throw new RangeError.range(charCode, 0, 0x10ffff);
45 } 38 }
46 39
47 @patch 40 @patch const factory String.fromEnvironment(String name,
48 const factory String.fromEnvironment(String name, {String defaultValue}) 41 {String defaultValue})
49 native "String_fromEnvironment"; 42 native "String_fromEnvironment";
50 } 43 }
51 44
45
52 /** 46 /**
53 * [_StringBase] contains common methods used by concrete String 47 * [_StringBase] contains common methods used by concrete String
54 * implementations, e.g., _OneByteString. 48 * implementations, e.g., _OneByteString.
55 */ 49 */
56 abstract class _StringBase { 50 abstract class _StringBase {
57 // Constants used by replaceAll encoding of string slices between matches. 51 // Constants used by replaceAll encoding of string slices between matches.
58 // A string slice (start+length) is encoded in a single Smi to save memory 52 // A string slice (start+length) is encoded in a single Smi to save memory
59 // overhead in the common case. 53 // overhead in the common case.
60 // We use fewer bits for length (11 bits) than for the start index (19+ bits). 54 // We use fewer bits for length (11 bits) than for the start index (19+ bits).
61 // For long strings, it's possible to have many large indices, 55 // For long strings, it's possible to have many large indices,
(...skipping 16 matching lines...) Expand all
78 // We pick 30 as a safe lower bound on available bits in a negative smi. 72 // We pick 30 as a safe lower bound on available bits in a negative smi.
79 // TODO(lrn): Consider allowing more bits for start on 64-bit systems. 73 // TODO(lrn): Consider allowing more bits for start on 64-bit systems.
80 static const int _maxUnsignedSmiBits = 30; 74 static const int _maxUnsignedSmiBits = 30;
81 75
82 // For longer strings, calling into C++ to create the result of a 76 // For longer strings, calling into C++ to create the result of a
83 // [replaceAll] is faster than [_joinReplaceAllOneByteResult]. 77 // [replaceAll] is faster than [_joinReplaceAllOneByteResult].
84 // TODO(lrn): See if this limit can be tweaked. 78 // TODO(lrn): See if this limit can be tweaked.
85 static const int _maxJoinReplaceOneByteStringLength = 500; 79 static const int _maxJoinReplaceOneByteStringLength = 500;
86 80
87 factory _StringBase._uninstantiable() { 81 factory _StringBase._uninstantiable() {
88 throw new UnsupportedError("_StringBase can't be instaniated"); 82 throw new UnsupportedError(
83 "_StringBase can't be instaniated");
89 } 84 }
90 85
91 int get hashCode native "String_getHashCode"; 86 int get hashCode native "String_getHashCode";
92 87
93 bool get _isOneByte { 88 bool get _isOneByte {
94 // Alternatively return false and override it on one-byte string classes. 89 // Alternatively return false and override it on one-byte string classes.
95 int id = ClassID.getID(this); 90 int id = ClassID.getID(this);
96 return id == ClassID.cidOneByteString || 91 return id == ClassID.cidOneByteString ||
97 id == ClassID.cidExternalOneByteString; 92 id == ClassID.cidExternalOneByteString;
98 } 93 }
99 94
100 /** 95 /**
101 * Create the most efficient string representation for specified 96 * Create the most efficient string representation for specified
102 * [charCodes]. 97 * [charCodes].
103 * 98 *
104 * Only uses the character codes betwen index [start] and index [end] of 99 * Only uses the character codes betwen index [start] and index [end] of
105 * `charCodes`. They must satisfy `0 <= start <= end <= charCodes.length`. 100 * `charCodes`. They must satisfy `0 <= start <= end <= charCodes.length`.
106 * 101 *
107 * The [limit] is an upper limit on the character codes in the iterable. 102 * The [limit] is an upper limit on the character codes in the iterable.
108 * It's `null` if unknown. 103 * It's `null` if unknown.
109 */ 104 */
110 static String createFromCharCodes( 105 static String createFromCharCodes(Iterable<int> charCodes,
111 Iterable<int> charCodes, int start, int end, int limit) { 106 int start, int end,
107 int limit) {
112 if (start == null) throw new ArgumentError.notNull("start"); 108 if (start == null) throw new ArgumentError.notNull("start");
113 if (charCodes == null) throw new ArgumentError(charCodes); 109 if (charCodes == null) throw new ArgumentError(charCodes);
114 // TODO(srdjan): Also skip copying of wide typed arrays. 110 // TODO(srdjan): Also skip copying of wide typed arrays.
115 final ccid = ClassID.getID(charCodes); 111 final ccid = ClassID.getID(charCodes);
116 bool isOneByteString = false; 112 bool isOneByteString = false;
117 if ((ccid != ClassID.cidArray) && 113 if ((ccid != ClassID.cidArray) &&
118 (ccid != ClassID.cidGrowableObjectArray) && 114 (ccid != ClassID.cidGrowableObjectArray) &&
119 (ccid != ClassID.cidImmutableArray)) { 115 (ccid != ClassID.cidImmutableArray)) {
120 if (charCodes is Uint8List) { 116 if (charCodes is Uint8List) {
121 end = RangeError.checkValidRange(start, end, charCodes.length); 117 end = RangeError.checkValidRange(start, end, charCodes.length);
(...skipping 27 matching lines...) Expand all
149 static int _scanCodeUnits(List<int> charCodes, int start, int end) { 145 static int _scanCodeUnits(List<int> charCodes, int start, int end) {
150 int bits = 0; 146 int bits = 0;
151 for (int i = start; i < end; i++) { 147 for (int i = start; i < end; i++) {
152 int code = charCodes[i]; 148 int code = charCodes[i];
153 if (code is! _Smi) throw new ArgumentError(charCodes); 149 if (code is! _Smi) throw new ArgumentError(charCodes);
154 bits |= code; 150 bits |= code;
155 } 151 }
156 return bits; 152 return bits;
157 } 153 }
158 154
159 static String _createStringFromIterable( 155 static String _createStringFromIterable(Iterable<int> charCodes,
160 Iterable<int> charCodes, int start, int end) { 156 int start, int end) {
161 // Treat charCodes as Iterable. 157 // Treat charCodes as Iterable.
162 if (charCodes is EfficientLengthIterable) { 158 if (charCodes is EfficientLengthIterable) {
163 int length = charCodes.length; 159 int length = charCodes.length;
164 end = RangeError.checkValidRange(start, end, length); 160 end = RangeError.checkValidRange(start, end, length);
165 List charCodeList = 161 List charCodeList = new List.from(charCodes.take(end).skip(start),
166 new List.from(charCodes.take(end).skip(start), growable: false); 162 growable: false);
167 return createFromCharCodes(charCodeList, 0, charCodeList.length, null); 163 return createFromCharCodes(charCodeList, 0, charCodeList.length, null);
168 } 164 }
169 // Don't know length of iterable, so iterate and see if all the values 165 // Don't know length of iterable, so iterate and see if all the values
170 // are there. 166 // are there.
171 if (start < 0) throw new RangeError.range(start, 0, charCodes.length); 167 if (start < 0) throw new RangeError.range(start, 0, charCodes.length);
172 var it = charCodes.iterator; 168 var it = charCodes.iterator;
173 for (int i = 0; i < start; i++) { 169 for (int i = 0; i < start; i++) {
174 if (!it.moveNext()) { 170 if (!it.moveNext()) {
175 throw new RangeError.range(start, 0, i); 171 throw new RangeError.range(start, 0, i);
176 } 172 }
177 } 173 }
178 List charCodeList; 174 List charCodeList;
179 int bits = 0; // Bitwise-or of all char codes in list. 175 int bits = 0; // Bitwise-or of all char codes in list.
180 if (end == null) { 176 if (end == null) {
181 var list = []; 177 var list = [];
182 while (it.moveNext()) { 178 while (it.moveNext()) {
183 int code = it.current; 179 int code = it.current;
184 bits |= code; 180 bits |= code;
185 list.add(code); 181 list.add(code);
186 } 182 }
187 charCodeList = makeListFixedLength(list); 183 charCodeList = makeListFixedLength(list);
188 } else { 184 } else {
189 if (end < start) { 185 if (end < start) {
(...skipping 29 matching lines...) Expand all
219 s._setAt(i, charCodes[start + i]); 215 s._setAt(i, charCodes[start + i]);
220 } 216 }
221 return s; 217 return s;
222 } 218 }
223 219
224 static String _createFromCodePoints(List<int> codePoints, int start, int end) 220 static String _createFromCodePoints(List<int> codePoints, int start, int end)
225 native "StringBase_createFromCodePoints"; 221 native "StringBase_createFromCodePoints";
226 222
227 String operator [](int index) native "String_charAt"; 223 String operator [](int index) native "String_charAt";
228 224
229 int codeUnitAt(int index); // Implemented in the subclasses. 225 int codeUnitAt(int index); // Implemented in the subclasses.
230 226
231 int get length native "String_getLength"; 227 int get length native "String_getLength";
232 228
233 bool get isEmpty { 229 bool get isEmpty {
234 return this.length == 0; 230 return this.length == 0;
235 } 231 }
236 232
237 bool get isNotEmpty => !isEmpty; 233 bool get isNotEmpty => !isEmpty;
238 234
239 String operator +(String other) native "String_concat"; 235 String operator +(String other) native "String_concat";
240 236
241 String toString() { 237 String toString() {
242 return this; 238 return this;
243 } 239 }
244 240
245 bool operator ==(Object other) { 241 bool operator ==(Object other) {
246 if (identical(this, other)) { 242 if (identical(this, other)) {
247 return true; 243 return true;
248 } 244 }
249 if ((other is! String) || (this.length != other.length)) { 245 if ((other is! String) ||
246 (this.length != other.length)) {
250 return false; 247 return false;
251 } 248 }
252 final len = this.length; 249 final len = this.length;
253 for (int i = 0; i < len; i++) { 250 for (int i = 0; i < len; i++) {
254 if (this.codeUnitAt(i) != other.codeUnitAt(i)) { 251 if (this.codeUnitAt(i) != other.codeUnitAt(i)) {
255 return false; 252 return false;
256 } 253 }
257 } 254 }
258 return true; 255 return true;
259 } 256 }
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
385 } 382 }
386 return _substringUncheckedNative(startIndex, endIndex); 383 return _substringUncheckedNative(startIndex, endIndex);
387 } 384 }
388 385
389 String _substringUncheckedNative(int startIndex, int endIndex) 386 String _substringUncheckedNative(int startIndex, int endIndex)
390 native "StringBase_substringUnchecked"; 387 native "StringBase_substringUnchecked";
391 388
392 // Checks for one-byte whitespaces only. 389 // Checks for one-byte whitespaces only.
393 static bool _isOneByteWhitespace(int codeUnit) { 390 static bool _isOneByteWhitespace(int codeUnit) {
394 if (codeUnit <= 32) { 391 if (codeUnit <= 32) {
395 return ((codeUnit == 32) || // Space. 392 return ((codeUnit == 32) || // Space.
396 ((codeUnit <= 13) && (codeUnit >= 9))); // CR, LF, TAB, etc. 393 ((codeUnit <= 13) && (codeUnit >= 9))); // CR, LF, TAB, etc.
397 } 394 }
398 return (codeUnit == 0x85) || (codeUnit == 0xA0); // NEL, NBSP. 395 return (codeUnit == 0x85) || (codeUnit == 0xA0); // NEL, NBSP.
399 } 396 }
400 397
401 // Characters with Whitespace property (Unicode 6.2). 398 // Characters with Whitespace property (Unicode 6.2).
402 // 0009..000D ; White_Space # Cc <control-0009>..<control-000D> 399 // 0009..000D ; White_Space # Cc <control-0009>..<control-000D>
403 // 0020 ; White_Space # Zs SPACE 400 // 0020 ; White_Space # Zs SPACE
404 // 0085 ; White_Space # Cc <control-0085> 401 // 0085 ; White_Space # Cc <control-0085>
405 // 00A0 ; White_Space # Zs NO-BREAK SPACE 402 // 00A0 ; White_Space # Zs NO-BREAK SPACE
406 // 1680 ; White_Space # Zs OGHAM SPACE MARK 403 // 1680 ; White_Space # Zs OGHAM SPACE MARK
407 // 180E ; White_Space # Zs MONGOLIAN VOWEL SEPARATOR 404 // 180E ; White_Space # Zs MONGOLIAN VOWEL SEPARATOR
408 // 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE 405 // 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE
409 // 2028 ; White_Space # Zl LINE SEPARATOR 406 // 2028 ; White_Space # Zl LINE SEPARATOR
410 // 2029 ; White_Space # Zp PARAGRAPH SEPARATOR 407 // 2029 ; White_Space # Zp PARAGRAPH SEPARATOR
411 // 202F ; White_Space # Zs NARROW NO-BREAK SPACE 408 // 202F ; White_Space # Zs NARROW NO-BREAK SPACE
412 // 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE 409 // 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE
413 // 3000 ; White_Space # Zs IDEOGRAPHIC SPACE 410 // 3000 ; White_Space # Zs IDEOGRAPHIC SPACE
414 // 411 //
415 // BOM: 0xFEFF 412 // BOM: 0xFEFF
416 static bool _isTwoByteWhitespace(int codeUnit) { 413 static bool _isTwoByteWhitespace(int codeUnit) {
417 if (codeUnit <= 32) { 414 if (codeUnit <= 32) {
418 return (codeUnit == 32) || ((codeUnit <= 13) && (codeUnit >= 9)); 415 return (codeUnit == 32) ||
416 ((codeUnit <= 13) && (codeUnit >= 9));
419 } 417 }
420 if (codeUnit < 0x85) return false; 418 if (codeUnit < 0x85) return false;
421 if ((codeUnit == 0x85) || (codeUnit == 0xA0)) return true; 419 if ((codeUnit == 0x85) || (codeUnit == 0xA0)) return true;
422 return (codeUnit <= 0x200A) 420 return (codeUnit <= 0x200A)
423 ? ((codeUnit == 0x1680) || (codeUnit == 0x180E) || (0x2000 <= codeUnit)) 421 ? ((codeUnit == 0x1680) ||
424 : ((codeUnit == 0x2028) || 422 (codeUnit == 0x180E) ||
425 (codeUnit == 0x2029) || 423 (0x2000 <= codeUnit))
426 (codeUnit == 0x202F) || 424 : ((codeUnit == 0x2028) ||
427 (codeUnit == 0x205F) || 425 (codeUnit == 0x2029) ||
428 (codeUnit == 0x3000) || 426 (codeUnit == 0x202F) ||
429 (codeUnit == 0xFEFF)); 427 (codeUnit == 0x205F) ||
428 (codeUnit == 0x3000) ||
429 (codeUnit == 0xFEFF));
430 } 430 }
431 431
432 int _firstNonWhitespace() { 432 int _firstNonWhitespace() {
433 final len = this.length; 433 final len = this.length;
434 int first = 0; 434 int first = 0;
435 for (; first < len; first++) { 435 for (; first < len; first++) {
436 if (!_isWhitespace(this.codeUnitAt(first))) { 436 if (!_isWhitespace(this.codeUnitAt(first))) {
437 break; 437 break;
438 } 438 }
439 } 439 }
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 // String contains only whitespaces. 498 // String contains only whitespaces.
499 return ""; 499 return "";
500 } 500 }
501 if (last == (len - 1)) { 501 if (last == (len - 1)) {
502 // Returns this string since it does not have trailing whitespaces. 502 // Returns this string since it does not have trailing whitespaces.
503 return this; 503 return this;
504 } 504 }
505 return _substringUnchecked(0, last + 1); 505 return _substringUnchecked(0, last + 1);
506 } 506 }
507 507
508 String operator *(int times) { 508 String operator*(int times) {
509 if (times <= 0) return ""; 509 if (times <= 0) return "";
510 if (times == 1) return this; 510 if (times == 1) return this;
511 StringBuffer buffer = new StringBuffer(this); 511 StringBuffer buffer = new StringBuffer(this);
512 for (int i = 1; i < times; i++) { 512 for (int i = 1; i < times; i++) {
513 buffer.write(this); 513 buffer.write(this);
514 } 514 }
515 return buffer.toString(); 515 return buffer.toString();
516 } 516 }
517 517
518 String padLeft(int width, [String padding = ' ']) { 518 String padLeft(int width, [String padding = ' ']) {
(...skipping 20 matching lines...) Expand all
539 bool contains(Pattern pattern, [int startIndex = 0]) { 539 bool contains(Pattern pattern, [int startIndex = 0]) {
540 if (pattern is String) { 540 if (pattern is String) {
541 if (startIndex < 0 || startIndex > this.length) { 541 if (startIndex < 0 || startIndex > this.length) {
542 throw new RangeError.range(startIndex, 0, this.length); 542 throw new RangeError.range(startIndex, 0, this.length);
543 } 543 }
544 return indexOf(pattern, startIndex) >= 0; 544 return indexOf(pattern, startIndex) >= 0;
545 } 545 }
546 return pattern.allMatches(this.substring(startIndex)).isNotEmpty; 546 return pattern.allMatches(this.substring(startIndex)).isNotEmpty;
547 } 547 }
548 548
549 String replaceFirst(Pattern pattern, String replacement, 549 String replaceFirst(Pattern pattern,
550 [int startIndex = 0]) { 550 String replacement,
551 [int startIndex = 0]) {
551 if (pattern is! Pattern) { 552 if (pattern is! Pattern) {
552 throw new ArgumentError("${pattern} is not a Pattern"); 553 throw new ArgumentError("${pattern} is not a Pattern");
553 } 554 }
554 if (replacement is! String) { 555 if (replacement is! String) {
555 throw new ArgumentError("${replacement} is not a String"); 556 throw new ArgumentError("${replacement} is not a String");
556 } 557 }
557 if (startIndex is! int) { 558 if (startIndex is! int) {
558 throw new ArgumentError("${startIndex} is not an int"); 559 throw new ArgumentError("${startIndex} is not an int");
559 } 560 }
560 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); 561 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex");
561 Iterator iterator = startIndex == 0 562 Iterator iterator =
562 ? pattern.allMatches(this).iterator 563 startIndex == 0 ? pattern.allMatches(this).iterator
563 : pattern.allMatches(this, startIndex).iterator; 564 : pattern.allMatches(this, startIndex).iterator;
564 if (!iterator.moveNext()) return this; 565 if (!iterator.moveNext()) return this;
565 Match match = iterator.current; 566 Match match = iterator.current;
566 return replaceRange(match.start, match.end, replacement); 567 return replaceRange(match.start, match.end, replacement);
567 } 568 }
568 569
569 String replaceRange(int start, int end, String replacement) { 570 String replaceRange(int start, int end, String replacement) {
570 int length = this.length; 571 int length = this.length;
571 end = RangeError.checkValidRange(start, end, length); 572 end = RangeError.checkValidRange(start, end, length);
572 bool replacementIsOneByte = replacement._isOneByte; 573 bool replacementIsOneByte = replacement._isOneByte;
573 if (start == 0 && end == length) return replacement; 574 if (start == 0 && end == length) return replacement;
574 int replacementLength = replacement.length; 575 int replacementLength = replacement.length;
575 int totalLength = start + (length - end) + replacementLength; 576 int totalLength = start + (length - end) + replacementLength;
576 if (replacementIsOneByte && this._isOneByte) { 577 if (replacementIsOneByte && this._isOneByte) {
577 var result = _OneByteString._allocate(totalLength); 578 var result = _OneByteString._allocate(totalLength);
578 int index = 0; 579 int index = 0;
579 index = result._setRange(index, this, 0, start); 580 index = result._setRange(index, this, 0, start);
580 index = result._setRange(start, replacement, 0, replacementLength); 581 index = result._setRange(start, replacement, 0, replacementLength);
581 result._setRange(index, this, end, length); 582 result._setRange(index, this, end, length);
582 return result; 583 return result;
583 } 584 }
584 List slices = []; 585 List slices = [];
585 _addReplaceSlice(slices, 0, start); 586 _addReplaceSlice(slices, 0, start);
586 if (replacement.length > 0) slices.add(replacement); 587 if (replacement.length > 0) slices.add(replacement);
587 _addReplaceSlice(slices, end, length); 588 _addReplaceSlice(slices, end, length);
588 return _joinReplaceAllResult( 589 return _joinReplaceAllResult(this, slices, totalLength,
589 this, slices, totalLength, replacementIsOneByte); 590 replacementIsOneByte);
590 } 591 }
591 592
592 static int _addReplaceSlice(List matches, int start, int end) { 593 static int _addReplaceSlice(List matches, int start, int end) {
593 int length = end - start; 594 int length = end - start;
594 if (length > 0) { 595 if (length > 0) {
595 if (length <= _maxLengthValue && start <= _maxStartValue) { 596 if (length <= _maxLengthValue && start <= _maxStartValue) {
596 matches.add(-((start << _lengthBits) | length)); 597 matches.add(-((start << _lengthBits) | length));
597 } else { 598 } else {
598 matches.add(start); 599 matches.add(start);
599 matches.add(end); 600 matches.add(end);
(...skipping 24 matching lines...) Expand all
624 } 625 }
625 } 626 }
626 length += _addReplaceSlice(matches, startIndex, this.length); 627 length += _addReplaceSlice(matches, startIndex, this.length);
627 bool replacementIsOneByte = replacement._isOneByte; 628 bool replacementIsOneByte = replacement._isOneByte;
628 if (replacementIsOneByte && 629 if (replacementIsOneByte &&
629 length < _maxJoinReplaceOneByteStringLength && 630 length < _maxJoinReplaceOneByteStringLength &&
630 this._isOneByte) { 631 this._isOneByte) {
631 // TODO(lrn): Is there a cut-off point, or is runtime always faster? 632 // TODO(lrn): Is there a cut-off point, or is runtime always faster?
632 return _joinReplaceAllOneByteResult(this, matches, length); 633 return _joinReplaceAllOneByteResult(this, matches, length);
633 } 634 }
634 return _joinReplaceAllResult(this, matches, length, replacementIsOneByte); 635 return _joinReplaceAllResult(this, matches, length,
636 replacementIsOneByte);
635 } 637 }
636 638
637 /** 639 /**
638 * As [_joinReplaceAllResult], but knowing that the result 640 * As [_joinReplaceAllResult], but knowing that the result
639 * is always a [_OneByteString]. 641 * is always a [_OneByteString].
640 */ 642 */
641 static String _joinReplaceAllOneByteResult( 643 static String _joinReplaceAllOneByteResult(String base,
642 String base, List matches, int length) { 644 List matches,
645 int length) {
643 _OneByteString result = _OneByteString._allocate(length); 646 _OneByteString result = _OneByteString._allocate(length);
644 int writeIndex = 0; 647 int writeIndex = 0;
645 for (int i = 0; i < matches.length; i++) { 648 for (int i = 0; i < matches.length; i++) {
646 var entry = matches[i]; 649 var entry = matches[i];
647 if (entry is _Smi) { 650 if (entry is _Smi) {
648 int sliceStart = entry; 651 int sliceStart = entry;
649 int sliceEnd; 652 int sliceEnd;
650 if (sliceStart < 0) { 653 if (sliceStart < 0) {
651 int bits = -sliceStart; 654 int bits = -sliceStart;
652 int sliceLength = bits & _lengthMask; 655 int sliceLength = bits & _lengthMask;
(...skipping 26 matching lines...) Expand all
679 * Combine the results of a [replaceAll] match into a new string. 682 * Combine the results of a [replaceAll] match into a new string.
680 * 683 *
681 * The [matches] lists contains Smi index pairs representing slices of 684 * The [matches] lists contains Smi index pairs representing slices of
682 * [base] and [String]s to be put in between the slices. 685 * [base] and [String]s to be put in between the slices.
683 * 686 *
684 * The total [length] of the resulting string is known, as is 687 * The total [length] of the resulting string is known, as is
685 * whether the replacement strings are one-byte strings. 688 * whether the replacement strings are one-byte strings.
686 * If they are, then we have to check the base string slices to know 689 * If they are, then we have to check the base string slices to know
687 * whether the result must be a one-byte string. 690 * whether the result must be a one-byte string.
688 */ 691 */
689 static String 692 static String _joinReplaceAllResult(String base, List matches, int length,
690 _joinReplaceAllResult(String base, List matches, int length, 693 bool replacementStringsAreOneByte)
691 bool replacementStringsAreOneByte)
692 native "StringBase_joinReplaceAllResult"; 694 native "StringBase_joinReplaceAllResult";
693 695
694 String replaceAllMapped(Pattern pattern, String replace(Match match)) { 696 String replaceAllMapped(Pattern pattern, String replace(Match match)) {
695 if (pattern == null) throw new ArgumentError.notNull("pattern"); 697 if (pattern == null) throw new ArgumentError.notNull("pattern");
696 if (replace == null) throw new ArgumentError.notNull("replace"); 698 if (replace == null) throw new ArgumentError.notNull("replace");
697 List matches = []; 699 List matches = [];
698 int length = 0; 700 int length = 0;
699 int startIndex = 0; 701 int startIndex = 0;
700 bool replacementStringsAreOneByte = true; 702 bool replacementStringsAreOneByte = true;
701 for (Match match in pattern.allMatches(this)) { 703 for (Match match in pattern.allMatches(this)) {
702 length += _addReplaceSlice(matches, startIndex, match.start); 704 length += _addReplaceSlice(matches, startIndex, match.start);
703 var replacement = "${replace(match)}"; 705 var replacement = "${replace(match)}";
704 matches.add(replacement); 706 matches.add(replacement);
705 length += replacement.length; 707 length += replacement.length;
706 replacementStringsAreOneByte = 708 replacementStringsAreOneByte =
707 replacementStringsAreOneByte && replacement._isOneByte; 709 replacementStringsAreOneByte && replacement._isOneByte;
708 startIndex = match.end; 710 startIndex = match.end;
709 } 711 }
710 if (matches.isEmpty) return this; 712 if (matches.isEmpty) return this;
711 length += _addReplaceSlice(matches, startIndex, this.length); 713 length += _addReplaceSlice(matches, startIndex, this.length);
712 if (replacementStringsAreOneByte && 714 if (replacementStringsAreOneByte &&
713 length < _maxJoinReplaceOneByteStringLength && 715 length < _maxJoinReplaceOneByteStringLength &&
714 this._isOneByte) { 716 this._isOneByte) {
715 return _joinReplaceAllOneByteResult(this, matches, length); 717 return _joinReplaceAllOneByteResult(this, matches, length);
716 } 718 }
717 return _joinReplaceAllResult( 719 return _joinReplaceAllResult(this, matches, length,
718 this, matches, length, replacementStringsAreOneByte); 720 replacementStringsAreOneByte);
719 } 721 }
720 722
721 String replaceFirstMapped(Pattern pattern, String replace(Match match), 723 String replaceFirstMapped(Pattern pattern, String replace(Match match),
722 [int startIndex = 0]) { 724 [int startIndex = 0]) {
723 if (pattern == null) throw new ArgumentError.notNull("pattern"); 725 if (pattern == null) throw new ArgumentError.notNull("pattern");
724 if (replace == null) throw new ArgumentError.notNull("replace"); 726 if (replace == null) throw new ArgumentError.notNull("replace");
725 if (startIndex == null) throw new ArgumentError.notNull("startIndex"); 727 if (startIndex == null) throw new ArgumentError.notNull("startIndex");
726 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); 728 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex");
727 729
728 var matches = pattern.allMatches(this, startIndex).iterator; 730 var matches = pattern.allMatches(this, startIndex).iterator;
729 if (!matches.moveNext()) return this; 731 if (!matches.moveNext()) return this;
730 var match = matches.current; 732 var match = matches.current;
731 var replacement = "${replace(match)}"; 733 var replacement = "${replace(match)}";
732 return replaceRange(match.start, match.end, replacement); 734 return replaceRange(match.start, match.end, replacement);
733 } 735 }
734 736
735 static String _matchString(Match match) => match[0]; 737 static String _matchString(Match match) => match[0];
736 static String _stringIdentity(String string) => string; 738 static String _stringIdentity(String string) => string;
737 739
738 String _splitMapJoinEmptyString( 740 String _splitMapJoinEmptyString(String onMatch(Match match),
739 String onMatch(Match match), String onNonMatch(String nonMatch)) { 741 String onNonMatch(String nonMatch)) {
740 // Pattern is the empty string. 742 // Pattern is the empty string.
741 StringBuffer buffer = new StringBuffer(); 743 StringBuffer buffer = new StringBuffer();
742 int length = this.length; 744 int length = this.length;
743 int i = 0; 745 int i = 0;
744 buffer.write(onNonMatch("")); 746 buffer.write(onNonMatch(""));
745 while (i < length) { 747 while (i < length) {
746 buffer.write(onMatch(new _StringMatch(i, this, ""))); 748 buffer.write(onMatch(new _StringMatch(i, this, "")));
747 // Special case to avoid splitting a surrogate pair. 749 // Special case to avoid splitting a surrogate pair.
748 int code = this.codeUnitAt(i); 750 int code = this.codeUnitAt(i);
749 if ((code & ~0x3FF) == 0xD800 && length > i + 1) { 751 if ((code & ~0x3FF) == 0xD800 && length > i + 1) {
750 // Leading surrogate; 752 // Leading surrogate;
751 code = this.codeUnitAt(i + 1); 753 code = this.codeUnitAt(i + 1);
752 if ((code & ~0x3FF) == 0xDC00) { 754 if ((code & ~0x3FF) == 0xDC00) {
753 // Matching trailing surrogate. 755 // Matching trailing surrogate.
754 buffer.write(onNonMatch(this.substring(i, i + 2))); 756 buffer.write(onNonMatch(this.substring(i, i + 2)));
755 i += 2; 757 i += 2;
756 continue; 758 continue;
757 } 759 }
758 } 760 }
759 buffer.write(onNonMatch(this[i])); 761 buffer.write(onNonMatch(this[i]));
760 i++; 762 i++;
761 } 763 }
762 buffer.write(onMatch(new _StringMatch(i, this, ""))); 764 buffer.write(onMatch(new _StringMatch(i, this, "")));
763 buffer.write(onNonMatch("")); 765 buffer.write(onNonMatch(""));
764 return buffer.toString(); 766 return buffer.toString();
765 } 767 }
766 768
767 String splitMapJoin(Pattern pattern, 769 String splitMapJoin(Pattern pattern,
768 {String onMatch(Match match), String onNonMatch(String nonMatch)}) { 770 {String onMatch(Match match),
771 String onNonMatch(String nonMatch)}) {
769 if (pattern is! Pattern) { 772 if (pattern is! Pattern) {
770 throw new ArgumentError("${pattern} is not a Pattern"); 773 throw new ArgumentError("${pattern} is not a Pattern");
771 } 774 }
772 if (onMatch == null) onMatch = _matchString; 775 if (onMatch == null) onMatch = _matchString;
773 if (onNonMatch == null) onNonMatch = _stringIdentity; 776 if (onNonMatch == null) onNonMatch = _stringIdentity;
774 if (pattern is String) { 777 if (pattern is String) {
775 String stringPattern = pattern; 778 String stringPattern = pattern;
776 if (stringPattern.isEmpty) { 779 if (stringPattern.isEmpty) {
777 return _splitMapJoinEmptyString(onMatch, onNonMatch); 780 return _splitMapJoinEmptyString(onMatch, onNonMatch);
778 } 781 }
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
877 result.add(this.substring(previousIndex, length)); 880 result.add(this.substring(previousIndex, length));
878 break; 881 break;
879 } 882 }
880 Match match = iterator.current; 883 Match match = iterator.current;
881 if (match.start == length) { 884 if (match.start == length) {
882 result.add(this.substring(previousIndex, length)); 885 result.add(this.substring(previousIndex, length));
883 break; 886 break;
884 } 887 }
885 int endIndex = match.end; 888 int endIndex = match.end;
886 if (startIndex == endIndex && endIndex == previousIndex) { 889 if (startIndex == endIndex && endIndex == previousIndex) {
887 ++startIndex; // empty match, advance and restart 890 ++startIndex; // empty match, advance and restart
888 continue; 891 continue;
889 } 892 }
890 result.add(this.substring(previousIndex, match.start)); 893 result.add(this.substring(previousIndex, match.start));
891 startIndex = previousIndex = endIndex; 894 startIndex = previousIndex = endIndex;
892 } 895 }
893 return result; 896 return result;
894 } 897 }
895 898
896 List<int> get codeUnits => new CodeUnits(this); 899 List<int> get codeUnits => new CodeUnits(this);
897 900
(...skipping 11 matching lines...) Expand all
909 } 912 }
910 return _concatRangeNative(strings, start, end); 913 return _concatRangeNative(strings, start, end);
911 } 914 }
912 915
913 // Call this method if not all list elements are known to be OneByteString(s). 916 // Call this method if not all list elements are known to be OneByteString(s).
914 // 'strings' must be an _List or _GrowableList. 917 // 'strings' must be an _List or _GrowableList.
915 static String _concatRangeNative(List<String> strings, int start, int end) 918 static String _concatRangeNative(List<String> strings, int start, int end)
916 native "String_concatRange"; 919 native "String_concatRange";
917 } 920 }
918 921
922
919 class _OneByteString extends _StringBase implements String { 923 class _OneByteString extends _StringBase implements String {
924
920 factory _OneByteString._uninstantiable() { 925 factory _OneByteString._uninstantiable() {
921 throw new UnsupportedError( 926 throw new UnsupportedError(
922 "_OneByteString can only be allocated by the VM"); 927 "_OneByteString can only be allocated by the VM");
923 } 928 }
924 929
925 int get hashCode native "String_getHashCode"; 930 int get hashCode native "String_getHashCode";
926 931
927 int codeUnitAt(int index) native "String_codeUnitAt"; 932 int codeUnitAt(int index) native "String_codeUnitAt";
928 933
929 bool _isWhitespace(int codeUnit) { 934 bool _isWhitespace(int codeUnit) {
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
1006 if (this.codeUnitAt(i) == patternCu0) { 1011 if (this.codeUnitAt(i) == patternCu0) {
1007 return true; 1012 return true;
1008 } 1013 }
1009 } 1014 }
1010 return false; 1015 return false;
1011 } 1016 }
1012 } 1017 }
1013 return super.contains(pattern, start); 1018 return super.contains(pattern, start);
1014 } 1019 }
1015 1020
1016 String operator *(int times) { 1021 String operator*(int times) {
1017 if (times <= 0) return ""; 1022 if (times <= 0) return "";
1018 if (times == 1) return this; 1023 if (times == 1) return this;
1019 int length = this.length; 1024 int length = this.length;
1020 if (this.isEmpty) return this; // Don't clone empty string. 1025 if (this.isEmpty) return this; // Don't clone empty string.
1021 _OneByteString result = _OneByteString._allocate(length * times); 1026 _OneByteString result = _OneByteString._allocate(length * times);
1022 int index = 0; 1027 int index = 0;
1023 for (int i = 0; i < times; i++) { 1028 for (int i = 0; i < times; i ++) {
1024 for (int j = 0; j < length; j++) { 1029 for (int j = 0; j < length; j++) {
1025 result._setAt(index++, this.codeUnitAt(j)); 1030 result._setAt(index++, this.codeUnitAt(j));
1026 } 1031 }
1027 } 1032 }
1028 return result; 1033 return result;
1029 } 1034 }
1030 1035
1031 String padLeft(int width, [String padding = ' ']) { 1036 String padLeft(int width, [String padding = ' ']) {
1032 int padCid = ClassID.getID(padding); 1037 int padCid = ClassID.getID(padding);
1033 if ((padCid != ClassID.cidOneByteString) && 1038 if ((padCid != ClassID.cidOneByteString) &&
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
1181 } 1186 }
1182 return result; 1187 return result;
1183 } 1188 }
1184 return this; 1189 return this;
1185 } 1190 }
1186 1191
1187 // Allocates a string of given length, expecting its content to be 1192 // Allocates a string of given length, expecting its content to be
1188 // set using _setAt. 1193 // set using _setAt.
1189 static _OneByteString _allocate(int length) native "OneByteString_allocate"; 1194 static _OneByteString _allocate(int length) native "OneByteString_allocate";
1190 1195
1191 static _OneByteString _allocateFromOneByteList(List<int> list, int start, 1196
1192 int end) native "OneByteString_allocateFromOneByteList"; 1197 static _OneByteString _allocateFromOneByteList(List<int> list,
1198 int start, int end)
1199 native "OneByteString_allocateFromOneByteList";
1193 1200
1194 // This is internal helper method. Code point value must be a valid 1201 // This is internal helper method. Code point value must be a valid
1195 // Latin1 value (0..0xFF), index must be valid. 1202 // Latin1 value (0..0xFF), index must be valid.
1196 void _setAt(int index, int codePoint) native "OneByteString_setAt"; 1203 void _setAt(int index, int codePoint) native "OneByteString_setAt";
1197 1204
1198 // Should be optimizable to a memory move. 1205 // Should be optimizable to a memory move.
1199 // Accepts both _OneByteString and _ExternalOneByteString as argument. 1206 // Accepts both _OneByteString and _ExternalOneByteString as argument.
1200 // Returns index after last character written. 1207 // Returns index after last character written.
1201 int _setRange(int index, String oneByteString, int start, int end) { 1208 int _setRange(int index, String oneByteString, int start, int end) {
1202 assert(oneByteString._isOneByte); 1209 assert(oneByteString._isOneByte);
1203 assert(0 <= start); 1210 assert(0 <= start);
1204 assert(start <= end); 1211 assert(start <= end);
1205 assert(end <= oneByteString.length); 1212 assert(end <= oneByteString.length);
1206 assert(0 <= index); 1213 assert(0 <= index);
1207 assert(index + (end - start) <= length); 1214 assert(index + (end - start) <= length);
1208 for (int i = start; i < end; i++) { 1215 for (int i = start; i < end; i++) {
1209 _setAt(index, oneByteString.codeUnitAt(i)); 1216 _setAt(index, oneByteString.codeUnitAt(i));
1210 index += 1; 1217 index += 1;
1211 } 1218 }
1212 return index; 1219 return index;
1213 } 1220 }
1214 } 1221 }
1215 1222
1223
1216 class _TwoByteString extends _StringBase implements String { 1224 class _TwoByteString extends _StringBase implements String {
1217 factory _TwoByteString._uninstantiable() { 1225 factory _TwoByteString._uninstantiable() {
1218 throw new UnsupportedError( 1226 throw new UnsupportedError(
1219 "_TwoByteString can only be allocated by the VM"); 1227 "_TwoByteString can only be allocated by the VM");
1220 } 1228 }
1221 1229
1222 static String _allocateFromTwoByteList(List list, int start, int end) 1230 static String _allocateFromTwoByteList(List list, int start, int end)
1223 native "TwoByteString_allocateFromTwoByteList"; 1231 native "TwoByteString_allocateFromTwoByteList";
1224 1232
1225 bool _isWhitespace(int codeUnit) { 1233 bool _isWhitespace(int codeUnit) {
1226 return _StringBase._isTwoByteWhitespace(codeUnit); 1234 return _StringBase._isTwoByteWhitespace(codeUnit);
1227 } 1235 }
1228 1236
1229 int codeUnitAt(int index) native "String_codeUnitAt"; 1237 int codeUnitAt(int index) native "String_codeUnitAt";
1230 1238
1231 bool operator ==(Object other) { 1239 bool operator ==(Object other) {
1232 return super == other; 1240 return super == other;
1233 } 1241 }
1234 } 1242 }
1235 1243
1244
1236 class _ExternalOneByteString extends _StringBase implements String { 1245 class _ExternalOneByteString extends _StringBase implements String {
1237 factory _ExternalOneByteString._uninstantiable() { 1246 factory _ExternalOneByteString._uninstantiable() {
1238 throw new UnsupportedError( 1247 throw new UnsupportedError(
1239 "_ExternalOneByteString can only be allocated by the VM"); 1248 "_ExternalOneByteString can only be allocated by the VM");
1240 } 1249 }
1241 1250
1242 bool _isWhitespace(int codeUnit) { 1251 bool _isWhitespace(int codeUnit) {
1243 return _StringBase._isOneByteWhitespace(codeUnit); 1252 return _StringBase._isOneByteWhitespace(codeUnit);
1244 } 1253 }
1245 1254
1246 int codeUnitAt(int index) native "String_codeUnitAt"; 1255 int codeUnitAt(int index) native "String_codeUnitAt";
1247 1256
1248 bool operator ==(Object other) { 1257 bool operator ==(Object other) {
1249 return super == other; 1258 return super == other;
1250 } 1259 }
1251 1260
1252 static int _getCid() native "ExternalOneByteString_getCid"; 1261 static int _getCid() native "ExternalOneByteString_getCid";
1253 } 1262 }
1254 1263
1264
1255 class _ExternalTwoByteString extends _StringBase implements String { 1265 class _ExternalTwoByteString extends _StringBase implements String {
1256 factory _ExternalTwoByteString._uninstantiable() { 1266 factory _ExternalTwoByteString._uninstantiable() {
1257 throw new UnsupportedError( 1267 throw new UnsupportedError(
1258 "_ExternalTwoByteString can only be allocated by the VM"); 1268 "_ExternalTwoByteString can only be allocated by the VM");
1259 } 1269 }
1260 1270
1261 bool _isWhitespace(int codeUnit) { 1271 bool _isWhitespace(int codeUnit) {
1262 return _StringBase._isTwoByteWhitespace(codeUnit); 1272 return _StringBase._isTwoByteWhitespace(codeUnit);
1263 } 1273 }
1264 1274
1265 int codeUnitAt(int index) native "String_codeUnitAt"; 1275 int codeUnitAt(int index) native "String_codeUnitAt";
1266 1276
1267 bool operator ==(Object other) { 1277 bool operator ==(Object other) {
1268 return super == other; 1278 return super == other;
1269 } 1279 }
1270 } 1280 }
1271 1281
1282
1272 class _StringMatch implements Match { 1283 class _StringMatch implements Match {
1273 const _StringMatch(this.start, this.input, this.pattern); 1284 const _StringMatch(this.start, this.input, this.pattern);
1274 1285
1275 int get end => start + pattern.length; 1286 int get end => start + pattern.length;
1276 String operator [](int g) => group(g); 1287 String operator[](int g) => group(g);
1277 int get groupCount => 0; 1288 int get groupCount => 0;
1278 1289
1279 String group(int group) { 1290 String group(int group) {
1280 if (group != 0) { 1291 if (group != 0) {
1281 throw new RangeError.value(group); 1292 throw new RangeError.value(group);
1282 } 1293 }
1283 return pattern; 1294 return pattern;
1284 } 1295 }
1285 1296
1286 List<String> groups(List<int> groups) { 1297 List<String> groups(List<int> groups) {
1287 List<String> result = new List<String>(); 1298 List<String> result = new List<String>();
1288 for (int g in groups) { 1299 for (int g in groups) {
1289 result.add(group(g)); 1300 result.add(group(g));
1290 } 1301 }
1291 return result; 1302 return result;
1292 } 1303 }
1293 1304
1294 final int start; 1305 final int start;
1295 final String input; 1306 final String input;
1296 final String pattern; 1307 final String pattern;
1297 } 1308 }
1298 1309
1310
1299 class _StringAllMatchesIterable extends Iterable<Match> { 1311 class _StringAllMatchesIterable extends Iterable<Match> {
1300 final String _input; 1312 final String _input;
1301 final String _pattern; 1313 final String _pattern;
1302 final int _index; 1314 final int _index;
1303 1315
1304 _StringAllMatchesIterable(this._input, this._pattern, this._index); 1316 _StringAllMatchesIterable(this._input, this._pattern, this._index);
1305 1317
1306 Iterator<Match> get iterator => 1318 Iterator<Match> get iterator =>
1307 new _StringAllMatchesIterator(_input, _pattern, _index); 1319 new _StringAllMatchesIterator(_input, _pattern, _index);
1308 1320
(...skipping 28 matching lines...) Expand all
1337 int end = index + _pattern.length; 1349 int end = index + _pattern.length;
1338 _current = new _StringMatch(index, _input, _pattern); 1350 _current = new _StringMatch(index, _input, _pattern);
1339 // Empty match, don't start at same location again. 1351 // Empty match, don't start at same location again.
1340 if (end == _index) end++; 1352 if (end == _index) end++;
1341 _index = end; 1353 _index = end;
1342 return true; 1354 return true;
1343 } 1355 }
1344 1356
1345 Match get current => _current; 1357 Match get current => _current;
1346 } 1358 }
OLDNEW
« no previous file with comments | « runtime/lib/string_buffer_patch.dart ('k') | runtime/lib/symbol_patch.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698