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