// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: ustring.cpp 
// Compiler Used: MSVC40, DJGPP 2.7.2.1, GCC 2.7.2.1, HP CPP 10.24
// Produced By: Doug Gaer 
// File Creation Date: 11/29/1996  
// Date Last Modified: 10/23/1998
// Copyright (c) 1997 Douglas M. Gaer
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
The VBD C++ classes are copyright (c) 1997, by Douglas M. Gaer.
All those who put this code or its derivatives in a commercial
product MUST mention this copyright in their documentation for
users of the products in which this code or its derivative
classes are used. Otherwise, you have the freedom to redistribute
verbatim copies of this source code, adapt it to your specific
needs, or improve the code and release your improvements to the
public provided that the modified files carry prominent notices
stating that you changed the files and the date of any change.

THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
CORRECTION.

The UString class is a user-defined string class used to create
and manipulate non null-terminated resizable, variable-length
strings of binary data. UString objects are implemented as a
concrete data type, just like the built-in data types: char,
int, long, float, and double. The UString class supports string
concatenation, find, fill, and sub-string creation.
*/
// ----------------------------------------------------------- // 
#include "ustring.hpp"

StrData StrData::Null_Ptr;

void *StrData::operator new(size_t StrSize, unsigned Bytes)
// Overloaded new operator used to allocate memory for the size
// of a StrData object plus number of bytes that represent the 
// length of the string's data. The memory for the string is
// allocated as an array of characters. It is the responsibility
// of the UString class to keep track of the string's length.
// The UString class must store the string data immediately following
// the reference count. A null pointer object will be statically 
// allocated when a new StrData object is constructed. Null strings
// will always point to the null pointer object.
{
  // StrSize already accounts for one character
  void *ptr = new char[StrSize + Bytes - 1];
  if(!ptr) { // If allocation fails reference Null_Ptr
    ptr = &Null_Ptr;
    ((StrData *)ptr)->RefCount++;
  }
  return ptr;
}

void StrData::operator delete(void *ptr)
// The delete operator is overloaded so that
// both the string data the reference count
// are deleted.
{
  delete[] (char *)ptr;
}

int UString::Alloc(unsigned Bytes, int GB)
{
  DataPtr = new(Bytes) StrData;
  TextPtr = DataPtr->Data;
  Len = 0;
  if (IsNull()) { // Could not allocate memory for string
    DimLen = 1;
    GrowBy = 0;
    return 0;
  }
  else {
    DimLen = Bytes;
    GrowBy = GB;
    return 1;
  }
}
      
void UString::Bind(const UString &s)
{
  // Bind DataPtr to the same that s is bound to
  DataPtr = s.DataPtr;
  DataPtr->RefCount++;
}

void UString::UnBind()
{
  DataPtr->RefCount--;

  // Make sure RefCount is a non-zero value
  if(DataPtr == &StrData::Null_Ptr && DataPtr->RefCount == 0)
    DataPtr->RefCount = 1;

  if(DataPtr->RefCount == 0) delete DataPtr;
}

void UString::NewBinding(const UString &s)
{
  // Unbind string from DataPtr that holds it, and
  // bind it to the DataPtr shared by s
  if(DataPtr != s.DataPtr) {
    UnBind();
    Bind(s);
  }
}

UString::UString(const char *s, unsigned Bytes, int GB)
{
  if(Alloc(Bytes, GB)) CopyN(s, Bytes);
}

UString::UString(const char *s, int GB)
{
  unsigned StringLen = strlen(s);
  unsigned DimensionLen = StringLen;
  if(GB) { // Compute next highest multiple of GB
    unsigned AddValue = (DimensionLen % GB) ? GB : 0;
    DimensionLen = (DimensionLen / GB) * GB + AddValue;
  }
  if(Alloc(DimensionLen, GB)) CopyN(s, StringLen);
}

UString::UString(const UString &s, unsigned Offset, unsigned Bytes, int GB)
// A constructor used to create a subset of this string.
{
  Bind(s);

  // Keep offset and length in range
  if(Offset > s.Len-1) Offset = s.Len - 1;
  if(Bytes + Offset > s.Len) Bytes = s.Len - Offset;

  // Set max and logical bounds of the substring
  DimLen = Bytes;
  Len = Bytes;
  GrowBy = GB;
  TextPtr = s.TextPtr + Offset; // Compute starting text element
}

UString::UString(const UString &s)
{
  Bind(s);
  Len = s.Len;
  DimLen = s.DimLen;
  GrowBy = s.GrowBy;
  TextPtr = s.TextPtr;
}

UString::~UString()
{
  UnBind();
}

UString &UString::operator=(const UString &s)
{
  if(this != &s) {
    NewBinding(s);
    Len = s.Len;
    DimLen = s.DimLen;
    GrowBy = s.GrowBy;
    TextPtr = s.TextPtr;
  }
  return *this;
}

