예제 #1
0
        /// <inheritdoc />
        protected override void ProcessCore(CsvReaderVisitorBase visitor)
        {
            var  tokenizer = new CsvTokenizer(_delimiter);
            bool ignoreUTF8ByteOrderMark = _ignoreUTF8ByteOrderMark;
            var  bytes = _sequence;

            if (bytes.IsSingleSegment)
            {
                CsvReadOnlyMemoryInput.ProcessFullSegment(bytes.First.Span, ignoreUTF8ByteOrderMark, tokenizer, visitor);
                return;
            }

            var enumerator = bytes.GetEnumerator();

            if (ignoreUTF8ByteOrderMark && EatUTF8BOM(tokenizer, visitor, ref enumerator))
            {
                return;
            }

            while (enumerator.MoveNext())
            {
                tokenizer.ProcessNextChunk(enumerator.Current.Span, visitor);
            }

            tokenizer.ProcessEndOfStream(visitor);
        }
예제 #2
0
        /// <summary>
        /// Describes the contents of this CSV data stream to a <see cref="CsvReaderVisitorBase"/>.
        /// </summary>
        /// <param name="visitor">
        /// The <see cref="CsvReaderVisitorBase"/> to describe this CSV data stream to.
        /// </param>
        /// <param name="progress">
        /// An optional <see cref="IProgress{T}"/> instance that will receive a report of the size
        /// of each chunk (in bytes) as processing finishes, followed by one more report with a zero
        /// when the last chunk in the stream has been processed.
        /// </param>
        /// <param name="cancellationToken">
        /// An optional <see cref="CancellationToken"/> that may be used to signal cancellation.
        /// </param>
        /// <returns>
        /// A <see cref="ValueTask"/> encapsulating the operation.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// Thrown when this stream has already been processed.
        /// </exception>
        /// <exception cref="OperationCanceledException">
        /// Thrown to acknowledge a canceled <paramref name="cancellationToken"/>.  Some subclasses
        /// may throw an instance of a subclass, such as <see cref="TaskCanceledException"/>.
        /// </exception>
        public async ValueTask ProcessAsync(CsvReaderVisitorBase visitor, IProgress <int> progress = null, CancellationToken cancellationToken = default)
        {
            if (!(Interlocked.CompareExchange(ref _processingHasStarted, ProcessingHasStartedSentinel, null) is null))
            {
                ThrowProcessingHasAlreadyStartedException();
            }

            await ProcessAsyncCore(visitor, progress, cancellationToken).ConfigureAwait(false);
        }
예제 #3
0
        /// <summary>
        /// Describes the contents of this CSV data stream to a <see cref="CsvReaderVisitorBase"/>.
        /// </summary>
        /// <param name="visitor">
        /// The <see cref="CsvReaderVisitorBase"/> to describe this CSV data stream to.
        /// </param>
        /// <exception cref="InvalidOperationException">
        /// Thrown when this stream has already been processed.
        /// </exception>
        public void Process(CsvReaderVisitorBase visitor)
        {
            if (!(Interlocked.CompareExchange(ref _processingHasStarted, ProcessingHasStartedSentinel, null) is null))
            {
                ThrowProcessingHasAlreadyStartedException();
            }

            ProcessCore(visitor);
        }
        /// <inheritdoc />
        protected override unsafe void ProcessCore(CsvReaderVisitorBase visitor)
        {
            var tokenizer = new CsvTokenizer(_delimiter);

            using (var fl = new FileStream(_csvFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan))
            {
                long length = fl.Length;
                if (length == 0)
                {
                    tokenizer.ProcessEndOfStream(visitor);
                    return;
                }

                using (var memoryMappedFile = MemoryMappedFile.CreateFromFile(fl, null, 0, MemoryMappedFileAccess.Read, HandleInheritability.None, leaveOpen: true))
                    using (var accessor = memoryMappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read))
                    {
                        var   handle = accessor.SafeMemoryMappedViewHandle;
                        byte *ptr    = null;
                        RuntimeHelpers.PrepareConstrainedRegions();
                        try
                        {
                            handle.AcquirePointer(ref ptr);

                            if (_ignoreUTF8ByteOrderMark)
                            {
                                var head = new ReadOnlySpan <byte>(UTF8BOM, 0, length < 3 ? unchecked ((int)length) : 3);
                                if (head.SequenceEqual(new ReadOnlySpan <byte>(ptr, head.Length)))
                                {
                                    length -= head.Length;
                                    ptr    += head.Length;
                                }
                            }

                            while (length > int.MaxValue)
                            {
                                tokenizer.ProcessNextChunk(new ReadOnlySpan <byte>(ptr, int.MaxValue), visitor);
                                length -= int.MaxValue;
                                ptr    += int.MaxValue;
                            }

                            tokenizer.ProcessNextChunk(new ReadOnlySpan <byte>(ptr, unchecked ((int)length)), visitor);
                            tokenizer.ProcessEndOfStream(visitor);
                        }
                        finally
                        {
                            if (ptr != null)
                            {
                                handle.ReleasePointer();
                            }
                        }
                    }
            }
        }
