private ReadResult Read()
        {
            if (_loadMore)
            {
                if (_remaining != 0)
                {
                    var take = (int)Math.Min(_remaining, _pageSize);
                    DebugLog($"Loading {take} bytes from offet {_offset}...");
                    var accessor = _file.CreateViewAccessor(_offset, take, MemoryMappedFileAccess.Read);

                    var next = new MappedPage(accessor, _offset, take);
                    Debug.Assert(next.RunningIndex == _offset);
                    _remaining -= take;
                    _offset    += take;

                    if (_first == null)
                    {
                        _first = _last = next;
                    }
                    else
                    {
                        _last.Next = next;
                        _last      = next;
                    }
                    DebugLog($"Loaded page {next}");
                }
                _loadMore = false;
            }

            if (_first == null)
            {
                Debug.Assert(_remaining == 0, "unexpected EOF");
                DebugLog($"Read has encountered EOF");
                return(new ReadResult(default, false, true));
        private static long CountAvailable(MappedPage page)
        {
            long total = 0;

            while (page != null)
            {
                total += page.Capacity - page.Consumed;
                page   = page.Next;
            }
            return(total);
        }
        /// <summary>
        /// Releases all resources associated with the object
        /// </summary>
        public void Dispose()
        {
            var page = _first;

            while (page != null)
            {
                try { page.Dispose(); } catch { }
                page = page.Next;
            }
            _first = _last = null;
            try { _file?.Dispose(); } catch { }
            _file = null;
        }
        /// <summary>
        /// Indicates how much data was consumed, and how much examined, from a read operation
        /// </summary>
        public override void AdvanceTo(SequencePosition consumed, SequencePosition examined)
        {
            var cPage = (MappedPage)consumed.GetObject();
            var ePage = (MappedPage)examined.GetObject();

            if (cPage == null || ePage == null)
            {
                if (_first == null)
                {
                    return;                 // that's fine - means they called Advance on an empty EOF
                }
                Throw.Argument("Invalid position; consumed/examined must remain inside the buffer");
            }

            Debug.Assert(ePage != null, "No examined page");
            Debug.Assert(cPage != null, "No consumed page");

            MappedPage newKeep;
            var        cOffset = consumed.GetInteger();

            if (cOffset == cPage.Capacity)
            {
                newKeep = cPage.Next;
            }
            else
            {
                newKeep          = cPage;
                newKeep.Consumed = cOffset;
            }
            if (newKeep == null)
            {
                DebugLog($"Retaining nothing");
                _last = null;
            }
            else
            {
                DebugLog($"Retaining page {newKeep}");
            }

            // now drop any pages we don't need
            if (newKeep != _first)
            {
                var page = _first;
                while (page != null && page != newKeep)
                {
                    DebugLog($"Dropping page {page}");
                    page.Dispose();
                    page = page.Next;
                }
                _first = newKeep;
            }

            // check whether they looked at everything
            if (_last == null)
            {
                _loadMore = true; // definitely
            }
            else
            {
                var eOffset = examined.GetInteger();
                _loadMore = ePage == _last && eOffset == ePage.Capacity;
            }
            DebugLog($"After AdvanceTo, {CountAvailable(_first)} available bytes, {_remaining} remaining unloaded bytes, load more: {_loadMore}");
        }