public void LegacyGeneratedCodeThrowsWithReadOnlySequence() { var message = new ParseContextEnabledMessageB { A = new LegacyGeneratedCodeMessageA { Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 } }, OptionalInt32 = 6789 }; var data = message.ToByteArray(); // if parsing started using ReadOnlySequence and we don't have a CodedInputStream // instance at hand, we cannot fall back to the legacy MergeFrom(CodedInputStream) // method and parsing will fail. As a consequence, one can only use parsing // from ReadOnlySequence if all the messages in the parsing tree have their generated // code up to date. var exception = Assert.Throws <InvalidProtocolBufferException>(() => { ParseContext.Initialize(new ReadOnlySequence <byte>(data), out ParseContext parseCtx); var parsed = new ParseContextEnabledMessageB(); ParsingPrimitivesMessages.ReadRawMessage(ref parseCtx, parsed); }); Assert.AreEqual($"Message {typeof(LegacyGeneratedCodeMessageA).Name} doesn't provide the generated method that enables ParseContext-based parsing. You might need to regenerate the generated protobuf code.", exception.Message); }
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); }
private static void AssertReadFromParseContext(ReadOnlySequence <byte> input, ParseContextAssertAction assertAction, bool assertIsAtEnd) { ParseContext.Initialize(input, out ParseContext parseCtx); assertAction(ref parseCtx); if (assertIsAtEnd) { Assert.IsTrue(SegmentedBufferHelper.IsAtEnd(ref parseCtx.buffer, ref parseCtx.state)); } }
/// <summary> /// Reads a top-level message or a nested message after the limits for this message have been pushed. /// (parser will proceed until the end of the current limit) /// NOTE: this method needs to be public because it's invoked by the generated code - e.g. msg.MergeFrom(CodedInputStream input) method /// </summary> public void ReadRawMessage(IMessage message) { ParseContext.Initialize(this, out ParseContext ctx); try { ParsingPrimitivesMessages.ReadRawMessage(ref ctx, message); } finally { ctx.CopyStateTo(this); } }
/// <summary> /// Reads an embedded group field from the stream. /// </summary> public void ReadGroup(IMessage builder) { ParseContext.Initialize(this, out ParseContext ctx); try { ParsingPrimitivesMessages.ReadGroup(ref ctx, builder); } finally { ctx.CopyStateTo(this); } }
/// <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, CodedInputStream stream) where TTarget : IExtendableMessage <TTarget> { ParseContext.Initialize(stream, out ParseContext ctx); try { return(TryMergeFieldFrom <TTarget>(ref set, ref ctx)); } finally { ctx.CopyStateTo(stream); } }
/// <summary> /// Create a new UnknownFieldSet if unknownFields is null. /// Parse a single field from <paramref name="input"/> and merge it /// into unknownFields. If <paramref name="input"/> is configured to discard unknown fields, /// <paramref name="unknownFields"/> will be returned as-is and the field will be skipped. /// </summary> /// <param name="unknownFields">The UnknownFieldSet which need to be merged</param> /// <param name="input">The coded input stream containing the field</param> /// <returns>The merged UnknownFieldSet</returns> public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields, CodedInputStream input) { ParseContext.Initialize(input, out ParseContext ctx); try { return(MergeFieldFrom(unknownFields, ref ctx)); } finally { ctx.CopyStateTo(input); } }
/// <summary> /// Reads an embedded message field value from the stream. /// </summary> public void ReadMessage(IMessage builder) { // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalMergeFrom method), // what we're doing here works fine, but could be more efficient. // What happens is that we first initialize a ParseContext from the current coded input stream only to parse the length of the message, at which point // 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). ParseContext.Initialize(buffer.AsSpan(), ref state, out ParseContext ctx); try { ParsingPrimitivesMessages.ReadMessage(ref ctx, builder); } finally { ctx.CopyStateTo(this); } }
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)); } }
public static KeyValuePair <TKey, TValue> ReadMapEntry <TKey, TValue>(ref ParseContext ctx, MapField <TKey, TValue> .Codec codec) { int length = ParsingPrimitives.ParseLength(ref ctx.buffer, ref ctx.state); if (ctx.state.recursionDepth >= ctx.state.recursionLimit) { throw InvalidProtocolBufferException.RecursionLimitExceeded(); } int oldLimit = SegmentedBufferHelper.PushLimit(ref ctx.state, length); ++ctx.state.recursionDepth; TKey key = codec.KeyCodec.DefaultValue; TValue value = codec.ValueCodec.DefaultValue; 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 { 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); } } CheckReadEndOfStreamTag(ref ctx.state); // Check that we've read exactly as much data as expected. if (!SegmentedBufferHelper.IsReachedLimit(ref ctx.state)) { throw InvalidProtocolBufferException.TruncatedMessage(); } --ctx.state.recursionDepth; SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit); return(new KeyValuePair <TKey, TValue>(key, value)); }