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