You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
305 lines
7.2 KiB
305 lines
7.2 KiB
// string.cpp --
|
|
// $Id$
|
|
// This is part of Metakit, see http://www.equi4.com/metakit/
|
|
|
|
/** @file
|
|
* yet another string implementation
|
|
*/
|
|
|
|
#include "header.h"
|
|
|
|
/* these definitions could be used instead of header.h ...
|
|
#define q4_UNIV 1
|
|
#define d4_inline
|
|
#include "mk4str.h"
|
|
#define d4_reentrant
|
|
#define d4_assert(x)
|
|
*/
|
|
|
|
#if q4_UNIV // until end of source
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef _AIX
|
|
#include <strings.h>
|
|
#endif
|
|
|
|
#if !q4_INLINE
|
|
#include "mk4str.inl"
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if q4_MSVC || q4_WATC || q4_BORC || (q4_MWCW && __MWERKS__ < 0x3000)
|
|
#define strcasecmp stricmp
|
|
#elif q4_WINCE
|
|
|
|
// MS C/C++ has this handy stricmp: a case-insensitive version of strcmp
|
|
// This version only works with 7-bit ASCII characters 0x00 through 0x7F
|
|
|
|
static int stricmp(const char* p1, const char* p2)
|
|
{
|
|
int c1, c2;
|
|
|
|
#ifdef d4_USE_UNOPTIMIZED_CODE
|
|
do
|
|
{
|
|
c1 = tolower(*p1++);
|
|
c2 = tolower(*p2++);
|
|
} while (c1 != 0 && c1 == c2);
|
|
#else
|
|
do
|
|
{
|
|
c1 = *p1++;
|
|
c2 = *p2++;
|
|
} while (c1 != 0 && (c1 == c2 || tolower(c1) == tolower(c2)));
|
|
|
|
c1 = tolower(c1);
|
|
c2 = tolower(c2);
|
|
#endif
|
|
|
|
return c1 - c2;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if q4_WINCE
|
|
const char* strrchr(const char* p, char ch)
|
|
{
|
|
const char* q = 0;
|
|
while (*p)
|
|
if (*p++ == ch)
|
|
q = p;
|
|
return q;
|
|
}
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// This string class implement functionality which is very similar to that
|
|
// provided by the CString class of the Microsoft Framework Classes (MFC).
|
|
//
|
|
// There are also several major differences:
|
|
//
|
|
// 1) This class uses reference counting to avoid massive copying.
|
|
// Consequently, function return as well as assignment is very fast.
|
|
// 2) Strings of up to 255 bytes can contain any data, even null bytes.
|
|
// Longer strings can not contain any null bytes past position 255.
|
|
// 3) This class can produce a "const char*" without overhead, but it
|
|
// can also cast to the byte-counted "const unsigned char*" used
|
|
// everywhere in Macintosh applications (as StringPtr, Str255, etc).
|
|
// 4) This source code is not derived from Microsoft's code in any way.
|
|
//
|
|
// A good way to use this class, is to always use c4_String for function
|
|
// return values and "const [unsigned] char*" for all parameters. Together,
|
|
// these two choices will remove the need for nearly any messy casts.
|
|
//
|
|
// Note: MFC 4.0 has now adopted refcounts, and is a good alternative to
|
|
// this code (but a bit bulkier, it also has Unicode support).
|
|
|
|
// 2001-11-27, stop releasing nullvec, to allow MT use
|
|
d4_reentrant static unsigned char* nullVec = 0;
|
|
|
|
static int fInc(unsigned char* p)
|
|
{
|
|
++*p;
|
|
if (*p)
|
|
return 1;
|
|
|
|
--*p;
|
|
return 0;
|
|
}
|
|
|
|
inline static void fDec(unsigned char* p)
|
|
{
|
|
--*p;
|
|
if (!*p && p != nullVec)
|
|
delete [] p;
|
|
}
|
|
|
|
c4_String::c4_String (char ch, int n /* =1 */)
|
|
{
|
|
if (n < 0)
|
|
n = 0;
|
|
|
|
_value = new unsigned char [n + 3];
|
|
|
|
_value[0] = 1; // see Init() member
|
|
memset(_value + 2, ch, n);
|
|
_value[1] = (unsigned char) (n <= 255 ? n : 255);
|
|
_value[n+2] = 0;
|
|
}
|
|
|
|
c4_String::c4_String (const char* p)
|
|
{
|
|
Init(p, p != 0 ? strlen(p) : 0);
|
|
}
|
|
|
|
c4_String::c4_String (const c4_String& s)
|
|
{
|
|
if (fInc(s._value))
|
|
_value = s._value;
|
|
else
|
|
Init(s.Data(), s.GetLength());
|
|
}
|
|
|
|
c4_String::~c4_String ()
|
|
{
|
|
fDec(_value);
|
|
}
|
|
|
|
const c4_String& c4_String::operator= (const c4_String& s)
|
|
{
|
|
unsigned char* oldVal = _value;
|
|
if (fInc(s._value))
|
|
_value = s._value;
|
|
else
|
|
Init(s.Data(), s.GetLength());
|
|
fDec(oldVal);
|
|
|
|
return *this;
|
|
}
|
|
|
|
c4_String operator+ (const c4_String& a, const c4_String& b)
|
|
{
|
|
const int aCnt = a.GetLength();
|
|
int sum = aCnt + b.GetLength();
|
|
|
|
c4_String result ('\0', sum); // set up correct size, then fix contents
|
|
memcpy(result._value + 2, a.Data(), aCnt);
|
|
memcpy(result._value + 2 + aCnt, b.Data(), sum - aCnt);
|
|
|
|
return result;
|
|
}
|
|
|
|
void c4_String::Init(const void* p, int n)
|
|
{
|
|
if (p == NULL || n <= 0)
|
|
{
|
|
// Optimization to significantly speed-up init of empty strings:
|
|
// share a common entry, which avoids *LOTS* of tiny mem allocs.
|
|
//
|
|
// Especially "new [...] c4_String" will benefit a lot, as well as:
|
|
//
|
|
// c4_String s; // this would have caused a new allocation
|
|
// s = ... // then immediately drops the count back
|
|
//
|
|
// 2001/11/27: changed to never release this empty vector, for MT use
|
|
// the new logic is to completely ignore its ref count
|
|
|
|
if (!nullVec)
|
|
{
|
|
// obtain a valid new empty string buffer to keep around
|
|
unsigned char* nv = new unsigned char [3];
|
|
nv[0] = nv[1] = nv[2] = 0;
|
|
// only set static value after item is fully inited (avoid MT race)
|
|
nullVec = nv;
|
|
}
|
|
|
|
_value = nullVec; // use this buffer as our empty string
|
|
return; // done... that was quick, wasn't it?
|
|
}
|
|
|
|
_value = new unsigned char [n + 3];
|
|
|
|
_value[0] = 1; // many assumptions here: set the reference count to 1
|
|
|
|
if (n > 0)
|
|
memcpy(_value + 2, p, n);
|
|
_value[1] = (unsigned char) (n <= 255 ? n : 255);
|
|
_value[n+2] = 0;
|
|
}
|
|
|
|
int c4_String::FullLength() const
|
|
{
|
|
int n = _value[1];
|
|
return n < 255 ? n : n + strlen((const char*) _value + 2 + 255);
|
|
}
|
|
|
|
c4_String c4_String::Mid(int nFirst, int nCount) const
|
|
{
|
|
if (nFirst >= GetLength())
|
|
return c4_String ();
|
|
|
|
if (nFirst + nCount > GetLength())
|
|
nCount = GetLength() - nFirst;
|
|
|
|
if (nFirst == 0 && nCount == GetLength())
|
|
return *this;
|
|
|
|
return c4_String (Data() + nFirst, nCount);
|
|
}
|
|
|
|
c4_String c4_String::Left(int nCount) const
|
|
{
|
|
if (nCount >= GetLength())
|
|
return *this;
|
|
|
|
return c4_String (Data(), nCount);
|
|
}
|
|
|
|
c4_String c4_String::Right(int nCount) const
|
|
{
|
|
if (nCount >= GetLength())
|
|
return *this;
|
|
|
|
return c4_String (Data() + GetLength() - nCount, nCount);
|
|
}
|
|
|
|
bool operator== (const c4_String& a, const c4_String& b)
|
|
{
|
|
return a._value == b._value || a.GetLength() == b.GetLength() &&
|
|
memcmp(a.Data(), b.Data(), a.GetLength()) == 0;
|
|
}
|
|
|
|
int c4_String::Compare(const char* str) const
|
|
{
|
|
return Data() == str ? 0 : strcmp(Data(), str);
|
|
}
|
|
|
|
int c4_String::CompareNoCase(const char* str) const
|
|
{
|
|
return Data() == str ? 0 : strcasecmp(Data(), str);
|
|
}
|
|
|
|
int c4_String::Find(char ch) const
|
|
{
|
|
const char* p = strchr(Data(), ch);
|
|
return p != 0 ? p - Data() : -1;
|
|
}
|
|
|
|
int c4_String::ReverseFind(char ch) const
|
|
{
|
|
const char* p = strrchr(Data(), ch);
|
|
return p != 0 ? p - Data() : -1;
|
|
}
|
|
|
|
int c4_String::FindOneOf(const char* set) const
|
|
{
|
|
const char* p = strpbrk(Data(), set);
|
|
return p != 0 ? p - Data() : -1;
|
|
}
|
|
|
|
int c4_String::Find(const char* sub) const
|
|
{
|
|
const char* p = strstr(Data(), sub);
|
|
return p != 0 ? p - Data() : -1;
|
|
}
|
|
|
|
c4_String c4_String::SpanIncluding(const char* set) const
|
|
{
|
|
return Left(strspn(Data(), set));
|
|
}
|
|
|
|
c4_String c4_String::SpanExcluding(const char* set) const
|
|
{
|
|
return Left(strcspn(Data(), set));
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
#endif // q4_UNIV
|