/// <summary>Unwraps inbound SSL records.</summary>
        private void Unwrap(IChannelHandlerContext ctx, IByteBuffer packet, int offset, int length)
        {
            bool pending = false;

            IByteBuffer outputBuffer = null;

            try
            {
#if NETCOREAPP || NETSTANDARD_2_0_GREATER
                ReadOnlyMemory <byte> inputIoBuffer = packet.GetReadableMemory(offset, length);
                _mediationStream.SetSource(inputIoBuffer, ctx.Allocator);
#else
                ArraySegment <byte> inputIoBuffer = packet.GetIoBuffer(offset, length);
                _mediationStream.SetSource(inputIoBuffer.Array, inputIoBuffer.Offset, ctx.Allocator);
#endif
                if (!EnsureAuthenticationCompleted(ctx))
                {
                    _mediationStream.ExpandSource(length);
                    return;
                }

                _mediationStream.ExpandSource(length);

                var currentReadFuture = _pendingSslStreamReadFuture;

                if (currentReadFuture is object)
                {
                    // restoring context from previous read
                    Debug.Assert(_pendingSslStreamReadBuffer is object);

                    outputBuffer = _pendingSslStreamReadBuffer;
                    var outputBufferLength = outputBuffer.WritableBytes;

                    _pendingSslStreamReadFuture = null;
                    _pendingSslStreamReadBuffer = null;

                    // there was a read pending already, so we make sure we completed that first
                    if (currentReadFuture.IsCompleted)
                    {
                        if (currentReadFuture.IsFailure())
                        {
                            // The decryption operation failed
                            ExceptionDispatchInfo.Capture(currentReadFuture.Exception.InnerException).Throw();
                        }
                        int read = currentReadFuture.Result;
                        if (0u >= (uint)read)
                        {
                            // Stream closed
                            NotifyClosePromise(null);
                            return;
                        }

                        // Now output the result of previous read and decide whether to do an extra read on the same source or move forward
                        outputBuffer.Advance(read);
                        _firedChannelRead = true;
                        ctx.FireChannelRead(outputBuffer);

                        currentReadFuture = null;
                        outputBuffer      = null;

                        if (0u >= (uint)_mediationStream.SourceReadableBytes)
                        {
                            // we just made a frame available for reading but there was already pending read so SslStream read it out to make further progress there

                            if (read < outputBufferLength)
                            {
                                // SslStream returned non-full buffer and there's no more input to go through ->
                                // typically it means SslStream is done reading current frame so we skip
                                return;
                            }

                            // we've read out `read` bytes out of current packet to fulfil previously outstanding read
                            outputBufferLength = length - read;
                            if ((uint)(outputBufferLength - 1) > SharedConstants.TooBigOrNegative) // <= 0
                            {
                                // after feeding to SslStream current frame it read out more bytes than current packet size
                                outputBufferLength = c_fallbackReadBufferSize;
                            }
                        }
                        outputBuffer      = ctx.Allocator.Buffer(outputBufferLength);
                        currentReadFuture = ReadFromSslStreamAsync(outputBuffer, outputBufferLength);
                    }
                }
                else
                {
                    // there was no pending read before so we estimate buffer of `length` bytes to be sufficient
                    outputBuffer      = ctx.Allocator.Buffer(length);
                    currentReadFuture = ReadFromSslStreamAsync(outputBuffer, length);
                }

                // read out the rest of SslStream's output (if any) at risk of going async
                // using FallbackReadBufferSize - buffer size we're ok to have pinned with the SslStream until it's done reading
                while (true)
                {
                    if (currentReadFuture is object)
                    {
                        if (!currentReadFuture.IsCompleted)
                        {
                            break;
                        }
                        if (currentReadFuture.IsFailure())
                        {
                            // The decryption operation failed
                            ExceptionDispatchInfo.Capture(currentReadFuture.Exception.InnerException).Throw();
                        }
                        int read = currentReadFuture.Result;

                        if (0u >= (uint)read)
                        {
                            // Stream closed
                            NotifyClosePromise(null);
                            return;
                        }

                        outputBuffer.Advance(read);
                        _firedChannelRead = true;
                        ctx.FireChannelRead(outputBuffer);

                        currentReadFuture = null;
                        outputBuffer      = null;
                        if (0u >= (uint)_mediationStream.SourceReadableBytes)
                        {
                            return;
                        }
                    }
                    outputBuffer      = ctx.Allocator.Buffer(c_fallbackReadBufferSize);
                    currentReadFuture = ReadFromSslStreamAsync(outputBuffer, c_fallbackReadBufferSize);
                }

                pending = true;
                _pendingSslStreamReadBuffer = outputBuffer;
                _pendingSslStreamReadFuture = currentReadFuture;
            }
            finally
            {
                _mediationStream.ResetSource(ctx.Allocator);
                if (!pending && outputBuffer is object)
                {
                    if (outputBuffer.IsReadable())
                    {
                        _firedChannelRead = true;
                        ctx.FireChannelRead(outputBuffer);
                    }
                    else
                    {
                        outputBuffer.SafeRelease();
                    }
                }
            }
        }