blob: feba1a3665b743ac6ea687ba39c34d63486e3f01 [file] [log] [blame]
// Copyright (c) 2011, 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.
part of dart.core;
/**
* A class for concatenating strings efficiently.
*
* Allows for the incremental building of a string using write*() methods.
* The strings are concatenated to a single string only when [toString] is
* called.
*/
class StringBuffer implements StringSink {
/** Creates the string buffer with an initial content. */
StringBuffer([Object content = ""]) {
write(content);
}
/**
* Returns the length of the content that has been accumulated so far.
* This is a constant-time operation.
*/
int get length => _partsCodeUnits + _bufferPosition;
/** Returns whether the buffer is empty. This is a constant-time operation. */
bool get isEmpty => length == 0;
/**
* Returns whether the buffer is not empty. This is a constant-time
* operation.
*/
bool get isNotEmpty => !isEmpty;
/// Adds the contents of [obj], converted to a string, to the buffer.
void write(Object obj) {
String str = '$obj';
if (str.isEmpty) return;
_consumeBuffer();
_addPart(str);
}
/// Adds the string representation of [charCode] to the buffer.
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;
}
}
void writeAll(Iterable objects, [String separator = ""]) {
Iterator iterator = objects.iterator;
if (!iterator.moveNext()) return;
if (separator.isEmpty) {
do {
write(iterator.current);
} while (iterator.moveNext());
} else {
write(iterator.current);
while (iterator.moveNext()) {
write(separator);
write(iterator.current);
}
}
}
void writeln([Object obj = ""]) {
write(obj);
write("\n");
}
/**
* Clears the string buffer.
*/
void clear() {
_parts = null;
_partsCodeUnits = _bufferPosition = _bufferCodeUnitMagnitude = 0;
}
/// Returns the contents of buffer as a concatenated string.
String toString() {
_consumeBuffer();
return (_partsCodeUnits == 0)
? ""
: _StringBase._concatRange(_parts, 0, _parts.length);
}
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;
/** 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) {
// Empirically this is a good capacity to minimize total bytes allocated.
_parts = new _GrowableList<String>.withCapacity(10)..add(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";
}