Commit 6b01539d authored by Jon Skeet's avatar Jon Skeet

Merge pull request #543 from jskeet/proto3-map

Proto3 map support for C#
parents 68a4ee26 286edc0f
......@@ -19,6 +19,7 @@ set(libprotoc_files
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_field_base.cc
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_generator.cc
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_helpers.cc
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_map_field.cc
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_message.cc
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_message_field.cc
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
......
......@@ -43,10 +43,12 @@ $PROTOC -Isrc --csharp_out=csharp/src/ProtocolBuffers/DescriptorProtos \
rm src/google/protobuf/descriptor_proto_file.proto
$PROTOC -Isrc --csharp_out=csharp/src/ProtocolBuffers.Test/TestProtos \
src/google/protobuf/map_unittest_proto3.proto \
src/google/protobuf/unittest_proto3.proto \
src/google/protobuf/unittest_import_proto3.proto \
src/google/protobuf/unittest_import_public_proto3.proto
$PROTOC -Icsharp/protos/extest --csharp_out=csharp/src/ProtocolBuffers.Test/TestProtos \
csharp/protos/extest/unittest_issues.proto
......
......@@ -155,7 +155,7 @@ namespace Google.ProtocolBuffers.Examples.AddressBook {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Name.Length != 0) hash ^= Name.GetHashCode();
if (Id != 0) hash ^= Id.GetHashCode();
if (Email.Length != 0) hash ^= Email.GetHashCode();
......@@ -200,6 +200,7 @@ namespace Google.ProtocolBuffers.Examples.AddressBook {
}
return size;
}
public void MergeFrom(Person other) {
if (other == null) {
return;
......@@ -329,7 +330,7 @@ namespace Google.ProtocolBuffers.Examples.AddressBook {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Number.Length != 0) hash ^= Number.GetHashCode();
if (Type != global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType.HOME) hash ^= Type.GetHashCode();
return hash;
......@@ -356,6 +357,7 @@ namespace Google.ProtocolBuffers.Examples.AddressBook {
}
return size;
}
public void MergeFrom(PhoneNumber other) {
if (other == null) {
return;
......@@ -456,7 +458,7 @@ namespace Google.ProtocolBuffers.Examples.AddressBook {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
hash ^= person_.GetHashCode();
return hash;
}
......@@ -477,6 +479,7 @@ namespace Google.ProtocolBuffers.Examples.AddressBook {
}
return size;
}
public void MergeFrom(AddressBook other) {
if (other == null) {
return;
......
This diff is collapsed.
using System;
using System.Collections.Generic;
using Google.Protobuf.Collections;
using Google.Protobuf.TestProtos;
using NUnit.Framework;
namespace Google.Protobuf
namespace Google.Protobuf.Collections
{
public class RepeatedFieldTest
{
......
#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 System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
namespace Google.Protobuf
{
/// <summary>
/// Helper methods when testing equality. NUnit's Assert.AreEqual and
/// Assert.AreNotEqual methods try to be clever with collections, which can
/// be annoying...
/// </summary>
internal static class EqualityTester
{
public static void AssertEquality<T>(T first, T second) where T : IEquatable<T>
{
Assert.IsTrue(first.Equals(second));
Assert.AreEqual(first.GetHashCode(), second.GetHashCode());
}
public static void AssertInequality<T>(T first, T second) where T : IEquatable<T>
{
Assert.IsFalse(first.Equals(second));
// While this isn't a requirement, the chances of this test failing due to
// coincidence rather than a bug are very small.
Assert.AreNotEqual(first.GetHashCode(), second.GetHashCode());
}
}
}
#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 System.Collections.Generic;
using System.IO;
using Google.Protobuf.TestProtos;
using NUnit.Framework;
namespace Google.Protobuf
{
public class FieldCodecTest
{
private static readonly List<ICodecTestData> Codecs = new List<ICodecTestData>
{
new FieldCodecTestData<bool>(FieldCodec.ForBool(100), true, "Bool"),
new FieldCodecTestData<string>(FieldCodec.ForString(100), "sample", "String"),
new FieldCodecTestData<ByteString>(FieldCodec.ForBytes(100), ByteString.CopyFrom(1, 2, 3), "Bytes"),
new FieldCodecTestData<int>(FieldCodec.ForInt32(100), -1000, "Int32"),
new FieldCodecTestData<int>(FieldCodec.ForSInt32(100), -1000, "SInt32"),
new FieldCodecTestData<int>(FieldCodec.ForSFixed32(100), -1000, "SFixed32"),
new FieldCodecTestData<uint>(FieldCodec.ForUInt32(100), 1234, "UInt32"),
new FieldCodecTestData<uint>(FieldCodec.ForFixed32(100), 1234, "Fixed32"),
new FieldCodecTestData<long>(FieldCodec.ForInt64(100), -1000, "Int64"),
new FieldCodecTestData<long>(FieldCodec.ForSInt64(100), -1000, "SInt64"),
new FieldCodecTestData<long>(FieldCodec.ForSFixed64(100), -1000, "SFixed64"),
new FieldCodecTestData<ulong>(FieldCodec.ForUInt64(100), 1234, "UInt64"),
new FieldCodecTestData<ulong>(FieldCodec.ForFixed64(100), 1234, "Fixed64"),
new FieldCodecTestData<float>(FieldCodec.ForFloat(100), 1234.5f, "Float"),
new FieldCodecTestData<double>(FieldCodec.ForDouble(100), 1234567890.5d, "Double"),
new FieldCodecTestData<ForeignEnum>(
FieldCodec.ForEnum(100, t => (int) t, t => (ForeignEnum) t), ForeignEnum.FOREIGN_BAZ, "Enum"),
new FieldCodecTestData<ForeignMessage>(
FieldCodec.ForMessage(100, ForeignMessage.Parser), new ForeignMessage { C = 10 }, "Message"),
};
[Test, TestCaseSource("Codecs")]
public void RoundTrip(ICodecTestData codec)
{
codec.TestRoundTrip();
}
[Test, TestCaseSource("Codecs")]
public void CalculateSize(ICodecTestData codec)
{
codec.TestCalculateSize();
}
[Test, TestCaseSource("Codecs")]
public void DefaultValue(ICodecTestData codec)
{
codec.TestDefaultValue();
}
public interface ICodecTestData
{
void TestRoundTrip();
void TestCalculateSize();
void TestDefaultValue();
}
public class FieldCodecTestData<T> : ICodecTestData
{
private readonly FieldCodec<T> codec;
private readonly T sampleValue;
private readonly string name;
public FieldCodecTestData(FieldCodec<T> codec, T sampleValue, string name)
{
this.codec = codec;
this.sampleValue = sampleValue;
this.name = name;
}
public void TestRoundTrip()
{
var stream = new MemoryStream();
var codedOutput = CodedOutputStream.CreateInstance(stream);
codec.Write(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.Read(codedInput));
Assert.IsTrue(codedInput.IsAtEnd);
}
public void TestCalculateSize()
{
var stream = new MemoryStream();
var codedOutput = CodedOutputStream.CreateInstance(stream);
codec.Write(codedOutput, sampleValue);
codedOutput.Flush();
Assert.AreEqual(stream.Position, codec.CalculateSize(sampleValue));
}
public void TestDefaultValue()
{
var stream = new MemoryStream();
var codedOutput = CodedOutputStream.CreateInstance(stream);
codec.Write(codedOutput, codec.DefaultValue);
codedOutput.Flush();
Assert.AreEqual(0, stream.Position);
Assert.AreEqual(0, codec.CalculateSize(codec.DefaultValue));
if (typeof(T).IsValueType)
{
Assert.AreEqual(default(T), codec.DefaultValue);
}
}
public string Description { get { return name; } }
public override string ToString()
{
return name;
}
}
}
}
using System;
using System.IO;
using Google.Protobuf.TestProtos;
using NUnit.Framework;
......@@ -9,6 +10,15 @@ namespace Google.Protobuf
/// </summary>
public class GeneratedMessageTest
{
[Test]
public void EmptyMessageFieldDistinctFromMissingMessageField()
{
// This demonstrates what we're really interested in...
var message1 = new TestAllTypes { SingleForeignMessage = new ForeignMessage() };
var message2 = new TestAllTypes(); // SingleForeignMessage is null
EqualityTester.AssertInequality(message1, message2);
}
[Test]
public void DefaultValues()
{
......@@ -146,6 +156,206 @@ namespace Google.Protobuf
Assert.AreEqual(message, parsed);
}
// Note that not every map within map_unittest_proto3 is used. They all go through very
// similar code paths. The fact that all maps are present is validation that we have codecs
// for every type.
[Test]
public void RoundTrip_Maps()
{
var message = new TestMap
{
MapBoolBool = {
{ false, true },
{ true, false }
},
MapInt32Bytes = {
{ 5, ByteString.CopyFrom(6, 7, 8) },
{ 25, ByteString.CopyFrom(1, 2, 3, 4, 5) },
{ 10, ByteString.Empty }
},
MapInt32ForeignMessage = {
{ 0, new ForeignMessage { C = 10 } },
{ 5, null },
},
MapInt32Enum = {
{ 1, MapEnum.MAP_ENUM_BAR },
{ 2000, MapEnum.MAP_ENUM_FOO }
}
};
byte[] bytes = message.ToByteArray();
TestMap parsed = TestMap.Parser.ParseFrom(bytes);
Assert.AreEqual(message, parsed);
}
[Test]
public void MapWithEmptyEntry()
{
var message = new TestMap
{
MapInt32Bytes = { { 0, ByteString.Empty } }
};
byte[] bytes = message.ToByteArray();
Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte)
var parsed = TestMap.Parser.ParseFrom(bytes);
Assert.AreEqual(1, parsed.MapInt32Bytes.Count);
Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);
}
[Test]
public void MapWithOnlyValue()
{
// Hand-craft the stream to contain a single entry with just a value.
var memoryStream = new MemoryStream();
var output = CodedOutputStream.CreateInstance(memoryStream);
output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
var nestedMessage = new ForeignMessage { C = 20 };
// Size of the entry (tag, size written by WriteMessage, data written by WriteMessage)
output.WriteRawVarint32((uint)(nestedMessage.CalculateSize() + 3));
output.WriteTag(2, WireFormat.WireType.LengthDelimited);
output.WriteMessage(nestedMessage);
output.Flush();
var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);
}
[Test]
public void MapIgnoresExtraFieldsWithinEntryMessages()
{
// Hand-craft the stream to contain a single entry with three fields
var memoryStream = new MemoryStream();
var output = CodedOutputStream.CreateInstance(memoryStream);
output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
var key = 10; // Field 1
var value = 20; // Field 2
var extra = 30; // Field 3
// Each field can be represented in a single byte, with a single byte tag.
// Total message size: 6 bytes.
output.WriteRawVarint32(6);
output.WriteTag(1, WireFormat.WireType.Varint);
output.WriteInt32(key);
output.WriteTag(2, WireFormat.WireType.Varint);
output.WriteInt32(value);
output.WriteTag(3, WireFormat.WireType.Varint);
output.WriteInt32(extra);
output.Flush();
var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
Assert.AreEqual(value, parsed.MapInt32Int32[key]);
}
[Test]
public void MapFieldOrderIsIrrelevant()
{
var memoryStream = new MemoryStream();
var output = CodedOutputStream.CreateInstance(memoryStream);
output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
var key = 10;
var value = 20;
// Each field can be represented in a single byte, with a single byte tag.
// Total message size: 4 bytes.
output.WriteRawVarint32(4);
output.WriteTag(2, WireFormat.WireType.Varint);
output.WriteInt32(value);
output.WriteTag(1, WireFormat.WireType.Varint);
output.WriteInt32(key);
output.Flush();
var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
Assert.AreEqual(value, parsed.MapInt32Int32[key]);
}
[Test]
public void MapNonContiguousEntries()
{
var memoryStream = new MemoryStream();
var output = CodedOutputStream.CreateInstance(memoryStream);
// Message structure:
// Entry for MapInt32Int32
// Entry for MapStringString
// Entry for MapInt32Int32
// First entry
var key1 = 10;
var value1 = 20;
output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
output.WriteRawVarint32(4);
output.WriteTag(1, WireFormat.WireType.Varint);
output.WriteInt32(key1);
output.WriteTag(2, WireFormat.WireType.Varint);
output.WriteInt32(value1);
// Second entry
var key2 = "a";
var value2 = "b";
output.WriteTag(TestMap.MapStringStringFieldNumber, WireFormat.WireType.LengthDelimited);
output.WriteRawVarint32(6); // 3 bytes per entry: tag, size, character
output.WriteTag(1, WireFormat.WireType.LengthDelimited);
output.WriteString(key2);
output.WriteTag(2, WireFormat.WireType.LengthDelimited);
output.WriteString(value2);
// Third entry
var key3 = 15;
var value3 = 25;
output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
output.WriteRawVarint32(4);
output.WriteTag(1, WireFormat.WireType.Varint);
output.WriteInt32(key3);
output.WriteTag(2, WireFormat.WireType.Varint);
output.WriteInt32(value3);
output.Flush();
var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
var expected = new TestMap
{
MapInt32Int32 = { { key1, value1 }, { key3, value3 } },
MapStringString = { { key2, value2 } }
};
Assert.AreEqual(expected, parsed);
}
[Test]
public void DuplicateKeys_LastEntryWins()
{
var memoryStream = new MemoryStream();
var output = CodedOutputStream.CreateInstance(memoryStream);
var key = 10;
var value1 = 20;
var value2 = 30;
// First entry
output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
output.WriteRawVarint32(4);
output.WriteTag(1, WireFormat.WireType.Varint);
output.WriteInt32(key);
output.WriteTag(2, WireFormat.WireType.Varint);
output.WriteInt32(value1);
// Second entry - same key, different value
output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
output.WriteRawVarint32(4);
output.WriteTag(1, WireFormat.WireType.Varint);
output.WriteInt32(key);
output.WriteTag(2, WireFormat.WireType.Varint);
output.WriteInt32(value2);
output.Flush();
var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
Assert.AreEqual(value2, parsed.MapInt32Int32[key]);
}
[Test]
public void CloneSingleNonMessageValues()
{
......
......@@ -74,8 +74,12 @@
<Compile Include="ByteStringTest.cs" />
<Compile Include="CodedInputStreamTest.cs" />
<Compile Include="CodedOutputStreamTest.cs" />
<Compile Include="EqualityTester.cs" />
<Compile Include="FieldCodecTest.cs" />
<Compile Include="GeneratedMessageTest.cs" />
<Compile Include="RepeatedFieldTest.cs" />
<Compile Include="Collections\MapFieldTest.cs" />
<Compile Include="Collections\RepeatedFieldTest.cs" />
<Compile Include="TestProtos\MapUnittestProto3.cs" />
<Compile Include="TestProtos\UnittestImportProto3.cs" />
<Compile Include="TestProtos\UnittestImportPublicProto3.cs" />
<Compile Include="TestProtos\UnittestIssues.cs" />
......@@ -99,9 +103,7 @@
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<Folder Include="Collections\" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
......
......@@ -120,7 +120,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (D != 0) hash ^= D.GetHashCode();
return hash;
}
......@@ -139,6 +139,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(ImportMessage other) {
if (other == null) {
return;
......
......@@ -105,7 +105,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (E != 0) hash ^= E.GetHashCode();
return hash;
}
......@@ -124,6 +124,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(PublicImportMessage other) {
if (other == null) {
return;
......
......@@ -168,7 +168,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO) hash ^= Value.GetHashCode();
hash ^= values_.GetHashCode();
hash ^= packedValues_.GetHashCode();
......@@ -212,6 +212,7 @@ namespace UnitTest.Issues.TestProtos {
}
return size;
}
public void MergeFrom(NegativeEnumMessage other) {
if (other == null) {
return;
......@@ -303,7 +304,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
return hash;
}
......@@ -314,6 +315,7 @@ namespace UnitTest.Issues.TestProtos {
int size = 0;
return size;
}
public void MergeFrom(DeprecatedChild other) {
if (other == null) {
return;
......@@ -456,7 +458,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (PrimitiveValue != 0) hash ^= PrimitiveValue.GetHashCode();
hash ^= primitiveArray_.GetHashCode();
if (messageValue_ != null) hash ^= MessageValue.GetHashCode();
......@@ -527,6 +529,7 @@ namespace UnitTest.Issues.TestProtos {
}
return size;
}
public void MergeFrom(DeprecatedFieldsMessage other) {
if (other == null) {
return;
......@@ -655,7 +658,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Item != 0) hash ^= Item.GetHashCode();
return hash;
}
......@@ -674,6 +677,7 @@ namespace UnitTest.Issues.TestProtos {
}
return size;
}
public void MergeFrom(ItemField other) {
if (other == null) {
return;
......
......@@ -456,14 +456,16 @@ namespace Google.Protobuf
}
/// <summary>
/// Returns true if the next tag is also part of the same unpacked array.
/// Peeks at the next tag in the stream. If it matches <paramref name="tag"/>,
/// the tag is consumed and the method returns <c>true</c>; otherwise, the
/// stream is left in the original position and the method returns <c>false</c>.
/// </summary>
private bool ContinueArray(uint currentTag)
public bool MaybeConsumeTag(uint tag)
{
uint next;
if (PeekNextTag(out next))
{
if (next == currentTag)
if (next == tag)
{
hasNextTag = false;
return true;
......@@ -486,17 +488,7 @@ namespace Google.Protobuf
}
return true;
}
uint next;
if (PeekNextTag(out next))
{
if (next == currentTag)
{
hasNextTag = false;
return true;
}
}
return false;
return MaybeConsumeTag(currentTag);
}
/// <summary>
......@@ -512,7 +504,7 @@ namespace Google.Protobuf
do
{
list.Add(ReadString());
} while (ContinueArray(fieldTag));
} while (MaybeConsumeTag(fieldTag));
}
public void ReadBytesArray(ICollection<ByteString> list)
......@@ -521,7 +513,7 @@ namespace Google.Protobuf
do
{
list.Add(ReadBytes());
} while (ContinueArray(fieldTag));
} while (MaybeConsumeTag(fieldTag));
}
public void ReadBoolArray(ICollection<bool> list)
......@@ -729,7 +721,7 @@ namespace Google.Protobuf
do
{
list.Add((T)(object) ReadEnum());
} while (ContinueArray(fieldTag));
} while (MaybeConsumeTag(fieldTag));
}
}
......@@ -742,7 +734,7 @@ namespace Google.Protobuf
T message = messageParser.CreateTemplate();
ReadMessage(message);
list.Add(message);
} while (ContinueArray(fieldTag));
} while (MaybeConsumeTag(fieldTag));
}
#endregion
......
......@@ -475,6 +475,14 @@ namespace Google.Protobuf
WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
}
/// <summary>
/// Writes an already-encoded tag.
/// </summary>
public void WriteTag(uint tag)
{
WriteRawVarint32(tag);
}
/// <summary>
/// Writes the given single-byte tag directly to the stream.
/// </summary>
......
This diff is collapsed.
......@@ -193,7 +193,7 @@ namespace Google.Protobuf.Collections
public override int GetHashCode()
{
int hash = 23;
int hash = 0;
for (int i = 0; i < count; i++)
{
hash = hash * 31 + array[i].GetHashCode();
......
using System;
using System.Collections.Generic;
namespace Google.Protobuf
{
/// <summary>
/// Factory methods for <see cref="FieldCodec{T}"/>.
/// </summary>
public static class FieldCodec
{
public static FieldCodec<string> ForString(uint tag)
{
return new FieldCodec<string>(input => input.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag);
}
public static FieldCodec<ByteString> ForBytes(uint tag)
{
return new FieldCodec<ByteString>(input => input.ReadBytes(), (output, value) => output.WriteBytes(value), CodedOutputStream.ComputeBytesSize, tag);
}
public static FieldCodec<bool> ForBool(uint tag)
{
return new FieldCodec<bool>(input => input.ReadBool(), (output, value) => output.WriteBool(value), CodedOutputStream.ComputeBoolSize, tag);
}
public static FieldCodec<int> ForInt32(uint tag)
{
return new FieldCodec<int>(input => input.ReadInt32(), (output, value) => output.WriteInt32(value), CodedOutputStream.ComputeInt32Size, tag);
}
public static FieldCodec<int> ForSInt32(uint tag)
{
return new FieldCodec<int>(input => input.ReadSInt32(), (output, value) => output.WriteSInt32(value), CodedOutputStream.ComputeSInt32Size, tag);
}
public static FieldCodec<uint> ForFixed32(uint tag)
{
return new FieldCodec<uint>(input => input.ReadFixed32(), (output, value) => output.WriteFixed32(value), CodedOutputStream.ComputeFixed32Size, tag);
}
public static FieldCodec<int> ForSFixed32(uint tag)
{
return new FieldCodec<int>(input => input.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), CodedOutputStream.ComputeSFixed32Size, tag);
}
public static FieldCodec<uint> ForUInt32(uint tag)
{
return new FieldCodec<uint>(input => input.ReadUInt32(), (output, value) => output.WriteUInt32(value), CodedOutputStream.ComputeUInt32Size, tag);
}
public static FieldCodec<long> ForInt64(uint tag)
{
return new FieldCodec<long>(input => input.ReadInt64(), (output, value) => output.WriteInt64(value), CodedOutputStream.ComputeInt64Size, tag);
}
public static FieldCodec<long> ForSInt64(uint tag)
{
return new FieldCodec<long>(input => input.ReadSInt64(), (output, value) => output.WriteSInt64(value), CodedOutputStream.ComputeSInt64Size, tag);
}
public static FieldCodec<ulong> ForFixed64(uint tag)
{
return new FieldCodec<ulong>(input => input.ReadFixed64(), (output, value) => output.WriteFixed64(value), CodedOutputStream.ComputeFixed64Size, tag);
}
public static FieldCodec<long> ForSFixed64(uint tag)
{
return new FieldCodec<long>(input => input.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), CodedOutputStream.ComputeSFixed64Size, tag);
}
public static FieldCodec<ulong> ForUInt64(uint tag)
{
return new FieldCodec<ulong>(input => input.ReadUInt64(), (output, value) => output.WriteUInt64(value), CodedOutputStream.ComputeUInt64Size, tag);
}
public static FieldCodec<float> ForFloat(uint tag)
{
return new FieldCodec<float>(input => input.ReadFloat(), (output, value) => output.WriteFloat(value), CodedOutputStream.ComputeFloatSize, tag);
}
public static FieldCodec<double> ForDouble(uint tag)
{
return new FieldCodec<double>(input => input.ReadDouble(), (output, value) => output.WriteDouble(value), CodedOutputStream.ComputeDoubleSize, tag);
}
// Enums are tricky. We can probably use expression trees to build these delegates automatically,
// but it's easy to generate the code fdor it.
public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32)
{
return new FieldCodec<T>(input => fromInt32(
input.ReadEnum()),
(output, value) => output.WriteEnum(toInt32(value)),
value => CodedOutputStream.ComputeEnumSize(toInt32(value)), tag);
}
public static FieldCodec<T> ForMessage<T>(uint tag, MessageParser<T> parser) where T : IMessage<T>
{
return new FieldCodec<T>(input => { T message = parser.CreateTemplate(); input.ReadMessage(message); return message; },
(output, value) => output.WriteMessage(value), message => CodedOutputStream.ComputeMessageSize(message), tag);
}
}
/// <summary>
/// An encode/decode pair for a single field. This effectively encapsulates
/// all the information needed to read or write the field value from/to a coded
/// stream.
/// </summary>
/// <remarks>
/// This never writes default values to the stream, and is not currently designed
/// to play well with packed arrays.
/// </remarks>
public sealed class FieldCodec<T>
{
private static readonly Func<T, bool> IsDefault;
private static readonly T Default;
static FieldCodec()
{
if (typeof(T) == typeof(string))
{
Default = (T)(object)"";
IsDefault = CreateDefaultValueCheck<string>(x => x.Length == 0);
}
else if (typeof(T) == typeof(ByteString))
{
Default = (T)(object)ByteString.Empty;
IsDefault = CreateDefaultValueCheck<ByteString>(x => x.Length == 0);
}
else if (!typeof(T).IsValueType)
{
// Default default
IsDefault = CreateDefaultValueCheck<T>(x => x == null);
}
else
{
// Default default
IsDefault = CreateDefaultValueCheck<T>(x => EqualityComparer<T>.Default.Equals(x, default(T)));
}
}
private static Func<T, bool> CreateDefaultValueCheck<TTmp>(Func<TTmp, bool> check)
{
return (Func<T, bool>)(object)check;
}
private readonly Func<CodedInputStream, T> reader;
private readonly Action<CodedOutputStream, T> writer;
private readonly Func<T, int> sizeComputer;
private readonly uint tag;
private readonly int tagSize;
internal FieldCodec(
Func<CodedInputStream, T> reader,
Action<CodedOutputStream, T> writer,
Func<T, int> sizeComputer,
uint tag)
{
this.reader = reader;
this.writer = writer;
this.sizeComputer = sizeComputer;
this.tag = tag;
tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
}
public uint Tag { get { return tag; } }
public T DefaultValue { get { return Default; } }
public void Write(CodedOutputStream output, T value)
{
if (!IsDefault(value))
{
output.WriteTag(tag);
writer(output, value);
}
}
public T Read(CodedInputStream input)
{
return reader(input);
}
public int CalculateSize(T value)
{
return IsDefault(value) ? 0 : sizeComputer(value) + CodedOutputStream.ComputeRawVarint32Size(tag);
}
}
}
......@@ -60,6 +60,7 @@
<Compile Include="CodedOutputStream.cs" />
<Compile Include="Collections\Dictionaries.cs" />
<Compile Include="Collections\Lists.cs" />
<Compile Include="Collections\MapField.cs" />
<Compile Include="Collections\ReadOnlyDictionary.cs" />
<Compile Include="Collections\RepeatedField.cs" />
<Compile Include="Collections\RepeatedFieldExtensions.cs" />
......@@ -84,6 +85,7 @@
<Compile Include="Descriptors\MethodDescriptor.cs" />
<Compile Include="Descriptors\PackageDescriptor.cs" />
<Compile Include="Descriptors\ServiceDescriptor.cs" />
<Compile Include="FieldCodec.cs" />
<Compile Include="FrameworkPortability.cs" />
<Compile Include="Freezable.cs" />
<Compile Include="MessageExtensions.cs" />
......
......@@ -425,6 +425,8 @@ libprotoc_la_SOURCES = \
google/protobuf/compiler/csharp/csharp_generator.cc \
google/protobuf/compiler/csharp/csharp_helpers.cc \
google/protobuf/compiler/csharp/csharp_helpers.h \
google/protobuf/compiler/csharp/csharp_map_field.cc \
google/protobuf/compiler/csharp/csharp_map_field.h \
google/protobuf/compiler/csharp/csharp_message.cc \
google/protobuf/compiler/csharp/csharp_message.h \
google/protobuf/compiler/csharp/csharp_message_field.cc \
......
......@@ -74,6 +74,12 @@ void EnumFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
"}\n");
}
void EnumFieldGenerator::GenerateCodecCode(io::Printer* printer) {
printer->Print(
variables_,
"pb::FieldCodec.ForEnum($tag$, x => (int) x, x => ($type_name$) x)");
}
EnumOneofFieldGenerator::EnumOneofFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal)
: PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal) {
......
......@@ -46,6 +46,7 @@ class EnumFieldGenerator : public PrimitiveFieldGenerator {
EnumFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
~EnumFieldGenerator();
virtual void GenerateCodecCode(io::Printer* printer);
virtual void GenerateParsingCode(io::Printer* printer);
virtual void GenerateSerializationCode(io::Printer* printer);
virtual void GenerateSerializedSizeCode(io::Printer* printer);
......
......@@ -65,6 +65,7 @@ void FieldGeneratorBase::SetCommonFieldVariables(
tag_bytes += ", " + SimpleItoa(tag_array[i]);
}
(*variables)["tag"] = SimpleItoa(tag);
(*variables)["tag_size"] = SimpleItoa(tag_size);
(*variables)["tag_bytes"] = tag_bytes;
......@@ -112,6 +113,11 @@ void FieldGeneratorBase::GenerateFreezingCode(io::Printer* printer) {
// special handling for freezing, so default to not generating any code.
}
void FieldGeneratorBase::GenerateCodecCode(io::Printer* printer) {
// No-op: expect this to be overridden by appropriate types.
// Could fail if we get called here though...
}
void FieldGeneratorBase::AddDeprecatedFlag(io::Printer* printer) {
if (descriptor_->options().deprecated())
{
......@@ -151,12 +157,16 @@ std::string FieldGeneratorBase::name() {
}
std::string FieldGeneratorBase::type_name() {
switch (descriptor_->type()) {
return type_name(descriptor_);
}
std::string FieldGeneratorBase::type_name(const FieldDescriptor* descriptor) {
switch (descriptor->type()) {
case FieldDescriptor::TYPE_ENUM:
return GetClassName(descriptor_->enum_type());
return GetClassName(descriptor->enum_type());
case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_GROUP:
return GetClassName(descriptor_->message_type());
return GetClassName(descriptor->message_type());
case FieldDescriptor::TYPE_DOUBLE:
return "double";
case FieldDescriptor::TYPE_FLOAT:
......
......@@ -49,6 +49,7 @@ class FieldGeneratorBase : public SourceGeneratorBase {
virtual void GenerateCloningCode(io::Printer* printer) = 0;
virtual void GenerateFreezingCode(io::Printer* printer);
virtual void GenerateCodecCode(io::Printer* printer);
virtual void GenerateMembers(io::Printer* printer) = 0;
virtual void GenerateMergingCode(io::Printer* printer) = 0;
virtual void GenerateParsingCode(io::Printer* printer) = 0;
......@@ -76,6 +77,7 @@ class FieldGeneratorBase : public SourceGeneratorBase {
std::string property_name();
std::string name();
std::string type_name();
std::string type_name(const FieldDescriptor* descriptor);
bool has_default_value();
bool is_nullable_type();
std::string default_value();
......
......@@ -46,6 +46,7 @@
#include <google/protobuf/compiler/csharp/csharp_field_base.h>
#include <google/protobuf/compiler/csharp/csharp_enum_field.h>
#include <google/protobuf/compiler/csharp/csharp_map_field.h>
#include <google/protobuf/compiler/csharp/csharp_message_field.h>
#include <google/protobuf/compiler/csharp/csharp_primitive_field.h>
#include <google/protobuf/compiler/csharp/csharp_repeated_enum_field.h>
......@@ -355,7 +356,11 @@ FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
case FieldDescriptor::TYPE_GROUP:
case FieldDescriptor::TYPE_MESSAGE:
if (descriptor->is_repeated()) {
return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal);
if (descriptor->is_map()) {
return new MapFieldGenerator(descriptor, fieldOrdinal);
} else {
return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal);
}
} else {
if (descriptor->containing_oneof()) {
return new MessageOneofFieldGenerator(descriptor, fieldOrdinal);
......
// 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.
#include <sstream>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/plugin.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_map_field.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal)
: FieldGeneratorBase(descriptor, fieldOrdinal) {
}
MapFieldGenerator::~MapFieldGenerator() {
}
void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
const FieldDescriptor* key_descriptor =
descriptor_->message_type()->FindFieldByName("key");
const FieldDescriptor* value_descriptor =
descriptor_->message_type()->FindFieldByName("value");
variables_["key_type_name"] = type_name(key_descriptor);
variables_["value_type_name"] = type_name(value_descriptor);
scoped_ptr<FieldGeneratorBase> key_generator(CreateFieldGenerator(key_descriptor, 1));
scoped_ptr<FieldGeneratorBase> value_generator(CreateFieldGenerator(value_descriptor, 2));
printer->Print(
variables_,
"private static readonly pbc::MapField<$key_type_name$, $value_type_name$>.Codec _map_$name$_codec\n"
" = new pbc::MapField<$key_type_name$, $value_type_name$>.Codec(");
key_generator->GenerateCodecCode(printer);
printer->Print(", ");
value_generator->GenerateCodecCode(printer);
printer->Print(
variables_,
", $tag$);\n"
"private readonly pbc::MapField<$key_type_name$, $value_type_name$> $name$_ = new pbc::MapField<$key_type_name$, $value_type_name$>();\n");
AddDeprecatedFlag(printer);
printer->Print(
variables_,
"public pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n"
" get { return $name$_; }\n"
"}\n");
}
void MapFieldGenerator::GenerateMergingCode(io::Printer* printer) {
printer->Print(
variables_,
"$name$_.Add(other.$name$_);\n");
}
void MapFieldGenerator::GenerateParsingCode(io::Printer* printer) {
printer->Print(
variables_,
"$name$_.AddEntriesFrom(input, _map_$name$_codec);\n");
}
void MapFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
printer->Print(
variables_,
"$name$_.WriteTo(output, _map_$name$_codec);\n");
}
void MapFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
printer->Print(
variables_,
"size += $name$_.CalculateSize(_map_$name$_codec);\n");
}
void MapFieldGenerator::WriteHash(io::Printer* printer) {
printer->Print(
variables_,
"hash ^= $property_name$.GetHashCode();\n");
}
void MapFieldGenerator::WriteEquals(io::Printer* printer) {
printer->Print(
variables_,
"if (!$property_name$.Equals(other.$property_name$)) return false;\n");
}
void MapFieldGenerator::WriteToString(io::Printer* printer) {
/*
variables_["field_name"] = GetFieldName(descriptor_);
printer->Print(
variables_,
"PrintField(\"$field_name$\", has$property_name$, $name$_, writer);\n");*/
}
void MapFieldGenerator::GenerateCloningCode(io::Printer* printer) {
printer->Print(variables_,
"$name$_ = other.$name$_.Clone();\n");
}
void MapFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
printer->Print(variables_,
"$name$_.Freeze();\n");
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
// Protocol Buffers - Google's data interchange format
// Copyright 2008 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.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_MAP_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_MAP_FIELD_H__
#include <string>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/csharp/csharp_field_base.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
class MapFieldGenerator : public FieldGeneratorBase {
public:
MapFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
~MapFieldGenerator();
virtual void GenerateCloningCode(io::Printer* printer);
virtual void GenerateFreezingCode(io::Printer* printer);
virtual void GenerateMembers(io::Printer* printer);
virtual void GenerateMergingCode(io::Printer* printer);
virtual void GenerateParsingCode(io::Printer* printer);
virtual void GenerateSerializationCode(io::Printer* printer);
virtual void GenerateSerializedSizeCode(io::Printer* printer);
virtual void WriteHash(io::Printer* printer);
virtual void WriteEquals(io::Printer* printer);
virtual void WriteToString(io::Printer* printer);
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapFieldGenerator);
};
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_MAP_FIELD_H__
......@@ -268,8 +268,6 @@ void MessageGenerator::Generate(io::Printer* printer) {
"}\n\n");
}
// TODO(jonskeet): Map properties
// Standard methods
GenerateFrameworkMethods(printer);
GenerateMessageSerializationMethods(printer);
......@@ -299,7 +297,6 @@ void MessageGenerator::Generate(io::Printer* printer) {
printer->Outdent();
printer->Print("}\n");
printer->Print("\n");
}
void MessageGenerator::GenerateCloningCode(io::Printer* printer) {
......@@ -408,9 +405,10 @@ void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) {
"}\n\n");
// GetHashCode
// Start with a non-zero value to easily distinguish between null and "empty" messages.
printer->Print(
"public override int GetHashCode() {\n"
" int hash = 0;\n");
" int hash = 1;\n");
printer->Indent();
for (int i = 0; i < descriptor_->field_count(); i++) {
scoped_ptr<FieldGeneratorBase> generator(
......@@ -451,7 +449,7 @@ void MessageGenerator::GenerateMessageSerializationMethods(io::Printer* printer)
}
printer->Print("return size;\n");
printer->Outdent();
printer->Print("}\n");
printer->Print("}\n\n");
}
void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
......@@ -469,7 +467,6 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
"if (other == null) {\n"
" return;\n"
"}\n");
// TODO(jonskeet): Maps?
// Merge non-oneof fields
for (int i = 0; i < descriptor_->field_count(); i++) {
if (!descriptor_->field(i)->containing_oneof()) {
......
......@@ -138,6 +138,12 @@ void MessageFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
"if ($has_property_check$) $property_name$.Freeze();\n");
}
void MessageFieldGenerator::GenerateCodecCode(io::Printer* printer) {
printer->Print(
variables_,
"pb::FieldCodec.ForMessage($tag$, $type_name$.Parser)");
}
MessageOneofFieldGenerator::MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal)
: MessageFieldGenerator(descriptor, fieldOrdinal) {
......
......@@ -46,6 +46,7 @@ class MessageFieldGenerator : public FieldGeneratorBase {
MessageFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
~MessageFieldGenerator();
virtual void GenerateCodecCode(io::Printer* printer);
virtual void GenerateCloningCode(io::Printer* printer);
virtual void GenerateFreezingCode(io::Printer* printer);
virtual void GenerateMembers(io::Printer* printer);
......
......@@ -129,7 +129,7 @@ void PrimitiveFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
"size += $tag_size$ + $fixed_size$;\n",
"fixed_size", SimpleItoa(fixedSize),
"tag_size", variables_["tag_size"]);
}
}
printer->Outdent();
printer->Print("}\n");
}
......@@ -155,6 +155,12 @@ void PrimitiveFieldGenerator::GenerateCloningCode(io::Printer* printer) {
"$name$_ = other.$name$_;\n");
}
void PrimitiveFieldGenerator::GenerateCodecCode(io::Printer* printer) {
printer->Print(
variables_,
"pb::FieldCodec.For$capitalized_type_name$($tag$)");
}
PrimitiveOneofFieldGenerator::PrimitiveOneofFieldGenerator(
const FieldDescriptor* descriptor, int fieldOrdinal)
: PrimitiveFieldGenerator(descriptor, fieldOrdinal) {
......
......@@ -46,6 +46,7 @@ class PrimitiveFieldGenerator : public FieldGeneratorBase {
PrimitiveFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
~PrimitiveFieldGenerator();
virtual void GenerateCodecCode(io::Printer* printer);
virtual void GenerateCloningCode(io::Printer* printer);
virtual void GenerateMembers(io::Printer* printer);
virtual void GenerateMergingCode(io::Printer* printer);
......
// Protocol Buffers - Google's data interchange format
// Copyright 2008 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.
// This file is mostly equivalent to map_unittest.proto, but imports
// unittest_proto3.proto instead of unittest.proto, so that it only
// uses proto3 messages. This makes it suitable for testing
// implementations which only support proto3.
// The TestRequiredMessageMap message has been removed as there are no
// required fields in proto3.
syntax = "proto3";
option cc_enable_arenas = true;
option csharp_namespace = "Google.Protobuf.TestProtos";
import "google/protobuf/unittest_proto3.proto";
// We don't put this in a package within proto2 because we need to make sure
// that the generated code doesn't depend on being in the proto2 namespace.
// In map_test_util.h we do "using namespace unittest = protobuf_unittest".
package protobuf_unittest;
// Tests maps.
message TestMap {
map<int32 , int32 > map_int32_int32 = 1;
map<int64 , int64 > map_int64_int64 = 2;
map<uint32 , uint32 > map_uint32_uint32 = 3;
map<uint64 , uint64 > map_uint64_uint64 = 4;
map<sint32 , sint32 > map_sint32_sint32 = 5;
map<sint64 , sint64 > map_sint64_sint64 = 6;
map<fixed32 , fixed32 > map_fixed32_fixed32 = 7;
map<fixed64 , fixed64 > map_fixed64_fixed64 = 8;
map<sfixed32, sfixed32> map_sfixed32_sfixed32 = 9;
map<sfixed64, sfixed64> map_sfixed64_sfixed64 = 10;
map<int32 , float > map_int32_float = 11;
map<int32 , double > map_int32_double = 12;
map<bool , bool > map_bool_bool = 13;
map<string , string > map_string_string = 14;
map<int32 , bytes > map_int32_bytes = 15;
map<int32 , MapEnum > map_int32_enum = 16;
map<int32 , ForeignMessage> map_int32_foreign_message = 17;
}
message TestMapSubmessage {
TestMap test_map = 1;
}
message TestMessageMap {
map<int32, TestAllTypes> map_int32_message = 1;
}
// Two map fields share the same entry default instance.
message TestSameTypeMap {
map<int32, int32> map1 = 1;
map<int32, int32> map2 = 2;
}
enum MapEnum {
MAP_ENUM_FOO = 0;
MAP_ENUM_BAR = 1;
MAP_ENUM_BAZ = 2;
}
message TestArenaMap {
map<int32 , int32 > map_int32_int32 = 1;
map<int64 , int64 > map_int64_int64 = 2;
map<uint32 , uint32 > map_uint32_uint32 = 3;
map<uint64 , uint64 > map_uint64_uint64 = 4;
map<sint32 , sint32 > map_sint32_sint32 = 5;
map<sint64 , sint64 > map_sint64_sint64 = 6;
map<fixed32 , fixed32 > map_fixed32_fixed32 = 7;
map<fixed64 , fixed64 > map_fixed64_fixed64 = 8;
map<sfixed32, sfixed32> map_sfixed32_sfixed32 = 9;
map<sfixed64, sfixed64> map_sfixed64_sfixed64 = 10;
map<int32 , float > map_int32_float = 11;
map<int32 , double > map_int32_double = 12;
map<bool , bool > map_bool_bool = 13;
map<int32 , MapEnum > map_int32_enum = 14;
map<int32 , ForeignMessage> map_int32_foreign_message = 15;
}
// Previously, message containing enum called Type cannot be used as value of
// map field.
message MessageContainingEnumCalledType {
enum Type {
TYPE_FOO = 0;
}
map<int32, MessageContainingEnumCalledType> type = 1;
}
// Previously, message cannot contain map field called "entry".
message MessageContainingMapCalledEntry {
map<int32, int32> entry = 1;
}
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