/// <summary>
        /// Skips all the data in the stream until a boundary is found.
        /// </summary>
        /// <param name="isEndBoundary">true if the boundary that was found is an end boundary; otherwise false.</param>
        /// <param name="isParentBoundary">true if the detected boundary is a parent boundary (i.e., the expected boundary is missing).</param>
        /// <returns>true if a boundary was found; otherwise false.</returns>
        internal bool SkipToBoundary(out bool isEndBoundary, out bool isParentBoundary)
        {
            DebugUtils.CheckNoExternalCallers();

            // Ensure we have a batch encoding; if not detect it on the first read/skip.
            this.EnsureBatchEncoding();

            ODataBatchReaderStreamScanResult scanResult = ODataBatchReaderStreamScanResult.NoMatch;

            while (scanResult != ODataBatchReaderStreamScanResult.Match)
            {
                int boundaryStartPosition, boundaryEndPosition;
                scanResult = this.batchBuffer.ScanForBoundary(
                    this.CurrentBoundaries,
                    /*stopAfterIfNotFound*/ int.MaxValue,
                    out boundaryStartPosition,
                    out boundaryEndPosition,
                    out isEndBoundary,
                    out isParentBoundary);
                switch (scanResult)
                {
                case ODataBatchReaderStreamScanResult.NoMatch:
                    if (this.underlyingStreamExhausted)
                    {
                        // there is nothing else to load from the underlying stream; the requested boundary does not exist
                        this.batchBuffer.SkipTo(this.batchBuffer.CurrentReadPosition + this.batchBuffer.NumberOfBytesInBuffer);
                        return(false);
                    }

                    // skip everything in the buffer and refill it from the underlying stream; continue scanning
                    this.underlyingStreamExhausted = this.batchBuffer.RefillFrom(this.inputContext.Stream, /*preserveFrom*/ ODataBatchReaderStreamBuffer.BufferLength);

                    break;

                case ODataBatchReaderStreamScanResult.PartialMatch:
                    if (this.underlyingStreamExhausted)
                    {
                        // there is nothing else to load from the underlying stream; the requested boundary does not exist
                        this.batchBuffer.SkipTo(this.batchBuffer.CurrentReadPosition + this.batchBuffer.NumberOfBytesInBuffer);
                        return(false);
                    }

                    this.underlyingStreamExhausted = this.batchBuffer.RefillFrom(this.inputContext.Stream, /*preserveFrom*/ boundaryStartPosition);

                    break;

                case ODataBatchReaderStreamScanResult.Match:
                    // If we found the expected boundary, position the reader on the position after the boundary end.
                    // If we found a parent boundary, position the reader on the boundary start so we'll detect the boundary
                    // again on the next Read call.
                    this.batchBuffer.SkipTo(isParentBoundary ? boundaryStartPosition : boundaryEndPosition + 1);
                    return(true);

                default:
                    throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStream_SkipToBoundary));
                }
            }

            throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStream_SkipToBoundary));
        }
        internal bool SkipToBoundary(out bool isEndBoundary, out bool isParentBoundary)
        {
            this.EnsureBatchEncoding();
            ODataBatchReaderStreamScanResult noMatch = ODataBatchReaderStreamScanResult.NoMatch;

            while (noMatch != ODataBatchReaderStreamScanResult.Match)
            {
                int num;
                int num2;
                switch (this.batchBuffer.ScanForBoundary(this.CurrentBoundaries, 0x7fffffff, out num, out num2, out isEndBoundary, out isParentBoundary))
                {
                case ODataBatchReaderStreamScanResult.NoMatch:
                    if (!this.underlyingStreamExhausted)
                    {
                        break;
                    }
                    this.batchBuffer.SkipTo(this.batchBuffer.CurrentReadPosition + this.batchBuffer.NumberOfBytesInBuffer);
                    return(false);

                case ODataBatchReaderStreamScanResult.PartialMatch:
                    if (!this.underlyingStreamExhausted)
                    {
                        goto Label_00BE;
                    }
                    this.batchBuffer.SkipTo(this.batchBuffer.CurrentReadPosition + this.batchBuffer.NumberOfBytesInBuffer);
                    return(false);

                case ODataBatchReaderStreamScanResult.Match:
                    this.batchBuffer.SkipTo(isParentBoundary ? num : (num2 + 1));
                    return(true);

                default:
                    throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStream_SkipToBoundary));
                }
                this.underlyingStreamExhausted = this.batchBuffer.RefillFrom(this.inputContext.Stream, 0x1f40);
                continue;
