| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 | /** * @fileoverview A buffer implementation that can decode data for protobufs. */goog.module('protobuf.binary.BufferDecoder');const ByteString = goog.require('protobuf.ByteString');const functions = goog.require('goog.functions');const {POLYFILL_TEXT_ENCODING, checkCriticalElementIndex, checkCriticalPositionIndex, checkState} = goog.require('protobuf.internal.checks');const {byteStringFromUint8ArrayUnsafe} = goog.require('protobuf.byteStringInternal');const {concatenateByteArrays} = goog.require('protobuf.binary.uint8arrays');const {decode} = goog.require('protobuf.binary.textencoding');/** * Returns a valid utf-8 decoder function based on TextDecoder if available or * a polyfill. * Some of the environments we run in do not have TextDecoder defined. * TextDecoder is faster than our polyfill so we prefer it over the polyfill. * @return {function(!DataView): string} */function getStringDecoderFunction() {  if (goog.global['TextDecoder']) {    const textDecoder = new goog.global['TextDecoder']('utf-8', {fatal: true});    return bytes => textDecoder.decode(bytes);  }  if (POLYFILL_TEXT_ENCODING) {    return decode;  } else {    throw new Error(        'TextDecoder is missing. ' +        'Enable protobuf.defines.POLYFILL_TEXT_ENCODING.');  }}/** @type {function(): function(!DataView): string} */const stringDecoderFunction =    functions.cacheReturnValue(() => getStringDecoderFunction());/** @type {function(): !DataView} */const emptyDataView =    functions.cacheReturnValue(() => new DataView(new ArrayBuffer(0)));class BufferDecoder {  /**   * @param {!Array<!BufferDecoder>} bufferDecoders   * @return {!BufferDecoder}   */  static merge(bufferDecoders) {    const uint8Arrays = bufferDecoders.map(b => b.asUint8Array());    const bytesArray = concatenateByteArrays(uint8Arrays);    return BufferDecoder.fromArrayBuffer(bytesArray.buffer);  }  /**   * @param {!ArrayBuffer} arrayBuffer   * @return {!BufferDecoder}   */  static fromArrayBuffer(arrayBuffer) {    return new BufferDecoder(        new DataView(arrayBuffer), 0, arrayBuffer.byteLength);  }  /**   * @param {!DataView} dataView   * @param {number} startIndex   * @param {number} length   * @private   */  constructor(dataView, startIndex, length) {    /** @private @const {!DataView} */    this.dataView_ = dataView;    /** @private @const {number} */    this.startIndex_ = startIndex;    /** @private @const {number} */    this.endIndex_ = this.startIndex_ + length;  }  /**   * Returns the start index of the underlying buffer.   * @return {number}   */  startIndex() {    return this.startIndex_;  }  /**   * Returns the end index of the underlying buffer.   * @return {number}   */  endIndex() {    return this.endIndex_;  }  /**   * Returns the length of the underlying buffer.   * @return {number}   */  length() {    return this.endIndex_ - this.startIndex_;  }  /**   * Returns a float32 from a given index   * @param {number} index   * @return {number}   */  getFloat32(index) {    return this.dataView_.getFloat32(index, true);  }  /**   * Returns a float64 from a given index   * @param {number} index   * @return {number}   */  getFloat64(index) {    return this.dataView_.getFloat64(index, true);  }  /**   * Returns an int32 from a given index   * @param {number} index   * @return {number}   */  getInt32(index) {    return this.dataView_.getInt32(index, true);  }  /**   * @param {number} index   * @return {number}   */  getUint8(index) {    return this.dataView_.getUint8(index);  }  /**   * Returns a uint32 from a given index   * @param {number} index   * @return {number}   */  getUint32(index) {    return this.dataView_.getUint32(index, true);  }  /**   * Returns two JS numbers each representing 32 bits of a 64 bit number.   * @param {number} index   * @return {{lowBits: number, highBits: number, dataStart: number}}   */  getVarint(index) {    let start = index;    let lowBits = 0;    let highBits = 0;    for (let shift = 0; shift < 28; shift += 7) {      const b = this.dataView_.getUint8(start++);      lowBits |= (b & 0x7F) << shift;      if ((b & 0x80) === 0) {        return {lowBits, highBits, dataStart: start};      }    }    const middleByte = this.dataView_.getUint8(start++);    // last four bits of the first 32 bit number    lowBits |= (middleByte & 0x0F) << 28;    // 3 upper bits are part of the next 32 bit number    highBits = (middleByte & 0x70) >> 4;    if ((middleByte & 0x80) === 0) {      return {lowBits, highBits, dataStart: start};    }    for (let shift = 3; shift <= 31; shift += 7) {      const b = this.dataView_.getUint8(start++);      highBits |= (b & 0x7F) << shift;      if ((b & 0x80) === 0) {        return {lowBits, highBits, dataStart: start};      }    }    checkState(false, 'Data is longer than 10 bytes');    return {lowBits, highBits, dataStart: start};  }  /**   * Skips over a varint at a given index and returns the next position.   * @param {number} index Start of the data.   * @return {number} Position of the first byte after the varint.   * @package   */  skipVarint(index) {    let cursor = index;    checkCriticalElementIndex(cursor, this.endIndex());    while (this.dataView_.getUint8(cursor++) & 0x80) {      checkCriticalElementIndex(cursor, this.endIndex());    }    checkCriticalPositionIndex(cursor, index + 10);    return cursor;  }  /**   * @param {number} startIndex   * @param {number} length   * @return {!BufferDecoder}   */  subBufferDecoder(startIndex, length) {    checkState(        startIndex >= this.startIndex(),        `Current start: ${this.startIndex()}, subBufferDecoder start: ${            startIndex}`);    checkState(length >= 0, `Length: ${length}`);    checkState(        startIndex + length <= this.endIndex(),        `Current end: ${this.endIndex()}, subBufferDecoder start: ${            startIndex}, subBufferDecoder length: ${length}`);    return new BufferDecoder(this.dataView_, startIndex, length);  }  /**   * Returns the buffer as a string.   * @return {string}   */  asString() {    // TODO: Remove this check when we no longer need to support IE    const stringDataView = this.length() === 0 ?        emptyDataView() :        new DataView(this.dataView_.buffer, this.startIndex_, this.length());    return stringDecoderFunction()(stringDataView);  }  /**   * Returns the buffer as a ByteString.   * @return {!ByteString}   */  asByteString() {    return byteStringFromUint8ArrayUnsafe(this.asUint8Array());  }  /**   * Returns the DataView as an Uint8Array. DO NOT MODIFY or expose the   * underlying buffer.   *   * @package   * @return {!Uint8Array}   */  asUint8Array() {    return new Uint8Array(        this.dataView_.buffer, this.startIndex_, this.length());  }}exports = BufferDecoder;
 |