public override async Task <int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            if (!_encapsulate)
            {
                return(await _stream.ReadAsync(buffer, offset, count, cancellationToken));
            }

            using (SNIEventScope.Create("<sc.SNI.SslOverTdsStream.ReadAsync |SNI|INFO|SCOPE> reading encapsulated bytes"))
            {
                if (_packetBytes > 0)
                {
                    // there are queued bytes from a previous packet available
                    // work out how many of the remaining bytes we can consume
                    int wantedCount = Math.Min(count, _packetBytes);
                    int readCount   = await _stream.ReadAsync(buffer, offset, wantedCount, cancellationToken);

                    if (readCount == 0)
                    {
                        // 0 means the connection was closed, tell the caller
                        return(0);
                    }
                    _packetBytes -= readCount;
                    return(readCount);
                }
                else
                {
                    byte[] headerBytes = ArrayPool <byte> .Shared.Rent(TdsEnums.HEADER_LEN);

                    Array.Clear(headerBytes, 0, headerBytes.Length);

                    // fetch the packet header to determine how long the packet is
                    int headerBytesRead = 0;
                    do
                    {
                        int headerBytesReadIteration = await _stream.ReadAsync(headerBytes, headerBytesRead, (TdsEnums.HEADER_LEN - headerBytesRead), cancellationToken);

                        if (headerBytesReadIteration == 0)
                        {
                            // 0 means the connection was closed, cleanup the rented array and then tell the caller
                            ArrayPool <byte> .Shared.Return(headerBytes, clearArray : true);

                            return(0);
                        }
                        headerBytesRead += headerBytesReadIteration;
                    } while (headerBytesRead < TdsEnums.HEADER_LEN);

                    // read the packet data size from the header and store it in case it is needed for a subsequent call
                    _packetBytes = ((headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET] << 8) | headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET + 1]) - TdsEnums.HEADER_LEN;

                    ArrayPool <byte> .Shared.Return(headerBytes, clearArray : true);

                    // read as much from the packet as the caller can accept
                    int packetBytesRead = await _stream.ReadAsync(buffer, offset, Math.Min(count, _packetBytes), cancellationToken);

                    _packetBytes -= packetBytesRead;
                    return(packetBytesRead);
                }
            }
        }
        public override void Write(ReadOnlySpan <byte> buffer)
        {
            // During the SSL negotiation phase, SSL is tunnelled over TDS packet type 0x12. After
            // negotiation, the underlying socket only sees SSL frames.
            if (!_encapsulate)
            {
                _stream.Write(buffer);
                _stream.Flush();
                return;
            }

            using (SNIEventScope.Create("<sc.SNI.SslOverTdsStream.Write |SNI|INFO|SCOPE> writing encapsulated bytes"))
            {
                ReadOnlySpan <byte> remaining = buffer;
                byte[] packetBuffer           = null;
                try
                {
                    while (remaining.Length > 0)
                    {
                        int dataLength   = Math.Min(PACKET_SIZE_WITHOUT_HEADER, remaining.Length);
                        int packetLength = TdsEnums.HEADER_LEN + dataLength;

                        if (packetBuffer == null)
                        {
                            packetBuffer = ArrayPool <byte> .Shared.Rent(packetLength);
                        }
                        else if (packetBuffer.Length < packetLength)
                        {
                            ArrayPool <byte> .Shared.Return(packetBuffer, clearArray : true);

                            packetBuffer = ArrayPool <byte> .Shared.Rent(packetLength);
                        }

                        SetupPreLoginPacketHeader(packetBuffer, dataLength, remaining.Length - dataLength);

                        Span <byte> data = packetBuffer.AsSpan(TdsEnums.HEADER_LEN, dataLength);
                        remaining.Slice(0, dataLength).CopyTo(data);

                        _stream.Write(packetBuffer.AsSpan(0, packetLength));
                        _stream.Flush();

                        remaining = remaining.Slice(dataLength);
                    }
                }
                finally
                {
                    if (packetBuffer != null)
                    {
                        ArrayPool <byte> .Shared.Return(packetBuffer, clearArray : true);
                    }
                }
            }
        }
        public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            if (!_encapsulate)
            {
                await _stream.WriteAsync(buffer, offset, count).ConfigureAwait(false);

                Task flushTask = _stream.FlushAsync();
                if (flushTask.Status == TaskStatus.RanToCompletion)
                {
                    await flushTask.ConfigureAwait(false);
                }
                return;
            }

            using (SNIEventScope.Create("<sc.SNI.SslOverTdsStream.WriteAsync |SNI|INFO|SCOPE> writing encapsulated bytes"))
            {
                int    remainingBytes = count;
                int    dataOffset     = offset;
                byte[] packetBuffer   = null;
                while (remainingBytes > 0)
                {
                    int dataLength   = Math.Min(PACKET_SIZE_WITHOUT_HEADER, remainingBytes);
                    int packetLength = TdsEnums.HEADER_LEN + dataLength;
                    remainingBytes -= dataLength;

                    if (packetBuffer == null)
                    {
                        packetBuffer = ArrayPool <byte> .Shared.Rent(packetLength);
                    }
                    else if (packetBuffer.Length < packetLength)
                    {
                        ArrayPool <byte> .Shared.Return(packetBuffer, clearArray : true);

                        packetBuffer = ArrayPool <byte> .Shared.Rent(packetLength);
                    }

                    SetupPreLoginPacketHeader(packetBuffer, dataLength, remainingBytes);

                    Array.Copy(buffer, dataOffset, packetBuffer, TdsEnums.HEADER_LEN, dataLength);

                    await _stream.WriteAsync(packetBuffer, 0, packetLength, cancellationToken).ConfigureAwait(false);

                    await _stream.FlushAsync().ConfigureAwait(false);

                    dataOffset += dataLength;
                }
                if (packetBuffer != null)
                {
                    ArrayPool <byte> .Shared.Return(packetBuffer, clearArray : true);
                }
            }
        }
        public override int Read(Span <byte> buffer)
        {
            if (!_encapsulate)
            {
                return(_stream.Read(buffer));
            }

            using (SNIEventScope.Create("<sc.SNI.SslOverTdsStream.Read |SNI|INFO|SCOPE> reading encapsulated bytes"))
            {
                if (_packetBytes > 0)
                {
                    // there are queued bytes from a previous packet available
                    // work out how many of the remaining bytes we can consume
                    int wantedCount = Math.Min(buffer.Length, _packetBytes);
                    int readCount   = _stream.Read(buffer.Slice(0, wantedCount));
                    if (readCount == 0)
                    {
                        // 0 means the connection was closed, tell the caller
                        return(0);
                    }
                    _packetBytes -= readCount;
                    return(readCount);
                }
                else
                {
                    Span <byte> headerBytes = stackalloc byte[TdsEnums.HEADER_LEN];

                    // fetch the packet header to determine how long the packet is
                    int headerBytesRead = 0;
                    do
                    {
                        int headerBytesReadIteration = _stream.Read(headerBytes.Slice(headerBytesRead, TdsEnums.HEADER_LEN - headerBytesRead));
                        if (headerBytesReadIteration == 0)
                        {
                            // 0 means the connection was closed, tell the caller
                            return(0);
                        }
                        headerBytesRead += headerBytesReadIteration;
                    } while (headerBytesRead < TdsEnums.HEADER_LEN);

                    // read the packet data size from the header and store it in case it is needed for a subsequent call
                    _packetBytes = ((headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET] << 8) | headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET + 1]) - TdsEnums.HEADER_LEN;

                    // read as much from the packet as the caller can accept
                    int packetBytesRead = _stream.Read(buffer.Slice(0, Math.Min(buffer.Length, _packetBytes)));
                    _packetBytes -= packetBytesRead;
                    return(packetBytesRead);
                }
            }
        }
        public override void Write(byte[] buffer, int offset, int count)
        {
            // During the SSL negotiation phase, SSL is tunnelled over TDS packet type 0x12. After
            // negotiation, the underlying socket only sees SSL frames.
            if (!_encapsulate)
            {
                _stream.Write(buffer, offset, count);
                _stream.Flush();
                return;
            }

            using (SNIEventScope.Create("<sc.SNI.SslOverTdsStream.Write |SNI|INFO|SCOPE> writing encapsulated bytes"))
            {
                int    remainingBytes = count;
                int    dataOffset     = offset;
                byte[] packetBuffer   = null;
                while (remainingBytes > 0)
                {
                    int dataLength   = Math.Min(PACKET_SIZE_WITHOUT_HEADER, remainingBytes);
                    int packetLength = TdsEnums.HEADER_LEN + dataLength;
                    remainingBytes -= dataLength;

                    if (packetBuffer == null)
                    {
                        packetBuffer = ArrayPool <byte> .Shared.Rent(packetLength);
                    }
                    else if (packetBuffer.Length < packetLength)
                    {
                        ArrayPool <byte> .Shared.Return(packetBuffer, clearArray : true);

                        packetBuffer = ArrayPool <byte> .Shared.Rent(packetLength);
                    }

                    SetupPreLoginPacketHeader(packetBuffer, dataLength, remainingBytes);

                    Array.Copy(buffer, dataOffset, packetBuffer, TdsEnums.HEADER_LEN, dataLength);

                    _stream.Write(packetBuffer, 0, packetLength);
                    _stream.Flush();

                    dataOffset += dataLength;
                }
                if (packetBuffer != null)
                {
                    ArrayPool <byte> .Shared.Return(packetBuffer, clearArray : true);
                }
            }
        }
        public override async ValueTask <int> ReadAsync(Memory <byte> buffer, CancellationToken cancellationToken = default)
        {
            if (!_encapsulate)
            {
                int read;
                {
                    ValueTask <int> readValueTask = _stream.ReadAsync(buffer, cancellationToken);
                    if (readValueTask.IsCompletedSuccessfully)
                    {
                        read = readValueTask.Result;
                    }
                    else
                    {
                        read = await readValueTask.ConfigureAwait(false);
                    }
                }
                return(read);
            }
            using (SNIEventScope.Create("<sc.SNI.SslOverTdsStream.ReadAsync |SNI|INFO|SCOPE> reading encapsulated bytes"))
            {
                if (_packetBytes > 0)
                {
                    // there are queued bytes from a previous packet available
                    // work out how many of the remaining bytes we can consume
                    int wantedCount = Math.Min(buffer.Length, _packetBytes);

                    int readCount;
                    {
                        ValueTask <int> remainderReadValueTask = _stream.ReadAsync(buffer.Slice(0, wantedCount), cancellationToken);
                        if (remainderReadValueTask.IsCompletedSuccessfully)
                        {
                            readCount = remainderReadValueTask.Result;
                        }
                        else
                        {
                            readCount = await remainderReadValueTask.ConfigureAwait(false);
                        }
                    }
                    if (readCount == 0)
                    {
                        // 0 means the connection was closed, tell the caller
                        return(0);
                    }
                    _packetBytes -= readCount;
                    return(readCount);
                }
                else
                {
                    byte[] headerBytes = ArrayPool <byte> .Shared.Rent(TdsEnums.HEADER_LEN);

                    // fetch the packet header to determine how long the packet is
                    int headerBytesRead = 0;
                    do
                    {
                        int headerBytesReadIteration;
                        {
                            ValueTask <int> headerReadValueTask = _stream.ReadAsync(headerBytes.AsMemory(headerBytesRead, (TdsEnums.HEADER_LEN - headerBytesRead)), cancellationToken);
                            if (headerReadValueTask.IsCompletedSuccessfully)
                            {
                                headerBytesReadIteration = headerReadValueTask.Result;
                            }
                            else
                            {
                                headerBytesReadIteration = await headerReadValueTask.ConfigureAwait(false);
                            }
                        }
                        if (headerBytesReadIteration == 0)
                        {
                            // 0 means the connection was closed, cleanup the rented array and then tell the caller
                            ArrayPool <byte> .Shared.Return(headerBytes, clearArray : true);

                            return(0);
                        }
                        headerBytesRead += headerBytesReadIteration;
                    } while (headerBytesRead < TdsEnums.HEADER_LEN);

                    // read the packet data size from the header and store it in case it is needed for a subsequent call
                    _packetBytes = ((headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET] << 8) | headerBytes[TdsEnums.HEADER_LEN_FIELD_OFFSET + 1]) - TdsEnums.HEADER_LEN;

                    ArrayPool <byte> .Shared.Return(headerBytes, clearArray : true);

                    // read as much from the packet as the caller can accept
                    int packetBytesRead;
                    {
                        ValueTask <int> packetReadValueTask = _stream.ReadAsync(buffer.Slice(0, Math.Min(buffer.Length, _packetBytes)), cancellationToken);
                        if (packetReadValueTask.IsCompletedSuccessfully)
                        {
                            packetBytesRead = packetReadValueTask.Result;
                        }
                        else
                        {
                            packetBytesRead = await packetReadValueTask.ConfigureAwait(false);
                        }
                    }
                    _packetBytes -= packetBytesRead;
                    return(packetBytesRead);
                }
            }
        }
        public override async ValueTask WriteAsync(ReadOnlyMemory <byte> buffer, CancellationToken cancellationToken = default)
        {
            if (!_encapsulate)
            {
                {
                    ValueTask valueTask = _stream.WriteAsync(buffer, cancellationToken);
                    if (!valueTask.IsCompletedSuccessfully)
                    {
                        await valueTask.ConfigureAwait(false);
                    }
                }
                Task flushTask = _stream.FlushAsync();
                if (flushTask.IsCompletedSuccessfully)
                {
                    await flushTask.ConfigureAwait(false);
                }
                return;
            }

            using (SNIEventScope.Create("<sc.SNI.SslOverTdsStream.WriteAsync |SNI|INFO|SCOPE> writing encapsulated bytes"))
            {
                ReadOnlyMemory <byte> remaining = buffer;
                byte[] packetBuffer             = null;
                try
                {
                    while (remaining.Length > 0)
                    {
                        int dataLength   = Math.Min(PACKET_SIZE_WITHOUT_HEADER, remaining.Length);
                        int packetLength = TdsEnums.HEADER_LEN + dataLength;

                        if (packetBuffer == null)
                        {
                            packetBuffer = ArrayPool <byte> .Shared.Rent(packetLength);
                        }
                        else if (packetBuffer.Length < packetLength)
                        {
                            ArrayPool <byte> .Shared.Return(packetBuffer, clearArray : true);

                            packetBuffer = ArrayPool <byte> .Shared.Rent(packetLength);
                        }

                        SetupPreLoginPacketHeader(packetBuffer, dataLength, remaining.Length - dataLength);

                        remaining.Span.Slice(0, dataLength).CopyTo(packetBuffer.AsSpan(TdsEnums.HEADER_LEN, dataLength));

                        {
                            ValueTask packetWriteValueTask = _stream.WriteAsync(new ReadOnlyMemory <byte>(packetBuffer, 0, packetLength), cancellationToken);
                            if (!packetWriteValueTask.IsCompletedSuccessfully)
                            {
                                await packetWriteValueTask.ConfigureAwait(false);
                            }
                        }

                        await _stream.FlushAsync().ConfigureAwait(false);


                        remaining = remaining.Slice(dataLength);
                    }
                }
                finally
                {
                    if (packetBuffer != null)
                    {
                        ArrayPool <byte> .Shared.Return(packetBuffer, clearArray : true);
                    }
                }
            }
        }