Эх сурвалжийг харах

Project import generated by Copybara

PiperOrigin-RevId: 310614231
Daniel Kurka 5 жил өмнө
parent
commit
bb3460d71b

+ 285 - 0
js/experimental/runtime/kernel/message_set.js

@@ -0,0 +1,285 @@
+/*
+##########################################################
+#                                                        #
+#  __          __     _____  _   _ _____ _   _  _____    #
+#  \ \        / /\   |  __ \| \ | |_   _| \ | |/ ____|   #
+#   \ \  /\  / /  \  | |__) |  \| | | | |  \| | |  __    #
+#    \ \/  \/ / /\ \ |  _  /| . ` | | | | . ` | | |_ |   #
+#     \  /\  / ____ \| | \ \| |\  |_| |_| |\  | |__| |   #
+#      \/  \/_/    \_\_|  \_\_| \_|_____|_| \_|\_____|   #
+#                                                        #
+#                                                        #
+##########################################################
+# Do not use this class in your code. This class purely  #
+# exists to make proto code generation easier.           #
+##########################################################
+*/
+goog.module('protobuf.runtime.MessageSet');
+
+const InternalMessage = goog.require('protobuf.binary.InternalMessage');
+const Kernel = goog.require('protobuf.runtime.Kernel');
+
+// These are the tags for the old MessageSet format, which was defined as:
+//   message MessageSet {
+//     repeated group Item = 1 {
+//       required uint32 type_id = 2;
+//       optional bytes message = 3;
+//     }
+//   }
+/** @const {number} */
+const MSET_GROUP_FIELD_NUMBER = 1;
+/** @const {number} */
+const MSET_TYPE_ID_FIELD_NUMBER = 2;
+/** @const {number} */
+const MSET_MESSAGE_FIELD_NUMBER = 3;
+
+/**
+ * @param {!Kernel} kernel
+ * @return {!Map<number, !Item>}
+ */
+function createItemMap(kernel) {
+  const itemMap = new Map();
+  let totalCount = 0;
+  for (const item of kernel.getRepeatedGroupIterable(
+           MSET_GROUP_FIELD_NUMBER, Item.fromKernel)) {
+    itemMap.set(item.getTypeId(), item);
+    totalCount++;
+  }
+
+  // Normalize the entries.
+  if (totalCount > itemMap.size) {
+    writeItemMap(kernel, itemMap);
+  }
+  return itemMap;
+}
+
+/**
+ * @param {!Kernel} kernel
+ * @param {!Map<number, !Item>} itemMap
+ */
+function writeItemMap(kernel, itemMap) {
+  kernel.setRepeatedGroupIterable(MSET_GROUP_FIELD_NUMBER, itemMap.values());
+}
+
+/**
+ * @implements {InternalMessage}
+ * @final
+ */
+class MessageSet {
+  /**
+   * @param {!Kernel} kernel
+   * @return {!MessageSet}
+   */
+  static fromKernel(kernel) {
+    const itemMap = createItemMap(kernel);
+    return new MessageSet(kernel, itemMap);
+  }
+
+  /**
+   * @return {!MessageSet}
+   */
+  static createEmpty() {
+    return MessageSet.fromKernel(Kernel.createEmpty());
+  }
+
+  /**
+   * @param {!Kernel} kernel
+   * @param {!Map<number, !Item>} itemMap
+   * @private
+   */
+  constructor(kernel, itemMap) {
+    /** @const {!Kernel} @private */
+    this.kernel_ = kernel;
+    /** @const {!Map<number, !Item>} @private */
+    this.itemMap_ = itemMap;
+  }
+
+
+
+  // code helpers for code gen
+
+  /**
+   * @param {number} typeId
+   * @param {function(!Kernel):T} instanceCreator
+   * @param {number=} pivot
+   * @return {?T}
+   * @template T
+   */
+  getMessageOrNull(typeId, instanceCreator, pivot) {
+    const item = this.itemMap_.get(typeId);
+    return item ? item.getMessageOrNull(instanceCreator, pivot) : null;
+  }
+
+  /**
+   * @param {number} typeId
+   * @param {function(!Kernel):T} instanceCreator
+   * @param {number=} pivot
+   * @return {T}
+   * @template T
+   */
+  getMessageAttach(typeId, instanceCreator, pivot) {
+    let item = this.itemMap_.get(typeId);
+    if (item) {
+      return item.getMessageAttach(instanceCreator, pivot);
+    }
+    const message = instanceCreator(Kernel.createEmpty());
+    this.setMessage(typeId, message);
+    return message;
+  }
+
+  /**
+   * @param {number} typeId
+   * @param {number=} pivot
+   * @return {?Kernel}
+   */
+  getMessageAccessorOrNull(typeId, pivot) {
+    const item = this.itemMap_.get(typeId);
+    return item ? item.getMessageAccessorOrNull(pivot) : null;
+  }
+
+
+  /**
+   * @param {number} typeId
+   */
+  clearMessage(typeId) {
+    if (this.itemMap_.delete(typeId)) {
+      writeItemMap(this.kernel_, this.itemMap_);
+    }
+  }
+
+  /**
+   * @param {number} typeId
+   * @return {boolean}
+   */
+  hasMessage(typeId) {
+    return this.itemMap_.has(typeId);
+  }
+
+  /**
+   * @param {number} typeId
+   * @param {!InternalMessage} value
+   */
+  setMessage(typeId, value) {
+    const item = this.itemMap_.get(typeId);
+    if (item) {
+      item.setMessage(value);
+    } else {
+      this.itemMap_.set(typeId, Item.create(typeId, value));
+      writeItemMap(this.kernel_, this.itemMap_);
+    }
+  }
+
+  /**
+   * @return {!Kernel}
+   * @override
+   */
+  internalGetKernel() {
+    return this.kernel_;
+  }
+}
+
+/**
+ * @implements {InternalMessage}
+ * @final
+ */
+class Item {
+  /**
+   * @param {number} typeId
+   * @param {!InternalMessage} message
+   * @return {!Item}
+   */
+  static create(typeId, message) {
+    const messageSet = Item.fromKernel(Kernel.createEmpty());
+    messageSet.setTypeId_(typeId);
+    messageSet.setMessage(message);
+    return messageSet;
+  }
+
+
+  /**
+   * @param {!Kernel} kernel
+   * @return {!Item}
+   */
+  static fromKernel(kernel) {
+    return new Item(kernel);
+  }
+
+  /**
+   * @param {!Kernel} kernel
+   * @private
+   */
+  constructor(kernel) {
+    /** @const {!Kernel} @private */
+    this.kernel_ = kernel;
+  }
+
+  /**
+   * @param {function(!Kernel):T} instanceCreator
+   * @param {number=} pivot
+   * @return {T}
+   * @template T
+   */
+  getMessage(instanceCreator, pivot) {
+    return this.kernel_.getMessage(
+        MSET_MESSAGE_FIELD_NUMBER, instanceCreator, pivot);
+  }
+
+  /**
+   * @param {function(!Kernel):T} instanceCreator
+   * @param {number=} pivot
+   * @return {?T}
+   * @template T
+   */
+  getMessageOrNull(instanceCreator, pivot) {
+    return this.kernel_.getMessageOrNull(
+        MSET_MESSAGE_FIELD_NUMBER, instanceCreator, pivot);
+  }
+
+  /**
+   * @param {function(!Kernel):T} instanceCreator
+   * @param {number=} pivot
+   * @return {T}
+   * @template T
+   */
+  getMessageAttach(instanceCreator, pivot) {
+    return this.kernel_.getMessageAttach(
+        MSET_MESSAGE_FIELD_NUMBER, instanceCreator, pivot);
+  }
+
+  /**
+   * @param {number=} pivot
+   * @return {?Kernel}
+   */
+  getMessageAccessorOrNull(pivot) {
+    return this.kernel_.getMessageAccessorOrNull(
+        MSET_MESSAGE_FIELD_NUMBER, pivot);
+  }
+
+  /** @param {!InternalMessage} value */
+  setMessage(value) {
+    this.kernel_.setMessage(MSET_MESSAGE_FIELD_NUMBER, value);
+  }
+
+  /** @return {number} */
+  getTypeId() {
+    return this.kernel_.getUint32WithDefault(MSET_TYPE_ID_FIELD_NUMBER);
+  }
+
+  /**
+   * @param {number} value
+   * @private
+   */
+  setTypeId_(value) {
+    this.kernel_.setUint32(MSET_TYPE_ID_FIELD_NUMBER, value);
+  }
+
+  /**
+   * @return {!Kernel}
+   * @override
+   */
+  internalGetKernel() {
+    return this.kernel_;
+  }
+}
+
+exports = MessageSet;

