Commit 25a28580 authored by Jon Skeet's avatar Jon Skeet

Support packed primitive types

parent 0ca3fecf
No preview for this file type
......@@ -252,6 +252,8 @@ message FileOptions {
}
optional OptimizeMode optimize_for = 9 [default=CODE_SIZE];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
......@@ -298,6 +300,11 @@ message FieldOptions {
STRING_PIECE = 2;
}
// The packed option can be enabled for repeated primitive fields to enable
// a more efficient representation on the wire. Rather than repeatedly
// writing the tag and type for each element, the entire array is encoded as
// a single length-delimited blob.
optional bool packed = 2;
// EXPERIMENTAL. DO NOT USE.
// For "map" fields, the name of the field in the enclosed type that
......
......@@ -458,6 +458,44 @@ message TestExtremeDefaultValues {
optional string utf8_string = 6 [default = "\341\210\264"];
}
message TestPackedTypes {
repeated int32 packed_int32 = 90 [packed = true];
repeated int64 packed_int64 = 91 [packed = true];
repeated uint32 packed_uint32 = 92 [packed = true];
repeated uint64 packed_uint64 = 93 [packed = true];
repeated sint32 packed_sint32 = 94 [packed = true];
repeated sint64 packed_sint64 = 95 [packed = true];
repeated fixed32 packed_fixed32 = 96 [packed = true];
repeated fixed64 packed_fixed64 = 97 [packed = true];
repeated sfixed32 packed_sfixed32 = 98 [packed = true];
repeated sfixed64 packed_sfixed64 = 99 [packed = true];
repeated float packed_float = 100 [packed = true];
repeated double packed_double = 101 [packed = true];
repeated bool packed_bool = 102 [packed = true];
repeated ForeignEnum packed_enum = 103 [packed = true];
}
message TestPackedExtensions {
extensions 1 to max;
}
extend TestPackedExtensions {
repeated int32 packed_int32_extension = 90 [packed = true];
repeated int64 packed_int64_extension = 91 [packed = true];
repeated uint32 packed_uint32_extension = 92 [packed = true];
repeated uint64 packed_uint64_extension = 93 [packed = true];
repeated sint32 packed_sint32_extension = 94 [packed = true];
repeated sint64 packed_sint64_extension = 95 [packed = true];
repeated fixed32 packed_fixed32_extension = 96 [packed = true];
repeated fixed64 packed_fixed64_extension = 97 [packed = true];
repeated sfixed32 packed_sfixed32_extension = 98 [packed = true];
repeated sfixed64 packed_sfixed64_extension = 99 [packed = true];
repeated float packed_float_extension = 100 [packed = true];
repeated double packed_double_extension = 101 [packed = true];
repeated bool packed_bool_extension = 102 [packed = true];
repeated ForeignEnum packed_enum_extension = 103 [packed = true];
}
// Test that RPC services work.
message FooRequest {}
message FooResponse {}
......
......@@ -523,8 +523,10 @@ namespace Google.ProtocolBuffers.ProtoBench {
if (HasField4) {
output.WriteString(4, Field4);
}
foreach (ulong element in Field5List) {
output.WriteFixed64(5, element);
if (field5_.Count > 0) {
foreach (ulong element in field5_) {
output.WriteFixed64(5, element);
}
}
if (HasField6) {
output.WriteInt32(6, Field6);
......@@ -677,8 +679,11 @@ namespace Google.ProtocolBuffers.ProtoBench {
if (HasField4) {
size += pb::CodedOutputStream.ComputeStringSize(4, Field4);
}
foreach (ulong element in Field5List) {
size += pb::CodedOutputStream.ComputeFixed64Size(5, element);
{
int dataSize = 0;
dataSize = 8 * field5_.Count;
size += dataSize;
size += 1 * field5_.Count;
}
if (HasField59) {
size += pb::CodedOutputStream.ComputeBoolSize(59, Field59);
......@@ -3116,8 +3121,10 @@ namespace Google.ProtocolBuffers.ProtoBench {
if (HasField13) {
output.WriteString(13, Field13);
}
foreach (string element in Field14List) {
output.WriteString(14, element);
if (field14_.Count > 0) {
foreach (string element in field14_) {
output.WriteString(14, element);
}
}
if (HasField15) {
output.WriteUInt64(15, Field15);
......@@ -3128,8 +3135,10 @@ namespace Google.ProtocolBuffers.ProtoBench {
if (HasField20) {
output.WriteInt32(20, Field20);
}
foreach (string element in Field22List) {
output.WriteString(22, element);
if (field22_.Count > 0) {
foreach (string element in field22_) {
output.WriteString(22, element);
}
}
if (HasField24) {
output.WriteString(24, Field24);
......@@ -3149,8 +3158,10 @@ namespace Google.ProtocolBuffers.ProtoBench {
if (HasField31) {
output.WriteMessage(31, Field31);
}
foreach (int element in Field73List) {
output.WriteInt32(73, element);
if (field73_.Count > 0) {
foreach (int element in field73_) {
output.WriteInt32(73, element);
}
}
UnknownFields.WriteTo(output);
}
......@@ -3174,8 +3185,13 @@ namespace Google.ProtocolBuffers.ProtoBench {
if (HasField13) {
size += pb::CodedOutputStream.ComputeStringSize(13, Field13);
}
foreach (string element in Field14List) {
size += pb::CodedOutputStream.ComputeStringSize(14, element);
{
int dataSize = 0;
foreach (string element in Field14List) {
dataSize += pb::CodedOutputStream.ComputeStringSizeNoTag(element);
}
size += dataSize;
size += 1 * field14_.Count;
}
if (HasField15) {
size += pb::CodedOutputStream.ComputeUInt64Size(15, Field15);
......@@ -3195,11 +3211,21 @@ namespace Google.ProtocolBuffers.ProtoBench {
if (HasField16) {
size += pb::CodedOutputStream.ComputeStringSize(16, Field16);
}
foreach (string element in Field22List) {
size += pb::CodedOutputStream.ComputeStringSize(22, element);
{
int dataSize = 0;
foreach (string element in Field22List) {
dataSize += pb::CodedOutputStream.ComputeStringSizeNoTag(element);
}
size += dataSize;
size += 2 * field22_.Count;
}
foreach (int element in Field73List) {
size += pb::CodedOutputStream.ComputeInt32Size(73, element);
{
int dataSize = 0;
foreach (int element in Field73List) {
dataSize += pb::CodedOutputStream.ComputeInt32SizeNoTag(element);
}
size += dataSize;
size += 2 * field73_.Count;
}
if (HasField20) {
size += pb::CodedOutputStream.ComputeInt32Size(20, Field20);
......@@ -4110,17 +4136,23 @@ namespace Google.ProtocolBuffers.ProtoBench {
if (HasField109) {
output.WriteInt32(109, Field109);
}
foreach (string element in Field127List) {
output.WriteString(127, element);
if (field127_.Count > 0) {
foreach (string element in field127_) {
output.WriteString(127, element);
}
}
foreach (string element in Field128List) {
output.WriteString(128, element);
if (field128_.Count > 0) {
foreach (string element in field128_) {
output.WriteString(128, element);
}
}
if (HasField129) {
output.WriteInt32(129, Field129);
}
foreach (long element in Field130List) {
output.WriteInt64(130, element);
if (field130_.Count > 0) {
foreach (long element in field130_) {
output.WriteInt64(130, element);
}
}
if (HasField131) {
output.WriteInt64(131, Field131);
......@@ -4240,20 +4272,35 @@ namespace Google.ProtocolBuffers.ProtoBench {
foreach (global::Google.ProtocolBuffers.ProtoBench.SpeedMessage3.Types.Group1 element in Group1List) {
size += pb::CodedOutputStream.ComputeGroupSize(10, element);
}
foreach (string element in Field128List) {
size += pb::CodedOutputStream.ComputeStringSize(128, element);
{
int dataSize = 0;
foreach (string element in Field128List) {
dataSize += pb::CodedOutputStream.ComputeStringSizeNoTag(element);
}
size += dataSize;
size += 2 * field128_.Count;
}
if (HasField131) {
size += pb::CodedOutputStream.ComputeInt64Size(131, Field131);
}
foreach (string element in Field127List) {
size += pb::CodedOutputStream.ComputeStringSize(127, element);
{
int dataSize = 0;
foreach (string element in Field127List) {
dataSize += pb::CodedOutputStream.ComputeStringSizeNoTag(element);
}
size += dataSize;
size += 2 * field127_.Count;
}
if (HasField129) {
size += pb::CodedOutputStream.ComputeInt32Size(129, Field129);
}
foreach (long element in Field130List) {
size += pb::CodedOutputStream.ComputeInt64Size(130, element);
{
int dataSize = 0;
foreach (long element in Field130List) {
dataSize += pb::CodedOutputStream.ComputeInt64SizeNoTag(element);
}
size += dataSize;
size += 2 * field130_.Count;
}
if (HasField205) {
size += pb::CodedOutputStream.ComputeBoolSize(205, Field205);
......
......@@ -97,6 +97,45 @@ namespace Google.ProtocolBuffers.ProtoGen {
}
}
/// <summary>
/// For encodings with fixed sizes, returns that size in bytes. Otherwise
/// returns -1. TODO(jonskeet): Make this less ugly.
/// </summary>
protected int FixedSize {
get {
switch (Descriptor.FieldType) {
case FieldType.UInt32:
case FieldType.UInt64:
case FieldType.Int32:
case FieldType.Int64:
case FieldType.SInt32:
case FieldType.SInt64:
case FieldType.Enum:
case FieldType.Bytes:
case FieldType.String:
case FieldType.Message:
case FieldType.Group:
return -1;
case FieldType.Float:
return WireFormat.FloatSize;
case FieldType.SFixed32:
return WireFormat.SFixed32Size;
case FieldType.Fixed32:
return WireFormat.Fixed32Size;
case FieldType.Double:
return WireFormat.DoubleSize;
case FieldType.SFixed64:
return WireFormat.SFixed64Size;
case FieldType.Fixed64:
return WireFormat.Fixed64Size;
case FieldType.Bool:
return WireFormat.BoolSize;
default:
throw new InvalidOperationException("Invalid field descriptor type");
}
}
}
protected bool IsNullableType {
get {
switch (Descriptor.FieldType) {
......
......@@ -391,7 +391,7 @@ namespace Google.ProtocolBuffers.ProtoGen {
writer.WriteLine(" break;");
writer.WriteLine("}");
foreach (FieldDescriptor field in sortedFields) {
uint tag = WireFormat.MakeTag(field.FieldNumber, WireFormat.GetWireType(field.FieldType));
uint tag = WireFormat.MakeTag(field);
writer.WriteLine("case {0}: {{", tag);
writer.Indent();
SourceGenerators.CreateFieldGenerator(field).GenerateParsingCode(writer);
......
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.DescriptorProtos;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
......@@ -11,6 +9,9 @@ namespace Google.ProtocolBuffers.ProtoGen {
}
public void GenerateMembers(TextGenerator writer) {
if (Descriptor.IsPacked && Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
writer.WriteLine("private int {0}MemoizedSerializedSize;", Name);
}
writer.WriteLine("private pbc::PopsicleList<{0}> {1}_ = new pbc::PopsicleList<{0}>();", TypeName, Name);
writer.WriteLine("public scg::IList<{0}> {1}List {{", TypeName, PropertyName);
writer.WriteLine(" get {{ return pbc::Lists.AsReadOnly({0}_); }}", Name);
......@@ -66,6 +67,15 @@ namespace Google.ProtocolBuffers.ProtoGen {
}
public void GenerateParsingCode(TextGenerator writer) {
// If packed, set up the while loop
if (Descriptor.IsPacked) {
writer.WriteLine("int length = input.ReadInt32();");
writer.WriteLine("int oldLimit = input.PushLimit(length);");
writer.WriteLine("while (!input.ReachedLimit) {");
writer.Indent();
}
// Read and store the enum
// TODO(jonskeet): Make a more efficient way of doing this
writer.WriteLine("int rawValue = input.ReadEnum();");
writer.WriteLine("if (!global::System.Enum.IsDefined(typeof({0}), rawValue)) {{", TypeName);
......@@ -73,17 +83,56 @@ namespace Google.ProtocolBuffers.ProtoGen {
writer.WriteLine("} else {");
writer.WriteLine(" Add{0}(({1}) rawValue);", PropertyName, TypeName);
writer.WriteLine("}");
if (Descriptor.IsPacked) {
writer.Outdent();
writer.WriteLine("}");
writer.WriteLine("input.PopLimit(oldLimit);");
}
}
public void GenerateSerializationCode(TextGenerator writer) {
writer.WriteLine("foreach ({0} element in {1}List) {{", TypeName, PropertyName);
writer.WriteLine(" output.WriteEnum({0}, (int) element);", Number);
writer.WriteLine("if ({0}_.Count > 0) {{", Name);
writer.Indent();
if (Descriptor.IsPacked) {
writer.WriteLine("output.WriteRawVarint32({0});", WireFormat.MakeTag(Descriptor));
writer.WriteLine("output.WriteRawVarint32((uint) {0}MemoizedSerializedSize);", Name);
writer.WriteLine("foreach (int element in {0}_) {{", Name);
writer.WriteLine(" output.WriteEnumNoTag(element);");
writer.WriteLine("}");
} else {
writer.WriteLine("foreach (int element in {0}_) {{", Name);
writer.WriteLine(" output.WriteEnum({0}, element);", Number);
writer.WriteLine("}");
}
writer.Outdent();
writer.WriteLine("}");
}
public void GenerateSerializedSizeCode(TextGenerator writer) {
writer.WriteLine("foreach ({0} element in {1}List) {{", TypeName, PropertyName);
writer.WriteLine(" size += pb::CodedOutputStream.ComputeEnumSize({0}, (int) element);", Number);
writer.WriteLine("{");
writer.Indent();
writer.WriteLine("int dataSize = 0;");
writer.WriteLine("if ({0}_.Count > 0) {{", Name);
writer.Indent();
writer.WriteLine("foreach ({0} element in {1}_) {{", TypeName, Name);
writer.WriteLine(" dataSize += pb::CodedOutputStream.ComputeEnumSizeNoTag((int) element);");
writer.WriteLine("}");
writer.WriteLine("size += dataSize;");
int tagSize = CodedOutputStream.ComputeTagSize(Descriptor.FieldNumber);
if (Descriptor.IsPacked) {
writer.WriteLine("size += {0};", tagSize);
writer.WriteLine("size += pb::CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);");
} else {
writer.WriteLine("size += {0} * {1}_.Count;", tagSize, Name);
}
writer.Outdent();
writer.WriteLine("}");
// cache the data size for packed fields.
if (Descriptor.IsPacked) {
writer.WriteLine("{0}MemoizedSerializedSize = dataSize;", Name);
}
writer.Outdent();
writer.WriteLine("}");
}
}
......
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.DescriptorProtos;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.ProtoGen {
......@@ -11,6 +12,9 @@ namespace Google.ProtocolBuffers.ProtoGen {
}
public void GenerateMembers(TextGenerator writer) {
if (Descriptor.IsPacked && Descriptor.File.Options.OptimizeFor == FileOptions.Types.OptimizeMode.SPEED) {
writer.WriteLine("private int {0}MemoizedSerializedSize;", Name);
}
writer.WriteLine("private pbc::PopsicleList<{0}> {1}_ = new pbc::PopsicleList<{0}>();", TypeName, Name);
writer.WriteLine("public scg::IList<{0}> {1}List {{", TypeName, PropertyName);
writer.WriteLine(" get {{ return pbc::Lists.AsReadOnly({0}_); }}", Name);
......@@ -68,18 +72,60 @@ namespace Google.ProtocolBuffers.ProtoGen {
}
public void GenerateParsingCode(TextGenerator writer) {
writer.WriteLine("Add{0}(input.Read{1}());", PropertyName, CapitalizedTypeName);
if (Descriptor.IsPacked) {
writer.WriteLine("int length = input.ReadInt32();");
writer.WriteLine("int limit = input.PushLimit(length);");
writer.WriteLine("while (!input.ReachedLimit) {");
writer.WriteLine(" Add{0}(input.Read{1}());", PropertyName, CapitalizedTypeName);
writer.WriteLine("}");
writer.WriteLine("input.PopLimit(limit);");
} else {
writer.WriteLine("Add{0}(input.Read{1}());", PropertyName, CapitalizedTypeName);
}
}
public void GenerateSerializationCode(TextGenerator writer) {
writer.WriteLine("foreach ({0} element in {1}List) {{", TypeName, PropertyName);
writer.WriteLine(" output.Write{0}({1}, element);", CapitalizedTypeName, Number);
writer.WriteLine("if ({0}_.Count > 0) {{", Name);
writer.Indent();
if (Descriptor.IsPacked) {
writer.WriteLine("output.WriteRawVarint32({0});", WireFormat.MakeTag(Descriptor));
writer.WriteLine("output.WriteRawVarint32((uint) {0}MemoizedSerializedSize);", Name);
writer.WriteLine("foreach ({0} element in {1}_) {{", TypeName, Name);
writer.WriteLine(" output.Write{0}NoTag(element);", CapitalizedTypeName);
writer.WriteLine("}");
} else {
writer.WriteLine("foreach ({0} element in {1}_) {{", TypeName, Name);
writer.WriteLine(" output.Write{0}({1}, element);", CapitalizedTypeName, Number);
writer.WriteLine("}");
}
writer.Outdent();
writer.WriteLine("}");
}
public void GenerateSerializedSizeCode(TextGenerator writer) {
writer.WriteLine("foreach ({0} element in {1}List) {{", TypeName, PropertyName);
writer.WriteLine(" size += pb::CodedOutputStream.Compute{0}Size({1}, element);", CapitalizedTypeName, Number);
writer.WriteLine("{");
writer.Indent();
writer.WriteLine("int dataSize = 0;");
if (FixedSize == -1) {
writer.WriteLine("foreach ({0} element in {1}List) {{", TypeName, PropertyName);
writer.WriteLine(" dataSize += pb::CodedOutputStream.Compute{0}SizeNoTag(element);", CapitalizedTypeName, Number);
writer.WriteLine("}");
} else {
writer.WriteLine("dataSize = {0} * {1}_.Count;", FixedSize, Name);
}
writer.WriteLine("size += dataSize;");
int tagSize = CodedOutputStream.ComputeTagSize(Descriptor.FieldNumber);
if (Descriptor.IsPacked) {
writer.WriteLine("size += {0};", tagSize);
writer.WriteLine("size += pb::CodedOutputStream.ComputeInt32SizeNoTag(dataSize);");
} else {
writer.WriteLine("size += {0} * {1}_.Count;", tagSize, Name);
}
// cache the data size for packed fields.
if (Descriptor.IsPacked) {
writer.WriteLine("{0}MemoizedSerializedSize = dataSize;", Name);
}
writer.Outdent();
writer.WriteLine("}");
}
}
......
......@@ -73,6 +73,20 @@ namespace Google.ProtocolBuffers {
TestUtil.AssertAllFieldsSet((TestAllTypes) message.WrappedMessage);
}
[Test]
public void PackedSerialization() {
IMessage abstractMessage = new AbstractMessageWrapper(TestUtil.GetPackedSet());
TestUtil.AssertPackedFieldsSet(TestPackedTypes.ParseFrom(abstractMessage.ToByteString()));
Assert.AreEqual(TestUtil.GetPackedSet().ToByteString(), abstractMessage.ToByteString());
}
[Test]
public void PackedParsing() {
AbstractMessageWrapper.Builder builder = new AbstractMessageWrapper.Builder(TestPackedTypes.CreateBuilder());
AbstractMessageWrapper message = builder.MergeFrom(TestUtil.GetPackedSet().ToByteString()).Build();
TestUtil.AssertPackedFieldsSet((TestPackedTypes)message.WrappedMessage);
}
[Test]
public void OptimizedForSize() {
// We're mostly only Checking that this class was compiled successfully.
......
......@@ -196,6 +196,18 @@ namespace Google.ProtocolBuffers {
}
}
/// <summary>
/// Tests writing a whole message with every packed field type. Ensures the
/// wire format of packed fields is compatible with C++.
/// </summary>
[Test]
public void WriteWholePackedFieldsMessage() {
TestPackedTypes message = TestUtil.GetPackedSet();
byte[] rawBytes = message.ToByteArray();
TestUtil.AssertEqualBytes(TestUtil.GetGoldenPackedFieldsMessage().ToByteArray(),
rawBytes);
}
[Test]
public void EncodeZigZag32() {
......
......@@ -38,11 +38,13 @@ namespace Google.ProtocolBuffers {
private ReflectionTester reflectionTester;
private ReflectionTester extensionsReflectionTester;
private ReflectionTester packedReflectionTester;
[SetUp]
public void SetUp() {
reflectionTester = ReflectionTester.CreateTestAllTypesInstance();
extensionsReflectionTester = ReflectionTester.CreateTestAllExtensionsInstance();
packedReflectionTester = ReflectionTester.CreateTestPackedTypesInstance();
}
[Test]
......@@ -135,6 +137,33 @@ namespace Google.ProtocolBuffers {
reflectionTester.AssertAllFieldsSetViaReflection(message2);
}
[Test]
public void DynamicMessagePackedSerialization() {
IBuilder builder = DynamicMessage.CreateBuilder(TestPackedTypes.Descriptor);
packedReflectionTester.SetPackedFieldsViaReflection(builder);
IMessage message = builder.WeakBuild();
ByteString rawBytes = message.ToByteString();
TestPackedTypes message2 = TestPackedTypes.ParseFrom(rawBytes);
TestUtil.AssertPackedFieldsSet(message2);
// In fact, the serialized forms should be exactly the same, byte-for-byte.
Assert.AreEqual(TestUtil.GetPackedSet().ToByteString(), rawBytes);
}
[Test]
public void testDynamicMessagePackedParsing() {
TestPackedTypes.Builder builder = TestPackedTypes.CreateBuilder();
TestUtil.SetPackedFields(builder);
TestPackedTypes message = builder.Build();
ByteString rawBytes = message.ToByteString();
IMessage message2 = DynamicMessage.ParseFrom(TestPackedTypes.Descriptor, rawBytes);
packedReflectionTester.AssertPackedFieldsSetViaReflection(message2);
}
[Test]
public void DynamicMessageCopy() {
TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
......@@ -62,12 +62,20 @@ namespace Google.ProtocolBuffers {
TestUtil.AssertAllFieldsSet(message2);
}
[Test]
public void SerializationPacked() {
TestPackedTypes message = TestUtil.GetPackedSet();
ByteString rawBytes = message.ToByteString();
Assert.AreEqual(rawBytes.Length, message.SerializedSize);
TestPackedTypes message2 = TestPackedTypes.ParseFrom(rawBytes);
TestUtil.AssertPackedFieldsSet(message2);
}
[Test]
public void SerializeExtensions() {
// TestAllTypes and TestAllExtensions should have compatible wire formats,
// so if we serealize a TestAllExtensions then parse it as TestAllTypes
// so if we serialize a TestAllExtensions then parse it as TestAllTypes
// it should work.
TestAllExtensions message = TestUtil.GetAllExtensionsSet();
ByteString rawBytes = message.ToByteString();
Assert.AreEqual(rawBytes.Length, message.SerializedSize);
......@@ -77,6 +85,19 @@ namespace Google.ProtocolBuffers {
TestUtil.AssertAllFieldsSet(message2);
}
[Test]
public void SerializePackedExtensions() {
// TestPackedTypes and TestPackedExtensions should have compatible wire
// formats; check that they serialize to the same string.
TestPackedExtensions message = TestUtil.GetPackedExtensionsSet();
ByteString rawBytes = message.ToByteString();
TestPackedTypes message2 = TestUtil.GetPackedSet();
ByteString rawBytes2 = message2.ToByteString();
Assert.AreEqual(rawBytes, rawBytes2);
}
[Test]
public void ParseExtensions() {
// TestAllTypes and TestAllExtensions should have compatible wire formats,
......@@ -90,12 +111,23 @@ namespace Google.ProtocolBuffers {
TestUtil.RegisterAllExtensions(registry);
registry = registry.AsReadOnly();
TestAllExtensions message2 =
TestAllExtensions.ParseFrom(rawBytes, registry);
TestAllExtensions message2 = TestAllExtensions.ParseFrom(rawBytes, registry);
TestUtil.AssertAllExtensionsSet(message2);
}
[Test]
public void ParsePackedExtensions() {
// Ensure that packed extensions can be properly parsed.
TestPackedExtensions message = TestUtil.GetPackedExtensionsSet();
ByteString rawBytes = message.ToByteString();
ExtensionRegistry registry = TestUtil.CreateExtensionRegistry();
TestPackedExtensions message2 = TestPackedExtensions.ParseFrom(rawBytes, registry);
TestUtil.AssertPackedExtensionsSet(message2);
}
[Test]
public void ExtensionsSerializedSize() {
Assert.AreEqual(TestUtil.GetAllSet().SerializedSize, TestUtil.GetAllExtensionsSet().SerializedSize);
......
......@@ -110,8 +110,21 @@ namespace Google.ProtocolBuffers {
if (field.IsRepeated) {
// We know it's an IList<T>, but not the exact type - so
// IEnumerable is the best we can do. (C# generics aren't covariant yet.)
foreach (object element in (IEnumerable)entry.Value) {
output.WriteField(field.FieldType, field.FieldNumber, element);
IEnumerable valueList = (IEnumerable) entry.Value;
if (field.IsPacked) {
output.WriteTag(field.FieldNumber, WireFormat.WireType.LengthDelimited);
int dataSize = 0;
foreach (object element in valueList) {
dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element);
}
output.WriteRawVarint32((uint)dataSize);
foreach (object element in valueList) {
output.WriteFieldNoTag(field.FieldType, element);
}
} else {
foreach (object element in valueList) {
output.WriteField(field.FieldType, field.FieldNumber, element);
}
}
} else {
output.WriteField(field.FieldType, field.FieldNumber, entry.Value);
......@@ -136,8 +149,19 @@ namespace Google.ProtocolBuffers {
foreach (KeyValuePair<FieldDescriptor, object> entry in AllFields) {
FieldDescriptor field = entry.Key;
if (field.IsRepeated) {
foreach (object element in (IEnumerable) entry.Value) {
size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, element);
IEnumerable valueList = (IEnumerable) entry.Value;
if (field.IsPacked) {
int dataSize = 0;
foreach (object element in valueList) {
dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element);
}
size += dataSize;
size += CodedOutputStream.ComputeTagSize(field.FieldNumber);
size += CodedOutputStream.ComputeRawVarint32Size((uint)dataSize);
} else {
foreach (object element in valueList) {
size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, element);
}
}
} else {
size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, entry.Value);
......
......@@ -608,6 +608,20 @@ namespace Google.ProtocolBuffers {
RecomputeBufferSizeAfterLimit();
}
/// <summary>
/// Returns whether or not all the data before the limit has been read.
/// </summary>
/// <returns></returns>
public bool ReachedLimit {
get {
if (currentLimit == int.MaxValue) {
return false;
}
int currentAbsolutePosition = totalBytesRetired + bufferPos;
return currentAbsolutePosition >= currentLimit;
}
}
/// <summary>
/// Called when buffer is empty to read more bytes from the
/// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
......
This diff is collapsed.
......@@ -70,27 +70,27 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
"AioJCOgHEICAgIACIogBCg5NZXNzYWdlT3B0aW9ucxImChdtZXNzYWdlX3Nl" +
"dF93aXJlX2Zvcm1hdBgBIAEoCDoFZmFsc2USQwoUdW5pbnRlcnByZXRlZF9v" +
"cHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRP" +
"cHRpb24qCQjoBxCAgICAAiLVAQoMRmllbGRPcHRpb25zEjIKBWN0eXBlGAEg" +
"ASgOMiMuZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucy5DVHlwZRIcChRl" +
"eHBlcmltZW50YWxfbWFwX2tleRgJIAEoCRJDChR1bmludGVycHJldGVkX29w" +
"dGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9w" +
"dGlvbiIjCgVDVHlwZRIICgRDT1JEEAESEAoMU1RSSU5HX1BJRUNFEAIqCQjo" +
"BxCAgICAAiJdCgtFbnVtT3B0aW9ucxJDChR1bmludGVycHJldGVkX29wdGlv" +
"bhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlv" +
"bioJCOgHEICAgIACImIKEEVudW1WYWx1ZU9wdGlvbnMSQwoUdW5pbnRlcnBy" +
"ZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJw" +
"cmV0ZWRPcHRpb24qCQjoBxCAgICAAiJgCg5TZXJ2aWNlT3B0aW9ucxJDChR1" +
"bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYu" +
"VW5pbnRlcnByZXRlZE9wdGlvbioJCOgHEICAgIACIl8KDU1ldGhvZE9wdGlv" +
"bnMSQwoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnBy" +
"b3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICAAiKFAgoTVW5p" +
"bnRlcnByZXRlZE9wdGlvbhI7CgRuYW1lGAIgAygLMi0uZ29vZ2xlLnByb3Rv" +
"YnVmLlVuaW50ZXJwcmV0ZWRPcHRpb24uTmFtZVBhcnQSGAoQaWRlbnRpZmll" +
"cl92YWx1ZRgDIAEoCRIaChJwb3NpdGl2ZV9pbnRfdmFsdWUYBCABKAQSGgoS" +
"bmVnYXRpdmVfaW50X3ZhbHVlGAUgASgDEhQKDGRvdWJsZV92YWx1ZRgGIAEo" +
"ARIUCgxzdHJpbmdfdmFsdWUYByABKAwaMwoITmFtZVBhcnQSEQoJbmFtZV9w" +
"YXJ0GAEgAigJEhQKDGlzX2V4dGVuc2lvbhgCIAIoCEIpChNjb20uZ29vZ2xl" +
"LnByb3RvYnVmQhBEZXNjcmlwdG9yUHJvdG9zSAE="),
"cHRpb24qCQjoBxCAgICAAiLlAQoMRmllbGRPcHRpb25zEjIKBWN0eXBlGAEg" +
"ASgOMiMuZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucy5DVHlwZRIOCgZw" +
"YWNrZWQYAiABKAgSHAoUZXhwZXJpbWVudGFsX21hcF9rZXkYCSABKAkSQwoU" +
"dW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVm" +
"LlVuaW50ZXJwcmV0ZWRPcHRpb24iIwoFQ1R5cGUSCAoEQ09SRBABEhAKDFNU" +
"UklOR19QSUVDRRACKgkI6AcQgICAgAIiXQoLRW51bU9wdGlvbnMSQwoUdW5p" +
"bnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVu" +
"aW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICAAiJiChBFbnVtVmFsdWVPcHRp" +
"b25zEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5w" +
"cm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIiYAoOU2Vy" +
"dmljZU9wdGlvbnMSQwoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQu" +
"Z29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICA" +
"AiJfCg1NZXRob2RPcHRpb25zEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcH" +
"IAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI" +
"6AcQgICAgAIihQIKE1VuaW50ZXJwcmV0ZWRPcHRpb24SOwoEbmFtZRgCIAMo" +
"CzItLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uLk5hbWVQ" +
"YXJ0EhgKEGlkZW50aWZpZXJfdmFsdWUYAyABKAkSGgoScG9zaXRpdmVfaW50" +
"X3ZhbHVlGAQgASgEEhoKEm5lZ2F0aXZlX2ludF92YWx1ZRgFIAEoAxIUCgxk" +
"b3VibGVfdmFsdWUYBiABKAESFAoMc3RyaW5nX3ZhbHVlGAcgASgMGjMKCE5h" +
"bWVQYXJ0EhEKCW5hbWVfcGFydBgBIAIoCRIUCgxpc19leHRlbnNpb24YAiAC" +
"KAhCKQoTY29tLmdvb2dsZS5wcm90b2J1ZkIQRGVzY3JpcHRvclByb3Rvc0gB"),
new pbd::FileDescriptor[] {
});
#endregion
......@@ -155,7 +155,7 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
= Descriptor.MessageTypes[10];
internal static pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions, global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions.Builder> internal__static_google_protobuf_FieldOptions__FieldAccessorTable
= new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions, global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions.Builder>(internal__static_google_protobuf_FieldOptions__Descriptor,
new string[] { "Ctype", "ExperimentalMapKey", "UninterpretedOption", });
new string[] { "Ctype", "Packed", "ExperimentalMapKey", "UninterpretedOption", });
internal static readonly pbd::MessageDescriptor internal__static_google_protobuf_EnumOptions__Descriptor
= Descriptor.MessageTypes[11];
internal static pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.DescriptorProtos.EnumOptions, global::Google.ProtocolBuffers.DescriptorProtos.EnumOptions.Builder> internal__static_google_protobuf_EnumOptions__FieldAccessorTable
......@@ -541,8 +541,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
if (HasPackage) {
output.WriteString(2, Package);
}
foreach (string element in DependencyList) {
output.WriteString(3, element);
if (dependency_.Count > 0) {
foreach (string element in dependency_) {
output.WriteString(3, element);
}
}
foreach (global::Google.ProtocolBuffers.DescriptorProtos.DescriptorProto element in MessageTypeList) {
output.WriteMessage(4, element);
......@@ -575,8 +577,13 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
if (HasPackage) {
size += pb::CodedOutputStream.ComputeStringSize(2, Package);
}
foreach (string element in DependencyList) {
size += pb::CodedOutputStream.ComputeStringSize(3, element);
{
int dataSize = 0;
foreach (string element in DependencyList) {
dataSize += pb::CodedOutputStream.ComputeStringSizeNoTag(element);
}
size += dataSize;
size += 1 * dependency_.Count;
}
foreach (global::Google.ProtocolBuffers.DescriptorProtos.DescriptorProto element in MessageTypeList) {
size += pb::CodedOutputStream.ComputeMessageSize(4, element);
......@@ -4413,6 +4420,15 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
get { return ctype_; }
}
private bool hasPacked;
private bool packed_ = false;
public bool HasPacked {
get { return hasPacked; }
}
public bool Packed {
get { return packed_; }
}
private bool hasExperimentalMapKey;
private string experimentalMapKey_ = "";
public bool HasExperimentalMapKey {
......@@ -4448,6 +4464,9 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
if (HasCtype) {
output.WriteEnum(1, (int) Ctype);
}
if (HasPacked) {
output.WriteBool(2, Packed);
}
if (HasExperimentalMapKey) {
output.WriteString(9, ExperimentalMapKey);
}
......@@ -4468,6 +4487,9 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
if (HasCtype) {
size += pb::CodedOutputStream.ComputeEnumSize(1, (int) Ctype);
}
if (HasPacked) {
size += pb::CodedOutputStream.ComputeBoolSize(2, Packed);
}
if (HasExperimentalMapKey) {
size += pb::CodedOutputStream.ComputeStringSize(9, ExperimentalMapKey);
}
......@@ -4561,6 +4583,9 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
if (other.HasCtype) {
Ctype = other.Ctype;
}
if (other.HasPacked) {
Packed = other.Packed;
}
if (other.HasExperimentalMapKey) {
ExperimentalMapKey = other.ExperimentalMapKey;
}
......@@ -4601,6 +4626,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
}
break;
}
case 16: {
Packed = input.ReadBool();
break;
}
case 74: {
ExperimentalMapKey = input.ReadString();
break;
......@@ -4634,6 +4663,24 @@ namespace Google.ProtocolBuffers.DescriptorProtos {
return this;
}
public bool HasPacked {
get { return result.HasPacked; }
}
public bool Packed {
get { return result.Packed; }
set { SetPacked(value); }
}
public Builder SetPacked(bool value) {
result.hasPacked = true;
result.packed_ = value;
return this;
}
public Builder ClearPacked() {
result.hasPacked = false;
result.packed_ = false;
return this;
}
public bool HasExperimentalMapKey {
get { return result.HasExperimentalMapKey; }
}
......
......@@ -176,6 +176,10 @@ namespace Google.ProtocolBuffers.Descriptors {
get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REPEATED; }
}
public bool IsPacked {
get { return Proto.Options.Packed; }
}
/// <valule>
/// Indicates whether or not the field had an explicitly-defined default value.
/// </value>
......
......@@ -368,8 +368,23 @@ namespace Google.ProtocolBuffers {
output.WriteMessageSetExtension(field.FieldNumber, (IMessage) value);
} else {
if (field.IsRepeated) {
foreach (object element in (IEnumerable) value) {
output.WriteField(field.FieldType, field.FieldNumber, element);
IEnumerable valueList = (IEnumerable) value;
if (field.IsPacked) {
output.WriteTag(field.FieldNumber, WireFormat.WireType.LengthDelimited);
// Compute the total data size so the length can be written.
int dataSize = 0;
foreach (object element in valueList) {
dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element);
}
output.WriteRawVarint32((uint)dataSize);
// Write the data itself, without any tags.
foreach (object element in valueList) {
output.WriteFieldNoTag(field.FieldType, element);
}
} else {
foreach (object element in valueList) {
output.WriteField(field.FieldType, field.FieldNumber, element);
}
}
} else {
output.WriteField(field.FieldType, field.FieldNumber, value);
......@@ -389,11 +404,20 @@ namespace Google.ProtocolBuffers {
object value = entry.Value;
if (field.IsExtension && field.ContainingType.Options.MessageSetWireFormat) {
size += CodedOutputStream.ComputeMessageSetExtensionSize(field.FieldNumber, (IMessage) value);
size += CodedOutputStream.ComputeMessageSetExtensionSize(field.FieldNumber, (IMessage)value);
} else {
if (field.IsRepeated) {
foreach (object element in (IEnumerable) value) {
size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, element);
IEnumerable valueList = (IEnumerable)value;
if (field.IsPacked) {
int dataSize = 0;
foreach (object element in valueList) {
dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element);
}
size += dataSize + CodedOutputStream.ComputeTagSize(field.FieldNumber) + CodedOutputStream.ComputeRawVarint32Size((uint)dataSize);
} else {
foreach (object element in valueList) {
size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, element);
}
}
} else {
size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, value);
......
......@@ -497,50 +497,73 @@ namespace Google.ProtocolBuffers {
}
// Unknown field or wrong wire type. Skip.
if (field == null || wireType != WireFormat.GetWireType(field.FieldType)) {
if (field == null || wireType != WireFormat.GetWireType(field)) {
return MergeFieldFrom(tag, input);
}
object value;
switch (field.FieldType) {
case FieldType.Group:
case FieldType.Message: {
IBuilder subBuilder;
if (defaultFieldInstance != null) {
subBuilder = defaultFieldInstance.WeakCreateBuilderForType();
} else {
subBuilder = builder.CreateBuilderForField(field);
}
if (!field.IsRepeated) {
subBuilder.WeakMergeFrom((IMessage)builder[field]);
}
if (field.FieldType == FieldType.Group) {
input.ReadGroup(field.FieldNumber, subBuilder, extensionRegistry);
} else {
input.ReadMessage(subBuilder, extensionRegistry);
}
value = subBuilder.WeakBuild();
break;
}
case FieldType.Enum: {
if (field.IsPacked) {
int length = (int)input.ReadRawVarint32();
int limit = input.PushLimit(length);
if (field.FieldType == FieldType.Enum) {
while (!input.ReachedLimit) {
int rawValue = input.ReadEnum();
value = field.EnumType.FindValueByNumber(rawValue);
// If the number isn't recognized as a valid value for this enum,
// drop it.
object value = field.EnumType.FindValueByNumber(rawValue);
if (value == null) {
MergeVarintField(fieldNumber, (ulong)rawValue);
// If the number isn't recognized as a valid value for this
// enum, drop it (don't even add it to unknownFields).
return true;
}
break;
builder.WeakAddRepeatedField(field, value);
}
default:
value = input.ReadPrimitiveField(field.FieldType);
break;
}
if (field.IsRepeated) {
builder.WeakAddRepeatedField(field, value);
} else {
while (!input.ReachedLimit) {
Object value = input.ReadPrimitiveField(field.FieldType);
builder.WeakAddRepeatedField(field, value);
}
}
input.PopLimit(limit);
} else {
builder[field] = value;
object value;
switch (field.FieldType) {
case FieldType.Group:
case FieldType.Message: {
IBuilder subBuilder;
if (defaultFieldInstance != null) {
subBuilder = defaultFieldInstance.WeakCreateBuilderForType();
} else {
subBuilder = builder.CreateBuilderForField(field);
}
if (!field.IsRepeated) {
subBuilder.WeakMergeFrom((IMessage)builder[field]);
}
if (field.FieldType == FieldType.Group) {
input.ReadGroup(field.FieldNumber, subBuilder, extensionRegistry);
} else {
input.ReadMessage(subBuilder, extensionRegistry);
}
value = subBuilder.WeakBuild();
break;
}
case FieldType.Enum: {
int rawValue = input.ReadEnum();
value = field.EnumType.FindValueByNumber(rawValue);
// If the number isn't recognized as a valid value for this enum,
// drop it.
if (value == null) {
MergeVarintField(fieldNumber, (ulong)rawValue);
return true;
}
break;
}
default:
value = input.ReadPrimitiveField(field.FieldType);
break;
}
if (field.IsRepeated) {
builder.WeakAddRepeatedField(field, value);
} else {
builder[field] = value;
}
}
return true;
}
......
......@@ -46,6 +46,19 @@ namespace Google.ProtocolBuffers {
/// </para>
/// </summary>
public static class WireFormat {
#region Fixed sizes.
// TODO(jonskeet): Move these somewhere else. They're messy. Consider making FieldType a smarter kind of enum
internal const int Fixed32Size = 4;
internal const int Fixed64Size = 8;
internal const int SFixed32Size = 4;
internal const int SFixed64Size = 8;
internal const int FloatSize = 4;
internal const int DoubleSize = 8;
internal const int BoolSize = 1;
#endregion
public enum WireType : uint {
Varint = 0,
Fixed64 = 1,
......@@ -93,6 +106,18 @@ namespace Google.ProtocolBuffers {
return (uint) (fieldNumber << TagTypeBits) | (uint) wireType;
}
public static uint MakeTag(FieldDescriptor field) {
return MakeTag(field.FieldNumber, GetWireType(field));
}
/// <summary>
/// Returns the wire type for the given field descriptor. This differs
/// from GetWireType(FieldType) for packed repeated fields.
/// </summary>
internal static WireType GetWireType(FieldDescriptor descriptor) {
return descriptor.IsPacked ? WireType.LengthDelimited : GetWireType(descriptor.FieldType);
}
/// <summary>
/// Converts a field type to its wire type. Done with a switch for the sake
/// of speed - this is significantly faster than a dictionary lookup.
......
Current task list (not in order)
- Performance framework
- Optionally remove dependencies to core and csharp options (2.0.3
will remove core dependency)
- Optionally remove dependencies to csharp options
- Remove multifile support
- Mono support
- Docs
- Clean up protogen code
- Add flags to protogen
- Avoid using reflection for messages which don't need it (is this
possible?)
- Add RegisterAllExtensions
- Add ToBuilder changes from Google's r92
- Silverlight changes (as per email to Jon)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment