| /** | |
| * @fileoverview This file contains utilities for converting binary, | |
| * wire-format protocol buffers into Javascript data structures. | |
| * | |
| * jspb's BinaryReader class wraps the BinaryDecoder class to add methods | |
| * that understand the protocol buffer syntax and can do the type checking and | |
| * bookkeeping necessary to parse trees of nested messages. | |
| * | |
| * Major caveat - Users of this library _must_ keep their Javascript proto | |
| * parsing code in sync with the original .proto file - presumably you'll be | |
| * using the typed jspb code generator, but if you bypass that you'll need | |
| * to keep things in sync by hand. | |
| * | |
| * @author aappleby@google.com (Austin Appleby) | |
| */ | |
| goog.module('jspb.binary.reader'); | |
| goog.module.declareLegacyNamespace(); | |
| const BinaryConstants = goog.require('jspb.BinaryConstants'); | |
| const asserts = goog.require('goog.asserts'); | |
| const errors = goog.require('jspb.binary.errors'); | |
| const utils = goog.require('jspb.utils'); | |
| const { BinaryDecoder } = goog.require('jspb.binary.decoder'); | |
| const { ByteSource } = goog.require('jspb.binary.bytesource'); | |
| const { ByteString } = goog.requireType('jspb.bytestring'); | |
| /** | |
| * Whether to enforce that string fields are valid utf8. | |
| * | |
| * <p>Currently set to `ALWAYS`, can be set to `DEPRECATED_PROTO3_ONLY` to only | |
| * enforce utf8 for proto3 string fields, for proto2 string fields it will use | |
| * replacement characters when encoding errors are found. | |
| * | |
| * <p>TODO: Remove the flag, simplify BinaryReader to remove | |
| * readStringRequireUtf8 and related support in the code generator et. al. | |
| * | |
| * @define {string} | |
| */ | |
| const ENFORCE_UTF8 = goog.define('jspb.binary.ENFORCE_UTF8', 'ALWAYS'); | |
| // Constrain the set of values to only these two. | |
| asserts.assert( | |
| ENFORCE_UTF8 === 'DEPRECATED_PROTO3_ONLY' || ENFORCE_UTF8 === 'ALWAYS'); | |
| const /** boolean */ UTF8_PARSING_ERRORS_ARE_FATAL = ENFORCE_UTF8 === 'ALWAYS'; | |
| /** | |
| * @define {number} Maximum allowed recursion depth for group and submessage | |
| * decoding. | |
| */ | |
| // 100 is the normal protobuf limit. | |
| const MAX_RECURSION_DEPTH = | |
| goog.define('jspb.binary.MAX_RECURSION_DEPTH', (2 * 100)); | |
| /** | |
| * Recursion depth counter for groups and submessages during synchronous binary deserialization. | |
| * @type {number} | |
| */ | |
| let recursionDepth = 0; | |
| /** | |
| * Describes options for BinaryReaders. | |
| * | |
| * @record | |
| */ | |
| class BinaryReaderOptions { | |
| constructor() { | |
| /** | |
| * Whether to ignore unknown fields found when parsing. | |
| * | |
| * Normally, if unknown tag numbers are encountered when parsing a message, | |
| * the tag and value are stored in the message instance and then written | |
| * back out when the message is serialized. This allows applications to | |
| * preserve data in messages that have new field definitions which they | |
| * don't yet know about. However, this behavior can have performance | |
| * implications. This property disables this behavior during parsing. | |
| * | |
| * @type {boolean|undefined} | |
| */ | |
| this.discardUnknownFields; | |
| /** | |
| * When set to `true` bytes fields will be views into the original buffer | |
| * instead of being copies. | |
| * | |
| * This allows teams to reduce copies at the cost of pinning the original | |
| * `ByteSource` in memory. | |
| * | |
| * How this works ultimate depends on how `bytes` fields are parsed. | |
| * | |
| * If `bytes` fields are read as `Uint8Array` via the `readBytes` method (as | |
| * is done by jsproto and immutablejs): `readBytes` will return views onto | |
| * the original buffer as `Uint8Array` objects. Additionally, because | |
| * Uint8Array objects are mutable this may allow unexpected mutations of the | |
| * `ByteSource` or for mutations of the bytesource to affect later read | |
| * operations. If the source is a ByteString, this option is ignored in | |
| * order to preserve the immutability semantics of ByteStrings. | |
| * | |
| * If the `bytes` are read as `ByteString` via the `readByteString` method, | |
| * then this option is only effective if the | |
| * source is also a `ByteString`, otherwise copies need to be made to | |
| * preserve the immutability of the produced `ByteString` objects. | |
| * | |
| * The default is `false` | |
| * @type {boolean|undefined} | |
| */ | |
| this.aliasBytesFields; | |
| /** | |
| * Whether we should treat newly deserialized data as being immutable. | |
| * | |
| * With this option, we treat newly deserialized data (e.g. from a | |
| * base64-encoded string) as being immutable, so that it can be safely | |
| * aliased in a ByteString. If you set this option, you then cannot alias | |
| * into a Uint8Array, as its underlying ArrayBuffer could be unwrapped. | |
| * | |
| * @type {boolean|undefined} | |
| */ | |
| this.treatNewDataAsImmutable; | |
| } | |
| } | |
| /* | |
| * Handling Errors | |
| * | |
| * There are two classes of errors that should be considered below. | |
| * | |
| * Simple consistency checks: use `goog.asserts` | |
| * | |
| * These are cases related to function invariants. For example, in order to | |
| * call `readString` the current field must have WireType.DELIMITED. Since we | |
| * also control all the callsites it is reasonable to rely on our own tests to | |
| * catch mistakes. | |
| * | |
| * Data based conditions: use `jspb.binary.invalid_encoding_errors` | |
| * | |
| * These are conditions where the _data_ is wrong in some way. For example, a | |
| * varint is overlong, a delimited field overflows/underflows. These cases | |
| * cannot be eliminated by better testing since they depend on the structure of | |
| * data supplied to the parsing routines, so `goog.asserts` are not a good | |
| * approach | |
| */ | |
| /** | |
| * BinaryReader implements the decoders for all the wire types specified in | |
| * https://developers.google.com/protocol-buffers/docs/encoding. | |
| * | |
| * @struct | |
| * @final | |
| */ | |
| class BinaryReader { | |
| /** | |
| * @param {?ByteSource|!ByteString=} bytes The bytes we're reading from. | |
| * @param {number=} start The optional offset to start reading at. | |
| * @param {number=} length The optional length of the block to read - | |
| * we'll throw an assertion if we go off the end of the block. | |
| * @param {!BinaryReaderOptions=} options Options for this BinaryReader. | |
| */ | |
| constructor(bytes, start, length, options) { | |
| /** | |
| * Current options for this reader | |
| * @private {boolean} | |
| */ | |
| this.discardUnknownFields; | |
| /** | |
| * Wire-format decoder. | |
| * @const {!BinaryDecoder} | |
| */ | |
| this.decoder_ = BinaryDecoder.alloc(bytes, start, length, options); | |
| /** | |
| * Cursor immediately before the field tag. | |
| * @private {number} | |
| */ | |
| this.fieldCursor_ = this.decoder_.getCursor(); | |
| /** | |
| * Field number of the next field in the buffer, filled in by nextField(). | |
| * @private {number} | |
| */ | |
| this.nextField_ = BinaryConstants.INVALID_FIELD_NUMBER; | |
| /** | |
| * The combined wire-type and field number of the next field in the buffer, | |
| * filled in by nextField(). | |
| * @private {number} | |
| */ | |
| this.nextTag_ = BinaryConstants.INVALID_TAG; | |
| /** | |
| * Wire type of the next proto field in the buffer, filled in by | |
| * nextField(). | |
| * @private {!BinaryConstants.WireType} | |
| */ | |
| this.nextWireType_ = BinaryConstants.WireType.INVALID; | |
| this.setOptions(options); | |
| } | |
| /** | |
| * @param {!BinaryReaderOptions=} options options for this decoder. | |
| * @private | |
| */ | |
| setOptions({ discardUnknownFields = false } = {}) { | |
| this.discardUnknownFields = discardUnknownFields; | |
| } | |
| /** | |
| * Pops an instance off the instance cache, or creates one if the cache is | |
| * empty. | |
| * @param {?ByteSource|!ByteString=} bytes The bytes we're reading from. | |
| * @param {number=} start The optional offset to start reading at. | |
| * @param {number=} length The optional length of the block to read - | |
| * we'll throw an assertion if we go off the end of the block. | |
| * @param {!BinaryReaderOptions=} options | |
| * @return {!BinaryReader} | |
| * @suppress {visibility} accesses private properties of decoder | |
| */ | |
| static alloc(bytes, start, length, options) { | |
| if (BinaryReader.instanceCache_.length) { | |
| const newReader = BinaryReader.instanceCache_.pop(); | |
| newReader.setOptions(options); | |
| newReader.decoder_.init(bytes, start, length, options); | |
| return newReader; | |
| } else { | |
| return new BinaryReader(bytes, start, length, options); | |
| } | |
| } | |
| /** | |
| * Puts this instance back in the instance cache. | |
| */ | |
| free() { | |
| this.decoder_.clear(); | |
| this.nextTag_ = BinaryConstants.INVALID_TAG; | |
| this.nextField_ = BinaryConstants.INVALID_FIELD_NUMBER; | |
| this.nextWireType_ = BinaryConstants.WireType.INVALID; | |
| if (BinaryReader.instanceCache_.length < 100) { | |
| BinaryReader.instanceCache_.push(this); | |
| } | |
| } | |
| /** | |
| * Returns the cursor immediately before the current field's tag. | |
| * @return {number} The internal read cursor. | |
| */ | |
| getFieldCursor() { | |
| return this.fieldCursor_; | |
| } | |
| /** | |
| * Returns the internal read cursor. | |
| * @return {number} The internal read cursor. | |
| */ | |
| getCursor() { | |
| return this.decoder_.getCursor(); | |
| } | |
| /** @return {boolean} */ | |
| dataIsImmutable() { | |
| return this.decoder_.dataIsImmutable(); | |
| } | |
| /** | |
| * Returns the raw buffer. | |
| * | |
| * Throws if the internal buffer is immutable. | |
| * | |
| * @return {?Uint8Array} The raw buffer. | |
| */ | |
| getBuffer() { | |
| return this.decoder_.getBuffer(); | |
| } | |
| /** | |
| * Returns the raw buffer as a byte string. | |
| * | |
| * Throws if the internal buffer is mutable. | |
| * | |
| * @return {?ByteString} The raw buffer. | |
| */ | |
| getBufferAsByteString() { | |
| return this.decoder_.getBufferAsByteString(); | |
| } | |
| /** | |
| * @return {number} The combined wire type and field number of the next field | |
| * in the buffer, or INVALID_TAG if there is no next field. This is an | |
| * unsigned 32-bit integer value with the lower three bits the wire type | |
| * and the upper 29 bits the field number. | |
| */ | |
| getTag() { | |
| return this.nextTag_; | |
| } | |
| /** | |
| * @return {number} The field number of the next field in the buffer, or | |
| * INVALID_FIELD_NUMBER if there is no next field. | |
| */ | |
| getFieldNumber() { | |
| return this.nextField_; | |
| } | |
| /** | |
| * @return {!BinaryConstants.WireType} The wire type of the next field | |
| * in the stream, or WireType.INVALID if there is no next field. | |
| */ | |
| getWireType() { | |
| return this.nextWireType_; | |
| } | |
| /** | |
| * @return {boolean} Whether the current wire type is an end-group tag. Used | |
| * as | |
| * an exit condition in decoder loops in generated code. | |
| */ | |
| isEndGroup() { | |
| return this.nextWireType_ == BinaryConstants.WireType.END_GROUP; | |
| } | |
| /** | |
| * @return {boolean} Whether the current wire type is a delimited field. Used | |
| * to | |
| * conditionally parse packed repeated fields. | |
| */ | |
| isDelimited() { | |
| return this.nextWireType_ == BinaryConstants.WireType.DELIMITED; | |
| } | |
| /** | |
| * Rewinds the stream cursor to the beginning of the buffer and resets all | |
| * internal state. | |
| * @package | |
| */ | |
| reset() { | |
| this.decoder_.reset(); | |
| this.fieldCursor_ = this.decoder_.getCursor(); | |
| this.nextTag_ = BinaryConstants.INVALID_TAG; | |
| this.nextField_ = BinaryConstants.INVALID_FIELD_NUMBER; | |
| this.nextWireType_ = BinaryConstants.WireType.INVALID; | |
| } | |
| /** | |
| * Advances the stream cursor by the given number of bytes. | |
| * @param {number} count The number of bytes to advance by. | |
| */ | |
| advance(count) { | |
| this.decoder_.advance(count); | |
| } | |
| /** | |
| * Reads the next field header in the stream if there is one, returns true if | |
| * we saw a valid field header or false if we've read the whole stream. | |
| * Throws an error if we encountered a deprecated START_GROUP/END_GROUP field. | |
| * @return {boolean} True if the stream contains more fields. | |
| */ | |
| nextField() { | |
| // If we're at the end of the block, there are no more fields. | |
| if (this.decoder_.atEnd()) { | |
| return false; | |
| } | |
| this.assertPriorFieldWasRead(); | |
| // No need to check cursor position here, as that is readUnsignedVarint32s | |
| // responsibility. | |
| // Otherwise just read the header of the next field. | |
| this.fieldCursor_ = this.decoder_.getCursor(); | |
| const header = BinaryDecoder.readUnsignedVarint32(this.decoder_); | |
| const nextField = parseFieldNumber(header); | |
| const nextWireType = parseWireType(header); | |
| // If the wire type isn't one of the valid ones, something's broken. | |
| if (!BinaryConstants.isValidWireType(nextWireType)) { | |
| throw errors.invalidWireTypeError(nextWireType, this.fieldCursor_); | |
| } | |
| // Zero is not a valid field number and we should never see a negative as | |
| // we've already shifted right unsigned. | |
| if (nextField < 1) { | |
| throw errors.invalidFieldNumberError(nextField, this.fieldCursor_); | |
| } | |
| this.nextTag_ = header; | |
| this.nextField_ = nextField; | |
| this.nextWireType_ = nextWireType; | |
| return true; | |
| } | |
| /** | |
| * If the stream contains a following field with the specified tag (field | |
| * number and wire type), reads its field header and makes the reader ready to | |
| * read its value. If the stream does not contain another field, or its tag is | |
| * different, does not change the state of the stream. | |
| * | |
| * TIP: methods like `readMessage` may destroy the reader state for the tag, | |
| * so call `getTag()` outside a loop, before calling reader methods to pass | |
| * into this method. | |
| * | |
| * @param {number} tag The field tag to look for. | |
| * @return {boolean} Whether another instance of the current tag is read. | |
| */ | |
| nextFieldIfTagEqualTo(tag) { | |
| this.assertPriorFieldWasRead(); | |
| asserts.assert( | |
| BinaryConstants.isValidWireType(parseWireType(tag)) && | |
| parseFieldNumber(tag) > 0, | |
| 'Must pass a valid tag.'); | |
| const fieldCursorOrNegative = | |
| this.decoder_.readUnsignedVarint32IfEqualTo(tag); | |
| const matched = fieldCursorOrNegative >= 0; | |
| if (matched) { | |
| this.fieldCursor_ = fieldCursorOrNegative; | |
| this.nextTag_ = tag; | |
| this.nextField_ = parseFieldNumber(tag); | |
| this.nextWireType_ = parseWireType(tag); | |
| } | |
| return matched; | |
| } | |
| /** | |
| * Helper to ensure that the prior field was correctly read by advancing the | |
| * cursor. | |
| * @private | |
| */ | |
| assertPriorFieldWasRead() { | |
| if (asserts.ENABLE_ASSERTS && | |
| this.nextTag_ !== BinaryConstants.INVALID_TAG) { | |
| // If we aren't at the first field, make sure that the previous caller of | |
| // nextField actually read the field (by calling an appropriate `read*` or | |
| // `skip*` method). | |
| // To do this we go back and redo the work to move cursor to where it was | |
| // at the end of the last nextField() call and just ensure that we have | |
| // advanced beyond that | |
| const currentCursor = this.decoder_.getCursor(); | |
| this.decoder_.setCursor(this.fieldCursor_); | |
| BinaryDecoder.readUnsignedVarint32(this.decoder_); | |
| if (this.nextWireType_ === BinaryConstants.WireType.END_GROUP || | |
| this.nextWireType_ === BinaryConstants.WireType.START_GROUP) { | |
| // This case should be impossible, as every read* skip* method would | |
| // likely throw. Direct mutation of the cursor could do it. | |
| asserts.assert( | |
| currentCursor === this.decoder_.getCursor(), | |
| 'Expected to not advance the cursor. Group tags do not have values.'); | |
| } else { | |
| asserts.assert( | |
| currentCursor > this.decoder_.getCursor(), | |
| 'Expected to read the field, did you forget to call a read or skip method?'); | |
| } | |
| this.decoder_.setCursor(currentCursor); | |
| } | |
| } | |
| /** | |
| * Skips over the next varint field in the binary stream. | |
| */ | |
| skipVarintField() { | |
| // This case should be impossible but jsproto calls this method without | |
| // first checking wiretype | |
| if (this.nextWireType_ != BinaryConstants.WireType.VARINT) { | |
| asserts.fail('Invalid wire type for skipVarintField'); | |
| this.skipField(); | |
| return; | |
| } | |
| this.decoder_.skipVarint(); | |
| } | |
| /** | |
| * Skips over the next delimited field in the binary stream. | |
| * @return {number} The length of the delimited field payload, not including | |
| * the field header. | |
| */ | |
| skipDelimitedField() { | |
| // This case should be impossible but jsproto calls this method without | |
| // first checking wiretype | |
| if (this.nextWireType_ != BinaryConstants.WireType.DELIMITED) { | |
| asserts.fail('Invalid wire type for skipDelimitedField'); | |
| this.skipField(); | |
| return 0; | |
| } | |
| const length = BinaryDecoder.readUnsignedVarint32(this.decoder_); | |
| this.decoder_.advance(length); | |
| return length; | |
| } | |
| /** | |
| * Skips over the next fixed32 field in the binary stream. | |
| * @private | |
| */ | |
| skipFixed32Field() { | |
| asserts.assert(this.nextWireType_ === BinaryConstants.WireType.FIXED32); | |
| this.decoder_.advance(4); | |
| } | |
| /** | |
| * Skips over the next fixed64 field in the binary stream. | |
| * @private | |
| */ | |
| skipFixed64Field() { | |
| asserts.assert(this.nextWireType_ === BinaryConstants.WireType.FIXED64); | |
| this.decoder_.advance(8); | |
| } | |
| /** | |
| * Increments the recursion depth counter and checks against the limit. | |
| * Throws a SyntaxError if the recursion depth limit is exceeded. | |
| */ | |
| pushRecursion() { | |
| if (recursionDepth >= MAX_RECURSION_DEPTH) { | |
| throw errors.maxRecursionDepthExceededError(); | |
| } | |
| recursionDepth++; | |
| } | |
| /** | |
| * Decrements the recursion depth counter. | |
| */ | |
| popRecursion() { | |
| if (recursionDepth > 0) { | |
| recursionDepth--; | |
| } | |
| } | |
| /** | |
| * Skips over the next group field in the binary stream. | |
| * @private | |
| */ | |
| skipGroup() { | |
| const previousField = this.nextField_; | |
| this.pushRecursion(); | |
| try { | |
| do { | |
| if (!this.nextField()) { | |
| throw errors.unmatchedStartGroupEofError(); | |
| } | |
| if (this.nextWireType_ == BinaryConstants.WireType.END_GROUP) { | |
| // Group end: check that it matches top-of-stack. | |
| if (this.nextField_ != previousField) { | |
| throw errors.unmatchedStartGroupError(); | |
| } | |
| return; | |
| } | |
| this.skipField(); | |
| } while (true); | |
| } catch (err) { | |
| if (err instanceof RangeError) { | |
| throw errors.maxRecursionDepthExceededError(); | |
| } | |
| throw err; | |
| } finally { | |
| this.popRecursion(); | |
| } | |
| } | |
| /** | |
| * Skips over the next field in the binary stream - this is useful if we're | |
| * decoding a message that contain unknown fields. | |
| */ | |
| skipField() { | |
| try { | |
| switch (this.nextWireType_) { | |
| case BinaryConstants.WireType.VARINT: | |
| this.skipVarintField(); | |
| break; | |
| case BinaryConstants.WireType.FIXED64: | |
| this.skipFixed64Field(); | |
| break; | |
| case BinaryConstants.WireType.DELIMITED: | |
| this.skipDelimitedField(); | |
| break; | |
| case BinaryConstants.WireType.FIXED32: | |
| this.skipFixed32Field(); | |
| break; | |
| case BinaryConstants.WireType.START_GROUP: | |
| this.skipGroup(); | |
| break; | |
| default: | |
| throw errors.invalidWireTypeError( | |
| this.nextWireType_, this.fieldCursor_); | |
| } | |
| } catch (err) { | |
| if (err instanceof RangeError) { | |
| throw errors.maxRecursionDepthExceededError(); | |
| } | |
| throw err; | |
| } | |
| } | |
| /** | |
| * Skips over the entire content to the end of the stream. | |
| */ | |
| skipToEnd() { | |
| this.decoder_.setCursor(this.decoder_.getEnd()); | |
| } | |
| /** | |
| * Reads a single field as an uninterpreted bytestring. | |
| * @return {!ByteString|undefined} | |
| */ | |
| readUnknownField() { | |
| // read the field cursor prior to calling skipField, otherwise skipping | |
| // a group will reset the field cursor. | |
| const begin = this.getFieldCursor(); | |
| this.skipField(); | |
| return this.readUnknownFieldsStartingFrom(begin); | |
| } | |
| /** | |
| * Reads a range of unknown field(s) as a single bytestring. | |
| * @param {number} fieldOffset | |
| * @return {!ByteString|undefined} | |
| */ | |
| readUnknownFieldsStartingFrom(fieldOffset) { | |
| if (!this.discardUnknownFields) { | |
| // It is important that this is actually an immutable reference since the | |
| // unknownFieldset takes 'ownership' over the data. So we read as a | |
| // ByteString. | |
| const currentOffset = this.decoder_.getCursor(); | |
| const fieldLength = currentOffset - fieldOffset; | |
| this.decoder_.setCursor(fieldOffset); | |
| const unknownField = this.decoder_.readByteString(fieldLength); | |
| // double check our own math. | |
| asserts.assert(currentOffset == this.decoder_.getCursor()); | |
| // lazily instantiate the unknown fields structure. | |
| return unknownField; | |
| } | |
| return undefined; | |
| } | |
| /** | |
| * Reads a field of any valid non-message type from the binary stream. | |
| * | |
| * Returns `null` if the type could not be parsed because the data on the wire | |
| * doesn't match the `fieldType`. | |
| * @param {!BinaryConstants.FieldType} fieldType | |
| * @return {boolean|number|string|!Uint8Array|null} | |
| */ | |
| readAny(fieldType) { | |
| // If the wire types don't match just return null, it means that there is an | |
| // invalid value on the wire. In normal apps/jspb we handle this as an | |
| // 'unknown field' but in this API we just skip over it as we have no place | |
| // to store unknowns | |
| if (BinaryConstants.FieldTypeToWireType(fieldType) !== this.nextWireType_) { | |
| return null; | |
| } | |
| const fieldTypes = BinaryConstants.FieldType; | |
| switch (fieldType) { | |
| case fieldTypes.DOUBLE: | |
| return this.readDouble(); | |
| case fieldTypes.FLOAT: | |
| return this.readFloat(); | |
| case fieldTypes.INT64: | |
| return this.readInt64(); | |
| case fieldTypes.UINT64: | |
| return this.readUint64(); | |
| case fieldTypes.INT32: | |
| return this.readInt32(); | |
| case fieldTypes.FIXED64: | |
| return this.readFixed64(); | |
| case fieldTypes.FIXED32: | |
| return this.readFixed32(); | |
| case fieldTypes.BOOL: | |
| return this.readBool(); | |
| case fieldTypes.STRING: | |
| return this.readString(); | |
| case fieldTypes.GROUP: | |
| asserts.fail('Group field type not supported in readAny()'); | |
| case fieldTypes.MESSAGE: | |
| asserts.fail('Message field type not supported in readAny()'); | |
| case fieldTypes.BYTES: | |
| return this.readBytes(); | |
| case fieldTypes.UINT32: | |
| return this.readUint32(); | |
| case fieldTypes.ENUM: | |
| return this.readEnum(); | |
| case fieldTypes.SFIXED32: | |
| return this.readSfixed32(); | |
| case fieldTypes.SFIXED64: | |
| return this.readSfixed64(); | |
| case fieldTypes.SINT32: | |
| return this.readSint32(); | |
| case fieldTypes.SINT64: | |
| return this.readSint64(); | |
| default: | |
| asserts.fail('Invalid field type in readAny()'); | |
| } | |
| return null; | |
| } | |
| /** | |
| * Deserialize a proto into the provided message object using the provided | |
| * reader function. This function is templated as we currently have one client | |
| * who is using manual deserialization instead of the code-generated versions. | |
| * @template T,A,B,C | |
| * @param {T} message | |
| * @param {function(T, !BinaryReader, A, B, C)} reader | |
| * @param {A=} contextA | |
| * @param {B=} contextB | |
| * @param {C=} contextC | |
| * @return {T} | |
| */ | |
| readMessage(message, reader, contextA, contextB, contextC) { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.DELIMITED); | |
| // Save the current endpoint of the decoder and move it to the end of the | |
| // embedded message. | |
| const oldEnd = this.decoder_.getEnd(); | |
| const length = BinaryDecoder.readUnsignedVarint32(this.decoder_); | |
| const newEnd = this.decoder_.getCursor() + length; | |
| let underflowLength = newEnd - oldEnd; | |
| if (underflowLength <= 0) { | |
| this.decoder_.setEnd(newEnd); | |
| // Deserialize the embedded message. | |
| this.pushRecursion(); | |
| try { | |
| reader(message, this, contextA, contextB, contextC); | |
| } catch (err) { | |
| if (err instanceof RangeError) { | |
| throw errors.maxRecursionDepthExceededError(); | |
| } | |
| throw err; | |
| } finally { | |
| this.popRecursion(); | |
| } | |
| underflowLength = newEnd - this.decoder_.getCursor(); | |
| } | |
| if (underflowLength) { | |
| throw errors.messageLengthMismatchError(length, length - underflowLength); | |
| } | |
| // Advance the decoder past the embedded message and restore the endpoint. | |
| this.decoder_.setCursor(newEnd); | |
| this.decoder_.setEnd(oldEnd); | |
| return message; | |
| } | |
| /** | |
| * Deserialize a proto into the provided message object using the provided | |
| * reader function, assuming that the message is serialized as a group | |
| * with the given tag. | |
| * @template T | |
| * @param {number} field | |
| * @param {T} message | |
| * @param {function(T, !BinaryReader)} reader | |
| * @return {T} | |
| */ | |
| readGroup(field, message, reader) { | |
| // Ensure that the wire type is correct. | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.START_GROUP); | |
| // Ensure that the field number is correct. | |
| asserts.assert(this.nextField_ == field); | |
| // Deserialize the message. The deserialization will stop at an END_GROUP | |
| // tag. | |
| this.pushRecursion(); | |
| try { | |
| reader(message, this); | |
| } catch (err) { | |
| if (err instanceof RangeError) { | |
| throw errors.maxRecursionDepthExceededError(); | |
| } | |
| throw err; | |
| } finally { | |
| this.popRecursion(); | |
| } | |
| if (this.nextWireType_ !== BinaryConstants.WireType.END_GROUP) { | |
| // This case should be impossible assuming we trust all the reader | |
| // callbacks. In other words this really means the `reader` did the | |
| // wrong thing. | |
| throw errors.groupDidNotEndWithEndGroupError(); | |
| } else if (this.nextField_ !== field) { | |
| // this can happen if start and end group tags are nested improperly | |
| throw errors.unmatchedStartGroupError(); | |
| } | |
| return message; | |
| } | |
| /** | |
| * Whether the current field could be a valid start of a message set group. | |
| * @return {boolean} | |
| */ | |
| isMessageSetGroup() { | |
| return this.getTag() === MESSAGE_SET_START_GROUP_TAG; | |
| } | |
| /** | |
| * Deserialize a message-set wire-format group, calling the provided callaback | |
| * with the type ID and reader to read the message with. | |
| * | |
| * See go/messageset-wire-format | |
| * @param {function(number, !BinaryReader): void} readerCallback Function | |
| * called with the type ID and a reader for the message content. | |
| */ | |
| readMessageSetGroup(readerCallback) { | |
| asserts.assert(this.isMessageSetGroup()); | |
| // A message set group is encodes like: | |
| // repeated group Item = 1 { | |
| // required uint32 typeId = 2; | |
| // required bytes message = 3; | |
| // } | |
| // | |
| let typeId = 0; | |
| // The offset to the message payload, or -1 if consumed. | |
| let messageCursor = 0; | |
| this.pushRecursion(); | |
| try { | |
| while (this.nextField() && !this.isEndGroup()) { | |
| try { | |
| // See go/malformed-message-set-parsing If malformed messages repeat the | |
| // typeId or message fields, only use the first value of either within | |
| // the group. | |
| if (this.getTag() === MESSAGE_SET_TYPE_ID_TAG && !typeId) { | |
| typeId = this.readUint32(); | |
| if (messageCursor) { | |
| asserts.assert(messageCursor > 0); | |
| // Backup the parsing to the message payload. | |
| // No need to restore the position, we'll simply reread the type id | |
| // again, but skip doing anything because its already been read once. | |
| // Reset these. Because we are going to modify the cursor we need to | |
| // skip the consistency checks in nextField() and pretend like this is | |
| // a fresh BinaryReader instance. | |
| if (asserts.ENABLE_ASSERTS) { | |
| this.nextTag_ = BinaryConstants.INVALID_TAG; | |
| this.nextWireType_ = BinaryConstants.WireType.INVALID; | |
| } | |
| // Reset our cursor to where the message was, and pretend like we had | |
| // not seen the message payload yet. | |
| this.decoder_.setCursor(messageCursor); | |
| messageCursor = 0; | |
| } | |
| } else if (this.getTag() === MESSAGE_SET_MESSAGE_TAG && !messageCursor) { | |
| if (typeId) { | |
| messageCursor = -1; | |
| this.readMessage(typeId, readerCallback); | |
| } else { | |
| // Save the cursor to read the message | |
| // after we have the type ID. | |
| messageCursor = this.getFieldCursor(); | |
| this.skipDelimitedField(); | |
| } | |
| } else { | |
| // Either we have already read the payload or this is not a valid | |
| // messageset member. As a practical matter we don't have a place to | |
| // store this unknown field and simply skipping is consistent with the | |
| // Java and C++ impls | |
| this.skipField(); | |
| } | |
| } catch (err) { | |
| if (err instanceof RangeError) { | |
| throw errors.maxRecursionDepthExceededError(); | |
| } | |
| throw err; | |
| } | |
| } | |
| } catch (err) { | |
| if (err instanceof RangeError) { | |
| throw errors.maxRecursionDepthExceededError(); | |
| } | |
| throw err; | |
| } finally { | |
| this.popRecursion(); | |
| } | |
| // If we do not have an end tag, if we did not have a message, or if we | |
| // did not have a field number, drop out. | |
| if (this.getTag() !== MESSAGE_SET_END_TAG || !messageCursor || !typeId) { | |
| throw errors.malformedBinaryBytesForMessageSet(); | |
| } | |
| } | |
| /** | |
| * Reads a signed 32-bit integer field from the binary stream, or throws an | |
| * error if the next field in the stream is not of the correct wire type. | |
| * | |
| * @return {number} The value of the signed 32-bit integer field. | |
| */ | |
| readInt32() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.VARINT); | |
| return BinaryDecoder.readSignedVarint32(this.decoder_); | |
| } | |
| /** | |
| * Reads a signed 64-bit integer field from the binary stream, or throws an | |
| * error if the next field in the stream is not of the correct wire type. | |
| * | |
| * @return {number} The value of the signed 64-bit integer field. | |
| */ | |
| readInt64() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.VARINT); | |
| return BinaryDecoder.readSignedVarint64(this.decoder_); | |
| } | |
| /** | |
| * Reads a signed 64-bit integer field from the binary stream, or throws an | |
| * error if the next field in the stream is not of the correct wire type. | |
| * | |
| * Returns the value as a string. | |
| * | |
| * @return {string} The value of the signed 64-bit integer field as a decimal | |
| * string. | |
| */ | |
| readInt64String() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.VARINT); | |
| return BinaryDecoder.readSignedVarint64String(this.decoder_); | |
| } | |
| /** | |
| * Reads an unsigned 32-bit integer field from the binary stream, or throws an | |
| * error if the next field in the stream is not of the correct wire type. | |
| * | |
| * @return {number} The value of the unsigned 32-bit integer field. | |
| */ | |
| readUint32() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.VARINT); | |
| return BinaryDecoder.readUnsignedVarint32(this.decoder_); | |
| } | |
| /** | |
| * Reads an unsigned 64-bit integer field from the binary stream, or throws an | |
| * error if the next field in the stream is not of the correct wire type. | |
| * | |
| * @return {number} The value of the unsigned 64-bit integer field. | |
| */ | |
| readUint64() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.VARINT); | |
| return BinaryDecoder.readUnsignedVarint64(this.decoder_); | |
| } | |
| /** | |
| * Reads an unsigned 64-bit integer field from the binary stream, or throws an | |
| * error if the next field in the stream is not of the correct wire type. | |
| * | |
| * Returns the value as a string. | |
| * | |
| * @return {string} The value of the unsigned 64-bit integer field as a | |
| * decimal string. | |
| */ | |
| readUint64String() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.VARINT); | |
| return BinaryDecoder.readUnsignedVarint64String(this.decoder_); | |
| } | |
| /** | |
| * Reads a signed zigzag-encoded 32-bit integer field from the binary stream, | |
| * or throws an error if the next field in the stream is not of the correct | |
| * wire type. | |
| * | |
| * @return {number} The value of the signed 32-bit integer field. | |
| */ | |
| readSint32() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.VARINT); | |
| return BinaryDecoder.readZigzagVarint32(this.decoder_); | |
| } | |
| /** | |
| * Reads a signed zigzag-encoded 64-bit integer field from the binary stream, | |
| * or throws an error if the next field in the stream is not of the correct | |
| * wire type. | |
| * | |
| * @return {number} The value of the signed 64-bit integer field. | |
| */ | |
| readSint64() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.VARINT); | |
| return BinaryDecoder.readZigzagVarint64(this.decoder_); | |
| } | |
| /** | |
| * Reads a signed zigzag-encoded 64-bit integer field from the binary stream, | |
| * or throws an error if the next field in the stream is not of the correct | |
| * wire type. | |
| * | |
| * @return {string} The value of the signed 64-bit integer field as a decimal | |
| * string. | |
| */ | |
| readSint64String() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.VARINT); | |
| return BinaryDecoder.readZigzagVarint64String(this.decoder_); | |
| } | |
| /** | |
| * Reads an unsigned 32-bit fixed-length integer fiield from the binary | |
| * stream, or throws an error if the next field in the stream is not of the | |
| * correct wire type. | |
| * | |
| * @return {number} The value of the double field. | |
| */ | |
| readFixed32() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.FIXED32); | |
| return BinaryDecoder.readUint32(this.decoder_); | |
| } | |
| /** | |
| * Reads an unsigned 64-bit fixed-length integer fiield from the binary | |
| * stream, or throws an error if the next field in the stream is not of the | |
| * correct wire type. | |
| * | |
| * @return {number} The value of the float field. | |
| */ | |
| readFixed64() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.FIXED64); | |
| return BinaryDecoder.readUint64(this.decoder_); | |
| } | |
| /** | |
| * Reads an unsigned 64-bit integer field from the binary stream as a string, | |
| * or throws an error if the next field in the stream is not of the correct | |
| * wire type. | |
| * | |
| * Returns the value as a string. | |
| * | |
| * @return {string} The value of the unsigned 64-bit integer field as a | |
| * decimal string. | |
| */ | |
| readFixed64String() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.FIXED64); | |
| return BinaryDecoder.readUint64String(this.decoder_); | |
| } | |
| /** | |
| * Reads a signed 32-bit fixed-length integer fiield from the binary stream, | |
| * or throws an error if the next field in the stream is not of the correct | |
| * wire type. | |
| * | |
| * @return {number} The value of the signed 32-bit integer field. | |
| */ | |
| readSfixed32() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.FIXED32); | |
| return BinaryDecoder.readInt32(this.decoder_); | |
| } | |
| /** | |
| * Reads a signed 32-bit fixed-length integer fiield from the binary stream, | |
| * or throws an error if the next field in the stream is not of the correct | |
| * wire type. | |
| * | |
| * @return {string} The value of the signed 32-bit integer field as a decimal | |
| * string. | |
| */ | |
| readSfixed32String() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.FIXED32); | |
| return BinaryDecoder.readInt32(this.decoder_).toString(); | |
| } | |
| /** | |
| * Reads a signed 64-bit fixed-length integer fiield from the binary stream, | |
| * or throws an error if the next field in the stream is not of the correct | |
| * wire type. | |
| * | |
| * @return {number} The value of the sfixed64 field. | |
| */ | |
| readSfixed64() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.FIXED64); | |
| return BinaryDecoder.readInt64(this.decoder_); | |
| } | |
| /** | |
| * Reads a signed 64-bit fixed-length integer field from the binary stream, | |
| * or throws an error if the next field in the stream is not of the correct | |
| * wire type. | |
| * | |
| * Returns the value as a string. | |
| * | |
| * @return {string} The value of the sfixed64 field as a decimal string. | |
| */ | |
| readSfixed64String() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.FIXED64); | |
| return BinaryDecoder.readInt64String(this.decoder_); | |
| } | |
| /** | |
| * Reads a 32-bit floating-point field from the binary stream, or throws an | |
| * error if the next field in the stream is not of the correct wire type. | |
| * | |
| * @return {number} The value of the float field. | |
| */ | |
| readFloat() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.FIXED32); | |
| return BinaryDecoder.readFloat(this.decoder_); | |
| } | |
| /** | |
| * Reads a 64-bit floating-point field from the binary stream, or throws an | |
| * error if the next field in the stream is not of the correct wire type. | |
| * | |
| * @return {number} The value of the double field. | |
| */ | |
| readDouble() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.FIXED64); | |
| return BinaryDecoder.readDouble(this.decoder_); | |
| } | |
| /** | |
| * Reads a boolean field from the binary stream, or throws an error if the | |
| * next field in the stream is not of the correct wire type. | |
| * | |
| * @return {boolean} The value of the boolean field. | |
| */ | |
| readBool() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.VARINT); | |
| return BinaryDecoder.readBool(this.decoder_); | |
| } | |
| /** | |
| * Reads an enum field from the binary stream, or throws an error if the next | |
| * field in the stream is not of the correct wire type. | |
| * | |
| * @return {number} The value of the enum field. | |
| */ | |
| readEnum() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.VARINT); | |
| return BinaryDecoder.readSignedVarint32(this.decoder_); | |
| } | |
| /** | |
| * Reads a string field from the binary stream, or throws an error if the next | |
| * field in the stream is not of the correct wire type. | |
| * | |
| * @return {string} The value of the string field. | |
| */ | |
| readString() { | |
| // delegate to the other reader so that inlining can eliminate this method | |
| // in the common case. | |
| if (UTF8_PARSING_ERRORS_ARE_FATAL) { | |
| return this.readStringRequireUtf8(); | |
| } | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.DELIMITED); | |
| const length = BinaryDecoder.readUnsignedVarint32(this.decoder_); | |
| return this.decoder_.readString(length, /*parsingErrorsAreFatal=*/ false); | |
| } | |
| /** | |
| * Reads a string field from the binary stream, or throws an error if the next | |
| * field in the stream is not of the correct wire type, or if the string is | |
| * not valid utf8. | |
| * | |
| * @return {string} The value of the string field. | |
| */ | |
| readStringRequireUtf8() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.DELIMITED); | |
| const length = BinaryDecoder.readUnsignedVarint32(this.decoder_); | |
| return this.decoder_.readString(length, /*parsingErrorsAreFatal=*/ true); | |
| } | |
| /** | |
| * Reads a length-prefixed block of bytes from the binary stream, or returns | |
| * null if the next field in the stream has an invalid length value. | |
| * | |
| * @return {!Uint8Array} The block of bytes. | |
| */ | |
| readBytes() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.DELIMITED); | |
| const length = BinaryDecoder.readUnsignedVarint32(this.decoder_); | |
| return this.decoder_.readBytes(length); | |
| } | |
| /** | |
| * Reads a length-prefixed block of bytes from the binary stream, or returns | |
| * null if the next field in the stream has an invalid length value. | |
| * | |
| * @return {!ByteString} The block of bytes. | |
| */ | |
| readByteString() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.DELIMITED); | |
| const length = BinaryDecoder.readUnsignedVarint32(this.decoder_); | |
| return this.decoder_.readByteString(length); | |
| } | |
| /** | |
| * Reads a 64-bit varint field from the stream and invokes `convert` to | |
| * produce the return value, or throws an error if the next field in the | |
| * stream is not of the correct wire type. | |
| * | |
| * @param {function(number, number): T} convert Conversion function to produce | |
| * the result value, takes parameters (lowBits, highBits). | |
| * @return {T} | |
| * @template T | |
| */ | |
| readSplitVarint64(convert) { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.VARINT); | |
| return BinaryDecoder.readSplitVarint64(this.decoder_, convert); | |
| } | |
| /** | |
| * Reads a 64-bit zig-zag varint field from the stream and invokes `convert` | |
| * to produce the return value, or throws an error if the next field in the | |
| * stream is not of the correct wire type. | |
| * | |
| * @param {function(number, number): T} convert Conversion function to produce | |
| * the result value, takes parameters (lowBits, highBits). | |
| * @return {T} | |
| * @template T | |
| */ | |
| readSplitZigzagVarint64(convert) { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.VARINT); | |
| return BinaryDecoder.readSplitVarint64( | |
| this.decoder_, | |
| (lowBits, highBits) => utils.fromZigzag64(lowBits, highBits, convert)); | |
| } | |
| /** | |
| * Reads a 64-bit fixed64 field from the stream and invokes `convert` | |
| * to produce the return value, or throws an error if the next field in the | |
| * stream is not of the correct wire type. | |
| * | |
| * @param {function(number, number): T} convert Conversion function to produce | |
| * the result value, takes parameters (lowBits, highBits). | |
| * @return {T} | |
| * @template T | |
| */ | |
| readSplitFixed64(convert) { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.FIXED64); | |
| return BinaryDecoder.readSplitFixed64(this.decoder_, convert); | |
| } | |
| /** | |
| * Reads a packed scalar field using the supplied raw reader function. | |
| * @param {function(!BinaryDecoder):T} decodeMethod | |
| * @param {!Array<T>} dst | |
| * @template T | |
| * @private | |
| */ | |
| readPackedFieldInto_(decodeMethod, dst) { | |
| const length = this.readPackedFieldLength_(); | |
| const end = this.decoder_.getCursor() + length; | |
| while (this.decoder_.getCursor() < end) { | |
| dst.push(decodeMethod(this.decoder_)); | |
| } | |
| } | |
| /** | |
| * Reads the length of a packed field. | |
| * @return {number} | |
| * @private | |
| */ | |
| readPackedFieldLength_() { | |
| asserts.assert(this.nextWireType_ == BinaryConstants.WireType.DELIMITED); | |
| return BinaryDecoder.readUnsignedVarint32(this.decoder_); | |
| } | |
| /** | |
| * Reads an int32 field that might be packed | |
| * @param {!Array<number>} dst | |
| */ | |
| readPackableInt32Into(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readSignedVarint32, dst); | |
| } else { | |
| dst.push(this.readInt32()); | |
| } | |
| } | |
| /** | |
| * Reads an int64 field that might be packed | |
| * @param {!Array<number>}dst | |
| */ | |
| readPackableInt64Into(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readSignedVarint64, dst); | |
| } else { | |
| dst.push(this.readInt64()); | |
| } | |
| } | |
| /** | |
| * Reads an int64 field that might be packed | |
| * @param {!Array<string>} dst | |
| */ | |
| readPackableInt64StringInto(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readSignedVarint64String, dst); | |
| } else { | |
| dst.push(this.readInt64String()); | |
| } | |
| } | |
| /** | |
| * Reads an int32 field that might be packed | |
| * @param {!Array<number>} dst | |
| */ | |
| readPackableUint32Into(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readUnsignedVarint32, dst); | |
| } else { | |
| dst.push(this.readUint32()); | |
| } | |
| } | |
| /** | |
| * Reads an uint64 field that might be packed | |
| * @param {!Array<number>} dst | |
| */ | |
| readPackableUint64Into(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readUnsignedVarint64, dst); | |
| } else { | |
| dst.push(this.readUint64()); | |
| } | |
| } | |
| /** | |
| * Reads an uint64 field that might be packed | |
| * @param {!Array<string>} dst | |
| */ | |
| readPackableUint64StringInto(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readUnsignedVarint64String, dst); | |
| } else { | |
| dst.push(this.readUint64String()); | |
| } | |
| } | |
| /** | |
| * Reads a possibly packed sint32 field, | |
| * @param {!Array<number>} dst | |
| */ | |
| readPackableSint32Into(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readZigzagVarint32, dst); | |
| } else { | |
| dst.push(this.readSint32()); | |
| } | |
| } | |
| /** | |
| * Reads a possibly packed sint64 field, | |
| * @param {!Array<number>} dst | |
| */ | |
| readPackableSint64Into(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readZigzagVarint64, dst); | |
| } else { | |
| dst.push(this.readSint64()); | |
| } | |
| } | |
| /** | |
| * Reads a possibly packed sint64 field as an array of strings. | |
| * @param {!Array<string>} dst | |
| */ | |
| readPackableSint64StringInto(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readZigzagVarint64String, dst); | |
| } else { | |
| dst.push(this.readSint64String()); | |
| } | |
| } | |
| /** | |
| * Reads a possibly packed fixed32 field. | |
| * @param {!Array<number>} dst | |
| */ | |
| readPackableFixed32Into(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readUint32, dst); | |
| } else { | |
| dst.push(this.readFixed32()); | |
| } | |
| } | |
| /** | |
| * Reads a possibly packed fixed64 field. | |
| * @param {!Array<number>} dst | |
| */ | |
| readPackableFixed64Into(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readUint64, dst); | |
| } else { | |
| dst.push(this.readFixed64()); | |
| } | |
| } | |
| /** | |
| * Reads a possibly packed fixed64 field as an array fo strings | |
| * @param {!Array<string>} dst | |
| */ | |
| readPackableFixed64StringInto(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readUint64String, dst); | |
| } else { | |
| dst.push(this.readFixed64String()); | |
| } | |
| } | |
| /** | |
| * Reads a possibly packed sfixed32 | |
| * @param {!Array<number>} dst | |
| */ | |
| readPackableSfixed32Into(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readInt32, dst); | |
| } else { | |
| dst.push(this.readSfixed32()); | |
| } | |
| } | |
| /** | |
| * Reads a possibly packed sfixed64 field. | |
| * @param {!Array<number>} dst | |
| */ | |
| readPackableSfixed64Into(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readInt64, dst); | |
| } else { | |
| dst.push(this.readSfixed64()); | |
| } | |
| } | |
| /** | |
| * Reads a possibly packed sfixed64 field as an array of strings | |
| * @param {!Array<string>} dst | |
| */ | |
| readPackableSfixed64StringInto(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readInt64String, dst); | |
| } else { | |
| dst.push(this.readSfixed64String()); | |
| } | |
| } | |
| /** | |
| * Reads a packed fixed32 field, which consists of a length header and a list | |
| * of unsigned 32-bit ints. | |
| * @return {!Array<number>} | |
| * @export | |
| */ | |
| readPackedFixed32() { | |
| const values = []; | |
| this.readPackableFixed32Into(values); | |
| return values; | |
| } | |
| /** | |
| * Reads a packed fixed64 field, which consists of a length header and a list | |
| * of unsigned 64-bit ints. | |
| * @return {!Array<number>} | |
| * @export | |
| */ | |
| readPackedFixed64() { | |
| const values = []; | |
| this.readPackableFixed64Into(values); | |
| return values; | |
| } | |
| /** | |
| * Reads a packed fixed64 field, which consists of a length header and a list | |
| * of unsigned 64-bit ints. Returns a list of strings. | |
| * @return {!Array<string>} | |
| * @export | |
| */ | |
| readPackedFixed64String() { | |
| const values = []; | |
| this.readPackableFixed64StringInto(values); | |
| return values; | |
| } | |
| /** | |
| * Reads a packed sfixed32 field, which consists of a length header and a list | |
| * of 32-bit ints. | |
| * @return {!Array<number>} | |
| * @export | |
| */ | |
| readPackedSfixed32() { | |
| const values = []; | |
| this.readPackableSfixed32Into(values); | |
| return values; | |
| } | |
| /** | |
| * Reads a packed sfixed64 field, which consists of a length header and a list | |
| * of 64-bit ints. Returns a list of strings. | |
| * @return {!Array<string>} | |
| * @export | |
| */ | |
| readPackedSfixed64String() { | |
| const values = []; | |
| this.readPackableSfixed64StringInto(values); | |
| return values; | |
| } | |
| /** | |
| * Reads a possibly packed float field | |
| * @param {!Array<number>} dst | |
| */ | |
| readPackableFloatInto(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readFloat, dst); | |
| } else { | |
| dst.push(this.readFloat()); | |
| } | |
| } | |
| /** | |
| * Reads a possibly packed double field and appends it to `dst`. | |
| * @param {!Array<number>} dst | |
| */ | |
| readPackableDoubleInto(dst) { | |
| if (this.isDelimited()) { | |
| this.decoder_.readDoubleArrayInto(this.readPackedFieldLength_() / 8, dst); | |
| } else { | |
| dst.push(this.readDouble()); | |
| } | |
| } | |
| /** | |
| * Reads a possibly packed bool field | |
| * @param {!Array<boolean>} dst | |
| */ | |
| readPackableBoolInto(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readBool, dst); | |
| } else { | |
| dst.push(this.readBool()); | |
| } | |
| } | |
| /** | |
| * Reads a possibly packed enum field | |
| * @param {!Array<number>} dst | |
| */ | |
| readPackableEnumInto(dst) { | |
| if (this.isDelimited()) { | |
| this.readPackedFieldInto_(BinaryDecoder.readEnum, dst); | |
| } else { | |
| dst.push(this.readEnum()); | |
| } | |
| } | |
| /** | |
| * Visible for testing. | |
| * @package | |
| */ | |
| static resetInstanceCache() { | |
| BinaryReader.instanceCache_ = []; | |
| } | |
| /** | |
| * Visible for testing. | |
| * @return {!Array<!BinaryReader>} | |
| * @package | |
| */ | |
| static getInstanceCache() { | |
| return BinaryReader.instanceCache_; | |
| } | |
| } | |
| /** | |
| * @param {number} tag | |
| * @return {!BinaryConstants.WireType} | |
| */ | |
| function parseWireType(tag) { | |
| return /** @type {!BinaryConstants.WireType} */ (tag & 0x7); | |
| } | |
| /** | |
| * @param {number} tag | |
| * @return {number} | |
| */ | |
| function parseFieldNumber(tag) { | |
| return tag >>> 3; | |
| } | |
| /** | |
| * Global pool of BinaryReader instances. | |
| * @private {!Array<!BinaryReader>} | |
| */ | |
| BinaryReader.instanceCache_ = []; | |
| const /** number */ MESSAGE_SET_START_GROUP_TAG = utils.makeTag( | |
| BinaryConstants.MESSAGE_SET_GROUP_NUMBER, | |
| BinaryConstants.WireType.START_GROUP); | |
| const /** number */ MESSAGE_SET_TYPE_ID_TAG = utils.makeTag( | |
| BinaryConstants.MESSAGE_SET_TYPE_ID_FIELD_NUMBER, | |
| BinaryConstants.WireType.VARINT); | |
| const /** number */ MESSAGE_SET_MESSAGE_TAG = utils.makeTag( | |
| BinaryConstants.MESSAGE_SET_MESSAGE_FIELD_NUMBER, | |
| BinaryConstants.WireType.DELIMITED); | |
| const /** number */ MESSAGE_SET_END_TAG = utils.makeTag( | |
| BinaryConstants.MESSAGE_SET_GROUP_NUMBER, | |
| BinaryConstants.WireType.END_GROUP); | |
| exports = { | |
| BinaryReader, | |
| BinaryReaderOptions, | |
| MAX_RECURSION_DEPTH, | |
| UTF8_PARSING_ERRORS_ARE_FATAL, | |
| }; |