Label_00BE:
                this.underlyingStreamExhausted = this.batchBuffer.RefillFrom(this.inputContext.Stream, num);
            }
            throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStream_SkipToBoundary));
        }
Esempio n. 3
0
        /// <summary>
        /// Reads a line (all bytes until a line resource set) from the underlying stream.
        /// </summary>
        /// <returns>Returns the string that was read from the underlying stream (not including a terminating line resource set), or null if the end of input was reached.</returns>
        private string ReadLine()
        {
            Debug.Assert(this.batchEncoding != null, "Batch encoding should have been established on first call to SkipToBoundary.");
            Debug.Assert(this.lineBuffer != null && this.lineBuffer.Length == LineBufferLength, "Line buffer should have been created.");

            // The number of bytes in the line buffer that make up the line.
            int lineBufferSize = 0;

            // Start with the pre-allocated line buffer array.
            byte[] bytesForString = this.lineBuffer;

            ODataBatchReaderStreamScanResult scanResult = ODataBatchReaderStreamScanResult.NoMatch;

            while (scanResult != ODataBatchReaderStreamScanResult.Match)
            {
                int byteCount, lineEndStartPosition, lineEndEndPosition;
                scanResult = this.BatchBuffer.ScanForLineEnd(out lineEndStartPosition, out lineEndEndPosition);

                switch (scanResult)
                {
                case ODataBatchReaderStreamScanResult.NoMatch:
                    // Copy all the bytes in the BatchBuffer into the result byte[] and then continue
                    byteCount = this.BatchBuffer.NumberOfBytesInBuffer;
                    if (byteCount > 0)
                    {
                        // TODO: [Design] Consider security limits for data being read
                        ODataBatchUtils.EnsureArraySize(ref bytesForString, lineBufferSize, byteCount);
                        Buffer.BlockCopy(this.BatchBuffer.Bytes, this.BatchBuffer.CurrentReadPosition, bytesForString, lineBufferSize, byteCount);
                        lineBufferSize += byteCount;
                    }

                    if (this.underlyingStreamExhausted)
                    {
                        if (lineBufferSize == 0)
                        {
                            // If there's nothing more to pull from the underlying stream, and we didn't read anything
                            // in this invocation of ReadLine(), return null to indicate end of input.
                            return(null);
                        }

                        // Nothing more to read; stop looping
                        scanResult = ODataBatchReaderStreamScanResult.Match;
                        this.BatchBuffer.SkipTo(this.BatchBuffer.CurrentReadPosition + byteCount);
                    }
                    else
                    {
                        this.underlyingStreamExhausted = this.BatchBuffer.RefillFrom(this.multipartMixedBatchInputContext.Stream, /*preserveFrom*/ ODataBatchReaderStreamBuffer.BufferLength);
                    }

                    break;

                case ODataBatchReaderStreamScanResult.PartialMatch:
                    // We found the start of a line end in the buffer but could not verify whether we saw all of it.
                    // This can happen if a line end is represented as \r\n and we found \r at the very last position in the buffer.
                    // In this case we copy the bytes into the result byte[] and continue at the start of the line end; this will guarantee
                    // that the next scan will find the full line end, not find any additional bytes and then skip the full line end.
                    // It is safe to copy the string right here because we will also accept \r as a line end; we are just not sure whether there
                    // will be a subsequent \n.
                    // This can also happen if the last byte in the stream is \r.
                    byteCount = lineEndStartPosition - this.BatchBuffer.CurrentReadPosition;
                    if (byteCount > 0)
                    {
                        ODataBatchUtils.EnsureArraySize(ref bytesForString, lineBufferSize, byteCount);
                        Buffer.BlockCopy(this.BatchBuffer.Bytes, this.BatchBuffer.CurrentReadPosition, bytesForString, lineBufferSize, byteCount);
                        lineBufferSize += byteCount;
                    }

                    if (this.underlyingStreamExhausted)
                    {
                        // Nothing more to read; stop looping
                        scanResult = ODataBatchReaderStreamScanResult.Match;
                        this.BatchBuffer.SkipTo(lineEndStartPosition + 1);
                    }
                    else
                    {
                        this.underlyingStreamExhausted = this.BatchBuffer.RefillFrom(this.multipartMixedBatchInputContext.Stream, /*preserveFrom*/ lineEndStartPosition);
                    }

                    break;

                case ODataBatchReaderStreamScanResult.Match:
                    // We found a line end in the buffer
                    Debug.Assert(lineEndStartPosition >= this.BatchBuffer.CurrentReadPosition, "Line end must be at or after current position.");
                    Debug.Assert(lineEndEndPosition < this.BatchBuffer.CurrentReadPosition + this.BatchBuffer.NumberOfBytesInBuffer, "Line end must finish withing buffer range.");

                    byteCount = lineEndStartPosition - this.BatchBuffer.CurrentReadPosition;
                    if (byteCount > 0)
                    {
                        ODataBatchUtils.EnsureArraySize(ref bytesForString, lineBufferSize, byteCount);
                        Buffer.BlockCopy(this.BatchBuffer.Bytes, this.BatchBuffer.CurrentReadPosition, bytesForString, lineBufferSize, byteCount);
                        lineBufferSize += byteCount;
                    }

                    this.BatchBuffer.SkipTo(lineEndEndPosition + 1);
                    break;

                default:
                    throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStream_ReadLine));
                }
            }

            Debug.Assert(bytesForString != null, "bytesForString != null");

            return(this.CurrentEncoding.GetString(bytesForString, 0, lineBufferSize));
        }
