// SciTE - Scintilla based Text Editor /** @file SString.h ** A simple string class. **/ // Copyright 1998-2001 by Neil Hodgson // The License.txt file describes the conditions under which this software may be distributed. #ifndef SSTRING_H #define SSTRING_H // These functions are implemented because each platform calls them something different. int CompareCaseInsensitive(const char *a, const char *b); int CompareNCaseInsensitive(const char *a, const char *b, size_t len); bool EqualCaseInsensitive(const char *a, const char *b); // Define another string class. // While it would be 'better' to use std::string, that doubles the executable size. // An SString may contain embedded nul characters. /** * @brief A simple string class. * * Hold the length of the string for quick operations, * can have a buffer bigger than the string to avoid too many memory allocations and copies. * May have embedded zeroes as a result of @a substitute, but relies too heavily on C string * functions to allow reliable manipulations of these strings, other than simple appends, etc. **/ class SString { public: /** Type of string lengths (sizes) and positions (indexes). */ typedef size_t lenpos_t; /** Out of bounds value indicating that the string argument should be measured. */ enum { measure_length=0xffffffffU}; private: char *s; ///< The C string lenpos_t sSize; ///< The size of the buffer, less 1: ie. the maximum size of the string lenpos_t sLen; ///< The size of the string in s lenpos_t sizeGrowth; ///< Minimum growth size when appending strings enum { sizeGrowthDefault = 64 }; bool grow(lenpos_t lenNew) { while (sizeGrowth * 6 < lenNew) { sizeGrowth *= 2; } char *sNew = new char[lenNew + sizeGrowth + 1]; if (sNew) { if (s) { memcpy(sNew, s, sLen); delete []s; } s = sNew; s[sLen] = '\0'; sSize = lenNew + sizeGrowth; } return sNew != 0; } SString &assign(const char *sOther, lenpos_t sSize_=measure_length) { if (!sOther) { sSize_ = 0; } else if (sSize_ == measure_length) { sSize_ = strlen(sOther); } if (sSize > 0 && sSize_ <= sSize) { // Does not allocate new buffer if the current is big enough if (s && sSize_) { memcpy(s, sOther, sSize_); } s[sSize_] = '\0'; sLen = sSize_; } else { delete []s; s = StringAllocate(sOther, sSize_); if (s) { sSize = sSize_; // Allow buffer bigger than real string, thus providing space to grow sLen = strlen(s); } else { sSize = sLen = 0; } } return *this; } public: SString() : s(0), sSize(0), sLen(0), sizeGrowth(sizeGrowthDefault) { } SString(const SString &source) : sizeGrowth(sizeGrowthDefault) { s = StringAllocate(source.s); sSize = sLen = (s) ? strlen(s) : 0; } SString(const char *s_) : sizeGrowth(sizeGrowthDefault) { s = StringAllocate(s_); sSize = sLen = (s) ? strlen(s) : 0; } SString(const char *s_, lenpos_t first, lenpos_t last) : sizeGrowth(sizeGrowthDefault) { // note: expects the "last" argument to point one beyond the range end (a la STL iterators) s = StringAllocate(s_ + first, last - first); sSize = sLen = (s) ? strlen(s) : 0; } SString(int i) : sizeGrowth(sizeGrowthDefault) { char number[32]; sprintf(number, "%0d", i); s = StringAllocate(number); sSize = sLen = (s) ? strlen(s) : 0; } SString(double d, int precision) : sizeGrowth(sizeGrowthDefault) { char number[32]; sprintf(number, "%.*f", precision, d); s = StringAllocate(number); sSize = sLen = (s) ? strlen(s) : 0; } ~SString() { delete []s; s = 0; sSize = 0; sLen = 0; } void clear() { if (s) { *s = '\0'; } sLen = 0; } /** Size of buffer. */ lenpos_t size() const { if (s) return sSize; else return 0; } /** Size of string in buffer. */ lenpos_t length() const { return sLen; } SString &operator=(const char *source) { return assign(source); } SString &operator=(const SString &source) { if (this != &source) { assign(source.c_str()); } return *this; } bool operator==(const SString &sOther) const { if ((s == 0) && (sOther.s == 0)) return true; if ((s == 0) || (sOther.s == 0)) return false; return strcmp(s, sOther.s) == 0; } bool operator!=(const SString &sOther) const { return !operator==(sOther); } bool operator==(const char *sOther) const { if ((s == 0) && (sOther == 0)) return true; if ((s == 0) || (sOther == 0)) return false; return strcmp(s, sOther) == 0; } bool operator!=(const char *sOther) const { return !operator==(sOther); } bool contains(char ch) { if (s && *s) return strchr(s, ch) != 0; else return false; } void setsizegrowth(lenpos_t sizeGrowth_) { sizeGrowth = sizeGrowth_; } const char *c_str() const { if (s) return s; else return ""; } /** Give ownership of buffer to caller which must use delete[] to free buffer. */ char *detach() { char *sRet = s; s = 0; sSize = 0; sLen = 0; return sRet; } char operator[](lenpos_t i) const { if (s && i < sSize) // Or < sLen? Depends on the use, both are OK return s[i]; else return '\0'; } SString substr(lenpos_t subPos, lenpos_t subLen=measure_length) const { if (subPos >= sLen) { return SString(); // return a null string if start index is out of bounds } if ((subLen == measure_length) || (subPos + subLen > sLen)) { subLen = sLen - subPos; // can't substr past end of source string } return SString(s, subPos, subPos + subLen); } SString &lowercase(lenpos_t subPos = 0, lenpos_t subLen=measure_length) { if ((subLen == measure_length) || (subPos + subLen > sLen)) { subLen = sLen - subPos; // don't apply past end of string } for (lenpos_t i = subPos; i < subPos + subLen; i++) { if (s[i] < 'A' || s[i] > 'Z') continue; else s[i] = static_cast(s[i] - 'A' + 'a'); } return *this; } SString &append(const char *sOther, lenpos_t sLenOther=measure_length, char sep = '\0') { if (!sOther) { return *this; } if (sLenOther == measure_length) { sLenOther = strlen(sOther); } int lenSep = 0; if (sLen && sep) { // Only add a separator if not empty lenSep = 1; } lenpos_t lenNew = sLen + sLenOther + lenSep; // Conservative about growing the buffer: don't do it, unless really needed if ((lenNew + 1 < sSize) || (grow(lenNew))) { if (lenSep) { s[sLen] = sep; sLen++; } memcpy(&s[sLen], sOther, sLenOther); sLen += sLenOther; s[sLen] = '\0'; } return *this; } SString &operator+=(const char *sOther) { return append(sOther, static_cast(measure_length)); } SString &operator+=(const SString &sOther) { return append(sOther.s, sOther.sLen); } SString &operator+=(char ch) { return append(&ch, 1); } SString &appendwithseparator(const char *sOther, char sep) { return append(sOther, strlen(sOther), sep); } SString &insert(lenpos_t pos, const char *sOther, lenpos_t sLenOther=measure_length) { if (!sOther) { return *this; } if (sLenOther == measure_length) { sLenOther = strlen(sOther); } lenpos_t lenNew = sLen + sLenOther; // Conservative about growing the buffer: don't do it, unless really needed if ((lenNew + 1 < sSize) || grow(lenNew)) { lenpos_t moveChars = sLen - pos + 1; for (lenpos_t i = moveChars; i > 0; i--) { s[pos + sLenOther + i - 1] = s[pos + i - 1]; } memcpy(s + pos, sOther, sLenOther); sLen = lenNew; } return *this; } /** Remove @a len characters from the @a pos position, included. * Characters at pos + len and beyond replace characters at pos. * If @a len is 0, or greater than the length of the string * starting at @a pos, the string is just truncated at @a pos. */ void remove(lenpos_t pos, lenpos_t len) { if (len < 1 || pos + len >= sLen) { s[pos] = '\0'; sLen = pos; } else { for (lenpos_t i = pos; i < sLen - len + 1; i++) { s[i] = s[i+len]; } sLen -= len; } } SString &change(lenpos_t pos, char ch) { if (pos >= sLen) { // character changed must be in string bounds return *this; } *(s + pos) = ch; return *this; } /** Read an integral numeric value from the string. */ int value() const { if (s) return atoi(s); else return 0; } int search(const char *sFind, lenpos_t start=0) const { if (start < sLen) { const char *sFound = strstr(s + start, sFind); if (sFound) { return sFound - s; } } return -1; } bool contains(const char *sFind) { return search(sFind) >= 0; } int substitute(char chFind, char chReplace) { int c = 0; char *t = s; while (t) { t = strchr(t, chFind); if (t) { *t = chReplace; t++; c++; } } return c; } int substitute(const char *sFind, const char *sReplace) { int c = 0; lenpos_t lenFind = strlen(sFind); lenpos_t lenReplace = strlen(sReplace); int posFound = search(sFind); while (posFound >= 0) { remove(posFound, lenFind); insert(posFound, sReplace, lenReplace); posFound = search(sFind, posFound + lenReplace); c++; } return c; } int remove(const char *sFind) { return substitute(sFind, ""); } /** * Duplicate a C string. * Allocate memory of the given size, or big enough to fit the string if length isn't given; * then copy the given string in the allocated memory. * @return the pointer to the new string */ static char *StringAllocate( const char *s, ///< The string to duplicate lenpos_t len=measure_length) ///< The length of memory to allocate. Optional. { if (s == 0) { return 0; } if (len == measure_length) { len = strlen(s); } char *sNew = new char[len + 1]; if (sNew) { memcpy(sNew, s, len); sNew[len] = '\0'; } return sNew; } }; /** * Duplicate a C string. * Allocate memory of the given size, or big enough to fit the string if length isn't given; * then copy the given string in the allocated memory. * @return the pointer to the new string */ inline char *StringDup( const char *s, ///< The string to duplicate SString::lenpos_t len=SString::measure_length) ///< The length of memory to allocate. Optional. { return SString::StringAllocate(s, len); } #endif