Project import generated by Copybara

PiperOrigin-RevId: 310614231
pull/7478/head
Daniel Kurka 5 years ago committed by Copybara-Service
parent 12236c6977
commit bb3460d71b
  1. 285
      js/experimental/runtime/kernel/message_set.js
  2. 262
      js/experimental/runtime/kernel/message_set_test.js
  3. 10
      js/experimental/runtime/testing/binary/test_message.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;

@ -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);
});
});

@ -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;

Loading…
Cancel
Save