public void ReadStringGreaterThanCurrentLimit() { MemoryStream ms = new MemoryStream(); CodedOutputStream output = new CodedOutputStream(ms); uint tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited); output.WriteRawVarint32(tag); output.WriteRawVarint32(4); output.WriteRawBytes(new byte[4]); // Pad with a few random bytes. output.Flush(); ms.Position = 0; CodedInputStream input = new CodedInputStream(ms.ToArray()); Assert.AreEqual(tag, input.ReadTag()); // Specify limit smaller than data length input.PushLimit(3); Assert.Throws <InvalidProtocolBufferException>(() => input.ReadString()); AssertReadFromParseContext(new ReadOnlySequence <byte>(ms.ToArray()), (ref ParseContext ctx) => { Assert.AreEqual(tag, ctx.ReadTag()); SegmentedBufferHelper.PushLimit(ref ctx.state, 3); try { ctx.ReadString(); Assert.Fail(); } catch (InvalidProtocolBufferException) { } }, true); }
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> /// Parses the next tag. /// If the end of logical stream was reached, an invalid tag of 0 is returned. /// </summary> public static uint ParseTag(ref ReadOnlySpan <byte> buffer, ref ParserInternalState state) { // The "nextTag" logic is there only as an optimization for reading non-packed repeated / map // fields and is strictly speaking not necessary. // TODO(jtattermusch): look into simplifying the ParseTag logic. if (state.hasNextTag) { state.lastTag = state.nextTag; state.hasNextTag = false; return(state.lastTag); } // Optimize for the incredibly common case of having at least two bytes left in the buffer, // and those two bytes being enough to get the tag. This will be true for fields up to 4095. if (state.bufferPos + 2 <= state.bufferSize) { int tmp = buffer[state.bufferPos++]; if (tmp < 128) { state.lastTag = (uint)tmp; } else { int result = tmp & 0x7f; if ((tmp = buffer[state.bufferPos++]) < 128) { result |= tmp << 7; state.lastTag = (uint)result; } else { // Nope, rewind and go the potentially slow route. state.bufferPos -= 2; state.lastTag = ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); } } } else { if (SegmentedBufferHelper.IsAtEnd(ref buffer, ref state)) { state.lastTag = 0; return(0); } state.lastTag = ParsingPrimitives.ParseRawVarint32(ref buffer, ref state); } if (WireFormat.GetTagFieldNumber(state.lastTag) == 0) { // If we actually read a tag with a field of 0, that's not a valid tag. throw InvalidProtocolBufferException.InvalidTag(); } return(state.lastTag); }
/// <summary> /// Creates a new CodedInputStream reading data from the given /// stream and buffer, using the default limits. /// </summary> internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, bool leaveOpen) { this.input = input; this.buffer = buffer; this.state.bufferPos = bufferPos; this.state.bufferSize = bufferSize; this.state.sizeLimit = DefaultSizeLimit; this.state.recursionLimit = DefaultRecursionLimit; SegmentedBufferHelper.Initialize(this, out this.state.segmentedBufferHelper); this.leaveOpen = leaveOpen; this.state.currentLimit = int.MaxValue; }
internal static void Initialize(ReadOnlySequence <byte> input, int recursionLimit, out ParseContext ctx) { ctx.buffer = default; ctx.state = default; ctx.state.lastTag = 0; ctx.state.recursionDepth = 0; ctx.state.sizeLimit = DefaultSizeLimit; ctx.state.recursionLimit = recursionLimit; ctx.state.currentLimit = int.MaxValue; SegmentedBufferHelper.Initialize(input, out ctx.state.segmentedBufferHelper, out ctx.buffer); ctx.state.bufferPos = 0; ctx.state.bufferSize = ctx.buffer.Length; ctx.state.DiscardUnknownFields = false; ctx.state.ExtensionRegistry = null; }
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 void Initialize(ReadOnlySequence <byte> sequence, out SegmentedBufferHelper instance, out ReadOnlySpan <byte> firstSpan) { instance.codedInputStream = null; if (sequence.IsSingleSegment) { firstSpan = sequence.First.Span; instance.totalLength = firstSpan.Length; instance.readOnlySequenceEnumerator = default; } else { instance.readOnlySequenceEnumerator = sequence.GetEnumerator(); instance.totalLength = (int)sequence.Length; // set firstSpan to the first segment instance.readOnlySequenceEnumerator.MoveNext(); firstSpan = instance.readOnlySequenceEnumerator.Current.Span; } }
public static void ReadMessage(ref ParseContext ctx, IMessage message) { 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; ReadRawMessage(ref ctx, message); 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); }
internal static T Read <T>(ref ParseContext ctx, FieldCodec <T> codec) { int length = ctx.ReadLength(); int oldLimit = SegmentedBufferHelper.PushLimit(ref ctx.state, length); uint tag; T value = codec.DefaultValue; while ((tag = ctx.ReadTag()) != 0) { if (tag == codec.Tag) { value = codec.Read(ref ctx); } else { ParsingPrimitivesMessages.SkipLastField(ref ctx.buffer, ref ctx.state); } } ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref ctx.state); SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit); return(value); }
/// <summary> /// Discards the current limit, returning the previous limit. /// </summary> internal void PopLimit(int oldLimit) { SegmentedBufferHelper.PopLimit(ref state, oldLimit); }
/// <summary> /// Sets currentLimit to (current position) + byteLimit. This is called /// when descending into a length-delimited embedded message. The previous /// limit is returned. /// </summary> /// <returns>The old limit.</returns> internal int PushLimit(int byteLimit) { return(SegmentedBufferHelper.PushLimit(ref state, byteLimit)); }
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)); }
public static void Initialize(CodedInputStream codedInputStream, out SegmentedBufferHelper instance) { instance.totalLength = codedInputStream.InternalInputStream == null ? (int?)codedInputStream.InternalBuffer.Length : null; instance.readOnlySequenceEnumerator = default; instance.codedInputStream = codedInputStream; }