|
@@ -448,12 +448,10 @@ namespace Google.Protobuf.Collections
|
|
|
[SecuritySafeCritical]
|
|
|
public void AddEntriesFrom(ref ParseContext ctx, Codec codec)
|
|
|
{
|
|
|
- var adapter = new Codec.MessageAdapter(codec);
|
|
|
do
|
|
|
{
|
|
|
- adapter.Reset();
|
|
|
- ctx.ReadMessage(adapter);
|
|
|
- this[adapter.Key] = adapter.Value;
|
|
|
+ KeyValuePair<TKey, TValue> entry = ParsingPrimitivesMessages.ReadMapEntry(ref ctx, codec);
|
|
|
+ this[entry.Key] = entry.Value;
|
|
|
} while (ParsingPrimitives.MaybeConsumeTag(ref ctx.buffer, ref ctx.state, codec.MapTag));
|
|
|
}
|
|
|
|
|
@@ -485,13 +483,13 @@ namespace Google.Protobuf.Collections
|
|
|
[SecuritySafeCritical]
|
|
|
public void WriteTo(ref WriteContext ctx, Codec codec)
|
|
|
{
|
|
|
- var message = new Codec.MessageAdapter(codec);
|
|
|
foreach (var entry in list)
|
|
|
{
|
|
|
- message.Key = entry.Key;
|
|
|
- message.Value = entry.Value;
|
|
|
ctx.WriteTag(codec.MapTag);
|
|
|
- ctx.WriteMessage(message);
|
|
|
+
|
|
|
+ WritingPrimitives.WriteLength(ref ctx.buffer, ref ctx.state, CalculateEntrySize(codec, entry));
|
|
|
+ codec.KeyCodec.WriteTagAndValue(ref ctx, entry.Key);
|
|
|
+ codec.ValueCodec.WriteTagAndValue(ref ctx, entry.Value);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -506,18 +504,22 @@ namespace Google.Protobuf.Collections
|
|
|
{
|
|
|
return 0;
|
|
|
}
|
|
|
- var message = new Codec.MessageAdapter(codec);
|
|
|
int size = 0;
|
|
|
foreach (var entry in list)
|
|
|
{
|
|
|
- message.Key = entry.Key;
|
|
|
- message.Value = entry.Value;
|
|
|
+ int entrySize = CalculateEntrySize(codec, entry);
|
|
|
+
|
|
|
size += CodedOutputStream.ComputeRawVarint32Size(codec.MapTag);
|
|
|
- size += CodedOutputStream.ComputeMessageSize(message);
|
|
|
+ size += CodedOutputStream.ComputeLengthSize(entrySize) + entrySize;
|
|
|
}
|
|
|
return size;
|
|
|
}
|
|
|
|
|
|
+ private static int CalculateEntrySize(Codec codec, KeyValuePair<TKey, TValue> entry)
|
|
|
+ {
|
|
|
+ return codec.KeyCodec.CalculateSizeWithTag(entry.Key) + codec.ValueCodec.CalculateSizeWithTag(entry.Value);
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Returns a string representation of this repeated field, in the same
|
|
|
/// way as it would be represented by the default JSON formatter.
|
|
@@ -659,96 +661,8 @@ namespace Google.Protobuf.Collections
|
|
|
/// </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, IBufferMessage
|
|
|
- {
|
|
|
- private static readonly byte[] ZeroLengthMessageStreamData = new byte[] { 0 };
|
|
|
-
|
|
|
- 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)
|
|
|
- {
|
|
|
- // Message adapter is an internal class and we know that all the parsing will happen via InternalMergeFrom.
|
|
|
- throw new NotImplementedException();
|
|
|
- }
|
|
|
-
|
|
|
- [SecuritySafeCritical]
|
|
|
- public void InternalMergeFrom(ref ParseContext ctx)
|
|
|
- {
|
|
|
- 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)
|
|
|
- {
|
|
|
- if (ctx.state.CodedInputStream != null)
|
|
|
- {
|
|
|
- // the decoded message might not support parsing from ParseContext, so
|
|
|
- // we need to allow fallback to the legacy MergeFrom(CodedInputStream) parsing.
|
|
|
- Value = codec.valueCodec.Read(new CodedInputStream(ZeroLengthMessageStreamData));
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- ParseContext.Initialize(new ReadOnlySequence<byte>(ZeroLengthMessageStreamData), out ParseContext zeroLengthCtx);
|
|
|
- Value = codec.valueCodec.Read(ref zeroLengthCtx);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void WriteTo(CodedOutputStream output)
|
|
|
- {
|
|
|
- // Message adapter is an internal class and we know that all the writing will happen via InternalWriteTo.
|
|
|
- throw new NotImplementedException();
|
|
|
- }
|
|
|
-
|
|
|
- [SecuritySafeCritical]
|
|
|
- public void InternalWriteTo(ref WriteContext ctx)
|
|
|
- {
|
|
|
- codec.keyCodec.WriteTagAndValue(ref ctx, Key);
|
|
|
- codec.valueCodec.WriteTagAndValue(ref ctx, Value);
|
|
|
- }
|
|
|
-
|
|
|
- public int CalculateSize()
|
|
|
- {
|
|
|
- return codec.keyCodec.CalculateSizeWithTag(Key) + codec.valueCodec.CalculateSizeWithTag(Value);
|
|
|
- }
|
|
|
-
|
|
|
- MessageDescriptor IMessage.Descriptor { get { return null; } }
|
|
|
- }
|
|
|
+ internal FieldCodec<TKey> KeyCodec => keyCodec;
|
|
|
+ internal FieldCodec<TValue> ValueCodec => valueCodec;
|
|
|
}
|
|
|
|
|
|
private class MapView<T> : ICollection<T>, ICollection
|