Project import generated by Copybara

PiperOrigin-RevId: 293911829
pull/7213/head
rafikamal 5 years ago committed by Rafi Kamal
parent 46b0fd9cec
commit becf5afd34
  1. 146
      js/experimental/runtime/kernel/buffer_decoder.js
  2. 158
      js/experimental/runtime/kernel/buffer_decoder_test.js
  3. 62
      js/experimental/runtime/kernel/indexer.js
  4. 178
      js/experimental/runtime/kernel/reader.js
  5. 14
      js/experimental/runtime/kernel/reader_test.js
  6. 14
      js/experimental/runtime/kernel/writer.js

@ -6,7 +6,7 @@ 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 {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');
@ -72,7 +72,9 @@ class BufferDecoder {
/** @private @const {number} */
this.startIndex_ = startIndex;
/** @private @const {number} */
this.endIndex_ = this.startIndex_ + length;
this.endIndex_ = startIndex + length;
/** @private {number} */
this.cursor_ = startIndex;
}
/**
@ -99,12 +101,38 @@ class BufferDecoder {
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);
}
@ -114,6 +142,7 @@ class BufferDecoder {
* @return {number}
*/
getFloat64(index) {
this.cursor_ = index + 8;
return this.dataView_.getFloat64(index, true);
}
@ -123,45 +152,40 @@ class BufferDecoder {
* @return {number}
*/
getInt32(index) {
this.cursor_ = index + 4;
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) {
this.cursor_ = index + 4;
return this.dataView_.getUint32(index, true);
}
/**
* Returns two JS numbers each representing 32 bits of a 64 bit number.
* 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, dataStart: number}}
* @return {{lowBits: number, highBits: number}}
*/
getVarint(index) {
let start = index;
this.cursor_ = index;
let lowBits = 0;
let highBits = 0;
for (let shift = 0; shift < 28; shift += 7) {
const b = this.dataView_.getUint8(start++);
const b = this.dataView_.getUint8(this.cursor_++);
lowBits |= (b & 0x7F) << shift;
if ((b & 0x80) === 0) {
return {lowBits, highBits, dataStart: start};
return {lowBits, highBits};
}
}
const middleByte = this.dataView_.getUint8(start++);
const middleByte = this.dataView_.getUint8(this.cursor_++);
// last four bits of the first 32 bit number
lowBits |= (middleByte & 0x0F) << 28;
@ -170,36 +194,100 @@ class BufferDecoder {
highBits = (middleByte & 0x70) >> 4;
if ((middleByte & 0x80) === 0) {
return {lowBits, highBits, dataStart: start};
return {lowBits, highBits};
}
for (let shift = 3; shift <= 31; shift += 7) {
const b = this.dataView_.getUint8(start++);
const b = this.dataView_.getUint8(this.cursor_++);
highBits |= (b & 0x7F) << shift;
if ((b & 0x80) === 0) {
return {lowBits, highBits, dataStart: start};
return {lowBits, highBits};
}
}
checkState(false, 'Data is longer than 10 bytes');
return {lowBits, highBits, dataStart: start};
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 at a given index and returns the next position.
* Skips over a varint at a given index.
* @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());
this.cursor_ = index;
while (this.dataView_.getUint8(this.cursor_++) & 0x80) {
}
checkCriticalPositionIndex(cursor, index + 10);
return cursor;
checkCriticalPositionIndex(this.cursor_, index + 10);
}
/**

@ -5,7 +5,7 @@
goog.module('protobuf.binary.varintsTest');
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder');
const {CHECK_CRITICAL_BOUNDS, CHECK_STATE} = goog.require('protobuf.internal.checks');
const {CHECK_CRITICAL_STATE, CHECK_STATE} = goog.require('protobuf.internal.checks');
goog.setTestOnly();
@ -17,25 +17,47 @@ function createArrayBuffer(...bytes) {
return new Uint8Array(bytes).buffer;
}
describe('setCursor does', () => {
it('set the cursor at the position specified', () => {
const bufferDecoder =
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x0, 0x1));
expect(bufferDecoder.cursor()).toBe(0);
bufferDecoder.setCursor(1);
expect(bufferDecoder.cursor()).toBe(1);
});
});
describe('skip does', () => {
it('advance the cursor', () => {
const bufferDecoder =
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x0, 0x1, 0x2));
bufferDecoder.setCursor(1);
bufferDecoder.skip(1);
expect(bufferDecoder.cursor()).toBe(2);
});
});
describe('Skip varint does', () => {
it('skip a varint', () => {
const bufferDecoder =
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x01));
expect(bufferDecoder.skipVarint(0)).toBe(1);
bufferDecoder.skipVarint(0);
expect(bufferDecoder.cursor()).toBe(1);
});
it('fail when varint is larger than 10 bytes', () => {
const bufferDecoder = BufferDecoder.fromArrayBuffer(createArrayBuffer(
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00));
if (CHECK_CRITICAL_BOUNDS) {
if (CHECK_CRITICAL_STATE) {
expect(() => bufferDecoder.skipVarint(0)).toThrow();
} else {
// Note in unchecked mode we produce invalid output for invalid inputs.
// This test just documents our behavior in those cases.
// These values might change at any point and are not considered
// what the implementation should be doing here.
expect(bufferDecoder.skipVarint(0)).toBe(11);
bufferDecoder.skipVarint(0);
expect(bufferDecoder.cursor()).toBe(11);
}
});
@ -50,28 +72,144 @@ describe('readVarint64 does', () => {
it('read zero', () => {
const bufferDecoder =
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x00));
const {dataStart, lowBits, highBits} = bufferDecoder.getVarint(0);
expect(dataStart).toBe(1);
const {lowBits, highBits} = bufferDecoder.getVarint(0);
expect(lowBits).toBe(0);
expect(highBits).toBe(0);
expect(bufferDecoder.cursor()).toBe(1);
});
it('read one', () => {
const bufferDecoder =
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x01));
const {dataStart, lowBits, highBits} = bufferDecoder.getVarint(0);
expect(dataStart).toBe(1);
const {lowBits, highBits} = bufferDecoder.getVarint(0);
expect(lowBits).toBe(1);
expect(highBits).toBe(0);
expect(bufferDecoder.cursor()).toBe(1);
});
it('read max value', () => {
const bufferDecoder = BufferDecoder.fromArrayBuffer(createArrayBuffer(
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01));
const {dataStart, lowBits, highBits} = bufferDecoder.getVarint(0);
expect(dataStart).toBe(10);
const {lowBits, highBits} = bufferDecoder.getVarint(0);
expect(lowBits).toBe(-1);
expect(highBits).toBe(-1);
expect(bufferDecoder.cursor()).toBe(10);
});
});
describe('readUnsignedVarint32 does', () => {
it('read zero', () => {
const bufferDecoder =
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x00));
const result = bufferDecoder.getUnsignedVarint32();
expect(result).toBe(0);
expect(bufferDecoder.cursor()).toBe(1);
});
it('read one', () => {
const bufferDecoder =
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x01));
const result = bufferDecoder.getUnsignedVarint32();
expect(result).toBe(1);
expect(bufferDecoder.cursor()).toBe(1);
});
it('read max int32', () => {
const bufferDecoder = BufferDecoder.fromArrayBuffer(
createArrayBuffer(0xFF, 0xFF, 0xFF, 0xFF, 0x0F));
const result = bufferDecoder.getUnsignedVarint32();
expect(result).toBe(4294967295);
expect(bufferDecoder.cursor()).toBe(5);
});
it('read max value', () => {
const bufferDecoder = BufferDecoder.fromArrayBuffer(createArrayBuffer(
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01));
const result = bufferDecoder.getUnsignedVarint32();
expect(result).toBe(4294967295);
expect(bufferDecoder.cursor()).toBe(10);
});
it('fail if data is longer than 10 bytes', () => {
const bufferDecoder = BufferDecoder.fromArrayBuffer(createArrayBuffer(
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01));
if (CHECK_CRITICAL_STATE) {
expect(() => bufferDecoder.getUnsignedVarint32()).toThrow();
} else {
// Note in unchecked mode we produce invalid output for invalid inputs.
// This test just documents our behavior in those cases.
// These values might change at any point and are not considered
// what the implementation should be doing here.
const result = bufferDecoder.getUnsignedVarint32();
expect(result).toBe(4294967295);
expect(bufferDecoder.cursor()).toBe(10);
}
});
});
describe('readUnsignedVarint32At does', () => {
it('reads from a specific index', () => {
const bufferDecoder =
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x1, 0x2));
const result = bufferDecoder.getUnsignedVarint32At(1);
expect(result).toBe(2);
expect(bufferDecoder.cursor()).toBe(2);
});
});
describe('getFloat32 does', () => {
it('read one', () => {
const bufferDecoder = BufferDecoder.fromArrayBuffer(
createArrayBuffer(0x00, 0x00, 0x80, 0x3F));
const result = bufferDecoder.getFloat32(0);
expect(result).toBe(1);
expect(bufferDecoder.cursor()).toBe(4);
});
});
describe('getFloat64 does', () => {
it('read one', () => {
const bufferDecoder = BufferDecoder.fromArrayBuffer(
createArrayBuffer(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F));
const result = bufferDecoder.getFloat64(0);
expect(result).toBe(1);
expect(bufferDecoder.cursor()).toBe(8);
});
});
describe('getInt32 does', () => {
it('read one', () => {
const bufferDecoder = BufferDecoder.fromArrayBuffer(
createArrayBuffer(0x01, 0x00, 0x00, 0x00));
const result = bufferDecoder.getInt32(0);
expect(result).toBe(1);
expect(bufferDecoder.cursor()).toBe(4);
});
it('read minus one', () => {
const bufferDecoder = BufferDecoder.fromArrayBuffer(
createArrayBuffer(0xFF, 0xFF, 0xFF, 0xFF));
const result = bufferDecoder.getInt32(0);
expect(result).toBe(-1);
expect(bufferDecoder.cursor()).toBe(4);
});
});
describe('getUint32 does', () => {
it('read one', () => {
const bufferDecoder =
BufferDecoder.fromArrayBuffer(createArrayBuffer(0x01, 0x00, 0x00, 0x0));
const result = bufferDecoder.getUint32(0);
expect(result).toBe(1);
expect(bufferDecoder.cursor()).toBe(4);
});
it('read max uint32', () => {
const bufferDecoder = BufferDecoder.fromArrayBuffer(
createArrayBuffer(0xFF, 0xFF, 0xFF, 0xFF));
const result = bufferDecoder.getUint32(0);
expect(result).toBe(4294967295);
expect(bufferDecoder.cursor()).toBe(4);
});
});

@ -8,7 +8,7 @@ const BufferDecoder = goog.require('protobuf.binary.BufferDecoder');
const Storage = goog.require('protobuf.binary.Storage');
const WireType = goog.require('protobuf.binary.WireType');
const {Field} = goog.require('protobuf.binary.field');
const {checkCriticalPositionIndex, checkCriticalState} = goog.require('protobuf.internal.checks');
const {checkCriticalElementIndex, checkCriticalState} = goog.require('protobuf.internal.checks');
/**
* Appends a new entry in the index array for the given field number.
@ -57,8 +57,6 @@ class Indexer {
constructor(bufferDecoder) {
/** @private @const {!BufferDecoder} */
this.bufferDecoder_ = bufferDecoder;
/** @private {number} */
this.cursor_ = bufferDecoder.startIndex();
}
/**
@ -66,15 +64,18 @@ class Indexer {
* @return {!Storage<!Field>}
*/
index(pivot) {
this.bufferDecoder_.setCursor(this.bufferDecoder_.startIndex());
const storage = new Storage(pivot);
while (this.hasNextByte_()) {
const tag = this.readVarInt32_();
while (this.bufferDecoder_.hasNext()) {
const tag = this.bufferDecoder_.getUnsignedVarint32();
const wireType = tagToWireType(tag);
const fieldNumber = tagToFieldNumber(tag);
checkCriticalState(
fieldNumber > 0, `Invalid field number ${fieldNumber}`);
addIndexEntry(storage, fieldNumber, wireType, this.cursor_);
addIndexEntry(
storage, fieldNumber, wireType, this.bufferDecoder_.cursor());
checkCriticalState(
!this.skipField_(wireType, fieldNumber),
@ -93,14 +94,18 @@ class Indexer {
skipField_(wireType, fieldNumber) {
switch (wireType) {
case WireType.VARINT:
this.cursor_ = this.bufferDecoder_.skipVarint(this.cursor_);
checkCriticalElementIndex(
this.bufferDecoder_.cursor(), this.bufferDecoder_.endIndex());
this.bufferDecoder_.skipVarint(this.bufferDecoder_.cursor());
return false;
case WireType.FIXED64:
this.skip_(8);
this.bufferDecoder_.skip(8);
return false;
case WireType.DELIMITED:
const length = this.readVarInt32_();
this.skip_(length);
checkCriticalElementIndex(
this.bufferDecoder_.cursor(), this.bufferDecoder_.endIndex());
const length = this.bufferDecoder_.getUnsignedVarint32();
this.bufferDecoder_.skip(length);
return false;
case WireType.START_GROUP:
checkCriticalState(this.skipGroup_(fieldNumber), 'No end group found.');
@ -109,23 +114,13 @@ class Indexer {
// Signal that we found a stop group to the caller
return true;
case WireType.FIXED32:
this.skip_(4);
this.bufferDecoder_.skip(4);
return false;
default:
throw new Error(`Invalid wire type: ${wireType}`);
}
}
/**
* Seeks forward by the given amount.
* @param {number} skipAmount
* @private
*/
skip_(skipAmount) {
this.cursor_ += skipAmount;
checkCriticalPositionIndex(this.cursor_, this.bufferDecoder_.endIndex());
}
/**
* Skips over fields until it finds the end of a given group.
* @param {number} groupFieldNumber
@ -138,8 +133,8 @@ class Indexer {
// Note: Since we are calling skipField from here nested groups will be
// handled by recursion of this method and thus we will not see a nested
// STOP GROUP here unless there is something wrong with the input data.
while (this.hasNextByte_()) {
const tag = this.readVarInt32_();
while (this.bufferDecoder_.hasNext()) {
const tag = this.bufferDecoder_.getUnsignedVarint32();
const wireType = tagToWireType(tag);
const fieldNumber = tagToFieldNumber(tag);
@ -153,26 +148,6 @@ class Indexer {
}
return false;
}
/**
* Returns a JS number for a 32 bit var int.
* @return {number}
* @private
*/
readVarInt32_() {
const {lowBits, dataStart} = this.bufferDecoder_.getVarint(this.cursor_);
this.cursor_ = dataStart;
return lowBits;
}
/**
* Returns true if there are more bytes to read in the array.
* @return {boolean}
* @private
*/
hasNextByte_() {
return this.cursor_ < this.bufferDecoder_.endIndex();
}
}
/**
@ -186,7 +161,6 @@ function buildIndex(bufferDecoder, pivot) {
return new Indexer(bufferDecoder).index(pivot);
}
exports = {
buildIndex,
};

@ -6,24 +6,13 @@ goog.module('protobuf.binary.reader');
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder');
const ByteString = goog.require('protobuf.ByteString');
const Int64 = goog.require('protobuf.Int64');
const {checkState} = goog.require('protobuf.internal.checks');
/******************************************************************************
* OPTIONAL FUNCTIONS
******************************************************************************/
/**
* Reads a boolean from the binary bytes.
* Also returns the first position after the boolean.
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes.
* @param {number} index Start of the data.
* @return {{value: boolean, nextCursor: number}}
*/
function readBoolValue(bufferDecoder, index) {
const {lowBits, highBits, dataStart} = bufferDecoder.getVarint(index);
return {value: lowBits !== 0 || highBits !== 0, nextCursor: dataStart};
}
/**
* Reads a boolean value from the binary bytes.
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes.
@ -32,11 +21,12 @@ function readBoolValue(bufferDecoder, index) {
* @package
*/
function readBool(bufferDecoder, start) {
return readBoolValue(bufferDecoder, start).value;
const {lowBits, highBits} = bufferDecoder.getVarint(start);
return lowBits !== 0 || highBits !== 0;
}
/**
* Reads a double value from the binary bytes.
* Reads a ByteString value from the binary bytes.
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes.
* @param {number} start Start of the data.
* @return {!ByteString}
@ -46,21 +36,6 @@ function readBytes(bufferDecoder, start) {
return readDelimited(bufferDecoder, start).asByteString();
}
/**
* Reads a int32 value from the binary bytes encoded as varint.
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes.
* @param {number} index Start of the data.
* @return {{value: number, nextCursor: number}}
* @package
*/
function readInt32Value(bufferDecoder, index) {
const {lowBits, dataStart} = bufferDecoder.getVarint(index);
// Negative 32 bit integers are encoded with 64 bit values.
// Clients are expected to truncate back to 32 bits.
// This is why we are dropping the upper bytes here.
return {value: lowBits | 0, nextCursor: dataStart};
}
/**
* Reads a int32 value from the binary bytes encoded as varint.
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes.
@ -69,19 +44,10 @@ function readInt32Value(bufferDecoder, index) {
* @package
*/
function readInt32(bufferDecoder, start) {
return readInt32Value(bufferDecoder, start).value;
}
/**
* Reads a int32 value from the binary bytes encoded as varint.
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes.
* @param {number} index Start of the data.
* @return {{ value: !Int64, nextCursor: number}}
* @package
*/
function readInt64Value(bufferDecoder, index) {
const {lowBits, highBits, dataStart} = bufferDecoder.getVarint(index);
return {value: Int64.fromBits(lowBits, highBits), nextCursor: dataStart};
// Negative 32 bit integers are encoded with 64 bit values.
// Clients are expected to truncate back to 32 bits.
// This is why we are dropping the upper bytes here.
return bufferDecoder.getUnsignedVarint32At(start) | 0;
}
/**
@ -92,7 +58,8 @@ function readInt64Value(bufferDecoder, index) {
* @package
*/
function readInt64(bufferDecoder, start) {
return readInt64Value(bufferDecoder, start).value;
const {lowBits, highBits} = bufferDecoder.getVarint(start);
return Int64.fromBits(lowBits, highBits);
}
/**
@ -130,19 +97,6 @@ function readSfixed64(bufferDecoder, start) {
return Int64.fromBits(lowBits, highBits);
}
/**
* Reads a sint32 value from the binary bytes encoded as varint.
* Also returns the first position after the boolean.
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes.
* @param {number} index Start of the data.
* @return {{value: number, nextCursor: number}}
*/
function readSint32Value(bufferDecoder, index) {
const {lowBits, dataStart} = bufferDecoder.getVarint(index);
// Truncate upper bits and convert from zig zag to signd int
return {value: (lowBits >>> 1) ^ -(lowBits & 0x01), nextCursor: dataStart};
}
/**
* Reads a sint32 value from the binary bytes encoded as varint.
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes.
@ -151,26 +105,9 @@ function readSint32Value(bufferDecoder, index) {
* @package
*/
function readSint32(bufferDecoder, start) {
return readSint32Value(bufferDecoder, start).value;
}
/**
* Reads a sint64 value from the binary bytes encoded as varint.
* Also returns the first position after the value.
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes.
* @param {number} index Start of the data.
* @return {{value: !Int64, nextCursor: number}}
* @package
*/
function readSint64Value(bufferDecoder, index) {
const {lowBits, highBits, dataStart} = bufferDecoder.getVarint(index);
const sign = -(lowBits & 0x01);
const decodedLowerBits = ((lowBits >>> 1) | (highBits & 0x01) << 31) ^ sign;
const decodedUpperBits = (highBits >>> 1) ^ sign;
return {
value: Int64.fromBits(decodedLowerBits, decodedUpperBits),
nextCursor: dataStart
};
const bits = bufferDecoder.getUnsignedVarint32At(start);
// Truncate upper bits and convert from zig zag to signd int
return (bits >>> 1) ^ -(bits & 0x01);
}
/**
@ -181,7 +118,11 @@ function readSint64Value(bufferDecoder, index) {
* @package
*/
function readSint64(bufferDecoder, start) {
return readSint64Value(bufferDecoder, start).value;
const {lowBits, highBits} = bufferDecoder.getVarint(start);
const sign = -(lowBits & 0x01);
const decodedLowerBits = ((lowBits >>> 1) | (highBits & 0x01) << 31) ^ sign;
const decodedUpperBits = (highBits >>> 1) ^ sign;
return Int64.fromBits(decodedLowerBits, decodedUpperBits);
}
/**
@ -192,9 +133,8 @@ function readSint64(bufferDecoder, start) {
* @package
*/
function readDelimited(bufferDecoder, start) {
const {lowBits, dataStart} = bufferDecoder.getVarint(start);
const unsignedLength = lowBits >>> 0;
return bufferDecoder.subBufferDecoder(dataStart, unsignedLength);
const unsignedLength = bufferDecoder.getUnsignedVarint32At(start);
return bufferDecoder.subBufferDecoder(bufferDecoder.cursor(), unsignedLength);
}
/**
@ -208,18 +148,6 @@ function readString(bufferDecoder, start) {
return readDelimited(bufferDecoder, start).asString();
}
/**
* Reads a uint32 value from the binary bytes encoded as varint.
* Also returns the first position after the value.
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes.
* @param {number} index Start of the data.
* @return {{value: number, nextCursor: number}}
*/
function readUint32Value(bufferDecoder, index) {
const {lowBits, dataStart} = bufferDecoder.getVarint(index);
return {value: lowBits >>> 0, nextCursor: dataStart};
}
/**
* Reads a uint32 value from the binary bytes encoded as varint.
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes.
@ -228,7 +156,7 @@ function readUint32Value(bufferDecoder, index) {
* @package
*/
function readUint32(bufferDecoder, start) {
return readUint32Value(bufferDecoder, start).value;
return bufferDecoder.getUnsignedVarint32At(start);
}
/**
@ -266,7 +194,7 @@ function readSfixed32(bufferDecoder, start) {
* @package
*/
function readPackedBool(bufferDecoder, start) {
return readPackedVariableLength(bufferDecoder, start, readBoolValue);
return readPacked(bufferDecoder, start, readBool);
}
/**
@ -278,7 +206,7 @@ function readPackedBool(bufferDecoder, start) {
* @package
*/
function readPackedDouble(bufferDecoder, start) {
return readPackedFixed(bufferDecoder, start, 8, readDouble);
return readPacked(bufferDecoder, start, readDouble);
}
/**
@ -290,7 +218,7 @@ function readPackedDouble(bufferDecoder, start) {
* @package
*/
function readPackedFixed32(bufferDecoder, start) {
return readPackedFixed(bufferDecoder, start, 4, readFixed32);
return readPacked(bufferDecoder, start, readFixed32);
}
/**
@ -302,7 +230,7 @@ function readPackedFixed32(bufferDecoder, start) {
* @package
*/
function readPackedFloat(bufferDecoder, start) {
return readPackedFixed(bufferDecoder, start, 4, readFloat);
return readPacked(bufferDecoder, start, readFloat);
}
/**
@ -314,7 +242,7 @@ function readPackedFloat(bufferDecoder, start) {
* @package
*/
function readPackedInt32(bufferDecoder, start) {
return readPackedVariableLength(bufferDecoder, start, readInt32Value);
return readPacked(bufferDecoder, start, readInt32);
}
/**
@ -326,7 +254,7 @@ function readPackedInt32(bufferDecoder, start) {
* @package
*/
function readPackedInt64(bufferDecoder, start) {
return readPackedVariableLength(bufferDecoder, start, readInt64Value);
return readPacked(bufferDecoder, start, readInt64);
}
/**
@ -338,7 +266,7 @@ function readPackedInt64(bufferDecoder, start) {
* @package
*/
function readPackedSfixed32(bufferDecoder, start) {
return readPackedFixed(bufferDecoder, start, 4, readSfixed32);
return readPacked(bufferDecoder, start, readSfixed32);
}
/**
@ -350,7 +278,7 @@ function readPackedSfixed32(bufferDecoder, start) {
* @package
*/
function readPackedSfixed64(bufferDecoder, start) {
return readPackedFixed(bufferDecoder, start, 8, readSfixed64);
return readPacked(bufferDecoder, start, readSfixed64);
}
/**
@ -362,7 +290,7 @@ function readPackedSfixed64(bufferDecoder, start) {
* @package
*/
function readPackedSint32(bufferDecoder, start) {
return readPackedVariableLength(bufferDecoder, start, readSint32Value);
return readPacked(bufferDecoder, start, readSint32);
}
/**
@ -374,7 +302,7 @@ function readPackedSint32(bufferDecoder, start) {
* @package
*/
function readPackedSint64(bufferDecoder, start) {
return readPackedVariableLength(bufferDecoder, start, readSint64Value);
return readPacked(bufferDecoder, start, readSint64);
}
/**
@ -386,51 +314,25 @@ function readPackedSint64(bufferDecoder, start) {
* @package
*/
function readPackedUint32(bufferDecoder, start) {
return readPackedVariableLength(bufferDecoder, start, readUint32Value);
}
/**
* Read packed variable length values.
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes.
* @param {number} start Start of the data.
* @param {function(!BufferDecoder, number):{value:T, nextCursor: number}}
* valueFunction
* @return {!Array<T>}
* @package
* @template T
*/
function readPackedVariableLength(bufferDecoder, start, valueFunction) {
const /** !Array<T> */ result = [];
const {lowBits, dataStart} = bufferDecoder.getVarint(start);
let cursor = dataStart;
const unsignedLength = lowBits >>> 0;
while (cursor < dataStart + unsignedLength) {
const {value, nextCursor} = valueFunction(bufferDecoder, cursor);
cursor = nextCursor;
result.push(value);
}
return result;
return readPacked(bufferDecoder, start, readUint32);
}
/**
* Read a packed fixed values.
* Read packed values.
* @param {!BufferDecoder} bufferDecoder Binary format encoded bytes.
* @param {number} start Start of the data.
* @param {number} size End of the data.
* @param {function(!BufferDecoder, number):T} valueFunction
* @return {!Array<T>}
* @package
* @template T
*/
function readPackedFixed(bufferDecoder, start, size, valueFunction) {
const {lowBits, dataStart} = bufferDecoder.getVarint(start);
const unsignedLength = lowBits >>> 0;
const noOfEntries = unsignedLength / size;
const /** !Array<T> */ result = new Array(noOfEntries);
let cursor = dataStart;
for (let i = 0; i < noOfEntries; i++) {
result[i] = valueFunction(bufferDecoder, cursor);
cursor += size;
function readPacked(bufferDecoder, start, valueFunction) {
const /** !Array<T> */ result = [];
const unsignedLength = bufferDecoder.getUnsignedVarint32At(start);
const dataStart = bufferDecoder.cursor();
while (bufferDecoder.cursor() < dataStart + unsignedLength) {
checkState(bufferDecoder.cursor() > 0);
result.push(valueFunction(bufferDecoder, bufferDecoder.cursor()));
}
return result;
}

@ -12,7 +12,7 @@ goog.setTestOnly();
const BufferDecoder = goog.require('protobuf.binary.BufferDecoder');
const ByteString = goog.require('protobuf.ByteString');
const reader = goog.require('protobuf.binary.reader');
const {CHECK_STATE} = goog.require('protobuf.internal.checks');
const {CHECK_CRITICAL_STATE} = goog.require('protobuf.internal.checks');
const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper');
const {encode} = goog.require('protobuf.binary.textencoding');
const {getBoolPairs} = goog.require('protobuf.binary.boolTestPairs');
@ -45,7 +45,7 @@ const {getUint32Pairs} = goog.require('protobuf.binary.uint32TestPairs');
describe('Read bool does', () => {
for (const pair of getBoolPairs()) {
it(`decode ${pair.name}`, () => {
if (pair.error && CHECK_STATE) {
if (pair.error && CHECK_CRITICAL_STATE) {
expect(() => reader.readBool(pair.bufferDecoder, 0)).toThrow();
} else {
const d = reader.readBool(
@ -120,7 +120,7 @@ describe('readInt32 does', () => {
for (const pair of getInt32Pairs()) {
it(`decode ${pair.name}`, () => {
if (pair.error && CHECK_STATE) {
if (pair.error && CHECK_CRITICAL_STATE) {
expect(() => reader.readInt32(pair.bufferDecoder, 0)).toThrow();
} else {
const d = reader.readInt32(pair.bufferDecoder, 0);
@ -166,7 +166,7 @@ describe('readSint32 does', () => {
for (const pair of getSint32Pairs()) {
it(`decode ${pair.name}`, () => {
if (pair.error && CHECK_STATE) {
if (pair.error && CHECK_CRITICAL_STATE) {
expect(() => reader.readSint32(pair.bufferDecoder, 0)).toThrow();
} else {
const d = reader.readSint32(pair.bufferDecoder, 0);
@ -184,7 +184,7 @@ describe('readInt64 does', () => {
for (const pair of getInt64Pairs()) {
it(`decode ${pair.name}`, () => {
if (pair.error && CHECK_STATE) {
if (pair.error && CHECK_CRITICAL_STATE) {
expect(() => reader.readInt64(pair.bufferDecoder, 0)).toThrow();
} else {
const d = reader.readInt64(pair.bufferDecoder, 0);
@ -202,7 +202,7 @@ describe('readSint64 does', () => {
for (const pair of getSint64Pairs()) {
it(`decode ${pair.name}`, () => {
if (pair.error && CHECK_STATE) {
if (pair.error && CHECK_CRITICAL_STATE) {
expect(() => reader.readSint64(pair.bufferDecoder, 0)).toThrow();
} else {
const d = reader.readSint64(pair.bufferDecoder, 0);
@ -220,7 +220,7 @@ describe('readUint32 does', () => {
for (const pair of getUint32Pairs()) {
it(`decode ${pair.name}`, () => {
if (pair.error && CHECK_STATE) {
if (pair.error && CHECK_CRITICAL_STATE) {
expect(() => reader.readUint32(pair.bufferDecoder, 0)).toThrow();
} else {
const d = reader.readUint32(pair.bufferDecoder, 0);

@ -450,12 +450,13 @@ class Writer {
getLength_(bufferDecoder, start, wireType) {
switch (wireType) {
case WireType.VARINT:
return bufferDecoder.skipVarint(start) - start;
bufferDecoder.skipVarint(start);
return bufferDecoder.cursor() - start;
case WireType.FIXED64:
return 8;
case WireType.DELIMITED:
const {lowBits: dataLength, dataStart} = bufferDecoder.getVarint(start);
return dataLength + dataStart - start;
const dataLength = bufferDecoder.getUnsignedVarint32At(start);
return dataLength + bufferDecoder.cursor() - start;
case WireType.START_GROUP:
return this.getGroupLength_(bufferDecoder, start);
case WireType.FIXED32:
@ -477,12 +478,13 @@ class Writer {
// corresponding stop group
let cursor = start;
while (cursor < bufferDecoder.endIndex()) {
const {lowBits: tag, dataStart} = bufferDecoder.getVarint(cursor);
const tag = bufferDecoder.getUnsignedVarint32At(cursor);
const wireType = /** @type {!WireType} */ (tag & 0x07);
if (wireType === WireType.END_GROUP) {
return dataStart - start;
return bufferDecoder.cursor() - start;
}
cursor = dataStart + this.getLength_(bufferDecoder, dataStart, wireType);
cursor = bufferDecoder.cursor() +
this.getLength_(bufferDecoder, bufferDecoder.cursor(), wireType);
}
throw new Error('No end group found');
}

Loading…
Cancel
Save