#Region "Microsoft.VisualBasic::e26ed07920e1f0909beca743bb32e2ec, Google.Protobuf\Stream\CodedInputStream.vb" ' Author: ' ' asuka (amethyst.asuka@gcmodeller.org) ' xie (genetics@smrucc.org) ' xieguigang (xie.guigang@live.com) ' ' Copyright (c) 2018 GPL3 Licensed ' ' ' GNU GENERAL PUBLIC LICENSE (GPL3) ' ' ' This program is free software: you can redistribute it and/or modify ' it under the terms of the GNU General Public License as published by ' the Free Software Foundation, either version 3 of the License, or ' (at your option) any later version. ' ' This program is distributed in the hope that it will be useful, ' but WITHOUT ANY WARRANTY; without even the implied warranty of ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ' GNU General Public License for more details. ' ' You should have received a copy of the GNU General Public License ' along with this program. If not, see . ' /********************************************************************************/ ' Summaries: ' Class CodedInputStream ' ' Properties: IsAtEnd, LastTag, Position, ReachedLimit, RecursionLimit ' SizeLimit ' ' Constructor: (+6 Overloads) Sub New ' ' Function: CreateWithLimits, DecodeZigZag32, DecodeZigZag64, MaybeConsumeTag, PeekTag ' PushLimit, ReadBool, ReadBytes, ReadDouble, ReadEnum ' ReadFixed32, ReadFixed64, ReadFloat, ReadInt32, ReadInt64 ' ReadLength, ReadRawByte, ReadRawBytes, ReadRawLittleEndian32, ReadRawLittleEndian64 ' (+2 Overloads) ReadRawVarint32, ReadRawVarint64, ReadSFixed32, ReadSFixed64, ReadSInt32 ' ReadSInt64, ReadString, ReadTag, ReadUInt32, ReadUInt64 ' RefillBuffer, SlowReadRawVarint32 ' ' Sub: CheckReadEndOfStreamTag, Dispose, PopLimit, ReadMessage, RecomputeBufferSizeAfterLimit ' SkipGroup, SkipImpl, SkipLastField, SkipRawBytes ' ' ' /********************************************************************************/ #End Region #Region "Copyright notice and license" ' Protocol Buffers - Google's data interchange format ' Copyright 2008 Google Inc. All rights reserved. ' https://developers.google.com/protocol-buffers/ ' ' Redistribution and use in source and binary forms, with or without ' modification, are permitted provided that the following conditions are ' met: ' ' * Redistributions of source code must retain the above copyright ' notice, this list of conditions and the following disclaimer. ' * Redistributions in binary form must reproduce the above ' copyright notice, this list of conditions and the following disclaimer ' in the documentation and/or other materials provided with the ' distribution. ' * Neither the name of Google Inc. nor the names of its ' contributors may be used to endorse or promote products derived from ' this software without specific prior written permission. ' ' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ' "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ' LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ' A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ' OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ' SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ' LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ' DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ' THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ' (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ' OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #End Region Imports System.IO Imports Microsoft.VisualBasic.Language Namespace Google.Protobuf ''' ''' Reads and decodes protocol message fields. ''' ''' ''' ''' This class is generally used by generated code to read appropriate ''' primitives from the stream. It effectively encapsulates the lowest ''' levels of protocol buffer format. ''' ''' ''' Repeated fields and map fields are not handled by this class; use ''' and to serialize such fields. ''' ''' Public NotInheritable Class CodedInputStream Implements IDisposable ''' ''' Whether to leave the underlying stream open when disposing of this stream. ''' This is always true when there's no stream. ''' Private ReadOnly leaveOpen As Boolean ''' ''' Buffer of data read from the stream or provided at construction time. ''' Private ReadOnly buffer As Byte() ''' ''' The index of the buffer at which we need to refill from the stream (if there is one). ''' Private bufferSizeField As Integer Private bufferSizeAfterLimit As Integer = 0 ''' ''' The position within the current buffer (i.e. the next byte to read) ''' Private bufferPos As Integer = 0 ''' ''' The stream to read further input from, or null if the byte array buffer was provided ''' directly on construction, with no further data available. ''' Private ReadOnly input As Stream ''' ''' The last tag we read. 0 indicates we've read to the end of the stream ''' (or haven't read anything yet). ''' Private lastTagField As UInteger = 0 ''' ''' The next tag, used to store the value read by PeekTag. ''' Private nextTag As UInteger = 0 Private hasNextTag As Boolean = False Friend Const DefaultRecursionLimit As Integer = 64 Friend Const DefaultSizeLimit As Integer = 64 << 20 ' 64MB Friend Const BufferSize As Integer = 4096 ''' ''' The total number of bytes read before the current buffer. The ''' total bytes read up to the current position can be computed as ''' totalBytesRetired + bufferPos. ''' Private totalBytesRetired As Integer = 0 ''' ''' The absolute position of the end of the current message. ''' Private currentLimit As Integer = Integer.MaxValue Private recursionDepth As Integer = 0 Private ReadOnly recursionLimitField As Integer Private ReadOnly sizeLimitField As Integer #Region "Construction" ' Note that the checks are performed such that we don't end up checking obviously-valid things ' like non-null references for arrays we've just created. ''' ''' Creates a new CodedInputStream reading data from the given byte array. ''' Public Sub New(buffer As Byte()) Me.New(Nothing, CheckNotNull(buffer, "buffer"), 0, buffer.Length) End Sub ''' ''' Creates a new that reads from the given byte array slice. ''' Public Sub New(buffer As Byte(), offset As Integer, length As Integer) Me.New(Nothing, CheckNotNull(buffer, "buffer"), offset, offset + length) If offset < 0 OrElse offset > buffer.Length Then Throw New ArgumentOutOfRangeException("offset", "Offset must be within the buffer") End If If length < 0 OrElse offset + length > buffer.Length Then Throw New ArgumentOutOfRangeException("length", "Length must be non-negative and within the buffer") End If End Sub ''' ''' Creates a new reading data from the given stream, which will be disposed ''' when the returned object is disposed. ''' ''' The stream to read from. Public Sub New(input As Stream) Me.New(input, False) End Sub ''' ''' Creates a new reading data from the given stream. ''' ''' The stream to read from. ''' true to leave open when the returned ''' is disposed; false to dispose of the given stream when the ''' returned object is disposed. Public Sub New(input As Stream, leaveOpen As Boolean) Me.New(CheckNotNull(input, "input"), New Byte(4095) {}, 0, 0) Me.leaveOpen = leaveOpen End Sub ''' ''' Creates a new CodedInputStream reading data from the given ''' stream and buffer, using the default limits. ''' Friend Sub New(input As Stream, buffer As Byte(), bufferPos As Integer, bufferSize As Integer) Me.input = input Me.buffer = buffer Me.bufferPos = bufferPos bufferSizeField = bufferSize sizeLimitField = DefaultSizeLimit recursionLimitField = DefaultRecursionLimit End Sub ''' ''' Creates a new CodedInputStream reading data from the given ''' stream and buffer, using the specified limits. ''' ''' ''' This chains to the version with the default limits instead of vice versa to avoid ''' having to check that the default values are valid every time. ''' Friend Sub New(input As Stream, buffer As Byte(), bufferPos As Integer, bufferSize As Integer, sizeLimit As Integer, recursionLimit As Integer) Me.New(input, buffer, bufferPos, bufferSize) If sizeLimit <= 0 Then Throw New ArgumentOutOfRangeException("sizeLimit", "Size limit must be positive") End If If recursionLimit <= 0 Then Throw New ArgumentOutOfRangeException("recursionLimit!", "Recursion limit must be positive") End If sizeLimitField = sizeLimit recursionLimitField = recursionLimit End Sub #End Region ''' ''' Creates a with the specified size and recursion limits, reading ''' from an input stream. ''' ''' ''' This method exists separately from the constructor to reduce the number of constructor overloads. ''' It is likely to be used considerably less frequently than the constructors, as the default limits ''' are suitable for most use cases. ''' ''' The input stream to read from ''' The total limit of data to read from the stream. ''' The maximum recursion depth to allow while reading. ''' A CodedInputStream reading from with the specified size ''' and recursion limits. Public Shared Function CreateWithLimits(input As Stream, sizeLimit As Integer, recursionLimit As Integer) As CodedInputStream Return New CodedInputStream(input, New Byte(4095) {}, 0, 0, sizeLimit, recursionLimit) End Function ''' ''' Returns the current position in the input stream, or the position in the input buffer ''' Public ReadOnly Property Position As Long Get If input IsNot Nothing Then Return input.Position - (bufferSizeField + bufferSizeAfterLimit - bufferPos) End If Return bufferPos End Get End Property ''' ''' Returns the last tag read, or 0 if no tags have been read or we've read beyond ''' the end of the stream. ''' Friend ReadOnly Property LastTag As UInteger Get Return lastTagField End Get End Property ''' ''' Returns the size limit for this stream. ''' ''' ''' This limit is applied when reading from the underlying stream, as a sanity check. It is ''' not applied when reading from a byte array data source without an underlying stream. ''' The default value is 64MB. ''' ''' ''' The size limit. ''' Public ReadOnly Property SizeLimit As Integer Get Return sizeLimitField End Get End Property ''' ''' Returns the recursion limit for this stream. This limit is applied whilst reading messages, ''' to avoid maliciously-recursive data. ''' ''' ''' The default limit is 64. ''' ''' ''' The recursion limit for this stream. ''' Public ReadOnly Property RecursionLimit As Integer Get Return recursionLimitField End Get End Property ''' ''' Disposes of this instance, potentially closing any underlying stream. ''' ''' ''' As there is no flushing to perform here, disposing of a which ''' was constructed with the leaveOpen option parameter set to true (or one which ''' was constructed to read from a byte array) has no effect. ''' Public Sub Dispose() Implements IDisposable.Dispose If Not leaveOpen Then input.Dispose() End If End Sub #Region "Validation" ''' ''' Verifies that the last call to ReadTag() returned tag 0 - in other words, ''' we've reached the end of the stream when we expected to. ''' ''' The ''' tag read was not the one specified Friend Sub CheckReadEndOfStreamTag() If lastTagField <> 0 Then Throw InvalidProtocolBufferException.MoreDataAvailable() End If End Sub #End Region #Region "Reading of tags etc" ''' ''' Peeks at the next field tag. This is like calling , but the ''' tag is not consumed. (So a subsequent call to will return the ''' same value.) ''' Public Function PeekTag() As UInteger If hasNextTag Then Return nextTag End If Dim savedLast = lastTagField nextTag = ReadTag() hasNextTag = True lastTagField = savedLast ' Undo the side effect of ReadTag Return nextTag End Function ''' ''' Reads a field tag, returning the tag of 0 for "end of stream". ''' ''' ''' If this method returns 0, it doesn't necessarily mean the end of all ''' the data in this CodedInputStream; it may be the end of the logical stream ''' for an embedded message, for example. ''' ''' The next field tag, or 0 for end of stream. (0 is never a valid tag.) Public Function ReadTag() As UInteger If hasNextTag Then lastTagField = nextTag hasNextTag = False Return lastTagField End If ' 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 bufferPos + 2 <= bufferSizeField Then Dim tmp As i32 = buffer(Math.Min(Threading.Interlocked.Increment(bufferPos), bufferPos - 1)) If tmp < 128 Then lastTagField = CUInt(tmp) Else Dim result = tmp And &H7F If (tmp = buffer(Math.Min(Threading.Interlocked.Increment(bufferPos), bufferPos - 1))) < 128 Then result = result Or tmp << 7 lastTagField = CUInt(result) Else ' Nope, rewind and go the potentially slow route. bufferPos -= 2 lastTagField = ReadRawVarint32() End If End If Else If IsAtEnd Then lastTagField = 0 Return 0 ' This is the only case in which we return 0. End If lastTagField = ReadRawVarint32() End If If lastTagField = 0 Then ' If we actually read zero, that's not a valid tag. Throw InvalidProtocolBufferException.InvalidTag() End If Return lastTagField End Function ''' ''' Skips the data for the field with the tag we've just read. ''' This should be called directly after , when ''' the caller wishes to skip an unknown field. ''' ''' ''' This method throws if the last-read tag was an end-group tag. ''' If a caller wishes to skip a group, they should skip the whole group, by calling this method after reading the ''' start-group tag. This behavior allows callers to call this method on any field they don't understand, correctly ''' resulting in an error if an end-group tag has not been paired with an earlier start-group tag. ''' ''' The last tag was an end-group tag ''' The last read operation read to the end of the logical stream Public Sub SkipLastField() If lastTagField = 0 Then Throw New InvalidOperationException("SkipLastField cannot be called at the end of a stream") End If Select Case GetTagWireType(lastTagField) Case WireType.StartGroup SkipGroup(lastTagField) Case WireType.EndGroup Throw New InvalidProtocolBufferException("SkipLastField called on an end-group tag, indicating that the corresponding start-group was missing") Case WireType.Fixed32 ReadFixed32() Case WireType.Fixed64 ReadFixed64() Case WireType.LengthDelimited Dim length = ReadLength() SkipRawBytes(length) Case WireType.Varint ReadRawVarint32() End Select End Sub Private Sub SkipGroup(startGroupTag As UInteger) ' Note: Currently we expect this to be the way that groups are read. We could put the recursion ' depth changes into the ReadTag method instead, potentially... recursionDepth += 1 If recursionDepth >= recursionLimitField Then Throw InvalidProtocolBufferException.RecursionLimitExceeded() End If Dim tag As UInteger While True tag = ReadTag() If tag = 0 Then Throw InvalidProtocolBufferException.TruncatedMessage() End If ' Can't call SkipLastField for this case- that would throw. If GetTagWireType(tag) = WireType.EndGroup Then Exit While End If ' This recursion will allow us to handle nested groups. SkipLastField() End While Dim startField = GetTagFieldNumber(startGroupTag) Dim endField = GetTagFieldNumber(tag) If startField <> endField Then Throw New InvalidProtocolBufferException($"Mismatched end-group tag. Started with field {startField}; ended with field {endField}") End If recursionDepth -= 1 End Sub ''' ''' Reads a double field from the stream. ''' Public Function ReadDouble() As Double Return BitConverter.Int64BitsToDouble(CLng(ReadRawLittleEndian64())) End Function ''' ''' Reads a float field from the stream. ''' Public Function ReadFloat() As Single If BitConverter.IsLittleEndian AndAlso 4 <= bufferSizeField - bufferPos Then Dim ret = BitConverter.ToSingle(buffer, bufferPos) bufferPos += 4 Return ret Else Dim rawBytes = ReadRawBytes(4) If Not BitConverter.IsLittleEndian Then Reverse(rawBytes) End If Return BitConverter.ToSingle(rawBytes, 0) End If End Function ''' ''' Reads a uint64 field from the stream. ''' Public Function ReadUInt64() As ULong Return ReadRawVarint64() End Function ''' ''' Reads an int64 field from the stream. ''' Public Function ReadInt64() As Long Return CLng(ReadRawVarint64()) End Function ''' ''' Reads an int32 field from the stream. ''' Public Function ReadInt32() As Integer Return CInt(ReadRawVarint32()) End Function ''' ''' Reads a fixed64 field from the stream. ''' Public Function ReadFixed64() As ULong Return ReadRawLittleEndian64() End Function ''' ''' Reads a fixed32 field from the stream. ''' Public Function ReadFixed32() As UInteger Return ReadRawLittleEndian32() End Function ''' ''' Reads a bool field from the stream. ''' Public Function ReadBool() As Boolean Return ReadRawVarint32() <> 0 End Function ''' ''' Reads a string field from the stream. ''' Public Function ReadString() As String Dim length As Integer = ReadLength() ' No need to read any data for an empty string. If length = 0 Then Return "" End If If length <= bufferSizeField - bufferPos Then ' Fast path: We already have the bytes in a contiguous buffer, so ' just copy directly from it. Dim result = CodedOutputStream.Utf8Encoding.GetString(buffer, bufferPos, length) bufferPos += length Return result End If ' Slow path: Build a byte array first then copy it. Return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(length), 0, length) End Function ''' ''' Reads an embedded message field value from the stream. ''' Public Sub ReadMessage(builder As IMessage) Dim length As Integer = ReadLength() If recursionDepth >= recursionLimitField Then Throw InvalidProtocolBufferException.RecursionLimitExceeded() End If Dim oldLimit = PushLimit(length) Threading.Interlocked.Increment(recursionDepth) builder.MergeFrom(Me) CheckReadEndOfStreamTag() ' Check that we've read exactly as much data as expected. If Not ReachedLimit Then Throw InvalidProtocolBufferException.TruncatedMessage() End If Threading.Interlocked.Decrement(recursionDepth) PopLimit(oldLimit) End Sub ''' ''' Reads a bytes field value from the stream. ''' Public Function ReadBytes() As ByteString Dim length As Integer = ReadLength() If length <= bufferSizeField - bufferPos AndAlso length > 0 Then ' Fast path: We already have the bytes in a contiguous buffer, so ' just copy directly from it. Dim result = ByteString.CopyFrom(buffer, bufferPos, length) bufferPos += length Return result Else ' Slow path: Build a byte array and attach it to a new ByteString. Return ByteString.AttachBytes(ReadRawBytes(length)) End If End Function ''' ''' Reads a uint32 field value from the stream. ''' Public Function ReadUInt32() As UInteger Return ReadRawVarint32() End Function ''' ''' Reads an enum field value from the stream. ''' Public Function ReadEnum() As Integer ' Currently just a pass-through, but it's nice to separate it logically from WriteInt32. Return CInt(ReadRawVarint32()) End Function ''' ''' Reads an sfixed32 field value from the stream. ''' Public Function ReadSFixed32() As Integer Return CInt(ReadRawLittleEndian32()) End Function ''' ''' Reads an sfixed64 field value from the stream. ''' Public Function ReadSFixed64() As Long Return CLng(ReadRawLittleEndian64()) End Function ''' ''' Reads an sint32 field value from the stream. ''' Public Function ReadSInt32() As Integer Return DecodeZigZag32(ReadRawVarint32()) End Function ''' ''' Reads an sint64 field value from the stream. ''' Public Function ReadSInt64() As Long Return DecodeZigZag64(ReadRawVarint64()) End Function ''' ''' Reads a length for length-delimited data. ''' ''' ''' This is internally just reading a varint, but this method exists ''' to make the calling code clearer. ''' Public Function ReadLength() As Integer Return CInt(ReadRawVarint32()) End Function ''' ''' Peeks at the next tag in the stream. If it matches , ''' the tag is consumed and the method returns true; otherwise, the ''' stream is left in the original position and the method returns false. ''' Public Function MaybeConsumeTag(tag As UInteger) As Boolean If PeekTag() = tag Then hasNextTag = False Return True End If Return False End Function #End Region #Region "Underlying reading primitives" ''' ''' Same code as ReadRawVarint32, but read each byte individually, checking for ''' buffer overflow. ''' Private Function SlowReadRawVarint32() As UInteger Dim tmp As i32 = ReadRawByte() If tmp < 128 Then Return tmp End If Dim result = tmp And &H7F If (tmp = ReadRawByte()) < 128 Then result = result Or tmp << 7 Else result = result Or (tmp And &H7F) << 7 If (tmp = ReadRawByte()) < 128 Then result = result Or tmp << 14 Else result = result Or (tmp And &H7F) << 14 If (tmp = ReadRawByte()) < 128 Then result = result Or tmp << 21 Else result = result Or (tmp And &H7F) << 21 result = result Or (tmp = ReadRawByte()) << 28 If tmp >= 128 Then ' Discard upper 32 bits. For i = 0 To 5 - 1 If ReadRawByte() < 128 Then Return result End If Next Throw InvalidProtocolBufferException.MalformedVarint() End If End If End If End If Return result End Function ''' ''' Reads a raw Varint from the stream. If larger than 32 bits, discard the upper bits. ''' This method is optimised for the case where we've got lots of data in the buffer. ''' That means we can check the size just once, then just read directly from the buffer ''' without constant rechecking of the buffer length. ''' Friend Function ReadRawVarint32() As UInteger If bufferPos + 5 > bufferSizeField Then Return SlowReadRawVarint32() End If Dim tmp As i32 = buffer(Math.Min(Threading.Interlocked.Increment(bufferPos), bufferPos - 1)) If tmp < 128 Then Return tmp End If Dim result = tmp And &H7F If (tmp = buffer(Math.Min(Threading.Interlocked.Increment(bufferPos), bufferPos - 1))) < 128 Then result = result Or tmp << 7 Else result = result Or (tmp And &H7F) << 7 If (tmp = buffer(Math.Min(Threading.Interlocked.Increment(bufferPos), bufferPos - 1))) < 128 Then result = result Or tmp << 14 Else result = result Or (tmp And &H7F) << 14 If (tmp = buffer(Math.Min(Threading.Interlocked.Increment(bufferPos), bufferPos - 1))) < 128 Then result = result Or tmp << 21 Else result = result Or (tmp And &H7F) << 21 result = result Or (tmp = buffer(Math.Min(Threading.Interlocked.Increment(bufferPos), bufferPos - 1))) << 28 If tmp >= 128 Then ' Discard upper 32 bits. ' Note that this has to use ReadRawByte() as we only ensure we've ' got at least 5 bytes at the start of the method. This lets us ' use the fast path in more cases, and we rarely hit this section of code. For i = 0 To 5 - 1 If ReadRawByte() < 128 Then Return result End If Next Throw InvalidProtocolBufferException.MalformedVarint() End If End If End If End If Return result End Function ''' ''' Reads a varint from the input one byte at a time, so that it does not ''' read any bytes after the end of the varint. If you simply wrapped the ''' stream in a CodedInputStream and used ReadRawVarint32(Stream) ''' then you would probably end up reading past the end of the varint since ''' CodedInputStream buffers its input. ''' ''' ''' Friend Shared Function ReadRawVarint32(input As Stream) As UInteger Dim result = 0 Dim offset = 0 While offset < 32 Dim b As Integer = input.ReadByte() If b = -1 Then Throw InvalidProtocolBufferException.TruncatedMessage() End If result = result Or (b And &H7F) << offset If (b And &H80) = 0 Then Return result End If offset += 7 End While ' Keep reading up to 64 bits. While offset < 64 Dim b As Integer = input.ReadByte() If b = -1 Then Throw InvalidProtocolBufferException.TruncatedMessage() End If If (b And &H80) = 0 Then Return result End If offset += 7 End While Throw InvalidProtocolBufferException.MalformedVarint() End Function ''' ''' Reads a raw varint from the stream. ''' Friend Function ReadRawVarint64() As ULong Dim shift = 0 Dim result As ULong = 0 While shift < 64 Dim b As Byte = ReadRawByte() result = result Or CULng(b And &H7F) << shift If (b And &H80) = 0 Then Return result End If shift += 7 End While Throw InvalidProtocolBufferException.MalformedVarint() End Function ''' ''' Reads a 32-bit little-endian integer from the stream. ''' Friend Function ReadRawLittleEndian32() As UInteger Dim b1 As UInteger = ReadRawByte() Dim b2 As UInteger = ReadRawByte() Dim b3 As UInteger = ReadRawByte() Dim b4 As UInteger = ReadRawByte() Return b1 Or b2 << 8 Or b3 << 16 Or b4 << 24 End Function ''' ''' Reads a 64-bit little-endian integer from the stream. ''' Friend Function ReadRawLittleEndian64() As ULong Dim b1 As ULong = ReadRawByte() Dim b2 As ULong = ReadRawByte() Dim b3 As ULong = ReadRawByte() Dim b4 As ULong = ReadRawByte() Dim b5 As ULong = ReadRawByte() Dim b6 As ULong = ReadRawByte() Dim b7 As ULong = ReadRawByte() Dim b8 As ULong = ReadRawByte() Return b1 Or b2 << 8 Or b3 << 16 Or b4 << 24 Or b5 << 32 Or b6 << 40 Or b7 << 48 Or b8 << 56 End Function ''' ''' Decode a 32-bit value with ZigZag encoding. ''' ''' ''' ZigZag encodes signed integers into values that can be efficiently ''' encoded with varint. (Otherwise, negative values must be ''' sign-extended to 64 bits to be varint encoded, thus always taking ''' 10 bytes on the wire.) ''' Friend Shared Function DecodeZigZag32(n As UInteger) As Integer Return CInt(n >> 1) Xor -CInt(n And 1) End Function ''' ''' Decode a 32-bit value with ZigZag encoding. ''' ''' ''' ZigZag encodes signed integers into values that can be efficiently ''' encoded with varint. (Otherwise, negative values must be ''' sign-extended to 64 bits to be varint encoded, thus always taking ''' 10 bytes on the wire.) ''' Friend Shared Function DecodeZigZag64(n As ULong) As Long Return n >> 1 Xor -(n And 1) End Function #End Region #Region "Internal reading and buffer management" ''' ''' Sets currentLimit to (current position) + byteLimit. This is called ''' when descending into a length-delimited embedded message. The previous ''' limit is returned. ''' ''' The old limit. Friend Function PushLimit(byteLimit As Integer) As Integer If byteLimit < 0 Then Throw InvalidProtocolBufferException.NegativeSize() End If byteLimit += totalBytesRetired + bufferPos Dim oldLimit = currentLimit If byteLimit > oldLimit Then Throw InvalidProtocolBufferException.TruncatedMessage() End If currentLimit = byteLimit RecomputeBufferSizeAfterLimit() Return oldLimit End Function Private Sub RecomputeBufferSizeAfterLimit() bufferSizeField += bufferSizeAfterLimit Dim bufferEnd = totalBytesRetired + bufferSizeField If bufferEnd > currentLimit Then ' Limit is in current buffer. bufferSizeAfterLimit = bufferEnd - currentLimit bufferSizeField -= bufferSizeAfterLimit Else bufferSizeAfterLimit = 0 End If End Sub ''' ''' Discards the current limit, returning the previous limit. ''' Friend Sub PopLimit(oldLimit As Integer) currentLimit = oldLimit RecomputeBufferSizeAfterLimit() End Sub ''' ''' Returns whether or not all the data before the limit has been read. ''' ''' Friend ReadOnly Property ReachedLimit As Boolean Get If currentLimit = Integer.MaxValue Then Return False End If Dim currentAbsolutePosition = totalBytesRetired + bufferPos Return currentAbsolutePosition >= currentLimit End Get End Property ''' ''' Returns true if the stream has reached the end of the input. This is the ''' case if either the end of the underlying input source has been reached or ''' the stream has reached a limit created using PushLimit. ''' Public ReadOnly Property IsAtEnd As Boolean Get Return bufferPos = bufferSizeField AndAlso Not RefillBuffer(False) End Get End Property ''' ''' Called when buffer is empty to read more bytes from the ''' input. If is true, RefillBuffer() gurantees that ''' either there will be at least one byte in the buffer when it returns ''' or it will throw an exception. If is false, ''' RefillBuffer() returns false if no more bytes were available. ''' ''' ''' Private Function RefillBuffer(mustSucceed As Boolean) As Boolean If bufferPos < bufferSizeField Then Throw New InvalidOperationException("RefillBuffer() called when buffer wasn't empty.") End If If totalBytesRetired + bufferSizeField = currentLimit Then ' Oops, we hit a limit. If mustSucceed Then Throw InvalidProtocolBufferException.TruncatedMessage() Else Return False End If End If totalBytesRetired += bufferSizeField bufferPos = 0 bufferSizeField = If(input Is Nothing, 0, input.Read(buffer, 0, buffer.Length)) If bufferSizeField < 0 Then Throw New InvalidOperationException("Stream.Read returned a negative count") End If If bufferSizeField = 0 Then If mustSucceed Then Throw InvalidProtocolBufferException.TruncatedMessage() Else Return False End If Else RecomputeBufferSizeAfterLimit() Dim totalBytesRead = totalBytesRetired + bufferSizeField + bufferSizeAfterLimit If totalBytesRead > sizeLimitField OrElse totalBytesRead < 0 Then Throw InvalidProtocolBufferException.SizeLimitExceeded() End If Return True End If End Function ''' ''' Read one byte from the input. ''' ''' ''' the end of the stream or the current limit was reached ''' Friend Function ReadRawByte() As Byte If bufferPos = bufferSizeField Then RefillBuffer(True) End If Return buffer(Math.Min(Threading.Interlocked.Increment(bufferPos), bufferPos - 1)) End Function ''' ''' Reads a fixed size of bytes from the input. ''' ''' ''' the end of the stream or the current limit was reached ''' Friend Function ReadRawBytes(size As Integer) As Byte() If size < 0 Then Throw InvalidProtocolBufferException.NegativeSize() End If If totalBytesRetired + bufferPos + size > currentLimit Then ' Read to the end of the stream (up to the current limit) anyway. SkipRawBytes(currentLimit - totalBytesRetired - bufferPos) ' Then fail. Throw InvalidProtocolBufferException.TruncatedMessage() End If If size <= bufferSizeField - bufferPos Then ' We have all the bytes we need already. Dim bytes = New Byte(size - 1) {} Copy(buffer, bufferPos, bytes, 0, size) bufferPos += size Return bytes ElseIf size < buffer.Length Then ' Reading more bytes than are in the buffer, but not an excessive number ' of bytes. We can safely allocate the resulting array ahead of time. ' First copy what we have. Dim bytes = New Byte(size - 1) {} Dim pos = bufferSizeField - bufferPos Copy(buffer, bufferPos, bytes, 0, pos) bufferPos = bufferSizeField ' We want to use RefillBuffer() and then copy from the buffer into our ' byte array rather than reading directly into our byte array because ' the input may be unbuffered. RefillBuffer(True) While size - pos > bufferSizeField System.Buffer.BlockCopy(buffer, 0, bytes, pos, bufferSizeField) pos += bufferSizeField bufferPos = bufferSizeField RefillBuffer(True) End While Copy(buffer, 0, bytes, pos, size - pos) bufferPos = size - pos Return bytes Else ' The size is very large. For security reasons, we can't allocate the ' entire byte array yet. The size comes directly from the input, so a ' maliciously-crafted message could provide a bogus very large size in ' order to trick the app into allocating a lot of memory. We avoid this ' by allocating and reading only a small chunk at a time, so that the ' malicious message must actually *be* extremely large to cause ' problems. Meanwhile, we limit the allowed size of a message elsewhere. ' Remember the buffer markers since we'll have to copy the bytes out of ' it later. Dim originalBufferPos = bufferPos Dim originalBufferSize = bufferSizeField ' Mark the current buffer consumed. totalBytesRetired += bufferSizeField bufferPos = 0 bufferSizeField = 0 ' Read all the rest of the bytes we need. Dim sizeLeft = size - (originalBufferSize - originalBufferPos) Dim chunks As New List(Of Byte())() While sizeLeft > 0 Dim chunk = New Byte(Math.Min(sizeLeft, buffer.Length) - 1) {} Dim pos = 0 While pos < chunk.Length Dim n = If(input Is Nothing, -1, input.Read(chunk, pos, chunk.Length - pos)) If n <= 0 Then Throw InvalidProtocolBufferException.TruncatedMessage() End If totalBytesRetired += n pos += n End While sizeLeft -= chunk.Length chunks.Add(chunk) End While ' OK, got everything. Now concatenate it all into one buffer. Dim bytes = New Byte(size - 1) {} ' Start by copying the leftover bytes from this.buffer. Dim newPos = originalBufferSize - originalBufferPos Copy(buffer, originalBufferPos, bytes, 0, newPos) ' And now all the chunks. For Each chunk In chunks System.Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length) newPos += chunk.Length Next ' Done. Return bytes End If End Function ''' ''' Reads and discards bytes. ''' ''' the end of the stream ''' or the current limit was reached Private Sub SkipRawBytes(size As Integer) If size < 0 Then Throw InvalidProtocolBufferException.NegativeSize() End If If totalBytesRetired + bufferPos + size > currentLimit Then ' Read to the end of the stream anyway. SkipRawBytes(currentLimit - totalBytesRetired - bufferPos) ' Then fail. Throw InvalidProtocolBufferException.TruncatedMessage() End If If size <= bufferSizeField - bufferPos Then ' We have all the bytes we need already. bufferPos += size Else ' Skipping more bytes than are in the buffer. First skip what we have. Dim pos = bufferSizeField - bufferPos ' ROK 5/7/2013 Issue #54: should retire all bytes in buffer (bufferSize) ' totalBytesRetired += pos; totalBytesRetired += bufferSizeField bufferPos = 0 bufferSizeField = 0 ' Then skip directly from the InputStream for the rest. If pos < size Then If input Is Nothing Then Throw InvalidProtocolBufferException.TruncatedMessage() End If SkipImpl(size - pos) totalBytesRetired += size - pos End If End If End Sub ''' ''' Abstraction of skipping to cope with streams which can't really skip. ''' Private Sub SkipImpl(amountToSkip As Integer) If input.CanSeek Then Dim previousPosition = input.Position input.Position += amountToSkip If input.Position <> previousPosition + amountToSkip Then Throw InvalidProtocolBufferException.TruncatedMessage() End If Else Dim skipBuffer = New Byte(Math.Min(1024, amountToSkip) - 1) {} While amountToSkip > 0 Dim bytesRead = input.Read(skipBuffer, 0, Math.Min(skipBuffer.Length, amountToSkip)) If bytesRead <= 0 Then Throw InvalidProtocolBufferException.TruncatedMessage() End If amountToSkip -= bytesRead End While End If End Sub #End Region End Class End Namespace