<commit_before>//---------------------------------------------------------------------------
#include "GStr.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "GBase.h"
#include <stdarg.h>
#include <errno.h>

//---------------------------------------------------------------------------

GStr::Data GStr::null_data;

//=========================================

GStr::Data * GStr::new_data(int length) {
//static method to return a new Data object (allocate length)
//content is undefined, but it's null terminated
    if (length > 0) {
        Data* data;
        GMALLOC(data, sizeof(Data)+length);
        data->ref_count = 0;
        data->length = length;
        data->chars[length] = '\0';
        return data;
        }
    else
        return &null_data;
 }

GStr::Data* GStr::new_data(const char* str) {
//static method to return a new Data object (allocate length)
//as a copy of a given string
 if (str==NULL) return &null_data;
 int length=strlen(str);
 if (length > 0) {
        Data* data;
        GMALLOC(data, sizeof(Data)+length);
        strcpy(data->chars, str);
        data->ref_count = 0;
        data->length = length;
        data->chars[length] = '\0';
        return data;
        }
    else
        return &null_data;
 }
 
void GStr::replace_data(int len) {

    if (len == my_data->length && my_data->ref_count <= 1)
        return;

    if (my_data != &null_data && --my_data->ref_count == 0)
        GFREE(my_data);

    if (len > 0) {
        //my_data = (Data *) malloc(sizeof(Data) + len);
        GMALLOC(my_data, sizeof(Data) + len);
        my_data->ref_count = 1;
        my_data->length = len;
        my_data->chars[len] = '\0';
    }
    else
        my_data = &null_data;
}

void GStr::replace_data(Data *data) {
    if (my_data != &null_data && --my_data->ref_count == 0)
        GFREE(my_data);
    if (data != &null_data)
        data->ref_count++;
    my_data = data;
}

void GStr::make_unique() {//make sure it's not a reference to other string
    if (my_data->ref_count > 1) {
        Data *data = new_data(length());
        ::memcpy(data->chars, chars(), length());
        my_data->ref_count--;
        my_data = data;
        my_data->ref_count++;
    }
}

bool operator==(const char *s1, const GStr& s2){
  if (s1==NULL) return s2.is_empty();
  return (strcmp(s1, s2.chars()) == 0);
  }

bool operator<(const char *s1, const GStr& s2) {
  if (s1==NULL) return !s2.is_empty();
  return (strcmp(s1, s2.chars()) < 0);
  }

bool operator<=(const char *s1, const GStr& s2){
 if (s1==NULL) return true;
 return (strcmp(s1, s2.chars()) <= 0);
 }

bool operator>(const char *s1, const GStr& s2) {
  if (s1==NULL) return false;
 return (strcmp(s1, s2.chars()) > 0);
 }


GStr::GStr():my_data(&null_data) {
 fTokenDelimiter=NULL;
 fTokenizeMode=tkCharSet;
 fLastTokenStart=0;
 readbuf=NULL;
 readbufsize=0;
 }

GStr::GStr(const GStr& s): my_data(&null_data){
 fTokenDelimiter=NULL;
 fTokenizeMode=tkCharSet;
 fLastTokenStart=0;
 readbuf=NULL;
 readbufsize=0;
 replace_data(s.my_data);
 }

GStr::GStr(const char *s): my_data(&null_data) {
  fTokenDelimiter=NULL;
  fTokenizeMode=tkCharSet;
  fLastTokenStart=0;
  readbuf=NULL;
  readbufsize=0;
  my_data=new_data(s);
  my_data->ref_count = 1;
 }

GStr::GStr(const int i): my_data(&null_data) {
 fTokenDelimiter=NULL;
 fTokenizeMode=tkCharSet;
 fLastTokenStart=0;
 readbuf=NULL;
 readbufsize=0;
 char buf[20];
 sprintf(buf,"%d",i);
 const int len = ::strlen(buf);
 replace_data(len);
 ::memcpy(chrs(), buf, len);
 }

GStr::GStr(const double f): my_data(&null_data) {
 fTokenDelimiter=NULL;
 fTokenizeMode=tkCharSet;
 fLastTokenStart=0;
 readbuf=NULL;
 readbufsize=0;
 char buf[20];
 sprintf(buf,"%f",f);
 const int len = ::strlen(buf);
 replace_data(len);
 ::memcpy(chrs(), buf, len);
 }

GStr::GStr(char c, int n): my_data(&null_data) {
  fTokenDelimiter=NULL;
  fTokenizeMode=tkCharSet;
  fLastTokenStart=0;
  readbuf=NULL;
  readbufsize=0;
  replace_data(n); ::memset(chrs(), c, n);
  }

GStr::~GStr() {  
  if (my_data != &null_data && --my_data->ref_count == 0)
             GFREE(my_data);
  GFREE(fTokenDelimiter);
  GFREE(readbuf);
  }

char& GStr::operator[](int idx){
//returns reference to char (can be l-value)
  if (idx < 0) idx += length();
  if (idx < 0 || idx >= length()) invalid_index_error("operator[]");
  make_unique();  //because the user will probably modify this char!
  return chrs()[idx]; 
  }

