IRC parsing, tokenization, and state handling in C#
at tunit 96 lines 3.4 kB view raw
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}