Преглед на файлове

make things build after codegen change

Jan Tattermusch преди 5 години
родител
ревизия
220e7be708

+ 53 - 1
csharp/src/Google.Protobuf/Collections/MapField.cs

@@ -33,6 +33,7 @@
 using Google.Protobuf.Compatibility;
 using Google.Protobuf.Reflection;
 using System;
+using System.Buffers;
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
@@ -431,6 +432,28 @@ namespace Google.Protobuf.Collections
             } while (input.MaybeConsumeTag(codec.MapTag));
         }
 
+        /// <summary>
+        /// Adds entries to the map from the given parse context.
+        /// </summary>
+        /// <remarks>
+        /// It is assumed that the input is initially positioned after the tag specified by the codec.
+        /// This method will continue reading entries from the input until the end is reached, or
+        /// a different tag is encountered.
+        /// </remarks>
+        /// <param name="ctx">Input to read from</param>
+        /// <param name="codec">Codec describing how the key/value pairs are encoded</param>
+        public void AddEntriesFrom(ref ParseContext ctx, Codec codec)
+        {
+            // TODO: deduplicate code?
+            var adapter = new Codec.MessageAdapter(codec);
+            do
+            {
+                adapter.Reset();
+                ctx.ReadMessage(adapter);
+                this[adapter.Key] = adapter.Value;
+            } while (ParsingPrimitives.MaybeConsumeTag(ref ctx.buffer, ref ctx.state, codec.MapTag));
+        }
+
         /// <summary>
         /// Writes the contents of this map to the given coded output stream, using the specified codec
         /// to encode each entry.
@@ -620,7 +643,7 @@ namespace Google.Protobuf.Collections
             /// 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
+            internal class MessageAdapter : IMessage, IBufferMessage
             {
                 private static readonly byte[] ZeroLengthMessageStreamData = new byte[] { 0 };
 
@@ -666,6 +689,35 @@ namespace Google.Protobuf.Collections
                     }
                 }
 
+                public void MergeFrom_Internal(ref ParseContext ctx)
+                {
+                    // TODO(jtattermusch): deduplicate code
+                    uint tag;
+                    while ((tag = ctx.ReadTag()) != 0)
+                    {
+                        if (tag == codec.keyCodec.Tag)
+                        {
+                            Key = codec.keyCodec.Read(ref ctx);
+                        }
+                        else if (tag == codec.valueCodec.Tag)
+                        {
+                            Value = codec.valueCodec.Read(ref ctx);
+                        }
+                        else 
+                        {
+                            ParsingPrimitivesMessages.SkipLastField(ref ctx.buffer, ref ctx.state);
+                        }
+                    }
+
+                    // Corner case: a map entry with a key but no value, where the value type is a message.
+                    // Read it as if we'd seen input with no data (i.e. create a "default" message).
+                    if (Value == null)
+                    {
+                        var zeroLengthCtx = new ParseContext(new ReadOnlySequence<byte>(ZeroLengthMessageStreamData));
+                        Value = codec.valueCodec.Read(ref zeroLengthCtx);
+                    }
+                }
+
                 public void WriteTo(CodedOutputStream output)
                 {
                     codec.keyCodec.WriteTagAndValue(output, Key);

+ 2 - 2
csharp/src/Google.Protobuf/ExtensionRegistry.cs

@@ -80,9 +80,9 @@ namespace Google.Protobuf
         /// </summary>
         bool ICollection<Extension>.IsReadOnly => false;
 
-        internal bool ContainsInputField(CodedInputStream stream, Type target, out Extension extension)
+        internal bool ContainsInputField(uint lastTag, Type target, out Extension extension)
         {
-            return extensions.TryGetValue(new ObjectIntPair<Type>(target, WireFormat.GetTagFieldNumber(stream.LastTag)), out extension);
+            return extensions.TryGetValue(new ObjectIntPair<Type>(target, WireFormat.GetTagFieldNumber(lastTag)), out extension);
         }
 
         /// <summary>

+ 31 - 1
csharp/src/Google.Protobuf/ExtensionSet.cs

@@ -192,7 +192,7 @@ namespace Google.Protobuf
                 extensionValue.MergeFrom(stream);
                 return true;
             }
-            else if (stream.ExtensionRegistry != null && stream.ExtensionRegistry.ContainsInputField(stream, typeof(TTarget), out extension))
+            else if (stream.ExtensionRegistry != null && stream.ExtensionRegistry.ContainsInputField(stream.LastTag, typeof(TTarget), out extension))
             {
                 IExtensionValue value = extension.CreateValue();
                 value.MergeFrom(stream);
@@ -206,6 +206,36 @@ namespace Google.Protobuf
             }
         }
 
+        /// <summary>
+        /// Tries to merge a field from the coded input, returning true if the field was merged.
+        /// If the set is null or the field was not otherwise merged, this returns false.
+        /// </summary>
+        public static bool TryMergeFieldFrom<TTarget>(ref ExtensionSet<TTarget> set, ref ParseContext ctx) where TTarget : IExtendableMessage<TTarget>
+        {
+            // TODO(jtattermusch): deduplicate code
+            Extension extension;
+            int lastFieldNumber = WireFormat.GetTagFieldNumber(ctx.LastTag);
+
+            IExtensionValue extensionValue;
+            if (set != null && set.ValuesByNumber.TryGetValue(lastFieldNumber, out extensionValue))
+            {
+                extensionValue.MergeFrom(ref ctx);
+                return true;
+            }
+            else if (ctx.ExtensionRegistry != null && ctx.ExtensionRegistry.ContainsInputField(ctx.LastTag, typeof(TTarget), out extension))
+            {
+                IExtensionValue value = extension.CreateValue();
+                value.MergeFrom(ref ctx);
+                set = (set ?? new ExtensionSet<TTarget>());
+                set.ValuesByNumber.Add(extension.FieldNumber, value);
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
         /// <summary>
         /// Merges the second set into the first set, creating a new instance if first is null
         /// </summary>

+ 8 - 0
csharp/src/Google.Protobuf/ExtensionValue.cs

@@ -39,6 +39,9 @@ namespace Google.Protobuf
     internal interface IExtensionValue : IEquatable<IExtensionValue>, IDeepCloneable<IExtensionValue>
     {
         void MergeFrom(CodedInputStream input);
+
+        void MergeFrom(ref ParseContext ctx);
+
         void MergeFrom(IExtensionValue value);
         void WriteTo(CodedOutputStream output);
         int CalculateSize();
@@ -198,6 +201,11 @@ namespace Google.Protobuf
             field.AddEntriesFrom(input, codec);
         }
 
+        public void MergeFrom(ref ParseContext ctx)
+        {
+            field.AddEntriesFrom(ref ctx, codec);
+        }
+
         public void MergeFrom(IExtensionValue value)
         {
             if (value is RepeatedExtensionValue<T>)

+ 2 - 2
csharp/src/Google.Protobuf/ParsingPrimitives.cs

@@ -138,8 +138,8 @@ namespace Google.Protobuf
         }
 
         /// <summary>
-        /// Peeks at the next field tag. This is like calling <see cref="ReadTag"/>, but the
-        /// tag is not consumed. (So a subsequent call to <see cref="ReadTag"/> will return the
+        /// Peeks at the next field tag. This is like calling <see cref="ParseTag"/>, but the
+        /// tag is not consumed. (So a subsequent call to <see cref="ParseTag"/> will return the
         /// same value.)
         /// </summary>
         public static uint PeekTag(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state)