blob: b23aa96bcf3e07236a70ed4cfc8e0fe1670ab3aa [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 protobuf;
class CodedBufferWriter {
final List<TypedData> _output = <TypedData>[];
int _runningSizeInBytes = 0;
static final _WRITE_FUNCTION_MAP = _makeWriteFunctionMap();
static ByteData _toVarint32(int value) {
// Varint encoding always fits into 5 bytes.
Uint8List result = new Uint8List(5);
int i = 0;
while (value >= 0x80) {
result[i++] = 0x80 | (value & 0x7f);
value >>= 7;
}
result[i++] = value;
return new ByteData.view(result.buffer, 0, i);
}
static ByteData _toVarint64(Int64 value) {
// Varint encoding always fits into 10 bytes.
Uint8List result = new Uint8List(10);
int i = 0;
ByteData bytes =
new ByteData.view(new Uint8List.fromList(value.toBytes()).buffer, 0, 8);
int lo = bytes.getUint32(0, Endianness.LITTLE_ENDIAN);
int hi = bytes.getUint32(4, Endianness.LITTLE_ENDIAN);
while (hi > 0 || lo >= 0x80) {
result[i++] = 0x80 | (lo & 0x7f);
lo = (lo >> 7) | ((hi & 0x7f) << 25);
hi >>= 7;
}
result[i++] = lo;
return new ByteData.view(result.buffer, 0, i);
}
static ByteData _int32ToBytes(int value) => _toVarint32(value & 0xffffffff);
static _makeWriteFunctionMap() {
writeBytesNoTag(output, List<int> value) {
output.writeInt32NoTag(value.length);
output.writeRawBytes(
new Uint8List(value.length)..setRange(0, value.length, value));
}
makeWriter(convertor) => ((output, value) {
output.writeRawBytes(convertor(value));
});
int _encodeZigZag32(int value) => (value << 1) ^ (value >> 31);
Int64 _encodeZigZag64(Int64 value) => (value << 1) ^ (value >> 63);
ByteData makeByteData32(int value) =>
new ByteData(4)..setUint32(0, value, Endianness.LITTLE_ENDIAN);
ByteData makeByteData64(Int64 value) {
var data = new Uint8List.fromList(value.toBytes());
return new ByteData.view(data.buffer, 0, 8);
}
return new Map<int, dynamic>()
..[PbFieldType._BOOL_BIT] = makeWriter(
(value) => _int32ToBytes(value ? 1 : 0))
..[PbFieldType._BYTES_BIT] = writeBytesNoTag
..[PbFieldType._STRING_BIT] = (output, value) {
writeBytesNoTag(output, _UTF8.encode(value));
}
..[PbFieldType._DOUBLE_BIT] = makeWriter((double value) {
if (value.isNaN) return new ByteData(8)
..setUint32(0, 0x00000000, Endianness.LITTLE_ENDIAN)
..setUint32(4, 0x7ff80000, Endianness.LITTLE_ENDIAN);
return new ByteData(8)
..setFloat64(0, value, Endianness.LITTLE_ENDIAN);
})
..[PbFieldType._FLOAT_BIT] = makeWriter((double value) {
const double MIN_FLOAT_DENORM = 1.401298464324817E-45;
const double MAX_FLOAT = 3.4028234663852886E38;
// TODO(antonm): reevaluate once semantics of odd values
// writes is clear.
if (value.isNaN) return makeByteData32(0x7fc00000);
if (value.abs() < MIN_FLOAT_DENORM) {
return makeByteData32(value.isNegative ? 0x80000000 : 0x00000000);
}
if (value.isInfinite || value.abs() > MAX_FLOAT) {
return makeByteData32(value.isNegative ? 0xff800000 : 0x7f800000);
}
return new ByteData(4)
..setFloat32(0, value, Endianness.LITTLE_ENDIAN);
})
..[PbFieldType._ENUM_BIT] = makeWriter(
(value) => _int32ToBytes(value.value))
..[PbFieldType._GROUP_BIT] = (output, value) {
value.writeToCodedBufferWriter(output);
}
..[PbFieldType._INT32_BIT] = makeWriter(_int32ToBytes)
..[PbFieldType._INT64_BIT] = makeWriter(
(value) => _toVarint64(value))
..[PbFieldType._SINT32_BIT] = makeWriter(
(int value) => _int32ToBytes(_encodeZigZag32(value)))
..[PbFieldType._SINT64_BIT] = makeWriter(
(Int64 value) => _toVarint64(_encodeZigZag64(value)))
..[PbFieldType._UINT32_BIT] = makeWriter(_toVarint32)
..[PbFieldType._UINT64_BIT] = makeWriter(_toVarint64)
..[PbFieldType._FIXED32_BIT] = makeWriter(makeByteData32)
..[PbFieldType._FIXED64_BIT] = makeWriter(makeByteData64)
..[PbFieldType._SFIXED32_BIT] = makeWriter(makeByteData32)
..[PbFieldType._SFIXED64_BIT] = makeWriter(makeByteData64)
..[PbFieldType._MESSAGE_BIT] = (output, value) {
output._withDeferredSizeCalculation(() {
value.writeToCodedBufferWriter(output);
});
};
}
static final _OPEN_TAG_MAP = _makeOpenTagMap();
static _makeOpenTagMap() {
return new Map<int, int>()
..[PbFieldType._BOOL_BIT] = WIRETYPE_VARINT
..[PbFieldType._BYTES_BIT] = WIRETYPE_LENGTH_DELIMITED
..[PbFieldType._STRING_BIT] = WIRETYPE_LENGTH_DELIMITED
..[PbFieldType._DOUBLE_BIT] = WIRETYPE_FIXED64
..[PbFieldType._FLOAT_BIT] = WIRETYPE_FIXED32
..[PbFieldType._ENUM_BIT] = WIRETYPE_VARINT
..[PbFieldType._GROUP_BIT] = WIRETYPE_START_GROUP
..[PbFieldType._INT32_BIT] = WIRETYPE_VARINT
..[PbFieldType._INT64_BIT] = WIRETYPE_VARINT
..[PbFieldType._SINT32_BIT] = WIRETYPE_VARINT
..[PbFieldType._SINT64_BIT] = WIRETYPE_VARINT
..[PbFieldType._UINT32_BIT] = WIRETYPE_VARINT
..[PbFieldType._UINT64_BIT] = WIRETYPE_VARINT
..[PbFieldType._FIXED32_BIT] = WIRETYPE_FIXED32
..[PbFieldType._FIXED64_BIT] = WIRETYPE_FIXED64
..[PbFieldType._SFIXED32_BIT] = WIRETYPE_FIXED32
..[PbFieldType._SFIXED64_BIT] = WIRETYPE_FIXED64
..[PbFieldType._MESSAGE_BIT] = WIRETYPE_LENGTH_DELIMITED;
}
void _withDeferredSizeCalculation(continuation) {
// Reserve a place for size data.
int index = _output.length;
_output.add(null);
int currentRunningSizeInBytes = _runningSizeInBytes;
continuation();
int writtenSizeInBytes = _runningSizeInBytes - currentRunningSizeInBytes;
TypedData sizeMarker = _int32ToBytes(writtenSizeInBytes);
_output[index] = sizeMarker;
_runningSizeInBytes += sizeMarker.lengthInBytes;
}
void writeField(int fieldNumber, int fieldType, fieldValue) {
var valueType = fieldType & ~0x07;
var writeFunction = _WRITE_FUNCTION_MAP[valueType];
writeTag(int wireFormat) {
writeInt32NoTag(makeTag(fieldNumber, wireFormat));
}
if ((fieldType & PbFieldType._PACKED_BIT) != 0) {
if (!fieldValue.isEmpty) {
writeTag(WIRETYPE_LENGTH_DELIMITED);
_withDeferredSizeCalculation(() {
for (var value in fieldValue) {
writeFunction(this, value);
}
});
}
return;
}
writeValue(value) {
writeTag(_OPEN_TAG_MAP[valueType]);
writeFunction(this, value);
if (valueType == PbFieldType._GROUP_BIT) {
writeTag(WIRETYPE_END_GROUP);
}
}
if ((fieldType & PbFieldType._REPEATED_BIT) != 0) {
fieldValue.forEach(writeValue);
return;
}
writeValue(fieldValue);
}
void writeInt32NoTag(int value) {
writeRawBytes(_int32ToBytes(value));
}
void writeRawBytes(TypedData value) {
_output.add(value);
_runningSizeInBytes += value.lengthInBytes;
}
Uint8List toBuffer() {
Uint8List result = new Uint8List(_runningSizeInBytes);
int position = 0;
for (var typedData in _output) {
Uint8List asBytes = new Uint8List.view(
typedData.buffer, typedData.offsetInBytes, typedData.lengthInBytes);
result.setRange(position, position + typedData.lengthInBytes, asBytes);
position += typedData.lengthInBytes;
}
return result;
}
}