1
0
mirror of https://github.com/stefanocasazza/ULib.git synced 2025-09-28 19:05:55 +08:00
ULib/include/ulib/string.h
2015-04-24 20:09:44 +02:00

1573 lines
48 KiB
C++

// ============================================================================
//
// = LIBRARY
// ULib - c++ library
//
// = FILENAME
// string.h - Components for manipulating sequences of characters
//
// = AUTHOR
// Stefano Casazza
//
// ============================================================================
#ifndef ULIB_USTRING_H
#define ULIB_USTRING_H 1
#include <ulib/internal/common.h>
#include <ulib/base/hash.h>
#include <ulib/base/utility.h>
// macro for constant string like "xxx"
#define U_STRING_RFIND(x,str) (x).rfind(str,U_NOT_FOUND,U_CONSTANT_SIZE(str)) // string constant
#define U_STRING_FIND(x,start,str) (x).find(str,(start),U_CONSTANT_SIZE(str)) // string constant
#define U_STRING_FIND_EXT(x,start,str,n) (x).find(str,(start),U_CONSTANT_SIZE(str),n) // string constant
#define U_STRING_FINDNOCASE_EXT(x,start,str,n) (x).findnocase(str,(start),U_CONSTANT_SIZE(str),n) // string constant
#define U_STRING_FROM_CONSTANT(str) UString(str,U_CONSTANT_SIZE(str)) // string constant
#define U_ENDS_WITH(x,str) u_endsWith((x).data(),(x).size(),U_CONSTANT_TO_PARAM(str)) // string constant
// UString content and size
#define U_STRING_TO_PARAM(str) (str).data(),(str).size()
#define U_STRING_TO_TRACEX(str,start,count) count,(str).data()+start
#ifdef DEBUG_DEBUG
#define U_STRING_TO_TRACE(str) U_min(128,(str).size()),(str).data()
#else
#define U_STRING_TO_TRACE(str) (str).size(), (str).data()
#endif
/**
* UStringRep: string representation
* ---------------------------------------------------------------------------------------------------------
* The string object requires only one allocation. The allocation function which gets a block of raw bytes
* and with room enough and constructs a UStringRep object at the front. Invariants:
* ---------------------------------------------------------------------------------------------------------
* 1. string really contains length+1 characters; last is set to '\0' for sure on call to c_str()
* 2. capacity >= length - allocated memory is always capacity+1
* 3. references has two states:
* 0: 1 reference
* n>0: n+1 references
* 4. all fields == 0 is an empty string, given the extra storage beyond-the-end for a null terminator;
* thus, the shared empty string representation needs no constructor
* ---------------------------------------------------------------------------------------------------------
* Note that the UStringRep object is a POD so that you can have a static "empty string" UStringRep object
* already "constructed" before static constructors have run. The reference-count encoding is chosen so that
* a 0 indicates 1 reference, so you never try to destroy the empty-string UStringRep object
* ---------------------------------------------------------------------------------------------------------
*/
#ifdef DEBUG
# define U_STRINGREP_FROM_CONSTANT(c_str) (void*)U_CHECK_MEMORY_SENTINEL, 0, 0, U_CONSTANT_SIZE(c_str), 0, 0, c_str
#elif defined(U_SUBSTR_INC_REF)
# define U_STRINGREP_FROM_CONSTANT(c_str) 0, U_CONSTANT_SIZE(c_str), 0, 0, c_str
#else
# define U_STRINGREP_FROM_CONSTANT(c_str) U_CONSTANT_SIZE(c_str), 0, 0, c_str
#endif
#define U_STRING_FROM_STRINGREP_STORAGE_WITH_VAR(var,n) UString(&(var[n]))
#define U_STRING_FROM_STRINGREP_STORAGE(n) U_STRING_FROM_STRINGREP_STORAGE_WITH_VAR(stringrep_storage,n)
class UCDB;
class URDB;
class UHTTP;
class UValue;
class UString;
class UStringExt;
class UOrmDriver;
class UHttpPlugIn;
class Application;
class UServer_Base;
class UHashMapNode;
class URDBClient_Base;
class UClientImage_Base;
template <class T> class UVector;
template <class T> class UHashMap;
template <class T> class UJsonTypeHandler;
class U_EXPORT UStringRep {
public:
// Check for memory error
U_MEMORY_TEST
// Allocator e Deallocator
U_MEMORY_ALLOCATOR
U_MEMORY_DEALLOCATOR
#if defined(U_SUBSTR_INC_REF) || defined(DEBUG)
UStringRep* parent; // manage substring to increment reference of source string
# ifdef DEBUG
int32_t child; // manage substring to capture event 'DEAD OF SOURCE STRING WITH CHILD ALIVE'...
static bool check_dead_of_source_string_with_child_alive;
# endif
#endif
// --------------------------
// references has two states:
// --------------------------
// 0: 1 reference
// n>0: n+1 references
// --------------------------
bool uniq() const
{
U_TRACE(0, "UStringRep::uniq()")
U_CHECK_MEMORY
bool result = (references == 0); // NB: 0 -> 1 reference
U_RETURN(result);
}
void hold()
{
U_TRACE(0, "UStringRep::hold()")
U_CHECK_MEMORY
U_INTERNAL_DUMP("this = %p parent = %p references = %d child = %d", this, parent, references, child)
++references;
}
void release(); // NB: we don't use delete (dtor) because add a deallocation to the destroy object process...
// NB: we don't use new (ctor) because we want an allocation with more space for string data...
static UStringRep* create(uint32_t length, uint32_t capacity, const char* ptr);
// Size and Capacity
uint32_t size() const { return _length; }
uint32_t length() const { return _length; }
bool empty() const
{
U_TRACE(0, "UStringRep::empty()")
U_CHECK_MEMORY
U_RETURN(_length == 0);
}
uint32_t capacity() const
{
U_TRACE(0, "UStringRep::capacity()")
U_CHECK_MEMORY
U_RETURN(_capacity);
}
bool writeable() const
{
U_TRACE(0, "UStringRep::writeable()")
U_CHECK_MEMORY
U_RETURN(_capacity != 0); // mode: 0 -> const
}
bool isNullTerminated() const { return (str [_length] == '\0'); }
void setNullTerminated() const { ((char*)str)[_length] = '\0'; }
uint32_t space() const
{
U_TRACE(0, "UStringRep::space()")
U_CHECK_MEMORY
if ((int32_t)_capacity > 0) U_RETURN(_capacity - _length);
U_RETURN(0);
}
uint32_t remain(const char* ptr) const
{
U_TRACE(0, "UStringRep::remain(%p)", ptr)
U_CHECK_MEMORY
U_INTERNAL_ASSERT((str + ((int32_t)_capacity > 0 ? _capacity : _length)) >= ptr)
U_RETURN((str + _length) - ptr);
}
uint32_t distance(const char* ptr) const
{
U_TRACE(0, "UStringRep::distance(%p)", ptr)
U_CHECK_MEMORY
U_INTERNAL_ASSERT((str + ((int32_t)_capacity > 0 ? _capacity : _length)) >= ptr)
U_RETURN(ptr - str);
}
static uint32_t fold(uint32_t pos, uint32_t off, uint32_t sz)
{
U_TRACE(0, "UStringRep::fold(%u,%u,%u)", pos, off, sz)
uint32_t dist = (sz - pos),
newoff = (off < dist ? off : dist);
U_RETURN(newoff);
}
uint32_t fold(uint32_t pos, uint32_t off) const { return fold(pos, off, _length); }
// The maximum number of individual char elements of an individual string is determined by max_size()
static uint32_t max_size() { return U_STRING_MAX_SIZE; }
// C-Style String
char* data() const { return (char*)str; }
void copy(char* s, uint32_t n = U_NOT_FOUND, uint32_t pos = 0) const;
// ELEMENT ACCESS
char* begin() { return (char*)str; }
const char* begin() const { return str; }
char* end() { return (char*)(str + _length); }
const char* end() const { return str + _length; }
char* rbegin() { return (char*)(str + _length - 1); }
const char* rbegin() const { return str + _length - 1; }
char* rend() { return (char*)(str + 1); }
const char* rend() const { return str + 1; }
char at(uint32_t pos) const
{
U_TRACE(0, "UStringRep::at(%u)", pos)
U_CHECK_MEMORY
U_INTERNAL_ASSERT_MINOR(pos, _length)
U_RETURN(str[pos]);
}
char first_char() const
{
U_TRACE(0, "UStringRep::first_char()")
U_CHECK_MEMORY
U_INTERNAL_ASSERT_MAJOR(_length, 0)
U_RETURN(str[0]);
}
char last_char() const
{
U_TRACE(0, "UStringRep::last_char()")
U_CHECK_MEMORY
U_INTERNAL_ASSERT_MAJOR(_length, 0)
U_RETURN(str[_length - 1]);
}
char operator[](uint32_t pos) const { return str[pos]; }
const char* c_pointer(uint32_t pos) const { return (str + pos); }
// Compare
int compare(const char* s, uint32_t n) const
{
U_TRACE(0, "UStringRep::compare(%.*S,%u)", n, s, n)
U_CHECK_MEMORY
int r = memcmp(str, s, U_min(_length, n));
U_INTERNAL_DUMP("str = %.*S", U_min(_length, n), str)
if (r == 0) r = (_length - n);
U_RETURN(r);
}
int compare(const UStringRep* rep) const { return compare(rep->str, rep->_length); }
int compare(uint32_t pos, uint32_t n1, const char* s, uint32_t n2) const
{
U_TRACE(0, "UStringRep::compare(%u,%u,%.*S,%u)", pos, n1, n2, s, n2)
U_CHECK_MEMORY
U_INTERNAL_ASSERT((pos + n1) <= _length)
int r = memcmp(str + pos, s, U_min(n1, n2));
if (r == 0) r = (n1 - n2);
U_RETURN(r);
}
int compare(const UStringRep* rep, uint32_t depth) const __pure;
// Compare with ignore case
int comparenocase(const char* s, uint32_t n) const
{
U_TRACE(0, "UStringRep::comparenocase(%.*S,%u)", n, s, n)
U_CHECK_MEMORY
int r = u__strncasecmp(str, s, U_min(_length, n));
U_INTERNAL_DUMP("str = %.*S", U_min(_length, n), str)
if (r == 0) r = (_length - n);
U_RETURN(r);
}
int comparenocase(const UStringRep* rep) const { return comparenocase(rep->str, rep->_length); }
// Equal
bool equal(const UStringRep* rep) const { return equal(rep->str, rep->_length); }
bool equal(const char* s, uint32_t n) const __pure;
// Equal with ignore case
bool equal(const UStringRep* rep, bool ignore_case) const { return equal(rep->str, rep->_length, ignore_case); }
bool equal(const char* s, uint32_t n, bool ignore_case) const __pure;
// Substring
bool isSubStringOf(UStringRep* rep) const __pure;
UStringRep* substr(const char* t, uint32_t tlen);
UStringRep* substr(uint32_t pos, uint32_t n) { return substr(str + pos, n); }
// Assignment
void size_adjust()
{
U_TRACE(0+256, "UStringRep::size_adjust()")
U_CHECK_MEMORY
# ifdef DEBUG
if (references)
{
string_rep_share = this;
U_DUMP_OBJECT("shared with this", checkIfReferences)
U_INTERNAL_ASSERT_MSG(false, "CANNOT ADJUST SIZE OF A REFERENCED STRING...")
}
# endif
_length = u__strlen(str, __PRETTY_FUNCTION__);
U_INTERNAL_ASSERT(invariant())
}
void size_adjust(uint32_t value)
{
U_TRACE(0+256, "UStringRep::size_adjust(%u)", value)
U_CHECK_MEMORY
# ifdef DEBUG
if (references)
{
string_rep_share = this;
U_DUMP_OBJECT("shared with this", checkIfReferences)
U_INTERNAL_ASSERT_MSG(false, "CANNOT ADJUST SIZE OF A REFERENCED STRING...")
}
# endif
_length = value;
U_INTERNAL_ASSERT(invariant())
}
void size_adjust(const char* ptr)
{
U_TRACE(0+256, "UStringRep::size_adjust(%p)", ptr)
U_CHECK_MEMORY
# ifdef DEBUG
if (references)
{
string_rep_share = this;
U_DUMP_OBJECT("shared with this", checkIfReferences)
U_INTERNAL_ASSERT_MSG(false, "CANNOT ADJUST SIZE OF A REFERENCED STRING...")
}
# endif
_length = distance(ptr);
U_INTERNAL_ASSERT(invariant())
}
void size_adjust_force()
{
U_TRACE(0, "UStringRep::size_adjust_force()")
U_CHECK_MEMORY
_length = u__strlen(str, __PRETTY_FUNCTION__);
U_INTERNAL_ASSERT(invariant())
}
void size_adjust_force(uint32_t value)
{
U_TRACE(0, "UStringRep::size_adjust_force(%u)", value)
U_CHECK_MEMORY
_length = value;
U_INTERNAL_ASSERT(invariant())
}
void replace( const char* s, uint32_t n);
static void assign(UStringRep*& rep, const char* s, uint32_t n);
#ifdef DEBUG
bool invariant() const;
const char* dump(bool reset) const;
#endif
// EXTENSION
bool isText(uint32_t pos) const __pure;
bool isUTF8(uint32_t pos) const __pure;
bool isUTF16(uint32_t pos) const __pure;
bool isBase64(uint32_t pos) const __pure;
bool isBinary(uint32_t pos) const __pure;
bool isEndHeader(uint32_t pos) const __pure;
bool isWhiteSpace(uint32_t pos) const __pure;
bool isPrintable(uint32_t pos, bool bline = false) const __pure;
uint32_t findWhiteSpace(uint32_t pos) const __pure;
// UTF8 <--> ISO Latin 1
static UStringRep* toUTF8(const unsigned char* t, uint32_t tlen);
static UStringRep* fromUTF8(const unsigned char* t, uint32_t tlen);
bool strtob() const __pure;
#ifdef HAVE_STRTOF
float strtof() const;
#endif
double strtod() const;
#ifdef HAVE_STRTOLD
long double strtold() const;
#endif
long strtol(int base = 0) const;
#ifdef HAVE_STRTOULL
int64_t strtoll(int base = 0) const;
#endif
uint32_t hash(bool ignore_case = false) const
{
U_TRACE(0, "UStringRep::hash(%b)", ignore_case)
U_CHECK_MEMORY
uint32_t result = u_hash((unsigned char*)str, _length, ignore_case);
U_INTERNAL_ASSERT_MAJOR(result, 0)
U_RETURN(result);
}
// for constant string
void trim();
// if the string is quoted...
bool isQuoted(unsigned char c) const
{
U_TRACE(0, "UStringRep::isQuoted(%C)", c)
U_CHECK_MEMORY
bool result = (str[0] == c &&
str[_length-1] == c);
U_RETURN(result);
}
// ...unquote it
void unQuote()
{
U_TRACE(0, "UStringRep::unQuote()")
U_CHECK_MEMORY
U_INTERNAL_ASSERT_MAJOR(_length, 2)
U_INTERNAL_ASSERT_EQUALS(_capacity, 0)
++str;
_length -= 2;
}
// STREAM
#ifdef U_STDCPP_ENABLE
void write(ostream& os) const;
friend ostream& operator<<(ostream& os, const UStringRep& r) { r.write(os); return os; }
#endif
// JSON
void fromValue(UStringRep* r);
protected:
uint32_t _length,
_capacity, // [0 const | -1 mmap]...
references; // NB: must be here, see string_rep_null...
const char* str;
// ----------------> maybe unnamed array of char...
// The following storage is init'd to 0 by the linker, resulting (carefully) in an empty string with zero(=>1) reference...
static UStringRep* string_rep_null;
#ifdef DEBUG
static int32_t max_child;
static UStringRep* parent_destroy;
static UStringRep* string_rep_share;
static bool checkIfChild( const char* name_class, const void* ptr_object);
static bool checkIfReferences(const char* name_class, const void* ptr_object);
#endif
private:
UStringRep()
{
U_TRACE(0, "UStringRep::UStringRep()")
u__memcpy(this, string_rep_null, sizeof(UStringRep), __PRETTY_FUNCTION__);
}
explicit UStringRep(const char* t, uint32_t tlen); // NB: to use with caution...
~UStringRep();
#ifdef U_COMPILER_DELETE_MEMBERS
UStringRep(const UStringRep&) = delete;
UStringRep& operator=(const UStringRep&) = delete;
#else
UStringRep(const UStringRep&) {}
UStringRep& operator=(const UStringRep&) { return *this; }
#endif
void shift(ptrdiff_t diff)
{
U_TRACE(0, "UStringRep::shift(%p)", diff)
U_CHECK_MEMORY
U_INTERNAL_ASSERT_DIFFERS(diff, 0)
U_INTERNAL_ASSERT_EQUALS(_capacity, 0) // mode: 0 -> const
str += diff;
U_INTERNAL_ASSERT_DIFFERS(str[0], 0)
U_INTERNAL_ASSERT(invariant())
}
void set(uint32_t length, uint32_t capacity, const char* ptr) U_NO_EXPORT;
friend class UCDB;
friend class URDB;
friend class UHTTP;
friend class UString;
friend class UStringExt;
friend class UOrmDriver;
friend class UHttpPlugIn;
friend class Application;
friend class UHashMapNode;
friend class UServer_Base;
friend class URDBClient_Base;
friend class UClientImage_Base;
friend void ULib_init();
friend struct UObjectIO;
template <class T> friend class UVector;
template <class T> friend class UHashMap;
template <class T> friend class UJsonTypeHandler;
template <class T> friend void u_construct(const T*, uint32_t);
};
class U_EXPORT UString {
public:
// Allocator e Deallocator
U_MEMORY_ALLOCATOR
U_MEMORY_DEALLOCATOR
static const UString* str_host;
static const UString* str_cookie;
static const UString* str_connection;
static const UString* str_user_agent;
static const UString* str_authorization;
static const UString* str_content_type;
static const UString* str_content_length;
static const UString* str_accept;
static const UString* str_accept_encoding;
static const UString* str_referer;
static const UString* str_X_Real_IP;
static const UString* str_Transfer_Encoding;
static const UString* str_X_Progress_ID;
static const UString* str_chunked;
static const UString* str_without_mac;
static const UString* str_encoding;
static const UString* str_user;
static const UString* str_name;
static const UString* str_localhost;
static const UString* str_http;
static const UString* str_filename;
static const UString* str_msg_rfc;
static const UString* str_txt_plain;
static const UString* str_address;
static const UString* str_ns;
static const UString* str_METHOD_NAME;
static const UString* str_RESPONSE_TYPE;
static const UString* str_xmlns;
static const UString* str_fault;
static const UString* str_boolean;
static const UString* str_byte;
static const UString* str_unsignedByte;
static const UString* str_short;
static const UString* str_unsignedShort;
static const UString* str_int;
static const UString* str_unsignedInt;
static const UString* str_long;
static const UString* str_unsignedLong;
static const UString* str_float;
static const UString* str_double;
static const UString* str_string;
static const UString* str_base64Binary;
static const UString* str_PORT;
static const UString* str_USER;
static const UString* str_SERVER;
static const UString* str_CA_FILE;
static const UString* str_CA_PATH;
static const UString* str_KEY_FILE;
static const UString* str_LOG_FILE;
static const UString* str_PASSWORD;
static const UString* str_PID_FILE;
static const UString* str_CERT_FILE;
static const UString* str_LOG_FILE_SZ;
static const UString* str_SOCKET_NAME;
static const UString* str_VERIFY_MODE;
static const UString* str_ENVIRONMENT;
static const UString* str_CLIENT_QUEUE_DIR;
static const UString* str_point;
static void str_allocate();
// null string (for container etc...)
static UString& getStringNull()
{
U_INTERNAL_ASSERT_EQUALS((bool)*string_null, false)
return *string_null;
}
protected:
static UString* string_null;
friend void ULib_init();
friend class UValue;
template <class T> friend class UVector;
friend class UStringExt;
friend class UClientImage_Base;
explicit UString(uint32_t len, uint32_t sz, char* ptr); // NB: for UStringExt::deflate()...
public:
// mutable
UStringRep* rep;
// SERVICES
char* data() const { return rep->data(); }
bool empty() const { return rep->empty(); }
operator bool() const { return (rep->_length != 0); }
uint32_t size() const { return rep->size(); }
uint32_t space() const { return rep->space(); }
uint32_t length() const { return rep->length(); }
uint32_t capacity() const { return rep->capacity(); }
static uint32_t max_size() { return U_STRING_MAX_SIZE; }
protected:
// in 'memory reference' distinction is made between set, copy, e assign...
void _set(UStringRep* r)
{
U_TRACE(0, "UString::_set(%p)", r)
U_INTERNAL_ASSERT_DIFFERS(rep,r)
rep->release();
rep = r;
U_CHECK_MEMORY_OBJECT(rep)
}
void _copy(UStringRep* r)
{
U_TRACE(0, "UString::_copy(%p)", r)
rep = r;
rep->hold();
U_CHECK_MEMORY_OBJECT(rep)
}
public:
void _assign(UStringRep* r)
{
U_TRACE(0, "UString::_assign(%p)", r)
// NB: works also int the case of (rep == r)...
r->hold(); // 1. take a copy of new resource
rep->release(); // 2. release existing resource
rep = r; // 3. bind copy to self
U_CHECK_MEMORY_OBJECT(rep)
}
// COSTRUTTORI
UString() : rep(UStringRep::string_rep_null)
{
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "", 0)
rep->hold();
U_INTERNAL_ASSERT(invariant())
}
explicit UString(UStringRep* r) : rep(r)
{
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "%p", r)
rep->hold();
U_INTERNAL_ASSERT(invariant())
}
explicit UString(ustringrep* r);
UString(const UString& str) : rep(str.rep)
{
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "%p", &str)
rep->hold();
U_INTERNAL_ASSERT(invariant())
}
explicit UString(const char* t);
explicit UString(const char* t, uint32_t tlen);
// NB: Avoid constructors with a single integer argument! Use the explicit keyword if you can't avoid them...
explicit UString(uint32_t n)
{
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "%u", n) // problem with sanitize address
rep = UStringRep::create(0U, n, 0);
U_INTERNAL_ASSERT(invariant())
}
explicit UString(uint32_t n, unsigned char c);
// Copy from string
explicit UString(void* t)
{
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "%S", (char*)t)
U_INTERNAL_ASSERT_POINTER(t)
uint32_t tlen = u__strlen((char*)t, __PRETTY_FUNCTION__);
rep = UStringRep::create(tlen, tlen, (const char*)t);
U_INTERNAL_ASSERT(invariant())
}
explicit UString(void* t, uint32_t tlen)
{
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "%.*S,%u", tlen, (char*)t, tlen)
U_INTERNAL_ASSERT_POINTER(t)
rep = UStringRep::create(tlen, tlen, (const char*)t);
U_INTERNAL_ASSERT(invariant())
}
explicit UString(const UString& str, uint32_t pos, uint32_t n = U_NOT_FOUND);
// SUBSTRING
explicit UString(UStringRep* _rep, const char* t, uint32_t tlen)
{
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "%p,%p,%u", _rep, t, tlen)
rep = _rep->substr(t, tlen);
U_INTERNAL_ASSERT(invariant())
}
explicit UString(UStringRep* _rep, uint32_t pos, uint32_t n = U_NOT_FOUND)
{
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "%p,%u,%u", _rep, pos, n)
rep = _rep->substr(pos, _rep->fold(pos, n));
U_INTERNAL_ASSERT(invariant())
}
UString substr(const char* t, uint32_t tlen) const
{
U_TRACE(0, "UString::substr(%.*S,%u)", tlen, t, tlen)
UString result(rep, t, tlen);
U_RETURN_STRING(result);
}
UString substr(uint32_t pos, uint32_t n = U_NOT_FOUND) const
{
U_TRACE(0, "UString::substr(%u,%u)", pos, n)
U_INTERNAL_ASSERT(pos <= rep->_length)
return substr(rep->str + pos, rep->fold(pos, n));
}
bool isSubStringOf(const UString& str) const { return rep->isSubStringOf(str.rep); }
// destructor
~UString()
{
U_TRACE_UNREGISTER_OBJECT(0, UString)
U_INTERNAL_ASSERT_POINTER(rep)
U_CHECK_MEMORY_OBJECT(rep)
rep->release();
}
// Replace
UString& replace(uint32_t pos, uint32_t n1, uint32_t n2, char c); // NB: unsigned char conflict with a uint32_t at the same parameter position...
UString& replace(uint32_t pos, uint32_t n1, const char* s, uint32_t n2);
UString& replace(uint32_t pos1, uint32_t n1, const UString& str, uint32_t pos2, uint32_t n2 = U_NOT_FOUND)
{
U_TRACE(0, "UString::replace(%u,%u,%p,%u,%u)", pos1, n1, &str, pos2, n2)
U_INTERNAL_ASSERT(pos2 <= str.size())
return replace(pos1, n1, str.data() + pos2, str.rep->fold(pos2, n2));
}
UString& replace(const char* s) { return replace(0U, size(), s, u__strlen(s, __PRETTY_FUNCTION__)); }
UString& replace(unsigned char c) { return replace(0U, size(), 1U, c); }
UString& replace(const UString& str) { return replace(0U, size(), U_STRING_TO_PARAM(str)); }
UString& replace(const char* s, uint32_t n) { return replace(0U, size(), s, n); }
UString& replace(uint32_t pos, uint32_t n, const char* s) { return replace(pos, n, s, u__strlen(s, __PRETTY_FUNCTION__)); }
UString& replace(uint32_t pos, uint32_t n, const UString& str) { return replace(pos, n, U_STRING_TO_PARAM(str)); }
// Assignment
// NB: assign() NON GARANTISCE PROPRIETA' DELLA STRINGA, replace() SI...
UString& assign(const char* s, uint32_t n)
{
U_TRACE(0, "UString::assign(%S,%u)", s, n)
UStringRep::assign(rep, s, n);
U_INTERNAL_ASSERT(invariant())
return *this;
}
UString& assign(const UString& str, uint32_t pos, uint32_t n = U_NOT_FOUND)
{
U_TRACE(0, "UString::assign(%p,%u,%u)", &str, pos, n)
U_INTERNAL_ASSERT(pos <= str.size())
return assign(str.data() + pos, str.rep->fold(pos, n));
}
UString& assign(const char* s);
UString& assign(const UString& str) { return assign(U_STRING_TO_PARAM(str)); }
UString& assign(uint32_t n, unsigned char c) { return replace(0U, size(), n, c); }
UString& operator=(const char* s) { return assign(s); }
UString& operator=(unsigned char c) { return assign(1U, c); }
UString& operator=(const UString& str);
void swap(UString& str)
{
U_TRACE(0, "UString::swap(%p)", &str)
UStringRep* tmp = rep;
rep = str.rep;
str.rep = tmp;
}
void swap(UString& lhs, UString& rhs) { lhs.swap(rhs); }
#ifdef U_COMPILER_RVALUE_REFS
# if !defined(__GNUC__) || defined(DEBUG) // g++ problem...!!!
UString(UString && str)
{
U_TRACE(0, "UString::UString(move)")
rep = str.rep;
str.rep = UStringRep::string_rep_null;
}
# endif
UString& operator=(UString && str)
{
U_TRACE(0, "UString::operator=(move)")
swap(str);
return *this;
}
#endif
void shift(ptrdiff_t diff) { rep->shift(diff); }
// Make room for a total of n element
bool reserve(uint32_t n); // NB: return true if has changed rep...
static void _reserve(UString& buffer, uint32_t n);
// Element access
const char* begin() const { return rep->begin(); }
const char* end() const { return rep->end(); }
const char* rbegin() const { return rep->rbegin(); }
const char* rend() const { return rep->rend(); }
__pure char at(uint32_t pos) const { return rep->at(pos); }
char operator[](uint32_t pos) const { return rep->operator[](pos); }
char* _begin()
{
U_TRACE(0, "UString::_begin()")
if (uniq() == false) duplicate();
return rep->begin();
}
char* _end()
{
U_TRACE(0, "UString::_end()")
if (uniq() == false) duplicate();
return rep->end();
}
char* _rbegin()
{
U_TRACE(0, "UString::_rbegin()")
if (uniq() == false) duplicate();
return rep->rbegin();
}
char* _rend()
{
U_TRACE(0, "UString::_rend()")
if (uniq() == false) duplicate();
return rep->rend();
}
// Modifiers
void push(unsigned char c) { (void) append(1U, c); }
void push_back(unsigned char c) { (void) append(1U, c); }
UString& append(uint32_t n, char c); // NB: unsigned char conflict with a uint32_t at the same parameter position...
UString& append(const char* s, uint32_t n);
UString& append(const UString& str, uint32_t pos, uint32_t n = U_NOT_FOUND)
{
U_TRACE(0, "UString::append(%p,%u,%u)", &str, pos, n)
U_INTERNAL_ASSERT(pos <= str.size())
return append(str.data() + pos, str.rep->fold(pos, n));
}
UString& append(const char* s);
UString& append(UStringRep* _rep);
UString& append(const UString& str);
UString& operator+=(const char* s) { return append(s, u__strlen(s, __PRETTY_FUNCTION__)); }
UString& operator+=(unsigned char c) { return append(1U, c); }
UString& operator+=(const UString& str);
// OPTMIZE APPEND (BUFFERED)
static char* ptrbuf;
static char* appbuf;
void _append(unsigned char c)
{
U_TRACE(0, "UString::_append(%C)", c)
U_INTERNAL_ASSERT_POINTER(ptrbuf)
U_INTERNAL_ASSERT_POINTER(appbuf)
if ((ptrbuf - appbuf) == 1024)
{
(void) append(appbuf, 1024);
ptrbuf = appbuf;
}
*ptrbuf++ = c;
}
void _append()
{
U_TRACE(0, "UString::_append()")
U_INTERNAL_ASSERT_POINTER(ptrbuf)
U_INTERNAL_ASSERT_POINTER(appbuf)
if (ptrbuf > appbuf)
{
(void) append(appbuf, ptrbuf - appbuf);
ptrbuf = appbuf;
}
}
// operator +
friend UString operator+(const UString& lhs, const char* rhs);
friend UString operator+(const UString& lhs, char rhs);
friend UString operator+(const UString& lhs, const UString& rhs);
friend UString operator+(char lhs, const UString& rhs);
friend UString operator+(const char* lhs, const UString& rhs);
UString& insert(uint32_t pos, const UString& str) { return replace(pos, 0, U_STRING_TO_PARAM(str)); }
UString& insert(uint32_t pos1, const UString& str, uint32_t pos2, uint32_t n = U_NOT_FOUND)
{
U_TRACE(0, "UString::insert(%u,%p,%u,%u)", pos1, &str, pos2, n)
U_INTERNAL_ASSERT(pos2 <= str.size())
return replace(pos1, 0, str.data() + pos2, str.rep->fold(pos2, n));
}
UString& insert(uint32_t pos, char c) { return replace(pos, 0, 1, c); }
UString& insert(uint32_t pos, uint32_t n, char c) { return replace(pos, 0, n, c); } // NB: uchar conflict with a uint32_t at the same parameter position
UString& insert(uint32_t pos, const char* s) { return replace(pos, 0, s, u__strlen(s, __PRETTY_FUNCTION__)); }
UString& insert(uint32_t pos, const char* s, uint32_t n) { return replace(pos, 0, s, n); }
void clear();
void resize(uint32_t n, unsigned char c = '\0');
// can shrink the space used (capacity)...
bool shrink();
UString& erase(uint32_t pos = 0, uint32_t n = U_NOT_FOUND);
// C-Style String
void setNullTerminated() const;
const char* c_str() const
{
U_TRACE(0, "UString::c_str()")
if (isNullTerminated() == false) setNullTerminated();
U_RETURN(rep->str);
}
char* c_strdup() const;
char* c_strndup(uint32_t pos = 0, uint32_t n = U_NOT_FOUND) const;
UString copy() const;
void copy(char* s, uint32_t n = U_NOT_FOUND, uint32_t pos = 0) const { rep->copy(s, n, pos); }
// STRING OPERATIONS
// The `find' function searches string for a specified string (possibly a single character) and returns
// its starting position. You can supply the parameter pos to specify the position where search must begin
uint32_t find(const char* s, uint32_t pos, uint32_t s_len, uint32_t how_much = U_NOT_FOUND) const __pure;
uint32_t find(const UString& str, uint32_t pos = 0, uint32_t how_much = U_NOT_FOUND) const __pure;
uint32_t find(const char* s, uint32_t pos = 0) const { return find(s, pos, u__strlen(s, __PRETTY_FUNCTION__), U_NOT_FOUND); }
uint32_t find(unsigned char c, uint32_t pos = 0) const __pure;
// The `rfind' function searches from end to beginning string for a specified string (possibly a single
// character) and returns its starting position. You can supply the parameter pos to specify the position
// where search must begin
uint32_t rfind(const char* s, uint32_t pos, uint32_t n) const __pure;
uint32_t rfind(const char* s, uint32_t pos = U_NOT_FOUND) const { return rfind(s, pos, u__strlen(s, __PRETTY_FUNCTION__)); }
uint32_t rfind(unsigned char c, uint32_t pos = U_NOT_FOUND) const __pure;
uint32_t rfind(const UString& str, uint32_t pos = U_NOT_FOUND) const { return rfind(str.data(), pos, str.size()); }
// The `find_first_of' function searches string for the first match of any character stored in s and returns its position
// The `find_last_of' function searches string for the last match of any character stored in s and returns its position
uint32_t find_first_of(const char* s, uint32_t pos, uint32_t n) const __pure;
uint32_t find_first_of(const char* s, uint32_t pos = 0) const { return find_first_of(s, pos, u__strlen(s, __PRETTY_FUNCTION__)); }
uint32_t find_first_of(unsigned char c, uint32_t pos = 0) const { return find(c, pos); }
uint32_t find_first_of(const UString& str, uint32_t pos = 0) const { return find_first_of(str.data(), pos, str.size()); }
uint32_t find_last_of(const char* s, uint32_t pos, uint32_t n) const __pure;
uint32_t find_last_of(const char* s, uint32_t pos = U_NOT_FOUND) const { return find_last_of(s, pos, u__strlen(s, __PRETTY_FUNCTION__)); }
uint32_t find_last_of(unsigned char c, uint32_t pos = U_NOT_FOUND) const { return rfind(c, pos); }
uint32_t find_last_of(const UString& str, uint32_t pos = U_NOT_FOUND) const { return find_last_of(str.data(), pos, str.size()); }
// The `find_first_not_of' function searches the first element of string that doesn't match any character stored in s
// and returns its position
// The `find_last_not_of' function searches the last element of string that doesn't match any character stored in s
// and returns its position
uint32_t find_first_not_of(const char* s, uint32_t pos, uint32_t n) const __pure;
uint32_t find_first_not_of(const char* s, uint32_t pos = 0) const { return find_first_not_of(s, pos, u__strlen(s, __PRETTY_FUNCTION__)); }
uint32_t find_first_not_of(unsigned char c, uint32_t pos = 0) const __pure;
uint32_t find_first_not_of(const UString& str, uint32_t pos = 0) const { return find_first_not_of(str.data(), pos, str.size()); }
uint32_t find_last_not_of(const char* s, uint32_t pos, uint32_t n) const __pure;
uint32_t find_last_not_of(const char* s, uint32_t pos = U_NOT_FOUND) const { return find_last_not_of(s, pos, u__strlen(s, __PRETTY_FUNCTION__)); }
uint32_t find_last_not_of(unsigned char c, uint32_t pos = U_NOT_FOUND) const __pure;
uint32_t find_last_not_of(const UString& str, uint32_t pos = U_NOT_FOUND) const { return find_last_not_of(str.data(), pos, str.size()); }
// Find with ignore case
uint32_t findnocase(const char* s, uint32_t pos, uint32_t s_len, uint32_t how_much = U_NOT_FOUND) const __pure;
uint32_t findnocase(const UString& str, uint32_t pos = 0, uint32_t how_much = U_NOT_FOUND) const __pure;
uint32_t findnocase(const char* s, uint32_t pos = 0) const { return findnocase(s, pos, u__strlen(s, __PRETTY_FUNCTION__), U_NOT_FOUND); }
__pure uint32_t findWhiteSpace(uint32_t pos = 0) const { return rep->findWhiteSpace(pos); }
// Compare
int compare(const char* s) const { return rep->compare(s, u__strlen(s, __PRETTY_FUNCTION__)); }
int compare(const char* s, uint32_t n) const { return rep->compare(s, n); }
int compare(UStringRep* _rep) const { return rep->compare(_rep); }
int compare(const UString& str) const { return rep->compare(str.rep); }
int compare(uint32_t pos, const char* s) const
{ return rep->compare(pos, u__strlen(s, __PRETTY_FUNCTION__), s, u__strlen(s, __PRETTY_FUNCTION__)); }
int compare(uint32_t pos, uint32_t n1, const char* s, uint32_t n2) const
{ return rep->compare(pos, U_min(size() - pos, n1), s, U_min(u__strlen(s, __PRETTY_FUNCTION__), n2)); }
int compare(uint32_t pos, uint32_t n, const char* s) const
{ return rep->compare(pos, U_min(size() - pos, n), s, u__strlen(s, __PRETTY_FUNCTION__)); }
__pure int compare(uint32_t pos, uint32_t n, const UString& str) const
{ return rep->compare(pos, U_min(size() - pos, n), U_STRING_TO_PARAM(str)); }
__pure int compare(uint32_t pos1, uint32_t n1, const UString& str, uint32_t pos2, uint32_t n2) const
{ return rep->compare(pos1, U_min(size() - pos1, n1), str.data() + pos2,
U_min(str.size() - pos2, n2)); }
// Compare with ignore case
int comparenocase(const char* s, uint32_t n) const { return rep->comparenocase(s, n); }
int comparenocase(const char* s) const { return rep->comparenocase(s, u__strlen(s, __PRETTY_FUNCTION__)); }
int comparenocase(UStringRep* _rep) const { return rep->comparenocase(_rep); }
int comparenocase(const UString& str) const { return rep->comparenocase(str.rep); }
// Same string representation
bool same(UStringRep* _rep) const { return (rep == _rep); }
bool same(const UString& str) const { return same(str.rep); }
// Equal
bool equal(const char* s) const __pure;
bool equal(const char* s, uint32_t n) const __pure;
bool equal(UStringRep* _rep) const __pure;
bool equal(const UString& str) const { return equal(str.rep); }
// Equal with ignore case
bool equal(UStringRep* _rep, bool ignore_case) const { return same(_rep) || rep->equal(_rep, ignore_case); }
bool equal(const UString& str, bool ignore_case) const { return equal(str.rep, ignore_case); }
bool equal(const char* s, uint32_t n, bool ignore_case) const { return rep->equal(s, n, ignore_case); }
bool equalnocase(const char* s) const { return rep->equal(s, u__strlen(s, __PRETTY_FUNCTION__), true); }
bool equalnocase(const char* s, uint32_t n) const { return rep->equal(s, n, true); }
bool equalnocase(UStringRep* _rep) const { return same(_rep) || rep->equal(_rep, true); }
bool equalnocase(const UString& str) const __pure;
// STREAM
#ifdef U_STDCPP_ENABLE
istream& getline(istream& is, unsigned char delim = '\n');
friend U_EXPORT istream& operator>>(istream& is, UString& str);
friend U_EXPORT ostream& operator<<(ostream& os, const UString& str);
// --------------------------------------------------------------
// STREAM - EXTENSION TO STDLIBC++
// --------------------------------------------------------------
void get(istream& is);
void write(ostream& os) const { rep->write(os); }
// --------------------------------------------------------------
#endif
#ifdef DEBUG
bool invariant() const;
# ifdef U_STDCPP_ENABLE
const char* dump(bool reset) const;
# endif
#endif
// -----------------------------------------------------------------------------------------------------------------------
// START EXTENSION TO STDLIBC++
// -----------------------------------------------------------------------------------------------------------------------
bool isNull() const { return (rep == UStringRep::string_rep_null); }
bool isNullTerminated() const { return rep->isNullTerminated(); }
bool isText(uint32_t pos = 0) const { return rep->isText(pos); }
bool isUTF8(uint32_t pos = 0) const { return rep->isUTF8(pos); }
bool isUTF16(uint32_t pos = 0) const { return rep->isUTF16(pos); }
bool isBinary(uint32_t pos = 0) const { return rep->isBinary(pos); }
bool isBase64(uint32_t pos = 0) const { return rep->isBase64(pos); }
bool isEndHeader(uint32_t pos = 0) const { return rep->isEndHeader(pos); }
bool isWhiteSpace(uint32_t pos = 0) const { return rep->isWhiteSpace(pos); }
bool isPrintable(uint32_t pos = 0, bool bline = false) const { return rep->isPrintable(pos, bline); }
char last_char() const { return rep->last_char(); }
char first_char() const { return rep->first_char(); }
char c_char(uint32_t pos) const { return rep->at(pos); }
char* c_pointer(uint32_t pos) const { return (char*)rep->c_pointer(pos); }
uint32_t remain( const char* ptr) const { return rep->remain(ptr); }
__pure uint32_t distance(const char* ptr) const { return rep->distance(ptr); }
void setFromInode(uint64_t* p) { (void) replace((const char*)p, sizeof(uint64_t)); }
uint32_t hash(bool ignore_case = false) const { return rep->hash(ignore_case); }
// references
void hold() const { rep->hold(); }
void release() const { rep->release(); }
uint32_t reference() const { return rep->references; }
// for constant string
void trim() const { rep->trim(); }
// if the string is quoted...
bool isQuoted(unsigned char c = '"') const { return rep->isQuoted(c); }
// ...unquote it
void unQuote();
// set uniq
void duplicate() const;
bool uniq() const { return rep->uniq(); }
bool writeable() const { return rep->writeable(); }
// manage UString as constant string...
void setConstant(const char* t, uint32_t tlen)
{
U_TRACE(0, "UString::setConstant(%.*S,%u)", tlen, t, tlen)
_set(U_NEW(UStringRep(t, tlen)));
U_INTERNAL_ASSERT(invariant())
}
// manage UString as memory mapped area...
bool isMmap() const
{
U_TRACE(0, "UString::isMmap()")
bool result = (rep->_capacity == U_NOT_FOUND);
U_RETURN(result);
}
void mmap(const char* map, uint32_t len);
// manage UString as buffer...
void setEmpty();
void setEmptyForce();
void setBuffer(uint32_t n);
void moveToBeginDataInBuffer(uint32_t n);
void snprintf( const char* format, ...);
void snprintf_add(const char* format, ...);
void setFromData(const char** ptr, uint32_t sz);
void setFromData(const char** ptr, uint32_t sz, unsigned char delim);
void size_adjust() { rep->size_adjust(); }
void size_adjust_force() { rep->size_adjust_force(); }
void size_adjust(uint32_t value) { rep->size_adjust(value); }
void size_adjust(const char* ptr) { rep->size_adjust(ptr); }
void size_adjust_force(uint32_t value) { rep->size_adjust_force(value); }
void setFromNumber32(uint32_t number)
{
U_TRACE(0, "UString::setFromNumber32(%u)", number)
# ifdef DEBUG
vsnprintf_check("4294967295");
# endif
char* ptr = (char*)rep->str;
ptr[(rep->_length = u_num2str32(ptr, number))] = '\0';
U_INTERNAL_ASSERT(invariant())
}
void setFromNumber32s(int32_t number)
{
U_TRACE(0, "UString::setFromNumber32s(%d)", number)
# ifdef DEBUG
vsnprintf_check("4294967295");
# endif
char* ptr = (char*)rep->str;
ptr[(rep->_length = u_num2str32s(ptr, number))] = '\0';
U_INTERNAL_ASSERT(invariant())
}
#ifdef DEBUG
void vsnprintf_check(const char* format) const;
#endif
void setFromNumber64(uint64_t number)
{
U_TRACE(0, "UString::setFromNumber64(%llu)", number)
# ifdef DEBUG
vsnprintf_check("18446744073709551615");
# endif
char* ptr = (char*)rep->str;
ptr[(rep->_length = u_num2str64(ptr, number))] = '\0';
U_INTERNAL_ASSERT(invariant())
}
void setFromNumber64s(int64_t number)
{
U_TRACE(0, "UString::setFromNumber64s(%lld)", number)
# ifdef DEBUG
vsnprintf_check("18446744073709551615");
# endif
char* ptr = (char*)rep->str;
ptr[(rep->_length = u_num2str64s(ptr, number))] = '\0';
U_INTERNAL_ASSERT(invariant())
}
void vsnprintf(const char* format, va_list argp)
{
U_TRACE(0, "UString::vsnprintf(%S)", format)
# ifdef DEBUG
vsnprintf_check(format);
# endif
// NB: +1 because we want space for null-terminator...
rep->_length = u__vsnprintf(rep->data(), rep->_capacity+1, format, argp);
U_INTERNAL_DUMP("ret = %u buffer_size = %u", rep->_length, rep->_capacity+1)
U_INTERNAL_ASSERT(invariant())
}
void vsnprintf_add(const char* format, va_list argp)
{
U_TRACE(0, "UString::vsnprintf_add(%S)", format)
# ifdef DEBUG
vsnprintf_check(format);
# endif
// NB: +1 because we want space for null-terminator...
uint32_t ret = u__vsnprintf(c_pointer(rep->_length), rep->space()+1, format, argp);
U_INTERNAL_DUMP("ret = %u buffer_size = %u", ret, rep->space()+1)
rep->_length += ret;
U_INTERNAL_ASSERT(invariant())
}
bool strtob() const { return rep->strtob(); }
#ifdef HAVE_STRTOF
float strtof() const { return rep->strtof(); }
#endif
double strtod() const { return rep->strtod(); }
#ifdef HAVE_STRTOLD
long double strtold() const { return rep->strtold(); }
#endif
long strtol(int base = 0) const { return rep->strtol(base); }
#ifdef HAVE_STRTOULL
int64_t strtoll(int base = 0) const { return rep->strtoll(base); }
#endif
// UTF8 <--> ISO Latin 1
static UString toUTF8(const unsigned char* t, uint32_t tlen)
{
U_TRACE(0, "UString::toUTF8(%.*S,%u)", tlen, t, tlen)
UString utf8(UStringRep::toUTF8(t, tlen));
U_RETURN_STRING(utf8);
}
static UString fromUTF8(const unsigned char* t, uint32_t tlen)
{
U_TRACE(0, "UString::fromUTF8(%.*S,%u)", tlen, t, tlen)
UString isolat1(UStringRep::fromUTF8(t, tlen));
U_RETURN_STRING(isolat1);
}
// -----------------------------------------------------------------------------------------------------------------------
// END EXTENSION TO STDLIBC++
// -----------------------------------------------------------------------------------------------------------------------
private:
char* __append(uint32_t n) U_NO_EXPORT;
char* __replace(uint32_t pos, uint32_t n1, uint32_t n2) U_NO_EXPORT;
};
// operator ==
inline bool operator==(const UStringRep& lhs, const UStringRep& rhs) { return lhs.equal(&rhs); }
inline bool operator==(const UString& lhs, const UString& rhs) { return lhs.equal(rhs); }
inline bool operator==(const char* lhs, const UString& rhs) { return rhs.equal(lhs); }
inline bool operator==(const UString& lhs, const char* rhs) { return lhs.equal(rhs); }
// operator !=
inline bool operator!=(const UStringRep& lhs, const UStringRep& rhs) { return lhs.equal(&rhs) == false; }
inline bool operator!=(const UString& lhs, const UString& rhs) { return lhs.equal(rhs) == false; }
inline bool operator!=(const char* lhs, const UString& rhs) { return rhs.equal(lhs) == false; }
inline bool operator!=(const UString& lhs, const char* rhs) { return lhs.equal(rhs) == false; }
// operator <
inline bool operator<(const UStringRep& lhs, const UStringRep& rhs) { return lhs.compare(&rhs) < 0; }
inline bool operator<(const UString& lhs, const UString& rhs) { return lhs.compare(rhs) < 0; }
inline bool operator<(const char* lhs, const UString& rhs) { return rhs.compare(lhs) > 0; }
inline bool operator<(const UString& lhs, const char* rhs) { return lhs.compare(rhs) < 0; }
// operator >
inline bool operator>(const UStringRep& lhs, const UStringRep& rhs) { return lhs.compare(&rhs) > 0; }
inline bool operator>(const UString& lhs, const UString& rhs) { return lhs.compare(rhs) > 0; }
inline bool operator>(const char* lhs, const UString& rhs) { return rhs.compare(lhs) < 0; }
inline bool operator>(const UString& lhs, const char* rhs) { return lhs.compare(rhs) > 0; }
// operator <=
inline bool operator<=(const UStringRep& lhs, const UStringRep& rhs) { return lhs.compare(&rhs) <= 0; }
inline bool operator<=(const UString& lhs, const UString& rhs) { return lhs.compare(rhs) <= 0; }
inline bool operator<=(const char* lhs, const UString& rhs) { return rhs.compare(lhs) >= 0; }
inline bool operator<=(const UString& lhs, const char* rhs) { return lhs.compare(rhs) <= 0; }
// operator >=
inline bool operator>=(const UStringRep& lhs, const UStringRep& rhs) { return lhs.compare(&rhs) >= 0; }
inline bool operator>=(const UString& lhs, const UString& rhs) { return lhs.compare(rhs) >= 0; }
inline bool operator>=(const char* lhs, const UString& rhs) { return rhs.compare(lhs) <= 0; }
inline bool operator>=(const UString& lhs, const char* rhs) { return lhs.compare(rhs) >= 0; }
#endif