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