text: /*
   *
   * Created by jief in 1997.
   * Copyright (c) 2020 Jief
   * All rights reserved.
   *
   */
  
  #if !defined(__XSTRINGABSTRACT_H__)
  #define __XSTRINGABSTRACT_H__
  
  #include <XToolsConf.h>
  #include "XToolsCommon.h"
  #include "unicode_conversions.h"
  
  #ifndef DEBUG_ALL
  #define DEBUG_XStringAbstract 0
  #else
  #define DEBUG_XStringAbstract DEBUG_ALL
  #endif
  
  #if DEBUG_XStringAbstract == 0
  #define DBG_XSTRING(...)
  #else
  #define DBG_XSTRING(...) DebugLog(DEBUG_XStringAbstract, __VA_ARGS__)
  #endif
  
  //#define XSTRING_CACHING_OF_SIZE
  
  #define asciiToLower(ch) (((ch >= L'A') && (ch <= L'Z')) ? ((ch - L'A') + L'a') : ch)
  #define asciiToUpper(ch) (((ch >= L'a') && (ch <= L'z')) ? ((ch - L'a') + L'A') : ch)
  
  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;
  }
  
  template<typename S, typename O>
  int XStringAbstract__startWithOrEqualTo(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 true;
  }
  /*
   * Returns 1 if src > other
   */
  template<typename S, typename O>
  int XStringAbstract__compare(const S* src, const O* other, bool ignoreCase)
  {
    if ( src == NULL || *src == 0 ) return other == NULL || *other == 0 ? 0 : -1;
    if ( other == NULL || *other == 0 ) return 1;
  //	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;
  }
  
  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;
  }
  
  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);
  }
  
  /*
   * Find the last occurence of other, until pos
   * NOTE : do not pass SIZE_T_MAX as pos. maximum value is SIZE_T_MAX-1
   */
  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>
  class __String
  {
  public:
  	typedef T char_t;
  	typedef ThisXStringClass xs_t;
  protected:
    T *__m_data;
  protected:
    #ifdef XSTRING_CACHING_OF_SIZE
    size_t __m_size;
    #endif
  
  	// convenience method. Did it this way to avoid #define in header. They can have an impact on other headers
  	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; }
  
  
    #ifdef XSTRING_CACHING_OF_SIZE
      #if 1 // if 1, the size will be checked. For debug purpose.
        void checkSizeCached() const { // Debug method
          if ( size_of_utf_string(__m_data) != __m_size ) {
            panic("bug XString");
          }
        }
        #define XSTRING_CHECK_SIZE __String<T, ThisXStringClass>::checkSizeCached()
      #else
        #define XSTRING_CHECK_SIZE
      #endif
    #else
      #define XSTRING_CHECK_SIZE
    #endif
    
    // Method _data is protected intentionally. It's a 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, use dataSized and specify the size
  	// pos is counted in logical char (UTF32 char)
  	template<typename IntegralType, enable_if(is_integral(IntegralType))>
  	T* _data(IntegralType pos) const
  	{
      XSTRING_CHECK_SIZE;
  		if ( pos<0 ) {
        log_technical_bug("T* data(int i) -> i < 0");
        return __m_data;
      }
   		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
   		return __m_data + offset;
  	}
  
  public:
  		#ifdef XSTRING_CACHING_OF_SIZE
  	    constexpr __String(const T* s, size_t size) : __m_data((T*)s), __m_size(size) { } // Do NOT call with size != strlen(s). This is for litteral operator, not for public usage.
  	  #else
  	    constexpr __String(const T* s) : __m_data((T*)s) {}
  	  #endif
  
  //	constexpr __String() : m_data(&nullChar) {  }
  	constexpr __String(const __String&) = delete;
  	constexpr __String() = delete;
  
  	// no assignement, no destructor
  
  	constexpr const T* s() const { XSTRING_CHECK_SIZE; return __m_data; }
  	constexpr const T* data() const { XSTRING_CHECK_SIZE; return __m_data; }
  
    template<typename IntegralType, enable_if(is_integral(IntegralType))>
    constexpr const T* data(IntegralType pos) const { return _data(pos); }
  
  
    size_t length() const { return length_of_utf_string(data()); } // TODO: caching length
    
    #ifdef XSTRING_CACHING_OF_SIZE
      size_t size() const { XSTRING_CHECK_SIZE; return __m_size; }
      size_t sizeInNativeChars() const { return size(); }
      size_t sizeInBytes() const { return size()*sizeof(T); }
      size_t sizeInBytesIncludingTerminator() const { return (size()+1)*sizeof(T); } // usefull for unit tests
    #else
      size_t size() const { XSTRING_CHECK_SIZE; return size_of_utf_string(data()); }
      size_t sizeInNativeChars() const { return size_of_utf_string(data()); }
      size_t sizeInBytes() const { return size_of_utf_string(data())*sizeof(T); }
      size_t sizeInBytesIncludingTerminator() const { return (size_of_utf_string(data())+1)*sizeof(T); } // usefull for unit tests
    #endif
  
  
  	/* Empty ? */
  	bool isEmpty() const { return data() == nullptr  ||  *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) {
  #ifdef JIEF_DEBUG
        panic("__String<T>::char32At(size_t i) : i < 0. System halted\n");
  #else
        return 0;
  #endif
  		}
  		size_t nb = 0;
  		const T *p = data();
  		char32_t char32;
  		do {
  			p = get_char32_from_string(p, &char32);
  			if (!char32) {
  #ifdef JIEF_DEBUG
          if ( (unsigned_type(IntegralType))i == nb ) return 0; // no panic if we want to access the null terminator
  				panic("__String::char32At(size_t i) : i >= length(). System halted\n");
  #else
          return 0;
  #endif
  			}
  			nb += 1;
  		} while (nb <= (unsigned_type(IntegralType))i);
  		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; // � 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); }
  
  
  	char32_t lastChar() const { if ( length() > 0 ) return char32At(length()-1); else return 0; }
  //	/* 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); }
  
  	/* Copy Assign */ // Only other XString, no litteral at the moment.
  //	__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; }
  
  	//--------------------------------------------------------------------- indexOf, rindexOf, contains, subString, startWith
  	
  	/* indexOf */
  	size_t indexOf(char32_t char32Searched, size_t Pos = 0) const
  	{
  		char32_t buf[2] = { char32Searched, 0};
  		return XStringAbstract__indexOf(data(), Pos, buf, false);
  	}
  	template<typename O>
  	size_t indexOf(const O* S, size_t Pos = 0) const { return XStringAbstract__indexOf(data(), Pos, S, false); }
  	template<typename O, class OtherXStringClass>
  	size_t indexOf(const __String<O, OtherXStringClass>& S, size_t Pos = 0) const { return indexOf(S.s(), Pos); }
  	/* IC */
  	size_t indexOfIC(char32_t char32Searched, size_t Pos = 0) const
  	{
  		char32_t buf[2] = { char32Searched, 0};
  		return XStringAbstract__indexOf(data(), Pos, buf, true);
  	}
  	template<typename O>
  	size_t indexOfIC(const O* S, size_t Pos = 0) const { return XStringAbstract__indexOf(data(), Pos, S, true); }
  	template<typename O, class OtherXStringClass>
  	size_t indexOfIC(const __String<O, OtherXStringClass>& S, size_t Pos = 0) const { return indexOfIC(S.s(), Pos); }
  
  
  	/* rindexOf */
  	size_t rindexOf(const char32_t char32Searched, size_t Pos = MAX_XSIZE-1) const
  	{
  		char32_t buf[2] = { char32Searched, 0};
  		return XStringAbstract__rindexOf(data(), Pos, buf, false);
  	}
  	template<typename O>
  	size_t rindexOf(const O* S, size_t Pos = MAX_XSIZE-1) const { return XStringAbstract__rindexOf(data(), Pos, S, false); }
  	template<typename O, class OtherXStringClass>
  	size_t rindexOf(const __String<O, OtherXStringClass>& S, size_t Pos = MAX_XSIZE-1) const { return rindexOf(S.s(), Pos); }
  	/* IC */
  	size_t rindexOfIC(const char32_t char32Searched, size_t Pos = MAX_XSIZE-1) const
  	{
  		char32_t buf[2] = { char32Searched, 0};
  		return XStringAbstract__rindexOf(data(), Pos, buf, true);
  	}
  	template<typename O>
  	size_t rindexOfIC(const O* S, size_t Pos = MAX_XSIZE-1) const { return XStringAbstract__rindexOf(data(), Pos, S, true); }
  	template<typename O, class OtherXStringClass>
  	size_t rindexOfIC(const __String<O, OtherXStringClass>& S, size_t Pos = MAX_XSIZE-1) const { return rindexOf(S.s(), Pos); }
  
  	template<typename O, class OtherXStringClass>
  	bool contains(const __String<O, OtherXStringClass>& S) const { return indexOf(S) != MAX_XSIZE; }
  	template<typename O>
  	bool contains(const O* S) const { return indexOf(S) != MAX_XSIZE; }
  	template<typename O, class OtherXStringClass>
  	size_t containsIC(const __String<O, OtherXStringClass>& S) const { return indexOfIC(S) != MAX_XSIZE; }
  	template<typename O>
  	size_t containsIC(const O* S) const { return indexOfIC(S) != MAX_XSIZE; }
  
  
  	ThisXStringClass subString(size_t pos, size_t count) const
  	{
  //		if ( pos > length() ) return ThisXStringClass();
  //		if ( count > length()-pos ) count = length()-pos;
  		
  		ThisXStringClass ret;
  
  		const T* src = 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, enable_if(is_char(O))>
    bool startWith(O otherChar) const {
      O other[2] = { otherChar, 0};
      return XStringAbstract__startWith(data(), other, false);
    }
  	template<typename O, class OtherXStringClass>
  	bool startWith(const __String<O, OtherXStringClass>& otherS) const { return XStringAbstract__startWith(data(), otherS.data(), false); }
  	template<typename O>
  	bool startWith(const O* other) const { return XStringAbstract__startWith(data(), other, false); }
  	template<typename O, class OtherXStringClass>
  	bool startWithIC(const __String<O, OtherXStringClass>& otherS) const { return XStringAbstract__startWith(data(), otherS.data(), true); }
  	template<typename O>
  	bool startWithIC(const O* other) const { return XStringAbstract__startWith(data(), other, true); }
  
    template<typename O, enable_if(is_char(O))>
    bool startWithOrEqualTo(O otherChar) const {
      O other[2] = { otherChar, 0};
      return XStringAbstract__startWithOrEqualTo(data(), other, false);
    }
    template<typename O, class OtherXStringClass>
    bool startWithOrEqualTo(const __String<O, OtherXStringClass>& otherS) const { return XStringAbstract__startWithOrEqualTo(data(), otherS.data(), false); }
    template<typename O>
    bool startWithOrEqualTo(const O* other) const { return XStringAbstract__startWithOrEqualTo(data(), other, false); }
    template<typename O, class OtherXStringClass>
    bool startWithOrEqualToIC(const __String<O, OtherXStringClass>& otherS) const { return XStringAbstract__startWithOrEqualTo(data(), otherS.data(), true); }
    template<typename O>
    bool startWithOrEqualToIC(const O* other) const { return XStringAbstract__startWithOrEqualTo(data(), other, true); }
  
    template<typename O, class OtherXStringClass>
    bool endWithOrEqualToIC(const __String<O, OtherXStringClass>& otherS) const { if ( length() < otherS.length() ) return false; return XStringAbstract__rindexOf(data(), SIZE_T_MAX-1, otherS.data(), true) == length() - otherS.length(); }
  
  	//---------------------------------------------------------------------
  
  	ThisXStringClass basename() const
  	{
  		size_t lastSepPos = MAX_XSIZE;
  		size_t pos = 0;
  		const T *p = 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 == data() ) return ThisXStringClass().takeValueFrom(".");
  		}
  		return subString(lastSepPos+1, MAX_XSIZE);
  	}
  	ThisXStringClass dirname() const
  	{
  	  size_t idx;
      idx = rindexOf('\\');
      if ( idx != MAX_XSIZE ) return subString(0, idx);
      idx = rindexOf('/');
      if ( idx != MAX_XSIZE ) return subString(0, idx);
  		return ThisXStringClass();
  	}
  
  //	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;
  //}
  
  	//--------------------------------------------------------------------- strcmp, equal, comparison operator
  
  	template<typename O>
  	int strcmp(const O* S) const { return XStringAbstract__compare(data(), S, false); }
  //	int Compare(const char* S) const { return ::Compare<T, char>(data(), S); }
  //	int Compare(const char16_t* S) const { return ::Compare<T, char16_t>(data(), S); };
  //	int Compare(const char32_t* S) const { return ::Compare<T, char32_t>(data(), S); };
  //	int Compare(const wchar_t* S) const { return ::Compare<T, wchar_t>(data(), S); };
  //
  
    template<typename O>
    int strncmp(const O* S, size_t n) const { return XStringAbstract__ncompare(data(), S, n, false); }
  
  	template<typename O, class OtherXStringClass>
  	bool isEqual(const __String<O, OtherXStringClass>& S) const { return XStringAbstract__compare(data(), S.s(), false) == 0; }
  	template<typename O>
  	bool isEqual(const O* S) const { return XStringAbstract__compare(data(), S, false) == 0; }
  
  	template<typename O, class OtherXStringClass>
  	bool isEqualIC(const __String<O, OtherXStringClass>& S) const { return XStringAbstract__compare(data(), S.s(), true) == 0; }
  	template<typename O>
  	bool isEqualIC(const O* S) const { return XStringAbstract__compare(data(), S, true) == 0; }
  
  //	bool SubStringEqual(size_t Pos, const T* S) const { return (memcmp(data(Pos), S, wcslen(S)) == 0); }
  
    template<typename IntegralType, typename O, class OtherXStringClass>
    bool isEqualAtIC(IntegralType pos, const __String<O, OtherXStringClass>& S) const
    {
      
  #ifdef JIEF_DEBUG
      if ( pos < 0 ) panic("XString::equalAtIC -> i < 0");
  #else
      if ( pos < 0 ) return false;
  #endif
  
      if ( (unsigned_type(IntegralType))pos > length() - S.length() ) return false;
      return XStringAbstract__ncompare(data() + (unsigned_type(IntegralType))pos, S.s(), S.length(), true) == 0;
    }
  
  public:
  	// == operator
  	template<typename O, class OtherXStringClass>
  	bool operator == (const __String<O, OtherXStringClass>& s2) const { return (*this).strcmp(s2.s()) == 0; }
  //	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; }
  
  	template<typename O, class OtherXStringClass>
  	bool operator != (const __String<O, OtherXStringClass>& s2) const { return !(*this == s2); }
  //	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; }
  
  	template<typename O, class OtherXStringClass>
  	bool operator <  (const __String<O, OtherXStringClass>& s2) const { return (*this).strcmp(s2.s()) < 0; }
  //	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; }
  
  	template<typename O, class OtherXStringClass>
  	bool operator >  (const __String<O, OtherXStringClass>& s2) const { return (*this).strcmp(s2.s()) > 0; }
  //	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; }
  
  	template<typename O, class OtherXStringClass>
  	bool operator <= (const __String<O, OtherXStringClass>& s2) const { return (*this).strcmp(s2.s()) <= 0; }
  //	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; }
  
  	template<typename O, class OtherXStringClass>
  	bool operator >= (const __String<O, OtherXStringClass>& s2) const { return (*this).strcmp(s2.s()) >= 0; }
  //	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; }
  
  };
  
  
  //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  LString  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  
  template<class T, class ThisXStringClass>
  class LString : public __String<T, ThisXStringClass>
  {
  public:
  protected:
  #ifdef XSTRING_CACHING_OF_SIZE
    constexpr LString(const T* s) : __String<T, ThisXStringClass>(s, 0) {};
    constexpr LString(const T* s, size_t size) : __String<T, ThisXStringClass>(s, size) {};
  	constexpr LString(const LString& L) : __String<T, ThisXStringClass>(L.data(), L.size()) {};
  #else
  	constexpr LString(const T* s) : __String<T, ThisXStringClass>(s) {};
  	constexpr LString(const LString& L) : __String<T, ThisXStringClass>(L.data()) {};
  #endif
  
  	constexpr LString() = delete;
  
  	// no assignement, no destructor
  
  };
  
  //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  
  
  
  ///* __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)));
  
  
  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; };
  
  #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
  
  
  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; };
  
  #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
  
  /* __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 */
  template <typename T1, typename T2, typename Tdummy=void>
  struct __string_class_or;
  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; };
  
  
  /* ------------  get_char_ptr(x) --------------*/
  template<typename T, typename Tdummy=void>
  struct _xstringarray__char_type;
  
  template<typename T>
  struct _xstringarray__char_type<T, enable_if_t(is___String(T))>
  {
    static const typename T::char_t* getCharPtr(const T& t) { return t.s(); }
  };
  
  template<typename T>
  struct _xstringarray__char_type<T*, enable_if_t(is_char(T))>
  {
      static const T* getCharPtr(T* t) { return t; }
  };
  
  //template<typename T>
  //struct _xstringarray__char_type<const T*, enable_if_t(is_char(T))>
  //{
  //    static const T* getCharPtr(const T* t) { return t; }
  //};
  
  template<typename T>
  struct _xstringarray__char_type<const T[]>
  {
      static const T* getCharPtr(T* t) { return t; }
  };
  
  template<typename T, size_t _Np>
  struct _xstringarray__char_type<T[_Np]>
  {
      static const T* getCharPtr(const T* t) { return t; }
  };
  
  #ifdef _MSC_VER
  // I don't know why it's needed with VS.
  
  template<typename T>
  struct _xstringarray__char_type<T, enable_if_t(is___LString(T))>
  {
    static const typename T::char_t* getCharPtr(const T& t) { return t.s(); }
  };
  
  
  #endif
  
  #define get_char_ptr(x) _xstringarray__char_type<typeof(x)>::getCharPtr(x)
  
  
  
  //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  
  
  //#define data() super::data()
  
  template<class T, class ThisXStringClass>
  class XStringAbstract : public __String<T, ThisXStringClass>
  {
    using super = __String<T, ThisXStringClass>;
  	static T nullChar;
  
    protected:
    	size_t m_allocatedSize; // Must include null terminator. Real memory allocated is only m_allocatedSize (not m_allocatedSize+1)
  	
  	//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  	// Init , Alloc
  	//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  	
  	/*
  	 * nNewSize must include null terminator.
  	 */
  	void Alloc(size_t nNewAllocatedSize)
  	{
  			if ( m_allocatedSize == 0 ) {
          super::__m_data = (T*)malloc( nNewAllocatedSize*sizeof(T) );
        }
  			else {
          super::__m_data = (T*)Xrealloc(super::__m_data, nNewAllocatedSize*sizeof(T), m_allocatedSize*sizeof(T));
        }
  			if ( !super::__m_data ) {
          log_technical_bug("XStringAbstract::Alloc(%zu) : Xrealloc(%" PRIuPTR ", %lu, %zd) returned NULL. System halted\n", nNewAllocatedSize, uintptr_t(super::__m_data), nNewAllocatedSize*sizeof(T), m_allocatedSize*sizeof(T));
          m_allocatedSize = 0;
          return;
  			}
  			m_allocatedSize = nNewAllocatedSize;
  	}
  
  //  public:
    /*
     * Make sure this string has allocated size of at least nNewSize+1.
     */
  	bool CheckSize(size_t nNewAllocatedSize, 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 < nNewAllocatedSize+1 )
  		{
  			nNewAllocatedSize += nGrowBy;
  			if ( m_allocatedSize == 0 ) { //if ( *data() ) {
          // Even if m_allocatedSize == 0, data() might not be NULL because it can points to a litteral.
          // So we need to alloc and cpy the litteral in the newly allocated buffer.
  				size_t size = super::size();
  				if ( nNewAllocatedSize < size ) nNewAllocatedSize = size;
  				const T* m_dataSav = super::data();
  				//super::__m_data = NULL; // No need, Alloc will reassign it.
  				Alloc(nNewAllocatedSize+1);
  				utf_string_from_utf_string(super::__m_data, m_allocatedSize, m_dataSav);
          return true;
  			}else{
  				Alloc(nNewAllocatedSize+1);
          return true;
  			}
      }
  		return false;
  	}
  
  public:
  
  	/* default ctor */
  #ifdef XSTRING_CACHING_OF_SIZE
  	XStringAbstract() : __String<T, ThisXStringClass>(&nullChar, 0), m_allocatedSize(0) {}
  #else
  	XStringAbstract() : __String<T, ThisXStringClass>(&nullChar), m_allocatedSize(0) {}
  #endif
  	
  	/* copy ctor */
  #ifdef XSTRING_CACHING_OF_SIZE
  	XStringAbstract(const XStringAbstract& S) : __String<T, ThisXStringClass>(&nullChar, 0), m_allocatedSize(0)
  #else
  	XStringAbstract(const XStringAbstract& S) : __String<T, ThisXStringClass>(&nullChar), m_allocatedSize(0)
  #endif
  	{
      *this = S;
  	}
  
  	~XStringAbstract()
  	{
  		//DBG_XSTRING("Destructor :%ls\n", data());
  		if ( m_allocatedSize > 0 ) free((void*)super::__m_data);
  	}
  
  #ifdef XSTRING_CACHING_OF_SIZE
  	/* ctor */
  	template<class OtherLStringClass>
  	explicit XStringAbstract(const LString<T, OtherLStringClass>& S) : __String<T, ThisXStringClass>(S.s(), S.size()), m_allocatedSize(0) {}
  	
  	template<typename O, class OtherXStringClass>
  	explicit XStringAbstract<T, ThisXStringClass>(const XStringAbstract<O, OtherXStringClass>& S) : __String<T, ThisXStringClass>(&nullChar, 0), 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); }
  //
  #else
  	/* 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); }
  //
  #endif
  	/* Copy Assign */
  	XStringAbstract& operator=(const XStringAbstract &S) {
      if ( S.data()  &&  S.m_allocatedSize == 0 ) {
        // S points to a litteral
        if ( m_allocatedSize > 0 ) {
          delete super::__m_data;
          m_allocatedSize = 0;
        }
        super::__m_data = (T*)S.data(); // because it's a litteral, we don't copy. We need to cast, but we won't modify.
        #ifdef XSTRING_CACHING_OF_SIZE
          super::__m_size = S.size();
        #endif
      }else{
        strsicpy(S.s(), S.size());
      }
      return *this;
    }
    
  	/* Assign */
  	#ifndef _MSC_VER
  	#pragma GCC diagnostic push
  	#pragma GCC diagnostic ignored "-Weffc++"
  	#endif
  	template<typename O, class OtherXStringClass>
  	ThisXStringClass& operator =(const __String<O, OtherXStringClass>& S)	{ strcpy(S.s()); return *((ThisXStringClass*)this); }
  	#ifndef _MSC_VER
    #pragma GCC diagnostic pop
  	#endif
    
  // 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_ask("XStringAbstract::takeValueFromLiteral -> m_allocatedSize > 0");
  //    }
  //		super::__m_data = (T*)s;
  //#ifdef XSTRING_CACHING_OF_SIZE
  //  super::__m_size = strlen(s);
  //#endif
  //		return *((ThisXStringClass*)this);
  //	}
  
  public:
  	
  	size_t allocatedSize() const { return m_allocatedSize; }
  	
  	void setEmpty()
  	{
  		if ( m_allocatedSize <= 0 ) super::__m_data = &nullChar;
  		else super::__m_data[0] = 0;
      #ifdef XSTRING_CACHING_OF_SIZE
        super::__m_size = 0;
      #endif
  	}
  
  //  T* data() { return data(); }
    const T* data() const { return super::data(); }
  
    template<typename IntegralType, enable_if(is_integral(IntegralType))>
    const T* data(IntegralType pos) const { return super::_data(pos); }
  //  template<typename IntegralType, enable_if(is_integral(IntegralType))>
  //  T* data(IntegralType pos) { return super::_data(pos); }
  	
  	template<typename IntegralType, enable_if(is_integral(IntegralType))>
  	T* dataSized(IntegralType size)
  	{
  		if ( size<0 ) {
        log_technical_bug("T* dataSized() -> i < 0");
        return NULL;
      }
  		if ( (unsigned_type(IntegralType))size > MAX_XSIZE ) {
        log_technical_bug("T* dataSized() -> i > MAX_XSIZE");
        return NULL;
      }
  		CheckSize((unsigned_type(IntegralType))size, 0);
  		return super::_data(0);
  	}
    void updateSize() {
      #ifdef XSTRING_CACHING_OF_SIZE
        super::__m_size = 0; // Jief, TODO
      #endif
    }
  //
  //	// 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(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 = super::__m_data;
  		super::__m_data = &nullChar;
      #ifdef XSTRING_CACHING_OF_SIZE
        super::__m_size = 0;
      #endif
  		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(data(), &otherChar, 1);
  			CheckSize(newSize, 0);
  			utf_string_from_utf_string_len(super::__m_data, m_allocatedSize, &otherChar, 1);
  			super::__m_data[newSize] = 0;
        #ifdef XSTRING_CACHING_OF_SIZE
          super::__m_size = newSize;
        #endif
  		}else{
  			setEmpty();
  		}
  	}
  	/* strcpy */
  	template<typename O>
  	void strcpy(const O* other)
  	{
  		if ( other && *other ) {
  			size_t newSize = utf_size_of_utf_string(data(), other);
  			CheckSize(newSize, 0);
  			utf_string_from_utf_string(super::__m_data, m_allocatedSize, other);
        super::__m_data[newSize] = 0;
        #ifdef XSTRING_CACHING_OF_SIZE
          super::__m_size = newSize;
          XSTRING_CHECK_SIZE;
        #endif
  		}else{
  			setEmpty();
  		}
  	}
  	/* 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(data(), other, other_len);
  			CheckSize(newSize, 0);
  			utf_string_from_utf_string_len(super::__m_data, m_allocatedSize, other, other_len);
        super::__m_data[newSize] = 0;
        #ifdef XSTRING_CACHING_OF_SIZE
          super::__m_size = newSize;
          XSTRING_CHECK_SIZE;
        #endif
  		}else{
  			setEmpty();
  		}
  	}
    template<typename O>
    void strsicpy(const O* other, size_t other_size)
    {
      if ( other && *other && other_size > 0 ) {
        CheckSize(other_size, 0);
        utf_string_from_utf_string_size(super::__m_data, m_allocatedSize, other, other_size); // TODO:utf_string_from_utf_string_SIZE
        super::__m_data[other_size] = 0;
        #ifdef XSTRING_CACHING_OF_SIZE
          super::__m_size = other_size;
        #endif
      }else{
        setEmpty();
      }
    }
  
  	/* strcat char */
  	template<typename O, enable_if(is_char(O))>
  	void strcat(O otherChar)
  	{
  		if ( otherChar ) {
  			size_t currentSize = super::size();
  			size_t newSize = currentSize + utf_size_of_utf_string_len(data(), &otherChar, 1);
  			CheckSize(newSize);
  			utf_string_from_utf_string_len(super::__m_data+currentSize, m_allocatedSize, &otherChar, 1);
        super::__m_data[newSize] = 0;
        #ifdef XSTRING_CACHING_OF_SIZE
          super::__m_size = newSize;
        #endif
  		}else{
  			// nothing to do
  		}
  	}
    /* strcat char* */
    template<typename O>
    void strcat(const O* other)
    {
      if ( other && *other ) {
        size_t currentSize = super::size(); // size is number of T, not in bytes
        size_t newSize = currentSize + utf_size_of_utf_string(data(), other); // size is number of T, not in bytes
        CheckSize(newSize);
        utf_string_from_utf_string(super::__m_data+currentSize, m_allocatedSize-currentSize, other);
        super::__m_data[newSize] = 0;
        #ifdef XSTRING_CACHING_OF_SIZE
          super::__m_size = newSize;
        #endif
      }else{
        // nothing to do
      }
    }
  	/* strcat __String */
  	template<typename OtherCharType, class OtherXStringClass>
  	void strcat(const __String<OtherCharType, OtherXStringClass>& other)
  	{
  		size_t currentSize = super::size(); // size is number of T, not in bytes
  		size_t newSize = currentSize + utf_size_of_utf_string(data(), other.s()); // size is number of T, not in bytes
  		CheckSize(newSize);
  		utf_string_from_utf_string(super::__m_data+currentSize, m_allocatedSize-currentSize, other.s());
      super::__m_data[newSize] = 0;
      #ifdef XSTRING_CACHING_OF_SIZE
        super::__m_size = newSize;
        XSTRING_CHECK_SIZE;
      #endif
  	}
  	/* strncat */
  	template<typename O>
  	void strncat(const O* other, size_t other_len)
  	{
  		if ( other && *other && other_len > 0 ) {
  			size_t currentSize = super::size();
  			size_t other_size = utf_size_of_utf_string_len(data(), other, other_len);
  			size_t newSize = currentSize + other_size;
  			CheckSize(newSize);
  			utf_string_from_utf_string_len(super::__m_data+currentSize, m_allocatedSize, other, other_len);
        super::__m_data[newSize] = 0;
        #ifdef XSTRING_CACHING_OF_SIZE
          super::__m_size = newSize;
          XSTRING_CHECK_SIZE;
        #endif
  		}else{
  			// nothing to do
  		}
  	}
    /* strsicat */
    template<typename O>
    void strsicat(const O* other, size_t other_size)
    {
      if ( other && *other && other_size > 0 ) {
        size_t currentSize = super::size();
        size_t newSize = currentSize + other_size;
        CheckSize(newSize);
        utf_string_from_utf_string_size(super::__m_data+currentSize, m_allocatedSize, other, other_size);
        super::__m_data[newSize] = 0;
        #ifdef XSTRING_CACHING_OF_SIZE
          super::__m_size = newSize;
          XSTRING_CHECK_SIZE;
        #endif
      }else{
        // nothing to do
      }
    }
  
    /* insert char* */
    template<typename O>
    ThisXStringClass&  insertAtPos(const O* other, size_t other_len, size_t pos)
    {
      if ( !other || !*other ) return *((ThisXStringClass*)this);
      
      size_t currentLength = super::length();
      if ( pos >= currentLength ) {
        strncat(other, other_len);
        return *((ThisXStringClass*)this);
      }
  
      size_t currentSize = super::size();
      size_t otherSize = utf_size_of_utf_string_len(data(), other, other_len);
      CheckSize(currentSize+otherSize);
      size_t start = size_of_utf_string_len(data(), pos); // size is number of T, not in bytes
      memmove( super::__m_data + start + otherSize, super::__m_data + start, (currentSize-start+1)*sizeof(T)); // memmove handles overlapping memory move
      utf_stringnn_from_utf_string(super::__m_data+start, otherSize, other);
  //    data()[newSize] = 0;
      #ifdef XSTRING_CACHING_OF_SIZE
        super::__m_size = currentSize+otherSize;
        XSTRING_CHECK_SIZE;
      #endif
      return *((ThisXStringClass*)this);
    }
    
    /* insert char* */
    template<typename O>
    ThisXStringClass&  insertAtPos(const O* other, size_t pos)
    {
      if ( !other || !*other ) return *((ThisXStringClass*)this);
      
      size_t currentLength = super::length();
      if ( pos >= currentLength ) {
        strcat(other);
        return *((ThisXStringClass*)this);
      }
  
      size_t currentSize = super::size();
      size_t otherSize = utf_size_of_utf_string(data(), other);
      CheckSize(currentSize+otherSize);
      size_t start = size_of_utf_string_len(data(), pos); // size is number of T, not in bytes
      memmove( data() + start + otherSize, data() + start, (currentSize-start+1)*sizeof(T)); // memmove handles overlapping memory move
      utf_stringnn_from_utf_string(data()+start, otherSize, other);
  //    data()[newSize] = 0;
      #ifdef XSTRING_CACHING_OF_SIZE
        super::__m_size = currentSize+otherSize;
        XSTRING_CHECK_SIZE;
      #endif
      return *((ThisXStringClass*)this);
    }
    /* insert char */
    template<typename O, enable_if(is_char(O))>
    void insertAtPos(O otherChar, size_t pos)
    {
      insertAtPos(&otherChar, 1, pos);
    }
  
    ThisXStringClass& deleteCharsAtPos(size_t pos, size_t count=1)
    {
      size_t currentLength = super::length();
      if ( pos >= currentLength ) return *((ThisXStringClass*)this);
  
      size_t currentSize = super::size(); // size is number of T, not in bytes
      CheckSize(currentSize, 0); // Although we only delete, we have to CheckSize in case this string point to a litteral.
  
      size_t start = size_of_utf_string_len(data(), pos); // size is number of T, not in bytes
  
  //    if ( pos+count >= currentLength ) count = currentLength - pos;
      if ( pos+count >= currentLength ) {
        super::__m_data[start] = 0;
        #ifdef XSTRING_CACHING_OF_SIZE
          super::__m_size = start;
        #endif
      }else{
        size_t end = start + size_of_utf_string_len(data()+start, count); // size is number of T, not in bytes
        memmove( super::__m_data + start, super::__m_data + end, (currentSize-end+1)*sizeof(T)); // memmove handles overlapping memory move
        #ifdef XSTRING_CACHING_OF_SIZE
          super::__m_size -= end-start;
        #endif
      }
      // Handle length change when implementing caching length feature.
      #ifdef XSTRING_CACHING_OF_SIZE
        XSTRING_CHECK_SIZE;
      #endif
      return *((ThisXStringClass*)this);
    }
    
    ThisXStringClass& replaceAll(char32_t charToSearch, char32_t charToReplaceBy)
    {
      size_t currentSize = super::sizeInNativeChars(); // size is number of T, not in bytes
      size_t charToSearchSize = utf_size_of_utf_string_len(data(), &charToSearch, 1); // size is number of T, not in bytes
      size_t charToReplaceBySize = utf_size_of_utf_string_len(data(), &charToReplaceBy, 1); // size is number of T, not in bytes
      // careful because 'charToReplaceBySize - charToSearchSize' overflows when charToSearchSize > charToReplaceBySize, which happens.
  
      char32_t char32;
      T* previousData = super::__m_data;
      T* previousP = super::__m_data;
      T* p = get_char32_from_string(previousP, &char32);
      while ( char32 ) {
        if (!char32) break;
        if ( char32 == charToSearch ) {
          if ( CheckSize(currentSize + charToReplaceBySize - charToSearchSize) ) {
            previousP = super::__m_data + ( previousP - previousData ); // CheckSize have reallocated. Correct previousP, p and previousData.
            p = super::__m_data + ( p - previousData );
            previousData = super::__m_data;
          }
          memmove(p+charToReplaceBySize-charToSearchSize, p, uintptr_t(super::__m_data + currentSize - p + 1)*sizeof(T));
          p += charToReplaceBySize;
          p -= charToSearchSize;
          currentSize += charToReplaceBySize;
          currentSize -= charToSearchSize;
          utf_stringnn_from_utf_string(previousP, charToReplaceBySize, &charToReplaceBy);
        }
        previousP = p;
        p = get_char32_from_string(previousP, &char32);
      }
      #ifdef XSTRING_CACHING_OF_SIZE
        super::__m_size = currentSize;
        XSTRING_CHECK_SIZE;
      #endif
      return *((ThisXStringClass*)this);
    }
    
    template<typename OtherXStringClass1, class OtherXStringClass2, enable_if( is___String(OtherXStringClass1) && is___String(OtherXStringClass2))>
    ThisXStringClass& replaceAll(const OtherXStringClass1& search, const OtherXStringClass2& replaceBy )
    {
      size_t currentSize = super::sizeInNativeChars(); // size is number of T, not in bytes
      size_t sizeLeft = currentSize; // size is number of T, not in bytes
      size_t searchSize = utf_size_of_utf_string(data(), search.s()); // size is number of T, not in bytes
      size_t replaceBySize = utf_size_of_utf_string(data(), replaceBy.s()); // size is number of T, not in bytes
      // careful because 'charToReplaceBySize - charToSearchSize' overflows when charToSearchSize > charToReplaceBySize, which happens.
  
  //    size_t pos = super::indexOf(search);
      T* previousData = super::__m_data;
      T* previousP = super::__m_data;
      T* p = super::__m_data;
      size_t pos = XStringAbstract__indexOf((const T**)&p, search.s(), 0, false);
      while ( pos != MAX_XSIZE ) {
        if ( CheckSize(currentSize + replaceBySize - searchSize) ) {
            previousP = super::__m_data + ( previousP - previousData );
            p = super::__m_data + ( p - previousData );
            previousData = super::__m_data;
        }
        sizeLeft -= uintptr_t(p-previousP);
        memmove(p+replaceBySize, p+searchSize, (sizeLeft - searchSize + 1)*sizeof(T));
  //      memmove(data()+pos+replaceBySize-searchSize, data()+pos, (currentSize - pos + 1)*sizeof(T));
        utf_stringnn_from_utf_string(p, replaceBySize, replaceBy.s());
        p += replaceBySize;
        currentSize += replaceBySize;
        currentSize -= searchSize;
        sizeLeft -= searchSize;
  // sizeLeft is equal to utf_size_of_utf_string(p, p);
        previousP = p;
        pos = XStringAbstract__indexOf((const T**)&p, search.s(), 0, false);
      }
      #ifdef XSTRING_CACHING_OF_SIZE
        super::__m_size = currentSize;
        XSTRING_CHECK_SIZE;
      #endif
      return *((ThisXStringClass*)this);
    }
  
  
    void trim()
    {
      size_t lengthInNativeBytes = super::sizeInNativeChars();
      if ( lengthInNativeBytes == 0 ) return;
      T* start = 0;
      T* s = super::__m_data;
      while ( *s && unsigned_type(T)(*s) <= 32 ) s++;
      if ( !*s ) {
        super::__m_data[0] = 0;
        #ifdef XSTRING_CACHING_OF_SIZE
          super::__m_size = 0;
          XSTRING_CHECK_SIZE;
        #endif
        return;
      }
      start = s;
      s = super::__m_data + lengthInNativeBytes - 1;
      while ( *s && unsigned_type(T)(*s) <= 32 ) s--;
      size_t newSize = uintptr_t(s - start) + 1;
      CheckSize(newSize, 0); // We have to CheckSize in case this string point to a litteral.
      memmove(super::__m_data, start, newSize*sizeof(T));
      super::__m_data[newSize] = 0;
      #ifdef XSTRING_CACHING_OF_SIZE
        super::__m_size = newSize;
        XSTRING_CHECK_SIZE;
      #endif
    }
  
    void lowerAscii()
    {
      size_t currentSize = super::size(); // size is number of T, not in bytes
      CheckSize(currentSize, 0); // We have to CheckSize in case this string point to a litteral.
      T* s = super::__m_data;
      while ( *s ) {
        *s = asciiToLower(*s);
        s++;
      }
    }
  
    void upperAscii()
    {
      size_t currentSize = super::size(); // size is number of T, not in bytes
      CheckSize(currentSize, 0); // We have to CheckSize in case this string point to a litteral.
      T* s = super::__m_data;
      while ( *s ) {
        *s = asciiToUpper(*s);
        s++;
      }
    }
  
    /* size is in number of technical chars, NOT in bytes */
    ThisXStringClass& stealValueFrom(T* S, size_t allocatedSize) {
      if ( m_allocatedSize > 0 ) delete super::__m_data;
      super::__m_data = S;
      #ifdef XSTRING_CACHING_OF_SIZE
        super::__m_size = utf_size_of_utf_string(super::__m_data, super::__m_data);
      #endif
      m_allocatedSize = allocatedSize;
      return *((ThisXStringClass*)this);
    }
  
    // Not sure we should keep that. We cannot know the allocated size. Therefore, a future realloc may fail as EDK want the old size.
    ThisXStringClass& stealValueFrom(T* S) {
      if ( m_allocatedSize > 0 ) delete super::__m_data;
      super::__m_data = S;
      #ifdef XSTRING_CACHING_OF_SIZE
        super::__m_size = utf_size_of_utf_string(super::__m_data, super::__m_data);
        m_allocatedSize = super::__m_size + 1;
      #else
        m_allocatedSize = super::size() + 1;
      #endif
      return *((ThisXStringClass*)this);
    }
  
    ThisXStringClass& stealValueFrom(ThisXStringClass* S) {
      if ( m_allocatedSize > 0 ) delete super::__m_data;
      #ifdef XSTRING_CACHING_OF_SIZE
        super::__m_size = S->size();
      #endif
      m_allocatedSize = S->m_allocatedSize;
      // do forgetDataWithoutFreeing() last : it'll zero the value of size and m_allocatedSize
      super::__m_data = S->forgetDataWithoutFreeing();
      return *((ThisXStringClass*)this);
    }
  
  	/* takeValueFrom */
  	template<typename O, class OtherXStringClass>
  	ThisXStringClass& takeValueFrom(const __String<O, OtherXStringClass>& S) { *this = S; return *((ThisXStringClass*)this); }
    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); }
  //	template<typename O, class OtherXStringClass>
  //	ThisXStringClass& takeValueFrom(const __String<O, OtherXStringClass>& S, size_t len) { strncpy(S.s(), 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); }
  
  	
  };
  
  template<class T, class ThisXStringClass>
  T XStringAbstract<T, ThisXStringClass>::nullChar = 0;
  
  
  //------------------------------------------------------- + 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; }
  
  
  
  //-------------------------------------------------------
  
  #undef DBG_XSTRING
  #undef asciiToLower
  //#undef data()
  
  
  #endif // __XSTRINGABSTRACT_H__
  