Protocol Buffers - Google's data interchange format (grpc依赖)
https://developers.google.com/protocol-buffers/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
5.4 KiB
196 lines
5.4 KiB
/** |
|
* @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, |
|
};
|
|
|