Buffer for characters

image_pdfimage_print
   
 
// 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
{
  /// <summary>
  /// Buffer for characters.  This approximates StringBuilder
  /// but is designed to be faster for specific operations.
  /// This is about 30% faster for the operations I&#039;m interested in
  /// (Append, Clear, Length, ToString).
  /// This trades off memory for speed.
  /// </summary>
  /// <remarks>
  /// <para>To make Remove from the head fast, this is implemented
  /// as a ring buffer.</para>
  /// <para>This uses head and tail indices into a fixed-size 
  /// array. This will grow the array as necessary.</para>
  /// </remarks>
  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

    /// <summary>
    /// Gets/Sets the number of characters in the character buffer.
    /// Increasing the length this way provides indeterminate results.
    /// </summary>
    public int Length
    {
      get { return(tailIndex - headIndex); }
      set 
      { 
        tailIndex = headIndex + value;
        if (tailIndex >= capacity) throw new 
          IndexOutOfRangeException("Tail index greater than capacity");
      }
    }

    /// <summary>
    /// Returns the capacity of this character buffer.
    /// </summary>
    public int Capacity
    {
      get { return(capacity); }
    }

    #endregion

    #region Constructors

    /// <summary>
    /// Default constructor.
    /// </summary>
    public CharBuffer()
    {
      buffer = new char[capacity];
    }

    /// <summary>
    /// Construct with a specific capacity.
    /// </summary>
    /// <param name="capacity"></param>
    public CharBuffer(int capacity)
    {
      this.capacity = capacity;
      buffer = new char[capacity];
    }

    #endregion

    #region Non-Public Methods

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="requestedLen">The new requested length.</param>
    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;
    }

    /// <summary>
    /// Ensure that we&#039;re set for the requested length by 
    /// potentially growing or shifting contents.
    /// </summary>
    /// <param name="requestedLength"></param>
    protected void CheckCapacity(int requestedLength)
    {
      if (requestedLength + headIndex >= capacity)
      {
        // have to do something
        if ((requestedLength + headIndex > (capacity >> 1))
          &amp;&amp; (requestedLength < capacity - 1))
        {
          // we&#039;re more than half-way through the buffer, and shifting is enough
          // so just shift
          ShiftToZero();
        }
        else
        {
          // not far into buffer or shift wouldn&#039;t be enough anyway
          Grow(0);
        }
      }
    }

    /// <summary>
    /// Move the buffer contents such that headIndex becomes 0.
    /// </summary>
    protected void ShiftToZero()
    {
      int len = Length;
      for (int i = 0; i < len; i++)
      {
        buffer&#91;i&#93; = buffer&#91;i + headIndex&#93;;
      }
      headIndex = 0;
      tailIndex = len;
    }

    #endregion

    #region Public Methods and Indexer

    /// <summary>
    /// Overwrite this object&#039;s underlying buffer with the specified
    /// buffer.
    /// </summary>
    /// <param name="b">The character array.</param>
    /// <param name="len">The number of characters to consider filled
    /// in the input buffer.</param>
    public void SetBuffer(char[] b, int len)
    {
      capacity = b.Length;
      buffer = b;
      headIndex = 0;
      tailIndex = len;
    }

    /// <summary>
    /// Append a character to this buffer.
    /// </summary>
    /// <param name="c"></param>
    public void Append(char c)
    {
      if (tailIndex >= capacity) CheckCapacity(Length + 1);
      buffer[tailIndex++] = c;
    }

    /// <summary>
    /// Append a string to this buffer.
    /// </summary>
    /// <param name="s">The string to append.</param>
    public void Append(string s)
    {
      if (s.Length + tailIndex >= capacity) CheckCapacity(Length + s.Length);
      for(int i = 0; i < s.Length; i++)
        buffer&#91;tailIndex++&#93; = s&#91;i&#93;;
    }

    /// <summary>
    /// Append a string to this buffer.
    /// </summary>
    /// <param name="s">The string to append.</param>
    public void Append(CharBuffer s)
    {
      if (s.Length + tailIndex >= capacity) CheckCapacity(Length + s.Length);
      for(int i = 0; i < s.Length; i++)
        buffer&#91;tailIndex++&#93; = s&#91;i&#93;;
    }

    /// <summary>
    /// Remove a character at the specified index.
    /// </summary>
    /// <param name="i">The index of the character to remove.</param>
    /// <returns></returns>
    public void Remove(int i)
    {
      Remove(i, 1);
    }

    /// <summary>
    /// Remove a specified number of characters at the specified index.
    /// </summary>
    /// <param name="i">The index of the characters to remove.</param>
    /// <param name="n">The number of characters to remove.</param>
    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));
      }
    }

    /// <summary>
    /// Find the first instance of a character in the buffer, and
    /// return its index.  This returns -1 if the character is
    /// not found.
    /// </summary>
    /// <param name="c">The character to find.</param>
    /// <returns>The index of the specified character, or -1
    /// for not found.</returns>
    public int IndexOf(char c)
    {
      for (int i = headIndex; i < tailIndex; i++)
      {
        if (buffer&#91;i&#93; == c) return(i - headIndex);
      }
      return(-1);
    }

    /// <summary>
    /// Empty the buffer.
    /// </summary>
    public void Clear()
    {
      headIndex = 0;
      tailIndex = 0;
    }

    /// <summary>
    /// Indexer.
    /// </summary>
    public char this [int index]
    {
      get { return(buffer[index + headIndex]); }
      set { buffer[index + headIndex] = value; }
    }

    /// <summary>
    /// Return the current contents as a string.
    /// </summary>
    /// <returns>The new string.</returns>
    public override String ToString()
    {
      return(new String(buffer, headIndex, tailIndex - headIndex));
    }

    #endregion    
  }
}

   
     


This entry was posted in Data Types. Bookmark the permalink.