code: StringBuffer getString(StringBuffer sb, int startChunk, int startColumn,
                           int length)
    {
  
      int stop = (startChunk << m_chunkBits) + startColumn + length;
      int stopChunk = stop >>> m_chunkBits;
      int stopColumn = stop & m_chunkMask;
  
      // Factored out
      //StringBuffer sb=new StringBuffer(length);
      for (int i = startChunk; i < stopChunk; ++i)
      {
        if (i == 0 && m_innerFSB != null)
          m_innerFSB.getString(sb, startColumn, m_chunkSize - startColumn);
        else
          sb.append(m_array[i], startColumn, m_chunkSize - startColumn);
  
        startColumn = 0;  // after first chunk
      }
  
      if (stopChunk == 0 && m_innerFSB != null)
        m_innerFSB.getString(sb, startColumn, stopColumn - startColumn);
      else if (stopColumn > startColumn)
        sb.append(m_array[stopChunk], startColumn, stopColumn - startColumn);
  
      return sb;
    }
docs: Internal support for toString() and getString().
  PLEASE NOTE SIGNATURE CHANGE from earlier versions; it now appends into
  and returns a StringBuffer supplied by the caller. This simplifies
  m_innerFSB support.
  <p>
  Note that this operation has been somewhat deoptimized by the shift to a
  chunked array, as there is no factory method to produce a String object
  directly from an array of arrays and hence a double copy is needed.
  By presetting length we hope to minimize the heap overhead of building
  the intermediate StringBuffer.
  <p>
  (It really is a pity that Java didn't design String as a final subclass
  of MutableString, rather than having StringBuffer be a separate hierarchy.
  We'd avoid a <strong>lot</strong> of double-buffering.)
  
  
  @param sb
  @param startChunk
  @param startColumn
  @param length
  
  @return the contents of the FastStringBuffer as a standard Java string.