| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 | /** * @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, checkCriticalPositionIndex, checkCriticalState, 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_ = startIndex + length;    /** @private {number} */    this.cursor_ = startIndex;  }  /**   * 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 the start position of the next data, i.e. end position of the last   * read data + 1.   * @return {number}   */  cursor() {    return this.cursor_;  }  /**   * Sets the cursor to the specified position.   * @param {number} position   */  setCursor(position) {    this.cursor_ = position;  }  /**   * Returns if there is more data to read after the current cursor position.   * @return {boolean}   */  hasNext() {    return this.cursor_ < this.endIndex_;  }  /**   * Returns a float32 from a given index   * @param {number} index   * @return {number}   */  getFloat32(index) {    this.cursor_ = index + 4;    return this.dataView_.getFloat32(index, true);  }  /**   * Returns a float64 from a given index   * @param {number} index   * @return {number}   */  getFloat64(index) {    this.cursor_ = index + 8;    return this.dataView_.getFloat64(index, true);  }  /**   * Returns an int32 from a given index   * @param {number} index   * @return {number}   */  getInt32(index) {    this.cursor_ = index + 4;    return this.dataView_.getInt32(index, true);  }  /**   * Returns a uint32 from a given index   * @param {number} index   * @return {number}   */  getUint32(index) {    this.cursor_ = index + 4;    return this.dataView_.getUint32(index, true);  }  /**   * Returns two JS numbers each representing 32 bits of a 64 bit number. Also   * sets the cursor to the start of the next block of data.   * @param {number} index   * @return {{lowBits: number, highBits: number}}   */  getVarint(index) {    this.cursor_ = index;    let lowBits = 0;    let highBits = 0;    for (let shift = 0; shift < 28; shift += 7) {      const b = this.dataView_.getUint8(this.cursor_++);      lowBits |= (b & 0x7F) << shift;      if ((b & 0x80) === 0) {        return {lowBits, highBits};      }    }    const middleByte = this.dataView_.getUint8(this.cursor_++);    // 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};    }    for (let shift = 3; shift <= 31; shift += 7) {      const b = this.dataView_.getUint8(this.cursor_++);      highBits |= (b & 0x7F) << shift;      if ((b & 0x80) === 0) {        return {lowBits, highBits};      }    }    checkCriticalState(false, 'Data is longer than 10 bytes');    return {lowBits, highBits};  }  /**   * Returns an unsigned int32 number at the current cursor position. The upper   * bits are discarded if the varint is longer than 32 bits. Also sets the   * cursor to the start of the next block of data.   * @return {number}   */  getUnsignedVarint32() {    let b = this.dataView_.getUint8(this.cursor_++);    let result = b & 0x7F;    if ((b & 0x80) === 0) {      return result;    }    b = this.dataView_.getUint8(this.cursor_++);    result |= (b & 0x7F) << 7;    if ((b & 0x80) === 0) {      return result;    }    b = this.dataView_.getUint8(this.cursor_++);    result |= (b & 0x7F) << 14;    if ((b & 0x80) === 0) {      return result;    }    b = this.dataView_.getUint8(this.cursor_++);    result |= (b & 0x7F) << 21;    if ((b & 0x80) === 0) {      return result;    }    // Extract only last 4 bits    b = this.dataView_.getUint8(this.cursor_++);    result |= (b & 0x0F) << 28;    for (let readBytes = 5; ((b & 0x80) !== 0) && readBytes < 10; readBytes++) {      b = this.dataView_.getUint8(this.cursor_++);    }    checkCriticalState((b & 0x80) === 0, 'Data is longer than 10 bytes');    // Result can be have 32 bits, convert it to unsigned    return result >>> 0;  }  /**   * Returns an unsigned int32 number at the specified index. The upper bits are   * discarded if the varint is longer than 32 bits. Also sets the cursor to the   * start of the next block of data.   * @param {number} index   * @return {number}   */  getUnsignedVarint32At(index) {    this.cursor_ = index;    return this.getUnsignedVarint32();  }  /**   * Seeks forward by the given amount.   * @param {number} skipAmount   * @package   */  skip(skipAmount) {    this.cursor_ += skipAmount;    checkCriticalPositionIndex(this.cursor_, this.endIndex_);  }  /**   * Skips over a varint from the current cursor position.   * @package   */  skipVarint() {    const startIndex = this.cursor_;    while (this.dataView_.getUint8(this.cursor_++) & 0x80) {    }    checkCriticalPositionIndex(this.cursor_, startIndex + 10);  }  /**   * @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;
 |