Esempio n. 4
0
        /// <summary>
        /// Reads from the batch stream while ensuring that we stop reading at each boundary.
        /// </summary>
        /// <param name="userBuffer">The byte array to read bytes into.</param>
        /// <param name="userBufferOffset">The offset in the buffer where to start reading bytes into.</param>
        /// <param name="count">The number of bytes to read.</param>
        /// <returns>The number of bytes actually read.</returns>
        internal override int ReadWithDelimiter(byte[] userBuffer, int userBufferOffset, int count)
        {
            Debug.Assert(userBuffer != null, "userBuffer != null");
            Debug.Assert(userBufferOffset >= 0 && userBufferOffset < userBuffer.Length, "Offset must be within the range of the user buffer.");
            Debug.Assert(count >= 0, "count >= 0");
            Debug.Assert(this.batchEncoding != null, "Batch encoding should have been established on first call to SkipToBoundary.");

            if (count == 0)
            {
                // Nothing to read.
                return(0);
            }

            int remainingNumberOfBytesToRead            = count;
            ODataBatchReaderStreamScanResult scanResult = ODataBatchReaderStreamScanResult.NoMatch;

            while (remainingNumberOfBytesToRead > 0 && scanResult != ODataBatchReaderStreamScanResult.Match)
            {
                int  boundaryStartPosition, boundaryEndPosition;
                bool isEndBoundary, isParentBoundary;
                scanResult = this.BatchBuffer.ScanForBoundary(
                    this.CurrentBoundaries,
                    remainingNumberOfBytesToRead,
                    out boundaryStartPosition,
                    out boundaryEndPosition,
                    out isEndBoundary,
                    out isParentBoundary);

                int bytesBeforeBoundaryStart;
                switch (scanResult)
                {
                case ODataBatchReaderStreamScanResult.NoMatch:
                    // The boundary was not found in the buffer or after the required number of bytes to be read;
                    // Check whether we can satisfy the full read request from the buffer
                    // or whether we have to split the request and read more data into the buffer.
                    if (this.BatchBuffer.NumberOfBytesInBuffer >= remainingNumberOfBytesToRead)
                    {
                        // we can satisfy the full read request from the buffer
                        Buffer.BlockCopy(this.BatchBuffer.Bytes, this.BatchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, remainingNumberOfBytesToRead);
                        this.BatchBuffer.SkipTo(this.BatchBuffer.CurrentReadPosition + remainingNumberOfBytesToRead);
                        return(count);
                    }
                    else
                    {
                        // we can only partially satisfy the read request
                        int availableBytesToRead = this.BatchBuffer.NumberOfBytesInBuffer;
                        Buffer.BlockCopy(this.BatchBuffer.Bytes, this.BatchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, availableBytesToRead);
                        remainingNumberOfBytesToRead -= availableBytesToRead;
                        userBufferOffset             += availableBytesToRead;

                        // we exhausted the buffer; if the underlying stream is not exceeded, refill the buffer
                        if (this.underlyingStreamExhausted)
                        {
                            // We cannot fully satisfy the read request since there are not enough bytes in the stream.
                            // Return the number of bytes we read.
                            this.BatchBuffer.SkipTo(this.BatchBuffer.CurrentReadPosition + availableBytesToRead);
                            return(count - remainingNumberOfBytesToRead);
                        }
                        else
                        {
                            this.underlyingStreamExhausted = this.BatchBuffer.RefillFrom(this.multipartMixedBatchInputContext.Stream, /*preserveFrom*/ ODataBatchReaderStreamBuffer.BufferLength);
                        }
                    }

                    break;

                case ODataBatchReaderStreamScanResult.PartialMatch:
                    // A partial match for the boundary was found at the end of the buffer.
                    // If the underlying stream is not exceeded, refill the buffer. Otherwise return
                    // the available bytes.
                    if (this.underlyingStreamExhausted)
                    {
                        // We cannot fully satisfy the read request since there are not enough bytes in the stream.
                        // Return the remaining bytes in the buffer independently of where a potentially boundary
                        // start was detected since no full boundary can ever be detected if the stream is exhausted.
                        int bytesToReturn = Math.Min(this.BatchBuffer.NumberOfBytesInBuffer, remainingNumberOfBytesToRead);
                        Buffer.BlockCopy(this.BatchBuffer.Bytes, this.BatchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, bytesToReturn);
                        this.BatchBuffer.SkipTo(this.BatchBuffer.CurrentReadPosition + bytesToReturn);
                        remainingNumberOfBytesToRead -= bytesToReturn;
                        return(count - remainingNumberOfBytesToRead);
                    }
                    else
                    {
                        // Copy the bytes prior to the potential boundary start into the user buffer, refill the buffer and continue.
                        bytesBeforeBoundaryStart = boundaryStartPosition - this.BatchBuffer.CurrentReadPosition;
                        Debug.Assert(bytesBeforeBoundaryStart < remainingNumberOfBytesToRead, "When reporting a partial match we should never have read all the remaining bytes to read (or more).");

                        Buffer.BlockCopy(this.BatchBuffer.Bytes, this.BatchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, bytesBeforeBoundaryStart);
                        remainingNumberOfBytesToRead -= bytesBeforeBoundaryStart;
                        userBufferOffset             += bytesBeforeBoundaryStart;

                        this.underlyingStreamExhausted = this.BatchBuffer.RefillFrom(this.multipartMixedBatchInputContext.Stream, /*preserveFrom*/ boundaryStartPosition);
                    }

                    break;

                case ODataBatchReaderStreamScanResult.Match:
                    // We found the full boundary match; copy everything before the boundary to the buffer
                    bytesBeforeBoundaryStart = boundaryStartPosition - this.BatchBuffer.CurrentReadPosition;
                    Debug.Assert(bytesBeforeBoundaryStart <= remainingNumberOfBytesToRead, "When reporting a full match we should never have read more than the remaining bytes to read.");
                    Buffer.BlockCopy(this.BatchBuffer.Bytes, this.BatchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, bytesBeforeBoundaryStart);
                    remainingNumberOfBytesToRead -= bytesBeforeBoundaryStart;
                    userBufferOffset             += bytesBeforeBoundaryStart;

                    // position the reader on the position of the boundary start
                    this.BatchBuffer.SkipTo(boundaryStartPosition);

                    // return the number of bytes that were read
                    return(count - remainingNumberOfBytesToRead);

                default:
                    break;
                }
            }

            throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStream_ReadWithDelimiter));
        }
        /// <summary>
        /// Check whether the bytes in the buffer at the specified start index match the expected boundary string.
        /// </summary>
        /// <param name="lineEndStartPosition">The start of the line feed preceding the boundary (if present).</param>
        /// <param name="boundaryDelimiterStartPosition">The start position of the boundary delimiter.</param>
        /// <param name="boundary">The boundary string to check for.</param>
        /// <param name="boundaryStartPosition">If a match is detected, the start of the boundary delimiter,
        /// i.e., either the start of the leading line feed or of the leading dashes.</param>
        /// <param name="boundaryEndPosition">If a match is detected, the position of the boundary end; otherwise -1.</param>
        /// <param name="isEndBoundary">true if the detected boundary is an end boundary; otherwise false.</param>
        /// <returns>An <see cref="ODataBatchReaderStreamScanResult"/> indicating whether a match, a partial match or no match was found.</returns>
        private ODataBatchReaderStreamScanResult MatchBoundary(
            int lineEndStartPosition,
            int boundaryDelimiterStartPosition,
            string boundary,
            out int boundaryStartPosition,
            out int boundaryEndPosition,
            out bool isEndBoundary)
        {
            boundaryStartPosition = -1;
            boundaryEndPosition   = -1;

            int bufferLastByte = this.currentReadPosition + this.numberOfBytesInBuffer - 1;
            int boundaryEndPositionAfterLineFeed =
                boundaryDelimiterStartPosition + TwoDashesLength + boundary.Length + TwoDashesLength - 1;

            // NOTE: for simplicity reasons we require that the full end (!) boundary plus the maximum size
            //       of the line terminator fits into the buffer to get a non-partial match.
            //       By doing so we can reliably detect whether we are dealing with an end boundary or not.
            //       Otherwise the logic to figure out whether we found a boundary or not is riddled with
            //       corner cases that only complicate the code.
            bool isPartialMatch;
            int  matchLength;

            if (bufferLastByte < boundaryEndPositionAfterLineFeed + MaxLineFeedLength)
            {
                isPartialMatch = true;
                matchLength    = Math.Min(bufferLastByte, boundaryEndPositionAfterLineFeed) - boundaryDelimiterStartPosition + 1;
            }
            else
            {
                isPartialMatch = false;
                matchLength    = boundaryEndPositionAfterLineFeed - boundaryDelimiterStartPosition + 1;
            }

            if (this.MatchBoundary(boundary, boundaryDelimiterStartPosition, matchLength, out isEndBoundary))
            {
                boundaryStartPosition = lineEndStartPosition < 0 ? boundaryDelimiterStartPosition : lineEndStartPosition;

                if (isPartialMatch)
                {
                    isEndBoundary = false;
                    return(ODataBatchReaderStreamScanResult.PartialMatch);
                }
                else
                {
                    // If we fully matched the boundary compute the boundary end position
                    boundaryEndPosition = boundaryDelimiterStartPosition + TwoDashesLength + boundary.Length - 1;
                    if (isEndBoundary)
                    {
                        boundaryEndPosition += TwoDashesLength;
                    }

                    // Once we could match all the characters and delimiters of the boundary string
                    // (and the optional trailing '--') we now have to continue reading until the next
                    // line feed that terminates the boundary. Only whitespace characters may exist
                    // after the boundary and before the line feed.
                    int  terminatingLineFeedStartPosition, terminatingLineFeedEndPosition;
                    bool endOfBufferReached;
                    ODataBatchReaderStreamScanResult terminatingLineFeedScanResult =
                        this.ScanForLineEnd(
                            boundaryEndPosition + 1,
                            int.MaxValue,
                            /*allowLeadingWhitespaceOnly*/ true,
                            out terminatingLineFeedStartPosition,
                            out terminatingLineFeedEndPosition,
                            out endOfBufferReached);

                    switch (terminatingLineFeedScanResult)
                    {
                    case ODataBatchReaderStreamScanResult.NoMatch:
                        if (endOfBufferReached)
                        {
                            // Reached the end of the buffer and only found whitespaces.
                            if (boundaryStartPosition == 0)
                            {
                                // If the boundary starts at the first position in the buffer
                                // and we still could not find the end of it because there are
                                // so many whitespaces before the terminating line feed - fail
                                // (security limit on the whitespaces)
                                throw new ODataException(Strings.ODataBatchReaderStreamBuffer_BoundaryLineSecurityLimitReached(BufferLength));
                            }

                            // Report a partial match.
                            isEndBoundary = false;
                            return(ODataBatchReaderStreamScanResult.PartialMatch);
                        }
                        else
                        {
                            // Found a different character than whitespace or end-of-line
                            // so we did not match the boundary.
                            break;
                        }

                    case ODataBatchReaderStreamScanResult.PartialMatch:
                        // Found only a partial line end at the end of the buffer.
                        if (boundaryStartPosition == 0)
                        {
                            // If the boundary starts at the first position in the buffer
                            // and we still could not find the end of it because there are
                            // so many whitespaces before the terminating line feed - fail
                            // (security limit on the whitespaces)
                            throw new ODataException(Strings.ODataBatchReaderStreamBuffer_BoundaryLineSecurityLimitReached(BufferLength));
                        }

                        // Report a partial match.
                        isEndBoundary = false;
                        return(ODataBatchReaderStreamScanResult.PartialMatch);

                    case ODataBatchReaderStreamScanResult.Match:
                        // At this point we only found whitespace characters and then the terminating line feed;
                        // adjust the boundary end position
                        boundaryEndPosition = terminatingLineFeedEndPosition;
                        return(ODataBatchReaderStreamScanResult.Match);

                    default:
                        throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStreamBuffer_ScanForBoundary));
                    }
                }
            }

            return(ODataBatchReaderStreamScanResult.NoMatch);
        }
        /// <summary>
        /// Scans the current buffer for the specified boundary.
        /// </summary>
        /// <param name="boundaries">The boundary strings to search for; this enumerable is sorted from the inner-most boundary
        /// to the top-most boundary. The boundary strings don't include the leading line terminator or the leading dashes.</param>
        /// <param name="maxDataBytesToScan">Stop if no boundary (or boundary start) is found after this number of bytes.</param>
        /// <param name="boundaryStartPosition">The start position of the boundary or -1 if not found.
        /// Note that the start position is the first byte of the leading line terminator.</param>
        /// <param name="boundaryEndPosition">The end position of the boundary or -1 if not found.
        /// Note that the end position is the last byte of the trailing line terminator.</param>
        /// <param name="isEndBoundary">true if the boundary is an end boundary (followed by two dashes); otherwise false.</param>
        /// <param name="isParentBoundary">true if the detected boundary is the parent boundary; otherwise false.</param>
        /// <returns>An enumeration value indicating whether the boundary was completely, partially or not found in the buffer.</returns>
        internal ODataBatchReaderStreamScanResult ScanForBoundary(
            IEnumerable <string> boundaries,
            int maxDataBytesToScan,
            out int boundaryStartPosition,
            out int boundaryEndPosition,
            out bool isEndBoundary,
            out bool isParentBoundary)
        {
            Debug.Assert(boundaries != null, "boundaries != null");

            boundaryStartPosition = -1;
            boundaryEndPosition   = -1;
            isEndBoundary         = false;
            isParentBoundary      = false;

            int lineScanStartIndex = this.currentReadPosition;

            while (true)
            {
                // NOTE: a boundary has to start with a line terminator followed by two dashes ('-'),
                //       the actual boundary string, another two dashes (for an end boundary),
                //       optional whitespace characters and another line terminator.
                // NOTE: for WCF DS compatibility we do not require the leading line terminator.
                int lineEndStartPosition, boundaryDelimiterStartPosition;
                ODataBatchReaderStreamScanResult lineEndScanResult = this.ScanForBoundaryStart(
                    lineScanStartIndex,
                    maxDataBytesToScan,
                    out lineEndStartPosition,
                    out boundaryDelimiterStartPosition);

                switch (lineEndScanResult)
                {
                case ODataBatchReaderStreamScanResult.NoMatch:
                    // Did not find a line end or boundary delimiter in the buffer or after reading maxDataBytesToScan bytes.
                    // Report no boundary match.
                    return(ODataBatchReaderStreamScanResult.NoMatch);

                case ODataBatchReaderStreamScanResult.PartialMatch:
                    // Found a partial line end or boundary delimiter at the end of the buffer but before reading maxDataBytesToScan.
                    // Report a partial boundary match.
                    boundaryStartPosition = lineEndStartPosition < 0 ? boundaryDelimiterStartPosition : lineEndStartPosition;
                    return(ODataBatchReaderStreamScanResult.PartialMatch);

                case ODataBatchReaderStreamScanResult.Match:
                    // Found a full line end or boundary delimiter start ('--') before reading maxDataBytesToScan or
                    // hitting the end of the buffer. Start matching the boundary delimiters.
                    //
                    // Start with the expected boundary (the first one in the enumerable):
                    // * if we find a full match - return match because we are done
                    // * if we find a partial match - return partial match because we have to continue checking the expected boundary.
                    // * if we find no match - we know that it is not the expected boundary; check the parent boundary (if it exists).
                    isParentBoundary = false;
                    foreach (string boundary in boundaries)
                    {
                        ODataBatchReaderStreamScanResult boundaryMatch = this.MatchBoundary(
                            lineEndStartPosition,
                            boundaryDelimiterStartPosition,
                            boundary,
                            out boundaryStartPosition,
                            out boundaryEndPosition,
                            out isEndBoundary);
                        switch (boundaryMatch)
                        {
                        case ODataBatchReaderStreamScanResult.Match:
                            return(ODataBatchReaderStreamScanResult.Match);

                        case ODataBatchReaderStreamScanResult.PartialMatch:
                            boundaryEndPosition = -1;
                            isEndBoundary       = false;
                            return(ODataBatchReaderStreamScanResult.PartialMatch);

                        case ODataBatchReaderStreamScanResult.NoMatch:
                            // try the parent boundary (if any) or continue scanning
                            boundaryStartPosition = -1;
                            boundaryEndPosition   = -1;
                            isEndBoundary         = false;
                            isParentBoundary      = true;
                            break;

                        default:
                            throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStreamBuffer_ScanForBoundary));
                        }
                    }

                    // If we could not match the boundary, try again starting at the current boundary start index. Or if we already did that
                    // move one character ahead.
                    lineScanStartIndex = lineScanStartIndex == boundaryDelimiterStartPosition
                            ? boundaryDelimiterStartPosition + 1
                            : boundaryDelimiterStartPosition;

                    break;

                default:
                    throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStreamBuffer_ScanForBoundary));
                }
            }
        }
        internal int ReadWithDelimiter(byte[] userBuffer, int userBufferOffset, int count)
        {
            if (count == 0)
            {
                return(0);
            }
            int maxDataBytesToScan = count;
            ODataBatchReaderStreamScanResult noMatch = ODataBatchReaderStreamScanResult.NoMatch;

            while ((maxDataBytesToScan > 0) && (noMatch != ODataBatchReaderStreamScanResult.Match))
            {
                int  num2;
                int  num3;
                bool flag;
                bool flag2;
                int  num4;
                switch (this.batchBuffer.ScanForBoundary(this.CurrentBoundaries, maxDataBytesToScan, out num2, out num3, out flag, out flag2))
                {
                case ODataBatchReaderStreamScanResult.NoMatch:
                    if (this.batchBuffer.NumberOfBytesInBuffer < maxDataBytesToScan)
                    {
                        break;
                    }
                    Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, maxDataBytesToScan);
                    this.batchBuffer.SkipTo(this.batchBuffer.CurrentReadPosition + maxDataBytesToScan);
                    return(count);

                case ODataBatchReaderStreamScanResult.PartialMatch:
                {
                    if (!this.underlyingStreamExhausted)
                    {
                        goto Label_0168;
                    }
                    int num6 = Math.Min(this.batchBuffer.NumberOfBytesInBuffer, maxDataBytesToScan);
                    Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, num6);
                    this.batchBuffer.SkipTo(this.batchBuffer.CurrentReadPosition + num6);
                    maxDataBytesToScan -= num6;
                    return(count - maxDataBytesToScan);
                }

                case ODataBatchReaderStreamScanResult.Match:
                    num4 = num2 - this.batchBuffer.CurrentReadPosition;
                    Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, num4);
                    maxDataBytesToScan -= num4;
                    userBufferOffset   += num4;
                    this.batchBuffer.SkipTo(num2);
                    return(count - maxDataBytesToScan);

                default:
                {
                    continue;
                }
                }
                int numberOfBytesInBuffer = this.batchBuffer.NumberOfBytesInBuffer;
                Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, numberOfBytesInBuffer);
                maxDataBytesToScan -= numberOfBytesInBuffer;
                userBufferOffset   += numberOfBytesInBuffer;
                if (this.underlyingStreamExhausted)
                {
                    this.batchBuffer.SkipTo(this.batchBuffer.CurrentReadPosition + numberOfBytesInBuffer);
                    return(count - maxDataBytesToScan);
                }
                this.underlyingStreamExhausted = this.batchBuffer.RefillFrom(this.inputContext.Stream, 0x1f40);
                continue;
