Commit 4ae9b6c0 authored by Jon Skeet's avatar Jon Skeet

Merge pull request #560 from jskeet/csharp-repeated

Large changes to repeated field handling
parents 241e17ba f34d37a3
......@@ -131,6 +131,8 @@ namespace Google.Protobuf.Examples.AddressBook {
}
public const int PhoneFieldNumber = 4;
private static readonly pb::FieldCodec<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> _repeated_phone_codec
= pb::FieldCodec.ForMessage(34, global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber.Parser);
private readonly pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> phone_ = new pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber>();
public pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> Phone {
get { return phone_; }
......@@ -176,9 +178,7 @@ namespace Google.Protobuf.Examples.AddressBook {
output.WriteRawTag(26);
output.WriteString(Email);
}
if (phone_.Count > 0) {
output.WriteMessageArray(4, phone_);
}
phone_.WriteTo(output, _repeated_phone_codec);
}
public int CalculateSize() {
......@@ -192,12 +192,7 @@ namespace Google.Protobuf.Examples.AddressBook {
if (Email.Length != 0) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(Email);
}
if (phone_.Count > 0) {
foreach (global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber element in phone_) {
size += pb::CodedOutputStream.ComputeMessageSize(element);
}
size += 1 * phone_.Count;
}
size += phone_.CalculateSize(_repeated_phone_codec);
return size;
}
......@@ -241,7 +236,7 @@ namespace Google.Protobuf.Examples.AddressBook {
break;
}
case 34: {
input.ReadMessageArray(phone_, global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber.Parser);
phone_.AddEntriesFrom(input, _repeated_phone_codec);
break;
}
}
......@@ -437,6 +432,8 @@ namespace Google.Protobuf.Examples.AddressBook {
}
public const int PersonFieldNumber = 1;
private static readonly pb::FieldCodec<global::Google.Protobuf.Examples.AddressBook.Person> _repeated_person_codec
= pb::FieldCodec.ForMessage(10, global::Google.Protobuf.Examples.AddressBook.Person.Parser);
private readonly pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person> person_ = new pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person>();
public pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person> Person {
get { return person_; }
......@@ -464,19 +461,12 @@ namespace Google.Protobuf.Examples.AddressBook {
}
public void WriteTo(pb::CodedOutputStream output) {
if (person_.Count > 0) {
output.WriteMessageArray(1, person_);
}
person_.WriteTo(output, _repeated_person_codec);
}
public int CalculateSize() {
int size = 0;
if (person_.Count > 0) {
foreach (global::Google.Protobuf.Examples.AddressBook.Person element in person_) {
size += pb::CodedOutputStream.ComputeMessageSize(element);
}
size += 1 * person_.Count;
}
size += person_.CalculateSize(_repeated_person_codec);
return size;
}
......@@ -499,7 +489,7 @@ namespace Google.Protobuf.Examples.AddressBook {
}
break;
case 10: {
input.ReadMessageArray(person_, global::Google.Protobuf.Examples.AddressBook.Person.Parser);
person_.AddEntriesFrom(input, _repeated_person_codec);
break;
}
}
......
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2015 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using NUnit.Framework;
namespace Google.Protobuf
{
internal static class CodedInputStreamExtensions
{
public static void AssertNextTag(this CodedInputStream input, uint expectedTag)
{
uint tag;
Assert.IsTrue(input.ReadTag(out tag));
Assert.AreEqual(expectedTag, tag);
}
public static T ReadMessage<T>(this CodedInputStream stream, MessageParser<T> parser)
where T : IMessage<T>
{
var message = parser.CreateTemplate();
stream.ReadMessage(message);
return message;
}
}
}
......@@ -35,10 +35,8 @@
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using Google.Protobuf.Collections;
using Google.Protobuf.Descriptors;
using Google.Protobuf.TestProtos;
using NUnit.Framework;
......@@ -62,7 +60,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64() and
/// Parses the given bytes using ReadRawVarint32() and ReadRawVarint64()
/// </summary>
private static void AssertReadVarint(byte[] data, ulong value)
{
......@@ -232,66 +230,26 @@ namespace Google.Protobuf
Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
Assert.AreEqual(unchecked((long) 0x8000000000000000L), CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
}
/*
[Test]
public void ReadWholeMessage()
public void ReadWholeMessage_VaryingBlockSizes()
{
TestAllTypes message = TestUtil.GetAllSet();
TestAllTypes message = GeneratedMessageTest.GetSampleMessage();
byte[] rawBytes = message.ToByteArray();
Assert.AreEqual(rawBytes.Length, message.SerializedSize);
TestAllTypes message2 = TestAllTypes.ParseFrom(rawBytes);
TestUtil.AssertAllFieldsSet(message2);
Assert.AreEqual(rawBytes.Length, message.CalculateSize());
TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(rawBytes);
Assert.AreEqual(message, message2);
// Try different block sizes.
for (int blockSize = 1; blockSize < 256; blockSize *= 2)
{
message2 = TestAllTypes.ParseFrom(new SmallBlockInputStream(rawBytes, blockSize));
TestUtil.AssertAllFieldsSet(message2);
message2 = TestAllTypes.Parser.ParseFrom(new SmallBlockInputStream(rawBytes, blockSize));
Assert.AreEqual(message, message2);
}
}
[Test]
public void SkipWholeMessage()
{
TestAllTypes message = TestUtil.GetAllSet();
byte[] rawBytes = message.ToByteArray();
// Create two parallel inputs. Parse one as unknown fields while using
// skipField() to skip each field on the other. Expect the same tags.
CodedInputStream input1 = CodedInputStream.CreateInstance(rawBytes);
CodedInputStream input2 = CodedInputStream.CreateInstance(rawBytes);
UnknownFieldSet.Builder unknownFields = UnknownFieldSet.CreateBuilder();
uint tag;
string name;
while (input1.ReadTag(out tag, out name))
{
uint tag2;
Assert.IsTrue(input2.ReadTag(out tag2, out name));
Assert.AreEqual(tag, tag2);
unknownFields.MergeFieldFrom(tag, input1);
input2.SkipField();
}
}*/
/// <summary>
/// Test that a bug in SkipRawBytes has been fixed: if the skip
/// skips exactly up to a limit, this should bnot break things
/// </summary>
[Test]
public void SkipRawBytesBug()
{
byte[] rawBytes = new byte[] {1, 2};
CodedInputStream input = CodedInputStream.CreateInstance(rawBytes);
int limit = input.PushLimit(1);
input.SkipRawBytes(1);
input.PopLimit(limit);
Assert.AreEqual(2, input.ReadRawByte());
}
/*
public void ReadHugeBlob()
{
// Allocate and initialize a 1MB blob.
......@@ -302,24 +260,15 @@ namespace Google.Protobuf
}
// Make a message containing it.
TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
TestUtil.SetAllFields(builder);
builder.SetOptionalBytes(ByteString.CopyFrom(blob));
TestAllTypes message = builder.Build();
var message = new TestAllTypes { SingleBytes = ByteString.CopyFrom(blob) };
// Serialize and parse it. Make sure to parse from an InputStream, not
// directly from a ByteString, so that CodedInputStream uses buffered
// reading.
TestAllTypes message2 = TestAllTypes.ParseFrom(message.ToByteString().CreateCodedInput());
TestAllTypes message2 = TestAllTypes.Parser.ParseFrom(message.ToByteString());
Assert.AreEqual(message.OptionalBytes, message2.OptionalBytes);
// Make sure all the other fields were parsed correctly.
TestAllTypes message3 = TestAllTypes.CreateBuilder(message2)
.SetOptionalBytes(TestUtil.GetAllSet().OptionalBytes)
.Build();
TestUtil.AssertAllFieldsSet(message3);
}*/
Assert.AreEqual(message, message2);
}
[Test]
public void ReadMaliciouslyLargeBlob()
......@@ -461,66 +410,15 @@ namespace Google.Protobuf
}
}
enum TestNegEnum { None = 0, Value = -2 }
[Test]
public void TestNegativeEnum()
{
byte[] bytes = new byte[10] { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 };
byte[] bytes = { 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 };
CodedInputStream input = CodedInputStream.CreateInstance(bytes);
Assert.AreEqual((int)TestNegEnum.Value, input.ReadEnum());
Assert.AreEqual((int)SampleEnum.NegativeValue, input.ReadEnum());
Assert.IsTrue(input.IsAtEnd);
}
[Test]
public void TestNegativeEnumPackedArray()
{
int arraySize = 1 + (10 * 5);
int msgSize = 1 + 1 + arraySize;
byte[] bytes = new byte[msgSize];
CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);
output.WriteTag(8, WireFormat.WireType.LengthDelimited);
output.WritePackedInt32Array(new RepeatedField<int> { 0, -1, -2, -3, -4, -5 });
Assert.AreEqual(0, output.SpaceLeft);
CodedInputStream input = CodedInputStream.CreateInstance(bytes);
uint tag;
Assert.IsTrue(input.ReadTag(out tag));
RepeatedField<TestNegEnum> values = new RepeatedField<TestNegEnum>();
input.ReadEnumArray(values);
Assert.AreEqual(6, values.Count);
Assert.AreEqual(TestNegEnum.None, values[0]);
Assert.AreEqual(TestNegEnum.Value, values[2]);
// TODO(jonskeet): Test unknown value preservation
}
[Test]
public void TestNegativeEnumArray()
{
int arraySize = 1 + 1 + (11 * 5);
int msgSize = arraySize;
byte[] bytes = new byte[msgSize];
CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);
output.WriteInt32Array(8, new RepeatedField<int> { 0, -1, -2, -3, -4, -5 });
Assert.AreEqual(0, output.SpaceLeft);
CodedInputStream input = CodedInputStream.CreateInstance(bytes);
uint tag;
Assert.IsTrue(input.ReadTag(out tag));
RepeatedField<TestNegEnum> values = new RepeatedField<TestNegEnum>();
input.ReadEnumArray(values);
Assert.AreEqual(6, values.Count);
Assert.AreEqual(TestNegEnum.None, values[0]);
Assert.AreEqual(TestNegEnum.Value, values[2]);
// TODO(jonskeet): Test unknown value preservation
}
//Issue 71: CodedInputStream.ReadBytes go to slow path unnecessarily
[Test]
public void TestSlowPathAvoidance()
......
......@@ -35,9 +35,8 @@
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using Google.Protobuf.Collections;
using Google.Protobuf.TestProtos;
using NUnit.Framework;
namespace Google.Protobuf
......@@ -195,42 +194,24 @@ namespace Google.Protobuf
0x9abcdef012345678UL);
}
/*
[Test]
public void WriteWholeMessage()
public void WriteWholeMessage_VaryingBlockSizes()
{
TestAllTypes message = TestUtil.GetAllSet();
TestAllTypes message = GeneratedMessageTest.GetSampleMessage();
byte[] rawBytes = message.ToByteArray();
TestUtil.AssertEqualBytes(TestUtil.GoldenMessage.ToByteArray(), rawBytes);
// Try different block sizes.
for (int blockSize = 1; blockSize < 256; blockSize *= 2)
{
MemoryStream rawOutput = new MemoryStream();
CodedOutputStream output =
CodedOutputStream.CreateInstance(rawOutput, blockSize);
CodedOutputStream output = CodedOutputStream.CreateInstance(rawOutput, blockSize);
message.WriteTo(output);
output.Flush();
TestUtil.AssertEqualBytes(rawBytes, rawOutput.ToArray());
Assert.AreEqual(rawBytes, rawOutput.ToArray());
}
}
/// <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()
{
......@@ -296,66 +277,16 @@ namespace Google.Protobuf
public void TestNegativeEnumNoTag()
{
Assert.AreEqual(10, CodedOutputStream.ComputeInt32Size(-2));
Assert.AreEqual(10, CodedOutputStream.ComputeEnumSize((int) TestNegEnum.Value));
Assert.AreEqual(10, CodedOutputStream.ComputeEnumSize((int) SampleEnum.NegativeValue));
byte[] bytes = new byte[10];
CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);
output.WriteEnum((int) TestNegEnum.Value);
output.WriteEnum((int) SampleEnum.NegativeValue);
Assert.AreEqual(0, output.SpaceLeft);
Assert.AreEqual("FE-FF-FF-FF-FF-FF-FF-FF-FF-01", BitConverter.ToString(bytes));
}
enum TestNegEnum { None = 0, Value = -2 }
[Test]
public void TestNegativeEnumArrayPacked()
{
int arraySize = 1 + (10 * 5);
int msgSize = 1 + 1 + arraySize;
byte[] bytes = new byte[msgSize];
CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);
output.WriteTag(8, WireFormat.WireType.LengthDelimited);
output.WritePackedEnumArray(new RepeatedField<TestNegEnum> {
0, (TestNegEnum) (-1), TestNegEnum.Value, (TestNegEnum) (-3), (TestNegEnum) (-4), (TestNegEnum) (-5) });
Assert.AreEqual(0, output.SpaceLeft);
CodedInputStream input = CodedInputStream.CreateInstance(bytes);
uint tag;
Assert.IsTrue(input.ReadTag(out tag));
List<int> values = new List<int>();
input.ReadInt32Array(values);
Assert.AreEqual(6, values.Count);
for (int i = 0; i > -6; i--)
Assert.AreEqual(i, values[Math.Abs(i)]);
}
[Test]
public void TestNegativeEnumArray()
{
int arraySize = 1 + 1 + (11 * 5);
int msgSize = arraySize;
byte[] bytes = new byte[msgSize];
CodedOutputStream output = CodedOutputStream.CreateInstance(bytes);
output.WriteEnumArray(8, new RepeatedField<TestNegEnum> {
0, (TestNegEnum) (-1), TestNegEnum.Value, (TestNegEnum) (-3), (TestNegEnum) (-4), (TestNegEnum) (-5) });
Assert.AreEqual(0, output.SpaceLeft);
CodedInputStream input = CodedInputStream.CreateInstance(bytes);
uint tag;
Assert.IsTrue(input.ReadTag(out tag));
List<int> values = new List<int>();
input.ReadInt32Array(values);
Assert.AreEqual(6, values.Count);
for (int i = 0; i > -6; i--)
Assert.AreEqual(i, values[Math.Abs(i)]);
}
[Test]
public void TestCodedInputOutputPosition()
{
......@@ -405,7 +336,7 @@ namespace Google.Protobuf
Assert.AreEqual(130, cout.Position);
cout.Flush();
}
//Now test Input stream:
// Now test Input stream:
{
CodedInputStream cin = CodedInputStream.CreateInstance(new MemoryStream(bytes), new byte[50]);
uint tag;
......@@ -418,8 +349,8 @@ namespace Google.Protobuf
//Field 2:
Assert.IsTrue(cin.ReadTag(out tag) && tag >> 3 == 2);
Assert.AreEqual(4, cin.Position);
uint childlen = cin.ReadRawVarint32();
Assert.AreEqual(120u, childlen);
int childlen = cin.ReadLength();
Assert.AreEqual(120, childlen);
Assert.AreEqual(5, cin.Position);
int oldlimit = cin.PushLimit((int)childlen);
Assert.AreEqual(5, cin.Position);
......
......@@ -63,15 +63,21 @@ namespace Google.Protobuf
};
[Test, TestCaseSource("Codecs")]
public void RoundTrip(ICodecTestData codec)
public void RoundTripWithTag(ICodecTestData codec)
{
codec.TestRoundTrip();
codec.TestRoundTripWithTag();
}
[Test, TestCaseSource("Codecs")]
public void RoundTripRaw(ICodecTestData codec)
{
codec.TestRoundTripRaw();
}
[Test, TestCaseSource("Codecs")]
public void CalculateSize(ICodecTestData codec)
{
codec.TestCalculateSize();
codec.TestCalculateSizeWithTag();
}
[Test, TestCaseSource("Codecs")]
......@@ -82,8 +88,9 @@ namespace Google.Protobuf
public interface ICodecTestData
{
void TestRoundTrip();
void TestCalculateSize();
void TestRoundTripRaw();
void TestRoundTripWithTag();
void TestCalculateSizeWithTag();
void TestDefaultValue();
}
......@@ -100,45 +107,67 @@ namespace Google.Protobuf
this.name = name;
}
public void TestRoundTrip()
public void TestRoundTripRaw()
{
var stream = new MemoryStream();
var codedOutput = CodedOutputStream.CreateInstance(stream);
codec.Write(codedOutput, sampleValue);
codec.ValueWriter(codedOutput, sampleValue);
codedOutput.Flush();
stream.Position = 0;
var codedInput = CodedInputStream.CreateInstance(stream);
uint tag;
Assert.IsTrue(codedInput.ReadTag(out tag));
Assert.AreEqual(codec.Tag, tag);
Assert.AreEqual(sampleValue, codec.ValueReader(codedInput));
Assert.IsTrue(codedInput.IsAtEnd);
}
public void TestRoundTripWithTag()
{
var stream = new MemoryStream();
var codedOutput = CodedOutputStream.CreateInstance(stream);
codec.WriteTagAndValue(codedOutput, sampleValue);
codedOutput.Flush();
stream.Position = 0;
var codedInput = CodedInputStream.CreateInstance(stream);
codedInput.AssertNextTag(codec.Tag);
Assert.AreEqual(sampleValue, codec.Read(codedInput));
Assert.IsTrue(codedInput.IsAtEnd);
}
public void TestCalculateSize()
public void TestCalculateSizeWithTag()
{
var stream = new MemoryStream();
var codedOutput = CodedOutputStream.CreateInstance(stream);
codec.Write(codedOutput, sampleValue);
codec.WriteTagAndValue(codedOutput, sampleValue);
codedOutput.Flush();
Assert.AreEqual(stream.Position, codec.CalculateSize(sampleValue));
Assert.AreEqual(stream.Position, codec.CalculateSizeWithTag(sampleValue));
}
public void TestDefaultValue()
{
// WriteTagAndValue ignores default values
var stream = new MemoryStream();
var codedOutput = CodedOutputStream.CreateInstance(stream);
codec.Write(codedOutput, codec.DefaultValue);
codec.WriteTagAndValue(codedOutput, codec.DefaultValue);
codedOutput.Flush();
Assert.AreEqual(0, stream.Position);
Assert.AreEqual(0, codec.CalculateSize(codec.DefaultValue));
Assert.AreEqual(0, codec.CalculateSizeWithTag(codec.DefaultValue));
if (typeof(T).IsValueType)
{
Assert.AreEqual(default(T), codec.DefaultValue);
}
}
public string Description { get { return name; } }
// The plain ValueWriter/ValueReader delegates don't.
if (codec.DefaultValue != null) // This part isn't appropriate for message types.
{
codedOutput = CodedOutputStream.CreateInstance(stream);
codec.ValueWriter(codedOutput, codec.DefaultValue);
codedOutput.Flush();
Assert.AreNotEqual(0, stream.Position);
Assert.AreEqual(stream.Position, codec.ValueSizeCalculator(codec.DefaultValue));
stream.Position = 0;
var codedInput = CodedInputStream.CreateInstance(stream);
Assert.AreEqual(codec.DefaultValue, codec.ValueReader(codedInput));
}
}
public override string ToString()
{
......
......@@ -10,6 +10,61 @@ namespace Google.Protobuf
/// </summary>
public class GeneratedMessageTest
{
/// <summary>
/// Returns a sample TestAllTypes with all fields populated
/// </summary>
public static TestAllTypes GetSampleMessage()
{
return new TestAllTypes
{
SingleBool = true,
SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
SingleDouble = 23.5,
SingleFixed32 = 23,
SingleFixed64 = 1234567890123,
SingleFloat = 12.25f,
SingleForeignEnum = ForeignEnum.FOREIGN_BAR,
SingleForeignMessage = new ForeignMessage { C = 10 },
SingleImportEnum = ImportEnum.IMPORT_BAZ,
SingleImportMessage = new ImportMessage { D = 20 },
SingleInt32 = 100,
SingleInt64 = 3210987654321,
SingleNestedEnum = TestAllTypes.Types.NestedEnum.FOO,
SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 },
SinglePublicImportMessage = new PublicImportMessage { E = 54 },
SingleSfixed32 = -123,
SingleSfixed64 = -12345678901234,
SingleSint32 = -456,
SingleSint64 = -12345678901235,
SingleString = "test",
SingleUint32 = uint.MaxValue,
SingleUint64 = ulong.MaxValue,
RepeatedBool = { true, false },
RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6), ByteString.CopyFrom(new byte[1000]) },
RepeatedDouble = { -12.25, 23.5 },
RepeatedFixed32 = { uint.MaxValue, 23 },
RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
RepeatedFloat = { 100f, 12.25f },
RepeatedForeignEnum = { ForeignEnum.FOREIGN_FOO, ForeignEnum.FOREIGN_BAR },
RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } },
RepeatedImportEnum = { ImportEnum.IMPORT_BAZ, ImportEnum.IMPORT_ENUM_UNSPECIFIED },
RepeatedImportMessage = { new ImportMessage { D = 20 }, new ImportMessage { D = 25 } },
RepeatedInt32 = { 100, 200 },
RepeatedInt64 = { 3210987654321, long.MaxValue },
RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.FOO, TestAllTypes.Types.NestedEnum.NEG },
RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 35 }, new TestAllTypes.Types.NestedMessage { Bb = 10 } },
RepeatedPublicImportMessage = { new PublicImportMessage { E = 54 }, new PublicImportMessage { E = -1 } },
RepeatedSfixed32 = { -123, 123 },
RepeatedSfixed64 = { -12345678901234, 12345678901234 },
RepeatedSint32 = { -456, 100 },
RepeatedSint64 = { -12345678901235, 123 },
RepeatedString = { "foo", "bar" },
RepeatedUint32 = { uint.MaxValue, uint.MinValue },
RepeatedUint64 = { ulong.MaxValue, uint.MinValue },
OneofString = "Oneof string"
};
}
[Test]
public void EmptyMessageFieldDistinctFromMissingMessageField()
{
......@@ -485,5 +540,83 @@ namespace Google.Protobuf
Assert.Throws<InvalidOperationException>(() => frozen.RepeatedDouble.Add(0.0));
Assert.Throws<InvalidOperationException>(() => frozen.RepeatedNestedMessage.Add(new TestAllTypes.Types.NestedMessage()));
}
[Test]
public void OneofProperties()
{
// Switch the oneof case between each of the different options, and check everything behaves
// as expected in each case.
var message = new TestAllTypes();
Assert.AreEqual("", message.OneofString);
Assert.AreEqual(0, message.OneofUint32);
Assert.AreEqual(ByteString.Empty, message.OneofBytes);
Assert.IsNull(message.OneofNestedMessage);
Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
message.OneofString = "sample";
Assert.AreEqual("sample", message.OneofString);
Assert.AreEqual(0, message.OneofUint32);
Assert.AreEqual(ByteString.Empty, message.OneofBytes);
Assert.IsNull(message.OneofNestedMessage);
Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message.OneofFieldCase);
var bytes = ByteString.CopyFrom(1, 2, 3);
message.OneofBytes = bytes;
Assert.AreEqual("", message.OneofString);
Assert.AreEqual(0, message.OneofUint32);
Assert.AreEqual(bytes, message.OneofBytes);
Assert.IsNull(message.OneofNestedMessage);
Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofBytes, message.OneofFieldCase);
message.OneofUint32 = 20;
Assert.AreEqual("", message.OneofString);
Assert.AreEqual(20, message.OneofUint32);
Assert.AreEqual(ByteString.Empty, message.OneofBytes);
Assert.IsNull(message.OneofNestedMessage);
Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message.OneofFieldCase);
var nestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 25 };
message.OneofNestedMessage = nestedMessage;
Assert.AreEqual("", message.OneofString);
Assert.AreEqual(0, message.OneofUint32);
Assert.AreEqual(ByteString.Empty, message.OneofBytes);
Assert.AreEqual(nestedMessage, message.OneofNestedMessage);
Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofNestedMessage, message.OneofFieldCase);
message.ClearOneofField();
Assert.AreEqual("", message.OneofString);
Assert.AreEqual(0, message.OneofUint32);
Assert.AreEqual(ByteString.Empty, message.OneofBytes);
Assert.IsNull(message.OneofNestedMessage);
Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
}
[Test]
public void OneofSerialization_NonDefaultValue()
{
var message = new TestAllTypes();
message.OneofString = "this would take a bit of space";
message.OneofUint32 = 10;
var bytes = message.ToByteArray();
Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - no string!
var message2 = TestAllTypes.Parser.ParseFrom(bytes);
Assert.AreEqual(message, message2);
Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
}
[Test]
public void OneofSerialization_DefaultValue()
{
var message = new TestAllTypes();
message.OneofString = "this would take a bit of space";
message.OneofUint32 = 0; // This is the default value for UInt32; normally wouldn't be serialized
var bytes = message.ToByteArray();
Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - it's still serialized
var message2 = TestAllTypes.Parser.ParseFrom(bytes);
Assert.AreEqual(message, message2);
Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
}
}
}
......@@ -72,6 +72,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="ByteStringTest.cs" />
<Compile Include="CodedInputStreamExtensions.cs" />
<Compile Include="CodedInputStreamTest.cs" />
<Compile Include="CodedOutputStreamTest.cs" />
<Compile Include="EqualityTester.cs" />
......@@ -79,6 +80,7 @@
<Compile Include="GeneratedMessageTest.cs" />
<Compile Include="Collections\MapFieldTest.cs" />
<Compile Include="Collections\RepeatedFieldTest.cs" />
<Compile Include="SampleEnum.cs" />
<Compile Include="TestProtos\MapUnittestProto3.cs" />
<Compile Include="TestProtos\UnittestImportProto3.cs" />
<Compile Include="TestProtos\UnittestImportPublicProto3.cs" />
......
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2015 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
namespace Google.Protobuf
{
// Just a sample enum with positive and negative values to be used in tests.
internal enum SampleEnum
{
NegativeValue = -2,
None = 0,
PositiveValue = 3
}
}
\ No newline at end of file
......@@ -422,13 +422,15 @@ namespace UnitTest.Issues.TestProtos {
}
public const int ValuesFieldNumber = 2;
private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> values_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
private static readonly pb::FieldCodec<global::UnitTest.Issues.TestProtos.NegativeEnum> _repeated_values_codec
= pb::FieldCodec.ForEnum(16, x => (int) x, x => (global::UnitTest.Issues.TestProtos.NegativeEnum) x);private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> values_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> Values {
get { return values_; }
}
public const int PackedValuesFieldNumber = 3;
private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> packedValues_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
private static readonly pb::FieldCodec<global::UnitTest.Issues.TestProtos.NegativeEnum> _repeated_packedValues_codec
= pb::FieldCodec.ForEnum(26, x => (int) x, x => (global::UnitTest.Issues.TestProtos.NegativeEnum) x);private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> packedValues_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> PackedValues {
get { return packedValues_; }
}
......@@ -463,13 +465,8 @@ namespace UnitTest.Issues.TestProtos {
output.WriteRawTag(8);
output.WriteEnum((int) Value);
}
if (values_.Count > 0) {
output.WriteEnumArray(2, values_);
}
if (packedValues_.Count > 0) {
output.WriteRawTag(26);
output.WritePackedEnumArray(packedValues_);
}
values_.WriteTo(output, _repeated_values_codec);
packedValues_.WriteTo(output, _repeated_packedValues_codec);
}
public int CalculateSize() {
......@@ -477,22 +474,8 @@ namespace UnitTest.Issues.TestProtos {
if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO) {
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Value);
}
if (values_.Count > 0) {
int dataSize = 0;
foreach (global::UnitTest.Issues.TestProtos.NegativeEnum element in values_) {
dataSize += pb::CodedOutputStream.ComputeEnumSize((int) element);
}
size += dataSize;
size += 1 * values_.Count;
}
if (packedValues_.Count > 0) {
int dataSize = 0;
foreach (global::UnitTest.Issues.TestProtos.NegativeEnum element in packedValues_) {
dataSize += pb::CodedOutputStream.ComputeEnumSize((int) element);
}
size += dataSize;
size += 1 + pb::CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);
}
size += values_.CalculateSize(_repeated_values_codec);
size += packedValues_.CalculateSize(_repeated_packedValues_codec);
return size;
}
......@@ -524,12 +507,12 @@ namespace UnitTest.Issues.TestProtos {
}
case 18:
case 16: {
input.ReadEnumArray<global::UnitTest.Issues.TestProtos.NegativeEnum>(values_);
values_.AddEntriesFrom(input, _repeated_values_codec);
break;
}
case 26:
case 24: {
input.ReadEnumArray<global::UnitTest.Issues.TestProtos.NegativeEnum>(packedValues_);
packedValues_.AddEntriesFrom(input, _repeated_packedValues_codec);
break;
}
}
......@@ -678,6 +661,8 @@ namespace UnitTest.Issues.TestProtos {
}
public const int PrimitiveArrayFieldNumber = 2;
private static readonly pb::FieldCodec<int> _repeated_primitiveArray_codec
= pb::FieldCodec.ForInt32(18);
private readonly pbc::RepeatedField<int> primitiveArray_ = new pbc::RepeatedField<int>();
[global::System.ObsoleteAttribute()]
public pbc::RepeatedField<int> PrimitiveArray {
......@@ -696,6 +681,8 @@ namespace UnitTest.Issues.TestProtos {
}
public const int MessageArrayFieldNumber = 4;
private static readonly pb::FieldCodec<global::UnitTest.Issues.TestProtos.DeprecatedChild> _repeated_messageArray_codec
= pb::FieldCodec.ForMessage(34, global::UnitTest.Issues.TestProtos.DeprecatedChild.Parser);
private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedChild> messageArray_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedChild>();
[global::System.ObsoleteAttribute()]
public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedChild> MessageArray {
......@@ -714,7 +701,8 @@ namespace UnitTest.Issues.TestProtos {
}
public const int EnumArrayFieldNumber = 6;
private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum> enumArray_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum>();
private static readonly pb::FieldCodec<global::UnitTest.Issues.TestProtos.DeprecatedEnum> _repeated_enumArray_codec
= pb::FieldCodec.ForEnum(50, x => (int) x, x => (global::UnitTest.Issues.TestProtos.DeprecatedEnum) x);private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum> enumArray_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum>();
[global::System.ObsoleteAttribute()]
public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum> EnumArray {
get { return enumArray_; }
......@@ -756,25 +744,17 @@ namespace UnitTest.Issues.TestProtos {
output.WriteRawTag(8);
output.WriteInt32(PrimitiveValue);
}
if (primitiveArray_.Count > 0) {
output.WriteRawTag(18);
output.WritePackedInt32Array(primitiveArray_);
}
primitiveArray_.WriteTo(output, _repeated_primitiveArray_codec);
if (messageValue_ != null) {
output.WriteRawTag(26);
output.WriteMessage(MessageValue);
}
if (messageArray_.Count > 0) {
output.WriteMessageArray(4, messageArray_);
}
messageArray_.WriteTo(output, _repeated_messageArray_codec);
if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DEPRECATED_ZERO) {
output.WriteRawTag(40);
output.WriteEnum((int) EnumValue);
}
if (enumArray_.Count > 0) {
output.WriteRawTag(50);
output.WritePackedEnumArray(enumArray_);
}
enumArray_.WriteTo(output, _repeated_enumArray_codec);
}
public int CalculateSize() {
......@@ -782,34 +762,15 @@ namespace UnitTest.Issues.TestProtos {
if (PrimitiveValue != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(PrimitiveValue);
}
if (primitiveArray_.Count > 0) {
int dataSize = 0;
foreach (int element in primitiveArray_) {
dataSize += pb::CodedOutputStream.ComputeInt32Size(element);
}
size += dataSize;
size += 1 + pb::CodedOutputStream.ComputeInt32Size(dataSize);
}
size += primitiveArray_.CalculateSize(_repeated_primitiveArray_codec);
if (messageValue_ != null) {
size += 1 + pb::CodedOutputStream.ComputeMessageSize(MessageValue);
}
if (messageArray_.Count > 0) {
foreach (global::UnitTest.Issues.TestProtos.DeprecatedChild element in messageArray_) {
size += pb::CodedOutputStream.ComputeMessageSize(element);
}
size += 1 * messageArray_.Count;
}
size += messageArray_.CalculateSize(_repeated_messageArray_codec);
if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DEPRECATED_ZERO) {
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) EnumValue);
}
if (enumArray_.Count > 0) {
int dataSize = 0;
foreach (global::UnitTest.Issues.TestProtos.DeprecatedEnum element in enumArray_) {
dataSize += pb::CodedOutputStream.ComputeEnumSize((int) element);
}
size += dataSize;
size += 1 + pb::CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);
}
size += enumArray_.CalculateSize(_repeated_enumArray_codec);
return size;
}
......@@ -851,7 +812,7 @@ namespace UnitTest.Issues.TestProtos {
}
case 18:
case 16: {
input.ReadInt32Array(primitiveArray_);
primitiveArray_.AddEntriesFrom(input, _repeated_primitiveArray_codec);
break;
}
case 26: {
......@@ -862,7 +823,7 @@ namespace UnitTest.Issues.TestProtos {
break;
}
case 34: {
input.ReadMessageArray(messageArray_, global::UnitTest.Issues.TestProtos.DeprecatedChild.Parser);
messageArray_.AddEntriesFrom(input, _repeated_messageArray_codec);
break;
}
case 40: {
......@@ -871,7 +832,7 @@ namespace UnitTest.Issues.TestProtos {
}
case 50:
case 48: {
input.ReadEnumArray<global::UnitTest.Issues.TestProtos.DeprecatedEnum>(enumArray_);
enumArray_.AddEntriesFrom(input, _repeated_enumArray_codec);
break;
}
}
......
......@@ -50,7 +50,7 @@ namespace Google.Protobuf
private const int LittleEndian32Size = 4;
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// Computes the number of bytes that would be needed to encode a
/// double field, including the tag.
/// </summary>
public static int ComputeDoubleSize(double value)
......@@ -59,7 +59,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// Computes the number of bytes that would be needed to encode a
/// float field, including the tag.
/// </summary>
public static int ComputeFloatSize(float value)
......@@ -68,7 +68,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// Computes the number of bytes that would be needed to encode a
/// uint64 field, including the tag.
/// </summary>
public static int ComputeUInt64Size(ulong value)
......@@ -77,7 +77,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// Computes the number of bytes that would be needed to encode an
/// int64 field, including the tag.
/// </summary>
public static int ComputeInt64Size(long value)
......@@ -86,7 +86,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// Computes the number of bytes that would be needed to encode an
/// int32 field, including the tag.
/// </summary>
public static int ComputeInt32Size(int value)
......@@ -103,7 +103,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// Computes the number of bytes that would be needed to encode a
/// fixed64 field, including the tag.
/// </summary>
public static int ComputeFixed64Size(ulong value)
......@@ -112,7 +112,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// Computes the number of bytes that would be needed to encode a
/// fixed32 field, including the tag.
/// </summary>
public static int ComputeFixed32Size(uint value)
......@@ -121,7 +121,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// Computes the number of bytes that would be needed to encode a
/// bool field, including the tag.
/// </summary>
public static int ComputeBoolSize(bool value)
......@@ -130,7 +130,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// Computes the number of bytes that would be needed to encode a
/// string field, including the tag.
/// </summary>
public static int ComputeStringSize(String value)
......@@ -141,7 +141,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// Computes the number of bytes that would be needed to encode a
/// group field, including the tag.
/// </summary>
public static int ComputeGroupSize(IMessage value)
......@@ -150,7 +150,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// Computes the number of bytes that would be needed to encode an
/// embedded message field, including the tag.
/// </summary>
public static int ComputeMessageSize(IMessage value)
......@@ -160,7 +160,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// Computes the number of bytes that would be needed to encode a
/// bytes field, including the tag.
/// </summary>
public static int ComputeBytesSize(ByteString value)
......@@ -170,7 +170,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// Computes the number of bytes that would be needed to encode a
/// uint32 field, including the tag.
/// </summary>
public static int ComputeUInt32Size(uint value)
......@@ -179,7 +179,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// Computes the number of bytes that would be needed to encode a
/// enum field, including the tag. The caller is responsible for
/// converting the enum value to its numeric value.
/// </summary>
......@@ -190,7 +190,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// Computes the number of bytes that would be needed to encode an
/// sfixed32 field, including the tag.
/// </summary>
public static int ComputeSFixed32Size(int value)
......@@ -199,7 +199,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// Computes the number of bytes that would be needed to encode an
/// sfixed64 field, including the tag.
/// </summary>
public static int ComputeSFixed64Size(long value)
......@@ -208,7 +208,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// Computes the number of bytes that would be needed to encode an
/// sint32 field, including the tag.
/// </summary>
public static int ComputeSInt32Size(int value)
......@@ -217,7 +217,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// Computes the number of bytes that would be needed to encode an
/// sint64 field, including the tag.
/// </summary>
public static int ComputeSInt64Size(long value)
......@@ -226,7 +226,16 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a varint.
/// Computes the number of bytes that would be needed to encode a length,
/// as written by <see cref="WriteLength"/>.
/// </summary>
public static int ComputeLengthSize(int length)
{
return ComputeRawVarint32Size((uint) length);
}
/// <summary>
/// Computes the number of bytes that would be needed to encode a varint.
/// </summary>
public static int ComputeRawVarint32Size(uint value)
{
......@@ -250,7 +259,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a varint.
/// Computes the number of bytes that would be needed to encode a varint.
/// </summary>
public static int ComputeRawVarint64Size(ulong value)
{
......@@ -294,7 +303,7 @@ namespace Google.Protobuf
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a tag.
/// Computes the number of bytes that would be needed to encode a tag.
/// </summary>
public static int ComputeTagSize(int fieldNumber)
{
......
......@@ -334,6 +334,10 @@ namespace Google.Protobuf.Collections
public int CalculateSize(Codec codec)
{
if (Count == 0)
{
return 0;
}
var message = new Codec.MessageAdapter(codec);
int size = 0;
foreach (var entry in list)
......@@ -419,13 +423,13 @@ namespace Google.Protobuf.Collections
public void WriteTo(CodedOutputStream output)
{
codec.keyCodec.Write(output, Key);
codec.valueCodec.Write(output, Value);
codec.keyCodec.WriteTagAndValue(output, Key);
codec.valueCodec.WriteTagAndValue(output, Value);
}
public int CalculateSize()
{
return codec.keyCodec.CalculateSize(Key) + codec.valueCodec.CalculateSize(Value);
return codec.keyCodec.CalculateSizeWithTag(Key) + codec.valueCodec.CalculateSizeWithTag(Value);
}
}
}
......
......@@ -38,7 +38,7 @@ namespace Google.Protobuf.Collections
/// <summary>
/// Read-only wrapper around another dictionary.
/// </summary>
public sealed class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
internal sealed class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly IDictionary<TKey, TValue> wrapped;
......
......@@ -49,6 +49,113 @@ namespace Google.Protobuf.Collections
return clone;
}
public void AddEntriesFrom(CodedInputStream input, FieldCodec<T> codec)
{
// TODO: Inline some of the Add code, so we can avoid checking the size on every
// iteration and the mutability.
uint tag = input.LastTag;
var reader = codec.ValueReader;
// Value types can be packed or not.
if (typeof(T).IsValueType && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited)
{
int length = input.ReadLength();
if (length > 0)
{
int oldLimit = input.PushLimit(length);
while (!input.ReachedLimit)
{
Add(reader(input));
}
input.PopLimit(oldLimit);
}
// Empty packed field. Odd, but valid - just ignore.
}
else
{
// Not packed... (possibly not packable)
do
{
Add(reader(input));
} while (input.MaybeConsumeTag(tag));
}
}
public int CalculateSize(FieldCodec<T> codec)
{
if (count == 0)
{
return 0;
}
uint tag = codec.Tag;
if (typeof(T).IsValueType && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited)
{
int dataSize = CalculatePackedDataSize(codec);
return CodedOutputStream.ComputeRawVarint32Size(tag) +
CodedOutputStream.ComputeRawVarint32Size((uint)dataSize) +
dataSize;
}
else
{
var sizeCalculator = codec.ValueSizeCalculator;
int size = count * CodedOutputStream.ComputeRawVarint32Size(tag);
for (int i = 0; i < count; i++)
{
size += sizeCalculator(array[i]);
}
return size;
}
}
private int CalculatePackedDataSize(FieldCodec<T> codec)
{
int fixedSize = codec.FixedSize;
if (fixedSize == 0)
{
var calculator = codec.ValueSizeCalculator;
int tmp = 0;
for (int i = 0; i < count; i++)
{
tmp += calculator(array[i]);
}
return tmp;
}
else
{
return fixedSize * Count;
}
}
public void WriteTo(CodedOutputStream output, FieldCodec<T> codec)
{
if (count == 0)
{
return;
}
var writer = codec.ValueWriter;
var tag = codec.Tag;
if (typeof(T).IsValueType && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited)
{
// Packed primitive type
uint size = (uint)CalculatePackedDataSize(codec);
output.WriteTag(tag);
output.WriteRawVarint32(size);
for (int i = 0; i < count; i++)
{
writer(output, array[i]);
}
}
else
{
// Not packed: a simple tag/value pair for each value.
// Can't use codec.WriteTagAndValue, as that omits default values.
for (int i = 0; i < count; i++)
{
output.WriteTag(tag);
writer(output, array[i]);
}
}
}
public bool IsFrozen { get { return frozen; } }
public void Freeze()
......@@ -66,9 +173,9 @@ namespace Google.Protobuf.Collections
private void EnsureSize(int size)
{
size = Math.Max(size, MinArraySize);
if (array.Length < size)
{
size = Math.Max(size, MinArraySize);
int newSize = Math.Max(array.Length * 2, size);
var tmp = new T[newSize];
Array.Copy(array, 0, tmp, 0, array.Length);
......@@ -87,18 +194,6 @@ namespace Google.Protobuf.Collections
array[count++] = item;
}
/// <summary>
/// Hack to allow us to add enums easily... will only work with int-based types.
/// </summary>
/// <param name="readEnum"></param>
internal void AddInt32(int item)
{
this.CheckMutable();
EnsureSize(count + 1);
int[] castArray = (int[]) (object) array;
castArray[count++] = item;
}
public void Clear()
{
this.CheckMutable();
......@@ -180,16 +275,7 @@ namespace Google.Protobuf.Collections
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Returns an enumerator of the values in this list as integers.
/// Used for enum types.
/// </summary>
internal Int32Enumerator GetInt32Enumerator()
{
return new Int32Enumerator((int[])(object)array, count);
}
}
public override int GetHashCode()
{
......@@ -297,17 +383,7 @@ namespace Google.Protobuf.Collections
array[index] = value;
}
}
internal uint CalculateSize(Func<T, int> sizeComputer)
{
int size = 0;
for (int i = 0; i < count; i++)
{
size += sizeComputer(array[i]);
}
return (uint)size;
}
public struct Enumerator : IEnumerator<T>
{
private int index;
......@@ -355,46 +431,5 @@ namespace Google.Protobuf.Collections
{
}
}
internal struct Int32Enumerator : IEnumerator<int>
{
private int index;
private readonly int[] array;
private readonly int count;
public Int32Enumerator(int[] array, int count)
{
this.array = array;
this.index = -1;
this.count = count;
}
public bool MoveNext()
{
if (index + 1 >= count)
{
return false;
}
index++;
return true;
}
public void Reset()
{
index = -1;
}
// No guard here, as we're only going to use this internally...
public int Current { get { return array[index]; } }
object IEnumerator.Current
{
get { return Current; }
}
public void Dispose()
{
}
}
}
}
This diff is collapsed.
......@@ -38,7 +38,7 @@ namespace Google.Protobuf
{
ThrowHelper.ThrowIfNull(message, "message");
ThrowHelper.ThrowIfNull(input, "input");
int size = (int)CodedInputStream.ReadRawVarint32(input);
int size = (int) CodedInputStream.ReadRawVarint32(input);
Stream limitedStream = new LimitedInputStream(input, size);
message.MergeFrom(limitedStream);
}
......
......@@ -61,6 +61,13 @@ using System.Security;
[assembly: AssemblyVersion("2.4.1.555")]
[assembly: InternalsVisibleTo("Google.Protobuf.Test, PublicKey=" +
"00240000048000009400000006020000002400005253413100040000110000003b4611704c5379" +
"39c3e0fbe9447dd6fa5462507f9dd4fd9fbf0712457e415b037da6d2c4eb5d2c7d29c86380af68" +
"7cf400401bb183f2a70bd3b631c1fcb7db8aa66c766694a9fb53fa765df6303104da8c978f3b6d" +
"53909cd30685b8bc9922c726cd82b5995e9e2cfca6df7a2d189d851492e49f4b76f269ce6dfd08" +
"c34a7d98")]
#if !NOFILEVERSION
[assembly: AssemblyFileVersion("2.4.1.555")]
#endif
......
......@@ -53,13 +53,13 @@ namespace Google.Protobuf
#region Fixed sizes.
// TODO(jonskeet): Move these somewhere else. They're messy. Consider making FieldType a smarter kind of enum
public const int Fixed32Size = 4;
public const int Fixed64Size = 8;
public const int SFixed32Size = 4;
public const int SFixed64Size = 8;
public const int FloatSize = 4;
public const int DoubleSize = 8;
public const int BoolSize = 1;
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
......@@ -72,22 +72,7 @@ namespace Google.Protobuf
EndGroup = 4,
Fixed32 = 5
}
internal static class MessageSetField
{
internal const int Item = 1;
internal const int TypeID = 2;
internal const int Message = 3;
}
internal static class MessageSetTag
{
internal static readonly uint ItemStart = MakeTag(MessageSetField.Item, WireType.StartGroup);
internal static readonly uint ItemEnd = MakeTag(MessageSetField.Item, WireType.EndGroup);
internal static readonly uint TypeID = MakeTag(MessageSetField.TypeID, WireType.Varint);
internal static readonly uint Message = MakeTag(MessageSetField.Message, WireType.LengthDelimited);
}
private const int TagTypeBits = 3;
private const uint TagTypeMask = (1 << TagTypeBits) - 1;
......@@ -120,7 +105,6 @@ namespace Google.Protobuf
return (uint) (fieldNumber << TagTypeBits) | (uint) wireType;
}
#if !LITE
public static uint MakeTag(FieldDescriptor field)
{
return MakeTag(field.FieldNumber, GetWireType(field));
......@@ -135,8 +119,6 @@ namespace Google.Protobuf
return descriptor.IsPacked ? WireType.LengthDelimited : GetWireType(descriptor.FieldType);
}
#endif
/// <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.
......@@ -177,7 +159,7 @@ namespace Google.Protobuf
case FieldType.Enum:
return WireType.Varint;
default:
throw new ArgumentOutOfRangeException("No such field type");
throw new ArgumentOutOfRangeException("fieldType", "No such field type");
}
}
}
......
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