From da1c46401b27a96d2301bba240c5603af8bb9cb8 Mon Sep 17 00:00:00 2001 From: Rafi Kamal Date: Mon, 16 Mar 2020 23:09:16 -0700 Subject: [PATCH] Project import generated by Copybara PiperOrigin-RevId: 301311140 --- .../runtime/kernel/binary_storage.js | 130 ++++++++++++++++++ ...storage_test.js => binary_storage_test.js} | 30 ++-- js/experimental/runtime/kernel/indexer.js | 8 +- .../runtime/kernel/indexer_test.js | 6 +- js/experimental/runtime/kernel/kernel.js | 5 +- js/experimental/runtime/kernel/storage.js | 112 ++++----------- 6 files changed, 178 insertions(+), 113 deletions(-) create mode 100644 js/experimental/runtime/kernel/binary_storage.js rename js/experimental/runtime/kernel/{storage_test.js => binary_storage_test.js} (85%) diff --git a/js/experimental/runtime/kernel/binary_storage.js b/js/experimental/runtime/kernel/binary_storage.js new file mode 100644 index 0000000000..4cbde30a69 --- /dev/null +++ b/js/experimental/runtime/kernel/binary_storage.js @@ -0,0 +1,130 @@ +goog.module('protobuf.runtime.BinaryStorage'); + +const Storage = goog.require('protobuf.runtime.Storage'); +const {checkDefAndNotNull} = goog.require('protobuf.internal.checks'); + +/** + * Class storing all the fields of a binary protobuf message. + * + * @package + * @template FieldType + * @implements {Storage} + */ +class BinaryStorage { + /** + * @param {number=} pivot + */ + constructor(pivot = Storage.DEFAULT_PIVOT) { + /** + * Fields having a field number no greater than the pivot value are stored + * into an array for fast access. A field with field number X is stored into + * the array position X - 1. + * + * @private @const {!Array} + */ + this.array_ = new Array(pivot); + + /** + * Fields having a field number higher than the pivot value are stored into + * the map. We create the map only when it's needed, since even an empty map + * takes up a significant amount of memory. + * + * @private {?Map} + */ + this.map_ = null; + } + + /** + * Fields having a field number no greater than the pivot value are stored + * into an array for fast access. A field with field number X is stored into + * the array position X - 1. + * @return {number} + * @override + */ + getPivot() { + return this.array_.length; + } + + /** + * Sets a field in the specified field number. + * + * @param {number} fieldNumber + * @param {!FieldType} field + * @override + */ + set(fieldNumber, field) { + if (fieldNumber <= this.getPivot()) { + this.array_[fieldNumber - 1] = field; + } else { + if (this.map_) { + this.map_.set(fieldNumber, field); + } else { + this.map_ = new Map([[fieldNumber, field]]); + } + } + } + + /** + * Returns a field at the specified field number. + * + * @param {number} fieldNumber + * @return {!FieldType|undefined} + * @override + */ + get(fieldNumber) { + if (fieldNumber <= this.getPivot()) { + return this.array_[fieldNumber - 1]; + } else { + return this.map_ ? this.map_.get(fieldNumber) : undefined; + } + } + + /** + * Deletes a field from the specified field number. + * + * @param {number} fieldNumber + * @override + */ + delete(fieldNumber) { + if (fieldNumber <= this.getPivot()) { + delete this.array_[fieldNumber - 1]; + } else { + if (this.map_) { + this.map_.delete(fieldNumber); + } + } + } + + /** + * Executes the provided function once for each field. + * + * @param {function(!FieldType, number): void} callback + * @override + */ + forEach(callback) { + this.array_.forEach((field, fieldNumber) => { + if (field) { + callback(checkDefAndNotNull(field), fieldNumber + 1); + } + }); + if (this.map_) { + this.map_.forEach(callback); + } + } + + /** + * Creates a shallow copy of the storage. + * + * @return {!BinaryStorage} + * @override + */ + shallowCopy() { + const copy = new BinaryStorage(this.getPivot()); + this.forEach( + (field, fieldNumber) => + void copy.set(fieldNumber, field.shallowCopy())); + return copy; + } +} + +exports = BinaryStorage; diff --git a/js/experimental/runtime/kernel/storage_test.js b/js/experimental/runtime/kernel/binary_storage_test.js similarity index 85% rename from js/experimental/runtime/kernel/storage_test.js rename to js/experimental/runtime/kernel/binary_storage_test.js index e4958d3c89..2686f04e47 100644 --- a/js/experimental/runtime/kernel/storage_test.js +++ b/js/experimental/runtime/kernel/binary_storage_test.js @@ -1,11 +1,11 @@ /** * @fileoverview Tests for storage.js. */ -goog.module('protobuf.binary.StorageTest'); +goog.module('protobuf.runtime.BinaryStorageTest'); goog.setTestOnly(); -const Storage = goog.require('protobuf.binary.Storage'); +const BinaryStorage = goog.require('protobuf.runtime.BinaryStorage'); const {Field} = goog.require('protobuf.binary.field'); /** @@ -25,7 +25,7 @@ const /** !Field */ field4 = /** * Returns the number of fields stored. * - * @param {!Storage} storage + * @param {!BinaryStorage} storage * @return {number} */ function getStorageSize(storage) { @@ -34,9 +34,9 @@ function getStorageSize(storage) { return size; } -describe('Storage', () => { +describe('BinaryStorage', () => { it('sets and gets a field not greater than the pivot', () => { - const storage = new Storage(DEFAULT_PIVOT); + const storage = new BinaryStorage(DEFAULT_PIVOT); storage.set(1, field1); storage.set(DEFAULT_PIVOT, field2); @@ -47,7 +47,7 @@ describe('Storage', () => { }); it('sets and gets a field greater than the pivot', () => { - const storage = new Storage(DEFAULT_PIVOT); + const storage = new BinaryStorage(DEFAULT_PIVOT); storage.set(DEFAULT_PIVOT + 1, field1); storage.set(100000, field2); @@ -57,7 +57,7 @@ describe('Storage', () => { }); it('sets and gets a field when pivot is zero', () => { - const storage = new Storage(0); + const storage = new BinaryStorage(0); storage.set(0, field1); storage.set(100000, field2); @@ -68,7 +68,7 @@ describe('Storage', () => { }); it('sets and gets a field when pivot is undefined', () => { - const storage = new Storage(); + const storage = new BinaryStorage(); storage.set(0, field1); storage.set(DEFAULT_PIVOT, field2); @@ -81,7 +81,7 @@ describe('Storage', () => { }); it('returns undefined for nonexistent fields', () => { - const storage = new Storage(DEFAULT_PIVOT); + const storage = new BinaryStorage(DEFAULT_PIVOT); expect(storage.get(1)).toBeUndefined(); expect(storage.get(DEFAULT_PIVOT)).toBeUndefined(); @@ -91,7 +91,7 @@ describe('Storage', () => { it('returns undefined for nonexistent fields after map initialization', () => { - const storage = new Storage(DEFAULT_PIVOT); + const storage = new BinaryStorage(DEFAULT_PIVOT); storage.set(100001, field1); expect(storage.get(1)).toBeUndefined(); @@ -101,7 +101,7 @@ describe('Storage', () => { }); it('deletes a field in delete() when values are only in array', () => { - const storage = new Storage(DEFAULT_PIVOT); + const storage = new BinaryStorage(DEFAULT_PIVOT); storage.set(1, field1); storage.delete(1); @@ -111,7 +111,7 @@ describe('Storage', () => { it('deletes a field in delete() when values are both in array and map', () => { - const storage = new Storage(DEFAULT_PIVOT); + const storage = new BinaryStorage(DEFAULT_PIVOT); storage.set(DEFAULT_PIVOT, field2); storage.set(DEFAULT_PIVOT + 1, field3); @@ -123,7 +123,7 @@ describe('Storage', () => { }); it('deletes a field in delete() when values are only in map', () => { - const storage = new Storage(DEFAULT_PIVOT); + const storage = new BinaryStorage(DEFAULT_PIVOT); storage.set(100000, field4); storage.delete(100000); @@ -132,7 +132,7 @@ describe('Storage', () => { }); it('loops over all the elements in forEach()', () => { - const storage = new Storage(DEFAULT_PIVOT); + const storage = new BinaryStorage(DEFAULT_PIVOT); storage.set(1, field1); storage.set(DEFAULT_PIVOT, field2); storage.set(DEFAULT_PIVOT + 1, field3); @@ -150,7 +150,7 @@ describe('Storage', () => { }); it('creates a shallow copy of the storage in shallowCopy()', () => { - const storage = new Storage(DEFAULT_PIVOT); + const storage = new BinaryStorage(DEFAULT_PIVOT); storage.set(1, field1); storage.set(100000, field2); diff --git a/js/experimental/runtime/kernel/indexer.js b/js/experimental/runtime/kernel/indexer.js index ae418227e7..d4dea13515 100644 --- a/js/experimental/runtime/kernel/indexer.js +++ b/js/experimental/runtime/kernel/indexer.js @@ -4,15 +4,15 @@ */ goog.module('protobuf.binary.indexer'); +const BinaryStorage = goog.require('protobuf.runtime.BinaryStorage'); 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 {checkCriticalElementIndex, checkCriticalState} = goog.require('protobuf.internal.checks'); /** * Appends a new entry in the index array for the given field number. - * @param {!Storage} storage + * @param {!BinaryStorage} storage * @param {number} fieldNumber * @param {!WireType} wireType * @param {number} startIndex @@ -50,13 +50,13 @@ function tagToFieldNumber(tag) { * Creates an index of field locations in a given binary protobuf. * @param {!BufferDecoder} bufferDecoder * @param {number|undefined} pivot - * @return {!Storage} + * @return {!BinaryStorage} * @package */ function buildIndex(bufferDecoder, pivot) { bufferDecoder.setCursor(bufferDecoder.startIndex()); - const storage = new Storage(pivot); + const storage = new BinaryStorage(pivot); while (bufferDecoder.hasNext()) { const tag = bufferDecoder.getUnsignedVarint32(); const wireType = tagToWireType(tag); diff --git a/js/experimental/runtime/kernel/indexer_test.js b/js/experimental/runtime/kernel/indexer_test.js index 1c95a07b52..101e44283f 100644 --- a/js/experimental/runtime/kernel/indexer_test.js +++ b/js/experimental/runtime/kernel/indexer_test.js @@ -10,8 +10,8 @@ goog.setTestOnly(); // in this file have to know which checking level is enabled to make correct // assertions. // Test are run in all checking levels. +const BinaryStorage = goog.require('protobuf.runtime.BinaryStorage'); const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); -const Storage = goog.require('protobuf.binary.Storage'); const WireType = goog.require('protobuf.binary.WireType'); const {CHECK_CRITICAL_STATE} = goog.require('protobuf.internal.checks'); const {Field, IndexEntry} = goog.require('protobuf.binary.field'); @@ -21,7 +21,7 @@ const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper' /** * Returns the number of fields stored. * - * @param {!Storage} storage + * @param {!BinaryStorage} storage * @return {number} */ function getStorageSize(storage) { @@ -37,7 +37,7 @@ const PIVOT = 1; /** * Asserts a single IndexEntry at a given field number. - * @param {!Storage} storage + * @param {!BinaryStorage} storage * @param {number} fieldNumber * @param {...!IndexEntry} expectedEntries */ diff --git a/js/experimental/runtime/kernel/kernel.js b/js/experimental/runtime/kernel/kernel.js index 47ffc8cf2e..2a2b2d8c48 100644 --- a/js/experimental/runtime/kernel/kernel.js +++ b/js/experimental/runtime/kernel/kernel.js @@ -14,11 +14,12 @@ */ goog.module('protobuf.runtime.Kernel'); +const BinaryStorage = goog.require('protobuf.runtime.BinaryStorage'); const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); const ByteString = goog.require('protobuf.ByteString'); const Int64 = goog.require('protobuf.Int64'); const InternalMessage = goog.require('protobuf.binary.InternalMessage'); -const Storage = goog.require('protobuf.binary.Storage'); +const Storage = goog.require('protobuf.runtime.Storage'); const WireType = goog.require('protobuf.binary.WireType'); const Writer = goog.require('protobuf.binary.Writer'); const reader = goog.require('protobuf.binary.reader'); @@ -278,7 +279,7 @@ class Kernel { * @return {!Kernel} */ static createEmpty(pivot = undefined) { - return new Kernel(/* bufferDecoder= */ null, new Storage(pivot)); + return new Kernel(/* bufferDecoder= */ null, new BinaryStorage(pivot)); } /** diff --git a/js/experimental/runtime/kernel/storage.js b/js/experimental/runtime/kernel/storage.js index 56dc85fbbc..a3e1e4f004 100644 --- a/js/experimental/runtime/kernel/storage.js +++ b/js/experimental/runtime/kernel/storage.js @@ -1,57 +1,19 @@ -goog.module('protobuf.binary.Storage'); - -const {checkDefAndNotNull} = goog.require('protobuf.internal.checks'); - -/** - * 85% of the proto fields have a field number <= 24: - * https://plx.corp.google.com/scripts2/script_5d._f02af6_0000_23b1_a15f_001a1139dd02 - * - * @type {number} - */ -// LINT.IfChange -const DEFAULT_PIVOT = 24; -// LINT.ThenChange(//depot/google3/third_party/protobuf/javascript/runtime/kernel/storage_test.js, -// //depot/google3/net/proto2/contrib/js_proto/internal/kernel_message_generator.cc) +goog.module('protobuf.runtime.Storage'); /** - * Class storing all the fields of a protobuf message. + * Interface for getting and storing fields of a protobuf message. * + * @interface * @package * @template FieldType */ class Storage { /** - * @param {number=} pivot - */ - constructor(pivot = DEFAULT_PIVOT) { - /** - * Fields having a field number no greater than the pivot value are stored - * into an array for fast access. A field with field number X is stored into - * the array position X - 1. - * - * @private @const {!Array} - */ - this.array_ = new Array(pivot); - - /** - * Fields having a field number higher than the pivot value are stored into - * the map. We create the map only when it's needed, since even an empty map - * takes up a significant amount of memory. - * - * @private {?Map} - */ - this.map_ = null; - } - - /** - * Fields having a field number no greater than the pivot value are stored - * into an array for fast access. A field with field number X is stored into - * the array position X - 1. + * Returns the pivot value. + * * @return {number} */ - getPivot() { - return this.array_.length; - } + getPivot() {} /** * Sets a field in the specified field number. @@ -59,17 +21,7 @@ class Storage { * @param {number} fieldNumber * @param {!FieldType} field */ - set(fieldNumber, field) { - if (fieldNumber <= this.getPivot()) { - this.array_[fieldNumber - 1] = field; - } else { - if (this.map_) { - this.map_.set(fieldNumber, field); - } else { - this.map_ = new Map([[fieldNumber, field]]); - } - } - } + set(fieldNumber, field) {} /** * Returns a field at the specified field number. @@ -77,57 +29,39 @@ class Storage { * @param {number} fieldNumber * @return {!FieldType|undefined} */ - get(fieldNumber) { - if (fieldNumber <= this.getPivot()) { - return this.array_[fieldNumber - 1]; - } else { - return this.map_ ? this.map_.get(fieldNumber) : undefined; - } - } + get(fieldNumber) {} /** * Deletes a field from the specified field number. * * @param {number} fieldNumber */ - delete(fieldNumber) { - if (fieldNumber <= this.getPivot()) { - delete this.array_[fieldNumber - 1]; - } else { - if (this.map_) { - this.map_.delete(fieldNumber); - } - } - } + delete(fieldNumber) {} /** - * Executes the provided function once for each array element. + * Executes the provided function once for each field. * * @param {function(!FieldType, number): void} callback */ - forEach(callback) { - this.array_.forEach((field, fieldNumber) => { - if (field) { - callback(checkDefAndNotNull(field), fieldNumber + 1); - } - }); - if (this.map_) { - this.map_.forEach(callback); - } - } + forEach(callback) {} /** * Creates a shallow copy of the storage. * * @return {!Storage} */ - shallowCopy() { - const copy = new Storage(this.getPivot()); - this.forEach( - (field, fieldNumber) => - void copy.set(fieldNumber, field.shallowCopy())); - return copy; - } + shallowCopy() {} } +/** + * 85% of the proto fields have a field number <= 24: + * https://plx.corp.google.com/scripts2/script_5d._f02af6_0000_23b1_a15f_001a1139dd02 + * + * @type {number} + */ +// LINT.IfChange +Storage.DEFAULT_PIVOT = 24; +// LINT.ThenChange(//depot/google3/third_party/protobuf/javascript/runtime/kernel/binary_storage_test.js, +// //depot/google3/net/proto2/contrib/js_proto/internal/kernel_message_generator.cc) + exports = Storage;