예제 #5
0
        /// <inheritdoc />
        protected override async ValueTask ProcessAsyncCore(CsvReaderVisitorBase visitor, IProgress <int> progress, CancellationToken cancellationToken)
        {
            var tokenizer = new CsvTokenizer(_delimiter);
            var reader    = _reader;

            if (_ignoreUTF8ByteOrderMark && await EatUTF8BOMAsync(tokenizer, visitor, progress, cancellationToken).ConfigureAwait(false))
            {
                return;
            }

            while (true)
            {
                var result = await reader.ReadAsync(cancellationToken).ConfigureAwait(false);

                if (result.IsCanceled)
                {
                    throw new OperationCanceledException(cancellationToken);
                }

                var buffer = result.Buffer;
                foreach (var segment in buffer)
                {
                    tokenizer.ProcessNextChunk(segment.Span, visitor);
                }

                reader.AdvanceTo(buffer.End);
                if (progress != null)
                {
                    long totalLength = buffer.Length;
                    while (totalLength > int.MaxValue)
                    {
                        progress.Report(int.MaxValue);
                        totalLength -= int.MaxValue;
                    }

                    if (totalLength != 0)
                    {
                        progress.Report(unchecked ((int)totalLength));
                    }
                }

                if (result.IsCompleted)
                {
                    break;
                }
            }

            tokenizer.ProcessEndOfStream(visitor);
            progress?.Report(0);
        }
