/// <inheritdoc/>
            public async ValueTask <ChannelReaderBufferSlice <T> > StartReadAsync(
                int count,
                CancellationToken cancellationToken = default)
            {
                if (count <= 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(count), "Count should be non-zero positive.");
                }

                bool isReadStarted = false;

                while (true)
                {
                    Task <VoidResult> taskToAwait;

                    using (var guard = new StateReadGuard <TSyncObject>())
                    {
                        if (!isReadStarted)
                        {
                            EnsureSequentialUsage();
                            _readInProgress = true;
                            isReadStarted   = true;
                        }

                        int nonAllocatedLength = guard.GetAvailableDataForReading();
                        if (nonAllocatedLength == 0)
                        {
                            try
                            {
                                _owner.VerifyChannelNotClosed();
                            }
                            catch
                            {
                                _readInProgress = false;
                                throw;
                            }

                            Debug.Assert(_owner._dataAvailableCs != null, "_owner._dataAvailableCs != null");

                            taskToAwait = _owner._dataAvailableCs.Task;
                        }
                        else
                        {
                            LinkedListNode <SliceEntry> curNode = _owner._sliceEntries.Last;

                            // Searching for node to read from
                            while (curNode != null)
                            {
                                var sliceEntry = curNode.Value;

                                if (sliceEntry.Status == SliceEntryStatus.Data)
                                {
                                    Debug.Assert(sliceEntry.Length != 0, "sliceEntry.Length != 0");

                                    int allocateForReadLength = Math.Min(count, sliceEntry.Length);

                                    guard.NotifyAllocatedForReading(allocateForReadLength);

                                    if (allocateForReadLength == sliceEntry.Length)
                                    {
                                        sliceEntry.Status = SliceEntryStatus.AllocatedForRead;
                                        return(new ChannelReaderBufferSlice <T>(
                                                   sliceEntry.Buffer,
                                                   sliceEntry.Start,
                                                   sliceEntry.Length,
                                                   sliceEntry.Id));
                                    }

                                    int freeSpaceLeft = sliceEntry.Length - allocateForReadLength;
                                    sliceEntry.Length -= freeSpaceLeft;
                                    sliceEntry.Status  = SliceEntryStatus.AllocatedForRead;

                                    var newEntry = _owner.AllocateEmptyEntryAfter(curNode);
                                    newEntry.Buffer = sliceEntry.Buffer;
                                    newEntry.Start  = sliceEntry.Start + allocateForReadLength;
                                    newEntry.Length = freeSpaceLeft;
                                    newEntry.Status = SliceEntryStatus.Data;

                                    return(new ChannelReaderBufferSlice <T>(
                                               sliceEntry.Buffer,
                                               sliceEntry.Start,
                                               sliceEntry.Length,
                                               sliceEntry.Id));
                                }

                                curNode = curNode.Next;
                            }

                            Debug.Assert(false, "Available data counter does not corresponds to slice entries.");

                            throw new InvalidOperationException("Impossible state!");
                        }
                    }

                    await taskToAwait.WithCancellation(cancellationToken);
                }
            }
            /// <inheritdoc/>
            public ChannelReaderBufferSlice <T> TryStartRead(int count)
            {
                if (count <= 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(count), "Count should be non-zero positive.");
                }

                using (var guard = new StateReadGuard <TSyncObject>())
                {
                    EnsureSequentialUsage();

                    int nonAllocatedLength = guard.GetAvailableDataForReading();
                    if (nonAllocatedLength == 0)
                    {
                        return(new ChannelReaderBufferSlice <T>(EmptyBuffer, 0, 0, 0));
                    }

                    LinkedListNode <SliceEntry> curNode = _owner._sliceEntries.Last;

                    // Searching for node to read from
                    while (curNode != null)
                    {
                        var sliceEntry = curNode.Value;

                        if (sliceEntry.Status == SliceEntryStatus.Data)
                        {
                            Debug.Assert(sliceEntry.Length != 0, "sliceEntry.Length != 0");

                            int allocateForReadLength = Math.Min(count, sliceEntry.Length);

                            guard.NotifyAllocatedForReading(allocateForReadLength);

                            if (allocateForReadLength == sliceEntry.Length)
                            {
                                sliceEntry.Status = SliceEntryStatus.AllocatedForRead;
                                return(new ChannelReaderBufferSlice <T>(
                                           sliceEntry.Buffer,
                                           sliceEntry.Start,
                                           sliceEntry.Length,
                                           sliceEntry.Id));
                            }

                            int freeSpaceLeft = sliceEntry.Length - allocateForReadLength;
                            sliceEntry.Length -= freeSpaceLeft;
                            sliceEntry.Status  = SliceEntryStatus.AllocatedForRead;

                            var newEntry = _owner.AllocateEmptyEntryAfter(curNode);
                            newEntry.Buffer = sliceEntry.Buffer;
                            newEntry.Start  = sliceEntry.Start + allocateForReadLength;
                            newEntry.Length = freeSpaceLeft;
                            newEntry.Status = SliceEntryStatus.Data;

                            return(new ChannelReaderBufferSlice <T>(
                                       sliceEntry.Buffer,
                                       sliceEntry.Start,
                                       sliceEntry.Length,
                                       sliceEntry.Id));
                        }

                        curNode = curNode.Next;
                    }

                    Debug.Assert(false, "Available data counter does not corresponds to slice entries.");

                    throw new InvalidOperationException("Impossible state!");
                }
            }