From becf5afd34a25dc6763421045ffa1ac0060f6f0f Mon Sep 17 00:00:00 2001 From: rafikamal Date: Fri, 7 Feb 2020 15:47:43 -0800 Subject: [PATCH] Project import generated by Copybara PiperOrigin-RevId: 293911829 --- .../runtime/kernel/buffer_decoder.js | 146 +++++++++++--- .../runtime/kernel/buffer_decoder_test.js | 158 +++++++++++++++- js/experimental/runtime/kernel/indexer.js | 62 ++---- js/experimental/runtime/kernel/reader.js | 178 ++++-------------- js/experimental/runtime/kernel/reader_test.js | 14 +- js/experimental/runtime/kernel/writer.js | 14 +- 6 files changed, 338 insertions(+), 234 deletions(-) diff --git a/js/experimental/runtime/kernel/buffer_decoder.js b/js/experimental/runtime/kernel/buffer_decoder.js index 614659a1e6..2b4157e952 100644 --- a/js/experimental/runtime/kernel/buffer_decoder.js +++ b/js/experimental/runtime/kernel/buffer_decoder.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); } /** diff --git a/js/experimental/runtime/kernel/buffer_decoder_test.js b/js/experimental/runtime/kernel/buffer_decoder_test.js index aed045a8e5..7c549f8891 100644 --- a/js/experimental/runtime/kernel/buffer_decoder_test.js +++ b/js/experimental/runtime/kernel/buffer_decoder_test.js @@ -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); }); }); diff --git a/js/experimental/runtime/kernel/indexer.js b/js/experimental/runtime/kernel/indexer.js index c247c2faa6..e2819948b7 100644 --- a/js/experimental/runtime/kernel/indexer.js +++ b/js/experimental/runtime/kernel/indexer.js @@ -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} */ 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, }; diff --git a/js/experimental/runtime/kernel/reader.js b/js/experimental/runtime/kernel/reader.js index fec79cfad5..2b80e15bff 100644 --- a/js/experimental/runtime/kernel/reader.js +++ b/js/experimental/runtime/kernel/reader.js @@ -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} - * @package - * @template T - */ -function readPackedVariableLength(bufferDecoder, start, valueFunction) { - const /** !Array */ 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} * @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 */ 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 */ 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; } diff --git a/js/experimental/runtime/kernel/reader_test.js b/js/experimental/runtime/kernel/reader_test.js index a0cbcae67f..dd0208687b 100644 --- a/js/experimental/runtime/kernel/reader_test.js +++ b/js/experimental/runtime/kernel/reader_test.js @@ -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); diff --git a/js/experimental/runtime/kernel/writer.js b/js/experimental/runtime/kernel/writer.js index 1fc741869f..a0178d25a9 100644 --- a/js/experimental/runtime/kernel/writer.js +++ b/js/experimental/runtime/kernel/writer.js @@ -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'); }