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