#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