Merge pull request #8473 from Jensaarai/ReadOnlySpan
C#: Add ParseFrom/MergeFrom ReadOnlySpan<byte>
This commit is contained in:
commit
f0da89d8b6
@ -161,12 +161,21 @@ namespace Google.Protobuf
|
||||
|
||||
private static void AssertReadFromParseContext(ReadOnlySequence<byte> input, ParseContextAssertAction assertAction, bool assertIsAtEnd)
|
||||
{
|
||||
// Check as ReadOnlySequence<byte>
|
||||
ParseContext.Initialize(input, out ParseContext parseCtx);
|
||||
assertAction(ref parseCtx);
|
||||
if (assertIsAtEnd)
|
||||
{
|
||||
Assert.IsTrue(SegmentedBufferHelper.IsAtEnd(ref parseCtx.buffer, ref parseCtx.state));
|
||||
}
|
||||
|
||||
// Check as ReadOnlySpan<byte>
|
||||
ParseContext.Initialize(input.ToArray().AsSpan(), out ParseContext spanParseContext);
|
||||
assertAction(ref spanParseContext);
|
||||
if (assertIsAtEnd)
|
||||
{
|
||||
Assert.IsTrue(SegmentedBufferHelper.IsAtEnd(ref spanParseContext.buffer, ref spanParseContext.state));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -41,32 +41,38 @@ namespace Google.Protobuf
|
||||
{
|
||||
public static void AssertReadingMessage<T>(MessageParser<T> parser, byte[] bytes, Action<T> assert) where T : IMessage<T>
|
||||
{
|
||||
var parsedStream = parser.ParseFrom(bytes);
|
||||
var parsedMsg = parser.ParseFrom(bytes);
|
||||
assert(parsedMsg);
|
||||
|
||||
// Load content as single segment
|
||||
var parsedBuffer = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
|
||||
assert(parsedBuffer);
|
||||
parsedMsg = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
|
||||
assert(parsedMsg);
|
||||
|
||||
// Load content as multiple segments
|
||||
parsedBuffer = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
|
||||
assert(parsedBuffer);
|
||||
parsedMsg = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
|
||||
assert(parsedMsg);
|
||||
|
||||
assert(parsedStream);
|
||||
// Load content as ReadOnlySpan
|
||||
parsedMsg = parser.ParseFrom(new ReadOnlySpan<byte>(bytes));
|
||||
assert(parsedMsg);
|
||||
}
|
||||
|
||||
public static void AssertReadingMessage(MessageParser parser, byte[] bytes, Action<IMessage> assert)
|
||||
{
|
||||
var parsedStream = parser.ParseFrom(bytes);
|
||||
var parsedMsg = parser.ParseFrom(bytes);
|
||||
assert(parsedMsg);
|
||||
|
||||
// Load content as single segment
|
||||
var parsedBuffer = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
|
||||
assert(parsedBuffer);
|
||||
parsedMsg = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
|
||||
assert(parsedMsg);
|
||||
|
||||
// Load content as multiple segments
|
||||
parsedBuffer = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
|
||||
assert(parsedBuffer);
|
||||
parsedMsg = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
|
||||
assert(parsedMsg);
|
||||
|
||||
assert(parsedStream);
|
||||
// Load content as ReadOnlySpan
|
||||
parsedMsg = parser.ParseFrom(new ReadOnlySpan<byte>(bytes));
|
||||
assert(parsedMsg);
|
||||
}
|
||||
|
||||
public static void AssertReadingMessageThrows<TMessage, TException>(MessageParser<TMessage> parser, byte[] bytes)
|
||||
@ -76,6 +82,8 @@ namespace Google.Protobuf
|
||||
Assert.Throws<TException>(() => parser.ParseFrom(bytes));
|
||||
|
||||
Assert.Throws<TException>(() => parser.ParseFrom(new ReadOnlySequence<byte>(bytes)));
|
||||
|
||||
Assert.Throws<TException>(() => parser.ParseFrom(new ReadOnlySpan<byte>(bytes)));
|
||||
}
|
||||
|
||||
public static void AssertRoundtrip<T>(MessageParser<T> parser, T message, Action<T> additionalAssert = null) where T : IMessage<T>
|
||||
@ -87,20 +95,24 @@ namespace Google.Protobuf
|
||||
message.WriteTo(bufferWriter);
|
||||
Assert.AreEqual(bytes, bufferWriter.WrittenSpan.ToArray(), "Both serialization approaches need to result in the same data.");
|
||||
|
||||
var parsedMsg = parser.ParseFrom(bytes);
|
||||
Assert.AreEqual(message, parsedMsg);
|
||||
additionalAssert?.Invoke(parsedMsg);
|
||||
|
||||
// Load content as single segment
|
||||
var parsedBuffer = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
|
||||
Assert.AreEqual(message, parsedBuffer);
|
||||
additionalAssert?.Invoke(parsedBuffer);
|
||||
parsedMsg = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
|
||||
Assert.AreEqual(message, parsedMsg);
|
||||
additionalAssert?.Invoke(parsedMsg);
|
||||
|
||||
// Load content as multiple segments
|
||||
parsedBuffer = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
|
||||
Assert.AreEqual(message, parsedBuffer);
|
||||
additionalAssert?.Invoke(parsedBuffer);
|
||||
parsedMsg = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
|
||||
Assert.AreEqual(message, parsedMsg);
|
||||
additionalAssert?.Invoke(parsedMsg);
|
||||
|
||||
var parsedStream = parser.ParseFrom(bytes);
|
||||
|
||||
Assert.AreEqual(message, parsedStream);
|
||||
additionalAssert?.Invoke(parsedStream);
|
||||
// Load content as ReadOnlySpan
|
||||
parsedMsg = parser.ParseFrom(new ReadOnlySpan<byte>(bytes));
|
||||
Assert.AreEqual(message, parsedMsg);
|
||||
additionalAssert?.Invoke(parsedMsg);
|
||||
}
|
||||
|
||||
public static void AssertWritingMessage(IMessage message)
|
||||
|
@ -435,8 +435,7 @@ namespace Google.Protobuf
|
||||
// we will need to switch back again to CodedInputStream-based parsing (which involves copying and storing the state) to be able to
|
||||
// invoke the legacy MergeFrom(CodedInputStream) method.
|
||||
// For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it).
|
||||
var span = new ReadOnlySpan<byte>(buffer);
|
||||
ParseContext.Initialize(ref span, ref state, out ParseContext ctx);
|
||||
ParseContext.Initialize(buffer.AsSpan(), ref state, out ParseContext ctx);
|
||||
try
|
||||
{
|
||||
ParsingPrimitivesMessages.ReadMessage(ref ctx, builder);
|
||||
|
@ -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>
|
||||
|
@ -58,8 +58,27 @@ namespace Google.Protobuf
|
||||
internal ReadOnlySpan<byte> buffer;
|
||||
internal ParserInternalState state;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a <see cref="ParseContext"/>, building all <see cref="ParserInternalState"/> from defaults and
|
||||
/// the given <paramref name="buffer"/>.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void Initialize(ref ReadOnlySpan<byte> buffer, ref ParserInternalState state, out ParseContext ctx)
|
||||
internal static void Initialize(ReadOnlySpan<byte> buffer, out ParseContext ctx)
|
||||
{
|
||||
ParserInternalState state = default;
|
||||
state.sizeLimit = DefaultSizeLimit;
|
||||
state.recursionLimit = DefaultRecursionLimit;
|
||||
state.currentLimit = int.MaxValue;
|
||||
state.bufferSize = buffer.Length;
|
||||
|
||||
Initialize(buffer, ref state, out ctx);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a <see cref="ParseContext"/> using existing <see cref="ParserInternalState"/>, e.g. from <see cref="CodedInputStream"/>.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void Initialize(ReadOnlySpan<byte> buffer, ref ParserInternalState state, out ParseContext ctx)
|
||||
{
|
||||
ctx.buffer = buffer;
|
||||
ctx.state = state;
|
||||
|
Loading…
Reference in New Issue
Block a user