// CharBuffer.cs
//
// Copyright (C) 2003-2004 Ryan Seghers
//
// This software is provided AS IS. No warranty is granted,
// neither expressed nor implied. USE THIS SOFTWARE AT YOUR OWN RISK.
// NO REPRESENTATION OF MERCHANTABILITY or FITNESS FOR ANY
// PURPOSE is given.
//
// License to use this software is limited by the following terms:
// 1) This code may be used in any program, including programs developed
// for commercial purposes, provided that this notice is included verbatim.
//
// Also, in return for using this code, please attempt to make your fixes and
// updates available in some way, such as by sending your updates to the
// author.
using System;
namespace RTools_NTS.Util
{
///
/// Buffer for characters. This approximates StringBuilder
/// but is designed to be faster for specific operations.
/// This is about 30% faster for the operations I'm interested in
/// (Append, Clear, Length, ToString).
/// This trades off memory for speed.
///
///
/// To make Remove from the head fast, this is implemented
/// as a ring buffer.
/// This uses head and tail indices into a fixed-size
/// array. This will grow the array as necessary.
///
public class CharBuffer
{
#region Fields
int capacity = 128;
char[] buffer;
int headIndex; // index of first char
int tailIndex; // index 1 past last char
#endregion
#region Properties
///
/// Gets/Sets the number of characters in the character buffer.
/// Increasing the length this way provides indeterminate results.
///
public int Length
{
get { return(tailIndex - headIndex); }
set
{
tailIndex = headIndex + value;
if (tailIndex >= capacity) throw new
IndexOutOfRangeException("Tail index greater than capacity");
}
}
///
/// Returns the capacity of this character buffer.
///
public int Capacity
{
get { return(capacity); }
}
#endregion
#region Constructors
///
/// Default constructor.
///
public CharBuffer()
{
buffer = new char[capacity];
}
///
/// Construct with a specific capacity.
///
///
public CharBuffer(int capacity)
{
this.capacity = capacity;
buffer = new char[capacity];
}
#endregion
#region Non-Public Methods
///
/// Reallocate the buffer to be larger. For the new size, this
/// uses the max of the requested length and double the current
/// capacity.
/// This does not shift, meaning it does not change the head or
/// tail indices.
///
/// The new requested length.
protected void Grow(int requestedLen)
{
int newLen = Math.Max(capacity*2, requestedLen);
newLen = Math.Max(newLen, 16);
char[] newBuffer = new char[newLen];
Array.Copy(buffer, 0, newBuffer, 0, capacity);
buffer = newBuffer;
capacity = newLen;
}
///
/// Ensure that we're set for the requested length by
/// potentially growing or shifting contents.
///
///
protected void CheckCapacity(int requestedLength)
{
if (requestedLength + headIndex >= capacity)
{
// have to do something
if ((requestedLength + headIndex > (capacity >> 1))
&& (requestedLength < capacity - 1))
{
// we're more than half-way through the buffer, and shifting is enough
// so just shift
ShiftToZero();
}
else
{
// not far into buffer or shift wouldn't be enough anyway
Grow(0);
}
}
}
///
/// Move the buffer contents such that headIndex becomes 0.
///
protected void ShiftToZero()
{
int len = Length;
for (int i = 0; i < len; i++)
{
buffer[i] = buffer[i + headIndex];
}
headIndex = 0;
tailIndex = len;
}
#endregion
#region Public Methods and Indexer
///
/// Overwrite this object's underlying buffer with the specified
/// buffer.
///
/// The character array.
/// The number of characters to consider filled
/// in the input buffer.
public void SetBuffer(char[] b, int len)
{
capacity = b.Length;
buffer = b;
headIndex = 0;
tailIndex = len;
}
///
/// Append a character to this buffer.
///
///
public void Append(char c)
{
if (tailIndex >= capacity) CheckCapacity(Length + 1);
buffer[tailIndex++] = c;
}
///
/// Append a string to this buffer.
///
/// The string to append.
public void Append(string s)
{
if (s.Length + tailIndex >= capacity) CheckCapacity(Length + s.Length);
for(int i = 0; i < s.Length; i++)
buffer[tailIndex++] = s[i];
}
///
/// Append a string to this buffer.
///
/// The string to append.
public void Append(CharBuffer s)
{
if (s.Length + tailIndex >= capacity) CheckCapacity(Length + s.Length);
for(int i = 0; i < s.Length; i++)
buffer[tailIndex++] = s[i];
}
///
/// Remove a character at the specified index.
///
/// The index of the character to remove.
///
public void Remove(int i)
{
Remove(i, 1);
}
///
/// Remove a specified number of characters at the specified index.
///
/// The index of the characters to remove.
/// The number of characters to remove.
public void Remove(int i, int n)
{
n = Math.Min(n, Length);
if (i == 0)
{
headIndex += n;
}
else
{
Array.Copy(buffer, i + headIndex + n, buffer, i + headIndex,
tailIndex - (i + headIndex + n));
}
}
///
/// Find the first instance of a character in the buffer, and
/// return its index. This returns -1 if the character is
/// not found.
///
/// The character to find.
/// The index of the specified character, or -1
/// for not found.
public int IndexOf(char c)
{
for (int i = headIndex; i < tailIndex; i++)
{
if (buffer[i] == c) return(i - headIndex);
}
return(-1);
}
///
/// Empty the buffer.
///
public void Clear()
{
headIndex = 0;
tailIndex = 0;
}
///
/// Indexer.
///
public char this [int index]
{
get { return(buffer[index + headIndex]); }
set { buffer[index + headIndex] = value; }
}
///
/// Return the current contents as a string.
///
/// The new string.
public override String ToString()
{
return(new String(buffer, headIndex, tailIndex - headIndex));
}
#endregion
}
}