Label_0168:
                num4 = num2 - this.batchBuffer.CurrentReadPosition;
                Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, num4);
                maxDataBytesToScan            -= num4;
                userBufferOffset              += num4;
                this.underlyingStreamExhausted = this.batchBuffer.RefillFrom(this.inputContext.Stream, num2);
            }
            throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStream_ReadWithDelimiter));
        }
        internal string ReadLine()
        {
            int numberOfBytesInBuffer = 0;

            byte[] lineBuffer = this.lineBuffer;
            ODataBatchReaderStreamScanResult noMatch = ODataBatchReaderStreamScanResult.NoMatch;

            while (noMatch != ODataBatchReaderStreamScanResult.Match)
            {
                int num2;
                int num3;
                int num4;
                noMatch = this.batchBuffer.ScanForLineEnd(out num3, out num4);
                switch (noMatch)
                {
                case ODataBatchReaderStreamScanResult.NoMatch:
                {
                    num2 = this.batchBuffer.NumberOfBytesInBuffer;
                    if (num2 > 0)
                    {
                        ODataBatchUtils.EnsureArraySize(ref lineBuffer, numberOfBytesInBuffer, num2);
                        Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, lineBuffer, numberOfBytesInBuffer, num2);
                        numberOfBytesInBuffer += num2;
                    }
                    if (this.underlyingStreamExhausted)
                    {
                        noMatch = ODataBatchReaderStreamScanResult.Match;
                        this.batchBuffer.SkipTo(this.batchBuffer.CurrentReadPosition + num2);
                    }
                    else
                    {
                        this.underlyingStreamExhausted = this.batchBuffer.RefillFrom(this.inputContext.Stream, 0x1f40);
                    }
                    continue;
                }

                case ODataBatchReaderStreamScanResult.PartialMatch:
                {
                    num2 = num3 - this.batchBuffer.CurrentReadPosition;
                    if (num2 > 0)
                    {
                        ODataBatchUtils.EnsureArraySize(ref lineBuffer, numberOfBytesInBuffer, num2);
                        Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, lineBuffer, numberOfBytesInBuffer, num2);
                        numberOfBytesInBuffer += num2;
                    }
                    if (this.underlyingStreamExhausted)
                    {
                        noMatch = ODataBatchReaderStreamScanResult.Match;
                        this.batchBuffer.SkipTo(num3 + 1);
                    }
                    else
                    {
                        this.underlyingStreamExhausted = this.batchBuffer.RefillFrom(this.inputContext.Stream, num3);
                    }
                    continue;
                }

                case ODataBatchReaderStreamScanResult.Match:
                {
                    num2 = num3 - this.batchBuffer.CurrentReadPosition;
                    if (num2 > 0)
                    {
                        ODataBatchUtils.EnsureArraySize(ref lineBuffer, numberOfBytesInBuffer, num2);
                        Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, lineBuffer, numberOfBytesInBuffer, num2);
                        numberOfBytesInBuffer += num2;
                    }
                    this.batchBuffer.SkipTo(num4 + 1);
                    continue;
                }
                }
                throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStream_ReadLine));
            }
            if (lineBuffer == null)
            {
                return(string.Empty);
            }
            return(this.CurrentEncoding.GetString(lineBuffer, 0, numberOfBytesInBuffer));
        }