123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- /**
- * @fileoverview Contains classes that hold data for a protobuf field.
- */
- goog.module('protobuf.binary.field');
- const WireType = goog.requireType('protobuf.binary.WireType');
- const Writer = goog.requireType('protobuf.binary.Writer');
- const {checkDefAndNotNull, checkState} = goog.require('protobuf.internal.checks');
- /**
- * Number of bits taken to represent a wire type.
- * @const {number}
- */
- const WIRE_TYPE_LENGTH_BITS = 3;
- /** @const {number} */
- const WIRE_TYPE_EXTRACTOR = (1 << WIRE_TYPE_LENGTH_BITS) - 1;
- /**
- * An IndexEntry consists of the wire type and the position of a field in the
- * binary data. The wire type and the position are encoded into a single number
- * to save memory, which can be decoded using Field.getWireType() and
- * Field.getStartIndex() methods.
- * @typedef {number}
- */
- let IndexEntry;
- /**
- * An entry containing the index into the binary data and/or the corresponding
- * cached JS object(s) for a field.
- * @template T
- * @final
- * @package
- */
- class Field {
- /**
- * Creates a field and inserts the wireType and position of the first
- * occurrence of a field.
- * @param {!WireType} wireType
- * @param {number} startIndex
- * @return {!Field}
- */
- static fromFirstIndexEntry(wireType, startIndex) {
- return new Field([Field.encodeIndexEntry(wireType, startIndex)]);
- }
- /**
- * @param {T} decodedValue The cached JS object decoded from the binary data.
- * @param {function(!Writer, number, T):void|undefined} encoder Write function
- * to encode the cache into binary bytes.
- * @return {!Field}
- * @template T
- */
- static fromDecodedValue(decodedValue, encoder) {
- return new Field(null, decodedValue, encoder);
- }
- /**
- * @param {!WireType} wireType
- * @param {number} startIndex
- * @return {!IndexEntry}
- */
- static encodeIndexEntry(wireType, startIndex) {
- return startIndex << WIRE_TYPE_LENGTH_BITS | wireType;
- }
- /**
- * @param {!IndexEntry} indexEntry
- * @return {!WireType}
- */
- static getWireType(indexEntry) {
- return /** @type {!WireType} */ (indexEntry & WIRE_TYPE_EXTRACTOR);
- }
- /**
- * @param {!IndexEntry} indexEntry
- * @return {number}
- */
- static getStartIndex(indexEntry) {
- return indexEntry >> WIRE_TYPE_LENGTH_BITS;
- }
- /**
- * @param {?Array<!IndexEntry>} indexArray
- * @param {T=} decodedValue
- * @param {function(!Writer, number, T):void=} encoder
- * @private
- */
- constructor(indexArray, decodedValue = undefined, encoder = undefined) {
- checkState(
- !!indexArray || decodedValue !== undefined,
- 'At least one of indexArray and decodedValue must be set');
- /** @private {?Array<!IndexEntry>} */
- this.indexArray_ = indexArray;
- /** @private {T|undefined} */
- this.decodedValue_ = decodedValue;
- // TODO: Consider storing an enum to represent encoder
- /** @private {function(!Writer, number, T)|undefined} */
- this.encoder_ = encoder;
- }
- /**
- * Adds a new IndexEntry.
- * @param {!WireType} wireType
- * @param {number} startIndex
- */
- addIndexEntry(wireType, startIndex) {
- checkDefAndNotNull(this.indexArray_)
- .push(Field.encodeIndexEntry(wireType, startIndex));
- }
- /**
- * Returns the array of IndexEntry.
- * @return {?Array<!IndexEntry>}
- */
- getIndexArray() {
- return this.indexArray_;
- }
- /**
- * Caches the decoded value and sets the write function to encode cache into
- * binary bytes.
- * @param {T} decodedValue
- * @param {function(!Writer, number, T):void|undefined} encoder
- */
- setCache(decodedValue, encoder) {
- this.decodedValue_ = decodedValue;
- this.encoder_ = encoder;
- this.maybeRemoveIndexArray_();
- }
- /**
- * If the decoded value has been set.
- * @return {boolean}
- */
- hasDecodedValue() {
- return this.decodedValue_ !== undefined;
- }
- /**
- * Returns the cached decoded value. The value needs to be set when this
- * method is called.
- * @return {T}
- */
- getDecodedValue() {
- // Makes sure that the decoded value in the cache has already been set. This
- // prevents callers from doing `if (field.getDecodedValue()) {...}` to check
- // if a value exist in the cache, because the check might return false even
- // if the cache has a valid value set (e.g. 0 or empty string).
- checkState(this.decodedValue_ !== undefined);
- return this.decodedValue_;
- }
- /**
- * Returns the write function to encode cache into binary bytes.
- * @return {function(!Writer, number, T)|undefined}
- */
- getEncoder() {
- return this.encoder_;
- }
- /**
- * Returns a copy of the field, containing the original index entries and a
- * shallow copy of the cache.
- * @return {!Field}
- */
- shallowCopy() {
- // Repeated fields are arrays in the cache.
- // We have to copy the array to make sure that modifications to a repeated
- // field (e.g. add) are not seen on a cloned accessor.
- const copiedCache = this.hasDecodedValue() ?
- (Array.isArray(this.getDecodedValue()) ? [...this.getDecodedValue()] :
- this.getDecodedValue()) :
- undefined;
- return new Field(this.getIndexArray(), copiedCache, this.getEncoder());
- }
- /**
- * @private
- */
- maybeRemoveIndexArray_() {
- checkState(
- this.encoder_ === undefined || this.decodedValue_ !== undefined,
- 'Encoder exists but decoded value doesn\'t');
- if (this.encoder_ !== undefined) {
- this.indexArray_ = null;
- }
- }
- }
- exports = {
- IndexEntry,
- Field,
- };
|