char GStr::operator[](int idx) const {
//returns char copy (cannot be l-value!)
  if (idx < 0) idx += length();
  if (idx < 0 || idx >= length()) invalid_index_error("operator[]");
  return chars()[idx];
  }

GStr& GStr::operator=(const GStr& s) {
  make_unique(); //edit operation ahead
  replace_data(s.my_data); 
  return *this;
  }

GStr& GStr::operator=(const char *s) {
  make_unique(); //edit operation ahead
  if (s==NULL) {
    replace_data(0);
    return *this;
    }
  const int len = ::strlen(s); replace_data(len);
  ::memcpy(chrs(), s, len); 
  return *this;
  }

GStr& GStr::operator=(const double f) {
 make_unique(); //edit operation ahead
 char buf[20];
 sprintf(buf,"%f",f);
 const int len = ::strlen(buf);
 replace_data(len);
 ::memcpy(chrs(), buf, len);
 return *this;
}

GStr& GStr::operator=(const int i) {
 make_unique(); //edit operation ahead
 char buf[20];
 sprintf(buf,"%d",i);
 const int len = ::strlen(buf);
 replace_data(len);
 ::memcpy(chrs(), buf, len);
 return *this;
}

bool GStr::operator==(const GStr& s) const {
  if (s.is_empty()) return is_empty();
  return (length() == s.length()) &&
    (memcmp(chars(), s.chars(), length()) == 0);
  }

bool GStr::operator==(const char *s) const {
 if (s==NULL) return is_empty();
 return (strcmp(chars(), s) == 0);
 }

bool GStr::operator<(const GStr& s) const {
 if (s.is_empty()) return false;
 return (strcmp(chars(), s.chars()) < 0);
 }

bool GStr::operator<(const char *s) const {
 if (s==NULL) return false;
 return (strcmp(chars(), s) < 0);
 }

bool GStr::operator<=(const GStr& s) const {
 if (s.is_empty()) return is_empty();
 return (strcmp(chars(), s.chars()) <= 0);
 }

bool GStr::operator<=(const char *s) const {
 if (s==NULL) return is_empty();
 return (strcmp(chars(), s) <= 0);
 }

bool GStr::operator>(const GStr& s) const {
 if (s.is_empty()) return !is_empty();
 return (strcmp(chars(), s.chars()) > 0);
 }

bool GStr::operator>(const char *s) const {
 if (s==NULL) return !is_empty();
 return (strcmp(chars(), s) > 0);
 }

bool GStr::operator>=(const GStr& s) const {
 if (s.is_empty()) return true;
 return (strcmp(chars(), s.chars()) >= 0);
 }

bool GStr::operator>=(const char *s) const {
 if (s==NULL) return true;
 return (strcmp(chars(), s) >= 0);
 }

bool GStr::operator!=(const GStr& s) const {
  if (s.is_empty()) return !is_empty();
  return (length() != s.length()) ||
         (memcmp(chars(), s.chars(), length()) != 0);
  }

bool GStr::operator!=(const char *s) const {
 if (s==NULL) return !is_empty();
 return (strcmp(chars(), s) != 0);
 }

GStr& GStr::append(char c) {
 char buf[5];
 sprintf(buf,"%c",c);
 return append(buf);
 }

GStr& GStr::append(int i) {
 char buf[20];
 sprintf(buf,"%d",i);
 return append(buf);
 }

GStr& GStr::append(uint i) {
 char buf[20];
 sprintf(buf,"%u",i);
 return append(buf);
 }

GStr& GStr::append(long l) {
 char buf[20];
 sprintf(buf,"%ld",l);
 return append(buf);
 }

GStr& GStr::append(unsigned long l) {
 char buf[20];
 sprintf(buf,"%lu", l);
 return append(buf);
 }

GStr& GStr::append(double f) {
 char buf[30];
 sprintf(buf,"%f",f);
 return append(buf);
 }
 
bool GStr::is_empty() const {
  //return my_data == &null_data;
  return (length()==0);
  }

GStr GStr::copy() const {
 GStr newstring(*this);
 return newstring;
 }

GStr& GStr::clear() {
  make_unique(); //edit operation ahead
  replace_data(0);
  return *this;
  }

int GStr::index(const GStr& s, int start_index) const {
 return index(s.chars(), start_index);
 }

bool GStr::contains(const GStr& s) const {
 return (index(s, 0) >= 0);
 }

bool GStr::contains(const char *s) const {
 return (index(s, 0) >= 0);
 }

bool GStr::startsWith(const char *s) const {
 //return (index(s, 0) == 0);
 return ::startsWith(this->chars(), s);
 }

bool GStr::startsWith(const GStr& s) const {
 //return (index(s, 0) == 0);
 return ::startsWith(this->chars(), s.chars());
 }

bool GStr::endsWith(const char *s) const {
 //return (index(s, 0) == 0);
 return ::endsWith(this->chars(), s);
 }

bool GStr::endsWith(const GStr& s) const {
 //return (index(s, 0) == 0);
 return ::endsWith(this->chars(), s.chars());
 }

bool GStr::contains(char c) const {
 return (index(c, 0) >= 0);
 }

