C#: Add ParseFrom/MergeFrom ReadOnlySpan<byte>

Address #7885
pull/8473/head
Jensaarai 4 years ago
parent ee04809540
commit 76e3ffd9f0
  1. 33
      csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs
  2. 19
      csharp/src/Google.Protobuf/MessageExtensions.cs
  3. 26
      csharp/src/Google.Protobuf/MessageParser.cs
  4. 23
      csharp/src/Google.Protobuf/ParseContext.cs

@ -926,6 +926,39 @@ namespace Google.Protobuf
}
}
[Test]
public void TestParseFromSpan()
{
var testMessage = new TestAllTypes
{
RepeatedBool = { true, false },
SingleDouble = 42.0d,
SingleString = "testing"
};
ReadOnlySpan<byte> messagesBytes = testMessage.ToByteArray().AsSpan();
TestAllTypes parsedMessage = TestAllTypes.Parser.ParseFrom(messagesBytes);
Assert.AreEqual(testMessage, parsedMessage);
}
[Test]
public void TestMergeFromSpan()
{
var testMessage = new TestAllTypes
{
RepeatedBool = { true, false },
SingleDouble = 42.0d,
SingleString = "testing"
};
ReadOnlySpan<byte> messagesBytes = testMessage.ToByteArray().AsSpan();
var mergedMessage = new TestAllTypes();
mergedMessage.MergeFrom(messagesBytes);
Assert.AreEqual(testMessage, mergedMessage);
}
/// <returns>A serialized big message</returns>
private static byte[] GenerateBigSerializedMessage()
{

@ -79,6 +79,15 @@ namespace Google.Protobuf
public static void MergeFrom(this IMessage message, Stream input) =>
MergeFrom(message, input, false, null);
/// <summary>
/// Merges data from the given span into an existing message.
/// </summary>
/// <param name="message">The message to merge the data into.</param>
/// <param name="span">Span containing the data to merge, which must be protobuf-encoded binary data.</param>
[SecuritySafeCritical]
public static void MergeFrom(this IMessage message, ReadOnlySpan<byte> span) =>
MergeFrom(message, span, false, null);
/// <summary>
/// Merges length-delimited data from the given stream into an existing message.
/// </summary>
@ -294,6 +303,16 @@ namespace Google.Protobuf
ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref ctx.state);
}
[SecuritySafeCritical]
internal static void MergeFrom(this IMessage message, ReadOnlySpan<byte> data, bool discardUnknownFields, ExtensionRegistry registry)
{
ParseContext.Initialize(data, out ParseContext ctx);
ctx.DiscardUnknownFields = discardUnknownFields;
ctx.ExtensionRegistry = registry;
ParsingPrimitivesMessages.ReadRawMessage(ref ctx, message);
ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref ctx.state);
}
internal static void MergeDelimitedFrom(this IMessage message, Stream input, bool discardUnknownFields, ExtensionRegistry registry)
{
ProtoPreconditions.CheckNotNull(message, "message");

@ -128,6 +128,19 @@ namespace Google.Protobuf
return message;
}
/// <summary>
/// Parses a message from the given span.
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>The parsed message.</returns>
[SecuritySafeCritical]
public IMessage ParseFrom(ReadOnlySpan<byte> data)
{
IMessage message = factory();
message.MergeFrom(data, DiscardUnknownFields, Extensions);
return message;
}
/// <summary>
/// Parses a length-delimited message from the given stream.
/// </summary>
@ -315,6 +328,19 @@ namespace Google.Protobuf
return message;
}
/// <summary>
/// Parses a message from the given span.
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>The parsed message.</returns>
[SecuritySafeCritical]
public new T ParseFrom(ReadOnlySpan<byte> data)
{
T message = factory();
message.MergeFrom(data, DiscardUnknownFields, Extensions);
return message;
}
/// <summary>
/// Parses a length-delimited message from the given stream.
/// </summary>

@ -104,6 +104,29 @@ namespace Google.Protobuf
ctx.state.ExtensionRegistry = null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Initialize(ReadOnlySpan<byte> input, out ParseContext ctx)
{
Initialize(input, DefaultRecursionLimit, out ctx);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Initialize(ReadOnlySpan<byte> input, int recursionLimit, out ParseContext ctx)
{
ctx.buffer = input;
ctx.state = default;
ctx.state.lastTag = 0;
ctx.state.recursionDepth = 0;
ctx.state.sizeLimit = DefaultSizeLimit;
ctx.state.recursionLimit = recursionLimit;
ctx.state.currentLimit = int.MaxValue;
ctx.state.bufferPos = 0;
ctx.state.bufferSize = input.Length;
ctx.state.DiscardUnknownFields = false;
ctx.state.ExtensionRegistry = null;
}
/// <summary>
/// Returns the last tag read, or 0 if no tags have been read or we've read beyond
/// the end of the input.

Loading…
Cancel
Save