Merge pull request #543 from jskeet/proto3-map

Proto3 map support for C#
pull/550/head
Jon Skeet 10 years ago
commit 6b01539dfc
  1. 1
      cmake/libprotoc.cmake
  2. 2
      csharp/generate_protos.sh
  3. 9
      csharp/src/AddressBook/Addressbook.cs
  4. 354
      csharp/src/ProtocolBuffers.Test/Collections/MapFieldTest.cs
  5. 3
      csharp/src/ProtocolBuffers.Test/Collections/RepeatedFieldTest.cs
  6. 63
      csharp/src/ProtocolBuffers.Test/EqualityTester.cs
  7. 149
      csharp/src/ProtocolBuffers.Test/FieldCodecTest.cs
  8. 210
      csharp/src/ProtocolBuffers.Test/GeneratedMessageTest.cs
  9. 10
      csharp/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj
  10. 6938
      csharp/src/ProtocolBuffers.Test/TestProtos/MapUnittestProto3.cs
  11. 3
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestImportProto3.cs
  12. 3
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestImportPublicProto3.cs
  13. 12
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestIssues.cs
  14. 105
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestProto3.cs
  15. 28
      csharp/src/ProtocolBuffers/CodedInputStream.cs
  16. 8
      csharp/src/ProtocolBuffers/CodedOutputStream.cs
  17. 433
      csharp/src/ProtocolBuffers/Collections/MapField.cs
  18. 2
      csharp/src/ProtocolBuffers/Collections/RepeatedField.cs
  19. 66
      csharp/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs
  20. 188
      csharp/src/ProtocolBuffers/FieldCodec.cs
  21. 2
      csharp/src/ProtocolBuffers/ProtocolBuffers.csproj
  22. 2
      src/Makefile.am
  23. 6
      src/google/protobuf/compiler/csharp/csharp_enum_field.cc
  24. 1
      src/google/protobuf/compiler/csharp/csharp_enum_field.h
  25. 16
      src/google/protobuf/compiler/csharp/csharp_field_base.cc
  26. 2
      src/google/protobuf/compiler/csharp/csharp_field_base.h
  27. 7
      src/google/protobuf/compiler/csharp/csharp_helpers.cc
  28. 141
      src/google/protobuf/compiler/csharp/csharp_map_field.cc
  29. 71
      src/google/protobuf/compiler/csharp/csharp_map_field.h
  30. 9
      src/google/protobuf/compiler/csharp/csharp_message.cc
  31. 6
      src/google/protobuf/compiler/csharp/csharp_message_field.cc
  32. 1
      src/google/protobuf/compiler/csharp/csharp_message_field.h
  33. 8
      src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
  34. 1
      src/google/protobuf/compiler/csharp/csharp_primitive_field.h
  35. 120
      src/google/protobuf/map_unittest_proto3.proto

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