GStr& GStr::format(const char *fmt,...) {
// Format as in sprintf
  make_unique(); //edit operation ahead
  char* buf;
  GMALLOC(buf, strlen(fmt)+1024);
  va_list arguments;
  va_start(arguments,fmt);
  //+1K buffer, should be enough for common expressions
  int len=vsprintf(buf,fmt,arguments);
  va_end(arguments);
  replace_data(len); //this also adds the '\0' at the end!
                     //and sets the right len
  ::memcpy(chrs(), buf, len);
  GFREE(buf);
  return *this;
  }

GStr& GStr::appendfmt(const char *fmt,...) {
// Format as in sprintf
  make_unique(); //edit operation ahead
  char* buf;
  GMALLOC(buf, strlen(fmt)+1024);
  va_list arguments;
  va_start(arguments,fmt);
  //+1K buffer, should be enough for common expressions
  vsprintf(buf,fmt,arguments);
  va_end(arguments);
  append(buf);
  GFREE(buf);
  return *this;
  }

GStr& GStr::trim(char c) {
 register int istart;
 register int iend;
 for (istart=0; istart<length() && chars()[istart]==c;istart++) ;
 if (istart==length()) {
       make_unique(); //edit operation ahead
       replace_data(0); //string was entirely trimmed
       return *this;
       }
 for (iend=length()-1; iend>istart && chars()[iend]==c;iend--) ;
 int newlen=iend-istart+1;
 if (newlen==length())  //nothing to trim
           return *this; 
 make_unique(); //edit operation ahead
 Data *data = new_data(newlen);
 ::memcpy(data->chars, &chars()[istart], newlen);
 replace_data(data);
 return *this;
 }

GStr& GStr::trim(const char* c) {
 register int istart;
 register int iend;
 for (istart=0; istart<length() && strchr(c, chars()[istart])!=NULL ;istart++) ;
 if (istart==length()) {
        replace_data(0); //string was entirely trimmed
        return *this;
        }
 for (iend=length()-1; iend>istart && strchr(c, chars()[iend])!=NULL;iend--) ;
 int newlen=iend-istart+1;
 if (newlen==length())  //nothing to trim
           return *this; 
 make_unique(); //edit operation ahead
 Data *data = new_data(newlen);
 ::memcpy(data->chars, &chars()[istart], newlen);
 replace_data(data);
 return *this;
 }

GStr& GStr::trimR(char c) {
 //only trim the right end
 //register int istart;
 register int iend;
 for (iend=length()-1; iend>=0 && chars()[iend]==c;iend--) ;
 if (iend==-1) {
       replace_data(0); //string was entirely trimmed
       return *this;
       }
 int newlen=iend+1;
 if (newlen==length())  //nothing to trim
           return *this; 
 make_unique(); //edit operation ahead

 Data *data = new_data(newlen);
 ::memcpy(data->chars, chars(), newlen);
 replace_data(data);
 return *this;
 }

GStr& GStr::trimR(const char* c) {
 register int iend;
 for (iend=length()-1; iend>=0 && strchr(c,chars()[iend])!=NULL;iend--) ;
 if (iend==-1) {
       replace_data(0); //string was entirely trimmed
       return *this;
       }
 int newlen=iend+1;
 if (newlen==length())  //nothing to trim
           return *this; 
 make_unique(); //edit operation ahead
 Data *data = new_data(newlen);
 ::memcpy(data->chars, chars(), newlen);
 replace_data(data);
 return *this;
 }


GStr& GStr::chomp(const char* cstr) {
 register int iend;
 if (cstr==NULL || *cstr==0) return *this;
 //check if this ends with cstr
 int cend=strlen(cstr)-1;
 iend=my_data->length-1;
 while (iend>=0 && cend>=0) {
  if (my_data->chars[iend]!=cstr[cend]) return *this;
  iend--;
  cend--;
  }
 if (iend==-1) {
       replace_data(0); //string will be entirely trimmed
       return *this;
       }
 int newlen=iend+1;
 make_unique(); //edit operation ahead
 Data *data = new_data(newlen);
 ::memcpy(data->chars, chars(), newlen);
 replace_data(data);
 return *this;
 }

GStr& GStr::trimL(char c) {
 register int istart;
 for (istart=0; istart<length() && chars()[istart]==c;istart++) ;
 if (istart==length()) {
       replace_data(0); //string was entirely trimmed
       return *this;
       }
 int newlen=length()-istart;
 if (newlen==length())  //nothing to trim
           return *this; 
 make_unique(); //edit operation ahead
 Data *data = new_data(newlen);
 ::memcpy(data->chars, &chars()[istart], newlen);
 replace_data(data);
 return *this;
 }

GStr& GStr::trimL(const char* c) {
 register int istart;
 for (istart=0; istart<length() && strchr(c,chars()[istart])!=NULL;istart++) ;
 if (istart==length()) {
       replace_data(0); //string was entirely trimmed
       return *this;
       }
 int newlen=length()-istart;
 if (newlen==length())  //nothing to trim
           return *this; 
 make_unique(); //edit operation ahead

 Data *data = new_data(newlen);
 ::memcpy(data->chars, &chars()[istart], newlen);
 replace_data(data);
 return *this;
 }

