IRC parsing, tokenization, and state handling in C#
at tunit 84 lines 2.4 kB view raw
1using System.Text; 2 3namespace IRCTokens; 4 5public class StatefulDecoder 6{ 7 private byte[] _buffer; 8 private Encoding _encoding; 9 private Encoding _fallback; 10 11 public StatefulDecoder() 12 { 13 Clear(); 14 } 15 16 public Encoding Encoding 17 { 18 get => _encoding ?? Encoding.GetEncoding(Encoding.UTF8.CodePage, EncoderFallback.ExceptionFallback, 19 DecoderFallback.ExceptionFallback); 20 set 21 { 22 if (value != null) 23 _encoding = Encoding.GetEncoding(value.CodePage, EncoderFallback.ExceptionFallback, 24 DecoderFallback.ReplacementFallback); 25 } 26 } 27 28 public Encoding Fallback 29 { 30 get => _fallback ?? Encoding.GetEncoding(Encoding.GetEncoding("iso-8859-1").CodePage, 31 EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback); 32 set 33 { 34 if (value != null) 35 _fallback = Encoding.GetEncoding(value.CodePage, EncoderFallback.ReplacementFallback, 36 DecoderFallback.ReplacementFallback); 37 } 38 } 39 40 public string Pending => Encoding.GetString(_buffer); 41 42 public void Clear() 43 { 44 _buffer = []; 45 } 46 47 public List<Line> Push(string data) 48 { 49 var bytes = Encoding.GetBytes(data); 50 return Push(bytes, bytes.Length); 51 } 52 53 public List<Line> Push(byte[] data, int bytesReceived) 54 { 55 if (data == null) return null; 56 57 _buffer = _buffer == null ? [] : _buffer.Concat(data.Take(bytesReceived)).ToArray(); 58 59 // truncate message at NUL if found 60 if (_buffer.Contains((byte) 0)) 61 { 62 _buffer = _buffer 63 .Take(Array.IndexOf(_buffer, (byte) 0)) 64 .Concat([(byte) '\n']) 65 .ToArray(); 66 } 67 68 var listLines = _buffer.Split((byte) '\n').Select(l => l.Trim((byte) '\r')).ToList(); 69 _buffer = listLines.LastOrDefault() ?? []; 70 71 var decodeLines = new List<Line>(); 72 foreach (var line in listLines.SkipLast(1).Select(l => l.ToArray())) 73 try 74 { 75 decodeLines.Add(new Line(Encoding.GetString(line))); 76 } 77 catch (DecoderFallbackException) 78 { 79 decodeLines.Add(new Line(Fallback.GetString(line))); 80 } 81 82 return decodeLines; 83 } 84}