From 9df2defa295e1a3b315ed2a88791db51ff1f53e7 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Tue, 4 Aug 2015 11:26:48 +0100 Subject: Consume unknown fields when parsing. This is expected to be the cause of the conformance test failures. Generated code in next commit. --- .../Google.Protobuf.Test/GeneratedMessageTest.cs | 29 +++++++++++++++++++ csharp/src/Google.Protobuf/CodedInputStream.cs | 33 ++++++++++++++++++++++ .../protobuf/compiler/csharp/csharp_message.cc | 3 +- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs index 2b6265c1..1ef3d753 100644 --- a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs +++ b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs @@ -36,6 +36,8 @@ using Google.Protobuf.TestProtos; using NUnit.Framework; using System.Collections; using System.Collections.Generic; +using System.Linq; +using Google.Protobuf.WellKnownTypes; namespace Google.Protobuf { @@ -590,5 +592,32 @@ namespace Google.Protobuf Assert.AreEqual(message, message2); Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase); } + + [Test] + public void IgnoreUnknownFields_RealDataStillRead() + { + var message = SampleMessages.CreateFullTestAllTypes(); + var stream = new MemoryStream(); + var output = new CodedOutputStream(stream); + var unusedFieldNumber = 23456; + Assert.IsFalse(TestAllTypes.Descriptor.Fields.InDeclarationOrder().Select(x => x.FieldNumber).Contains(unusedFieldNumber)); + output.WriteTag(unusedFieldNumber, WireFormat.WireType.LengthDelimited); + output.WriteString("ignore me"); + message.WriteTo(output); + output.Flush(); + + stream.Position = 0; + var parsed = TestAllTypes.Parser.ParseFrom(stream); + Assert.AreEqual(message, parsed); + } + + [Test] + public void IgnoreUnknownFields_AllTypes() + { + // Simple way of ensuring we can skip all kinds of fields. + var data = SampleMessages.CreateFullTestAllTypes().ToByteArray(); + var empty = Empty.Parser.ParseFrom(data); + Assert.AreEqual(new Empty(), empty); + } } } diff --git a/csharp/src/Google.Protobuf/CodedInputStream.cs b/csharp/src/Google.Protobuf/CodedInputStream.cs index a1abfcb0..fee31e3b 100644 --- a/csharp/src/Google.Protobuf/CodedInputStream.cs +++ b/csharp/src/Google.Protobuf/CodedInputStream.cs @@ -308,6 +308,39 @@ namespace Google.Protobuf return true; } + /// + /// Consumes the data for the field with the tag we've just read. + /// This should be called directly after , when + /// the caller wishes to skip an unknown field. + /// + public void ConsumeLastField() + { + if (lastTag == 0) + { + throw new InvalidOperationException("ConsumeLastField cannot be called at the end of a stream"); + } + switch (WireFormat.GetTagWireType(lastTag)) + { + case WireFormat.WireType.StartGroup: + case WireFormat.WireType.EndGroup: + // TODO: Work out how to skip them instead? See issue 688. + throw new InvalidProtocolBufferException("Group tags not supported by proto3 C# implementation"); + case WireFormat.WireType.Fixed32: + ReadFixed32(); + break; + case WireFormat.WireType.Fixed64: + ReadFixed64(); + break; + case WireFormat.WireType.LengthDelimited: + var length = ReadLength(); + SkipRawBytes(length); + break; + case WireFormat.WireType.Varint: + ReadRawVarint32(); + break; + } + } + /// /// Reads a double field from the stream. /// diff --git a/src/google/protobuf/compiler/csharp/csharp_message.cc b/src/google/protobuf/compiler/csharp/csharp_message.cc index 6ea568ca..ea722455 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message.cc +++ b/src/google/protobuf/compiler/csharp/csharp_message.cc @@ -428,7 +428,8 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) { " if (pb::WireFormat.IsEndGroupTag(tag)) {\n" " return;\n" " }\n" - " break;\n"); // Note: we're ignoring unknown fields here. + " input.ConsumeLastField();\n" // We're not storing the data, but we still need to consume it. + " break;\n"); for (int i = 0; i < fields_by_number().size(); i++) { const FieldDescriptor* field = fields_by_number()[i]; internal::WireFormatLite::WireType wt = -- cgit v1.2.3