2020-04-29 22:34:28 +02:00
/*
* Copyright ( c ) 2019 Jief Luce .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS " AND
* ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
* ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
* LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
*/
2020-04-23 15:20:48 +02:00
# if !defined(__XSTRINGABSTRACT_H__)
# define __XSTRINGABSTRACT_H__
2020-04-24 08:36:29 +02:00
# include <XToolsConf.h>
2020-04-26 12:12:05 +02:00
# include "XToolsCommon.h"
2020-04-23 15:20:48 +02:00
# include "unicode_conversions.h"
# ifndef DEBUG_ALL
# define DEBUG_XStringAbstract 0
# else
2020-04-26 12:12:05 +02:00
# define DEBUG_XStringAbstract DEBUG_ALL
2020-04-23 15:20:48 +02:00
# endif
# if DEBUG_XStringAbstract == 0
# define DBG_XSTRING(...)
# else
# define DBG_XSTRING(...) DebugLog(DEBUG_XStringAbstract, __VA_ARGS__)
# endif
2020-04-27 11:50:49 +02:00
//#if __WCHAR_MAX__ <= 0xFFFFu
// #define wchar_cast char16_t
//#else
// #define wchar_cast char32_t
//#endif
2020-04-23 15:20:48 +02:00
# define asciiToLower(ch) (((ch >= L'A') && (ch <= L'Z')) ? ((ch - L'A') + L'a') : ch)
2020-04-26 01:54:13 +02:00
# define asciiToUpper(ch) (((ch >= L'a') && (ch <= L'z')) ? ((ch - L'a') + L'A') : ch)
2020-04-23 15:20:48 +02:00
2020-04-26 12:12:05 +02:00
template < typename S , typename O >
int XStringAbstract__startWith ( const S * src , const O * other , bool ignoreCase )
{
size_t nb = 0 ;
const S * src2 = src ;
const O * other2 = other ;
char32_t src_char32 ;
char32_t other_char32 ;
other2 = get_char32_from_string ( other2 , & other_char32 ) ;
if ( ! other_char32 ) return true ; // startWith with empty string is considered true
src2 = get_char32_from_string ( src2 , & src_char32 ) ;
while ( other_char32 ) {
if ( ignoreCase ) {
src_char32 = asciiToLower ( src_char32 ) ;
other_char32 = asciiToLower ( other_char32 ) ;
}
if ( src_char32 ! = other_char32 ) return false ;
src2 = get_char32_from_string ( src2 , & src_char32 ) ;
other2 = get_char32_from_string ( other2 , & other_char32 ) ;
nb + = 1 ;
} ;
return src_char32 ! = 0 ;
}
2020-04-23 15:20:48 +02:00
template < typename S , typename O >
int XStringAbstract__compare ( const S * src , const O * other , bool ignoreCase )
{
// size_t len_s = length_of_utf_string(src);
// size_t len_other = length_of_utf_string(other);
size_t nb = 0 ;
const S * src2 = src ;
const O * other2 = other ;
char32_t src_char32 ;
char32_t other_char32 ;
src2 = get_char32_from_string ( src2 , & src_char32 ) ;
other2 = get_char32_from_string ( other2 , & other_char32 ) ;
while ( src_char32 ) {
if ( ignoreCase ) {
src_char32 = asciiToLower ( src_char32 ) ;
other_char32 = asciiToLower ( other_char32 ) ;
}
if ( src_char32 ! = other_char32 ) break ;
src2 = get_char32_from_string ( src2 , & src_char32 ) ;
other2 = get_char32_from_string ( other2 , & other_char32 ) ;
nb + = 1 ;
} ;
if ( src_char32 = = other_char32 ) return 0 ;
return src_char32 > other_char32 ? 1 : - 1 ;
}
2020-04-26 13:52:10 +02:00
template < typename S , typename O >
int XStringAbstract__ncompare ( const S * src , const O * other , size_t n , bool ignoreCase )
{
if ( n = = 0 ) return 0 ; // string of 0 length are equal.
const S * src2 = src ;
const O * other2 = other ;
char32_t src_char32 ;
char32_t other_char32 ;
src2 = get_char32_from_string ( src2 , & src_char32 ) ;
other2 = get_char32_from_string ( other2 , & other_char32 ) ;
size_t nb = 1 ;
while ( src_char32 & & nb < n ) {
if ( ignoreCase ) {
src_char32 = asciiToLower ( src_char32 ) ;
other_char32 = asciiToLower ( other_char32 ) ;
}
if ( src_char32 ! = other_char32 ) break ;
src2 = get_char32_from_string ( src2 , & src_char32 ) ;
other2 = get_char32_from_string ( other2 , & other_char32 ) ;
nb + = 1 ;
} ;
if ( src_char32 = = other_char32 ) return 0 ;
return src_char32 > other_char32 ? 1 : - 1 ;
}
2020-04-23 15:20:48 +02:00
template < typename O , typename P >
size_t XStringAbstract__indexOf ( const O * * s , const P * other , size_t offsetRet , bool toLower )
{
size_t Idx = 0 ;
char32_t s_char32 ;
char32_t other_char32 ;
do
{
const O * s2 = * s ;
const P * other2 = other ;
do {
s2 = get_char32_from_string ( s2 , & s_char32 ) ;
other2 = get_char32_from_string ( other2 , & other_char32 ) ;
if ( toLower ) {
s_char32 = asciiToLower ( s_char32 ) ;
other_char32 = asciiToLower ( other_char32 ) ;
}
} while ( s_char32 & & other_char32 & & s_char32 = = other_char32 ) ;
if ( other_char32 = = 0 ) return Idx + offsetRet ;
* s = get_char32_from_string ( * s , & s_char32 ) ;
Idx + + ;
} while ( s_char32 ) ;
return MAX_XSIZE ;
}
template < typename O , typename P >
size_t XStringAbstract__indexOf ( const O * s , size_t Pos , const P * other , bool toLower )
{
if ( * other = = 0 ) return Pos ;
char32_t char32 = 1 ;
for ( size_t Idx = 0 ; Idx < Pos ; Idx + = 1 ) {
s = get_char32_from_string ( s , & char32 ) ;
}
if ( ! char32 ) return MAX_XSIZE ;
return XStringAbstract__indexOf ( & s , other , Pos , toLower ) ;
}
template < typename O , typename P >
size_t XStringAbstract__rindexOf ( const O * s , size_t Pos , const P * other , bool toLower )
{
if ( * other = = 0 ) return Pos > length_of_utf_string ( s ) ? length_of_utf_string ( s ) : Pos ;
size_t index = XStringAbstract__indexOf ( & s , other , 0 , toLower ) ;
size_t prev_index = index ; // initialize to index in case of index is already == Pos
char32_t char32 ;
s = get_char32_from_string ( s , & char32 ) ;
while ( char32 & & index < Pos ) {
prev_index = index ;
index = XStringAbstract__indexOf ( & s , other , index + 1 , toLower ) ;
s = get_char32_from_string ( s , & char32 ) ;
} ;
if ( index = = Pos ) return index ;
if ( prev_index < = Pos ) return prev_index ;
return MAX_XSIZE ;
}
template < class T , class ThisXStringClass >
2020-04-29 22:34:28 +02:00
class __String
2020-04-23 15:20:48 +02:00
{
public :
2020-04-29 22:34:28 +02:00
typedef T char_t ;
typedef ThisXStringClass xs_t ;
2020-04-23 15:20:48 +02:00
protected :
T * m_data ;
// convenience method. Did it this way to avoid #define in header. They can have an impact on other headers
2020-04-25 11:59:07 +02:00
size_t Xmin ( size_t x1 , size_t x2 ) const { if ( x1 < x2 ) return x1 ; return x2 ; }
size_t Xmax ( size_t x1 , size_t x2 ) const { if ( x1 > x2 ) return x1 ; return x2 ; }
2020-04-23 15:20:48 +02:00
// Methods _data is protected intentionally. They are const method returning non-const pointer. That's intentional, but dangerous. Do not expose to public.
// If you need a non-const pointer for low-level access, to use dataSized and have to specify the size
// pos is counted in logical char
template < typename IntegralType , enable_if ( is_integral ( IntegralType ) ) >
T * _data ( IntegralType pos ) const
{
if ( pos < 0 ) panic ( " T* data(int i) -> i < 0 " ) ;
2020-04-23 22:43:35 +02:00
size_t offset = size_of_utf_string_len ( m_data , ( unsigned_type ( IntegralType ) ) pos ) ; // If pos is too big, size_of_utf_string_len returns the end of the string
2020-04-23 15:20:48 +02:00
return m_data + offset ;
}
public :
// T* memoryOffset(size_t i) {
//
// }
public :
2020-04-29 22:34:28 +02:00
constexpr __String ( const T * s ) : m_data ( ( T * ) s ) { }
public :
// constexpr __String() : m_data(&nullChar) { }
constexpr __String ( const __String & ) = delete ;
constexpr __String ( ) = delete ;
2020-04-23 15:20:48 +02:00
2020-04-29 22:34:28 +02:00
// no assignement, no destructor
2020-04-23 15:20:48 +02:00
template < typename IntegralType , enable_if ( is_integral ( IntegralType ) ) >
T * data ( IntegralType pos ) const { return _data ( pos ) ; }
size_t length ( ) const { return length_of_utf_string ( m_data ) ; }
// size_t sizeZZ() const { return size_of_utf_string(m_data); }
2020-04-27 11:50:49 +02:00
size_t sizeInNativeChars ( ) const { return size_of_utf_string ( m_data ) ; }
2020-04-23 15:20:48 +02:00
size_t sizeInBytes ( ) const { return size_of_utf_string ( m_data ) * sizeof ( T ) ; }
2020-04-26 12:12:05 +02:00
size_t sizeInBytesIncludingTerminator ( ) const { return ( size_of_utf_string ( m_data ) + 1 ) * sizeof ( T ) ; } // usefull for unit tests
2020-04-23 15:20:48 +02:00
const T * wc_str ( ) const { return m_data ; }
const T * c_str ( ) const { return m_data ; }
const T * s ( ) const { return m_data ; }
const T * data ( ) const { return m_data ; } // todo delete
/* Empty ? */
bool isEmpty ( ) const { return m_data = = nullptr | | * m_data = = 0 ; }
bool notEmpty ( ) const { return ! isEmpty ( ) ; }
//--------------------------------------------------------------------- cast
// int ToInt() const;
// size_t ToUInt() const;
//--------------------------------------------------------------------- charAt, []
template < typename IntegralType , enable_if ( is_integral ( IntegralType ) ) >
char32_t char32At ( IntegralType i ) const
{
if ( i < 0 ) {
2020-04-29 22:34:28 +02:00
panic ( " __String<T>::char32At(size_t i) : i < 0. System halted \n " ) ;
2020-04-23 15:20:48 +02:00
}
size_t nb = 0 ;
const T * p = m_data ;
char32_t char32 ;
do {
p = get_char32_from_string ( p , & char32 ) ;
if ( ! char32 ) {
2020-04-26 12:12:05 +02:00
if ( ( unsigned_type ( IntegralType ) ) i = = nb ) return 0 ; // no panic if we want to access the null terminator
2020-04-29 22:34:28 +02:00
panic ( " __String::char32At(size_t i) : i >= length(). System halted \n " ) ;
2020-04-23 15:20:48 +02:00
}
nb + = 1 ;
2020-04-23 22:43:35 +02:00
} while ( nb < = ( unsigned_type ( IntegralType ) ) i ) ;
2020-04-23 15:20:48 +02:00
return char32 ;
}
template < typename IntegralType , enable_if ( is_integral ( IntegralType ) ) >
char16_t char16At ( IntegralType i ) const
{
char32_t char32 = char32At ( i ) ;
if ( char32 > = 0x10000 ) return 0xFFFD ; // <20> REPLACEMENT CHARACTER used to replace an unknown, unrecognized or unrepresentable character
return ( char16_t ) char32 ;
}
/* [] */
template < typename IntegralType , enable_if ( is_integral ( IntegralType ) ) >
char32_t operator [ ] ( IntegralType i ) const { return char32At ( i ) ; }
2020-04-25 11:59:07 +02:00
char32_t lastChar ( ) const { if ( length ( ) > 0 ) return char32At ( length ( ) - 1 ) ; else return 0 ; }
2020-04-29 22:34:28 +02:00
// /* copy ctor */
// __String<T, ThisXStringClass>(const __String<T, ThisXStringClass> &S) { Init(0); takeValueFrom(S); }
// /* ctor */
// template<typename O, class OtherXStringClass>
// explicit __String<T, ThisXStringClass>(const __String<O, OtherXStringClass>& S) { Init(0); takeValueFrom(S); }
//// template<typename O>
//// explicit __String<T, ThisXStringClass>(const O* S) { Init(0); takeValueFrom(S); }
2020-04-23 15:20:48 +02:00
/* Copy Assign */ // Only other XString, no litteral at the moment.
2020-04-29 22:34:28 +02:00
// __String<T, ThisXStringClass>& operator =(const __String<T, ThisXStringClass>& S) { strcpy(S.s()); return *this; }
// /* Assign */
// template<typename O, class OtherXStringClass>
// ThisXStringClass& operator =(const __String<O, OtherXStringClass>& S) { strcpy(S.s()); return *((ThisXStringClass*)this); }
//// template<class O>
//// ThisXStringClass& operator =(const O* S) { strcpy(S); return *this; }
2020-04-23 15:20:48 +02:00
2020-04-26 12:12:05 +02:00
//--------------------------------------------------------------------- indexOf, rindexOf, contains, subString
2020-04-23 15:20:48 +02:00
/* indexOf */
size_t indexOf ( char32_t char32Searched , size_t Pos = 0 ) const
{
char32_t buf [ 2 ] = { char32Searched , 0 } ;
return XStringAbstract__indexOf ( m_data , Pos , buf , false ) ;
}
template < typename O >
size_t indexOf ( const O * S , size_t Pos = 0 ) const { return XStringAbstract__indexOf ( m_data , Pos , S , false ) ; }
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
size_t indexOf ( const __String < O , OtherXStringClass > & S , size_t Pos = 0 ) const { return indexOf ( S . s ( ) , Pos ) ; }
2020-04-23 15:20:48 +02:00
/* IC */
size_t indexOfIC ( char32_t char32Searched , size_t Pos = 0 ) const
{
char32_t buf [ 2 ] = { char32Searched , 0 } ;
return XStringAbstract__indexOf ( m_data , Pos , buf , true ) ;
}
template < typename O >
size_t indexOfIC ( const O * S , size_t Pos = 0 ) const { return XStringAbstract__indexOf ( m_data , Pos , S , true ) ; }
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
size_t indexOfIC ( const __String < O , OtherXStringClass > & S , size_t Pos = 0 ) const { return indexOfIC ( S . s ( ) , Pos ) ; }
2020-04-23 15:20:48 +02:00
/* rindexOf */
size_t rindexOf ( const char32_t char32Searched , size_t Pos = MAX_XSIZE - 1 ) const
{
char32_t buf [ 2 ] = { char32Searched , 0 } ;
return XStringAbstract__rindexOf ( m_data , Pos , buf , false ) ;
}
template < typename O >
size_t rindexOf ( const O * S , size_t Pos = MAX_XSIZE - 1 ) const { return XStringAbstract__rindexOf ( m_data , Pos , S , false ) ; }
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
size_t rindexOf ( const __String < O , OtherXStringClass > & S , size_t Pos = MAX_XSIZE - 1 ) const { return rindexOf ( S . s ( ) , Pos ) ; }
2020-04-23 15:20:48 +02:00
/* IC */
size_t rindexOfIC ( const char32_t char32Searched , size_t Pos = MAX_XSIZE - 1 ) const
{
char32_t buf [ 2 ] = { char32Searched , 0 } ;
return XStringAbstract__rindexOf ( m_data , Pos , buf , true ) ;
}
template < typename O >
size_t rindexOfIC ( const O * S , size_t Pos = MAX_XSIZE - 1 ) const { return XStringAbstract__rindexOf ( m_data , Pos , S , true ) ; }
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
size_t rindexOfIC ( const __String < O , OtherXStringClass > & S , size_t Pos = MAX_XSIZE - 1 ) const { return rindexOf ( S . s ( ) , Pos ) ; }
2020-04-23 15:20:48 +02:00
2020-04-26 12:12:05 +02:00
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
bool contains ( const __String < O , OtherXStringClass > & S ) const { return indexOf ( S ) ! = MAX_XSIZE ; }
2020-04-26 12:12:05 +02:00
template < typename O >
bool contains ( const O * S ) const { return indexOf ( S ) ! = MAX_XSIZE ; }
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
size_t containsIC ( const __String < O , OtherXStringClass > & S ) const { return indexOfIC ( S ) ! = MAX_XSIZE ; }
2020-04-26 12:12:05 +02:00
template < typename O >
size_t containsIC ( const O * S ) const { return indexOfIC ( S ) ! = MAX_XSIZE ; }
2020-04-23 15:20:48 +02:00
2020-04-26 12:12:05 +02:00
ThisXStringClass subString ( size_t pos , size_t count ) const
{
2020-04-27 11:50:49 +02:00
// if ( pos > length() ) return ThisXStringClass();
// if ( count > length()-pos ) count = length()-pos;
2020-04-26 12:12:05 +02:00
ThisXStringClass ret ;
const T * src = m_data ;
char32_t char32 = 1 ;
while ( char32 & & pos > 0 ) {
src = get_char32_from_string ( src , & char32 ) ;
pos - = 1 ;
} ;
ret . strncat ( src , count ) ;
return ret ;
}
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
bool startWith ( const __String < O , OtherXStringClass > & otherS ) const { return XStringAbstract__startWith ( m_data , otherS . m_data , false ) ; }
2020-04-26 12:12:05 +02:00
template < typename O >
bool startWith ( const O * other ) const { return XStringAbstract__startWith ( m_data , other , false ) ; }
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
bool startWithIC ( const __String < O , OtherXStringClass > & otherS ) const { return XStringAbstract__startWith ( m_data , otherS . m_data , true ) ; }
2020-04-26 12:12:05 +02:00
template < typename O >
bool startWithIC ( const O * other ) const { return XStringAbstract__startWith ( m_data , other , true ) ; }
//--------------------------------------------------------------------- String modification
2020-04-23 15:20:48 +02:00
2020-04-25 13:33:01 +02:00
void lowerAscii ( )
2020-04-23 15:20:48 +02:00
{
T * s = m_data ;
while ( * s ) {
* s = asciiToLower ( * s ) ;
s + + ;
}
}
2020-04-26 01:54:13 +02:00
void upperAscii ( )
{
T * s = m_data ;
while ( * s ) {
* s = asciiToUpper ( * s ) ;
s + + ;
}
}
2020-04-23 15:20:48 +02:00
2020-04-25 13:33:01 +02:00
void trim ( )
{
T * start = 0 ;
size_t count = 0 ;
T * s = m_data ;
while ( * s & & unsigned_type ( T ) ( * s ) < = 32 ) s + + ;
start = s ;
while ( * s & & unsigned_type ( T ) ( * s ) > 32 ) s + + ;
count = uintptr_t ( s - start ) ;
memmove ( m_data , start , count * sizeof ( T ) ) ;
m_data [ count ] = 0 ;
}
2020-04-27 11:50:49 +02:00
//---------------------------------------------------------------------
ThisXStringClass basename ( ) const
{
size_t lastSepPos = MAX_XSIZE ;
size_t pos = 0 ;
const T * p = m_data ;
char32_t char32 ;
p = get_char32_from_string ( p , & char32 ) ;
while ( char32 ) {
if ( char32 = = U ' / ' | | char32 = = U ' \\ ' ) lastSepPos = pos ;
pos + = 1 ;
p = get_char32_from_string ( p , & char32 ) ;
} ;
if ( lastSepPos = = MAX_XSIZE ) {
if ( p = = m_data ) return ThisXStringClass ( ) . takeValueFrom ( " . " ) ;
}
return subString ( lastSepPos + 1 , MAX_XSIZE ) ;
}
// ThisXStringClass dirname() const
// {
// size_t idx = rindexOf('/');
// if ( idx == MAX_XSIZE ) return ThisXStringClass();
// return subString(0, idx);
// }
2020-04-23 15:20:48 +02:00
// void deleteCountCharsAt(size_t pos, size_t count=1);
//{
// if ( pos < size() ) {
// if ( count != MAX_XSIZE && pos + count < size() ) {
// memmove( _data(pos), data(pos+count), (size()-pos-count)*sizeof(T)); // memmove handles overlapping memory move
// setLength(size()-count);/* data()[length()-count]=0 done in setLength */
// }else{
// setSize(pos);/* data()[pos]=0 done in setLength */
// }
// }
//}
2020-04-29 22:34:28 +02:00
// void insert(const __String<T, ThisXStringClass>& Str, size_t pos);
2020-04-23 15:20:48 +02:00
//{
// if ( pos < size() ) {
// CheckSize(size()+Str.size());
// memmove(_data(pos + Str.size()), data(pos), (size()-pos)*sizeof(T));
// memmove(_data(pos), Str.data(), Str.size()*sizeof(T));
// setLength(size()+Str.size());
// }else{
// StrCat(Str);
// }
//}
// void ToLower(bool FirstCharIsCap = false);
// bool IsLetters() const;
// bool IsLettersNoAccent() const;
// bool IsDigits() const;
//{
// const T *p;
//
// p = data();
// if ( !*p ) return false;
// for ( ; *p ; p+=1 ) {
// if ( *p < '0' ) return false;
// if ( *p > '9' ) return false;
// }
// return true;
//}
// bool IsDigits(size_t pos, size_t count) const;
//{
// const T *p;
// const T *q;
//
// if ( pos >= size() ) {
// return false;
// }
// if ( pos+count > size() ) {
// return false;
// }
// p = data() + pos;
// q = p + count;
// for ( ; p < q ; p+=1 ) {
// if ( *p < '0' ) return false;
// if ( *p > '9' ) return false;
// }
// return true;
//}
// void Replace(T c1, T c2)
// {
// T* p;
//
// p = s();
// while ( *p ) {
// if ( *p == c1 ) *p = c2;
// p += 1;
// }
// }
2020-04-29 22:34:28 +02:00
// __String SubStringReplace(T c1, T c2);
2020-04-23 15:20:48 +02:00
//{
// T* p;
2020-04-29 22:34:28 +02:00
// __String Result;
2020-04-23 15:20:48 +02:00
//
// p = s();
// while ( *p ) {
// if ( *p == c1 ) Result += c2;
// else Result += *p;
// p++;
// }
// return Result;
//}
//---------------------------------------------------------------------
template < typename O >
int strcmp ( const O * S ) const { return XStringAbstract__compare ( m_data , S , false ) ; }
// int Compare(const char* S) const { return ::Compare<T, char>(m_data, S); }
// int Compare(const char16_t* S) const { return ::Compare<T, char16_t>(m_data, S); };
// int Compare(const char32_t* S) const { return ::Compare<T, char32_t>(m_data, S); };
// int Compare(const wchar_t* S) const { return ::Compare<T, wchar_t>(m_data, S); };
//
2020-04-26 15:07:30 +02:00
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
bool equal ( const __String < O , OtherXStringClass > & S ) const { return XStringAbstract__compare ( m_data , S . s ( ) , false ) = = 0 ; }
2020-04-26 15:07:30 +02:00
template < typename O >
bool equal ( const O * S ) const { return XStringAbstract__compare ( m_data , S , false ) = = 0 ; }
2020-04-23 15:20:48 +02:00
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
bool equalIC ( const __String < O , OtherXStringClass > & S ) const { return XStringAbstract__compare ( m_data , S . s ( ) , true ) = = 0 ; }
2020-04-23 15:20:48 +02:00
template < typename O >
2020-04-23 22:43:35 +02:00
bool equalIC ( const O * S ) const { return XStringAbstract__compare ( m_data , S , true ) = = 0 ; }
2020-04-26 15:07:30 +02:00
2020-04-23 15:20:48 +02:00
// bool SubStringEqual(size_t Pos, const T* S) const { return (memcmp(data(Pos), S, wcslen(S)) == 0); }
public :
// == operator
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
bool operator = = ( const __String < O , OtherXStringClass > & s2 ) const { return ( * this ) . strcmp ( s2 . s ( ) ) = = 0 ; }
2020-04-24 11:30:09 +02:00
// template<typename O>
// bool operator == (const O* s2) const { return (*this).strcmp(s2) == 0; }
// template<typename O>
// friend bool operator == (const O* s1, ThisXStringClass& s2) { return s2.strcmp(s1) == 0; }
2020-04-23 15:20:48 +02:00
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
bool operator ! = ( const __String < O , OtherXStringClass > & s2 ) const { return ! ( * this = = s2 ) ; }
2020-04-24 11:30:09 +02:00
// template<typename O>
// bool operator != (const O* s2) const { return !(*this == s2); }
// template<typename O>
// friend bool operator != (const O* s1, const ThisXStringClass& s2) { return s2.strcmp(s1) != 0; }
2020-04-23 15:20:48 +02:00
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
bool operator < ( const __String < O , OtherXStringClass > & s2 ) const { return ( * this ) . strcmp ( s2 . s ( ) ) < 0 ; }
2020-04-24 11:30:09 +02:00
// template<typename O>
// bool operator < (const O* s2) const { return (*this).strcmp(s2) < 0; }
// template<typename O>
// friend bool operator < (const O* s1, const ThisXStringClass& s2) { return s2.strcmp(s1) > 0; }
2020-04-23 15:20:48 +02:00
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
bool operator > ( const __String < O , OtherXStringClass > & s2 ) const { return ( * this ) . strcmp ( s2 . s ( ) ) > 0 ; }
2020-04-24 11:30:09 +02:00
// template<typename O>
// bool operator > (const O* s2) const { return (*this).strcmp(s2) > 0; }
// template<typename O>
// friend bool operator > (const O* s1, const ThisXStringClass& s2) { return s2.strcmp(s1) < 0; }
2020-04-23 15:20:48 +02:00
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
bool operator < = ( const __String < O , OtherXStringClass > & s2 ) const { return ( * this ) . strcmp ( s2 . s ( ) ) < = 0 ; }
2020-04-24 11:30:09 +02:00
// template<typename O>
// bool operator <= (const O* s2) const { return (*this).strcmp(s2) <= 0; }
// template<typename O>
// friend bool operator <= (const O* s1, const ThisXStringClass& s2) { return s2.strcmp(s1) >= 0; }
2020-04-23 15:20:48 +02:00
template < typename O , class OtherXStringClass >
2020-04-29 22:34:28 +02:00
bool operator > = ( const __String < O , OtherXStringClass > & s2 ) const { return ( * this ) . strcmp ( s2 . s ( ) ) > = 0 ; }
2020-04-24 11:30:09 +02:00
// template<typename O>
// bool operator >= (const O* s2) const { return (*this).strcmp(s2) >= 0; }
// template<typename O>
// friend bool operator >= (const O* s1, const ThisXStringClass& s2) { return s2.strcmp(s1) <= 0; }
2020-04-23 15:20:48 +02:00
} ;
2020-04-29 22:34:28 +02:00
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2020-04-23 15:20:48 +02:00
template < class T , class ThisXStringClass >
2020-04-29 22:34:28 +02:00
class LString : public __String < T , ThisXStringClass >
{
public :
protected :
constexpr LString ( const T * s ) : __String < T , ThisXStringClass > ( s ) { } ;
constexpr LString ( ) = delete ;
constexpr LString ( const LString & L ) : __String < T , ThisXStringClass > ( L . m_data ) { } ;
// no assignement, no destructor
} ;
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2020-04-23 15:20:48 +02:00
2020-04-29 22:34:28 +02:00
# define m_data __String<T, ThisXStringClass>::m_data
2020-04-27 11:50:49 +02:00
template < class T , class ThisXStringClass >
2020-04-29 22:34:28 +02:00
class XStringAbstract : public __String < T , ThisXStringClass >
2020-04-27 11:50:49 +02:00
{
2020-04-29 22:34:28 +02:00
static T nullChar ;
2020-04-27 11:50:49 +02:00
2020-04-29 22:34:28 +02:00
protected :
size_t m_allocatedSize ;
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Init , Alloc
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2020-04-27 11:50:49 +02:00
//
2020-04-29 22:34:28 +02:00
// void Init(size_t aSize=0)
// {
// //DBG_XSTRING("Init aSize=%d\n", aSize);
// // We don't allocate any memory at first. To not have to test all the time if m_data is null, we init it to an empty string
// m_data = &nullChar;
// m_allocatedSize = 0;
// // if aSize == 0, nothing is done, because m_allocatedSize == aSize
// CheckSize(aSize, 0);
// }
/*
* nNewSize doesn ' t include null terminator . Alloc ( 0 ) will allocate 1 char .
* NOTE : m_allocatedSize WILL include the the null terminator . Alloc ( 0 ) makes m_allocatedSize = = 1
*/
void Alloc ( size_t nNewSize )
{
if ( m_allocatedSize = = 0 ) m_data = ( T * ) malloc ( ( nNewSize + 1 ) * sizeof ( T ) ) ;
else m_data = ( T * ) Xrealloc ( m_data , ( nNewSize + 1 ) * sizeof ( T ) , ( m_allocatedSize + 1 ) * sizeof ( T ) ) ;
if ( ! m_data ) {
panic ( " XStringAbstract::Alloc(%zu) : Xrealloc(% " PRIuPTR " , %lu, %zd) returned NULL. System halted \n " , nNewSize , uintptr_t ( m_data ) , ( nNewSize + 1 ) * sizeof ( T ) , ( m_allocatedSize + 1 ) * sizeof ( T ) ) ;
}
m_allocatedSize = nNewSize ;
}
// public:
T * CheckSize ( size_t nNewSize , size_t nGrowBy = XStringGrowByDefault ) // nNewSize is in number of chars, NOT bytes
{
//DBG_XSTRING("CheckSize: m_size=%d, nNewSize=%d\n", m_size, nNewSize);
if ( m_allocatedSize < nNewSize )
{
nNewSize + = nGrowBy ;
if ( m_allocatedSize = = 0 ) { //if ( *m_data ) {
size_t len = __String < T , ThisXStringClass > : : length ( ) ;
if ( nNewSize < len ) nNewSize = len ;
T * m_dataSav = m_data ;
m_data = NULL ;
Alloc ( nNewSize ) ;
utf_string_from_utf_string ( m_data , m_allocatedSize , m_dataSav ) ;
} else {
Alloc ( nNewSize ) ;
}
// m_data[m_allocatedSize] = 0; // we allocated one more char (nNewSize+1). This \0 is an extra precaution. It's not for the normal null terminator. All string operation must considered that only m_allocatedSize bytes were allocated.
}
return m_data ;
}
// void setSize(size_t newSize) // nNewSize is in number of chars, NOT bytes
// {
// //DBG_XSTRING("setLength(%d)\n", len);
// CheckSize(newSize);
// // if ( len >= size() ) {
// // DBG_XSTRING("__String<T>::setLength(size_t len) : len >= size() (%d != %d). System halted\n", len, size());
// // panic();
// // }
// m_data[newSize] = 0; // we may rewrite a 0 in nullChar, if no memory were allocated. That's ok.
// }
public :
/* default ctor */
XStringAbstract ( ) : __String < T , ThisXStringClass > ( & nullChar ) , m_allocatedSize ( 0 ) { }
/* copy ctor */
XStringAbstract ( const XStringAbstract & S ) : __String < T , ThisXStringClass > ( & nullChar ) , m_allocatedSize ( 0 )
{
if ( S . m_data & & ! S . m_allocatedSize ) {
m_data = S . m_data ;
} else {
takeValueFrom ( S ) ;
}
}
2020-04-27 11:50:49 +02:00
~ XStringAbstract ( )
{
//DBG_XSTRING("Destructor :%ls\n", data());
2020-04-29 22:34:28 +02:00
if ( m_allocatedSize > 0 ) free ( ( void * ) m_data ) ;
}
/* ctor */
template < class OtherLStringClass >
explicit XStringAbstract ( const LString < T , OtherLStringClass > & S ) : __String < T , ThisXStringClass > ( S . s ( ) ) , m_allocatedSize ( 0 ) { }
template < typename O , class OtherXStringClass >
explicit XStringAbstract < T , ThisXStringClass > ( const XStringAbstract < O , OtherXStringClass > & S ) : __String < T , ThisXStringClass > ( & nullChar ) , m_allocatedSize ( 0 ) { takeValueFrom ( S ) ; }
template < typename O , class OtherXStringClass >
explicit XStringAbstract < T , ThisXStringClass > ( const LString < O , OtherXStringClass > & S ) : __String < T , ThisXStringClass > ( & nullChar ) , m_allocatedSize ( 0 ) { takeValueFrom ( S ) ; }
// TEMPORARILY DISABLED
// template<typename O>
// explicit __String<T, ThisXStringClass>(const O* S) { Init(0); takeValueFrom(S); }
//
/* Copy Assign */ // Only other XString, no litteral at the moment.
XStringAbstract & operator = ( const XStringAbstract & S ) { takeValueFrom ( S ) ; return * this ; }
/* Assign */
template < typename O , class OtherXStringClass >
ThisXStringClass & operator = ( const __String < O , OtherXStringClass > & S ) { strcpy ( S . s ( ) ) ; return * ( ( ThisXStringClass * ) this ) ; }
// TEMPORARILY DISABLED
// template<class O>
// ThisXStringClass& operator =(const O* S) { strcpy(S); return *this; }
protected :
ThisXStringClass & takeValueFromLiteral ( const T * s )
{
if ( m_allocatedSize > 0 ) panic ( " XStringAbstract::takeValueFromLiteral -> m_allocatedSize > 0 " ) ;
m_data = ( T * ) s ;
return * ( ( ThisXStringClass * ) this ) ;
}
public :
size_t allocatedSize ( ) const { return m_allocatedSize ; }
void setEmpty ( )
{
if ( m_allocatedSize < = 0 ) m_data = & nullChar ;
else m_data [ 0 ] = 0 ;
}
template < typename IntegralType , enable_if ( is_integral ( IntegralType ) ) >
T * dataSized ( IntegralType size )
{
if ( size < 0 ) panic ( " T* dataSized() -> i < 0 " ) ;
if ( ( unsigned_type ( IntegralType ) ) size > MAX_XSIZE ) panic ( " T* dataSized() -> i > MAX_XSIZE " ) ;
CheckSize ( ( size_t ) size ) ;
return __String < T , ThisXStringClass > : : _data ( 0 ) ;
}
//
// // Pos is counted in logical char but size is counted in physical char (char, char16_t, char32_t or wchar_t)
// template<typename IntegralType1, typename IntegralType2, enable_if(is_integral(IntegralType1) && is_integral(IntegralType2))>
// T* dataSized(IntegralType1 pos, IntegralType2 size)
// {
// if ( pos<0 ) panic("T* dataSized(xisize i, size_t sizeMin, size_t nGrowBy) -> i < 0");
// if ( size<0 ) panic("T* dataSized(xisize i, size_t sizeMin, size_t nGrowBy) -> i < 0");
// size_t offset = size_of_utf_string_len(m_data, (typename _xtools__make_unsigned<IntegralType1>::type)pos); // If pos is too big, size_of_utf_string_len returns the end of the string
// CheckSize(offset + (typename _xtools__make_unsigned<IntegralType2>::type)size);
// return _data(pos);
// }
T * forgetDataWithoutFreeing ( )
{
T * ret = m_data ;
m_data = & nullChar ;
m_allocatedSize = 0 ;
return ret ;
}
//--------------------------------------------------------------------- strcat, strcpy, operator =
/* strcpy char */
template < typename O , enable_if ( is_char ( O ) ) >
void strcpy ( O otherChar )
{
if ( otherChar ! = 0 ) {
size_t newSize = utf_size_of_utf_string_len ( m_data , & otherChar , 1 ) ;
CheckSize ( newSize + 1 , 0 ) ;
utf_string_from_utf_string_len ( m_data , m_allocatedSize , & otherChar , 1 ) ;
m_data [ newSize ] = 0 ;
} else {
setEmpty ( ) ;
}
}
/* strcpy */
template < typename O >
void strcpy ( const O * other )
{
if ( other & & * other ) {
size_t newSize = utf_size_of_utf_string ( m_data , other ) ;
CheckSize ( newSize + 1 , 0 ) ;
utf_string_from_utf_string ( m_data , m_allocatedSize , other ) ;
m_data [ newSize ] = 0 ;
} else {
setEmpty ( ) ;
}
2020-04-27 11:50:49 +02:00
}
2020-04-29 22:34:28 +02:00
/* strncpy */
template < typename O >
void strncpy ( const O * other , size_t other_len )
{
if ( other & & * other & & other_len > 0 ) {
size_t newSize = utf_size_of_utf_string_len ( m_data , other , other_len ) ;
CheckSize ( newSize + 1 , 0 ) ;
utf_string_from_utf_string_len ( m_data , m_allocatedSize , other , other_len ) ;
m_data [ newSize ] = 0 ;
} else {
setEmpty ( ) ;
}
}
/* strcat char */
template < typename O , enable_if ( is_char ( O ) ) >
void strcat ( O otherChar )
{
if ( otherChar ) {
size_t currentSize = size_of_utf_string ( m_data ) ;
size_t newSize = currentSize + utf_size_of_utf_string_len ( m_data , & otherChar , 1 ) ;
CheckSize ( newSize + 1 , 0 ) ;
utf_string_from_utf_string_len ( m_data + currentSize , m_allocatedSize , & otherChar , 1 ) ;
m_data [ newSize ] = 0 ;
} else {
// nothing to do
}
}
/* strcat char* */
template < typename O >
void strcat ( const O * other )
{
if ( other & & * other ) {
size_t currentSize = size_of_utf_string ( m_data ) ; // size is number of T, not in bytes
size_t newSize = currentSize + utf_size_of_utf_string ( m_data , other ) ; // size is number of T, not in bytes
CheckSize ( newSize + 1 , 0 ) ;
utf_string_from_utf_string ( m_data + currentSize , m_allocatedSize - currentSize , other ) ;
m_data [ newSize ] = 0 ;
} else {
// nothing to do
}
}
/* strcat __String */
template < typename OtherCharType , class OtherXStringClass >
void strcat ( const __String < OtherCharType , OtherXStringClass > & other )
{
size_t currentSize = size_of_utf_string ( m_data ) ; // size is number of T, not in bytes
size_t newSize = currentSize + utf_size_of_utf_string ( m_data , other . s ( ) ) ; // size is number of T, not in bytes
CheckSize ( newSize + 1 , 0 ) ;
utf_string_from_utf_string ( m_data + currentSize , m_allocatedSize - currentSize , other . s ( ) ) ;
m_data [ newSize ] = 0 ;
}
/* strncat */
template < typename O >
void strncat ( const O * other , size_t other_len )
{
if ( other & & * other & & other_len > 0 ) {
size_t currentSize = size_of_utf_string ( m_data ) ;
size_t newSize = currentSize + utf_size_of_utf_string_len ( m_data , other , other_len ) ;
CheckSize ( newSize + 1 , 0 ) ;
utf_string_from_utf_string_len ( m_data + currentSize , m_allocatedSize , other , other_len ) ;
m_data [ newSize ] = 0 ;
} else {
// nothing to do
}
}
/* takeValueFrom */
template < typename O , class OtherXStringClass >
ThisXStringClass & takeValueFrom ( const __String < O , OtherXStringClass > & S ) { strcpy ( S . s ( ) ) ; return * ( ( ThisXStringClass * ) this ) ; }
2020-05-03 17:07:34 +02:00
template < typename O >
ThisXStringClass & takeValueFrom ( const O * S ) { strcpy ( S ) ; return * ( ( ThisXStringClass * ) this ) ; }
template < typename O , enable_if ( is_char ( O ) ) >
ThisXStringClass & takeValueFrom ( const O C ) { strcpy ( C ) ; return * ( ( ThisXStringClass * ) this ) ; }
2020-04-29 22:34:28 +02:00
template < typename O , class OtherXStringClass >
ThisXStringClass & takeValueFrom ( const __String < O , OtherXStringClass > & S , size_t len ) { strncpy ( S . data ( 0 ) , len ) ; return * ( ( ThisXStringClass * ) this ) ; }
template < typename O >
ThisXStringClass & takeValueFrom ( const O * S , size_t len ) { strncpy ( S , len ) ; return * ( ( ThisXStringClass * ) this ) ; }
/* += */
template < typename O , class OtherXStringClass >
ThisXStringClass & operator + = ( const __String < O , OtherXStringClass > & S ) { strcat ( S . s ( ) ) ; return * ( ( ThisXStringClass * ) this ) ; }
template < typename O , enable_if ( is_char ( O ) ) >
ThisXStringClass & operator + = ( O S ) { strcat ( S ) ; return * ( ( ThisXStringClass * ) this ) ; }
template < typename O >
ThisXStringClass & operator + = ( const O * S ) { strcat ( S ) ; return * ( ( ThisXStringClass * ) this ) ; }
2020-04-27 11:50:49 +02:00
} ;
2020-04-29 22:34:28 +02:00
template < class T , class ThisXStringClass >
T XStringAbstract < T , ThisXStringClass > : : nullChar = 0 ;
2020-04-23 15:20:48 +02:00
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2020-04-29 22:34:28 +02:00
///* __String + char32_t */
//template<typename CharType1, class XStringClass1>
//XStringClass1 operator + (const __String<CharType1, XStringClass1>& p1, char32_t p2) { XStringClass1 s; s.takeValueFrom(p1); s.strcat(p2); return s; }
//
///* __String + __String */
//template<typename CharType1, class XStringClass1, typename CharType2, class XStringClass2>
//XStringClass1 operator + (const __String<CharType1, XStringClass1>& p1, const __String<CharType2, XStringClass2>& p2) { XStringClass1 s; s.takeValueFrom(p1); s.strcat(p2); return s; }
//
///* char* + __String */
//template<typename CharType1, typename CharType2, class XStringClass2>
//XStringClass2 operator + (const CharType1* p1, const __String<CharType2, XStringClass2>& p2) { XStringClass2 s; s.takeValueFrom(p1); s.strcat(p2); return s; }
//
///* __String + char* */
//template<typename T1, class XStringClass1, typename CharType2>
//XStringClass1 operator + (const __String<T1, XStringClass1>& p1, const CharType2* p2) { XStringClass1 s; s.takeValueFrom(p1); s.strcat(p2); return s; }
template < typename Base > _xtools__true_type is_base_of_test_func ( Base * ) ;
template < typename Base > _xtools__false_type is_base_of_test_func ( void * ) ;
template < typename B , typename D >
auto test_pre_is_base_of ( int ) - > decltype ( is_base_of_test_func < B > ( static_cast < D * > ( nullptr ) ) ) ;
2020-04-23 15:20:48 +02:00
2020-04-29 22:34:28 +02:00
template < class , class = _xtools__void_t < > , class = _xtools__void_t < > >
struct __string_type { typedef void type ; } ;
template < typename T >
struct __string_type < T , _xtools__void_t < typename T : : xs_t > , _xtools__void_t < typename T : : char_t > > { typedef __String < typename T : : char_t , typename T : : xs_t > type ; } ;
2020-04-23 15:20:48 +02:00
2020-04-29 22:34:28 +02:00
# define is___String_t(x) decltype(test_pre_is_base_of< typename __string_type<x>::type , x>(0))
# define is___String(x) is___String_t(x)::value
2020-04-23 15:20:48 +02:00
2020-04-29 22:34:28 +02:00
template < class , class = _xtools__void_t < > , class = _xtools__void_t < > >
struct __lstring_type { typedef void type ; } ;
template < typename T >
struct __lstring_type < T , _xtools__void_t < typename T : : xs_t > , _xtools__void_t < typename T : : char_t > > { typedef LString < typename T : : char_t , typename T : : xs_t > type ; } ;
2020-04-23 15:20:48 +02:00
2020-04-29 22:34:28 +02:00
# define is___LString_t(x) decltype(test_pre_is_base_of< typename __lstring_type<x>::type , x>(0))
# define is___LString(x) is___LString_t(x)::value
2020-04-23 15:20:48 +02:00
2020-04-29 22:34:28 +02:00
/* __string_class_or<T1, T2>::type is T1 is T1 is a subclass of __String. If T1 is not a subclass of __String, returns T2 if it's a subclass of __String */
2020-05-03 17:07:34 +02:00
template < typename T1 , typename T2 , typename Tdummy = void >
struct __string_class_or ;
2020-04-29 22:34:28 +02:00
template < typename T1 , typename T2 >
struct __string_class_or < T1 , T2 , enable_if_t ( ! is___String ( T1 ) & & ! is___String ( T2 ) ) > { /*typedef double type;*/ } ;
template < typename T1 , typename T2 >
struct __string_class_or < T1 , T2 , enable_if_t ( is___String ( T1 ) ) > { typedef typename T1 : : xs_t type ; } ;
template < typename T1 , typename T2 >
struct __string_class_or < T1 , T2 , enable_if_t ( ! is___String ( T1 ) & & is___String ( T2 ) ) > { typedef typename T2 : : xs_t type ; } ;
//------------------------------------------------------- + operator
template < typename T1 , typename T2 , enable_if ( is___String ( T1 ) | | is___String ( T2 ) ) >
typename __string_class_or < T1 , T2 > : : type operator + ( T1 p1 , T2 p2 ) { typename __string_class_or < T1 , T2 > : : type s ; s . takeValueFrom ( p1 ) ; s . strcat ( p2 ) ; return s ; }
//-------------------------------------------------------
2020-04-23 15:20:48 +02:00
# undef DBG_XSTRING
# undef asciiToLower
2020-04-29 22:34:28 +02:00
# undef m_data
2020-04-23 15:20:48 +02:00
# endif // __XSTRINGABSTRACT_H__