| // 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; |
| } |
| } |