// // String.cc // // String: (interface in htString.h) Just Another String class. // // Part of the ht://Dig package // Copyright (c) 1995-2004 The ht://Dig Group // For copyright details, see the file COPYING in your distribution // or the GNU Library General Public License (LGPL) version 2 or later // // // $Id: String.cc,v 1.40 2004/05/28 13:15:21 lha Exp $ // #ifdef HAVE_CONFIG_H #include "htconfig.h" #endif /* HAVE_CONFIG_H */ #include "htString.h" #include "Object.h" #ifndef _MSC_VER /* _WIN32 */ #include #else #include #endif #ifdef HAVE_STD #include #ifdef HAVE_NAMESPACES using namespace std; #endif #else #include #endif /* HAVE_STD */ #include #include #include const int MinimumAllocationSize = 4; // Should be power of two. #ifdef NOINLINE String::String() { Length = Allocated = 0; Data = 0; } #endif String::String(int init) { Length = 0; Allocated = init >= MinimumAllocationSize ? init : MinimumAllocationSize; Data = new char[Allocated]; } String::String(const char *s) { Allocated = Length = 0; Data = 0; int len; if (s) { len = strlen(s); copy(s, len, len); } } String::String(const char *s, int len) { Allocated = Length = 0; Data = 0; if (s && len > 0) copy(s, len, len); } String::String(const String &s) { Allocated = Length = 0; Data = 0; if (s.length() > 0) copy(s.Data, s.length(), s.length()); } // // This can be used for performance reasons if it is known the // String will need to grow. // String::String(const String &s, int allocation_hint) { Allocated = Length = 0; Data = 0; if (s.length() != 0) { if (allocation_hint < s.length()) allocation_hint = s.length(); copy(s.Data, s.length(), allocation_hint); } } String::~String() { if (Allocated) delete [] Data; } void String::operator = (const String &s) { if (s.length() > 0) { allocate_space(s.length()); Length = s.length(); copy_data_from(s.Data, Length); } else { Length = 0; } } void String::operator = (const char *s) { if (s) { int len = strlen(s); allocate_fix_space(len); Length = len; copy_data_from(s, Length); } else Length = 0; } void String::append(const String &s) { if (s.length() == 0) return; int new_len = Length + s.length(); reallocate_space(new_len); copy_data_from(s.Data, s.length(), Length); Length = new_len; } void String::append(const char *s) { if (!s) return; append(s,strlen(s)); } void String::append(const char *s, int slen) { if (!s || !slen) return; // if ( slen == 1 ) // { // append(*s); // return; // } int new_len = Length + slen; if (new_len + 1 > Allocated) reallocate_space(new_len); copy_data_from(s, slen, Length); Length = new_len; } void String::append(char ch) { int new_len = Length +1; if (new_len + 1 > Allocated) reallocate_space(new_len); Data[Length] = ch; Length = new_len; } int String::compare(const String& obj) const { int len; int result; const char *p1 = Data; const char *p2 = obj.Data; len = Length; result = 0; if (Length > obj.Length) { result = 1; len = obj.Length; } else if (Length < obj.Length) result = -1; while (len) { if (*p1 > *p2) return 1; if (*p1 < *p2) return -1; p1++; p2++; len--; } // // Strings are equal up to the shortest length. // The result depends upon the length difference. // return result; } int String::nocase_compare(const String &s) const { const char *p1 = get(); const char *p2 = s.get(); return mystrcasecmp(p1, p2); } int String::Write(int fd) const { int left = Length; char *wptr = Data; while (left) { int result = write(fd, wptr, left); if (result < 0) return result; left -= result; wptr += result; } return left; } const char *String::get() const { static const char *null = ""; if (!Allocated) return null; Data[Length] = '\0'; // We always leave room for this. return Data; } char *String::get() { static char *null = ""; if (!Allocated) return null; Data[Length] = '\0'; // We always leave room for this. return Data; } char *String::new_char() const { char *r; if (!Allocated) { r = new char[1]; *r = '\0'; return r; } Data[Length] = '\0'; // We always leave room for this. r = new char[Length + 1]; strcpy(r, Data); return r; } int String::as_integer(int def) const { if (Length <= 0) return def; Data[Length] = '\0'; return atoi(Data); } double String::as_double(double def) const { if (Length <= 0) return def; Data[Length] = '\0'; return atof(Data); } String String::sub(int start, int len) const { if (start > Length) return 0; if (len > Length - start) len = Length - start; return String(Data + start, len); } String String::sub(int start) const { return sub(start, Length - start); } int String::indexOf(const char *str) const { char *c; // // Set the first char after string end to zero to prevent finding // substrings including symbols after actual end of string // if (!Allocated) return -1; Data[Length] = '\0'; /* OLD CODE: for (i = 0; i < Length; i++) */ #ifdef HAVE_STRSTR if ((c = strstr(Data, str)) != NULL) return(c -Data); #else int len = strlen(str); int i; for (i = 0; i <= Length-len; i++) { if (strncmp(&Data[i], str, len) == 0) return i; } #endif return -1; } int String::indexOf(char ch) const { int i; for (i = 0; i < Length; i++) { if (Data[i] == ch) return i; } return -1; } int String::indexOf(char ch, int pos) const { if (pos >= Length) return -1; for (int i = pos; i < Length; i++) { if (Data[i] == ch) return i; } return -1; } int String::lastIndexOf(char ch, int pos) const { if (pos >= Length) return -1; while (pos >= 0) { if (Data[pos] == ch) return pos; pos--; } return -1; } int String::lastIndexOf(char ch) const { return lastIndexOf(ch, Length - 1); } #ifdef NOINLINE String &String::operator << (const char *str) { append(str); return *this; } String &String::operator << (char ch) { append(&ch, 1); return *this; } #endif String &String::operator << (int i) { char str[20]; sprintf(str, "%d", i); append(str); return *this; } String &String::operator << (unsigned int i) { char str[20]; sprintf(str, "%u", i); append(str); return *this; } String &String::operator << (long l) { char str[20]; sprintf(str, "%ld", l); append(str); return *this; } String &String::operator << (const String &s) { append(s.get(), s.length()); return *this; } char String::operator >> (char c) { c = '\0'; if (Allocated && Length) { c = Data[Length - 1]; Data[Length - 1] = '\0'; Length--; } return c; } int String::lowercase() { int converted = 0; for (int i = 0; i < Length; i++) { if (isupper((unsigned char)Data[i])) { Data[i] = tolower((unsigned char)Data[i]); converted++; } } return converted; } int String::uppercase() { int converted = 0; for (int i = 0; i < Length; i++) { if (islower((unsigned char)Data[i])) { Data[i] = toupper((unsigned char)Data[i]); converted++; } } return converted; } void String::replace(char c1, char c2) { for (int i = 0; i < Length; i++) if (Data[i] == c1) Data[i] = c2; } int String::remove(const char *chars) { if (Length <= 0) return 0; char *good, *bad; int skipped = 0; good = bad = Data; for (int i = 0; i < Length; i++) { if (strchr(chars, *bad)) skipped++; else *good++ = *bad; bad++; } Length -= skipped; return skipped; } String &String::chop(int n) { Length -= n; if (Length < 0) Length = 0; return *this; } String &String::chop(char ch) { while (Length > 0 && Data[Length - 1] == ch) Length--; return *this; } String &String::chop(const char *str) { while (Length > 0 && strchr(str, Data[Length - 1])) Length--; return *this; } void String::Serialize(String &dest) { dest.append((char *) &Length, sizeof(Length)); dest.append(get(), Length); } void String::Deserialize(String &source, int &index) { memcpy((char *) &Length, (char *) source.get() + index, sizeof(Length)); index += sizeof(Length); allocate_fix_space(Length); copy_data_from(source.get() + index, Length); index += Length; } //------------------------------------------------------------------------ // Non member operators. // String operator + (const String &a, const String &b) { String result(a, a.length() + b.length()); result.append(b); return result; } int operator == (const String &a, const String &b) { if (a.Length != b.Length) return 0; return a.compare(b) == 0; } int operator != (const String &a, const String &b) { return a.compare(b) != 0; } int operator < (const String &a, const String &b) { return a.compare(b) == -1; } int operator > (const String &a, const String &b) { return a.compare(b) == 1; } int operator <= (const String &a, const String &b) { return a.compare(b) <= 0; } int operator >= (const String &a, const String &b) { return a.compare(b) >= 0; } #ifndef NOSTREAM ostream &operator << (ostream &o, const String &s) { o.write(s.Data, s.length()); return o; } #endif /* NOSTREAM */ //------------------------------------------------------------------------ // Private Methods. // void String::copy_data_from(const char *s, int len, int dest_offset) { memcpy(Data + dest_offset, s, len); } void String::allocate_space(int len) { len++; // In case we want to add a null. if (len <= Allocated) return; if (Allocated) delete [] Data; Allocated = MinimumAllocationSize; while (Allocated < len) Allocated <<= 1; Data = new char[Allocated]; } void String::allocate_fix_space(int len) { len++; // In case we want to add a null. if (len <= Allocated) return; if (Allocated) delete [] Data; Allocated = len; if (Allocated < MinimumAllocationSize) Allocated = MinimumAllocationSize; Data = new char[Allocated]; } void String::reallocate_space(int len) { char *old_data = 0; int old_data_len = 0; if (Allocated) { old_data = Data; old_data_len = Length; Allocated = 0; } allocate_space(len); if (old_data) { copy_data_from(old_data, old_data_len); delete [] old_data; } } void String::copy(const char *s, int len, int allocation_hint) { if (len == 0 || allocation_hint == 0) return; // We're not actually copying anything! allocate_fix_space(allocation_hint); Length = len; copy_data_from(s, len); } #ifndef NOSTREAM void String::debug(ostream &o) { o << "Length: " << Length << " Allocated: " << Allocated << " Data: " << ((void*) Data) << " '" << *this << "'\n"; } #endif /* NOSTREAM */ int String::readLine(FILE *in) { Length = 0; allocate_fix_space(2048); while (fgets(Data + Length, Allocated - Length, in)) { Length += strlen(Data + Length); if (Length == 0) continue; if (Data[Length - 1] == '\n') { // // A full line has been read. Return it. // chop('\n'); return 1; } if (Allocated > Length + 1) { // // Not all available space filled. Probably EOF? // continue; } // // Only a partial line was read. Increase available space in // string and read some more. // reallocate_space(Allocated << 1); } chop('\n'); return Length > 0; } #ifndef NOSTREAM istream &operator >> (istream &in, String &line) { line.Length = 0; line.allocate_fix_space(2048); for (;;) { in.clear(); in.getline(line.Data + line.Length, line.Allocated - line.Length); line.Length += strlen(line.Data + line.Length); // if read whole line, or eof, or read fewer chars than the max... if (!in.fail() || in.eof() || line.Length + 1 < line.Allocated) break; // // Only a partial line was read. Increase available space in // string and read some more. // line.reallocate_space(line.Allocated << 1); } return in; } #endif /* NOSTREAM */