@ -0,0 +1,354 @@
#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 Google.Protobuf.TestProtos;
using NUnit.Framework;
namespace Google.Protobuf.Collections
{
/// <summary>
/// Tests for MapField which aren't reliant on the encoded format -
/// tests for serialization/deserialization are part of GeneratedMessageTest.
/// </summary>
public class MapFieldTest
{
// Protobuf-specific tests
[Test]
public void Freeze_FreezesMessages()
{
var message = new ForeignMessage { C = 20 };
var map = new MapField<string, ForeignMessage> { { "x", message } };
map.Freeze();
Assert.IsTrue(message.IsFrozen);
}
[Test]
public void Freeze_PreventsMutation()
{
var map = new MapField<string, string>();
map.Freeze();
Assert.IsTrue(map.IsFrozen);
Assert.IsTrue(map.IsReadOnly);
ICollection<KeyValuePair<string, string>> collection = map;
Assert.Throws<InvalidOperationException>(() => map["x"] = "y");
Assert.Throws<InvalidOperationException>(() => map.Add("x", "y"));
Assert.Throws<InvalidOperationException>(() => map.Remove("x"));
Assert.Throws<InvalidOperationException>(() => map.Clear());
Assert.Throws<InvalidOperationException>(() => collection.Add(NewKeyValuePair("x", "y")));
Assert.Throws<InvalidOperationException>(() => collection.Remove(NewKeyValuePair("x", "y")));
}
[Test]
public void Clone_ReturnsNonFrozen()
{
var map = new MapField<string, string>();
map.Freeze();
var clone = map.Clone();
clone.Add("x", "y");
}
[Test]
public void Clone_ClonesMessages()
{
var message = new ForeignMessage { C = 20 };
var map = new MapField<string, ForeignMessage> { { "x", message } };
var clone = map.Clone();
map["x"].C = 30;
Assert.AreEqual(20, clone["x"].C);
}
[Test]
public void Add_ForbidsNullKeys()
{
var map = new MapField<string, ForeignMessage>();
Assert.Throws<ArgumentNullException>(() => map.Add(null, new ForeignMessage()));
}
[Test]
public void Add_AcceptsNullMessageValues()
{
var map = new MapField<string, ForeignMessage>();
map.Add("missing", null);
Assert.IsNull(map["missing"]);
}
[Test]
public void Add_ForbidsNullStringValues()
{
var map = new MapField<string, string>();
Assert.Throws<ArgumentNullException>(() => map.Add("missing", null));
}
[Test]
public void Add_ForbidsNullByteStringValues()
{
var map = new MapField<string, ByteString>();
Assert.Throws<ArgumentNullException>(() => map.Add("missing", null));
}
[Test]
public void Indexer_ForbidsNullKeys()
{
var map = new MapField<string, ForeignMessage>();
Assert.Throws<ArgumentNullException>(() => map[null] = new ForeignMessage());
}
[Test]
public void Indexer_AcceptsNullMessageValues()
{
var map = new MapField<string, ForeignMessage>();
map["missing"] = null;
Assert.IsNull(map["missing"]);
}
[Test]
public void Indexer_ForbidsNullStringValues()
{
var map = new MapField<string, string>();
Assert.Throws<ArgumentNullException>(() => map["missing"] = null);
}
[Test]
public void Indexer_ForbidsNullByteStringValues()
{
var map = new MapField<string, ByteString>();
Assert.Throws<ArgumentNullException>(() => map["missing"] = null);
}
[Test]
public void AddPreservesInsertionOrder()
{
var map = new MapField<string, string>();
map.Add("a", "v1");
map.Add("b", "v2");
map.Add("c", "v3");
map.Remove("b");
map.Add("d", "v4");
CollectionAssert.AreEqual(new[] { "a", "c", "d" }, map.Keys);
CollectionAssert.AreEqual(new[] { "v1", "v3", "v4" }, map.Values);
}
[Test]
public void EqualityIsOrderInsensitive()
{
var map1 = new MapField<string, string>();
map1.Add("a", "v1");
map1.Add("b", "v2");
var map2 = new MapField<string, string>();
map2.Add("b", "v2");
map2.Add("a", "v1");
EqualityTester.AssertEquality(map1, map2);
}
[Test]
public void EqualityIsKeySensitive()
{
var map1 = new MapField<string, string>();
map1.Add("first key", "v1");
map1.Add("second key", "v2");
var map2 = new MapField<string, string>();
map2.Add("third key", "v1");
map2.Add("fourth key", "v2");
EqualityTester.AssertInequality(map1, map2);
}
[Test]
public void EqualityIsValueSensitive()
{
// Note: Without some care, it's a little easier than one might
// hope to see hash collisions, but only in some environments...
var map1 = new MapField<string, string>();
map1.Add("a", "first value");
map1.Add("b", "second value");
var map2 = new MapField<string, string>();
map2.Add("a", "third value");
map2.Add("b", "fourth value");
EqualityTester.AssertInequality(map1, map2);
}
[Test]
public void EqualityHandlesNullValues()
{
var map1 = new MapField<string, ForeignMessage>();
map1.Add("a", new ForeignMessage { C = 10 });
map1.Add("b", null);
var map2 = new MapField<string, ForeignMessage>();
map2.Add("a", new ForeignMessage { C = 10 });
map2.Add("b", null);
EqualityTester.AssertEquality(map1, map2);
// Check the null value isn't ignored entirely...
Assert.IsTrue(map1.Remove("b"));
EqualityTester.AssertInequality(map1, map2);
map1.Add("b", new ForeignMessage());
EqualityTester.AssertInequality(map1, map2);
map1["b"] = null;
EqualityTester.AssertEquality(map1, map2);
}
[Test]
public void Add_Dictionary()
{
var map1 = new MapField<string, string>
{
{ "x", "y" },
{ "a", "b" }
};
var map2 = new MapField<string, string>
{
{ "before", "" },
map1,
{ "after", "" }
};
var expected = new MapField<string, string>
{
{ "before", "" },
{ "x", "y" },
{ "a", "b" },
{ "after", "" }
};
Assert.AreEqual(expected, map2);
CollectionAssert.AreEqual(new[] { "before", "x", "a", "after" }, map2.Keys);
}
// General IDictionary<TKey, TValue> behavior tests
[Test]
public void Add_KeyAlreadyExists()
{
var map = new MapField<string, string>();
map.Add("foo", "bar");
Assert.Throws<ArgumentException>(() => map.Add("foo", "baz"));
}
[Test]
public void Add_Pair()
{
var map = new MapField<string, string>();
ICollection<KeyValuePair<string, string>> collection = map;
collection.Add(NewKeyValuePair("x", "y"));
Assert.AreEqual("y", map["x"]);
Assert.Throws<ArgumentException>(() => collection.Add(NewKeyValuePair("x", "z")));
}
[Test]
public void Contains_Pair()
{
var map = new MapField<string, string> { { "x", "y" } };
ICollection<KeyValuePair<string, string>> collection = map;
Assert.IsTrue(collection.Contains(NewKeyValuePair("x", "y")));
Assert.IsFalse(collection.Contains(NewKeyValuePair("x", "z")));
Assert.IsFalse(collection.Contains(NewKeyValuePair("z", "y")));
}
[Test]
public void Remove_Key()
{
var map = new MapField<string, string>();
map.Add("foo", "bar");
Assert.AreEqual(1, map.Count);
Assert.IsFalse(map.Remove("missing"));
Assert.AreEqual(1, map.Count);
Assert.IsTrue(map.Remove("foo"));
Assert.AreEqual(0, map.Count);
}
[Test]
public void Remove_Pair()
{
var map = new MapField<string, string>();
map.Add("foo", "bar");
ICollection<KeyValuePair<string, string>> collection = map;
Assert.AreEqual(1, map.Count);
Assert.IsFalse(collection.Remove(NewKeyValuePair("wrong key", "bar")));
Assert.AreEqual(1, map.Count);
Assert.IsFalse(collection.Remove(NewKeyValuePair("foo", "wrong value")));
Assert.AreEqual(1, map.Count);
Assert.IsTrue(collection.Remove(NewKeyValuePair("foo", "bar")));
Assert.AreEqual(0, map.Count);
Assert.Throws<ArgumentException>(() => collection.Remove(new KeyValuePair<string, string>(null, "")));
}
[Test]
public void CopyTo_Pair()
{
var map = new MapField<string, string>();
map.Add("foo", "bar");
ICollection<KeyValuePair<string, string>> collection = map;
KeyValuePair<string, string>[] array = new KeyValuePair<string, string>[3];
collection.CopyTo(array, 1);
Assert.AreEqual(NewKeyValuePair("foo", "bar"), array[1]);
}
[Test]
public void Clear()
{
var map = new MapField<string, string> { { "x", "y" } };
Assert.AreEqual(1, map.Count);
map.Clear();
Assert.AreEqual(0, map.Count);
map.Add("x", "y");
Assert.AreEqual(1, map.Count);
}
[Test]
public void Indexer_Get()
{
var map = new MapField<string, string> { { "x", "y" } };
Assert.AreEqual("y", map["x"]);
Assert.Throws<KeyNotFoundException>(() => { var ignored = map["z"]; });
}
[Test]
public void Indexer_Set()
{
var map = new MapField<string, string>();
map["x"] = "y";
Assert.AreEqual("y", map["x"]);
map["x"] = "z"; // This won't throw, unlike Add.
Assert.AreEqual("z", map["x"]);
}
private static KeyValuePair<TKey, TValue> NewKeyValuePair<TKey, TValue>(TKey key, TValue value)
{
return new KeyValuePair<TKey, TValue>(key, value);
}
}
}

