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

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

Issue 24251012: Improve the memory consumption and performance of our string buffer implementation. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Update comment. Created 7 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 | Annotate | Revision Log
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 patch class StringBuffer { 5 patch class StringBuffer {
6 /** Backing store for collected UTF-16 code units. */ 6 static const int _BUFFER_SIZE = 64;
7 Uint16List _buffer; 7 static const int _PARTS_TO_COMPACT = 128;
8 /** Number of code units collected. */ 8 static const int _PARTS_TO_COMPACT_SIZE_LIMIT = _PARTS_TO_COMPACT * 8;
9 int _length = 0; 9
10 /** 10 /**
11 * Collects the approximate maximal magnitude of the added code units. 11 * When strings are written to the string buffer, we add them to a
12 * list of string parts.
13 */
14 List _parts = null;
15
16 /**
17 * Total number of code units in the string parts. Does not include
18 * the code units added to the buffer.
19 */
20 int _partsCodeUnits = 0;
21
22 /**
23 * To preserve memory, we sometimes compact the parts. This combines
24 * several smaller parts into a single larger part to cut down on the
25 * cost that comes from the per-object memory overhead. We keep track
26 * of the last index where we ended our compaction and the number of
27 * code units added since the last compaction.
28 */
29 int _partsCompactionIndex = 0;
30 int _partsCodeUnitsSinceCompaction = 0;
31 _ObjectArray _partsCompactionArray = null;
32
33 /**
34 * The buffer is used to build up a string from code units. It is
35 * used when writing short strings or individul char codes to the
36 * buffer. The buffer is allocated on demand.
37 */
38 Uint16List _buffer = null;
39 int _bufferPosition = 0;
40
41 /**
42 * Collects the approximate maximal magnitude of the code units added
43 * to the buffer.
12 * 44 *
13 * The value of each added code unit is or'ed with this variable, so the 45 * The value of each added code unit is or'ed with this variable, so the
14 * most significant bit set in any code unit is also set in this value. 46 * most significant bit set in any code unit is also set in this value.
15 * If below 256, the string is a Latin-1 string. 47 * If below 256, the string in the buffer is a Latin-1 string.
16 */ 48 */
17 int _codeUnitMagnitude = 0; 49 int _bufferCodeUnitMagnitude = 0;
18 50
19 /// Creates the string buffer with an initial content. 51 /// Creates the string buffer with an initial content.
20 /* patch */ StringBuffer([Object content = ""]) 52 /* patch */ StringBuffer([Object content = ""]) {
21 : _buffer = new Uint16List(16) {
22 write(content); 53 write(content);
23 } 54 }
24 55
25 /* patch */ int get length => _length; 56 /* patch */ int get length => _partsCodeUnits + _bufferPosition;
26 57
27 /// Adds [obj] to the buffer.
28 /* patch */ void write(Object obj) { 58 /* patch */ void write(Object obj) {
29 String str; 59 String str;
30 if (obj is String) { 60 if (obj is String) {
31 str = obj; 61 str = obj;
32 } else { 62 } else {
33 // TODO(srdjan): The following four lines could be replaced by 63 // TODO(srdjan): The following four lines could be replaced by
34 // '$obj', but apparently this is too slow on the Dart VM. 64 // '$obj', but apparently this is too slow on the Dart VM.
Lasse Reichstein Nielsen 2013/09/20 11:40:17 Is this still true?
35 str = obj.toString(); 65 str = obj.toString();
36 if (str is! String) { 66 if (str is! String) {
37 throw new ArgumentError('toString() did not return a string'); 67 throw new ArgumentError('toString() did not return a string');
38 } 68 }
39 } 69 }
40 if (str.isEmpty) return; 70 if (str.isEmpty) return;
41 _ensureCapacity(str.length); 71 _consumeBuffer();
42 for (int i = 0; i < str.length; i++) { 72 _addPart(str);
43 int unit = str.codeUnitAt(i);
44 _buffer[_length + i] = unit;
45 _codeUnitMagnitude |= unit;
46 }
47 _length += str.length;
48 } 73 }
49 74
50 /* patch */ void writeCharCode(int charCode) { 75 /* patch */ void writeCharCode(int charCode) {
51 if (charCode <= 0xFFFF) { 76 if (charCode <= 0xFFFF) {
52 if (charCode < 0) { 77 if (charCode < 0) {
53 throw new RangeError.range(charCode, 0, 0x10FFFF); 78 throw new RangeError.range(charCode, 0, 0x10FFFF);
54 } 79 }
55 _ensureCapacity(1); 80 _ensureCapacity(1);
56 _buffer[_length++] = charCode; 81 _buffer[_bufferPosition++] = charCode;
57 _codeUnitMagnitude |= charCode; 82 _bufferCodeUnitMagnitude |= charCode;
58 } else { 83 } else {
59 if (charCode > 0x10FFFF) { 84 if (charCode > 0x10FFFF) {
60 throw new RangeError.range(charCode, 0, 0x10FFFF); 85 throw new RangeError.range(charCode, 0, 0x10FFFF);
61 } 86 }
62 _ensureCapacity(2); 87 _ensureCapacity(2);
63 int bits = charCode - 0x10000; 88 int bits = charCode - 0x10000;
64 _buffer[_length++] = 0xD800 | (bits >> 10); 89 _buffer[_bufferPosition++] = 0xD800 | (bits >> 10);
65 _buffer[_length++] = 0xDC00 | (bits & 0x3FF); 90 _buffer[_bufferPosition++] = 0xDC00 | (bits & 0x3FF);
66 _codeUnitMagnitude |= 0xFFFF; 91 _bufferCodeUnitMagnitude |= 0xFFFF;
67 } 92 }
68 } 93 }
69 94
70 /** Makes the buffer empty. */ 95 /** Makes the buffer empty. */
71 /* patch */ void clear() { 96 /* patch */ void clear() {
72 _length = 0; 97 _parts = null;
73 _codeUnitMagnitude = 0; 98 _partsCodeUnits = _bufferPosition = _bufferCodeUnitMagnitude = 0;
74 } 99 }
75 100
76 /** Returns the contents of buffer as a string. */ 101 /** Returns the contents of buffer as a string. */
77 /* patch */ String toString() { 102 /* patch */ String toString() {
78 if (_length == 0) return ""; 103 _consumeBuffer();
79 bool isLatin1 = _codeUnitMagnitude <= 0xFF; 104 if (_partsCodeUnits == 0) return "";
80 return _create(_buffer, _length, isLatin1); 105
106 // TODO(kasperl): It would be nice if concatAllNative would
107 // allow me to pass in a grownable array directly, but for
Lasse Reichstein Nielsen 2013/09/20 11:40:17 Should be fairly simple to do.
kasperl 2013/09/20 12:04:27 I guess I really want something like a _concatAllN
srdjan 2013/09/25 20:56:40 s/grownable/growable/
kasperl 2013/09/26 05:30:29 :-) Maybe you could help me fix the TODO? If conc
srdjan 2013/09/26 13:22:45 Yes, I will do that.
108 // now we have to copy the contents to a non-growable array.
109 int length = _parts.length;
110 _ObjectArray array = new _ObjectArray(length);
111 for (int i = 0; i < length; i++) array[i] = _parts[i];
112 return _StringBase._concatAllNative(array);
81 } 113 }
82 114
83 /** Ensures that the buffer has enough capacity to add n code units. */ 115 /** Ensures that the buffer has enough capacity to add n code units. */
84 void _ensureCapacity(int n) { 116 void _ensureCapacity(int n) {
85 int requiredCapacity = _length + n; 117 if (_buffer == null) {
86 if (requiredCapacity > _buffer.length) { 118 _buffer = new Uint16List(_BUFFER_SIZE);
87 _grow(requiredCapacity); 119 } else if (_bufferPosition + n > _buffer.length) {
120 _consumeBuffer();
88 } 121 }
89 } 122 }
90 123
91 /** Grows the buffer until it can contain [requiredCapacity] entries. */ 124 /**
92 void _grow(int requiredCapacity) { 125 * Consumes the content of the buffer by turning it into a string
93 int newCapacity = _buffer.length; 126 * and adding it as a part. After calling this the buffer position
94 do { 127 * will be reset to zero.
95 newCapacity *= 2; 128 */
96 } while (newCapacity < requiredCapacity); 129 void _consumeBuffer() {
97 List<int> newBuffer = new Uint16List(newCapacity); 130 if (_bufferPosition == 0) return;
98 newBuffer.setRange(0, _length, _buffer); 131 bool isLatin1 = _bufferCodeUnitMagnitude <= 0xFF;
99 _buffer = newBuffer; 132 String str = _create(_buffer, _bufferPosition, isLatin1);
133 _bufferPosition = _bufferCodeUnitMagnitude = 0;
Lasse Reichstein Nielsen 2013/09/20 11:40:17 Consider growing the buffer eventually?
134 _addPart(str);
100 } 135 }
101 136
102 /** 137 /**
138 * Adds a new part to this string buffer and keeps track of how
139 * many code units are contained in the parts.
140 */
141 void _addPart(String str) {
142 int length = str.length;
143 _partsCodeUnits += length;
144 _partsCodeUnitsSinceCompaction += length;
145
146 if (_parts == null) {
147 _parts = [ str ];
148 } else {
149 _parts.add(str);
150 int partsSinceCompaction = _parts.length - _partsCompactionIndex;
151 if (partsSinceCompaction == _PARTS_TO_COMPACT) {
152 _compact();
153 }
154 }
155 }
156
157 /**
158 * Compacts the last N parts if their average size allows us to save a
159 * lot of memory by turning them all into a single part.
160 */
161 void _compact() {
162 if (_partsCodeUnitsSinceCompaction < _PARTS_TO_COMPACT_SIZE_LIMIT) {
163 if (_partsCompactionArray == null) {
164 _partsCompactionArray = new _ObjectArray(_PARTS_TO_COMPACT);
Lasse Reichstein Nielsen 2013/09/20 11:40:17 Consider whether reusing the array is worth it - i
kasperl 2013/09/20 12:04:27 I've tried both and reusing the array seems prefer
165 }
166 for (int i = 0; i < _PARTS_TO_COMPACT; i++) {
167 _partsCompactionArray[i] = _parts[i + _partsCompactionIndex];
168 }
169 String compacted = _StringBase._concatAllNative(_partsCompactionArray);
170 _parts.length = _parts.length - _PARTS_TO_COMPACT;
171 _parts.add(compacted);
172 for (int i = 0; i < _PARTS_TO_COMPACT; i++) {
173 _partsCompactionArray[i] = null;
174 }
175 }
176 _partsCodeUnitsSinceCompaction = 0;
177 _partsCompactionIndex = _parts.length;
178 }
179
180 /**
103 * Create a [String] from the UFT-16 code units in buffer. 181 * Create a [String] from the UFT-16 code units in buffer.
104 */ 182 */
105 static String _create(Uint16List buffer, int length, bool isLatin1) 183 static String _create(Uint16List buffer, int length, bool isLatin1)
106 native "StringBuffer_createStringFromUint16Array"; 184 native "StringBuffer_createStringFromUint16Array";
107 } 185 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698