using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace IrcTokens { public class StatefulDecoder { private byte[] _buffer; private Encoding _encoding; private Encoding _fallback; public Encoding Encoding { get => _encoding ?? Encoding.GetEncoding(Encoding.UTF8.CodePage, EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback); set { if (value != null) { _encoding = Encoding.GetEncoding(value.CodePage, EncoderFallback.ExceptionFallback, DecoderFallback.ReplacementFallback); } } } public Encoding Fallback { get => _fallback ?? Encoding.GetEncoding(Encoding.GetEncoding("iso-8859-1").CodePage, EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback); set { if (value != null) { _fallback = Encoding.GetEncoding(value.CodePage, EncoderFallback.ReplacementFallback, DecoderFallback.ReplacementFallback); } } } public string Pending => Encoding.GetString(_buffer); public StatefulDecoder() { Clear(); } public void Clear() { _buffer = Array.Empty(); } public List Push(string data) { return Push(Encoding.GetBytes(data)); } public List Push(byte[] data) { if (data == null || data.Length == 0) { return null; } _buffer = _buffer.Concat(data).ToArray(); // simulate string.Split('\n') before decoding var newLineIndices = _buffer.Select((b, i) => b == '\n' ? i : -1).Where(i => i != -1).ToArray(); var lines = new List(); for (int i = 0, currentIndex = 0; i < newLineIndices.Length; ++i) { var n = new byte[newLineIndices[i] - currentIndex]; Array.Copy(_buffer, currentIndex, n, 0, newLineIndices[i] - currentIndex); currentIndex = newLineIndices[i] + 1; lines.Add(n); } var listLines = lines.Select(l => l.ToList()).ToList(); // simulate string.Trim('\r') before decoding foreach (var line in listLines) { var i = 0; while (line[i] == '\r') { line.RemoveAt(i); i++; } i = line.Count - 1; while (line[i] == '\r') { line.RemoveAt(i); i--; } } var decodeLines = new List(); foreach (var line in listLines.Select(l => l.ToArray())) { try { decodeLines.Add(Encoding.GetString(line)); } catch (DecoderFallbackException) { decodeLines.Add(Fallback.GetString(line)); } } return decodeLines .Select(l => new Line(l)) .ToList(); } } }