// Extendible reusable string buffer -*- c++ -*-

#ifdef __GNUC__
# pragma implementation
#endif // __GNUC__
#include "StringBuffer.h"
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include <assert.h>

/** @file StringBuffer.C
 * Extendible reusable string buffer
 */

/* Copyright © 1999-2002 Marko Mäkelä (msmakela@tcs.hut.fi).

   This file is part of MARIA, a reachability analyzer and model checker
   for high-level Petri nets.

   MARIA is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   MARIA is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   The GNU General Public License is often shipped with GNU software, and
   is generally kept in a file called COPYING or LICENSE.  If you do not
   have a copy of the license, write to the Free Software Foundation,
   59 Temple Place, Suite 330, Boston, MA 02111 USA. */

StringBuffer::StringBuffer () :
  myString (new char[1]), myLength (0), myMaxLength (0)
{
  *myString = 0;
}

StringBuffer::StringBuffer (const class StringBuffer& old) :
  myString (new char[old.myLength + 1]),
  myLength (old.myLength), myMaxLength (old.myLength)
{
  memcpy (myString, old.myString, myLength + 1);
}

class StringBuffer&
StringBuffer::operator= (const class StringBuffer& old)
{
  memcpy (create (old.myLength), old.myString, old.myLength + 1);
  return *this;
}

StringBuffer::~StringBuffer ()
{
  delete[] myString;
}

char*
StringBuffer::copy () const
{
  char* buf = new char[myLength + 1];
  memcpy (buf, myString, myLength + 1);
  return buf;
}

class StringBuffer&
StringBuffer::operator= (const char* string)
{
  size_t length = strlen (string);
  memcpy (create (length), string, length + 1);
  return *this;
}

char*
StringBuffer::create (unsigned length)
{
  if (length > myMaxLength) {
    delete[] myString;
    myString = new char[length + 1];
    myMaxLength = length;
  }

  myLength = length;
  *myString = 0;

  return myString;
}

char*
StringBuffer::extend (unsigned length)
{
  if (myLength + length > myMaxLength) {
    char*s = new char[myLength + length + 1];
    // The buffer might contain embedded NULL chars,
    // therefore memcpy () instead of strcpy ().
    memcpy (s, myString, myLength + 1);
    delete[] myString;
    myString = s;
    myMaxLength = myLength += length;
  }
  else
    myLength += length;

  myString[myLength] = 0;
  return myString;
}

void
StringBuffer::append (const char* string)
{
  const size_t s = strlen (string), len = myLength;
  memcpy (extend (s) + len, string, s + 1);
}

void
StringBuffer::append (unsigned num)
{
  unsigned length = myLength;
  if (num > INT_MAX)
    length += sprintf (extend (22) + length, "%uu", num);
  else
    length += sprintf (extend (21) + length, "%u", num);
  assert (myLength >= length);
  myLength = length;
}

void
StringBuffer::append (signed num)
{
  unsigned length = myLength;
  length += sprintf (extend (22) + length, "%d", num);
  assert (myLength >= length);
  myLength = length;
}

void
StringBuffer::append (const class StringBuffer& buf)
{
  unsigned l = getLength (), r = buf.getLength ();
  memcpy (extend (r) + l, buf.getString (), r);
}

void
StringBuffer::append (char c, unsigned num)
{
  const size_t len = myLength;
  memset (extend (num) + len, c, num);
}

void
StringBuffer::escape (unsigned offset)
{
  size_t numEscapes = 0, i;
  for (i = offset; i < myLength; i++) {
    register char c = myString[i];
    if ((c < '0' || c > '9') &&
	(c < 'A' || c > 'Z') &&
	(c < 'a' || c > 'z'))
      numEscapes++;
  }
  if (!numEscapes)
    return;
  extend (numEscapes *= 2);
  memmove (myString + offset + numEscapes, myString + offset,
	   myLength - offset - numEscapes);
  for (i = offset; i < myLength; i++) {
    register char c = myString[i + numEscapes];
    if ((c < '0' || c > '9') &&
	(c < 'A' || c > 'Z') &&
	(c < 'a' || c > 'z')) {
      myString[i] = '_';
      char d = c >> 4 & 0xf;
      myString[++i] = d > 9 ? d + ('a' - 10) : d + '0';
      d = c & 0xf;
      myString[++i] = d > 9 ? d + ('a' - 10) : d + '0';
      numEscapes -= 2;
    }
    else
      myString[i] = c;
  }
}
