protected override async Task Read(CancellationToken token)
        {
            _memoryPoolRentSize = Math.Min(_memoryPool.MaxBufferSize, (int)Stream.Length);

            var memoryOwner         = _memoryPool.Rent(_memoryPoolRentSize);
            var segmentHints        = new List <int>();
            int rentedChunks        = 1;
            int consumedMemoryIndex = 0;

            while (!Disposed)
            {
                int memoryStartIndex = 0;
                int bytesRead        = await Stream.ReadAsync(memoryOwner.Memory.Slice(consumedMemoryIndex));

                bool consumeSectionDelimiters = false;

                if (bytesRead == 0)
                {
                    break;
                }

                for (int i = 0; i < bytesRead; i++)
                {
                    // Read to the end of the line in the stream
                    var b = memoryOwner.Memory.Span[i];

                    if (consumeSectionDelimiters)
                    {
                        memoryStartIndex++;

                        if (IsSectionDelimiter(b))
                        {
                            continue;
                        }

                        consumeSectionDelimiters = false;
                    }

                    // The relative index into the memory, 0 indexed
                    var memoryIndex = i - memoryStartIndex;

                    if (IsSectionDelimiter(b))
                    {
                        // Current memory represents a row. Write it to be read by the reader.
                        // Memory should be trimmed to only number of bytes read. It may have
                        // been overallocated
                        QueueSection(memoryStartIndex, memoryIndex);
                        consumeSectionDelimiters = true;
                        memoryStartIndex         = i;
                    }

                    if (IsSegmentDelimiter(b))
                    {
                        segmentHints.Add(memoryIndex);
                    }
                }

                if (memoryStartIndex == 0)
                {
                    // None of the memory was used, check if the whole stream has been read
                    if (bytesRead == Stream.Length)
                    {
                        // The whole memory chunk is a section
                        QueueSection(0, bytesRead);
                    }
                    else
                    {
                        throw new OutOfMemoryException($"Unable to allocate enough memory to read section of stream. May be improved in the future");
                    }
                }
                else if ((memoryStartIndex + 1) < bytesRead)
                {
                    // The current allocated memory has run out of space, need to relocate
                    int rentSize  = (int)Stream.Length - (rentedChunks * _memoryPoolRentSize);
                    var tmpMemory = _memoryPool.Rent(Math.Min(rentSize, _memoryPoolRentSize));

                    // Only copy memory that's been used
                    var memToCopy = memoryOwner.Memory.Slice(memoryStartIndex, bytesRead - memoryStartIndex);
                    memToCopy.CopyTo(tmpMemory.Memory);

                    // Dispose of old memory and save new memory
                    memoryOwner.Dispose();
                    memoryOwner = tmpMemory;

                    // Set the consumed memory index to accomodate the new memory that was copied
                    consumedMemoryIndex = (bytesRead - memoryStartIndex - 1);
                }
            }

            void QueueSection(int start, int size)
            {
                var section = new MemoryManagedSection(memoryOwner, start, size, segmentHints, Settings.Encoding);

                _queue.Enqueue(section);
                segmentHints.Clear();
            }
        }
Exemple #2
0
        protected override Task Read(CancellationToken token)
        {
            _memoryPoolRentSize = Math.Min(_memoryPool.MaxBufferSize, (int)Stream.Length);

            var  memoryOwner = _memoryPool.Rent(_memoryPoolRentSize);
            int  bytesRead   = 0;
            bool consumeSectionDelimiters = false;
            var  segmentHints             = new List <int>();

            int memoryStartIndex = 0;
            int rentedChunks     = 1;

            while (!Disposed)
            {
                // Read to the end of the line in the stream
                int b = Stream.ReadByte();

                if (b == -1)
                {
                    break;
                }

                if (consumeSectionDelimiters)
                {
                    while (IsSectionDelimiter((byte)b))
                    {
                        b = Stream.ReadByte();

                        if (b == -1)
                        {
                            break;
                        }
                    }

                    consumeSectionDelimiters = false;
                }

                var memoryIndex = memoryStartIndex + bytesRead;
                memoryOwner.Memory.Span[memoryIndex] = (byte)b;

                if (IsSectionDelimiter((byte)b))
                {
                    // Current memory represents a row. Write it to be read by the reader.
                    // Memory should be trimmed to only number of bytes read. It may have
                    // been overallocated
                    QueueSection();
                    consumeSectionDelimiters = true;
                }
                else if (++bytesRead >= memoryOwner.Memory.Length)
                {
                    // The current allocated memory has run out of space, need to relocate
                    int rentSize  = (int)Stream.Length - (rentedChunks * _memoryPoolRentSize);
                    var tmpMemory = _memoryPool.Rent(Math.Min(rentSize, _memoryPoolRentSize));

                    // Only copy memory that's been used
                    var memToCopy = memoryOwner.Memory.Slice(memoryStartIndex, bytesRead);
                    memToCopy.CopyTo(tmpMemory.Memory);

                    // Dispose of old memory and save new memory
                    memoryOwner.Dispose();
                    memoryOwner = tmpMemory;

                    // Reset the start index, since old memory is no longer managed here
                    memoryStartIndex = 0;
                }

                if ((bytesRead > 0) && IsSegmentDelimiter((byte)b))
                {
                    segmentHints.Add(bytesRead - 1);
                }
            }

            if (bytesRead != 0)
            {
                QueueSection();
            }

            void QueueSection()
            {
                var section = new MemoryManagedSection(memoryOwner, memoryStartIndex, bytesRead, segmentHints, Settings.Encoding);

                _queue.Enqueue(section);
                segmentHints.Clear();
                memoryStartIndex += bytesRead;
                bytesRead         = 0;
            }

            return(Task.CompletedTask);
        }