+ 262 - 0
js/experimental/runtime/kernel/message_set_test.js

@@ -0,0 +1,262 @@
+/**
+ * @fileoverview Tests for message_set.js.
+ */
+goog.module('protobuf.runtime.MessageSetTest');
+
+goog.setTestOnly();
+
+const Kernel = goog.require('protobuf.runtime.Kernel');
+const MessageSet = goog.require('protobuf.runtime.MessageSet');
+const TestMessage = goog.require('protobuf.testing.binary.TestMessage');
+
+/**
+ * @param {...number} bytes
+ * @return {!ArrayBuffer}
+ */
+function createArrayBuffer(...bytes) {
+  return new Uint8Array(bytes).buffer;
+}
+
+describe('MessageSet does', () => {
+  it('returns no messages for empty set', () => {
+    const messageSet = MessageSet.createEmpty();
+    expect(messageSet.getMessageOrNull(12345, TestMessage.instanceCreator))
+        .toBeNull();
+  });
+
+  it('returns no kernel for empty set', () => {
+    const messageSet = MessageSet.createEmpty();
+    expect(messageSet.getMessageAccessorOrNull(12345)).toBeNull();
+  });
+
+  it('returns message that has been set', () => {
+    const messageSet = MessageSet.createEmpty();
+    const message = TestMessage.createEmpty();
+    messageSet.setMessage(12345, message);
+    expect(messageSet.getMessageOrNull(12345, TestMessage.instanceCreator))
+        .toBe(message);
+  });
+
+  it('returns null for cleared message', () => {
+    const messageSet = MessageSet.createEmpty();
+    const message = TestMessage.createEmpty();
+    messageSet.setMessage(12345, message);
+    messageSet.clearMessage(12345);
+    expect(messageSet.getMessageAccessorOrNull(12345)).toBeNull();
+  });
+
+  it('returns false for not present message', () => {
+    const messageSet = MessageSet.createEmpty();
+    expect(messageSet.hasMessage(12345)).toBe(false);
+  });
+
+  it('returns true for present message', () => {
+    const messageSet = MessageSet.createEmpty();
+    const message = TestMessage.createEmpty();
+    messageSet.setMessage(12345, message);
+    expect(messageSet.hasMessage(12345)).toBe(true);
+  });
+
+  it('returns false for cleared message', () => {
+    const messageSet = MessageSet.createEmpty();
+    const message = TestMessage.createEmpty();
+    messageSet.setMessage(12345, message);
+    messageSet.clearMessage(12345);
+    expect(messageSet.hasMessage(12345)).toBe(false);
+  });
+
+  it('returns false for cleared message without it being present', () => {
+    const messageSet = MessageSet.createEmpty();
+    messageSet.clearMessage(12345);
+    expect(messageSet.hasMessage(12345)).toBe(false);
+  });
+
+  const createMessageSet = () => {
+    const messageSet = MessageSet.createEmpty();
+    const message = TestMessage.createEmpty();
+    message.setInt32(1, 2);
+    messageSet.setMessage(12345, message);
+
+
+    const parsedKernel =
+        Kernel.fromArrayBuffer(messageSet.internalGetKernel().serialize());
+    return MessageSet.fromKernel(parsedKernel);
+  };
+
+  it('pass through pivot for getMessageOrNull', () => {
+    const messageSet = createMessageSet();
+    const message =
+        messageSet.getMessageOrNull(12345, TestMessage.instanceCreator, 2);
+    expect(message.internalGetKernel().getPivot()).toBe(2);
+  });
+
+  it('pass through pivot for getMessageAttach', () => {
+    const messageSet = createMessageSet();
+    const message =
+        messageSet.getMessageAttach(12345, TestMessage.instanceCreator, 2);
+    expect(message.internalGetKernel().getPivot()).toBe(2);
+  });
+
+  it('pass through pivot for getMessageAccessorOrNull', () => {
+    const messageSet = createMessageSet();
+    const kernel = messageSet.getMessageAccessorOrNull(12345, 2);
+    expect(kernel.getPivot()).toBe(2);
+  });
+
+  it('pick the last value in the stream', () => {
+    const arrayBuffer = createArrayBuffer(
+        0x52,  // Tag (field:10, length delimited)
+        0x14,  // Length of 20 bytes
+        0x0B,  // Start group fieldnumber 1
+        0x10,  // Tag (field 2, varint)
+        0xB9,  // 12345
+        0x60,  // 12345
+        0x1A,  // Tag (field 3, length delimited)
+        0x03,  // length 3
+        0xA0,  // Tag (fieldnumber 20, varint)
+        0x01,  // Tag (fieldnumber 20, varint)
+        0x1E,  // 30
+        0x0C,  // Stop Group field number 1
+        // second group
+        0x0B,  // Start group fieldnumber 1
+        0x10,  // Tag (field 2, varint)
+        0xB9,  // 12345
+        0x60,  // 12345
+        0x1A,  // Tag (field 3, length delimited)
+        0x03,  // length 3
+        0xA0,  // Tag (fieldnumber 20, varint)
+        0x01,  // Tag (fieldnumber 20, varint)
+        0x01,  // 1
+        0x0C   // Stop Group field number 1
+    );
+
+    const outerMessage = Kernel.fromArrayBuffer(arrayBuffer);
+
+    const messageSet = outerMessage.getMessage(10, MessageSet.fromKernel);
+
+    const message =
+        messageSet.getMessageOrNull(12345, TestMessage.instanceCreator);
+    expect(message.getInt32WithDefault(20)).toBe(1);
+  });
+
+  it('removes duplicates when read', () => {
+    const arrayBuffer = createArrayBuffer(
+        0x52,  // Tag (field:10, length delimited)
+        0x14,  // Length of 20 bytes
+        0x0B,  // Start group fieldnumber 1
+        0x10,  // Tag (field 2, varint)
+        0xB9,  // 12345
+        0x60,  // 12345
+        0x1A,  // Tag (field 3, length delimited)
+        0x03,  // length 3
+        0xA0,  // Tag (fieldnumber 20, varint)
+        0x01,  // Tag (fieldnumber 20, varint)
+        0x1E,  // 30
+        0x0C,  // Stop Group field number 1
+        // second group
+        0x0B,  // Start group fieldnumber 1
+        0x10,  // Tag (field 2, varint)
+        0xB9,  // 12345
+        0x60,  // 12345
+        0x1A,  // Tag (field 3, length delimited)
+        0x03,  // length 3
+        0xA0,  // Tag (fieldnumber 20, varint)
+        0x01,  // Tag (fieldnumber 20, varint)
+        0x01,  // 1
+        0x0C   // Stop Group field number 1
+    );
+
+
+    const outerMessage = Kernel.fromArrayBuffer(arrayBuffer);
+    outerMessage.getMessageAttach(10, MessageSet.fromKernel);
+
+    expect(outerMessage.serialize())
+        .toEqual(createArrayBuffer(
+            0x52,  // Tag (field:10, length delimited)
+            0x0A,  // Length of 10 bytes
+            0x0B,  // Start group fieldnumber 1
+            0x10,  // Tag (field 2, varint)
+            0xB9,  // 12345
+            0x60,  // 12345
+            0x1A,  // Tag (field 3, length delimited)
+            0x03,  // length 3
+            0xA0,  // Tag (fieldnumber 20, varint)
+            0x01,  // Tag (fieldnumber 20, varint)
+            0x01,  // 1
+            0x0C   // Stop Group field number 1
+            ));
+  });
+
+  it('allow for large typeIds', () => {
+    const messageSet = MessageSet.createEmpty();
+    const message = TestMessage.createEmpty();
+    messageSet.setMessage(0xFFFFFFFE >>> 0, message);
+    expect(messageSet.hasMessage(0xFFFFFFFE >>> 0)).toBe(true);
+  });
+});
+
+describe('Optional MessageSet does', () => {
+  // message Bar {
+  //  optional MessageSet mset = 10;
+  //}
+  //
+  // message Foo {
+  //  extend proto2.bridge.MessageSet {
+  //    optional Foo message_set_extension = 12345;
+  //  }
+  //  optional int32 f20 = 20;
+  //}
+
+  it('encode as a field', () => {
+    const fooMessage = Kernel.createEmpty();
+    fooMessage.setInt32(20, 30);
+
+    const messageSet = MessageSet.createEmpty();
+    messageSet.setMessage(12345, TestMessage.instanceCreator(fooMessage));
+
+    const barMessage = Kernel.createEmpty();
+    barMessage.setMessage(10, messageSet);
+
+    expect(barMessage.serialize())
+        .toEqual(createArrayBuffer(
+            0x52,  // Tag (field:10, length delimited)
+            0x0A,  // Length of 10 bytes
+            0x0B,  // Start group fieldnumber 1
+            0x10,  // Tag (field 2, varint)
+            0xB9,  // 12345
+            0x60,  // 12345
+            0x1A,  // Tag (field 3, length delimited)
+            0x03,  // length 3
+            0xA0,  // Tag (fieldnumber 20, varint)
+            0x01,  // Tag (fieldnumber 20, varint)
+            0x1E,  // 30
+            0x0C   // Stop Group field number 1
+            ));
+  });
+
+  it('deserializes', () => {
+    const fooMessage = Kernel.createEmpty();
+    fooMessage.setInt32(20, 30);
+
+    const messageSet = MessageSet.createEmpty();
+    messageSet.setMessage(12345, TestMessage.instanceCreator(fooMessage));
+
+
+    const barMessage = Kernel.createEmpty();
+    barMessage.setMessage(10, messageSet);
+
+    const arrayBuffer = barMessage.serialize();
+
+    const barMessageParsed = Kernel.fromArrayBuffer(arrayBuffer);
+    expect(barMessageParsed.hasFieldNumber(10)).toBe(true);
+
+    const messageSetParsed =
+        barMessageParsed.getMessage(10, MessageSet.fromKernel);
+
+    const fooMessageParsed =
+        messageSetParsed.getMessageOrNull(12345, TestMessage.instanceCreator)
+            .internalGetKernel();
+
+    expect(fooMessageParsed.getInt32WithDefault(20)).toBe(30);
+  });
+});

+ 8 - 2
js/experimental/runtime/testing/binary/test_message.js

@@ -13,6 +13,13 @@ const Kernel = goog.require('protobuf.runtime.Kernel');
  * @implements {InternalMessage}
  */
 class TestMessage {
+  /**
+   * @return {!TestMessage}
+   */
+  static createEmpty() {
+    return TestMessage.instanceCreator(Kernel.createEmpty());
+  }
+
   /**
    * @param {!Kernel} kernel
    * @return {!TestMessage}
@@ -31,7 +38,6 @@ class TestMessage {
 
   /**
    * @override
-   * @package
    * @return {!Kernel}
    */
   internalGetKernel() {
@@ -1760,4 +1766,4 @@ class TestMessage {
   }
 }
 
-exports = TestMessage;
+exports = TestMessage;