IRC parsing, tokenization, and state handling in C#
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}