GStr& GStr::padR(int len, char c) {
 //actually means align right in len
 if (length()>=len) return *this; //no room for padding
 make_unique(); //edit operation ahead
 Data *data = new_data(len);
 ::memset(data->chars,c,len-length());
 ::memcpy(&data->chars[len-length()], chars(), length());
 replace_data(data);
 return *this;
 }

GStr& GStr::padL(int len, char c) { //align left the string
 if (length()>=len) return *this; //no room for padding
 make_unique(); //edit operation ahead
 Data *data = new_data(len);
 ::memcpy(data->chars, chars(), length());
 ::memset(&data->chars[length()],c,len-length());
 replace_data(data);
 return *this;
 }

GStr& GStr::padC(int len, char c) {
 if (length()>=len) return *this; //no room for padding
 make_unique(); //edit operation ahead
 int istart=(len-length())/2;
 Data *data = new_data(len);
 if (istart>0)
      ::memset(data->chars, c, istart);
 ::memcpy(&data->chars[istart], chars(), length());
 int iend=istart+length();
 if (iend<len)
      ::memset(&data->chars[iend],c,len-iend);
 replace_data(data);
 return *this;
 }

GStr operator+(const char *s1, const GStr& s2) {
    const int s1_length = ::strlen(s1);

    if (s1_length == 0)
        return s2;
    else {
        GStr newstring;
        newstring.replace_data(s1_length + s2.length());
        ::memcpy(newstring.chrs(), s1, s1_length);
        ::memcpy(&(newstring.chrs())[s1_length], s2.chars(), s2.length());
        return newstring;
        }
}

//=========================================

GStr GStr::operator+(const GStr& s) const {
    if (length() == 0)
        return s;
    else if (s.length() == 0)
        return *this;
    else {
        GStr newstring;
        newstring.replace_data(length() + s.length());
        ::memcpy(newstring.chrs(), chars(), length());
        ::memcpy(&(newstring.chrs())[length()], s.chars(), s.length());
        return newstring;
        }
}

//=========================================

GStr GStr::operator+(const char *s) const {

    const int s_length = ::strlen(s);

    if (s_length == 0)
        return *this;
    else {
        GStr newstring;
        newstring.replace_data(length() + s_length);
        ::memcpy(newstring.chrs(), chars(), length());
        ::memcpy(&(newstring.chrs())[length()], s, s_length);
        return newstring;
        }
}

GStr GStr::operator+(const int i) const {
    char buf[20];
    sprintf(buf, "%d", i);
    const int s_length = ::strlen(buf);
    GStr newstring;
    newstring.replace_data(length() + s_length);
    ::memcpy(newstring.chrs(), chars(), length());
    ::memcpy(&(newstring.chrs())[length()], buf, s_length);
    return newstring;
}

GStr GStr::operator+(const char c) const {
    char buf[4];
    sprintf(buf, "%c", c);
    const int s_length = ::strlen(buf);
    GStr newstring;
    newstring.replace_data(length() + s_length);
    ::memcpy(newstring.chrs(), chars(), length());
    ::memcpy(&(newstring.chrs())[length()], buf, s_length);
    return newstring;
}

GStr GStr::operator+(const double f) const {
    char buf[30];
    sprintf(buf, "%f", f);
    const int s_length = ::strlen(buf);
    GStr newstring;
    newstring.replace_data(length() + s_length);
    ::memcpy(newstring.chrs(), chars(), length());
    ::memcpy(&(newstring.chrs())[length()], buf, s_length);
    return newstring;
}


//=========================================

bool GStr::is_space() const {

    if (my_data == &null_data)
        return false;

    for (register const char *p = chars(); *p; p++)
        if (!isspace(*p))
            return false;

    return true;
}

//=========================================

GStr GStr::substr(int idx, int len) const {
    // A negative idx specifies an idx from the right of the string.
    if (idx < 0)
        idx += length();

    // A length of -1 specifies the rest of the string.
    if (len < 0  || len>length()-idx)
        len = length() - idx;
    
    if (idx<0 || idx>=length() || len<0 )
        invalid_args_error("substr()");

    GStr newstring;
    newstring.replace_data(len);
    ::memcpy(newstring.chrs(), &chars()[idx], len);
    return newstring;
}

GStr& GStr::reverse() {
  make_unique();
  int l=0;
  int r=my_data->length-1;
  char c;
  while (l<r) {
     c=my_data->chars[l];
     my_data->chars[l]=my_data->chars[r];
     my_data->chars[r]=c;
     l++;r--;
     }
  return *this;
}


//transform: any character from 'from' is replaced with a coresponding
//char from 'to'

