IRC parsing, tokenization, and state handling in C#
1namespace IRCTokens;
2
3public static class EnumerableExtensions
4{
5 public static IEnumerable<byte[]> Split(this byte[] bytes, byte separator)
6 {
7 if (bytes == null || bytes.Length == 0)
8 {
9 return [];
10 }
11
12 var newLineIndices = bytes.Select((b, i) => b == separator ? i : -1).Where(i => i != -1).ToArray();
13 var lines = new byte[newLineIndices.Length + 1][];
14 var currentIndex = 0;
15 var arrIndex = 0;
16
17 for (var i = 0; i < newLineIndices.Length && currentIndex < bytes.Length; ++i)
18 {
19 var n = new byte[newLineIndices[i] - currentIndex];
20 Array.Copy(bytes, currentIndex, n, 0, newLineIndices[i] - currentIndex);
21 currentIndex = newLineIndices[i] + 1;
22 lines[arrIndex++] = n;
23 }
24
25 // Handle the last string at the end of the array if there is one.
26 if (currentIndex < bytes.Length)
27 {
28 lines[arrIndex] = bytes.Skip(currentIndex).ToArray();
29 }
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 else if (arrIndex == newLineIndices.Length)
33 {
34 lines[arrIndex] = [];
35 }
36
37 return lines.ToArray();
38 }
39
40 public static byte[] Trim(this IEnumerable<byte> bytes, byte separator)
41 {
42 if (bytes == null)
43 {
44 return [];
45 }
46
47 var byteList = new List<byte>(bytes);
48 var i = 0;
49
50 if (!byteList.Any())
51 {
52 return byteList.ToArray();
53 }
54
55 while (byteList[i] == separator)
56 {
57 byteList.RemoveAt(i);
58 i++;
59 }
60
61 i = byteList.Count - 1;
62 while (byteList[i] == separator)
63 {
64 byteList.RemoveAt(i);
65 i--;
66 }
67
68 return byteList.ToArray();
69 }
70
71#if !(REFERENCE_ASSEMBLY && (NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER))
72 /// <summary>
73 /// Bypasses a specified number of contiguous elements from the end of the sequence and returns the remaining elements.
74 /// Backported from <a href="https://github.com/dotnet/reactive/blob/ebab5fc37e8c0888f7b96107852a8794e9af1735/Ix.NET/Source/System.Interactive/System/Linq/Operators/SkipLast.cs">netcore</a>
75 /// </summary>
76 /// <typeparam name="TSource">Source sequence element type.</typeparam>
77 /// <param name="source">Source sequence.</param>
78 /// <param name="count">
79 /// The number of elements to skip from the end of the sequence before returning the remaining elements.
80 /// </param>
81 /// <returns>Sequence bypassing the specified number of elements counting from the end of the source sequence.</returns>
82 public static IEnumerable<TSource> SkipLast<TSource>(this IEnumerable<TSource> source, int count)
83 {
84 if (source == null)
85 {
86 throw new ArgumentNullException(nameof(source));
87 }
88
89 if (count < 0)
90 {
91 throw new ArgumentOutOfRangeException(nameof(count));
92 }
93
94 return SkipLastCore(source, count);
95 }
96
97 private static IEnumerable<TSource> SkipLastCore<TSource>(this IEnumerable<TSource> source, int count)
98 {
99 var q = new Queue<TSource>();
100
101 foreach (var x in source)
102 {
103 q.Enqueue(x);
104
105 if (q.Count > count)
106 {
107 yield return q.Dequeue();
108 }
109 }
110 }
111#endif
112}