| /** |
| * @fileoverview Implements Writer for writing data as the binary wire format |
| * bytes array. |
| */ |
| goog.module('protobuf.binary.Writer'); |
| |
| const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); |
| const ByteString = goog.require('protobuf.ByteString'); |
| const Int64 = goog.require('protobuf.Int64'); |
| const WireType = goog.require('protobuf.binary.WireType'); |
| const {POLYFILL_TEXT_ENCODING, checkFieldNumber, checkTypeUnsignedInt32, checkWireType} = goog.require('protobuf.internal.checks'); |
| const {concatenateByteArrays} = goog.require('protobuf.binary.uint8arrays'); |
| const {encode} = goog.require('protobuf.binary.textencoding'); |
| |
| /** |
| * Returns a valid utf-8 encoder function based on TextEncoder if available or |
| * a polyfill. |
| * Some of the environments we run in do not have TextEncoder defined. |
| * TextEncoder is faster than our polyfill so we prefer it over the polyfill. |
| * @return {function(string):!Uint8Array} |
| */ |
| function getEncoderFunction() { |
| if (goog.global['TextEncoder']) { |
| const textEncoder = new goog.global['TextEncoder']('utf-8'); |
| return s => s.length === 0 ? new Uint8Array(0) : textEncoder.encode(s); |
| } |
| if (POLYFILL_TEXT_ENCODING) { |
| return encode; |
| } else { |
| throw new Error( |
| 'TextEncoder is missing. ' + |
| 'Enable protobuf.defines.POLYFILL_TEXT_ENCODING'); |
| } |
| } |
| |
| /** @const {function(string): !Uint8Array} */ |
| const encoderFunction = getEncoderFunction(); |
| |
| /** |
| * Writer provides methods for encoding all protobuf supported type into a |
| * binary format bytes array. |
| * Check https://developers.google.com/protocol-buffers/docs/encoding for binary |
| * format definition. |
| * @final |
| * @package |
| */ |
| class Writer { |
| constructor() { |
| /** |
| * Blocks of data that needs to be serialized. After writing all the data, |
| * the blocks are concatenated into a single Uint8Array. |
| * @private {!Array<!Uint8Array>} |
| */ |
| this.blocks_ = []; |
| |
| /** |
| * A buffer for writing varint data (tag number + field number for each |
| * field, int32, uint32 etc.). Before writing a non-varint data block |
| * (string, fixed32 etc.), the buffer is appended to the block array as a |
| * new block, and a new buffer is started. |
| * |
| * We could've written each varint as a new block instead of writing |
| * multiple varints in this buffer. But this will increase the number of |
| * blocks, and concatenating many small blocks is slower than concatenating |
| * few large blocks. |
| * |
| * TODO: Experiment with writing data in a fixed-length |
| * Uint8Array instead of using a growing buffer. |
| * |
| * @private {!Array<number>} |
| */ |
| this.currentBuffer_ = []; |
| } |
| |
| /** |
| * Converts the encoded data into a Uint8Array. |
| * The writer is also reset. |
| * @return {!ArrayBuffer} |
| */ |
| getAndResetResultBuffer() { |
| this.closeAndStartNewBuffer_(); |
| const result = concatenateByteArrays(this.blocks_); |
| this.blocks_ = []; |
| return result.buffer; |
| } |
| |
| /** |
| * Encodes a (field number, wire type) tuple into a wire-format field header. |
| * @param {number} fieldNumber |
| * @param {!WireType} wireType |
| */ |
| writeTag(fieldNumber, wireType) { |
| checkFieldNumber(fieldNumber); |
| checkWireType(wireType); |
| const tag = fieldNumber << 3 | wireType; |
| this.writeUnsignedVarint32_(tag >>> 0); |
| } |
| |
| /** |
| * Appends the current buffer into the blocks array and starts a new buffer. |
| * @private |
| */ |
| closeAndStartNewBuffer_() { |
| this.blocks_.push(new Uint8Array(this.currentBuffer_)); |
| this.currentBuffer_ = []; |
| } |
| |
| /** |
| * Encodes a 32-bit integer into its wire-format varint representation and |
| * stores it in the buffer. |
| * @param {number} value |
| * @private |
| */ |
| writeUnsignedVarint32_(value) { |
| checkTypeUnsignedInt32(value); |
| while (value > 0x7f) { |
| this.currentBuffer_.push((value & 0x7f) | 0x80); |
| value = value >>> 7; |
| } |
| this.currentBuffer_.push(value); |
| } |
| |
| /**************************************************************************** |
| * OPTIONAL METHODS |
| ****************************************************************************/ |
| |
| /** |
| * Writes a boolean value field to the buffer as a varint. |
| * @param {boolean} value |
| * @private |
| */ |
| writeBoolValue_(value) { |
| this.currentBuffer_.push(value ? 1 : 0); |
| } |
| |
| /** |
| * Writes a boolean value field to the buffer as a varint. |
| * @param {number} fieldNumber |
| * @param {boolean} value |
| */ |
| writeBool(fieldNumber, value) { |
| this.writeTag(fieldNumber, WireType.VARINT); |
| this.writeBoolValue_(value); |
| } |
| |
| /** |
| * Writes a bytes value field to the buffer as a length delimited field. |
| * @param {number} fieldNumber |
| * @param {!ByteString} value |
| */ |
| writeBytes(fieldNumber, value) { |
| this.writeTag(fieldNumber, WireType.DELIMITED); |
| const buffer = value.toArrayBuffer(); |
| this.writeUnsignedVarint32_(buffer.byteLength); |
| this.writeRaw_(buffer); |
| } |
| |
| /** |
| * Writes a double value field to the buffer without tag. |
| * @param {number} value |
| * @private |
| */ |
| writeDoubleValue_(value) { |
| const buffer = new ArrayBuffer(8); |
| const view = new DataView(buffer); |
| view.setFloat64(0, value, true); |
| this.writeRaw_(buffer); |
| } |
| |
| /** |
| * Writes a double value field to the buffer. |
| * @param {number} fieldNumber |
| * @param {number} value |
| */ |
| writeDouble(fieldNumber, value) { |
| this.writeTag(fieldNumber, WireType.FIXED64); |
| this.writeDoubleValue_(value); |
| } |
| |
| /** |
| * Writes a fixed32 value field to the buffer without tag. |
| * @param {number} value |
| * @private |
| */ |
| writeFixed32Value_(value) { |
| const buffer = new ArrayBuffer(4); |
| const view = new DataView(buffer); |
| view.setUint32(0, value, true); |
| this.writeRaw_(buffer); |
| } |
| |
| /** |
| * Writes a fixed32 value field to the buffer. |
| * @param {number} fieldNumber |
| * @param {number} value |
| */ |
| writeFixed32(fieldNumber, value) { |
| this.writeTag(fieldNumber, WireType.FIXED32); |
| this.writeFixed32Value_(value); |
| } |
| |
| /** |
| * Writes a float value field to the buffer without tag. |
| * @param {number} value |
| * @private |
| */ |
| writeFloatValue_(value) { |
| const buffer = new ArrayBuffer(4); |
| const view = new DataView(buffer); |
| view.setFloat32(0, value, true); |
| this.writeRaw_(buffer); |
| } |
| |
| /** |
| * Writes a float value field to the buffer. |
| * @param {number} fieldNumber |
| * @param {number} value |
| */ |
| writeFloat(fieldNumber, value) { |
| this.writeTag(fieldNumber, WireType.FIXED32); |
| this.writeFloatValue_(value); |
| } |
| |
| /** |
| * Writes a int32 value field to the buffer as a varint without tag. |
| * @param {number} value |
| * @private |
| */ |
| writeInt32Value_(value) { |
| if (value >= 0) { |
| this.writeVarint64_(0, value); |
| } else { |
| this.writeVarint64_(0xFFFFFFFF, value); |
| } |
| } |
| |
| /** |
| * Writes a int32 value field to the buffer as a varint. |
| * @param {number} fieldNumber |
| * @param {number} value |
| */ |
| writeInt32(fieldNumber, value) { |
| this.writeTag(fieldNumber, WireType.VARINT); |
| this.writeInt32Value_(value); |
| } |
| |
| /** |
| * Writes a int64 value field to the buffer as a varint. |
| * @param {number} fieldNumber |
| * @param {!Int64} value |
| */ |
| writeInt64(fieldNumber, value) { |
| this.writeTag(fieldNumber, WireType.VARINT); |
| this.writeVarint64_(value.getHighBits(), value.getLowBits()); |
| } |
| |
| /** |
| * Writes a sfixed32 value field to the buffer. |
| * @param {number} value |
| * @private |
| */ |
| writeSfixed32Value_(value) { |
| const buffer = new ArrayBuffer(4); |
| const view = new DataView(buffer); |
| view.setInt32(0, value, true); |
| this.writeRaw_(buffer); |
| } |
| |
| /** |
| * Writes a sfixed32 value field to the buffer. |
| * @param {number} fieldNumber |
| * @param {number} value |
| */ |
| writeSfixed32(fieldNumber, value) { |
| this.writeTag(fieldNumber, WireType.FIXED32); |
| this.writeSfixed32Value_(value); |
| } |
| |
| /** |
| * Writes a sfixed64 value field to the buffer without tag. |
| * @param {!Int64} value |
| * @private |
| */ |
| writeSfixed64Value_(value) { |
| const buffer = new ArrayBuffer(8); |
| const view = new DataView(buffer); |
| view.setInt32(0, value.getLowBits(), true); |
| view.setInt32(4, value.getHighBits(), true); |
| this.writeRaw_(buffer); |
| } |
| |
| /** |
| * Writes a sfixed64 value field to the buffer. |
| * @param {number} fieldNumber |
| * @param {!Int64} value |
| */ |
| writeSfixed64(fieldNumber, value) { |
| this.writeTag(fieldNumber, WireType.FIXED64); |
| this.writeSfixed64Value_(value); |
| } |
| |
| /** |
| * Writes a uint32 value field to the buffer as a varint without tag. |
| * @param {number} value |
| * @private |
| */ |
| writeUint32Value_(value) { |
| this.writeVarint64_(0, value); |
| } |
| |
| /** |
| * Writes a uint32 value field to the buffer as a varint. |
| * @param {number} fieldNumber |
| * @param {number} value |
| */ |
| writeUint32(fieldNumber, value) { |
| this.writeTag(fieldNumber, WireType.VARINT); |
| this.writeUint32Value_(value); |
| } |
| |
| /** |
| * Writes the bits of a 64 bit number to the buffer as a varint. |
| * @param {number} highBits |
| * @param {number} lowBits |
| * @private |
| */ |
| writeVarint64_(highBits, lowBits) { |
| for (let i = 0; i < 28; i = i + 7) { |
| const shift = lowBits >>> i; |
| const hasNext = !((shift >>> 7) === 0 && highBits === 0); |
| const byte = (hasNext ? shift | 0x80 : shift) & 0xFF; |
| this.currentBuffer_.push(byte); |
| if (!hasNext) { |
| return; |
| } |
| } |
| |
| const splitBits = ((lowBits >>> 28) & 0x0F) | ((highBits & 0x07) << 4); |
| const hasMoreBits = !((highBits >> 3) === 0); |
| this.currentBuffer_.push( |
| (hasMoreBits ? splitBits | 0x80 : splitBits) & 0xFF); |
| |
| if (!hasMoreBits) { |
| return; |
| } |
| |
| for (let i = 3; i < 31; i = i + 7) { |
| const shift = highBits >>> i; |
| const hasNext = !((shift >>> 7) === 0); |
| const byte = (hasNext ? shift | 0x80 : shift) & 0xFF; |
| this.currentBuffer_.push(byte); |
| if (!hasNext) { |
| return; |
| } |
| } |
| |
| this.currentBuffer_.push((highBits >>> 31) & 0x01); |
| } |
| |
| /** |
| * Writes a sint32 value field to the buffer as a varint without tag. |
| * @param {number} value |
| * @private |
| */ |
| writeSint32Value_(value) { |
| value = (value << 1) ^ (value >> 31); |
| this.writeVarint64_(0, value); |
| } |
| |
| /** |
| * Writes a sint32 value field to the buffer as a varint. |
| * @param {number} fieldNumber |
| * @param {number} value |
| */ |
| writeSint32(fieldNumber, value) { |
| this.writeTag(fieldNumber, WireType.VARINT); |
| this.writeSint32Value_(value); |
| } |
| |
| /** |
| * Writes a sint64 value field to the buffer as a varint without tag. |
| * @param {!Int64} value |
| * @private |
| */ |
| writeSint64Value_(value) { |
| const highBits = value.getHighBits(); |
| const lowBits = value.getLowBits(); |
| |
| const sign = highBits >> 31; |
| const encodedLowBits = (lowBits << 1) ^ sign; |
| const encodedHighBits = ((highBits << 1) | (lowBits >>> 31)) ^ sign; |
| this.writeVarint64_(encodedHighBits, encodedLowBits); |
| } |
| |
| /** |
| * Writes a sint64 value field to the buffer as a varint. |
| * @param {number} fieldNumber |
| * @param {!Int64} value |
| */ |
| writeSint64(fieldNumber, value) { |
| this.writeTag(fieldNumber, WireType.VARINT); |
| this.writeSint64Value_(value); |
| } |
| |
| /** |
| * Writes a string value field to the buffer as a varint. |
| * @param {number} fieldNumber |
| * @param {string} value |
| */ |
| writeString(fieldNumber, value) { |
| this.writeTag(fieldNumber, WireType.DELIMITED); |
| const array = encoderFunction(value); |
| this.writeUnsignedVarint32_(array.length); |
| this.closeAndStartNewBuffer_(); |
| this.blocks_.push(array); |
| } |
| |
| /** |
| * Writes raw bytes to the buffer. |
| * @param {!ArrayBuffer} arrayBuffer |
| * @private |
| */ |
| writeRaw_(arrayBuffer) { |
| this.closeAndStartNewBuffer_(); |
| this.blocks_.push(new Uint8Array(arrayBuffer)); |
| } |
| |
| /** |
| * Writes raw bytes to the buffer. |
| * @param {!BufferDecoder} bufferDecoder |
| * @param {number} start |
| * @param {!WireType} wireType |
| * @package |
| */ |
| writeBufferDecoder(bufferDecoder, start, wireType) { |
| this.closeAndStartNewBuffer_(); |
| const dataLength = this.getLength_(bufferDecoder, start, wireType); |
| this.blocks_.push( |
| bufferDecoder.subBufferDecoder(start, dataLength).asUint8Array()); |
| } |
| |
| /** |
| * Returns the length of the data to serialize. Returns -1 when a STOP GROUP |
| * is found. |
| * @param {!BufferDecoder} bufferDecoder |
| * @param {number} start |
| * @param {!WireType} wireType |
| * @return {number} |
| * @private |
| */ |
| getLength_(bufferDecoder, start, wireType) { |
| switch (wireType) { |
| case WireType.VARINT: |
| bufferDecoder.setCursor(start); |
| bufferDecoder.skipVarint(); |
| return bufferDecoder.cursor() - start; |
| case WireType.FIXED64: |
| return 8; |
| case WireType.DELIMITED: |
| const dataLength = bufferDecoder.getUnsignedVarint32At(start); |
| return dataLength + bufferDecoder.cursor() - start; |
| case WireType.START_GROUP: |
| return this.getGroupLength_(bufferDecoder, start); |
| case WireType.FIXED32: |
| return 4; |
| default: |
| throw new Error(`Invalid wire type: ${wireType}`); |
| } |
| } |
| |
| /** |
| * Skips over fields until it finds the end of a given group. |
| * @param {!BufferDecoder} bufferDecoder |
| * @param {number} start |
| * @return {number} |
| * @private |
| */ |
| getGroupLength_(bufferDecoder, start) { |
| // On a start group we need to keep skipping fields until we find a |
| // corresponding stop group |
| let cursor = start; |
| while (cursor < bufferDecoder.endIndex()) { |
| const tag = bufferDecoder.getUnsignedVarint32At(cursor); |
| const wireType = /** @type {!WireType} */ (tag & 0x07); |
| if (wireType === WireType.END_GROUP) { |
| return bufferDecoder.cursor() - start; |
| } |
| cursor = bufferDecoder.cursor() + |
| this.getLength_(bufferDecoder, bufferDecoder.cursor(), wireType); |
| } |
| throw new Error('No end group found'); |
| } |
| |
| /** |
| * Write the whole bytes as a length delimited field. |
| * @param {number} fieldNumber |
| * @param {!ArrayBuffer} arrayBuffer |
| */ |
| writeDelimited(fieldNumber, arrayBuffer) { |
| this.writeTag(fieldNumber, WireType.DELIMITED); |
| this.writeUnsignedVarint32_(arrayBuffer.byteLength); |
| this.writeRaw_(arrayBuffer); |
| } |
| |
| /**************************************************************************** |
| * REPEATED METHODS |
| ****************************************************************************/ |
| |
| /** |
| * Writes repeated boolean values to the buffer as unpacked varints. |
| * @param {number} fieldNumber |
| * @param {!Array<boolean>} values |
| */ |
| writeRepeatedBool(fieldNumber, values) { |
| values.forEach(val => this.writeBool(fieldNumber, val)); |
| } |
| |
| /** |
| * Writes repeated boolean values to the buffer as packed varints. |
| * @param {number} fieldNumber |
| * @param {!Array<boolean>} values |
| */ |
| writePackedBool(fieldNumber, values) { |
| this.writeFixedPacked_( |
| fieldNumber, values, val => this.writeBoolValue_(val), 1); |
| } |
| |
| /** |
| * Writes repeated double values to the buffer as unpacked fixed64. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writeRepeatedDouble(fieldNumber, values) { |
| values.forEach(val => this.writeDouble(fieldNumber, val)); |
| } |
| |
| /** |
| * Writes repeated double values to the buffer as packed fixed64. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writePackedDouble(fieldNumber, values) { |
| this.writeFixedPacked_( |
| fieldNumber, values, val => this.writeDoubleValue_(val), 8); |
| } |
| |
| /** |
| * Writes repeated fixed32 values to the buffer as unpacked fixed32. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writeRepeatedFixed32(fieldNumber, values) { |
| values.forEach(val => this.writeFixed32(fieldNumber, val)); |
| } |
| |
| /** |
| * Writes repeated fixed32 values to the buffer as packed fixed32. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writePackedFixed32(fieldNumber, values) { |
| this.writeFixedPacked_( |
| fieldNumber, values, val => this.writeFixed32Value_(val), 4); |
| } |
| |
| /** |
| * Writes repeated float values to the buffer as unpacked fixed64. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writeRepeatedFloat(fieldNumber, values) { |
| values.forEach(val => this.writeFloat(fieldNumber, val)); |
| } |
| |
| /** |
| * Writes repeated float values to the buffer as packed fixed64. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writePackedFloat(fieldNumber, values) { |
| this.writeFixedPacked_( |
| fieldNumber, values, val => this.writeFloatValue_(val), 4); |
| } |
| |
| /** |
| * Writes repeated int32 values to the buffer as unpacked int32. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writeRepeatedInt32(fieldNumber, values) { |
| values.forEach(val => this.writeInt32(fieldNumber, val)); |
| } |
| |
| /** |
| * Writes repeated int32 values to the buffer as packed int32. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writePackedInt32(fieldNumber, values) { |
| this.writeVariablePacked_( |
| fieldNumber, values, (writer, val) => writer.writeInt32Value_(val)); |
| } |
| |
| /** |
| * Writes repeated int64 values to the buffer as unpacked varint. |
| * @param {number} fieldNumber |
| * @param {!Array<!Int64>} values |
| */ |
| writeRepeatedInt64(fieldNumber, values) { |
| values.forEach(val => this.writeInt64(fieldNumber, val)); |
| } |
| |
| /** |
| * Writes repeated int64 values to the buffer as packed varint. |
| * @param {number} fieldNumber |
| * @param {!Array<!Int64>} values |
| */ |
| writePackedInt64(fieldNumber, values) { |
| this.writeVariablePacked_( |
| fieldNumber, values, |
| (writer, val) => |
| writer.writeVarint64_(val.getHighBits(), val.getLowBits())); |
| } |
| |
| /** |
| * Writes repeated sfixed32 values to the buffer as unpacked fixed32. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writeRepeatedSfixed32(fieldNumber, values) { |
| values.forEach(val => this.writeSfixed32(fieldNumber, val)); |
| } |
| |
| /** |
| * Writes repeated sfixed32 values to the buffer as packed fixed32. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writePackedSfixed32(fieldNumber, values) { |
| this.writeFixedPacked_( |
| fieldNumber, values, val => this.writeSfixed32Value_(val), 4); |
| } |
| |
| /** |
| * Writes repeated sfixed64 values to the buffer as unpacked fixed64. |
| * @param {number} fieldNumber |
| * @param {!Array<!Int64>} values |
| */ |
| writeRepeatedSfixed64(fieldNumber, values) { |
| values.forEach(val => this.writeSfixed64(fieldNumber, val)); |
| } |
| |
| /** |
| * Writes repeated sfixed64 values to the buffer as packed fixed64. |
| * @param {number} fieldNumber |
| * @param {!Array<!Int64>} values |
| */ |
| writePackedSfixed64(fieldNumber, values) { |
| this.writeFixedPacked_( |
| fieldNumber, values, val => this.writeSfixed64Value_(val), 8); |
| } |
| |
| /** |
| * Writes repeated sint32 values to the buffer as unpacked sint32. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writeRepeatedSint32(fieldNumber, values) { |
| values.forEach(val => this.writeSint32(fieldNumber, val)); |
| } |
| |
| /** |
| * Writes repeated sint32 values to the buffer as packed sint32. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writePackedSint32(fieldNumber, values) { |
| this.writeVariablePacked_( |
| fieldNumber, values, (writer, val) => writer.writeSint32Value_(val)); |
| } |
| |
| /** |
| * Writes repeated sint64 values to the buffer as unpacked varint. |
| * @param {number} fieldNumber |
| * @param {!Array<!Int64>} values |
| */ |
| writeRepeatedSint64(fieldNumber, values) { |
| values.forEach(val => this.writeSint64(fieldNumber, val)); |
| } |
| |
| /** |
| * Writes repeated sint64 values to the buffer as packed varint. |
| * @param {number} fieldNumber |
| * @param {!Array<!Int64>} values |
| */ |
| writePackedSint64(fieldNumber, values) { |
| this.writeVariablePacked_( |
| fieldNumber, values, (writer, val) => writer.writeSint64Value_(val)); |
| } |
| |
| /** |
| * Writes repeated uint32 values to the buffer as unpacked uint32. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writeRepeatedUint32(fieldNumber, values) { |
| values.forEach(val => this.writeUint32(fieldNumber, val)); |
| } |
| |
| /** |
| * Writes repeated uint32 values to the buffer as packed uint32. |
| * @param {number} fieldNumber |
| * @param {!Array<number>} values |
| */ |
| writePackedUint32(fieldNumber, values) { |
| this.writeVariablePacked_( |
| fieldNumber, values, (writer, val) => writer.writeUint32Value_(val)); |
| } |
| |
| /** |
| * Writes repeated bytes values to the buffer. |
| * @param {number} fieldNumber |
| * @param {!Array<!ByteString>} values |
| */ |
| writeRepeatedBytes(fieldNumber, values) { |
| values.forEach(val => this.writeBytes(fieldNumber, val)); |
| } |
| |
| /** |
| * Writes packed fields with fixed length. |
| * @param {number} fieldNumber |
| * @param {!Array<T>} values |
| * @param {function(T)} valueWriter |
| * @param {number} entitySize |
| * @template T |
| * @private |
| */ |
| writeFixedPacked_(fieldNumber, values, valueWriter, entitySize) { |
| if (values.length === 0) { |
| return; |
| } |
| this.writeTag(fieldNumber, WireType.DELIMITED); |
| this.writeUnsignedVarint32_(values.length * entitySize); |
| this.closeAndStartNewBuffer_(); |
| values.forEach(value => valueWriter(value)); |
| } |
| |
| /** |
| * Writes packed fields with variable length. |
| * @param {number} fieldNumber |
| * @param {!Array<T>} values |
| * @param {function(!Writer, T)} valueWriter |
| * @template T |
| * @private |
| */ |
| writeVariablePacked_(fieldNumber, values, valueWriter) { |
| if (values.length === 0) { |
| return; |
| } |
| const writer = new Writer(); |
| values.forEach(val => valueWriter(writer, val)); |
| const bytes = writer.getAndResetResultBuffer(); |
| this.writeDelimited(fieldNumber, bytes); |
| } |
| |
| /** |
| * Writes repeated string values to the buffer. |
| * @param {number} fieldNumber |
| * @param {!Array<string>} values |
| */ |
| writeRepeatedString(fieldNumber, values) { |
| values.forEach(val => this.writeString(fieldNumber, val)); |
| } |
| } |
| |
| exports = Writer; |