aboutsummaryrefslogtreecommitdiff
path: root/csharp
diff options
context:
space:
mode:
Diffstat (limited to 'csharp')
-rw-r--r--csharp/Google.Protobuf.Tools.nuspec6
-rw-r--r--csharp/Google.Protobuf.Tools.targets11
-rw-r--r--csharp/README.md2
-rw-r--r--csharp/build_packages.bat2
-rw-r--r--csharp/compatibility_tests/v3.0.0/protos/csharp/protos/unittest_issues.proto4
-rw-r--r--csharp/protos/unittest_issues.proto4
-rw-r--r--csharp/src/Google.Protobuf.Conformance/Conformance.cs77
-rw-r--r--csharp/src/Google.Protobuf.Conformance/Program.cs3
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf.Test/ByteStringTest.cs2
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs0
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf.Test/Compatibility/StreamExtensionsTest.cs0
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf.Test/Compatibility/TypeExtensionsTest.cs0
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf.Test/FieldCodecTest.cs0
-rw-r--r--csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs99
-rw-r--r--csharp/src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs101
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf/ByteString.cs0
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf/Collections/RepeatedField.cs0
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs0
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf/Compatibility/StreamExtensions.cs0
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf/Compatibility/TypeExtensions.cs0
-rw-r--r--csharp/src/Google.Protobuf/Google.Protobuf.csproj12
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf/JsonFormatter.cs0
-rw-r--r--csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs8
-rw-r--r--csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs12
-rw-r--r--csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs63
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs10
-rw-r--r--csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs16
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/Any.cs3
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs50
-rw-r--r--[-rwxr-xr-x]csharp/src/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs0
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs2
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs105
32 files changed, 496 insertions, 96 deletions
diff --git a/csharp/Google.Protobuf.Tools.nuspec b/csharp/Google.Protobuf.Tools.nuspec
index 8a0d61e1..05779d44 100644
--- a/csharp/Google.Protobuf.Tools.nuspec
+++ b/csharp/Google.Protobuf.Tools.nuspec
@@ -8,8 +8,8 @@
<version>3.6.1</version>
<authors>Google Inc.</authors>
<owners>protobuf-packages</owners>
- <licenseUrl>https://github.com/google/protobuf/blob/master/LICENSE</licenseUrl>
- <projectUrl>https://github.com/google/protobuf</projectUrl>
+ <licenseUrl>https://github.com/protocolbuffers/protobuf/blob/master/LICENSE</licenseUrl>
+ <projectUrl>https://github.com/protocolbuffers/protobuf</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<releaseNotes>Tools for Protocol Buffers</releaseNotes>
<copyright>Copyright 2015, Google Inc.</copyright>
@@ -33,5 +33,7 @@
<file src="..\src\google\protobuf\timestamp.proto" target="tools\google\protobuf" />
<file src="..\src\google\protobuf\type.proto" target="tools\google\protobuf" />
<file src="..\src\google\protobuf\wrappers.proto" target="tools\google\protobuf" />
+ <file src="Google.Protobuf.Tools.targets" target="buildCrossTargeting" />
+ <file src="Google.Protobuf.Tools.targets" target="build" />
</files>
</package>
diff --git a/csharp/Google.Protobuf.Tools.targets b/csharp/Google.Protobuf.Tools.targets
new file mode 100644
index 00000000..682e11b0
--- /dev/null
+++ b/csharp/Google.Protobuf.Tools.targets
@@ -0,0 +1,11 @@
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <protoc_tools>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/../tools/'))</protoc_tools>
+ <protoc_linux64>$([System.IO.Path]::GetFullPath('$(protoc_tools)/linux_x64/protoc'))</protoc_linux64>
+ <protoc_linux86>$([System.IO.Path]::GetFullPath('$(protoc_tools)/linux_x86/protoc'))</protoc_linux86>
+ <protoc_macosx64>$([System.IO.Path]::GetFullPath('$(protoc_tools)/macosx_x64/protoc'))</protoc_macosx64>
+ <protoc_macosx86>$([System.IO.Path]::GetFullPath('$(protoc_tools)/macosx_x86/protoc'))</protoc_macosx86>
+ <protoc_windows64>$([System.IO.Path]::GetFullPath('$(protoc_tools)/windows_x64/protoc.exe'))</protoc_windows64>
+ <protoc_windows86>$([System.IO.Path]::GetFullPath('$(protoc_tools)/windows_x86/protoc.exe'))</protoc_windows86>
+ </PropertyGroup>
+</Project>
diff --git a/csharp/README.md b/csharp/README.md
index 9d1225f1..aafef16a 100644
--- a/csharp/README.md
+++ b/csharp/README.md
@@ -77,7 +77,7 @@ History of C# protobufs
This subtree was originally imported from https://github.com/jskeet/protobuf-csharp-port
and represents the latest development version of C# protobufs, that will now be developed
and maintained by Google. All the development will be done in open, under this repository
-(https://github.com/google/protobuf).
+(https://github.com/protocolbuffers/protobuf).
The previous project differs from this project in a number of ways:
diff --git a/csharp/build_packages.bat b/csharp/build_packages.bat
index d7205659..8157bbab 100644
--- a/csharp/build_packages.bat
+++ b/csharp/build_packages.bat
@@ -1,7 +1,7 @@
@rem Builds Google.Protobuf NuGet packages
dotnet restore src/Google.Protobuf.sln
-dotnet pack -c Release src/Google.Protobuf.sln || goto :error
+dotnet pack -c Release src/Google.Protobuf.sln /p:SourceLinkCreate=true || goto :error
goto :EOF
diff --git a/csharp/compatibility_tests/v3.0.0/protos/csharp/protos/unittest_issues.proto b/csharp/compatibility_tests/v3.0.0/protos/csharp/protos/unittest_issues.proto
index 6c9f7634..7bec1f80 100644
--- a/csharp/compatibility_tests/v3.0.0/protos/csharp/protos/unittest_issues.proto
+++ b/csharp/compatibility_tests/v3.0.0/protos/csharp/protos/unittest_issues.proto
@@ -19,7 +19,7 @@ message Issue307 {
}
// Old issue 13: http://code.google.com/p/protobuf-csharp-port/issues/detail?id=13
-// New issue 309: https://github.com/google/protobuf/issues/309
+// New issue 309: https://github.com/protocolbuffers/protobuf/issues/309
// message A {
// optional int32 _A = 1;
@@ -35,7 +35,7 @@ message Issue307 {
// Similar issue with numeric names
// Java code failed too, so probably best for this to be a restriction.
-// See https://github.com/google/protobuf/issues/308
+// See https://github.com/protocolbuffers/protobuf/issues/308
// message NumberField {
// optional int32 _01 = 1;
// }
diff --git a/csharp/protos/unittest_issues.proto b/csharp/protos/unittest_issues.proto
index 0d8793e1..1619f13d 100644
--- a/csharp/protos/unittest_issues.proto
+++ b/csharp/protos/unittest_issues.proto
@@ -18,7 +18,7 @@ message Issue307 {
}
// Old issue 13: http://code.google.com/p/protobuf-csharp-port/issues/detail?id=13
-// New issue 309: https://github.com/google/protobuf/issues/309
+// New issue 309: https://github.com/protocolbuffers/protobuf/issues/309
// message A {
// optional int32 _A = 1;
@@ -34,7 +34,7 @@ message Issue307 {
// Similar issue with numeric names
// Java code failed too, so probably best for this to be a restriction.
-// See https://github.com/google/protobuf/issues/308
+// See https://github.com/protocolbuffers/protobuf/issues/308
// message NumberField {
// optional int32 _01 = 1;
// }
diff --git a/csharp/src/Google.Protobuf.Conformance/Conformance.cs b/csharp/src/Google.Protobuf.Conformance/Conformance.cs
index f6118ea2..46793ce1 100644
--- a/csharp/src/Google.Protobuf.Conformance/Conformance.cs
+++ b/csharp/src/Google.Protobuf.Conformance/Conformance.cs
@@ -24,21 +24,25 @@ namespace Conformance {
static ConformanceReflection() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
- "ChFjb25mb3JtYW5jZS5wcm90bxILY29uZm9ybWFuY2UiowEKEkNvbmZvcm1h",
+ "ChFjb25mb3JtYW5jZS5wcm90bxILY29uZm9ybWFuY2Ui1QEKEkNvbmZvcm1h",
"bmNlUmVxdWVzdBIaChBwcm90b2J1Zl9wYXlsb2FkGAEgASgMSAASFgoManNv",
"bl9wYXlsb2FkGAIgASgJSAASOAoXcmVxdWVzdGVkX291dHB1dF9mb3JtYXQY",
"AyABKA4yFy5jb25mb3JtYW5jZS5XaXJlRm9ybWF0EhQKDG1lc3NhZ2VfdHlw",
- "ZRgEIAEoCUIJCgdwYXlsb2FkIrEBChNDb25mb3JtYW5jZVJlc3BvbnNlEhUK",
- "C3BhcnNlX2Vycm9yGAEgASgJSAASGQoPc2VyaWFsaXplX2Vycm9yGAYgASgJ",
- "SAASFwoNcnVudGltZV9lcnJvchgCIAEoCUgAEhoKEHByb3RvYnVmX3BheWxv",
- "YWQYAyABKAxIABIWCgxqc29uX3BheWxvYWQYBCABKAlIABIRCgdza2lwcGVk",
- "GAUgASgJSABCCAoGcmVzdWx0KjUKCldpcmVGb3JtYXQSDwoLVU5TUEVDSUZJ",
- "RUQQABIMCghQUk9UT0JVRhABEggKBEpTT04QAkIhCh9jb20uZ29vZ2xlLnBy",
- "b3RvYnVmLmNvbmZvcm1hbmNlYgZwcm90bzM="));
+ "ZRgEIAEoCRIwCg10ZXN0X2NhdGVnb3J5GAUgASgOMhkuY29uZm9ybWFuY2Uu",
+ "VGVzdENhdGVnb3J5QgkKB3BheWxvYWQisQEKE0NvbmZvcm1hbmNlUmVzcG9u",
+ "c2USFQoLcGFyc2VfZXJyb3IYASABKAlIABIZCg9zZXJpYWxpemVfZXJyb3IY",
+ "BiABKAlIABIXCg1ydW50aW1lX2Vycm9yGAIgASgJSAASGgoQcHJvdG9idWZf",
+ "cGF5bG9hZBgDIAEoDEgAEhYKDGpzb25fcGF5bG9hZBgEIAEoCUgAEhEKB3Nr",
+ "aXBwZWQYBSABKAlIAEIICgZyZXN1bHQqNQoKV2lyZUZvcm1hdBIPCgtVTlNQ",
+ "RUNJRklFRBAAEgwKCFBST1RPQlVGEAESCAoESlNPThACKmoKDFRlc3RDYXRl",
+ "Z29yeRIUChBVTlNQRUNJRklFRF9URVNUEAASDwoLQklOQVJZX1RFU1QQARIN",
+ "CglKU09OX1RFU1QQAhIkCiBKU09OX0lHTk9SRV9VTktOT1dOX1BBUlNJTkdf",
+ "VEVTVBADQiEKH2NvbS5nb29nbGUucHJvdG9idWYuY29uZm9ybWFuY2ViBnBy",
+ "b3RvMw=="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Conformance.WireFormat), }, new pbr::GeneratedClrTypeInfo[] {
- new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceRequest), global::Conformance.ConformanceRequest.Parser, new[]{ "ProtobufPayload", "JsonPayload", "RequestedOutputFormat", "MessageType" }, new[]{ "Payload" }, null, null),
+ new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Conformance.WireFormat), typeof(global::Conformance.TestCategory), }, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceRequest), global::Conformance.ConformanceRequest.Parser, new[]{ "ProtobufPayload", "JsonPayload", "RequestedOutputFormat", "MessageType", "TestCategory" }, new[]{ "Payload" }, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceResponse), global::Conformance.ConformanceResponse.Parser, new[]{ "ParseError", "SerializeError", "RuntimeError", "ProtobufPayload", "JsonPayload", "Skipped" }, new[]{ "Result" }, null, null)
}));
}
@@ -52,6 +56,26 @@ namespace Conformance {
[pbr::OriginalName("JSON")] Json = 2,
}
+ public enum TestCategory {
+ [pbr::OriginalName("UNSPECIFIED_TEST")] UnspecifiedTest = 0,
+ /// <summary>
+ /// Test binary wire format.
+ /// </summary>
+ [pbr::OriginalName("BINARY_TEST")] BinaryTest = 1,
+ /// <summary>
+ /// Test json wire format.
+ /// </summary>
+ [pbr::OriginalName("JSON_TEST")] JsonTest = 2,
+ /// <summary>
+ /// Similar to JSON_TEST. However, during parsing json, testee should ignore
+ /// unknown fields. This feature is optional. Each implementation can descide
+ /// whether to support it. See
+ /// https://developers.google.com/protocol-buffers/docs/proto3#json_options
+ /// for more detail.
+ /// </summary>
+ [pbr::OriginalName("JSON_IGNORE_UNKNOWN_PARSING_TEST")] JsonIgnoreUnknownParsingTest = 3,
+ }
+
#endregion
#region Messages
@@ -89,6 +113,7 @@ namespace Conformance {
public ConformanceRequest(ConformanceRequest other) : this() {
requestedOutputFormat_ = other.requestedOutputFormat_;
messageType_ = other.messageType_;
+ testCategory_ = other.testCategory_;
switch (other.PayloadCase) {
case PayloadOneofCase.ProtobufPayload:
ProtobufPayload = other.ProtobufPayload;
@@ -158,6 +183,22 @@ namespace Conformance {
}
}
+ /// <summary>Field number for the "test_category" field.</summary>
+ public const int TestCategoryFieldNumber = 5;
+ private global::Conformance.TestCategory testCategory_ = 0;
+ /// <summary>
+ /// Each test is given a specific test category. Some category may need
+ /// spedific support in testee programs. Refer to the defintion of TestCategory
+ /// for more information.
+ /// </summary>
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+ public global::Conformance.TestCategory TestCategory {
+ get { return testCategory_; }
+ set {
+ testCategory_ = value;
+ }
+ }
+
private object payload_;
/// <summary>Enum of possible cases for the "payload" oneof.</summary>
public enum PayloadOneofCase {
@@ -194,6 +235,7 @@ namespace Conformance {
if (JsonPayload != other.JsonPayload) return false;
if (RequestedOutputFormat != other.RequestedOutputFormat) return false;
if (MessageType != other.MessageType) return false;
+ if (TestCategory != other.TestCategory) return false;
if (PayloadCase != other.PayloadCase) return false;
return Equals(_unknownFields, other._unknownFields);
}
@@ -205,6 +247,7 @@ namespace Conformance {
if (payloadCase_ == PayloadOneofCase.JsonPayload) hash ^= JsonPayload.GetHashCode();
if (RequestedOutputFormat != 0) hash ^= RequestedOutputFormat.GetHashCode();
if (MessageType.Length != 0) hash ^= MessageType.GetHashCode();
+ if (TestCategory != 0) hash ^= TestCategory.GetHashCode();
hash ^= (int) payloadCase_;
if (_unknownFields != null) {
hash ^= _unknownFields.GetHashCode();
@@ -235,6 +278,10 @@ namespace Conformance {
output.WriteRawTag(34);
output.WriteString(MessageType);
}
+ if (TestCategory != 0) {
+ output.WriteRawTag(40);
+ output.WriteEnum((int) TestCategory);
+ }
if (_unknownFields != null) {
_unknownFields.WriteTo(output);
}
@@ -255,6 +302,9 @@ namespace Conformance {
if (MessageType.Length != 0) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(MessageType);
}
+ if (TestCategory != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) TestCategory);
+ }
if (_unknownFields != null) {
size += _unknownFields.CalculateSize();
}
@@ -272,6 +322,9 @@ namespace Conformance {
if (other.MessageType.Length != 0) {
MessageType = other.MessageType;
}
+ if (other.TestCategory != 0) {
+ TestCategory = other.TestCategory;
+ }
switch (other.PayloadCase) {
case PayloadOneofCase.ProtobufPayload:
ProtobufPayload = other.ProtobufPayload;
@@ -308,6 +361,10 @@ namespace Conformance {
MessageType = input.ReadString();
break;
}
+ case 40: {
+ testCategory_ = (global::Conformance.TestCategory) input.ReadEnum();
+ break;
+ }
}
}
}
diff --git a/csharp/src/Google.Protobuf.Conformance/Program.cs b/csharp/src/Google.Protobuf.Conformance/Program.cs
index 96dc354e..1eac00be 100644
--- a/csharp/src/Google.Protobuf.Conformance/Program.cs
+++ b/csharp/src/Google.Protobuf.Conformance/Program.cs
@@ -87,6 +87,9 @@ namespace Google.Protobuf.Conformance
switch (request.PayloadCase)
{
case ConformanceRequest.PayloadOneofCase.JsonPayload:
+ if (request.TestCategory == global::Conformance.TestCategory.JsonIgnoreUnknownParsingTest) {
+ return new ConformanceResponse { Skipped = "CSharp doesn't support skipping unknown fields in json parsing." };
+ }
var parser = new JsonParser(new JsonParser.Settings(20, typeRegistry));
message = parser.Parse<ProtobufTestMessages.Proto3.TestAllTypesProto3>(request.JsonPayload);
break;
diff --git a/csharp/src/Google.Protobuf.Test/ByteStringTest.cs b/csharp/src/Google.Protobuf.Test/ByteStringTest.cs
index afdd491f..84e6341e 100755..100644
--- a/csharp/src/Google.Protobuf.Test/ByteStringTest.cs
+++ b/csharp/src/Google.Protobuf.Test/ByteStringTest.cs
@@ -227,7 +227,7 @@ namespace Google.Protobuf
{
// We used to have an awful hash algorithm where only the last four
// bytes were relevant. This is a regression test for
- // https://github.com/google/protobuf/issues/2511
+ // https://github.com/protocolbuffers/protobuf/issues/2511
ByteString b1 = ByteString.CopyFrom(100, 1, 2, 3, 4);
ByteString b2 = ByteString.CopyFrom(200, 1, 2, 3, 4);
diff --git a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
index 8795fa65..8795fa65 100755..100644
--- a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
+++ b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
diff --git a/csharp/src/Google.Protobuf.Test/Compatibility/StreamExtensionsTest.cs b/csharp/src/Google.Protobuf.Test/Compatibility/StreamExtensionsTest.cs
index 48c0725f..48c0725f 100755..100644
--- a/csharp/src/Google.Protobuf.Test/Compatibility/StreamExtensionsTest.cs
+++ b/csharp/src/Google.Protobuf.Test/Compatibility/StreamExtensionsTest.cs
diff --git a/csharp/src/Google.Protobuf.Test/Compatibility/TypeExtensionsTest.cs b/csharp/src/Google.Protobuf.Test/Compatibility/TypeExtensionsTest.cs
index abbe3c95..abbe3c95 100755..100644
--- a/csharp/src/Google.Protobuf.Test/Compatibility/TypeExtensionsTest.cs
+++ b/csharp/src/Google.Protobuf.Test/Compatibility/TypeExtensionsTest.cs
diff --git a/csharp/src/Google.Protobuf.Test/FieldCodecTest.cs b/csharp/src/Google.Protobuf.Test/FieldCodecTest.cs
index 77641163..77641163 100755..100644
--- a/csharp/src/Google.Protobuf.Test/FieldCodecTest.cs
+++ b/csharp/src/Google.Protobuf.Test/FieldCodecTest.cs
diff --git a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
index 29a376e0..9abee951 100644
--- a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
+++ b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
@@ -30,10 +30,11 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
-using System.Linq;
using Google.Protobuf.TestProtos;
using NUnit.Framework;
-using UnitTest.Issues.TestProtos;
+using System;
+using System.Collections.Generic;
+using System.Linq;
namespace Google.Protobuf.Reflection
{
@@ -44,9 +45,32 @@ namespace Google.Protobuf.Reflection
public class DescriptorsTest
{
[Test]
- public void FileDescriptor()
+ public void FileDescriptor_GeneratedCode()
+ {
+ TestFileDescriptor(
+ UnittestProto3Reflection.Descriptor,
+ UnittestImportProto3Reflection.Descriptor,
+ UnittestImportPublicProto3Reflection.Descriptor);
+ }
+
+ [Test]
+ public void FileDescriptor_BuildFromByteStrings()
+ {
+ // The descriptors have to be supplied in an order such that all the
+ // dependencies come before the descriptors depending on them.
+ var descriptorData = new List<ByteString>
+ {
+ UnittestImportPublicProto3Reflection.Descriptor.Proto.ToByteString(),
+ UnittestImportProto3Reflection.Descriptor.Proto.ToByteString(),
+ UnittestProto3Reflection.Descriptor.Proto.ToByteString()
+ };
+ var converted = FileDescriptor.BuildFromByteStrings(descriptorData);
+ Assert.AreEqual(3, converted.Count);
+ TestFileDescriptor(converted[2], converted[1], converted[0]);
+ }
+
+ private void TestFileDescriptor(FileDescriptor file, FileDescriptor importedFile, FileDescriptor importedPublicFile)
{
- FileDescriptor file = UnittestProto3Reflection.Descriptor;
Assert.AreEqual("unittest_proto3.proto", file.Name);
Assert.AreEqual("protobuf_unittest3", file.Package);
@@ -56,17 +80,12 @@ namespace Google.Protobuf.Reflection
// unittest_proto3.proto doesn't have any public imports, but unittest_import_proto3.proto does.
Assert.AreEqual(0, file.PublicDependencies.Count);
- Assert.AreEqual(1, UnittestImportProto3Reflection.Descriptor.PublicDependencies.Count);
- Assert.AreEqual(UnittestImportPublicProto3Reflection.Descriptor, UnittestImportProto3Reflection.Descriptor.PublicDependencies[0]);
+ Assert.AreEqual(1, importedFile.PublicDependencies.Count);
+ Assert.AreEqual(importedPublicFile, importedFile.PublicDependencies[0]);
Assert.AreEqual(1, file.Dependencies.Count);
- Assert.AreEqual(UnittestImportProto3Reflection.Descriptor, file.Dependencies[0]);
+ Assert.AreEqual(importedFile, file.Dependencies[0]);
- MessageDescriptor messageType = TestAllTypes.Descriptor;
- Assert.AreSame(typeof(TestAllTypes), messageType.ClrType);
- Assert.AreSame(TestAllTypes.Parser, messageType.Parser);
- Assert.AreEqual(messageType, file.MessageTypes[0]);
- Assert.AreEqual(messageType, file.FindTypeByName<MessageDescriptor>("TestAllTypes"));
Assert.Null(file.FindTypeByName<MessageDescriptor>("NoSuchType"));
Assert.Null(file.FindTypeByName<MessageDescriptor>("protobuf_unittest3.TestAllTypes"));
for (int i = 0; i < file.MessageTypes.Count; i++)
@@ -77,8 +96,8 @@ namespace Google.Protobuf.Reflection
Assert.AreEqual(file.EnumTypes[0], file.FindTypeByName<EnumDescriptor>("ForeignEnum"));
Assert.Null(file.FindTypeByName<EnumDescriptor>("NoSuchType"));
Assert.Null(file.FindTypeByName<EnumDescriptor>("protobuf_unittest3.ForeignEnum"));
- Assert.AreEqual(1, UnittestImportProto3Reflection.Descriptor.EnumTypes.Count);
- Assert.AreEqual("ImportEnum", UnittestImportProto3Reflection.Descriptor.EnumTypes[0].Name);
+ Assert.AreEqual(1, importedFile.EnumTypes.Count);
+ Assert.AreEqual("ImportEnum", importedFile.EnumTypes[0].Name);
for (int i = 0; i < file.EnumTypes.Count; i++)
{
Assert.AreEqual(i, file.EnumTypes[i].Index);
@@ -98,6 +117,56 @@ namespace Google.Protobuf.Reflection
}
[Test]
+ public void FileDescriptor_BuildFromByteStrings_MissingDependency()
+ {
+ var descriptorData = new List<ByteString>
+ {
+ UnittestImportProto3Reflection.Descriptor.Proto.ToByteString(),
+ UnittestProto3Reflection.Descriptor.Proto.ToByteString(),
+ };
+ // This will fail, because we're missing UnittestImportPublicProto3Reflection
+ Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData));
+ }
+
+ [Test]
+ public void FileDescriptor_BuildFromByteStrings_DuplicateNames()
+ {
+ var descriptorData = new List<ByteString>
+ {
+ UnittestImportPublicProto3Reflection.Descriptor.Proto.ToByteString(),
+ UnittestImportPublicProto3Reflection.Descriptor.Proto.ToByteString(),
+ };
+ // This will fail due to the same name being used twice
+ Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData));
+ }
+
+ [Test]
+ public void FileDescriptor_BuildFromByteStrings_IncorrectOrder()
+ {
+ var descriptorData = new List<ByteString>
+ {
+ UnittestProto3Reflection.Descriptor.Proto.ToByteString(),
+ UnittestImportPublicProto3Reflection.Descriptor.Proto.ToByteString(),
+ UnittestImportProto3Reflection.Descriptor.Proto.ToByteString()
+ };
+ // This will fail, because the dependencies should come first
+ Assert.Throws<ArgumentException>(() => FileDescriptor.BuildFromByteStrings(descriptorData));
+
+ }
+
+ [Test]
+ public void MessageDescriptorFromGeneratedCodeFileDescriptor()
+ {
+ var file = UnittestProto3Reflection.Descriptor;
+
+ MessageDescriptor messageType = TestAllTypes.Descriptor;
+ Assert.AreSame(typeof(TestAllTypes), messageType.ClrType);
+ Assert.AreSame(TestAllTypes.Parser, messageType.Parser);
+ Assert.AreEqual(messageType, file.MessageTypes[0]);
+ Assert.AreEqual(messageType, file.FindTypeByName<MessageDescriptor>("TestAllTypes"));
+ }
+
+ [Test]
public void MessageDescriptor()
{
MessageDescriptor messageType = TestAllTypes.Descriptor;
@@ -163,7 +232,7 @@ namespace Google.Protobuf.Reflection
Assert.AreEqual("single_nested_enum", enumField.Name);
Assert.AreEqual(FieldType.Enum, enumField.FieldType);
- // Assert.AreEqual(TestAllTypes.Types.NestedEnum.DescriptorProtoFile, enumField.EnumType);
+ Assert.AreEqual(messageType.EnumTypes[0], enumField.EnumType);
Assert.AreEqual("single_foreign_message", messageField.Name);
Assert.AreEqual(FieldType.Message, messageField.FieldType);
diff --git a/csharp/src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs b/csharp/src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs
index 9ecd24c6..b8c07ef5 100644
--- a/csharp/src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs
+++ b/csharp/src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs
@@ -111,5 +111,106 @@ namespace Google.Protobuf.WellKnownTypes
var duration = new Timestamp { Seconds = 1, Nanos = -1 };
Assert.AreEqual("{ \"@warning\": \"Invalid Timestamp\", \"seconds\": \"1\", \"nanos\": -1 }", duration.ToString());
}
+
+ [Test]
+ public void Comparability()
+ {
+ Timestamp
+ a = null,
+ b = new Timestamp { Seconds = 1, Nanos = 1 },
+ c = new Timestamp { Seconds = 1, Nanos = 10 },
+ d = new Timestamp { Seconds = 10, Nanos = 1 },
+ e = new Timestamp { Seconds = 10, Nanos = 10 };
+
+ Assert.IsTrue(b.CompareTo(a) > 0); // null is always first (according to default behavior of Array.Sort)
+ Assert.IsTrue(b.CompareTo(b) == 0);
+ Assert.IsTrue(b.CompareTo(b.Clone()) == 0);
+ Assert.IsTrue(b.CompareTo(c) < 0);
+ Assert.IsTrue(b.CompareTo(d) < 0);
+ Assert.IsTrue(b.CompareTo(e) < 0);
+
+ Assert.IsTrue(c.CompareTo(a) > 0);
+ Assert.IsTrue(c.CompareTo(b) > 0);
+ Assert.IsTrue(c.CompareTo(c) == 0);
+ Assert.IsTrue(c.CompareTo(c.Clone()) == 0);
+ Assert.IsTrue(c.CompareTo(d) < 0);
+ Assert.IsTrue(c.CompareTo(e) < 0);
+
+ Assert.IsTrue(d.CompareTo(a) > 0);
+ Assert.IsTrue(d.CompareTo(b) > 0);
+ Assert.IsTrue(d.CompareTo(c) > 0);
+ Assert.IsTrue(d.CompareTo(d) == 0);
+ Assert.IsTrue(d.CompareTo(d.Clone()) == 0);
+ Assert.IsTrue(d.CompareTo(e) < 0);
+
+ Assert.IsTrue(e.CompareTo(a) > 0);
+ Assert.IsTrue(e.CompareTo(b) > 0);
+ Assert.IsTrue(e.CompareTo(c) > 0);
+ Assert.IsTrue(e.CompareTo(d) > 0);
+ Assert.IsTrue(e.CompareTo(e) == 0);
+ Assert.IsTrue(e.CompareTo(e.Clone()) == 0);
+ }
+
+
+ [Test]
+ public void ComparabilityOperators()
+ {
+ Timestamp
+ a = null,
+ b = new Timestamp { Seconds = 1, Nanos = 1 },
+ c = new Timestamp { Seconds = 1, Nanos = 10 },
+ d = new Timestamp { Seconds = 10, Nanos = 1 },
+ e = new Timestamp { Seconds = 10, Nanos = 10 };
+
+#pragma warning disable CS1718 // Comparison made to same variable
+ Assert.IsTrue(b > a);
+ Assert.IsTrue(b == b);
+ Assert.IsTrue(b == b.Clone());
+ Assert.IsTrue(b < c);
+ Assert.IsTrue(b < d);
+ Assert.IsTrue(b < e);
+
+ Assert.IsTrue(c > a);
+ Assert.IsTrue(c > b);
+ Assert.IsTrue(c == c);
+ Assert.IsTrue(c == c.Clone());
+ Assert.IsTrue(c < d);
+ Assert.IsTrue(c < e);
+
+ Assert.IsTrue(d > a);
+ Assert.IsTrue(d > b);
+ Assert.IsTrue(d > c);
+ Assert.IsTrue(d == d);
+ Assert.IsTrue(d == d.Clone());
+ Assert.IsTrue(d < e);
+
+ Assert.IsTrue(e > a);
+ Assert.IsTrue(e > b);
+ Assert.IsTrue(e > c);
+ Assert.IsTrue(e > d);
+ Assert.IsTrue(e == e);
+ Assert.IsTrue(e == e.Clone());
+
+ Assert.IsTrue(b >= a);
+ Assert.IsTrue(b <= c);
+ Assert.IsTrue(b <= d);
+ Assert.IsTrue(b <= e);
+
+ Assert.IsTrue(c >= a);
+ Assert.IsTrue(c >= b);
+ Assert.IsTrue(c <= d);
+ Assert.IsTrue(c <= e);
+
+ Assert.IsTrue(d >= a);
+ Assert.IsTrue(d >= b);
+ Assert.IsTrue(d >= c);
+ Assert.IsTrue(d <= e);
+
+ Assert.IsTrue(e >= a);
+ Assert.IsTrue(e >= b);
+ Assert.IsTrue(e >= c);
+ Assert.IsTrue(e >= d);
+#pragma warning restore CS1718 // Comparison made to same variable
+ }
}
}
diff --git a/csharp/src/Google.Protobuf/ByteString.cs b/csharp/src/Google.Protobuf/ByteString.cs
index 4abdb718..4abdb718 100755..100644
--- a/csharp/src/Google.Protobuf/ByteString.cs
+++ b/csharp/src/Google.Protobuf/ByteString.cs
diff --git a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
index c18b63e2..c18b63e2 100755..100644
--- a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
+++ b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
diff --git a/csharp/src/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs b/csharp/src/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs
index 95a02c72..95a02c72 100755..100644
--- a/csharp/src/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs
+++ b/csharp/src/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs
diff --git a/csharp/src/Google.Protobuf/Compatibility/StreamExtensions.cs b/csharp/src/Google.Protobuf/Compatibility/StreamExtensions.cs
index bf4bf220..bf4bf220 100755..100644
--- a/csharp/src/Google.Protobuf/Compatibility/StreamExtensions.cs
+++ b/csharp/src/Google.Protobuf/Compatibility/StreamExtensions.cs
diff --git a/csharp/src/Google.Protobuf/Compatibility/TypeExtensions.cs b/csharp/src/Google.Protobuf/Compatibility/TypeExtensions.cs
index 2f237138..2f237138 100755..100644
--- a/csharp/src/Google.Protobuf/Compatibility/TypeExtensions.cs
+++ b/csharp/src/Google.Protobuf/Compatibility/TypeExtensions.cs
diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.csproj b/csharp/src/Google.Protobuf/Google.Protobuf.csproj
index 11bc03d1..8ea3818a 100644
--- a/csharp/src/Google.Protobuf/Google.Protobuf.csproj
+++ b/csharp/src/Google.Protobuf/Google.Protobuf.csproj
@@ -13,12 +13,10 @@
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PackageTags>Protocol;Buffers;Binary;Serialization;Format;Google;proto;proto3</PackageTags>
<PackageReleaseNotes>C# proto3 support</PackageReleaseNotes>
- <PackageProjectUrl>https://github.com/google/protobuf</PackageProjectUrl>
- <PackageLicenseUrl>https://github.com/google/protobuf/blob/master/LICENSE</PackageLicenseUrl>
+ <PackageProjectUrl>https://github.com/protocolbuffers/protobuf</PackageProjectUrl>
+ <PackageLicenseUrl>https://github.com/protocolbuffers/protobuf/blob/master/LICENSE</PackageLicenseUrl>
<RepositoryType>git</RepositoryType>
- <RepositoryUrl>https://github.com/google/protobuf.git</RepositoryUrl>
- <IncludeSymbols>true</IncludeSymbols>
- <IncludeSource>true</IncludeSource>
+ <RepositoryUrl>https://github.com/protocolbuffers/protobuf.git</RepositoryUrl>
</PropertyGroup>
<!--
@@ -30,4 +28,8 @@
<TargetFrameworks>netstandard1.0</TargetFrameworks>
</PropertyGroup>
+ <ItemGroup>
+ <PackageReference Include="SourceLink.Create.CommandLine" Version="2.7.6" PrivateAssets="All" />
+ </ItemGroup>
+
</Project>
diff --git a/csharp/src/Google.Protobuf/JsonFormatter.cs b/csharp/src/Google.Protobuf/JsonFormatter.cs
index 4ae10d8b..4ae10d8b 100755..100644
--- a/csharp/src/Google.Protobuf/JsonFormatter.cs
+++ b/csharp/src/Google.Protobuf/JsonFormatter.cs
diff --git a/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs b/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs
index 99ca4bf3..9c2160fe 100644
--- a/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs
+++ b/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs
@@ -53,13 +53,13 @@ namespace Google.Protobuf.Reflection
private readonly HashSet<FileDescriptor> dependencies;
- internal DescriptorPool(FileDescriptor[] dependencyFiles)
+ internal DescriptorPool(IEnumerable<FileDescriptor> dependencyFiles)
{
dependencies = new HashSet<FileDescriptor>();
- for (int i = 0; i < dependencyFiles.Length; i++)
+ foreach (var dependencyFile in dependencyFiles)
{
- dependencies.Add(dependencyFiles[i]);
- ImportPublicDependencies(dependencyFiles[i]);
+ dependencies.Add(dependencyFile);
+ ImportPublicDependencies(dependencyFile);
}
foreach (FileDescriptor dependency in dependencyFiles)
diff --git a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
index 2a3d5c7a..152467d8 100644
--- a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
@@ -116,13 +116,18 @@ namespace Google.Protobuf.Reflection
/// that is the responsibility of the accessor.
/// </para>
/// <para>
- /// The value returned by this property will be non-null for all regular fields. However,
- /// if a message containing a map field is introspected, the list of nested messages will include
+ /// In descriptors for generated code, the value returned by this property will be non-null for all
+ /// regular fields. However, if a message containing a map field is introspected, the list of nested messages will include
/// an auto-generated nested key/value pair message for the field. This is not represented in any
/// generated type, and the value of the map field itself is represented by a dictionary in the
/// reflection API. There are never instances of those "hidden" messages, so no accessor is provided
/// and this property will return null.
/// </para>
+ /// <para>
+ /// In dynamically loaded descriptors, the value returned by this property will current be null;
+ /// if and when dynamic messages are supported, it will return a suitable accessor to work with
+ /// them.
+ /// </para>
/// </remarks>
public IFieldAccessor Accessor => accessor;
@@ -330,7 +335,8 @@ namespace Google.Protobuf.Reflection
private IFieldAccessor CreateAccessor()
{
// If we're given no property name, that's because we really don't want an accessor.
- // (At the moment, that means it's a map entry message...)
+ // This could be because it's a map message, or it could be that we're loading a FileDescriptor dynamically.
+ // TODO: Support dynamic messages.
if (propertyName == null)
{
return null;
diff --git a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
index be94cb10..799f7291 100644
--- a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
@@ -34,6 +34,7 @@ using Google.Protobuf.WellKnownTypes;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Linq;
namespace Google.Protobuf.Reflection
{
@@ -54,12 +55,12 @@ namespace Google.Protobuf.Reflection
ForceReflectionInitialization<Value.KindOneofCase>();
}
- private FileDescriptor(ByteString descriptorData, FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies, GeneratedClrTypeInfo generatedCodeInfo)
+ private FileDescriptor(ByteString descriptorData, FileDescriptorProto proto, IEnumerable<FileDescriptor> dependencies, DescriptorPool pool, bool allowUnknownDependencies, GeneratedClrTypeInfo generatedCodeInfo)
{
SerializedData = descriptorData;
DescriptorPool = pool;
Proto = proto;
- Dependencies = new ReadOnlyCollection<FileDescriptor>((FileDescriptor[]) dependencies.Clone());
+ Dependencies = new ReadOnlyCollection<FileDescriptor>(dependencies.ToList());
PublicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies);
@@ -67,11 +68,11 @@ namespace Google.Protobuf.Reflection
MessageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageType,
(message, index) =>
- new MessageDescriptor(message, this, null, index, generatedCodeInfo.NestedTypes[index]));
+ new MessageDescriptor(message, this, null, index, generatedCodeInfo?.NestedTypes[index]));
EnumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType,
(enumType, index) =>
- new EnumDescriptor(enumType, this, null, index, generatedCodeInfo.NestedEnums[index]));
+ new EnumDescriptor(enumType, this, null, index, generatedCodeInfo?.NestedEnums[index]));
Services = DescriptorUtil.ConvertAndMakeReadOnly(proto.Service,
(service, index) =>
@@ -98,13 +99,9 @@ namespace Google.Protobuf.Reflection
/// Extracts public dependencies from direct dependencies. This is a static method despite its
/// first parameter, as the value we're in the middle of constructing is only used for exceptions.
/// </summary>
- private static IList<FileDescriptor> DeterminePublicDependencies(FileDescriptor @this, FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies)
+ private static IList<FileDescriptor> DeterminePublicDependencies(FileDescriptor @this, FileDescriptorProto proto, IEnumerable<FileDescriptor> dependencies, bool allowUnknownDependencies)
{
- var nameToFileMap = new Dictionary<string, FileDescriptor>();
- foreach (var file in dependencies)
- {
- nameToFileMap[file.Name] = file;
- }
+ var nameToFileMap = dependencies.ToDictionary(file => file.Name);
var publicDependencies = new List<FileDescriptor>();
for (int i = 0; i < proto.PublicDependency.Count; i++)
{
@@ -114,8 +111,7 @@ namespace Google.Protobuf.Reflection
throw new DescriptorValidationException(@this, "Invalid public dependency index.");
}
string name = proto.Dependency[index];
- FileDescriptor file = nameToFileMap[name];
- if (file == null)
+ if (!nameToFileMap.TryGetValue(name, out var file))
{
if (!allowUnknownDependencies)
{
@@ -316,6 +312,49 @@ namespace Google.Protobuf.Reflection
}
/// <summary>
+ /// Converts the given descriptor binary data into FileDescriptor objects.
+ /// Note: reflection using the returned FileDescriptors is not currently supported.
+ /// </summary>
+ /// <param name="descriptorData">The binary file descriptor proto data. Must not be null, and any
+ /// dependencies must come before the descriptor which depends on them. (If A depends on B, and B
+ /// depends on C, then the descriptors must be presented in the order C, B, A.) This is compatible
+ /// with the order in which protoc provides descriptors to plugins.</param>
+ /// <returns>The file descriptors corresponding to <paramref name="descriptorData"/>.</returns>
+ public static IReadOnlyList<FileDescriptor> BuildFromByteStrings(IEnumerable<ByteString> descriptorData)
+ {
+ ProtoPreconditions.CheckNotNull(descriptorData, nameof(descriptorData));
+
+ // TODO: See if we can build a single DescriptorPool instead of building lots of them.
+ // This will all behave correctly, but it's less efficient than we'd like.
+ var descriptors = new List<FileDescriptor>();
+ var descriptorsByName = new Dictionary<string, FileDescriptor>();
+ foreach (var data in descriptorData)
+ {
+ var proto = FileDescriptorProto.Parser.ParseFrom(data);
+ var dependencies = new List<FileDescriptor>();
+ foreach (var dependencyName in proto.Dependency)
+ {
+ if (!descriptorsByName.TryGetValue(dependencyName, out var dependency))
+ {
+ throw new ArgumentException($"Dependency missing: {dependencyName}");
+ }
+ dependencies.Add(dependency);
+ }
+ var pool = new DescriptorPool(dependencies);
+ FileDescriptor descriptor = new FileDescriptor(
+ data, proto, dependencies, pool,
+ allowUnknownDependencies: false, generatedCodeInfo: null);
+ descriptors.Add(descriptor);
+ if (descriptorsByName.ContainsKey(descriptor.Name))
+ {
+ throw new ArgumentException($"Duplicate descriptor name: {descriptor.Name}");
+ }
+ descriptorsByName.Add(descriptor.Name, descriptor);
+ }
+ return new ReadOnlyCollection<FileDescriptor>(descriptors);
+ }
+
+ /// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
index 86942acc..dbb6768b 100755..100644
--- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
@@ -72,23 +72,21 @@ namespace Google.Protobuf.Reflection
ClrType = generatedCodeInfo?.ClrType;
ContainingType = parent;
- // Note use of generatedCodeInfo. rather than generatedCodeInfo?. here... we don't expect
- // to see any nested oneofs, types or enums in "not actually generated" code... we do
- // expect fields though (for map entry messages).
+ // If generatedCodeInfo is null, we just won't generate an accessor for any fields.
Oneofs = DescriptorUtil.ConvertAndMakeReadOnly(
proto.OneofDecl,
(oneof, index) =>
- new OneofDescriptor(oneof, file, this, index, generatedCodeInfo.OneofNames[index]));
+ new OneofDescriptor(oneof, file, this, index, generatedCodeInfo?.OneofNames[index]));
NestedTypes = DescriptorUtil.ConvertAndMakeReadOnly(
proto.NestedType,
(type, index) =>
- new MessageDescriptor(type, file, this, index, generatedCodeInfo.NestedTypes[index]));
+ new MessageDescriptor(type, file, this, index, generatedCodeInfo?.NestedTypes[index]));
EnumTypes = DescriptorUtil.ConvertAndMakeReadOnly(
proto.EnumType,
(type, index) =>
- new EnumDescriptor(type, file, this, index, generatedCodeInfo.NestedEnums[index]));
+ new EnumDescriptor(type, file, this, index, generatedCodeInfo?.NestedEnums[index]));
fieldsInDeclarationOrder = DescriptorUtil.ConvertAndMakeReadOnly(
proto.Field,
diff --git a/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs
index 5906c2e3..c026bea6 100644
--- a/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs
@@ -85,6 +85,16 @@ namespace Google.Protobuf.Reflection
/// Gets an accessor for reflective access to the values associated with the oneof
/// in a particular message.
/// </summary>
+ /// <remarks>
+ /// <para>
+ /// In descriptors for generated code, the value returned by this property will always be non-null.
+ /// </para>
+ /// <para>
+ /// In dynamically loaded descriptors, the value returned by this property will current be null;
+ /// if and when dynamic messages are supported, it will return a suitable accessor to work with
+ /// them.
+ /// </para>
+ /// </remarks>
/// <value>
/// The accessor used for reflective access.
/// </value>
@@ -110,6 +120,12 @@ namespace Google.Protobuf.Reflection
private OneofAccessor CreateAccessor(string clrName)
{
+ // We won't have a CLR name if this is from a dynamically-loaded FileDescriptor.
+ // TODO: Support dynamic messages.
+ if (clrName == null)
+ {
+ return null;
+ }
var caseProperty = containingType.ClrType.GetProperty(clrName + "Case");
if (caseProperty == null)
{
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs
index 378b61d4..dd991106 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs
@@ -159,7 +159,8 @@ namespace Google.Protobuf.WellKnownTypes {
private string typeUrl_ = "";
/// <summary>
/// A URL/resource name that uniquely identifies the type of the serialized
- /// protocol buffer message. The last segment of the URL's path must represent
+ /// protocol buffer message. This string must contain at least
+ /// one "/" character. The last segment of the URL's path must represent
/// the fully qualified name of the type (as in
/// `path/google.protobuf.Duration`). The name should be in a canonical form
/// (e.g., leading "." is not accepted).
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
index b73930b2..6ad31a50 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
@@ -25,11 +25,11 @@ namespace Google.Protobuf.WellKnownTypes {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"CiBnb29nbGUvcHJvdG9idWYvZmllbGRfbWFzay5wcm90bxIPZ29vZ2xlLnBy",
- "b3RvYnVmIhoKCUZpZWxkTWFzaxINCgVwYXRocxgBIAMoCUKJAQoTY29tLmdv",
+ "b3RvYnVmIhoKCUZpZWxkTWFzaxINCgVwYXRocxgBIAMoCUKMAQoTY29tLmdv",
"b2dsZS5wcm90b2J1ZkIORmllbGRNYXNrUHJvdG9QAVo5Z29vZ2xlLmdvbGFu",
"Zy5vcmcvZ2VucHJvdG8vcHJvdG9idWYvZmllbGRfbWFzaztmaWVsZF9tYXNr",
- "ogIDR1BCqgIeR29vZ2xlLlByb3RvYnVmLldlbGxLbm93blR5cGVzYgZwcm90",
- "bzM="));
+ "+AEBogIDR1BCqgIeR29vZ2xlLlByb3RvYnVmLldlbGxLbm93blR5cGVzYgZw",
+ "cm90bzM="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
@@ -108,57 +108,49 @@ namespace Google.Protobuf.WellKnownTypes {
/// describe the updated values, the API ignores the values of all
/// fields not covered by the mask.
///
- /// If a repeated field is specified for an update operation, the existing
- /// repeated values in the target resource will be overwritten by the new values.
- /// Note that a repeated field is only allowed in the last position of a `paths`
- /// string.
+ /// If a repeated field is specified for an update operation, new values will
+ /// be appended to the existing repeated field in the target resource. Note that
+ /// a repeated field is only allowed in the last position of a `paths` string.
///
/// If a sub-message is specified in the last position of the field mask for an
- /// update operation, then the existing sub-message in the target resource is
- /// overwritten. Given the target message:
+ /// update operation, then new value will be merged into the existing sub-message
+ /// in the target resource.
+ ///
+ /// For example, given the target message:
///
/// f {
/// b {
- /// d : 1
- /// x : 2
+ /// d: 1
+ /// x: 2
/// }
- /// c : 1
+ /// c: [1]
/// }
///
/// And an update message:
///
/// f {
/// b {
- /// d : 10
+ /// d: 10
/// }
+ /// c: [2]
/// }
///
/// then if the field mask is:
///
- /// paths: "f.b"
+ /// paths: ["f.b", "f.c"]
///
/// then the result will be:
///
/// f {
/// b {
- /// d : 10
+ /// d: 10
+ /// x: 2
/// }
- /// c : 1
+ /// c: [1, 2]
/// }
///
- /// However, if the update mask was:
- ///
- /// paths: "f.b.d"
- ///
- /// then the result would be:
- ///
- /// f {
- /// b {
- /// d : 10
- /// x : 2
- /// }
- /// c : 1
- /// }
+ /// An implementation may provide options to override this default behavior for
+ /// repeated and message fields.
///
/// In order to reset a field's value to the default, the field must
/// be in the mask and set to the default value in the provided resource.
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs
index 4b0670f6..4b0670f6 100755..100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs
index ef752be7..d1ab0f89 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs
@@ -116,7 +116,7 @@ namespace Google.Protobuf.WellKnownTypes {
/// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
/// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
/// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
- /// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
+ /// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
/// ) to obtain a formatter capable of generating timestamps in this format.
/// </summary>
public sealed partial class Timestamp : pb::IMessage<Timestamp> {
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs
index aa403473..a9251974 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs
@@ -36,7 +36,7 @@ using System.Text;
namespace Google.Protobuf.WellKnownTypes
{
- public partial class Timestamp : ICustomDiagnosticMessage
+ public partial class Timestamp : ICustomDiagnosticMessage, IComparable<Timestamp>
{
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
// Constants determined programmatically, but then hard-coded so they can be constant expressions.
@@ -223,6 +223,109 @@ namespace Google.Protobuf.WellKnownTypes
}
/// <summary>
+ /// Given another timestamp, returns 0 if the timestamps are equivalent, -1 if this timestamp precedes the other, and 1 otherwise
+ /// </summary>
+ /// <remarks>
+ /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+ /// </remarks>
+ /// <param name="other">Timestamp to compare</param>
+ /// <returns>an integer indicating whether this timestamp precedes or follows the other</returns>
+ public int CompareTo(Timestamp other)
+ {
+ return other == null ? 1
+ : Seconds < other.Seconds ? -1
+ : Seconds > other.Seconds ? 1
+ : Nanos < other.Nanos ? -1
+ : Nanos > other.Nanos ? 1
+ : 0;
+ }
+
+ /// <summary>
+ /// Compares two timestamps and returns whether the first is less than (chronologically precedes) the second
+ /// </summary>
+ /// <remarks>
+ /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+ /// </remarks>
+ /// <param name="a"></param>
+ /// <param name="b"></param>
+ /// <returns>true if a precedes b</returns>
+ public static bool operator <(Timestamp a, Timestamp b)
+ {
+ return a.CompareTo(b) < 0;
+ }
+
+ /// <summary>
+ /// Compares two timestamps and returns whether the first is greater than (chronologically follows) the second
+ /// </summary>
+ /// <remarks>
+ /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+ /// </remarks>
+ /// <param name="a"></param>
+ /// <param name="b"></param>
+ /// <returns>true if a follows b</returns>
+ public static bool operator >(Timestamp a, Timestamp b)
+ {
+ return a.CompareTo(b) > 0;
+ }
+
+ /// <summary>
+ /// Compares two timestamps and returns whether the first is less than (chronologically precedes) the second
+ /// </summary>
+ /// <remarks>
+ /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+ /// </remarks>
+ /// <param name="a"></param>
+ /// <param name="b"></param>
+ /// <returns>true if a precedes b</returns>
+ public static bool operator <=(Timestamp a, Timestamp b)
+ {
+ return a.CompareTo(b) <= 0;
+ }
+
+ /// <summary>
+ /// Compares two timestamps and returns whether the first is greater than (chronologically follows) the second
+ /// </summary>
+ /// <remarks>
+ /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+ /// </remarks>
+ /// <param name="a"></param>
+ /// <param name="b"></param>
+ /// <returns>true if a follows b</returns>
+ public static bool operator >=(Timestamp a, Timestamp b)
+ {
+ return a.CompareTo(b) >= 0;
+ }
+
+
+ /// <summary>
+ /// Returns whether two timestamps are equivalent
+ /// </summary>
+ /// <remarks>
+ /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+ /// </remarks>
+ /// <param name="a"></param>
+ /// <param name="b"></param>
+ /// <returns>true if the two timestamps refer to the same nanosecond</returns>
+ public static bool operator ==(Timestamp a, Timestamp b)
+ {
+ return ReferenceEquals(a, b) || (a is null ? (b is null ? true : false) : a.Equals(b));
+ }
+
+ /// <summary>
+ /// Returns whether two timestamps differ
+ /// </summary>
+ /// <remarks>
+ /// Make sure the timestamps are normalized. Comparing non-normalized timestamps is not specified and may give unexpected results.
+ /// </remarks>
+ /// <param name="a"></param>
+ /// <param name="b"></param>
+ /// <returns>true if the two timestamps differ</returns>
+ public static bool operator !=(Timestamp a, Timestamp b)
+ {
+ return !(a == b);
+ }
+
+ /// <summary>
/// Returns a string representation of this <see cref="Timestamp"/> for diagnostic purposes.
/// </summary>
/// <remarks>