예제 #6
0
        /// <inheritdoc />
        protected override async ValueTask ProcessAsyncCore(CsvReaderVisitorBase visitor, IProgress <int> progress, CancellationToken cancellationToken)
        {
            // not all streams support cancellation, so we might as well do this ourselves.  it
            // does involve a volatile read, so don't go overboard.
            cancellationToken.ThrowIfCancellationRequested();

            var tokenizer = new CsvTokenizer(_delimiter);
            var csvStream = _csvStream;
            int minReadBufferByteCount = _minReadBufferByteCount;
            var readBufferPool         = _readBufferPool;

            byte[] readBuffer;
            if (readBufferPool is null)
            {
                readBuffer = new byte[minReadBufferByteCount];
            }
            else
            {
                readBuffer = readBufferPool.Rent(minReadBufferByteCount);
            }

            try
            {
                if (_ignoreUTF8ByteOrderMark && await EatUTF8BOMAsync(tokenizer, visitor, csvStream, readBuffer, progress, cancellationToken).ConfigureAwait(false))
                {
                    return;
                }

                int cnt;
                while ((cnt = await csvStream.ReadAsync(readBuffer, 0, readBuffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
                {
                    // not all streams support cancellation, so we might as well do this ourselves.  it
                    // does involve a volatile read, so don't go overboard.
                    cancellationToken.ThrowIfCancellationRequested();

                    tokenizer.ProcessNextChunk(new ReadOnlySpan <byte>(readBuffer, 0, cnt), visitor);
                    progress?.Report(cnt);
                }
            }
            finally
            {
                readBufferPool?.Return(readBuffer, clearArray: true);
            }

            tokenizer.ProcessEndOfStream(visitor);
            progress?.Report(0);
        }
        private static bool EatUTF8BOM(CsvTokenizer tokenizer, CsvReaderVisitorBase visitor, Stream csvStream, byte[] readBuffer)
        {
            if (readBuffer.Length < 3)
            {
                // don't bother pooling; nobody should really ever care.
                readBuffer = new byte[3];
            }

            int byteCount = 0;

            while (byteCount < 3)
            {
                int readLength = csvStream.Read(readBuffer, byteCount, readBuffer.Length - byteCount);
                if (readLength == 0)
                {
                    if (byteCount != 0)
                    {
                        if (!new ReadOnlySpan <byte>(readBuffer, 0, byteCount).SequenceEqual(new ReadOnlySpan <byte>(UTF8BOM, 0, byteCount)))
                        {
                            tokenizer.ProcessNextChunk(new ReadOnlySpan <byte>(readBuffer, 0, byteCount), visitor);
                        }
                    }

                    tokenizer.ProcessEndOfStream(visitor);
                    return(true);
                }

                byteCount += readLength;
            }

            var buf = new ReadOnlySpan <byte>(readBuffer, 0, byteCount);

            if (buf.StartsWith(UTF8BOM))
            {
                buf = buf.Slice(3);
            }

            tokenizer.ProcessNextChunk(buf, visitor);

            return(false);
        }
        /// <inheritdoc />
        protected override void ProcessCore(CsvReaderVisitorBase visitor)
        {
            var tokenizer = new CsvTokenizer(_delimiter);
            var csvStream = _csvStream;
            int minReadBufferByteCount = _minReadBufferByteCount;
            var readBufferPool         = _readBufferPool;

            byte[] readBuffer;
            if (readBufferPool is null)
            {
                readBuffer = new byte[minReadBufferByteCount];
            }
            else
            {
                readBuffer = readBufferPool.Rent(minReadBufferByteCount);
            }

            try
            {
                if (_ignoreUTF8ByteOrderMark && EatUTF8BOM(tokenizer, visitor, csvStream, readBuffer))
                {
                    return;
                }

                int cnt;
                while ((cnt = csvStream.Read(readBuffer, 0, readBuffer.Length)) != 0)
                {
                    tokenizer.ProcessNextChunk(new ReadOnlySpan <byte>(readBuffer, 0, cnt), visitor);
                }
            }
            finally
            {
                readBufferPool?.Return(readBuffer, clearArray: true);
            }

            tokenizer.ProcessEndOfStream(visitor);
        }
예제 #9
0
        private static async ValueTask <bool> EatUTF8BOMAsync(CsvTokenizer tokenizer, CsvReaderVisitorBase visitor, Stream csvStream, byte[] readBuffer, IProgress <int> progress, CancellationToken cancellationToken)
        {
            if (readBuffer.Length < 3)
            {
                // don't bother pooling; nobody should really ever care.
                readBuffer = new byte[3];
            }

            int byteCount = 0;

            while (byteCount < 3)
            {
                int readLength = await csvStream.ReadAsync(readBuffer, byteCount, readBuffer.Length - byteCount, cancellationToken).ConfigureAwait(false);

                // not all streams support cancellation, so we might as well do this ourselves.  it
                // does involve a volatile read, so don't go overboard.
                cancellationToken.ThrowIfCancellationRequested();

                if (readLength == 0)
                {
                    if (byteCount != 0)
                    {
                        if (!new ReadOnlySpan <byte>(readBuffer, 0, byteCount).SequenceEqual(new ReadOnlySpan <byte>(UTF8BOM, 0, byteCount)))
                        {
                            tokenizer.ProcessNextChunk(new ReadOnlySpan <byte>(readBuffer, 0, byteCount), visitor);
                        }

                        progress?.Report(byteCount);
                    }

                    tokenizer.ProcessEndOfStream(visitor);
                    progress?.Report(0);
                    return(true);
                }

                byteCount += readLength;
            }

            var buf = new ReadOnlyMemory <byte>(readBuffer, 0, byteCount);

            if (buf.Span.StartsWith(UTF8BOM))
            {
                buf = buf.Slice(3);
            }

            tokenizer.ProcessNextChunk(buf.Span, visitor);
            progress?.Report(byteCount);

            return(false);
        }
예제 #10
0
 /// <summary>
 /// Implements the inner logic for <see cref="ProcessAsync"/>.
 /// </summary>
 /// <param name="visitor">
 /// The <see cref="CsvReaderVisitorBase"/> to describe this CSV data stream to.
 /// </param>
 /// <param name="progress">
 /// An optional <see cref="IProgress{T}"/> instance that will receive a report of the size
 /// of each chunk (in bytes) as processing finishes, followed by one more report with a zero
 /// when the last chunk in the stream has been processed.
 /// </param>
 /// <param name="cancellationToken">
 /// An optional <see cref="CancellationToken"/> that may be used to signal cancellation.
 /// </param>
 /// <returns>
 /// A <see cref="ValueTask"/> encapsulating the operation.
 /// </returns>
 /// <exception cref="InvalidOperationException">
 /// Thrown when this stream has already been processed.
 /// </exception>
 /// <exception cref="OperationCanceledException">
 /// Thrown to acknowledge a canceled <paramref name="cancellationToken"/>.  Some subclasses
 /// may throw an instance of a subclass, such as <see cref="TaskCanceledException"/>.
 /// </exception>
 protected abstract ValueTask ProcessAsyncCore(CsvReaderVisitorBase visitor, IProgress <int> progress, CancellationToken cancellationToken);
예제 #11
0
 /// <summary>
 /// Implements the inner logic for <see cref="Process"/>.
 /// </summary>
 /// <param name="visitor">
 /// The <see cref="CsvReaderVisitorBase"/> to describe this CSV data stream to.
 /// </param>
 /// <remarks>
 /// The base class will call this method at most once per instance.
 /// </remarks>
 protected abstract void ProcessCore(CsvReaderVisitorBase visitor);
예제 #12
0
        private static bool EatUTF8BOM(CsvTokenizer tokenizer, CsvReaderVisitorBase visitor, ref ReadOnlySequence <byte> .Enumerator enumerator)
        {
            ReadOnlyMemory <byte> segment;

            while (true)
            {
                if (!enumerator.MoveNext())
                {
                    tokenizer.ProcessEndOfStream(visitor);
                    return(true);
                }

                segment = enumerator.Current;
                if (!segment.IsEmpty)
                {
                    break;
                }
            }

            var span = segment.Span;

            ReadOnlySpan <byte> head = UTF8BOM;

            // this greed should **probably** pay off most of the time.
            if (span.Length >= 3)
            {
                if (span.StartsWith(head))
                {
                    span = span.Slice(3);
                }

                tokenizer.ProcessNextChunk(span, visitor);
                return(false);
            }

            int alreadyEaten = 0;

            while (true)
            {
                if (span[0] == head[alreadyEaten])
                {
                    span = span.Slice(1);
                    if (++alreadyEaten == 3)
                    {
                        tokenizer.ProcessNextChunk(span, visitor);
                        return(false);
                    }
                }
                else
                {
                    tokenizer.ProcessNextChunk(head.Slice(0, alreadyEaten), visitor);
                    tokenizer.ProcessNextChunk(span, visitor);
                    return(false);
                }

                if (span.IsEmpty)
                {
                    while (true)
                    {
                        if (!enumerator.MoveNext())
                        {
                            tokenizer.ProcessEndOfStream(visitor);
                            return(true);
                        }

                        segment = enumerator.Current;
                        if (!segment.IsEmpty)
                        {
                            break;
                        }
                    }

                    span = segment.Span;
                }
            }
        }
        internal static void ProcessFullSegment(ReadOnlySpan <byte> bytes, bool ignoreUTF8ByteOrderMark, CsvTokenizer tokenizer, CsvReaderVisitorBase visitor)
        {
            if (ignoreUTF8ByteOrderMark)
            {
                var head = new ReadOnlySpan <byte>(UTF8BOM, 0, bytes.Length < 3 ? bytes.Length : 3);
                if (bytes.StartsWith(head))
                {
                    bytes = bytes.Slice(head.Length);
                }
            }

            tokenizer.ProcessNextChunk(bytes, visitor);
            tokenizer.ProcessEndOfStream(visitor);
        }
 /// <inheritdoc />
 protected override void ProcessCore(CsvReaderVisitorBase visitor)
 {
     ProcessFullSegment(_memory.Span, _ignoreUTF8ByteOrderMark, new CsvTokenizer(_delimiter), visitor);
 }
예제 #15
0
        private async ValueTask <bool> EatUTF8BOMAsync(CsvTokenizer tokenizer, CsvReaderVisitorBase visitor, IProgress <int> progress, CancellationToken cancellationToken)
        {
            var reader = _reader;

            ReadOnlySequence <byte> buffer;

            // keep asking for more until we've seen either 3+ bytes or the end of the data.
            while (true)
            {
                var result = await reader.ReadAsync(cancellationToken).ConfigureAwait(false);

                if (result.IsCanceled)
                {
                    throw new OperationCanceledException(cancellationToken);
                }

                buffer = result.Buffer;
                if (buffer.Length >= 3)
                {
                    // we've seen 3+ bytes.
                    break;
                }

                if (result.IsCompleted)
                {
                    // we've seen the end of the data.
                    Finish();
                    tokenizer.ProcessEndOfStream(visitor);
                    reader.AdvanceTo(buffer.End);
                    progress?.Report(0);
                    return(true);
                }

                // tell the reader that we've looked at everything it had to give us, and we weren't
                // able to consume any of it, so the next read should have everything we've seen so
                // far, plus at least one more byte.
                reader.AdvanceTo(buffer.Start, buffer.End);
            }

            Finish();
            return(false);

            void Finish()
            {
                Span <byte> upToFirstThreeBytes = stackalloc byte[3];
                int         alreadyEaten        = 0;

                foreach (var segment in buffer)
                {
                    int lengthToCopy = 3 - alreadyEaten;
                    if (lengthToCopy > segment.Length)
                    {
                        lengthToCopy = segment.Length;
                    }

                    segment.Slice(0, lengthToCopy).Span.CopyTo(upToFirstThreeBytes.Slice(alreadyEaten, lengthToCopy));
                    alreadyEaten += lengthToCopy;
                    if (alreadyEaten == 3)
                    {
                        break;
                    }
                }

                upToFirstThreeBytes = upToFirstThreeBytes.Slice(0, alreadyEaten);
                var head = new ReadOnlySpan <byte>(UTF8BOM, 0, alreadyEaten);

                if (!upToFirstThreeBytes.SequenceEqual(head))
                {
                    tokenizer.ProcessNextChunk(upToFirstThreeBytes, visitor);
                }

                reader.AdvanceTo(buffer.GetPosition(alreadyEaten));
                progress?.Report(alreadyEaten);
            }
        }