/****************************************************************************
**
** Copyright (C) 1997-2015 by Dimitri van Heesch.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation under the terms of the GNU General Public License is hereby
** granted. No representations are made about the suitability of this software
** for any purpose. It is provided "as is" without express or implied warranty.
** See the GNU General Public License for more details.
**
** Note: this is a reimplementation of the qcstring.h that came with
** an Qt version 2.2.3. For short strings it stores the string data inside
** the object. For long strings it uses a separate array with reference counting.
**
**********************************************************************/
#ifndef QCSTRING_H
#define QCSTRING_H
#ifndef QT_H
#include "qarray.h"
#endif // QT_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if !defined(_OS_WIN32_) || defined(__MINGW32__)
#include <stdint.h>
#endif
#if defined(_OS_SUN_) && defined(_CC_GNU_)
#include <strings.h>
#endif
#include <assert.h>
class QGString;
/*****************************************************************************
Fixes and workarounds for some platforms
*****************************************************************************/
#if defined(_OS_HPUX_)
// HP-UX has badly defined strstr() etc.
// ### fix in 3.0: change hack_* to qt_hack_*
// by the way HP-UX is probably right, the standard has evolved and
// we'll have to adapt to it
inline char *hack_strstr( const char *s1, const char *s2 )
{ return (char *)strstr(s1, s2); }
inline char *hack_strchr( const char *s, int c )
{ return (char *)strchr(s, c); }
inline char *hack_strrchr( const char *s, int c )
{ return (char *)strrchr(s, c); }
#define strstr(s1,s2) hack_strstr((s1),(s2))
#define strchr(s,c) hack_strchr((s),(c))
#define strrchr(s,c) hack_strrchr((s),(c))
#endif
/*****************************************************************************
Safe and portable C string functions; extensions to standard string.h
*****************************************************************************/
Q_EXPORT void *qmemmove( void *dst, const void *src, uint len );
#if defined(_OS_SUN_) || defined(_CC_OC_)
#define memmove(s1,s2,n) qmemmove((s1),(s2),(n))
#endif
#if defined(_OS_WIN32_)
#define qsnprintf _snprintf
#else
#define qsnprintf snprintf
#endif
Q_EXPORT char *qstrdup( const char * );
Q_EXPORT inline uint cstrlen( const char *str )
{ return (uint)strlen(str); }
Q_EXPORT inline uint qstrlen( const char *str )
{ return str ? (uint)strlen(str) : 0; }
Q_EXPORT inline char *cstrcpy( char *dst, const char *src )
{ return strcpy(dst,src); }
Q_EXPORT inline char *qstrcpy( char *dst, const char *src )
{ return src ? strcpy(dst, src) : 0; }
Q_EXPORT char * qstrncpy(char *src,const char *dst, uint len);
Q_EXPORT inline int cstrcmp( const char *str1, const char *str2 )
{ return strcmp(str1,str2); }
Q_EXPORT inline int qstrcmp( const char *str1, const char *str2 )
{ return (str1 && str2) ? strcmp(str1,str2) : (int)((intptr_t)str2 - (intptr_t)str1); }
Q_EXPORT inline int cstrncmp( const char *str1, const char *str2, uint len )
{ return strncmp(str1,str2,len); }
Q_EXPORT inline int qstrncmp( const char *str1, const char *str2, uint len )
{ return (str1 && str2) ? strncmp(str1,str2,len) :
(int)((intptr_t)str2 - (intptr_t)str1); }
Q_EXPORT int qstricmp( const char *str1, const char *str2 );
Q_EXPORT int qstrnicmp( const char *str1, const char *str2, uint len );
/*****************************************************************************
QByteArray class
*****************************************************************************/
#if defined(Q_TEMPLATEDLL)
template class Q_EXPORT QArray<char>;
#endif
typedef QArray<char> QByteArray;
/*****************************************************************************
QByteArray stream functions
*****************************************************************************/
#ifndef QT_NO_DATASTREAM
Q_EXPORT QDataStream &operator<<( QDataStream &, const QByteArray & );
Q_EXPORT QDataStream &operator>>( QDataStream &, QByteArray & );
#endif
class QRegExp;
/** This is an alternative implementation of QCString. It provides basically
* the same functions but uses reference counting and copy on write.
*/
class QCString
{
public:
/** creates an empty string */
QCString() : m_rep()
{
}
/** destroys the string */
~QCString()
{
}
/** makes a copy of a string. */
QCString( const QCString &s ) : m_rep(s.m_rep)
{
}
/** creates a string with room for size characters
* @param[in] size the number of character to allocate (including the 0-terminator)
*/
explicit QCString( int size ) : m_rep(size)
{
}
/** creates a string from a plain C string.
* @param[in] str A zero terminated C string. When 0 an empty string is created.
*/
QCString( const char *str ) : m_rep(str)
{
}
/** creates a string from \a str and copies over the first \a maxlen characters. */
QCString( const char *str, uint maxlen ) : m_rep(str,maxlen)
{
}
/** replaces the contents by that of string \a s. */
QCString &operator=( const QCString &s )
{
m_rep = s.m_rep;
return *this;
}
/** replaces the contents by that of C string \a str. */
QCString &operator=( const char *str)
{
m_rep = str;
return *this;
}
/** Returns TRUE iff the string is empty. Equivalent to isEmpty(). */
bool isNull() const
{
return m_rep.isEmpty();
}
/** Returns TRUE iff the string is empty */
bool isEmpty() const
{
return m_rep.isEmpty();
}
/** Returns the length of the string, excluding the 0-terminator. Equivalent to size(). */
uint length() const
{
return m_rep.length();
}
/** Returns the length of the string, excluding the 0-terminator. */
uint size() const
{
return m_rep.length();
}
/** Returns a pointer to the contents of the string in the form of a 0-terminated C string */
const char *data() const
{
return m_rep.data();
}
/** Returns a writable pointer to the data.
* @warning if the string is shared it will modifying the string directly and
* this will overwrite all copies as well!
*/
char *rawData() const
{
return m_rep.rawData();
}
/** Resizes the string to hold \a newlen characters
* (this value should include the 0-terminator). If the string is enlarged the contents will
* be left unmodified.
*/
bool resize( uint newlen )
{
m_rep.resize(newlen);
return TRUE;
}
/** Truncates the string at position \a pos. */
bool truncate( uint pos )
{
return resize(pos+1);
}
/** Fills a string with a predefined character
* @param[in] c the character used to fill the string with.
* @param[in] len the number of character to fill. Use -1 to fill the whole string.
* @note the string will be resized to contain \a len characters. The contents of the
* string will be lost.
*/
bool fill( char c, int len = -1 )
{
m_rep.fill(c,len);
return TRUE;
}
/** Returns a deep copy of the string. */
QCString copy() const
{
if (length()==0) return QCString();
QCString cs(length()+1);
memcpy(cs.rawData(),data(),length());
return cs;
}
QCString &sprintf( const char *format, ... );
int find( char c, int index=0, bool cs=TRUE ) const;
int find( const char *str, int index=0, bool cs=TRUE ) const;
int find( const QCString &str, int index=0, bool cs=TRUE ) const;
int find( const QRegExp &rx, int index=0 ) const;
int findRev( char c, int index=-1, bool cs=TRUE) const;
int findRev( const char *str, int index=-1, bool cs=TRUE) const;
int findRev( const QRegExp &rx, int index=-1 ) const;
int contains( char c, bool cs=TRUE ) const;
int contains( const char *str, bool cs=TRUE ) const;
int contains( const QRegExp &rx ) const;
bool stripPrefix(const char *prefix);
QCString left( uint len ) const;
QCString right( uint len ) const;
QCString mid( uint index, uint len=0xffffffff) const;
QCString lower() const;
QCString upper() const;
QCString stripWhiteSpace() const;
QCString simplifyWhiteSpace() const;
QCString &assign( const char *str );
QCString &insert( uint index, const char *s );
QCString &insert( uint index, char c);
QCString &append( const char *s );
QCString &prepend( const char *s );
QCString &remove( uint index, uint len );
QCString &replace( uint index, uint len, const char *s);
QCString &replace( const QRegExp &rx, const char *str );
short toShort( bool *ok=0 ) const;
ushort toUShort( bool *ok=0 ) const;
int toInt( bool *ok=0 ) const;
uint toUInt( bool *ok=0 ) const;
long toLong( bool *ok=0 ) const;
ulong toULong( bool *ok=0 ) const;
uint64 toUInt64( bool *ok=0 ) const;
QCString &setNum(short n);
QCString &setNum(ushort n);
QCString &setNum(int n);
QCString &setNum(uint n);
QCString &setNum(long n);
QCString &setNum(ulong n);
/** Converts the string to a plain C string */
operator const char *() const
{
return (const char *)data();
}
/** Appends string \a str to this string and returns a reference to the result. */
QCString &operator+=( const char *str )
{
if (!str) return *this;
int len1 = length();
int len2 = (int)strlen(str);
resize(len1+len2+1);
memcpy(rawData()+len1,str,len2);
return *this;
}
/** Appends character \a c to this string and returns a reference to the result. */
QCString &operator+=( char c )
{
int len = length();
resize(len+2);
rawData()[len]=c;
return *this;
}
/** Returns a reference to the character at index \a i. */
char &at( uint i) const
{
return m_rep.at(i);
}
/** Indexing operator. Equavalent to at(). */
char &operator[]( int i ) const
{
return m_rep.at((uint)i);
}
private:
struct LSData;
// long string representation
struct LongStringRep
{
uchar isShort; // : 1; // should be shared with ShortStringRep
//uchar : 7;
LSData *d;
};
#define SHORT_STR_CAPACITY ((int)sizeof(LongStringRep)-2)
#define SHORT_STR_MAX_LEN (SHORT_STR_CAPACITY-1)
// short string representation
struct ShortStringRep
{
uchar isShort; // : 1; // should be shared with LongStringRep
uchar len; // : 7;
char str[SHORT_STR_CAPACITY]; // size including 0-terminator
};
// ref counting string header
struct LSHeader
{
int len; // length of string without 0 terminator
int refCount; // -1=leaked, 0=one ref & non-cost, n>0, n+1 refs, const
};
// ref counting string data and methods
struct LSData : public LSHeader
{
char *toStr()
{
return (char*)(this+1); // string data starts after the header
}
// creates a LSData item with room for size bytes (which includes the 0 terminator!)
// if size is zero, an empty string will be created.
static LSData *create(int size)
{
LSData *data;
data = (LSData*)malloc(sizeof(LSHeader)+size);
data->len = size-1;
data->refCount = 0;
data->toStr()[size-1] = 0;
return data;
}
// remove out reference to the data. Frees memory if no more users
void dispose()
{
if (--refCount<0) free(this);
}
// resizes LSData so it can hold size bytes (which includes the 0 terminator!)
// Since this is for long strings only, size should be > SHORT_STR_CAPACITY
static LSData *resize(LSData *d,int size)
{
if (d->len>0 && d->refCount==0) // non-const, non-empty
{
d = (LSData*)realloc(d,sizeof(LSHeader)+size);
d->len = size-1;
d->toStr()[size-1] = 0;
return d;
}
else // need to make a copy
{
LSData *newData = LSData::create(size);
int len = d->len;
if (len>=size) len=size-1;
memcpy(newData->toStr(),d->toStr(),len);
newData->toStr()[len]=0;
d->dispose();
return newData;
}
}
};
class StringRep
{
public:
StringRep()
{
initEmpty();
}
void initEmpty()
{
u.s.isShort=TRUE;
u.s.len=0;
//memset(u.s.str,0,SHORT_STR_CAPACITY);
}
~StringRep()
{
if (!u.s.isShort)
{
u.l.d->dispose();
}
}
StringRep(const StringRep &s)
{
if (&s!=this)
{
u.s.isShort = s.u.s.isShort;
if (s.u.s.isShort)
{
u.s.len = s.u.s.len;
memcpy(u.s.str,s.u.s.str,s.u.s.len+1);
}
else
{
u.l.d = s.u.l.d;
u.l.d->refCount++;
}
}
else // self-assignment
{
u = s.u; // avoid uninitialized warning from gcc
}
}
StringRep(int size)
{
u.s.isShort = size<=SHORT_STR_CAPACITY;
if (size<=SHORT_STR_CAPACITY) // init short string
{
if (size>0)
{
u.s.len = size-1;
u.s.str[size-1]='\0';
}
else
{
u.s.len = 0;
}
}
else // int long string
{
u.l.d = LSData::create(size);
}
}
StringRep(const char *str)
{
if (str)
{
int len = (int)strlen(str);
u.s.isShort = len<SHORT_STR_CAPACITY;
if (len<SHORT_STR_CAPACITY)
{
u.s.len = len;
qstrncpy(u.s.str,str,SHORT_STR_CAPACITY);
}
else
{
u.l.d = LSData::create(len+1);
memcpy(u.l.d->toStr(),str,u.l.d->len);
}
}
else // create empty string
{
initEmpty();
}
}
StringRep( const char *str, uint maxlen )
{
if (str && maxlen>0)
{
uint len=(uint)strlen(str);
if (len>maxlen) len=maxlen;
u.s.isShort = len<=SHORT_STR_MAX_LEN;
if (u.s.isShort)
{
u.s.len = len;
memcpy(u.s.str,str,len);
u.s.str[len]='\0';
}
else
{
u.l.d = LSData::create(len+1);
memcpy(u.l.d->toStr(),str,len);
}
}
else // create empty string
{
initEmpty();
}
}
StringRep &operator=(const StringRep &s)
{
if (&s!=this)
{
if (!u.s.isShort)
{
u.l.d->dispose();
}
u.s.isShort = s.u.s.isShort;
if (u.s.isShort) // copy by value
{
u.s.len = s.u.s.len;
memcpy(u.s.str,s.u.s.str,s.u.s.len+1);
}
else // copy by reference
{
u.l.d = s.u.l.d;
u.l.d->refCount++;
}
}
else // self-assignment
{
u = s.u; // avoid uninitialized warning from gcc
}
return *this;
}
StringRep &operator=(const char *str)
{
if (!u.s.isShort)
{
u.l.d->dispose();
}
if (str)
{
int len = (int)strlen(str);
u.s.isShort = len<SHORT_STR_CAPACITY;
if (len<SHORT_STR_CAPACITY)
{
u.s.len = len;
qstrncpy(u.s.str,str,SHORT_STR_CAPACITY);
}
else
{
u.l.d = LSData::create(len+1);
memcpy(u.l.d->toStr(),str,len);
}
}
else
{
initEmpty();
}
return *this;
}
bool isEmpty() const
{
return u.s.isShort && u.s.len==0;
}
uint length() const
{
uint l = u.s.isShort ? u.s.len : u.l.d->len;
return l;
}
const char *data() const
{
if (u.s.isShort)
{
return u.s.len==0 ? 0 : u.s.str;
}
else
{
return u.l.d->len==0 ? 0 : u.l.d->toStr();
}
}
char *rawData() const
{
if (u.s.isShort)
{
return u.s.len==0 ? 0 : (char*)u.s.str;
}
else
{
//assert(u.l.d->refCount==0); // string may not be shared when accessed raw
return u.l.d->len==0 ? 0 : u.l.d->toStr();
}
}
char &at(int i) const
{
if (u.s.isShort)
{
return (char&)u.s.str[i];
}
else
{
return u.l.d->toStr()[i];
}
}
bool resize( uint newlen )
{
if (u.s.isShort && newlen<=SHORT_STR_CAPACITY) // resize short string
{
if (newlen>0)
{
u.s.len = newlen-1;
u.s.str[newlen-1]='\0';
}
else // string becomes empty
{
initEmpty();
}
}
else if (u.s.isShort) // turn short string into long string
{
StringRep tmp = *this;
u.s.isShort=FALSE;
u.l.d = LSData::create(newlen);
if (tmp.u.s.len>0)
{
memcpy(u.l.d->toStr(),tmp.u.s.str,tmp.u.s.len+1);
}
else
{
u.l.d->toStr()[0]='\0';
}
}
else if (!u.s.isShort && newlen<=SHORT_STR_CAPACITY) // turn long string into short string
{
if (newlen>0)
{
StringRep tmp(newlen); // copy short part into tmp buffer
memcpy(tmp.u.s.str,u.l.d->toStr(),newlen-1);
tmp.u.s.str[newlen-1]='\0';
u.l.d->dispose();
u.s = tmp.u.s;
}
else
{
u.l.d->dispose();
initEmpty();
}
}
else // resize long string
{
u.l.d = u.l.d->resize(u.l.d,newlen);
}
return TRUE;
}
bool fill( char c, int len )
{
if (len<0) len=length();
if (!u.s.isShort) // detach from shared string
{
resize(len+1);
}
else if (len!=(int)length())
{
if (len>0)
{
resize(len+1);
}
}
if (len>0)
{
memset(rawData(),c,len);
}
return TRUE;
}
private:
union ShortOrLongStringSelector
{
ShortStringRep s;
LongStringRep l;
} u;
};
StringRep m_rep;
};
/*****************************************************************************
QCString stream functions
*****************************************************************************/
#ifndef QT_NO_DATASTREAM
Q_EXPORT QDataStream &operator<<( QDataStream &, const QCString & );
Q_EXPORT QDataStream &operator>>( QDataStream &, QCString & );
#endif
/*****************************************************************************
QCString non-member operators
*****************************************************************************/
Q_EXPORT inline bool operator==( const QCString &s1, const QCString &s2 )
{ return qstrcmp(s1.data(),s2.data()) == 0; }
Q_EXPORT inline bool operator==( const QCString &s1, const char *s2 )
{ return qstrcmp(s1.data(),s2) == 0; }
Q_EXPORT inline bool operator==( const char *s1, const QCString &s2 )
{ return qstrcmp(s1,s2.data()) == 0; }
Q_EXPORT inline bool operator!=( const QCString &s1, const QCString &s2 )
{ return qstrcmp(s1.data(),s2.data()) != 0; }
Q_EXPORT inline bool operator!=( const QCString &s1, const char *s2 )
{ return qstrcmp(s1.data(),s2) != 0; }
Q_EXPORT inline bool operator!=( const char *s1, const QCString &s2 )
{ return qstrcmp(s1,s2.data()) != 0; }
Q_EXPORT inline bool operator<( const QCString &s1, const QCString& s2 )
{ return qstrcmp(s1.data(),s2.data()) < 0; }
Q_EXPORT inline bool operator<( const QCString &s1, const char *s2 )
{ return qstrcmp(s1.data(),s2) < 0; }
Q_EXPORT inline bool operator<( const char *s1, const QCString &s2 )
{ return qstrcmp(s1,s2.data()) < 0; }
Q_EXPORT inline bool operator<=( const QCString &s1, const char *s2 )
{ return qstrcmp(s1.data(),s2) <= 0; }
Q_EXPORT inline bool operator<=( const char *s1, const QCString &s2 )
{ return qstrcmp(s1,s2.data()) <= 0; }
Q_EXPORT inline bool operator>( const QCString &s1, const char *s2 )
{ return qstrcmp(s1.data(),s2) > 0; }
Q_EXPORT inline bool operator>( const char *s1, const QCString &s2 )
{ return qstrcmp(s1,s2.data()) > 0; }
Q_EXPORT inline bool operator>=( const QCString &s1, const char *s2 )
{ return qstrcmp(s1.data(),s2) >= 0; }
Q_EXPORT inline bool operator>=( const char *s1, const QCString &s2 )
{ return qstrcmp(s1,s2.data()) >= 0; }
Q_EXPORT inline QCString operator+( const QCString &s1, const QCString &s2 )
{
QCString tmp(s1);
tmp += s2;
return tmp;
}
inline QCString operator+( const QCString &s1, const QGString &s2 );
inline QCString operator+( const QGString &s1, const QCString &s2 );
Q_EXPORT inline QCString operator+( const QCString &s1, const char *s2 )
{
QCString tmp(s1);
tmp += s2;
return tmp;
}
Q_EXPORT inline QCString operator+( const char *s1, const QCString &s2 )
{
QCString tmp(s1);
tmp += s2;
return tmp;
}
Q_EXPORT inline QCString operator+( const QCString &s1, char c2 )
{
QCString tmp( s1.data() );
tmp += c2;
return tmp;
}
Q_EXPORT inline QCString operator+( char c1, const QCString &s2 )
{
QCString tmp;
tmp += c1;
tmp += s2;
return tmp;
}
inline const char *qPrint(const char *s)
{
if (s) return s; else return "";
}
inline const char *qPrint(const QCString &s)
{
if (!s.isEmpty()) return s.data(); else return "";
}
#endif // QCSTRING_H