IRC parsing, tokenization, and state handling in C#

Fix stateful decoder i think

+65 -37
+1 -1
IrcSharp.sln
··· 5 5 MinimumVisualStudioVersion = 10.0.40219.1 6 6 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IrcTokens", "IrcTokens\IrcTokens.csproj", "{9E812F45-B2CD-42D2-8378-EBEBF8697905}" 7 7 EndProject 8 - Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TokensSample", "Sample\TokensSample.csproj", "{A45DA39B-6B47-4713-8049-3B36E0235B67}" 8 + Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TokensSample", "TokensSample\TokensSample.csproj", "{A45DA39B-6B47-4713-8049-3B36E0235B67}" 9 9 EndProject 10 10 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IrcStates", "IrcStates\IrcStates.csproj", "{233E3CB4-61F1-4368-9139-7E9F4A58ED2D}" 11 11 EndProject
+59
IrcTokens/Extensions.cs
··· 1 + using System; 2 + using System.Collections.Generic; 3 + using System.Linq; 4 + 5 + namespace IrcTokens 6 + { 7 + public static class Extensions 8 + { 9 + public static IEnumerable<byte[]> Split(this byte[] bytes, byte separator) 10 + { 11 + if (bytes == null || bytes.Length == 0) return new List<byte[]>(); 12 + 13 + var newLineIndices = bytes.Select((b, i) => b == separator ? i : -1).Where(i => i != -1).ToArray(); 14 + var lines = new byte[newLineIndices.Length + 1][]; 15 + var currentIndex = 0; 16 + var arrIndex = 0; 17 + 18 + for (var i = 0; i < newLineIndices.Length && currentIndex < bytes.Length; ++i) 19 + { 20 + var n = new byte[newLineIndices[i] - currentIndex]; 21 + Array.Copy(bytes, currentIndex, n, 0, newLineIndices[i] - currentIndex); 22 + currentIndex = newLineIndices[i] + 1; 23 + lines[arrIndex++] = n; 24 + } 25 + 26 + // Handle the last string at the end of the array if there is one. 27 + if (currentIndex < bytes.Length) 28 + lines[arrIndex] = bytes.Skip(currentIndex).ToArray(); 29 + else if (arrIndex == newLineIndices.Length) 30 + // We had a separator character at the end of a string. Rather than just allowing 31 + // a null character, we'll replace the last element in the array with an empty string. 32 + lines[arrIndex] = Array.Empty<byte>(); 33 + 34 + return lines.ToArray(); 35 + } 36 + 37 + public static byte[] Trim(this IEnumerable<byte> bytes, byte separator) 38 + { 39 + if (bytes == null || !bytes.Any()) return Array.Empty<byte>(); 40 + var byteList = new List<byte>(bytes); 41 + var i = 0; 42 + 43 + while (byteList[i] == separator) 44 + { 45 + byteList.RemoveAt(i); 46 + i++; 47 + } 48 + 49 + i = byteList.Count - 1; 50 + while (byteList[i] == separator) 51 + { 52 + byteList.RemoveAt(i); 53 + i--; 54 + } 55 + 56 + return byteList.ToArray(); 57 + } 58 + } 59 + }
+2 -2
IrcTokens/Line.cs
··· 104 104 /// </summary> 105 105 /// <param name="val">escaped string</param> 106 106 /// <returns>unescaped string</returns> 107 - public static string UnescapeTag(string val) 107 + private static string UnescapeTag(string val) 108 108 { 109 109 var unescaped = new StringBuilder(); 110 110 ··· 141 141 /// </summary> 142 142 /// <param name="val">string to escape</param> 143 143 /// <returns>escaped string</returns> 144 - public static string EscapeTag(string val) 144 + private static string EscapeTag(string val) 145 145 { 146 146 for (var i = 0; i < TagUnescaped.Length; ++i) 147 147 val = val?.Replace(TagUnescaped[i], TagEscaped[i], StringComparison.Ordinal);
+3 -34
IrcTokens/StatefulDecoder.cs
··· 56 56 { 57 57 if (data == null || data.Length == 0) return null; 58 58 59 - _buffer = _buffer.Concat(data).ToArray(); 60 - 61 - // simulate string.Split('\n') before decoding 62 - var newLineIndices = _buffer.Select((b, i) => b == '\n' ? i : -1).Where(i => i != -1).ToArray(); 63 - var lines = new List<byte[]>(); 59 + _buffer = _buffer == null ? Array.Empty<byte>() : _buffer.Concat(data).ToArray(); 64 60 65 - for (int i = 0, currentIndex = 0; i < newLineIndices.Length; ++i) 66 - { 67 - var n = new byte[newLineIndices[i] - currentIndex]; 68 - Array.Copy(_buffer, currentIndex, n, 0, newLineIndices[i] - currentIndex); 69 - currentIndex = newLineIndices[i] + 1; 70 - lines.Add(n); 71 - } 72 - 73 - var listLines = lines.Select(l => l.ToList()).ToList(); 74 - 75 - // simulate string.Trim('\r') before decoding 76 - foreach (var line in listLines) 77 - { 78 - var i = 0; 79 - while (line[i] == '\r') 80 - { 81 - line.RemoveAt(i); 82 - i++; 83 - } 84 - 85 - i = line.Count - 1; 86 - while (line[i] == '\r') 87 - { 88 - line.RemoveAt(i); 89 - i--; 90 - } 91 - } 92 - 93 - _buffer = listLines[^1].ToArray(); 61 + var listLines = _buffer.Split((byte) '\n').Select(l => l.Trim((byte) '\r')).ToList(); 62 + _buffer = listLines.Last(); 94 63 95 64 var decodeLines = new List<Line>(); 96 65 foreach (var line in listLines.SkipLast(1).Select(l => l.ToArray()))
Sample/Client.cs TokensSample/Client.cs
Sample/Program.cs TokensSample/Program.cs
Sample/TokensSample.csproj TokensSample/TokensSample.csproj