1// string.cpp -- 2// $Id: string.cpp 1230 2007-03-09 15:58:53Z jcw $ 3// This is part of Metakit, see http://www.equi4.com/metakit.html 4 5/** @file 6 * yet another string implementation 7 */ 8 9#include "header.h" 10 11/* these definitions could be used instead of header.h ... 12#define q4_UNIV 1 13#define d4_inline 14#include "mk4str.h" 15#define d4_reentrant 16#define d4_assert(x) 17 */ 18 19#if q4_UNIV // until end of source 20///////////////////////////////////////////////////////////////////////////// 21 22#include <ctype.h> 23#include <stdarg.h> 24#include <stdio.h> 25#include <stdlib.h> 26 27#ifdef _AIX 28#include <strings.h> 29#endif 30 31#if !q4_INLINE 32#include "mk4str.inl" 33#endif 34 35///////////////////////////////////////////////////////////////////////////// 36 37#if q4_WINCE 38 39// MS C/C++ has this handy stricmp: a case-insensitive version of strcmp 40// This version only works with 7-bit ASCII characters 0x00 through 0x7F 41 42static int strcasecmp(const char *p1, const char *p2) { 43 int c1, c2; 44 45#ifdef d4_USE_UNOPTIMIZED_CODE 46 do { 47 c1 = tolower(*p1++); 48 c2 = tolower(*p2++); 49 } while (c1 != 0 && c1 == c2); 50#else 51 do { 52 c1 = *p1++; 53 c2 = *p2++; 54 } while (c1 != 0 && (c1 == c2 || tolower(c1) == tolower(c2))); 55 56 c1 = tolower(c1); 57 c2 = tolower(c2); 58#endif 59 60 return c1 - c2; 61} 62 63const char *strrchr(const char *p, char ch) { 64 const char *q = 0; 65 while (*p) 66 if (*p++ == ch) 67 q = p; 68 return q; 69} 70 71#elif q4_MSVC || q4_WATC || q4_BORC || (q4_MWCW && __MWERKS__ < 0x3000) 72#define strcasecmp stricmp 73#endif 74 75///////////////////////////////////////////////////////////////////////////// 76// 77// This string class implement functionality which is very similar to that 78// provided by the CString class of the Microsoft Framework Classes (MFC). 79// 80// There are also several major differences: 81// 82// 1) This class uses reference counting to avoid massive copying. 83// Consequently, function return as well as assignment is very fast. 84// 2) Strings of up to 255 bytes can contain any data, even null bytes. 85// Longer strings can not contain any null bytes past position 255. 86// 3) This class can produce a "const char*" without overhead, but it 87// can also cast to the byte-counted "const unsigned char*" used 88// everywhere in Macintosh applications (as StringPtr, Str255, etc). 89// 4) This source code is not derived from Microsoft's code in any way. 90// 91// A good way to use this class, is to always use c4_String for function 92// return values and "const [unsigned] char*" for all parameters. Together, 93// these two choices will remove the need for nearly any messy casts. 94// 95// Note: MFC 4.0 has now adopted refcounts, and is a good alternative to 96// this code (but a bit bulkier, it also has Unicode support). 97 98// 2001-11-27, stop releasing nullvec, to allow MT use 99d4_reentrant static unsigned char *nullVec = 0; 100 101static int fInc(unsigned char *p) { 102 ++ *p; 103 if (*p) 104 return 1; 105 106 -- *p; 107 return 0; 108} 109 110inline static void fDec(unsigned char *p) { 111 -- *p; 112 if (! *p && p != nullVec) 113 delete [] p; 114} 115 116c4_String::c4_String(char ch, int n /* =1 */) { 117 if (n < 0) 118 n = 0; 119 120 _value = new unsigned char[n + 3]; 121 122 _value[0] = 1; // see Init() member 123 memset(_value + 2, ch, n); 124 _value[1] = (unsigned char)(n <= 255 ? n : 255); 125 _value[n + 2] = 0; 126} 127 128c4_String::c4_String(const char *p) { 129 Init(p, p != 0 ? strlen(p): 0); 130} 131 132c4_String::c4_String(const c4_String &s) { 133 if (fInc(s._value)) 134 _value = s._value; 135 else 136 Init(s.Data(), s.GetLength()); 137} 138 139c4_String::~c4_String() { 140 fDec(_value); 141} 142 143const c4_String &c4_String::operator = (const c4_String &s) { 144 unsigned char *oldVal = _value; 145 if (fInc(s._value)) 146 _value = s._value; 147 else 148 Init(s.Data(), s.GetLength()); 149 fDec(oldVal); 150 151 return *this; 152} 153 154c4_String operator + (const c4_String &a, const c4_String &b) { 155 const int aCnt = a.GetLength(); 156 int sum = aCnt + b.GetLength(); 157 158 c4_String result('\0', sum); // set up correct size, then fix contents 159 memcpy(result._value + 2, a.Data(), aCnt); 160 memcpy(result._value + 2+aCnt, b.Data(), sum - aCnt); 161 162 return result; 163} 164 165void c4_String::Init(const void *p, int n) { 166 if (p == NULL || n <= 0) { 167 // Optimization to significantly speed-up init of empty strings: 168 // share a common entry, which avoids *LOTS* of tiny mem allocs. 169 // 170 // Especially "new [...] c4_String" will benefit a lot, as well as: 171 // 172 // c4_String s; // this would have caused a new allocation 173 // s = ... // then immediately drops the count back 174 // 175 // 2001/11/27: changed to never release this empty vector, for MT use 176 // the new logic is to completely ignore its ref count 177 178 if (!nullVec) { 179 // obtain a valid new empty string buffer to keep around 180 unsigned char *nv = new unsigned char[3]; 181 nv[0] = nv[1] = nv[2] = 0; 182 // only set static value after item is fully inited (avoid MT race) 183 nullVec = nv; 184 } 185 186 _value = nullVec; // use this buffer as our empty string 187 return ; // done... that was quick, wasn't it? 188 } 189 190 _value = new unsigned char[n + 3]; 191 192 _value[0] = 1; // many assumptions here: set the reference count to 1 193 194 if (n > 0) 195 memcpy(_value + 2, p, n); 196 _value[1] = (unsigned char)(n <= 255 ? n : 255); 197 _value[n + 2] = 0; 198} 199 200int c4_String::FullLength()const { 201 int n = _value[1]; 202 return n < 255 ? n : n + strlen((const char*)_value + 2+255); 203} 204 205c4_String c4_String::Mid(int nFirst, int nCount)const { 206 if (nFirst >= GetLength()) 207 return c4_String(); 208 209 if (nFirst + nCount > GetLength()) 210 nCount = GetLength() - nFirst; 211 212 if (nFirst == 0 && nCount == GetLength()) 213 return *this; 214 215 return c4_String(Data() + nFirst, nCount); 216} 217 218c4_String c4_String::Left(int nCount)const { 219 if (nCount >= GetLength()) 220 return *this; 221 222 return c4_String(Data(), nCount); 223} 224 225c4_String c4_String::Right(int nCount)const { 226 if (nCount >= GetLength()) 227 return *this; 228 229 return c4_String(Data() + GetLength() - nCount, nCount); 230} 231 232bool operator == (const c4_String &a, const c4_String &b) { 233 return a._value == b._value || a.GetLength() == b.GetLength() && memcmp 234 (a.Data(), b.Data(), a.GetLength()) == 0; 235} 236 237int c4_String::Compare(const char *str)const { 238 return Data() == str ? 0 : strcmp(Data(), str); 239} 240 241int c4_String::CompareNoCase(const char *str)const { 242 return Data() == str ? 0 : strcasecmp(Data(), str); 243} 244 245int c4_String::Find(char ch)const { 246 const char *p = strchr(Data(), ch); 247 return p != 0 ? p - Data(): - 1; 248} 249 250int c4_String::ReverseFind(char ch)const { 251 const char *p = strrchr(Data(), ch); 252 return p != 0 ? p - Data(): - 1; 253} 254 255int c4_String::FindOneOf(const char *set)const { 256 const char *p = strpbrk(Data(), set); 257 return p != 0 ? p - Data(): - 1; 258} 259 260int c4_String::Find(const char *sub)const { 261 const char *p = strstr(Data(), sub); 262 return p != 0 ? p - Data(): - 1; 263} 264 265c4_String c4_String::SpanIncluding(const char *set)const { 266 return Left(strspn(Data(), set)); 267} 268 269c4_String c4_String::SpanExcluding(const char *set)const { 270 return Left(strcspn(Data(), set)); 271} 272 273///////////////////////////////////////////////////////////////////////////// 274#endif // q4_UNIV 275