To create a circular buffer for text, I'd use a StringBuilder
, with the capacity set to about twice the amount of data I want to display.
const int DisplaySize = 10000;
StringBuilder fifo = new StringBuilder(2 * DisplaySize);
string AppendToFifo( string s )
{
if (s.Length >= DisplaySize) {
// FACT: the display will only include data from s
// therefore, toss the entire buffer, and only keep the tail of s
fifo.Clear();
fifo.Append(s, s.Length - DisplaySize, DisplaySize);
return fifo.ToString();
}
if (fifo.Length + s.Length > fifo.Capacity) {
// FACT: we will overflow the fifo
// therefore, keep only data in the fifo that remains on the display
fifo.Remove(0, fifo.Length + s.Length - DisplaySize);
}
fifo.Append(s);
if (fifo.Length <= DisplaySize) {
// FACT: the entire fifo content fits on the display
// therefore, send it all
return fifo.ToString();
}
// FACT: the fifo content exceed the display size
// therefore, extract just the tail
return fifo.ToString(fifo.Length - DisplaySize, DisplaySize);
}
The fast path, when none of the if conditions are true, avoids all unnecessary copies (in the .NET world where strings are immutable, the final copy to create the output string cannot be avoided). And in the other cases only needed characters are copied. Increasing the capacity of the buffer will improve utilization fraction of the fast path. What I've been careful to avoid doing is creating a string object with the old content that remains on the display, which has no purpose except to concatenate with the new content, and immediately becomes garbage.
Obviously, if you use p/invoke to pass a pointer to the StringBuffer's content instead of copying out a substring, that will be even more efficient.