using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace System.Unicode.Builder { public class UnicodeDataFileReader : IDisposable { private struct AsciiCharBuffer { private readonly UnicodeDataFileReader reader; private int length; public AsciiCharBuffer(UnicodeDataFileReader reader) { this.reader = reader; this.length = 0; } public int Length { get { return length; } } private void EnsureExtraCapacity(int count) { if (count < 0) throw new ArgumentOutOfRangeException("requiredExtraCapacity"); if (reader.charBuffer.Length < checked(length + count)) Array.Resize(ref reader.charBuffer, Math.Max(length + count, reader.charBuffer.Length << 1)); } public void Append(byte[] value, int startIndex, int count) { if (value == null) throw new ArgumentNullException("value"); if (startIndex >= value.Length) throw new ArgumentOutOfRangeException("startIndex"); if (checked(count += startIndex) > value.Length) throw new ArgumentOutOfRangeException("count"); EnsureExtraCapacity(value.Length); var buffer = reader.charBuffer; for (int i = startIndex; i < count; ++i) { byte b = value[i]; if (b > 127) throw new InvalidDataException(string.Format("Unexpected character value: {0}.", b)); buffer[length++] = (char)b; } } public override string ToString() { return length > 0 ? new string(reader.charBuffer, 0, length) : string.Empty; } } private readonly Stream stream; private readonly byte[] byteBuffer; private char[] charBuffer; private int index; private int length; private bool hasField = false; private readonly bool leaveOpen; public UnicodeDataFileReader(Stream stream) : this(stream, false) { } public UnicodeDataFileReader(Stream stream, bool leaveOpen) { this.stream = stream; this.byteBuffer = new byte[8192]; this.charBuffer = new char[100]; this.leaveOpen = leaveOpen; } public void Dispose() { if (!leaveOpen) stream.Dispose(); } private bool RefillBuffer() { // Evilish line of code. 😈 return (length = stream.Read(byteBuffer, 0, byteBuffer.Length)) != (index = 0); } private static bool IsNewLineOrComment(byte b) { return b == '\n' || b == '#'; } /// Moves the stream to the next valid data row. /// if data is available; otherwise. public bool MoveToNextLine() { if (length == 0) { if (RefillBuffer()) { if (!IsNewLineOrComment(byteBuffer[index])) { hasField = true; goto Completed; } } else { return false; } } do { while (index < length) { if (byteBuffer[index++] == '\n') { if (index < length && !IsNewLineOrComment(byteBuffer[index])) { hasField = true; goto Completed; } } } } while (RefillBuffer()); hasField = false; Completed: ; return hasField; } /// Reads the next data field. /// This method will return on end of line. /// The text value of the read field, if available; otherwise. public string ReadField() { if (length == 0) throw new InvalidOperationException(); if (!hasField) return null; else if (index >= length) RefillBuffer(); // If the current character is a new line or a comment, we are at the end of a line. if (IsNewLineOrComment(byteBuffer[index])) { if (hasField) { hasField = false; return string.Empty; } else { return null; } } var buffer = new AsciiCharBuffer(this); int startOffset; int endOffset; do { startOffset = index; endOffset = -1; while (index < length) { byte b = byteBuffer[index]; if (IsNewLineOrComment(b)) // NB: Do not advance to the next byte when end of line has been reached. { endOffset = index; hasField = false; break; } else if(b == ';') { endOffset = index++; break; } else { ++index; } } if (endOffset >= 0) { buffer.Append(byteBuffer, startOffset, endOffset - startOffset); break; } else if (index > startOffset) { buffer.Append(byteBuffer, startOffset, index - startOffset); } } while (RefillBuffer()); return buffer.ToString(); } /// Skips the next data field. /// This method will return on end of line. /// if a field was skipped; otherwise. public bool SkipField() { if (length == 0) throw new InvalidOperationException(); if (!hasField) return false; else if (index >= length) RefillBuffer(); // If the current character is a new line or a comment, we are at the end of a line. if (IsNewLineOrComment(byteBuffer[index])) { hasField = false; return false; } do { while (index < length) { byte b = byteBuffer[index]; if (IsNewLineOrComment(b)) // NB: Do not advance to the next byte when end of line has been reached. { hasField = false; return true; } else { ++index; if (b == ';') { return true; } } } } while (RefillBuffer()); return true; } } }