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