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