Skip to content

Commit

Permalink
Reduce allocations in ReadNullTerminatedLines
Browse files Browse the repository at this point in the history
Eliminate use of temporary strings.
Only the buffer and a single StringBuilder is allocated.
  • Loading branch information
drewnoakes committed May 27, 2018
1 parent 0572f9f commit fb8358d
Showing 1 changed file with 32 additions and 24 deletions.
56 changes: 32 additions & 24 deletions GitCommands/StreamReaderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using JetBrains.Annotations;
Expand All @@ -12,43 +13,50 @@ internal static class StreamReaderExtensions
public static IEnumerable<string> ReadNullTerminatedLines([NotNull] this StreamReader reader, int bufferSize = 4096)
{
var buffer = new char[bufferSize];
var incompleteBlock = new StringBuilder();
var accumulator = new StringBuilder();

while (true)
{
int bytesRead = reader.ReadBlock(buffer, 0, bufferSize);
var charsRead = reader.ReadBlock(buffer, 0, bufferSize);

if (bytesRead == 0)
if (charsRead == 0)
{
break;
}

string bufferString = new string(buffer, 0, bytesRead);
string[] dataBlocks = bufferString.Split('\0');
var fromIndex = 0;

if (dataBlocks.Length > 1)
while (fromIndex < charsRead)
{
// There are at least two blocks, so we can return the first one
incompleteBlock.Append(dataBlocks[0]);
yield return incompleteBlock.ToString();
incompleteBlock.Clear();
var nullIndex = Array.IndexOf(buffer, '\0', fromIndex, charsRead - fromIndex);

if (nullIndex == -1)
{
var count = charsRead - fromIndex;
accumulator.Append(buffer, fromIndex, count);
break;
}

if (accumulator.Length == 0)
{
var count = nullIndex - fromIndex;
yield return count == 0 ? "" : new string(buffer, fromIndex, count);
}
else
{
var count = nullIndex - fromIndex;
accumulator.Append(buffer, fromIndex, count);
yield return accumulator.ToString();
accumulator.Clear();
}

fromIndex = nullIndex + 1;
}

int lastDataBlockIndex = dataBlocks.Length - 1;

// Return all the blocks until the last one
for (int i = 1; i < lastDataBlockIndex; i++)
{
yield return dataBlocks[i];
}

// Append the beginning of the last block
incompleteBlock.Append(dataBlocks[lastDataBlockIndex]);
}

if (incompleteBlock.Length > 0)
if (accumulator.Length != 0)
{
yield return incompleteBlock.ToString();
yield return accumulator.ToString();
}
}
}
Expand Down

0 comments on commit fb8358d

Please sign in to comment.