@ -1,10 +1,9 @@
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
{

@ -0,0 +1,63 @@
#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());
}
}
}

@ -0,0 +1,149 @@
#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;
}
}
}
}

@ -1,4 +1,5 @@
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;

@ -995,7 +995,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (SingleInt32 != 0) hash ^= SingleInt32.GetHashCode();
if (SingleInt64 != 0L) hash ^= SingleInt64.GetHashCode();
if (SingleUint32 != 0) hash ^= SingleUint32.GetHashCode();
@ -1472,6 +1472,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestAllTypes other) {
if (other == null) {
return;
@ -1905,7 +1906,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Bb != 0) hash ^= Bb.GetHashCode();
return hash;
}
@ -1924,6 +1925,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(NestedMessage other) {
if (other == null) {
return;
@ -2043,7 +2045,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (child_ != null) hash ^= Child.GetHashCode();
if (payload_ != null) hash ^= Payload.GetHashCode();
hash ^= repeatedChild_.GetHashCode();
@ -2080,6 +2082,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(NestedTestAllTypes other) {
if (other == null) {
return;
@ -2196,7 +2199,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (DeprecatedInt32 != 0) hash ^= DeprecatedInt32.GetHashCode();
return hash;
}
@ -2215,6 +2218,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestDeprecatedFields other) {
if (other == null) {
return;
@ -2306,7 +2310,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (C != 0) hash ^= C.GetHashCode();
return hash;
}
@ -2325,6 +2329,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(ForeignMessage other) {
if (other == null) {
return;
@ -2404,7 +2409,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
return hash;
}
@ -2415,6 +2420,7 @@ namespace Google.Protobuf.TestProtos {
int size = 0;
return size;
}
public void MergeFrom(TestReservedFields other) {
if (other == null) {
return;
@ -2500,7 +2506,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (foreignNested_ != null) hash ^= ForeignNested.GetHashCode();
return hash;
}
@ -2519,6 +2525,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestForeignNested other) {
if (other == null) {
return;
@ -2628,7 +2635,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (A != 0) hash ^= A.GetHashCode();
if (Bb != 0) hash ^= Bb.GetHashCode();
return hash;
@ -2655,6 +2662,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestReallyLargeTagNumber other) {
if (other == null) {
return;
@ -2766,7 +2774,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (a_ != null) hash ^= A.GetHashCode();
if (I != 0) hash ^= I.GetHashCode();
return hash;
@ -2793,6 +2801,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestRecursiveMessage other) {
if (other == null) {
return;
@ -2898,7 +2907,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (bb_ != null) hash ^= Bb.GetHashCode();
return hash;
}
@ -2917,6 +2926,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestMutualRecursionA other) {
if (other == null) {
return;
@ -3027,7 +3037,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (a_ != null) hash ^= A.GetHashCode();
if (OptionalInt32 != 0) hash ^= OptionalInt32.GetHashCode();
return hash;
@ -3054,6 +3064,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestMutualRecursionB other) {
if (other == null) {
return;
@ -3231,7 +3242,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (PrimitiveField != 0) hash ^= PrimitiveField.GetHashCode();
if (StringField.Length != 0) hash ^= StringField.GetHashCode();
if (EnumField != global::Google.Protobuf.TestProtos.ForeignEnum.FOREIGN_UNSPECIFIED) hash ^= EnumField.GetHashCode();
@ -3322,6 +3333,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestCamelCaseFieldNames other) {
if (other == null) {
return;
@ -3499,7 +3511,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (MyString.Length != 0) hash ^= MyString.GetHashCode();
if (MyInt != 0L) hash ^= MyInt.GetHashCode();
if (MyFloat != 0F) hash ^= MyFloat.GetHashCode();
@ -3542,6 +3554,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestFieldOrderings other) {
if (other == null) {
return;
@ -3673,7 +3686,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Oo != 0L) hash ^= Oo.GetHashCode();
if (Bb != 0) hash ^= Bb.GetHashCode();
return hash;
@ -3700,6 +3713,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(NestedMessage other) {
if (other == null) {
return;
@ -3803,7 +3817,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (SparseEnum != global::Google.Protobuf.TestProtos.TestSparseEnum.TEST_SPARSE_ENUM_UNSPECIFIED) hash ^= SparseEnum.GetHashCode();
return hash;
}
@ -3822,6 +3836,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(SparseEnumMessage other) {
if (other == null) {
return;
@ -3913,7 +3928,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Data.Length != 0) hash ^= Data.GetHashCode();
return hash;
}
@ -3932,6 +3947,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(OneString other) {
if (other == null) {
return;
@ -4020,7 +4036,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
hash ^= data_.GetHashCode();
return hash;
}
@ -4043,6 +4059,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(MoreString other) {
if (other == null) {
return;
@ -4132,7 +4149,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Data.Length != 0) hash ^= Data.GetHashCode();
return hash;
}
@ -4151,6 +4168,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(OneBytes other) {
if (other == null) {
return;
@ -4242,7 +4260,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Data.Length != 0) hash ^= Data.GetHashCode();
return hash;
}
@ -4261,6 +4279,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(MoreBytes other) {
if (other == null) {
return;
@ -4352,7 +4371,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Data != 0) hash ^= Data.GetHashCode();
return hash;
}
@ -4371,6 +4390,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(Int32Message other) {
if (other == null) {
return;
@ -4462,7 +4482,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Data != 0) hash ^= Data.GetHashCode();
return hash;
}
@ -4481,6 +4501,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(Uint32Message other) {
if (other == null) {
return;
@ -4572,7 +4593,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Data != 0L) hash ^= Data.GetHashCode();
return hash;
}
@ -4591,6 +4612,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(Int64Message other) {
if (other == null) {
return;
@ -4682,7 +4704,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Data != 0UL) hash ^= Data.GetHashCode();
return hash;
}
@ -4701,6 +4723,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(Uint64Message other) {
if (other == null) {
return;
@ -4792,7 +4815,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Data != false) hash ^= Data.GetHashCode();
return hash;
}
@ -4811,6 +4834,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(BoolMessage other) {
if (other == null) {
return;
@ -4954,7 +4978,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (fooCase_ == FooOneofCase.FooInt) hash ^= FooInt.GetHashCode();
if (fooCase_ == FooOneofCase.FooString) hash ^= FooString.GetHashCode();
if (fooCase_ == FooOneofCase.FooMessage) hash ^= FooMessage.GetHashCode();
@ -4989,6 +5013,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestOneof other) {
if (other == null) {
return;
@ -5216,7 +5241,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
hash ^= packedInt32_.GetHashCode();
hash ^= packedInt64_.GetHashCode();
hash ^= packedUint32_.GetHashCode();
@ -5395,6 +5420,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestPackedTypes other) {
if (other == null) {
return;
@ -5677,7 +5703,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
hash ^= unpackedInt32_.GetHashCode();
hash ^= unpackedInt64_.GetHashCode();
hash ^= unpackedUint32_.GetHashCode();
@ -5842,6 +5868,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestUnpackedTypes other) {
if (other == null) {
return;
@ -6052,7 +6079,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
hash ^= repeatedFixed32_.GetHashCode();
hash ^= repeatedInt32_.GetHashCode();
hash ^= repeatedFixed64_.GetHashCode();
@ -6135,6 +6162,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestRepeatedScalarDifferentTagSizes other) {
if (other == null) {
return;
@ -6255,7 +6283,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (A.Length != 0) hash ^= A.GetHashCode();
return hash;
}
@ -6274,6 +6302,7 @@ namespace Google.Protobuf.TestProtos {
}
return size;
}
public void MergeFrom(TestCommentInjectionMessage other) {
if (other == null) {
return;
@ -6353,7 +6382,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
return hash;
}
@ -6364,6 +6393,7 @@ namespace Google.Protobuf.TestProtos {
int size = 0;
return size;
}
public void MergeFrom(FooRequest other) {
if (other == null) {
return;
@ -6436,7 +6466,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
return hash;
}
@ -6447,6 +6477,7 @@ namespace Google.Protobuf.TestProtos {
int size = 0;
return size;
}
public void MergeFrom(FooResponse other) {
if (other == null) {
return;
@ -6519,7 +6550,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
return hash;
}
@ -6530,6 +6561,7 @@ namespace Google.Protobuf.TestProtos {
int size = 0;
return size;
}
public void MergeFrom(FooClientMessage other) {
if (other == null) {
return;
@ -6602,7 +6634,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
return hash;
}
@ -6613,6 +6645,7 @@ namespace Google.Protobuf.TestProtos {
int size = 0;
return size;
}
public void MergeFrom(FooServerMessage other) {
if (other == null) {
return;
@ -6685,7 +6718,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
return hash;
}
@ -6696,6 +6729,7 @@ namespace Google.Protobuf.TestProtos {
int size = 0;
return size;
}
public void MergeFrom(BarRequest other) {
if (other == null) {
return;
@ -6768,7 +6802,7 @@ namespace Google.Protobuf.TestProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
return hash;
}
@ -6779,6 +6813,7 @@ namespace Google.Protobuf.TestProtos {
int size = 0;
return size;
}
public void MergeFrom(BarResponse 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>

@ -0,0 +1,433 @@
#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;
using System.Collections.Generic;
using System.Linq;
namespace Google.Protobuf.Collections
{
/// <summary>
/// Representation of a map field in a Protocol Buffer message.
/// </summary>
/// <remarks>
/// This implementation preserves insertion order for simplicity of testing
/// code using maps fields. Overwriting an existing entry does not change the
/// position of that entry within the map. Equality is not order-sensitive.
/// For string keys, the equality comparison is provided by <see cref="StringComparer.Ordinal"/>.
/// </remarks>
/// <typeparam name="TKey">Key type in the map. Must be a type supported by Protocol Buffer map keys.</typeparam>
/// <typeparam name="TValue">Value type in the map. Must be a type supported by Protocol Buffers.</typeparam>
public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IFreezable, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>
{
// TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
private bool frozen;
private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>> map =
new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>();
private readonly LinkedList<KeyValuePair<TKey, TValue>> list = new LinkedList<KeyValuePair<TKey, TValue>>();
public MapField<TKey, TValue> Clone()
{
var clone = new MapField<TKey, TValue>();
// Keys are never cloneable. Values might be.
if (typeof(IDeepCloneable<TValue>).IsAssignableFrom(typeof(TValue)))
{
foreach (var pair in list)
{
clone.Add(pair.Key, pair.Value == null ? pair.Value : ((IDeepCloneable<TValue>) pair.Value).Clone());
}
}
else
{
// Nothing is cloneable, so we don't need to worry.
clone.Add(this);
}
return clone;
}
public void Add(TKey key, TValue value)
{
// Validation of arguments happens in ContainsKey and the indexer
if (ContainsKey(key))
{
throw new ArgumentException("Key already exists in map", "key");
}
this[key] = value;
}
public bool ContainsKey(TKey key)
{
ThrowHelper.ThrowIfNull(key, "key");
return map.ContainsKey(key);
}
public bool Remove(TKey key)
{
this.CheckMutable();
ThrowHelper.ThrowIfNull(key, "key");
LinkedListNode<KeyValuePair<TKey, TValue>> node;
if (map.TryGetValue(key, out node))
{
map.Remove(key);
node.List.Remove(node);
return true;
}
else
{
return false;
}
}
public bool TryGetValue(TKey key, out TValue value)
{
LinkedListNode<KeyValuePair<TKey, TValue>> node;
if (map.TryGetValue(key, out node))
{
value = node.Value.Value;
return true;
}
else
{
value = default(TValue);
return false;
}
}
public TValue this[TKey key]
{
get
{
ThrowHelper.ThrowIfNull(key, "key");
TValue value;
if (TryGetValue(key, out value))
{
return value;
}
throw new KeyNotFoundException();
}
set
{
ThrowHelper.ThrowIfNull(key, "key");
if (value == null && (typeof(TValue) == typeof(ByteString) || typeof(TValue) == typeof(string)))
{
ThrowHelper.ThrowIfNull(value, "value");
}
this.CheckMutable();
LinkedListNode<KeyValuePair<TKey, TValue>> node;
var pair = new KeyValuePair<TKey, TValue>(key, value);
if (map.TryGetValue(key, out node))
{
node.Value = pair;
}
else
{
node = list.AddLast(pair);
map[key] = node;
}
}
}
// TODO: Make these views?
public ICollection<TKey> Keys { get { return list.Select(t => t.Key).ToList(); } }
public ICollection<TValue> Values { get { return list.Select(t => t.Value).ToList(); } }
public void Add(IDictionary<TKey, TValue> entries)
{
ThrowHelper.ThrowIfNull(entries, "entries");
foreach (var pair in entries)
{
Add(pair.Key, pair.Value);
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
public void Clear()
{
this.CheckMutable();
list.Clear();
map.Clear();
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
TValue value;
return TryGetValue(item.Key, out value)
&& EqualityComparer<TValue>.Default.Equals(item.Value, value);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
list.CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
this.CheckMutable();
if (item.Key == null)
{
throw new ArgumentException("Key is null", "item");
}
LinkedListNode<KeyValuePair<TKey, TValue>> node;
if (map.TryGetValue(item.Key, out node) &&
EqualityComparer<TValue>.Default.Equals(item.Value, node.Value.Value))
{
map.Remove(item.Key);
node.List.Remove(node);
return true;
}
else
{
return false;
}
}
public int Count { get { return list.Count; } }
public bool IsReadOnly { get { return frozen; } }
public void Freeze()
{
if (IsFrozen)
{
return;
}
frozen = true;
// Only values can be frozen, as all the key types are simple.
// Everything can be done in-place, as we're just freezing objects.
if (typeof(IFreezable).IsAssignableFrom(typeof(TValue)))
{
for (var node = list.First; node != null; node = node.Next)
{
var pair = node.Value;
IFreezable freezableValue = pair.Value as IFreezable;
if (freezableValue != null)
{
freezableValue.Freeze();
}
}
}
}
public bool IsFrozen { get { return frozen; } }
public override bool Equals(object other)
{
return Equals(other as MapField<TKey, TValue>);
}
public override int GetHashCode()
{
var valueComparer = EqualityComparer<TValue>.Default;
int hash = 0;
foreach (var pair in list)
{
hash ^= pair.Key.GetHashCode() * 31 + valueComparer.GetHashCode(pair.Value);
}
return hash;
}
public bool Equals(MapField<TKey, TValue> other)
{
if (other == null)
{
return false;
}
if (other == this)
{
return true;
}
if (other.Count != this.Count)
{
return false;
}
var valueComparer = EqualityComparer<TValue>.Default;
foreach (var pair in this)
{
TValue value;
if (!other.TryGetValue(pair.Key, out value))
{
return false;
}
if (!valueComparer.Equals(value, pair.Value))
{
return false;
}
}
return true;
}
/// <summary>
/// Adds entries to the map from the given stream.
/// </summary>
/// <remarks>
/// It is assumed that the stream is initially positioned after the tag specified by the codec.
/// This method will continue reading entries from the stream until the end is reached, or
/// a different tag is encountered.
/// </remarks>
/// <param name="input">Stream to read from</param>
/// <param name="codec">Codec describing how the key/value pairs are encoded</param>
public void AddEntriesFrom(CodedInputStream input, Codec codec)
{
var adapter = new Codec.MessageAdapter(codec);
do
{
adapter.Reset();
input.ReadMessage(adapter);
this[adapter.Key] = adapter.Value;
} while (input.MaybeConsumeTag(codec.MapTag));
}
public void WriteTo(CodedOutputStream output, Codec codec)
{
var message = new Codec.MessageAdapter(codec);
foreach (var entry in list)
{
message.Key = entry.Key;
message.Value = entry.Value;
output.WriteTag(codec.MapTag);
output.WriteMessage(message);
}
}
public int CalculateSize(Codec codec)
{
var message = new Codec.MessageAdapter(codec);
int size = 0;
foreach (var entry in list)
{
message.Key = entry.Key;
message.Value = entry.Value;
size += CodedOutputStream.ComputeRawVarint32Size(codec.MapTag);
size += CodedOutputStream.ComputeMessageSize(message);
}
return size;
}
/// <summary>
/// A codec for a specific map field. This contains all the information required to encoded and
/// decode the nested messages.
/// </summary>
public sealed class Codec
{
private readonly FieldCodec<TKey> keyCodec;
private readonly FieldCodec<TValue> valueCodec;
private readonly uint mapTag;
public Codec(FieldCodec<TKey> keyCodec, FieldCodec<TValue> valueCodec, uint mapTag)
{
this.keyCodec = keyCodec;
this.valueCodec = valueCodec;
this.mapTag = mapTag;
}
/// <summary>
/// The tag used in the enclosing message to indicate map entries.
/// </summary>
internal uint MapTag { get { return mapTag; } }
/// <summary>
/// A mutable message class, used for parsing and serializing. This
/// delegates the work to a codec, but implements the <see cref="IMessage"/> interface
/// for interop with <see cref="CodedInputStream"/> and <see cref="CodedOutputStream"/>.
/// This is nested inside Codec as it's tightly coupled to the associated codec,
/// and it's simpler if it has direct access to all its fields.
/// </summary>
internal class MessageAdapter : IMessage
{
private readonly Codec codec;
internal TKey Key { get; set; }
internal TValue Value { get; set; }
internal MessageAdapter(Codec codec)
{
this.codec = codec;
}
internal void Reset()
{
Key = codec.keyCodec.DefaultValue;
Value = codec.valueCodec.DefaultValue;
}
public void MergeFrom(CodedInputStream input)
{
uint tag;
while (input.ReadTag(out tag))
{
if (tag == 0)
{
throw InvalidProtocolBufferException.InvalidTag();
}
if (tag == codec.keyCodec.Tag)
{
Key = codec.keyCodec.Read(input);
}
else if (tag == codec.valueCodec.Tag)
{
Value = codec.valueCodec.Read(input);
}
else if (WireFormat.IsEndGroupTag(tag))
{
// TODO(jonskeet): Do we need this? (Given that we don't support groups...)
return;
}
}
}
public void WriteTo(CodedOutputStream output)
{
codec.keyCodec.Write(output, Key);
codec.valueCodec.Write(output, Value);
}
public int CalculateSize()
{
return codec.keyCodec.CalculateSize(Key) + codec.valueCodec.CalculateSize(Value);
}
}
}
}
}

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

@ -335,7 +335,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
hash ^= file_.GetHashCode();
return hash;
}
@ -356,6 +356,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(FileDescriptorSet other) {
if (other == null) {
return;
@ -558,7 +559,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Name.Length != 0) hash ^= Name.GetHashCode();
if (Package.Length != 0) hash ^= Package.GetHashCode();
hash ^= dependency_.GetHashCode();
@ -685,6 +686,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(FileDescriptorProto other) {
if (other == null) {
return;
@ -938,7 +940,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Name.Length != 0) hash ^= Name.GetHashCode();
hash ^= field_.GetHashCode();
hash ^= extension_.GetHashCode();
@ -1047,6 +1049,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(DescriptorProto other) {
if (other == null) {
return;
@ -1204,7 +1207,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Start != 0) hash ^= Start.GetHashCode();
if (End != 0) hash ^= End.GetHashCode();
return hash;
@ -1231,6 +1234,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(ExtensionRange other) {
if (other == null) {
return;
@ -1341,7 +1345,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Start != 0) hash ^= Start.GetHashCode();
if (End != 0) hash ^= End.GetHashCode();
return hash;
@ -1368,6 +1372,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(ReservedRange other) {
if (other == null) {
return;
@ -1568,7 +1573,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Name.Length != 0) hash ^= Name.GetHashCode();
if (Number != 0) hash ^= Number.GetHashCode();
if (Label != global::Google.Protobuf.DescriptorProtos.FieldDescriptorProto.Types.Label.LABEL_OPTIONAL) hash ^= Label.GetHashCode();
@ -1651,6 +1656,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(FieldDescriptorProto other) {
if (other == null) {
return;
@ -1837,7 +1843,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Name.Length != 0) hash ^= Name.GetHashCode();
return hash;
}
@ -1856,6 +1862,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(OneofDescriptorProto other) {
if (other == null) {
return;
@ -1969,7 +1976,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Name.Length != 0) hash ^= Name.GetHashCode();
hash ^= value_.GetHashCode();
if (options_ != null) hash ^= Options.GetHashCode();
@ -2006,6 +2013,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(EnumDescriptorProto other) {
if (other == null) {
return;
@ -2140,7 +2148,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Name.Length != 0) hash ^= Name.GetHashCode();
if (Number != 0) hash ^= Number.GetHashCode();
if (options_ != null) hash ^= Options.GetHashCode();
@ -2175,6 +2183,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(EnumValueDescriptorProto other) {
if (other == null) {
return;
@ -2308,7 +2317,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Name.Length != 0) hash ^= Name.GetHashCode();
hash ^= method_.GetHashCode();
if (options_ != null) hash ^= Options.GetHashCode();
@ -2345,6 +2354,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(ServiceDescriptorProto other) {
if (other == null) {
return;
@ -2515,7 +2525,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Name.Length != 0) hash ^= Name.GetHashCode();
if (InputType.Length != 0) hash ^= InputType.GetHashCode();
if (OutputType.Length != 0) hash ^= OutputType.GetHashCode();
@ -2574,6 +2584,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(MethodDescriptorProto other) {
if (other == null) {
return;
@ -2871,7 +2882,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (JavaPackage.Length != 0) hash ^= JavaPackage.GetHashCode();
if (JavaOuterClassname.Length != 0) hash ^= JavaOuterClassname.GetHashCode();
if (JavaMultipleFiles != false) hash ^= JavaMultipleFiles.GetHashCode();
@ -3004,6 +3015,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(FileOptions other) {
if (other == null) {
return;
@ -3248,7 +3260,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (MessageSetWireFormat != false) hash ^= MessageSetWireFormat.GetHashCode();
if (NoStandardDescriptorAccessor != false) hash ^= NoStandardDescriptorAccessor.GetHashCode();
if (Deprecated != false) hash ^= Deprecated.GetHashCode();
@ -3301,6 +3313,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(MessageOptions other) {
if (other == null) {
return;
@ -3487,7 +3500,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Ctype != global::Google.Protobuf.DescriptorProtos.FieldOptions.Types.CType.STRING) hash ^= Ctype.GetHashCode();
if (Packed != false) hash ^= Packed.GetHashCode();
if (Jstype != global::Google.Protobuf.DescriptorProtos.FieldOptions.Types.JSType.JS_NORMAL) hash ^= Jstype.GetHashCode();
@ -3556,6 +3569,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(FieldOptions other) {
if (other == null) {
return;
@ -3726,7 +3740,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (AllowAlias != false) hash ^= AllowAlias.GetHashCode();
if (Deprecated != false) hash ^= Deprecated.GetHashCode();
hash ^= uninterpretedOption_.GetHashCode();
@ -3763,6 +3777,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(EnumOptions other) {
if (other == null) {
return;
@ -3875,7 +3890,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Deprecated != false) hash ^= Deprecated.GetHashCode();
hash ^= uninterpretedOption_.GetHashCode();
return hash;
@ -3904,6 +3919,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(EnumValueOptions other) {
if (other == null) {
return;
@ -4009,7 +4025,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Deprecated != false) hash ^= Deprecated.GetHashCode();
hash ^= uninterpretedOption_.GetHashCode();
return hash;
@ -4038,6 +4054,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(ServiceOptions other) {
if (other == null) {
return;
@ -4143,7 +4160,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (Deprecated != false) hash ^= Deprecated.GetHashCode();
hash ^= uninterpretedOption_.GetHashCode();
return hash;
@ -4172,6 +4189,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(MethodOptions other) {
if (other == null) {
return;
@ -4337,7 +4355,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
hash ^= name_.GetHashCode();
if (IdentifierValue.Length != 0) hash ^= IdentifierValue.GetHashCode();
if (PositiveIntValue != 0UL) hash ^= PositiveIntValue.GetHashCode();
@ -4406,6 +4424,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(UninterpretedOption other) {
if (other == null) {
return;
@ -4550,7 +4569,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
if (NamePart_.Length != 0) hash ^= NamePart_.GetHashCode();
if (IsExtension != false) hash ^= IsExtension.GetHashCode();
return hash;
@ -4577,6 +4596,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(NamePart other) {
if (other == null) {
return;
@ -4677,7 +4697,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
hash ^= location_.GetHashCode();
return hash;
}
@ -4698,6 +4718,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(SourceCodeInfo other) {
if (other == null) {
return;
@ -4827,7 +4848,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
public override int GetHashCode() {
int hash = 0;
int hash = 1;
hash ^= path_.GetHashCode();
hash ^= span_.GetHashCode();
if (LeadingComments.Length != 0) hash ^= LeadingComments.GetHashCode();
@ -4892,6 +4913,7 @@ namespace Google.Protobuf.DescriptorProtos {
}
return size;
}
public void MergeFrom(Location other) {
if (other == null) {
return;

@ -0,0 +1,188 @@
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);

@ -0,0 +1,141 @@
// 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

@ -0,0 +1,71 @@
// 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);

@ -0,0 +1,120 @@
// 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;
}
Loading…
Cancel
Save