writeVarString method
Writes a length-prefixed UTF-8 encoded string.
First writes the UTF-8 byte length as a VarUint, followed by the UTF-8 encoded string data.
allowMalformed controls how invalid UTF-16 sequences are handled:
- If true (default): replaces lone surrogates with U+FFFD (�)
- If false: throws FormatException on malformed input
Example:
final text = 'Hello, 世界! 🌍';
writer.writeVarString(text);
This is equivalent to:
final byteLength = getUtf8Length(text);
writer.writeVarUint(byteLength);
writer.writeString(text);
Implementation
@pragma('vm:prefer-inline')
@pragma('dart2js:tryInline')
void writeVarString(String value, {bool allowMalformed = true}) {
final len = value.length;
if (len == 0) {
writeVarUint(0);
return;
}
// 1. Optimistically estimate the VarInt size based on string character
// length. For pure ASCII strings, byte length matches character length
// exactly.
final estimatedSize = _varIntSize(len);
// Cache the initial offset locally to avoid redundant heap lookups.
final startOffset = _ws.offset;
// Ensure enough space for the prefix and the worst-case UTF-8 scenario
// (3 bytes per unit).
_ws
..ensureSize(estimatedSize + len * 3)
// 2. Reserve space for the estimated length prefix using a fast direct
// assignment.
..offset = startOffset + estimatedSize;
// 3. Write the actual string data directly into the buffer.
writeString(value, allowMalformed: allowMalformed);
// Cache the offset immediately after writing to determine the exact byte
// length.
var currentOffset = _ws.offset;
final byteLength = currentOffset - (startOffset + estimatedSize);
// 4. Determine the actual VarInt size required for the encoded byte length.
final actualSize = _varIntSize(byteLength);
// 5. If the optimistic estimate was wrong (e.g., due to multi-byte UTF-8
// characters), shift the written string data to accommodate the actual
//VarInt size.
if (actualSize != estimatedSize) {
final shift = actualSize - estimatedSize;
if (shift > 0) {
_ws.ensureSize(shift);
}
// Perform a fast native memory shift (memmove) using setRange.
_ws.list.setRange(
startOffset + actualSize,
currentOffset + shift,
_ws.list,
startOffset + estimatedSize,
);
// Adjust the local tracker instead of modifying the heap property
// repeatedly.
currentOffset += shift;
}
// 6. Backtrack to the start offset and write the authentic VarInt length
// prefix.
_ws.offset = startOffset;
writeVarUint(byteLength);
// 7. Advance the buffer's final offset to the absolute end of the payload.
_ws.offset = currentOffset;
}