| | 1 | | namespace ValidateLib.TabularData.Parsing |
| | 2 | | { |
| | 3 | | using System; |
| | 4 | | using System.IO; |
| | 5 | | using System.Runtime.CompilerServices; |
| | 6 | | using System.Text; |
| | 7 | |
|
| | 8 | | /// <summary> |
| | 9 | | /// Abstraction above classical <see cref="StreamReader"/> so that we buff some of the |
| | 10 | | /// characters and can move and forward in the stream in O(1) time. |
| | 11 | | /// </summary> |
| | 12 | | public class CustomStreamReader : StreamReader |
| | 13 | | { |
| | 14 | | private char[] buffer; |
| | 15 | | private int bufferSize; |
| | 16 | | private int charsCount; |
| | 17 | | private int position; |
| | 18 | | private int charsRead; |
| | 19 | | private int maxIndex; |
| 1 | 20 | | private bool firstFill = true; |
| | 21 | | private bool endOfStream = false; |
| | 22 | |
|
| 1 | 23 | | public CustomStreamReader(Stream stream, int bufferSize, int charsCount) : base(stream, Encoding.UTF8, true, buf |
| | 24 | | { |
| 1 | 25 | | this.bufferSize = bufferSize; |
| 1 | 26 | | this.charsCount = charsCount; |
| 1 | 27 | | buffer = new char[bufferSize]; |
| 1 | 28 | | position = 0; |
| 1 | 29 | | maxIndex = bufferSize - 1; |
| | 30 | |
|
| | 31 | |
|
| | 32 | | // Read the initial characters into the buffer |
| 1 | 33 | | FillBuffer(); |
| 1 | 34 | | } |
| | 35 | |
|
| 1 | 36 | | private bool IsPositionInBounds() => position <= maxIndex; |
| 1 | 37 | | private bool IsPositionOutOfBounds() => position > maxIndex; |
| | 38 | |
|
| | 39 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 40 | | // Read the next character in the buffer and move the pointer |
| | 41 | | public int ReadChar() |
| | 42 | | { |
| 1 | 43 | | if (IsPositionInBounds()) |
| | 44 | | { |
| 1 | 45 | | char nextChar = buffer[position++]; |
| 1 | 46 | | if (IsPositionOutOfBounds()) |
| | 47 | | { |
| 1 | 48 | | FillBuffer(); |
| | 49 | | } |
| 1 | 50 | | return nextChar; // Return the character as an integer |
| | 51 | | } |
| | 52 | |
|
| 1 | 53 | | return ReaderConstants.END_OF_STREAM_CONSTANT; // End of stream |
| | 54 | | } |
| | 55 | |
|
| | 56 | | /// <summary> |
| | 57 | | /// Read the specified number of characters into the provided array and move the pointer |
| | 58 | | /// </summary> |
| | 59 | | /// <param name="count">number of chars to read</param> |
| | 60 | | /// <param name="resultArray"> array where to put the read chars</param> |
| | 61 | | /// <returns> number of chars actually read</returns> |
| | 62 | | public int ReadChars(int count, char[] resultArray) |
| | 63 | | { |
| 1 | 64 | | int charsRead = 0; |
| | 65 | |
|
| 1 | 66 | | while (charsRead < count) |
| | 67 | | { |
| 1 | 68 | | int nextSymbol = ReadChar(); |
| 1 | 69 | | if (nextSymbol == ReaderConstants.END_OF_STREAM_CONSTANT) |
| | 70 | | break; |
| | 71 | |
|
| 1 | 72 | | resultArray[charsRead] = (char)nextSymbol; |
| 1 | 73 | | charsRead++; |
| | 74 | | } |
| | 75 | |
|
| 1 | 76 | | return charsRead; |
| | 77 | | } |
| | 78 | |
|
| | 79 | | // Move back on the pointer inside the buffer |
| | 80 | | public void MoveBack(int count) |
| | 81 | | { |
| 1 | 82 | | position = Math.Max(0, position - count); |
| 1 | 83 | | } |
| | 84 | |
|
| | 85 | | /// <summary> |
| | 86 | | /// Fill the buffer by reading from the underlying StreamReader |
| | 87 | | /// </summary> |
| | 88 | | /// <returns> True if there were any chars left in the underlying stream</returns> |
| | 89 | | private bool FillBuffer() |
| | 90 | | { |
| 1 | 91 | | if (firstFill) |
| | 92 | | { |
| 1 | 93 | | charsRead = base.Read(buffer, 0, bufferSize); |
| 1 | 94 | | maxIndex = charsRead - 1; |
| 1 | 95 | | position = 0; |
| 1 | 96 | | firstFill = false; |
| 1 | 97 | | if (charsRead < bufferSize) |
| 1 | 98 | | endOfStream = true; |
| | 99 | | } |
| | 100 | | else |
| | 101 | | { |
| | 102 | | // we rotate the end of the buffer to the front |
| 1 | 103 | | if (!endOfStream) |
| | 104 | | { |
| 1 | 105 | | Array.Copy(buffer, maxIndex + 1 - charsCount, buffer, 0, charsCount); |
| 1 | 106 | | charsRead = base.Read(buffer, charsCount, bufferSize - charsCount); |
| 1 | 107 | | maxIndex = charsCount + charsRead - 1; |
| 1 | 108 | | position = charsCount; |
| | 109 | | } |
| | 110 | | } |
| | 111 | |
|
| 1 | 112 | | if (charsRead == 0) |
| | 113 | | { |
| 1 | 114 | | endOfStream = true; |
| | 115 | | } |
| 1 | 116 | | return charsRead > 0; |
| | 117 | | } |
| | 118 | |
|
| | 119 | | } |
| | 120 | | } |