iw6-mod/deps/protobuf/js/experimental/runtime/kernel/field.js
2024-02-27 01:34:37 -05:00

197 lines
5.4 KiB
JavaScript

/**
* @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,
};