GStr&  GStr::tr(const char *rfrom, const char* rto) {
 if (length() == 0 || rfrom==NULL || strlen(rfrom)==0)
        return *this;
 unsigned int l=strlen(rfrom);       
 if (rto!=NULL && strlen(rto)!=l)
      invalid_args_error("tr()");
 make_unique(); //edit operation ahead
 Data *data = new_data(length());<commit_msg>fixed old bug in GStr::split()<commit_after>//---------------------------------------------------------------------------
#include "GStr.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "GBase.h"
#include <stdarg.h>
#include <errno.h>

//---------------------------------------------------------------------------

GStr::Data GStr::null_data;

//=========================================

GStr::Data * GStr::new_data(int length) {
//static method to return a new Data object (allocate length)
//content is undefined, but it's null terminated
    if (length > 0) {
        Data* data;
        GMALLOC(data, sizeof(Data)+length);
        data->ref_count = 0;
        data->length = length;
        data->chars[length] = '\0';
        return data;
        }
    else
        return &null_data;
 }

GStr::Data* GStr::new_data(const char* str) {
//static method to return a new Data object (allocate length)
//as a copy of a given string
 if (str==NULL) return &null_data;
 int length=strlen(str);
 if (length > 0) {
        Data* data;
        GMALLOC(data, sizeof(Data)+length);
        strcpy(data->chars, str);
        data->ref_count = 0;
        data->length = length;
        data->chars[length] = '\0';
        return data;
        }
    else
        return &null_data;
 }
 
void GStr::replace_data(int len) {

    if (len == my_data->length && my_data->ref_count <= 1)
        return;

    if (my_data != &null_data && --my_data->ref_count == 0)
        GFREE(my_data);

    if (len > 0) {
        //my_data = (Data *) malloc(sizeof(Data) + len);
        GMALLOC(my_data, sizeof(Data) + len);
        my_data->ref_count = 1;
        my_data->length = len;
        my_data->chars[len] = '\0';
    }
    else
        my_data = &null_data;
}

void GStr::replace_data(Data *data) {
    if (my_data != &null_data && --my_data->ref_count == 0)
        GFREE(my_data);
    if (data != &null_data)
        data->ref_count++;
    my_data = data;
}

void GStr::make_unique() {//make sure it's not a reference to other string
    if (my_data->ref_count > 1) {
        Data *data = new_data(length());
        ::memcpy(data->chars, chars(), length());
        my_data->ref_count--;
        my_data = data;
        my_data->ref_count++;
    }
}

bool operator==(const char *s1, const GStr& s2){
  if (s1==NULL) return s2.is_empty();
  return (strcmp(s1, s2.chars()) == 0);
  }

bool operator<(const char *s1, const GStr& s2) {
  if (s1==NULL) return !s2.is_empty();
  return (strcmp(s1, s2.chars()) < 0);
  }

bool operator<=(const char *s1, const GStr& s2){
 if (s1==NULL) return true;
 return (strcmp(s1, s2.chars()) <= 0);
 }

bool operator>(const char *s1, const GStr& s2) {
  if (s1==NULL) return false;
 return (strcmp(s1, s2.chars()) > 0);
 }


GStr::GStr():my_data(&null_data) {
 fTokenDelimiter=NULL;
 fTokenizeMode=tkCharSet;
 fLastTokenStart=0;
 readbuf=NULL;
 readbufsize=0;
 }

GStr::GStr(const GStr& s): my_data(&null_data){
 fTokenDelimiter=NULL;
 fTokenizeMode=tkCharSet;
 fLastTokenStart=0;
 readbuf=NULL;
 readbufsize=0;
 replace_data(s.my_data);
 }

GStr::GStr(const char *s): my_data(&null_data) {
  fTokenDelimiter=NULL;
  fTokenizeMode=tkCharSet;
  fLastTokenStart=0;
  readbuf=NULL;
  readbufsize=0;
  my_data=new_data(s);
  my_data->ref_count = 1;
 }

GStr::GStr(const int i): my_data(&null_data) {
 fTokenDelimiter=NULL;
 fTokenizeMode=tkCharSet;
 fLastTokenStart=0;
 readbuf=NULL;
 readbufsize=0;
 char buf[20];
 sprintf(buf,"%d",i);
 const int len = ::strlen(buf);
 replace_data(len);
 ::memcpy(chrs(), buf, len);
 }

GStr::GStr(const double f): my_data(&null_data) {
 fTokenDelimiter=NULL;
 fTokenizeMode=tkCharSet;
 fLastTokenStart=0;
 readbuf=NULL;
 readbufsize=0;
 char buf[20];
 sprintf(buf,"%f",f);
 const int len = ::strlen(buf);
 replace_data(len);
 ::memcpy(chrs(), buf, len);
 }

GStr::GStr(char c, int n): my_data(&null_data) {
  fTokenDelimiter=NULL;
  fTokenizeMode=tkCharSet;
  fLastTokenStart=0;
  readbuf=NULL;
  readbufsize=0;
  replace_data(n); ::memset(chrs(), c, n);
  }

GStr::~GStr() {  
  if (my_data != &null_data && --my_data->ref_count == 0)
             GFREE(my_data);
  GFREE(fTokenDelimiter);
  GFREE(readbuf);
  }

char& GStr::operator[](int idx){
//returns reference to char (can be l-value)
  if (idx < 0) idx += length();
  if (idx < 0 || idx >= length()) invalid_index_error("operator[]");
  make_unique();  //because the user will probably modify this char!
  return chrs()[idx]; 
  }

char GStr::operator[](int idx) const {
//returns char copy (cannot be l-value!)
  if (idx < 0) idx += length();
  if (idx < 0 || idx >= length()) invalid_index_error("operator[]");
  return chars()[idx];
  }

GStr& GStr::operator=(const GStr& s) {
  make_unique(); //edit operation ahead
  replace_data(s.my_data); 
  return *this;
  }

GStr& GStr::operator=(const char *s) {
  make_unique(); //edit operation ahead
  if (s==NULL) {
    replace_data(0);
    return *this;
    }
  const int len = ::strlen(s); replace_data(len);
  ::memcpy(chrs(), s, len); 
  return *this;
  }

GStr& GStr::operator=(const double f) {
 make_unique(); //edit operation ahead
 char buf[20];
 sprintf(buf,"%f",f);
 const int len = ::strlen(buf);
 replace_data(len);
 ::memcpy(chrs(), buf, len);
 return *this;
}

GStr& GStr::operator=(const int i) {
 make_unique(); //edit operation ahead
 char buf[20];
 sprintf(buf,"%d",i);
 const int len = ::strlen(buf);
 replace_data(len);
 ::memcpy(chrs(), buf, len);
 return *this;
}

bool GStr::operator==(const GStr& s) const {
  if (s.is_empty()) return is_empty();
  return (length() == s.length()) &&
    (memcmp(chars(), s.chars(), length()) == 0);
  }

bool GStr::operator==(const char *s) const {
 if (s==NULL) return is_empty();
 return (strcmp(chars(), s) == 0);
 }

bool GStr::operator<(const GStr& s) const {
 if (s.is_empty()) return false;
 return (strcmp(chars(), s.chars()) < 0);
 }

bool GStr::operator<(const char *s) const {
 if (s==NULL) return false;
 return (strcmp(chars(), s) < 0);
 }

bool GStr::operator<=(const GStr& s) const {
 if (s.is_empty()) return is_empty();
 return (strcmp(chars(), s.chars()) <= 0);
 }

bool GStr::operator<=(const char *s) const {
 if (s==NULL) return is_empty();
 return (strcmp(chars(), s) <= 0);
 }

bool GStr::operator>(const GStr& s) const {
 if (s.is_empty()) return !is_empty();
 return (strcmp(chars(), s.chars()) > 0);
 }

bool GStr::operator>(const char *s) const {
 if (s==NULL) return !is_empty();
 return (strcmp(chars(), s) > 0);
 }

bool GStr::operator>=(const GStr& s) const {
 if (s.is_empty()) return true;
 return (strcmp(chars(), s.chars()) >= 0);
 }

bool GStr::operator>=(const char *s) const {
 if (s==NULL) return true;
 return (strcmp(chars(), s) >= 0);
 }

bool GStr::operator!=(const GStr& s) const {
  if (s.is_empty()) return !is_empty();
  return (length() != s.length()) ||
         (memcmp(chars(), s.chars(), length()) != 0);
  }

bool GStr::operator!=(const char *s) const {
 if (s==NULL) return !is_empty();
 return (strcmp(chars(), s) != 0);
 }

GStr& GStr::append(char c) {
 char buf[5];
 sprintf(buf,"%c",c);
 return append(buf);
 }

GStr& GStr::append(int i) {
 char buf[20];
 sprintf(buf,"%d",i);
 return append(buf);
 }

GStr& GStr::append(uint i) {
 char buf[20];
 sprintf(buf,"%u",i);
 return append(buf);
 }

GStr& GStr::append(long l) {
 char buf[20];
 sprintf(buf,"%ld",l);
 return append(buf);
 }

GStr& GStr::append(unsigned long l) {
 char buf[20];
 sprintf(buf,"%lu", l);
 return append(buf);
 }

GStr& GStr::append(double f) {
 char buf[30];
 sprintf(buf,"%f",f);
 return append(buf);
 }
 
bool GStr::is_empty() const {
  //return my_data == &null_data;
  return (length()==0);
  }

GStr GStr::copy() const {
 GStr newstring(*this);
 return newstring;
 }

GStr& GStr::clear() {
  make_unique(); //edit operation ahead
  replace_data(0);
  return *this;
  }

int GStr::index(const GStr& s, int start_index) const {
 return index(s.chars(), start_index);
 }

bool GStr::contains(const GStr& s) const {
 return (index(s, 0) >= 0);
 }

bool GStr::contains(const char *s) const {
 return (index(s, 0) >= 0);
 }

bool GStr::startsWith(const char *s) const {
 //return (index(s, 0) == 0);
 return ::startsWith(this->chars(), s);
 }

bool GStr::startsWith(const GStr& s) const {
 //return (index(s, 0) == 0);
 return ::startsWith(this->chars(), s.chars());
 }

bool GStr::endsWith(const char *s) const {
 //return (index(s, 0) == 0);
 return ::endsWith(this->chars(), s);
 }

bool GStr::endsWith(const GStr& s) const {
 //return (index(s, 0) == 0);
 return ::endsWith(this->chars(), s.chars());
 }

bool GStr::contains(char c) const {
 return (index(c, 0) >= 0);
 }

GStr& GStr::format(const char *fmt,...) {
// Format as in sprintf
  make_unique(); //edit operation ahead
  char* buf;
  GMALLOC(buf, strlen(fmt)+1024);
  va_list arguments;
  va_start(arguments,fmt);
  //+1K buffer, should be enough for common expressions
  int len=vsprintf(buf,fmt,arguments);
  va_end(arguments);
  replace_data(len); //this also adds the '\0' at the end!
                     //and sets the right len
  ::memcpy(chrs(), buf, len);
  GFREE(buf);
  return *this;
  }

GStr& GStr::appendfmt(const char *fmt,...) {
// Format as in sprintf
  make_unique(); //edit operation ahead
  char* buf;
  GMALLOC(buf, strlen(fmt)+1024);
  va_list arguments;
  va_start(arguments,fmt);
  //+1K buffer, should be enough for common expressions
  vsprintf(buf,fmt,arguments);
  va_end(arguments);
  append(buf);
  GFREE(buf);
  return *this;
  }

GStr& GStr::trim(char c) {
 register int istart;
 register int iend;
 for (istart=0; istart<length() && chars()[istart]==c;istart++) ;
 if (istart==length()) {
       make_unique(); //edit operation ahead
       replace_data(0); //string was entirely trimmed
       return *this;
       }
 for (iend=length()-1; iend>istart && chars()[iend]==c;iend--) ;
 int newlen=iend-istart+1;
 if (newlen==length())  //nothing to trim
           return *this; 
 make_unique(); //edit operation ahead
 Data *data = new_data(newlen);
 ::memcpy(data->chars, &chars()[istart], newlen);
 replace_data(data);
 return *this;
 }

GStr& GStr::trim(const char* c) {
 register int istart;
 register int iend;
 for (istart=0; istart<length() && strchr(c, chars()[istart])!=NULL ;istart++) ;
 if (istart==length()) {
        replace_data(0); //string was entirely trimmed
        return *this;
        }
 for (iend=length()-1; iend>istart && strchr(c, chars()[iend])!=NULL;iend--) ;
 int newlen=iend-istart+1;
 if (newlen==length())  //nothing to trim
           return *this; 
 make_unique(); //edit operation ahead
 Data *data = new_data(newlen);
 ::memcpy(data->chars, &chars()[istart], newlen);
 replace_data(data);
 return *this;
 }

GStr& GStr::trimR(char c) {
 //only trim the right end
 //register int istart;
 register int iend;
 for (iend=length()-1; iend>=0 && chars()[iend]==c;iend--) ;
 if (iend==-1) {
       replace_data(0); //string was entirely trimmed
       return *this;
       }
 int newlen=iend+1;
 if (newlen==length())  //nothing to trim
           return *this; 
 make_unique(); //edit operation ahead

 Data *data = new_data(newlen);
 ::memcpy(data->chars, chars(), newlen);
 replace_data(data);
 return *this;
 }

GStr& GStr::trimR(const char* c) {
 register int iend;
 for (iend=length()-1; iend>=0 && strchr(c,chars()[iend])!=NULL;iend--) ;
 if (iend==-1) {
       replace_data(0); //string was entirely trimmed
       return *this;
       }
 int newlen=iend+1;
 if (newlen==length())  //nothing to trim
           return *this; 
 make_unique(); //edit operation ahead
 Data *data = new_data(newlen);
 ::memcpy(data->chars, chars(), newlen);
 replace_data(data);
 return *this;
 }


GStr& GStr::chomp(const char* cstr) {
 register int iend;
 if (cstr==NULL || *cstr==0) return *this;
 //check if this ends with cstr
 int cend=strlen(cstr)-1;
 iend=my_data->length-1;
 while (iend>=0 && cend>=0) {
  if (my_data->chars[iend]!=cstr[cend]) return *this;
  iend--;
  cend--;
  }
 if (iend==-1) {
       replace_data(0); //string will be entirely trimmed
       return *this;
       }
 int newlen=iend+1;
 make_unique(); //edit operation ahead
 Data *data = new_data(newlen);
 ::memcpy(data->chars, chars(), newlen);
 replace_data(data);
 return *this;
 }

GStr& GStr::trimL(char c) {
 register int istart;
 for (istart=0; istart<length() && chars()[istart]==c;istart++) ;
 if (istart==length()) {
       replace_data(0); //string was entirely trimmed
       return *this;
       }
 int newlen=length()-istart;
 if (newlen==length())  //nothing to trim
           return *this; 
 make_unique(); //edit operation ahead
 Data *data = new_data(newlen);
 ::memcpy(data->chars, &chars()[istart], newlen);
 replace_data(data);
 return *this;
 }

GStr& GStr::trimL(const char* c) {
 register int istart;
 for (istart=0; istart<length() && strchr(c,chars()[istart])!=NULL;istart++) ;
 if (istart==length()) {
       replace_data(0); //string was entirely trimmed
       return *this;
       }
 int newlen=length()-istart;
 if (newlen==length())  //nothing to trim
           return *this; 
 make_unique(); //edit operation ahead

 Data *data = new_data(newlen);
 ::memcpy(data->chars, &chars()[istart], newlen);
 replace_data(data);
 return *this;
 }

GStr& GStr::padR(int len, char c) {
 //actually means align right in len
 if (length()>=len) return *this; //no room for padding
 make_unique(); //edit operation ahead
 Data *data = new_data(len);
 ::memset(data->chars,c,len-length());
 ::memcpy(&data->chars[len-length()], chars(), length());
 replace_data(data);
 return *this;
 }

GStr& GStr::padL(int len, char c) { //align left the string
 if (length()>=len) return *this; //no room for padding
 make_unique(); //edit operation ahead
 Data *data = new_data(len);
 ::memcpy(data->chars, chars(), length());
 ::memset(&data->chars[length()],c,len-length());
 replace_data(data);
 return *this;
 }

GStr& GStr::padC(int len, char c) {
 if (length()>=len) return *this; //no room for padding
 make_unique(); //edit operation ahead
 int istart=(len-length())/2;
 Data *data = new_data(len);
 if (istart>0)
      ::memset(data->chars, c, istart);
 ::memcpy(&data->chars[istart], chars(), length());
 int iend=istart+length();
 if (iend<len)
      ::memset(&data->chars[iend],c,len-iend);
 replace_data(data);
 return *this;
 }

GStr operator+(const char *s1, const GStr& s2) {
    const int s1_length = ::strlen(s1);

    if (s1_length == 0)
        return s2;
    else {
        GStr newstring;
        newstring.replace_data(s1_length + s2.length());
        ::memcpy(newstring.chrs(), s1, s1_length);
        ::memcpy(&(newstring.chrs())[s1_length], s2.chars(), s2.length());
        return newstring;
        }
}

//=========================================

GStr GStr::operator+(const GStr& s) const {
    if (length() == 0)
        return s;
    else if (s.length() == 0)
        return *this;
    else {
        GStr newstring;
        newstring.replace_data(length() + s.length());
        ::memcpy(newstring.chrs(), chars(), length());
        ::memcpy(&(newstring.chrs())[length()], s.chars(), s.length());
        return newstring;
        }
}

//=========================================

GStr GStr::operator+(const char *s) const {

    const int s_length = ::strlen(s);

    if (s_length == 0)
        return *this;
    else {
        GStr newstring;
        newstring.replace_data(length() + s_length);
        ::memcpy(newstring.chrs(), chars(), length());
        ::memcpy(&(newstring.chrs())[length()], s, s_length);
        return newstring;
        }
}

GStr GStr::operator+(const int i) const {
    char buf[20];
    sprintf(buf, "%d", i);
    const int s_length = ::strlen(buf);
    GStr newstring;
    newstring.replace_data(length() + s_length);
    ::memcpy(newstring.chrs(), chars(), length());
    ::memcpy(&(newstring.chrs())[length()], buf, s_length);
    return newstring;
}

GStr GStr::operator+(const char c) const {
    char buf[4];
    sprintf(buf, "%c", c);
    const int s_length = ::strlen(buf);
    GStr newstring;
    newstring.replace_data(length() + s_length);
    ::memcpy(newstring.chrs(), chars(), length());
    ::memcpy(&(newstring.chrs())[length()], buf, s_length);
    return newstring;
}

GStr GStr::operator+(const double f) const {
    char buf[30];
    sprintf(buf, "%f", f);
    const int s_length = ::strlen(buf);
    GStr newstring;
    newstring.replace_data(length() + s_length);
    ::memcpy(newstring.chrs(), chars(), length());
    ::memcpy(&(newstring.chrs())[length()], buf, s_length);
    return newstring;
}


//=========================================

bool GStr::is_space() const {

    if (my_data == &null_data)
        return false;

    for (register const char *p = chars(); *p; p++)
        if (!isspace(*p))
            return false;

    return true;
}

//=========================================

GStr GStr::substr(int idx, int len) const {
    // A negative idx specifies an idx from the right of the string.
    if (idx < 0)
        idx += length();
    else if (idx>=length()) {
             len=0;
             idx=length();
             }
    if (len) {
      // A length of -1 specifies the rest of the string.
      if (len < 0  || len>length()-idx)
        len = length() - idx;
      if (idx<0 || idx>=length() || len<0 )
        invalid_args_error("substr()");
    }

    GStr newstring;
    if (len) {
      newstring.replace_data(len);
      ::memcpy(newstring.chrs(), &chars()[idx], len);
    }
    return newstring;
}

GStr& GStr::reverse() {
  make_unique();
  int l=0;
  int r=my_data->length-1;
  char c;
  while (l<r) {
     c=my_data->chars[l];
     my_data->chars[l]=my_data->chars[r];
     my_data->chars[r]=c;
     l++;r--;
     }
  return *this;
}


//transform: any character from 'from' is replaced with a coresponding
//char from 'to'

GStr&  GStr::tr(const char *rfrom, const char* rto) {
 if (length() == 0 || rfrom==NULL || strlen(rfrom)==0)
        return *this;
 unsigned int l=strlen(rfrom);       
 if (rto!=NULL && strlen(rto)!=l)
      invalid_args_error("tr()");
 make_unique(); //edit operation ahead
 Data *data = new_data(length());
{'fraction_non_alphanumeric': 0.20037105751391465, 'fraction_numerical': 0.009941018469803118, 'mean_word_length': 3.480089318943059, 'pattern_counts': {'":': 0, '<': 67, '<?xml version=': 0, '>': 168, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5580924', 'n_tokens_mistral': 12983, 'n_tokens_neox': 12803, 'n_words': 3196}