unsigned UString::InsReplAt(unsigned Pos,
			   const char *s, unsigned Bytes, int Ins)
{
  unsigned Room, Needed, AddVal;
  
  if(Ins) {
    Room = DimLen - Len;
    if(Bytes > Room && GrowBy != 0) {
      Needed = (Bytes - Room);
      AddVal = (Needed % GrowBy) ? GrowBy : 0;
      Needed = (Needed / GrowBy) * GrowBy + AddVal;
      Grow_By(Needed);
    }
  }

  if (!EnsureUnique()) return 0; // Can't update
  if (Pos >= Len) Pos = Len; // Don't allow gaps

  if(Ins) { // Keep things in range. May have to truncate
    if(Bytes > DimLen - Len) Bytes = DimLen - Len;
    if(Pos < Len) {
      // Make room in the middle for inserted data
      memmove(TextPtr+Pos+Bytes, TextPtr+Pos, Len-Pos);
    }
  }
  else {
    if(Bytes > DimLen - Pos) Bytes = DimLen -Pos;
  }

  // Copy in source, compute final length
  memmove(TextPtr+Pos, s, Bytes);
  if(Ins) {
    Len += Bytes;
  }
  else {
    if((Pos+Bytes) > Len) Len = Pos+Bytes;
  }
  return Bytes;
}

int UString::Realloc(unsigned NewDimLen, int Keep)
{
  if(GrowBy == 0) return 0;

  StrData *NewStrData = new(NewDimLen) StrData;
  if(NewStrData == &StrData::Null_Ptr) return 0;

  if(Keep) { // Copy old data into new space.
    if(NewDimLen < Len) Len = NewDimLen;
    memmove(NewStrData->Data, TextPtr, Len);
  }
  else { // Do not keep old data
    Len = 0;
  }

  UnBind();                // Unbind from old data
  DataPtr = NewStrData;    // Bind to new Data
  TextPtr = DataPtr->Data; // Point to starting text
  DimLen = NewDimLen;      // Record new allocated length
  return 1;
}

int UString::EnsureUnique()
{
  if(!IsUnique()) {
    // Create a unique clone of this string
    UString buf(DimLen, GrowBy);
    buf.CopyN(TextPtr, Len);
    if(buf.IsNull()) return 0; // Could not copy
    NewBinding(buf);           // Bind to copy
    TextPtr = buf.TextPtr;     // Initialize starting text element
  }
  return 1;
}

void UString::SetLength(unsigned Bytes)
{
  if(Bytes > DimLen) Bytes = DimLen;
  Len = Bytes;
}

void UString::CopyN(const char *s, unsigned Bytes)
{
  Len = 0; InsReplAt(0, s, Bytes);
}

void UString::Copy(const char *s)
{
  CopyN(s, strlen(s));
}

void UString::Copy(const UString &s)
{
  CopyN(s.TextPtr, s.Len);
}

unsigned UString::Find(char *s, unsigned Offset)
// Returns index of first occurrence of pattern char *s   
// Returns 0xffff if pattern not found
{
  char *Start = TextPtr + Offset; // Start of string data
  char *Next = Start;             // Next string element
  char *Pattern = s;              // Next pattern element
  unsigned i = Offset;            // Next string element index
  
  while(i < Len && *Pattern) {
    if (*Next == *Pattern) {
      Pattern++;
      if(*Pattern == 0) return i; // Pattern was found
      Next++;
    }
    else {
      i++;
      Start++;
      Next = Start;
      Pattern = s;
    }
  }
  return NoMatch; // No match was found
}

unsigned UString::Find(char *s, unsigned Bytes, unsigned Offset) const
// Returns index of first occurrence of pattern char *s   
// Returns 0xffff if pattern not found
{
  char *Start = TextPtr + Offset; // Start of string data
  char *Next = Start;             // Next string element
  char *Pattern = s;              // Next pattern element
  unsigned i = Offset, j = 1;     // String and pattern indexes
  
  while(i < Len && j <= Bytes) {
    if (*Next == *Pattern) { // Matching character
      if(j == Bytes) return i; // Entire match was found
      Next++; Pattern++; j++;
    }
    else { // Try next spot in string
      i++;
      Start++;
      Next = Start;
      Pattern = s; j = 1;
    }
  }
  return NoMatch; // No match was found
}

unsigned UString::DeleteAt(unsigned Pos, unsigned Bytes)
{
  unsigned long buf; // long is used to prevent overflows
  unsigned End;

  if(Pos < Len && Bytes !=0) {
    if(!EnsureUnique()) return 0; // Can't update
    buf = Pos + Bytes;
    if(buf > Len) buf = Len;
    End = unsigned(buf);
    Bytes = End - Pos;
    // Move the elements up to take their place
    memmove(TextPtr+Pos, TextPtr+End, Len-End);
    Len -= Bytes;
  }
  else
    Bytes = 0;

  return Bytes;
}

