/* xstring.cpp Aug 31 1998 Chris Lacher Implementations of String methods, friends, and other overloads In this implementation the cases (str == 0) and (size == 0) are treated as equal (empty) Strings. Note that size, the return value for Size(), is maintained as one less than the length of the containing array by these methods. The Length() method, on the other hand, returns the value of strlen() applied to the private C-string datum. Size() and Length() should return the same value. However, this relationship could be corrupted by the client, by for example assigning S[i] = '\0'. Copyright 1998 - 2003, R.C. Lacher */ #ifndef _XSTRING_CCP #define _XSTRING_CCP #include // EXIT_FAILURE and other definitions #include // the C string function library #include "xstring.h" // #include "debug.h" namespace fsu { //-------------------- // class String //-------------------- static const size_t init_buff_size = 25; // used as size increment for local buffer during extraction // file scope // String operator overloads // first the String comparison function, used to define the comparison operators: int String::StrCmp(const String& S1, const String& S2) // returns: 0 if S1 == S2 // - if S1 < S2 // + if S1 > S2 // using lexicographic ordering { if ((S1.Size() == 0) && (S2.Size() == 0)) return 0; else if ((S1.Size() == 0) && (S2.Size() != 0)) return -1; else if ((S1.Size() != 0) && (S2.Size() == 0)) return 1; else return (strcmp(S1.str, S2.str)); } // end StrCmp() // second the comparison operators: int operator == (const String& S1, const String& S2) { return (String::StrCmp(S1, S2) == 0); } int operator != (const String& S1, const String& S2) { return (String::StrCmp(S1, S2) != 0); } int operator > (const String& S1, const String& S2) { return (String::StrCmp(S1, S2) > 0); } int operator >= (const String& S1, const String& S2) { return (String::StrCmp(S1, S2) >= 0); } int operator < (const String& S1, const String& S2) { return (String::StrCmp(S1, S2) < 0); } int operator <= (const String& S1, const String& S2) { return (String::StrCmp(S1, S2) <= 0); } // third the I/O operators: std::ostream& operator << (std::ostream& os, const String& S) { if (S.str != 0) os << S.str; return os; } std::istream& operator >> (std::istream& is, String& S) { // debug d("operator >> ()"); size_t curr_size = 0, buff_size = init_buff_size; // skip clearspace char x; is >> x; if (is.fail() || is.eof()) { return is; } // space for temporary char storage char* buffer = String::newstr(buff_size); // insert x and continue reading contiguous non-clearspace buffer[curr_size++] = x; x = is.peek(); while ((x != ' ') && (x != '\n') && (x != '\t') && (!is.eof())) { if (curr_size == buff_size) // need more buffer { buff_size *= 2; char* newbuffer = String::newstr(buff_size); for (size_t i = 0; i < curr_size; ++i) { newbuffer[i] = buffer[i]; } delete [] buffer; buffer = newbuffer; } buffer[curr_size++] = x; is.get(); x = is.peek(); } buffer[curr_size] = '\0'; S.Wrap(buffer); delete [] buffer; return is; } // fourth the concatenation operator: const String operator + (const String&S1, const String& S2) { String catString; char* catstr; int i, len1, len2, catlen; len1 = S1.Size(); len2 = S2.Size(); catlen = len1 + len2; catstr = String::newstr(catlen); for (i = 0; i < len1; ++i) { catstr[i] = S1.Element(i); } for (i = len1; i < catlen; ++i) { catstr[i] = S2.Element(i - len1); } catString.Wrap(catstr); delete [] catstr; return catString; } // String member functions // constructors String::String() : str(0), size(0) { // debug d("String constructor 1"); } String::String(const char* Cptr) : str(0), size(0) { // debug d("String constructor 2"); Wrap(Cptr); } String::String (size_t sz, char fill) : str(0), size(0) { SetSize(sz,fill); } String::~String() { // debug d("String destructor"); if (str) delete [] str; } String::String(const String& S) { // debug d("String copy constructor"); Clone(S); } // String operators String& String::operator = (const String& S) { // debug d("assignment"); if (this != &S) { if (size != S.size) { Clear(); Clone(S); } else { xstrcpy (str, S.str); } } return *this; } char& String::operator [] (size_t n) const // overload of the array access operator // Note: [] returns a reference to the element, hence can be used on // the left (receiving end) of assignment. // Distinctions between [] and Element(): // Element(n) returns a value, equal to the element str[n] // when 0 <= n < size and to '\0' otherwise. // Element(n) cannot be used to assign a (new) value to the element. // [n] returns a reference to the element when 0 <= n < size // and exits otherwise. // [n] can be used to assign a value to any element except the last. { if ((size == 0) || (n >= size)) { Error("index out of range"); } return str[n]; } String::operator const char* () const // Overload of the operator "const char*" for String objects; // typical use is in automatic type conversion. For example, any // function from string.h that takes const char* parameter can now // automatically be called on String objects. See Length() // implementation below. { return str; } // String builders void String::Wrap(const char* Cptr) { // debug d("Wrap()"); Clear(); if (Cptr) { size = xstrlen(Cptr); str = newstr(size); xstrcpy(str, Cptr); } } void String::GetLine (std::istream& is) { // debug d("GetLine()"); size_t curr_size = 0, buff_size = init_buff_size; char* buffer; buffer = new char [buff_size + 1]; char x = is.get(); while ((x != '\n') && (!is.eof())) { if (curr_size == buff_size) // need more buffer { buff_size *= 2; char* newbuffer = new char [buff_size + 1]; for (size_t i = 0; i < curr_size; ++i) { newbuffer[i] = buffer[i]; } delete [] buffer; buffer = newbuffer; } buffer[curr_size++] = x; x = is.get(); } buffer[curr_size] = '\0'; Wrap(buffer); delete [] buffer; } int String::SetSize (size_t sz, char fill) { if (str == 0) { str = newstr(sz); if (str == 0) return 0; size = sz; for (size_t j = 0; j < size; ++j) str[j] = fill; return 1; } if (sz != Size()) { char* newdata = newstr(sz); if (newdata == 0) return 0; size_t i; if (sz < Size()) { for (i = 0; i < sz; ++i) newdata[i] = str[i]; } else { for (i = 0; i < Size(); ++i) newdata[i] = str[i]; for (i = Size(); i < sz; ++i) newdata[i] = fill; } delete [] str; str = newdata; size = sz; } return 1; } void String::Clear() { // debug d("Clear()"); if (str) { delete [] str; str = 0; size = 0; } } // String data accessors size_t String::Size() const { return size; } size_t String::Length () const // Note: this is an application of automatic type conversion // from String to const char* { if (str != 0) return strlen (*this); return 0; } char String::Element(size_t n) const { if ((size == 0) || (n >= size)) return '\0'; else return str[n]; } void String::Dump (std::ostream& os) const { os << "String::Size() = " << Size() << '\n' << "String::Length() = " << Length() << '\n' << "c-string operator <<() : " << str << '\n' << "String:: operator <<() : " << *this << '\n'; } // private methods void String::Clone(const String& S) // Dangerous -- take care not to apply to *this ! { // debug d("Clone()"); size = S.Size(); if (size > 0) { str = newstr(size); xstrcpy (str, S.str); } else { str = 0; } } // end Clone() char* String::newstr(int n) // creates a new (C) string of size n (array size = n+1) // with memory allocation error message { // debug d("newstr()"); char* Cptr; if (n >= 0) { Cptr = new char [n + 1]; if (Cptr == 0) { Error("memory allocation failure"); } Cptr[n] = '\0'; } else Cptr = 0; return Cptr; } // end newstr() int String::xstrlen(const char* s) // a version of strlen() { // debug d("xstrlen()"); int len = 0; if (s != 0) { for (len = 0; s[len] != '\0'; ++len){} } return len; } // end xstrlen() void String::xstrcpy(char* s1, const char* s2) // a version of strcpy(); DOES NOT CHECK OPERAND ARRAY SIZES { // debug d("xstrcpy()"); if (s2 != 0) { if (s1 != 0) { int i; for (i = 0; s2[i] != '\0'; ++i) s1[i] = s2[i]; s1[i] = '\0'; } else { Error("xstrcpy() operand 0"); } } } // end xstrcpy() void String::Error(const char* msg) { std::cerr << "** String error: " << msg << '\n'; exit (EXIT_FAILURE); } } // namespace fsu #endif