···11+using System;
22+using System.Collections.Generic;
33+using System.Linq;
44+55+namespace IrcTokens
66+{
77+ public static class Extensions
88+ {
99+ public static IEnumerable<byte[]> Split(this byte[] bytes, byte separator)
1010+ {
1111+ if (bytes == null || bytes.Length == 0) return new List<byte[]>();
1212+1313+ var newLineIndices = bytes.Select((b, i) => b == separator ? i : -1).Where(i => i != -1).ToArray();
1414+ var lines = new byte[newLineIndices.Length + 1][];
1515+ var currentIndex = 0;
1616+ var arrIndex = 0;
1717+1818+ for (var i = 0; i < newLineIndices.Length && currentIndex < bytes.Length; ++i)
1919+ {
2020+ var n = new byte[newLineIndices[i] - currentIndex];
2121+ Array.Copy(bytes, currentIndex, n, 0, newLineIndices[i] - currentIndex);
2222+ currentIndex = newLineIndices[i] + 1;
2323+ lines[arrIndex++] = n;
2424+ }
2525+2626+ // Handle the last string at the end of the array if there is one.
2727+ if (currentIndex < bytes.Length)
2828+ lines[arrIndex] = bytes.Skip(currentIndex).ToArray();
2929+ else if (arrIndex == newLineIndices.Length)
3030+ // We had a separator character at the end of a string. Rather than just allowing
3131+ // a null character, we'll replace the last element in the array with an empty string.
3232+ lines[arrIndex] = Array.Empty<byte>();
3333+3434+ return lines.ToArray();
3535+ }
3636+3737+ public static byte[] Trim(this IEnumerable<byte> bytes, byte separator)
3838+ {
3939+ if (bytes == null || !bytes.Any()) return Array.Empty<byte>();
4040+ var byteList = new List<byte>(bytes);
4141+ var i = 0;
4242+4343+ while (byteList[i] == separator)
4444+ {
4545+ byteList.RemoveAt(i);
4646+ i++;
4747+ }
4848+4949+ i = byteList.Count - 1;
5050+ while (byteList[i] == separator)
5151+ {
5252+ byteList.RemoveAt(i);
5353+ i--;
5454+ }
5555+5656+ return byteList.ToArray();
5757+ }
5858+ }
5959+}
+2-2
IrcTokens/Line.cs
···104104 /// </summary>
105105 /// <param name="val">escaped string</param>
106106 /// <returns>unescaped string</returns>
107107- public static string UnescapeTag(string val)
107107+ private static string UnescapeTag(string val)
108108 {
109109 var unescaped = new StringBuilder();
110110···141141 /// </summary>
142142 /// <param name="val">string to escape</param>
143143 /// <returns>escaped string</returns>
144144- public static string EscapeTag(string val)
144144+ private static string EscapeTag(string val)
145145 {
146146 for (var i = 0; i < TagUnescaped.Length; ++i)
147147 val = val?.Replace(TagUnescaped[i], TagEscaped[i], StringComparison.Ordinal);
+3-34
IrcTokens/StatefulDecoder.cs
···5656 {
5757 if (data == null || data.Length == 0) return null;
58585959- _buffer = _buffer.Concat(data).ToArray();
6060-6161- // simulate string.Split('\n') before decoding
6262- var newLineIndices = _buffer.Select((b, i) => b == '\n' ? i : -1).Where(i => i != -1).ToArray();
6363- var lines = new List<byte[]>();
5959+ _buffer = _buffer == null ? Array.Empty<byte>() : _buffer.Concat(data).ToArray();
64606565- for (int i = 0, currentIndex = 0; i < newLineIndices.Length; ++i)
6666- {
6767- var n = new byte[newLineIndices[i] - currentIndex];
6868- Array.Copy(_buffer, currentIndex, n, 0, newLineIndices[i] - currentIndex);
6969- currentIndex = newLineIndices[i] + 1;
7070- lines.Add(n);
7171- }
7272-7373- var listLines = lines.Select(l => l.ToList()).ToList();
7474-7575- // simulate string.Trim('\r') before decoding
7676- foreach (var line in listLines)
7777- {
7878- var i = 0;
7979- while (line[i] == '\r')
8080- {
8181- line.RemoveAt(i);
8282- i++;
8383- }
8484-8585- i = line.Count - 1;
8686- while (line[i] == '\r')
8787- {
8888- line.RemoveAt(i);
8989- i--;
9090- }
9191- }
9292-9393- _buffer = listLines[^1].ToArray();
6161+ var listLines = _buffer.Split((byte) '\n').Select(l => l.Trim((byte) '\r')).ToList();
6262+ _buffer = listLines.Last();
94639564 var decodeLines = new List<Line>();
9665 foreach (var line in listLines.SkipLast(1).Select(l => l.ToArray()))