… is that the right name?
It basically works like a combination of the StringBuilder and Queue objects. You enqueue arrays of T, and you dequeue custom sized chunks of the array. What makes it nice is that I’ve optimized memory usage so it never creates any temp arrays internally except the one returned to you when you eat (of course).
Added some comments in case you need to change anything.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
/// <summary> /// Works like a Queue object except it enqueues arrays and lets you dequeue custom sized chunks of the arrays enqueued /// </summary> /// <typeparam name="T">Array type</typeparam> public class ArrayBuilderQueue<T> { private List<Array> buffer = new List<Array>(); private int currentArrayPos = 0; public int Length { get; private set; } /// <summary> /// Adds an array to the queue /// </summary> /// <param name="items">Array of items to add</param> public void Enqueue(T[] items) { lock (buffer) { if (items == null) return; buffer.Add(items); Length += items.Length; } } public void Clear() { lock (buffer) { buffer.Clear(); Length = 0; currentArrayPos = 0; } } /// <summary> /// Dequeues items from buffer. /// </summary> /// <param name="count">Number of items</param> /// <returns>Array of items</returns> public T[] Dequeue(int count) { lock (buffer) { count = Math.Min(count, Length); T[] ret = new T[count]; int eaten = 0; int retPos = 0; while (eaten < count) { // Check how much remains of first array int bl = 0; if (buffer.Count > 0) bl = buffer[0].Length; int remaining = bl - currentArrayPos; // Nothing? while (remaining == 0 && buffer.Count > 0) { // Remove it and recalculate buffer.RemoveAt(0); currentArrayPos = 0; if (buffer.Count > 0) { // Still got some, try next if (buffer[0] != null) remaining = buffer[0].Length - currentArrayPos; } } // Copy data to return var int eatSize = Math.Min(remaining, count - eaten); if (eatSize > 0) Array.Copy(buffer[0], currentArrayPos, ret, retPos, eatSize); // Update internal tracking retPos += eatSize; // Where we are in buffer we are copying to currentArrayPos += eatSize; // Where we are in current array after eating eaten += eatSize; // How much we have eaten (main loop) Length -= eatSize; // Length was just decreased } return ret; } } } |
And the unit test if you want (doesn’t cover all scenarios):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[TestMethod] public void BufferBuilder() { Common.Utils.ArrayBuilderQueue<byte> bufferBuilder = new ArrayBuilderQueue<byte>(); bufferBuilder.Enqueue(System.Text.Encoding.ASCII.GetBytes("This is a test\r\n")); bufferBuilder.Enqueue(System.Text.Encoding.ASCII.GetBytes("Of the ")); bufferBuilder.Enqueue(System.Text.Encoding.ASCII.GetBytes("BufferBuilder class.\r\n")); bufferBuilder.Enqueue(System.Text.Encoding.ASCII.GetBytes("Have fun!\r\n")); string str = null; str = System.Text.Encoding.ASCII.GetString(bufferBuilder.Dequeue(8)); Assert.IsTrue(str == "This is "); str = System.Text.Encoding.ASCII.GetString(bufferBuilder.Dequeue(8)); Assert.IsTrue(str == "a test\r\n"); str = System.Text.Encoding.ASCII.GetString(bufferBuilder.Dequeue(7)); Assert.IsTrue(str == "Of the "); str = System.Text.Encoding.ASCII.GetString(bufferBuilder.Dequeue(20)); Assert.IsTrue(str == "BufferBuilder class."); str = System.Text.Encoding.ASCII.GetString(bufferBuilder.Dequeue(13)); Assert.IsTrue(str == "\r\nHave fun!\r\n"); bufferBuilder.Clear(); bufferBuilder.Enqueue(System.Text.Encoding.ASCII.GetBytes("1234567890")); str = System.Text.Encoding.ASCII.GetString(bufferBuilder.Dequeue(10)); Assert.IsTrue(str == "1234567890"); } |