void UString::Fill(const char *p, unsigned Bytes, unsigned Offset, unsigned Ln)
{
  if (DimLen == 0 || Bytes == 0) return; // Nothing to do
  if (!EnsureUnique()) return;           // Can't fill
  // Keep parms in range
  if (Ln == 0) Ln = DimLen;
  if (Offset >= DimLen) Offset = DimLen-1; // DimLen must be > 0!
  if (Ln + Offset > DimLen) Ln = DimLen - Offset;
  Len = Ln + Offset;
  char *t = TextPtr + Offset;
  if (Bytes == 1) {
     // Use fast method for single character fills
     memset(t, *p, Ln);
  }
  else {
    // Multi-character pattern fills
    unsigned np = Bytes;
    const char *s = p;
    while(Ln) {
      *t++ = *s++;
      if (np == 1) {
         np = Bytes;
         s = p;
      } else np--;
      Ln--;
    }
  }
}

UString UString::Mid(unsigned Index, unsigned Bytes) const 
// Returns substring starting at Index, for number of bytes
{
  // Call the substring constructor 
  UString buf(*this, Index, Bytes);

  return buf;
}

UString UString::Left(unsigned Ln) const
// Returns left substring of length Ln
{
  // Call the substring constructor 
  UString buf(*this, 0, Ln);

  return buf;
}

UString UString::Right(unsigned Ln) const
// Returns right substring of length Ln
{
  if(Ln > Len) Ln = Len;

  // Call the substring constructor 
  UString buf(*this, Len - Ln, Ln);
  
  return buf;
}

const char *UString::NullTermStr()
// If a null byte has to be added, the string is grown.
// If the string can't grow, the last character is replaced.
{
  static char Null_String[1] = {0};
  if(IsNull()) return Null_String;
  char *ptr = TextPtr;
  unsigned Bytes = Len;

  // Search for null byte already in string
  while(Bytes--) if(*ptr++ == 0) return TextPtr;

  // Grow if length == allocated length grow the string
  if(Len == DimLen) Grow();

  // Make sure string can grow and is unique
  if(DimLen == 0 || !EnsureUnique()) return Null_String;
  if(Len == DimLen) Len--; // Forced to replace good character
  TextPtr[Len] = 0;
  return TextPtr;
}

char *UString::c_str() 
// If a null byte has to be added, the string is grown.
// If the string can't grow, the last character is replaced.
{
  static char Null_String[1] = {0};
  if(IsNull()) return Null_String;
  char *ptr = TextPtr;
  unsigned Bytes = Len;

  // Search for null byte already in string
  while(Bytes--) if(*ptr++ == 0) return TextPtr;

  // Grow if length == allocated length grow the string
  if(Len == DimLen) Grow();

  // Make sure string can grow and is unique
  if(DimLen == 0 || !EnsureUnique()) return Null_String;
  if(Len == DimLen) Len--; // Forced to replace good character
  TextPtr[Len] = 0;
  return TextPtr;
}

const char *UString::c_str() const 
// If a null byte has to be added the last character is replaced.
{
  static char Null_String[1] = {0};
  if(IsNull()) return Null_String;
  char *ptr = TextPtr;
  unsigned Bytes = Len;

  // Search for null byte already in string
  while(Bytes--) if(*ptr++ == 0) return TextPtr;

  TextPtr[Len] = 0;
  return TextPtr;
}

ostream &operator<<(ostream &os, const UString &s)
{
  return os.write(s.TextPtr, s.Len);
}

istream &operator>>(istream &os, UString &s)
{
  os >> setw(s.DimLen) >> s.TextPtr;
  s.SetLength(strlen(s.TextPtr));
  return os;
}

int StringCompare(const UString &a, const UString &b)
// Returns -1 if a < b, 0 if a == b, and 1 if a > b
{
  unsigned an = a.Len;
  unsigned bn = b.Len;
  unsigned sn = (an < bn) ? an : bn;
  unsigned char *ap = (unsigned char *)a.TextPtr;
  unsigned char *bp = (unsigned char *)b.TextPtr;

  for(unsigned i = 0; i < sn; i++) {
    if(*ap < *bp) return -1;
    if(*ap++ > *bp++) return 1;
  }

  if(an == bn) return 0;
  if(an < bn) return -1;
  return 1;
}

// General-purpose string routines that need to be ported from UNIX to DOS
int CaseICmp(const UString &s1, const UString &s2)
// Compare two UString objects without regard to the case of the letters
{
#ifdef __DOS__
  return stricmp(s1.c_str(), s2.c_str());
#endif

#ifdef __UNIX__
  return strcasecmp(s1.c_str(), s2.c_str());
#endif
}

int CaseICmp(UString &s1, UString &s2)
// Compare two UString objects without regard to the case of the letters
{
#ifdef __DOS__
  return stricmp(s1.c_str(), s2.c_str());
#endif

#ifdef __UNIX__
  return strcasecmp(s1.c_str(), s2.c_str());
#endif
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //
