| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| patch class StringBuffer { | |
| static const int _BUFFER_SIZE = 64; | |
| static const int _PARTS_TO_COMPACT = 128; | |
| static const int _PARTS_TO_COMPACT_SIZE_LIMIT = _PARTS_TO_COMPACT * 8; | |
| /** | |
| * When strings are written to the string buffer, we add them to a | |
| * list of string parts. | |
| */ | |
| List<String> _parts; | |
| /** | |
| * Total number of code units in the string parts. Does not include | |
| * the code units added to the buffer. | |
| */ | |
| int _partsCodeUnits = 0; | |
| /** | |
| * To preserve memory, we sometimes compact the parts. This combines | |
| * several smaller parts into a single larger part to cut down on the | |
| * cost that comes from the per-object memory overhead. We keep track | |
| * of the last index where we ended our compaction and the number of | |
| * code units added since the last compaction. | |
| */ | |
| int _partsCompactionIndex = 0; | |
| int _partsCodeUnitsSinceCompaction = 0; | |
| /** | |
| * The buffer is used to build up a string from code units. It is | |
| * used when writing short strings or individual char codes to the | |
| * buffer. The buffer is allocated on demand. | |
| */ | |
| Uint16List _buffer; | |
| int _bufferPosition = 0; | |
| /** | |
| * Collects the approximate maximal magnitude of the code units added | |
| * to the buffer. | |
| * | |
| * The value of each added code unit is or'ed with this variable, so the | |
| * most significant bit set in any code unit is also set in this value. | |
| * If below 256, the string in the buffer is a Latin-1 string. | |
| */ | |
| int _bufferCodeUnitMagnitude = 0; | |
| /// Creates the string buffer with an initial content. | |
| /* patch */ StringBuffer([Object content = ""]) { | |
| write(content); | |
| } | |
| /* patch */ int get length => _partsCodeUnits + _bufferPosition; | |
| /* patch */ void write(Object obj) { | |
| String str; | |
| if (obj is String) { | |
| str = obj; | |
| } else { | |
| // TODO(srdjan): The following four lines could be replaced by | |
| // '$obj', but apparently this is too slow on the Dart VM. | |
| str = obj.toString(); | |
| if (str is! String) { | |
| throw new ArgumentError('toString() did not return a string'); | |
| } | |
| } | |
| if (str.isEmpty) return; | |
| _consumeBuffer(); | |
| _addPart(str); | |
| } | |
| /* patch */ void writeCharCode(int charCode) { | |
| if (charCode <= 0xFFFF) { | |
| if (charCode < 0) { | |
| throw new RangeError.range(charCode, 0, 0x10FFFF); | |
| } | |
| _ensureCapacity(1); | |
| _buffer[_bufferPosition++] = charCode; | |
| _bufferCodeUnitMagnitude |= charCode; | |
| } else { | |
| if (charCode > 0x10FFFF) { | |
| throw new RangeError.range(charCode, 0, 0x10FFFF); | |
| } | |
| _ensureCapacity(2); | |
| int bits = charCode - 0x10000; | |
| _buffer[_bufferPosition++] = 0xD800 | (bits >> 10); | |
| _buffer[_bufferPosition++] = 0xDC00 | (bits & 0x3FF); | |
| _bufferCodeUnitMagnitude |= 0xFFFF; | |
| } | |
| } | |
| /** Makes the buffer empty. */ | |
| /* patch */ void clear() { | |
| _parts = null; | |
| _partsCodeUnits = _bufferPosition = _bufferCodeUnitMagnitude = 0; | |
| } | |
| /** Returns the contents of buffer as a string. */ | |
| /* patch */ String toString() { | |
| _consumeBuffer(); | |
| return (_partsCodeUnits == 0) ? | |
| "" : | |
| _StringBase._concatRange(_parts, 0, _parts.length); | |
| } | |
| /** Ensures that the buffer has enough capacity to add n code units. */ | |
| void _ensureCapacity(int n) { | |
| if (_buffer == null) { | |
| _buffer = new Uint16List(_BUFFER_SIZE); | |
| } else if (_bufferPosition + n > _buffer.length) { | |
| _consumeBuffer(); | |
| } | |
| } | |
| /** | |
| * Consumes the content of the buffer by turning it into a string | |
| * and adding it as a part. After calling this the buffer position | |
| * will be reset to zero. | |
| */ | |
| void _consumeBuffer() { | |
| if (_bufferPosition == 0) return; | |
| bool isLatin1 = _bufferCodeUnitMagnitude <= 0xFF; | |
| String str = _create(_buffer, _bufferPosition, isLatin1); | |
| _bufferPosition = _bufferCodeUnitMagnitude = 0; | |
| _addPart(str); | |
| } | |
| /** | |
| * Adds a new part to this string buffer and keeps track of how | |
| * many code units are contained in the parts. | |
| */ | |
| void _addPart(String str) { | |
| int length = str.length; | |
| _partsCodeUnits += length; | |
| _partsCodeUnitsSinceCompaction += length; | |
| if (_parts == null) { | |
| _parts = [ str ]; | |
| } else { | |
| _parts.add(str); | |
| int partsSinceCompaction = _parts.length - _partsCompactionIndex; | |
| if (partsSinceCompaction == _PARTS_TO_COMPACT) { | |
| _compact(); | |
| } | |
| } | |
| } | |
| /** | |
| * Compacts the last N parts if their average size allows us to save a | |
| * lot of memory by turning them all into a single part. | |
| */ | |
| void _compact() { | |
| if (_partsCodeUnitsSinceCompaction < _PARTS_TO_COMPACT_SIZE_LIMIT) { | |
| String compacted = _StringBase._concatRange( | |
| _parts, | |
| _partsCompactionIndex, // Start | |
| _partsCompactionIndex + _PARTS_TO_COMPACT // End | |
| ); | |
| _parts.length = _parts.length - _PARTS_TO_COMPACT; | |
| _parts.add(compacted); | |
| } | |
| _partsCodeUnitsSinceCompaction = 0; | |
| _partsCompactionIndex = _parts.length; | |
| } | |
| /** | |
| * Create a [String] from the UFT-16 code units in buffer. | |
| */ | |
| static String _create(Uint16List buffer, int length, bool isLatin1) | |
| native "StringBuffer_createStringFromUint16Array"; | |
| } |