Realloc fail on union

Hi.

I'm trying to write a string library with SSO.

I'm using union that old short string under 11 char, and pointer, length and capacity for long string.

The library is a rewrite of WString.h.

The trouble is: if I create a short string, I cannot create a long string after. Create any long string work until I create a short.
Digging , I found that it was the allocation that fail. but I can't find why.

Tested on Arduino DUE.
Here the example I use:



#include "WString_OP.h"

void setup() {
  Serial.begin(9600);

Serial.println("long to long");
  MyString s5("tests3______");
  //MyString s6("ab");
  Serial.println(s5());
  //s5 = s5 + 'c';
  //s5.concat( s6 );
  Serial.println(s5());
  

  Serial.println("s to s");
  MyString s1("test");
  Serial.println(s1());
  MyString s2 = ("short");
  s1 = s2;
  Serial.println(s1());


  Serial.println("s to l");
  MyString s3("test");
  Serial.println(s3());
  //MyString s4 = ("long string test 1");
  
  MyString s4("long string test 1");
  s3 = s4;
  Serial.println(s3());

}

WString_OP.h:




/*
  WString.h - String library for Wiring & Arduino
  ...mostly rewritten by Paul Stoffregen...
  Copyright (c) 2009-10 Hernando Barragan.  All right reserved.
  Copyright 2011, Paul Stoffregen, paul@pjrc.com

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "arduino.h"

#ifndef String_class_OP_h
#define String_class_OP_h
#ifdef __cplusplus

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <avr/pgmspace.h>
#include <avr/dtostrf.h>

// When compiling programs with this class, the following gcc parameters
// dramatically increase performance and memory (RAM) efficiency, typically
// with little or no increase in code size.
//     -felide-constructors
//     -std=c++0x

class __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))

#define LONG_STRING 1

#define SHORT_STRING 0

#define SHORT_STRING_CAPACTY 11

#define LONG_STRING_FLAG 1<<7
#define LONG_STRING_FLAG_TO_LONG (1UL << 31)
/*
bool shortStringValide (const char* str)
{
  return ( strlen(str)<= SHORT_STRING_CAPACTY ) ? 1 : 0; 
}

bool shortStringValide (int len)
{
  return ( len <= SHORT_STRING_CAPACTY ) ? 1 : 0; 
}*/


// An inherited class for holding the result of a concatenation.  These
// result objects are assumed to be writable by subsequent concatenations.
class MyStringSumHelper;






// The string class
class MyString
{

// use a function pointer to allow for "if (s)" without the
	// complications of an operator bool(). for more information, see:
	// http://www.artima.com/cppsource/safebool.html
	typedef void (MyString::*MyStringIfHelperType)() const;
	void MyStringIfHelper() const {}

public:
	// constructors                                 ---> should add shortstring description
	// creates a copy of the initial value.
	// if the initial value is null or invalid, or if memory allocation
	// fails, the string will be marked as invalid (i.e. "if (s)" will
	// be false).
  MyString( const char* cstr = "");
  MyString(const MyString &value);
  MyString(const __FlashStringHelper *str); // to implement
     #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
	MyString(MyString &&rval); // to test
	//MyString(MyStringSumHelper &&rval); //to impelement
	#endif
  explicit MyString(char c);
  explicit MyString(unsigned char, unsigned char base=10);
  explicit MyString(int, unsigned char base=10);
  explicit MyString(unsigned int, unsigned char base=10);
  explicit MyString(long, unsigned char base=10);
	explicit MyString(unsigned long, unsigned char base=10);
	explicit MyString(float, unsigned char decimalPlaces=2);
	explicit MyString(double, unsigned char decimalPlaces=2);
  ~MyString();



  //!!!!!!!!!! have to add short string memory management description


  
  // memory management
	// return true on success, false on failure (in which case, the string
	// is left unchanged).  reserve(0), if successful, will validate an
	// invalid string (i.e., "if (s)" will be true afterwards)
  unsigned char reserve(unsigned int size);
  inline const unsigned int length() const;
  const char * operator()() const;
  MyString & operator = (const MyString &rhs);
  MyString & operator = (const char *cstr);
  MyString & operator = (const __FlashStringHelper *str); //to test
     #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
	MyString & operator = (MyString &&rval);
	//MyString & operator = (MyStringSumHelper &&rval);
	#endif

  	// concatenate (works w/ built-in types)

	// returns true on success, false on failure (in which case, the string
	// is left unchanged).  if the argument is null or invalid, the
	// concatenation is considered unsucessful.
	unsigned char concat(const MyString &str);
	unsigned char concat(const char *cstr);
  unsigned char concat(char c);
  unsigned char concat(unsigned char c);
	unsigned char concat(int num);//not singlely tested but other overload works
	unsigned char concat(unsigned int num);//not singlely tested but other overload works
	unsigned char concat(long num);//not singlely tested but other overload works
	unsigned char concat(unsigned long num);//not singlely tested but other overload works
	unsigned char concat(float num);//not singlely tested but other overload works
	unsigned char concat(double num);//not singlely tested but other overload works
	//unsigned char concat(const __FlashStringHelper * str);

  friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, const MyString &rhs);
	friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, const char *cstr);
	friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, char c);
	friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, unsigned char num);
	friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, int num);
	friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, unsigned int num);
	friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, long num);
	friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, unsigned long num);
	friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, float num);
	friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, double num);
	//friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, const __FlashStringHelper *rhs);

protected:
  //DATA structure
  struct LongString
  {
    char* data;
    unsigned long capacity;
    unsigned long len;
  };

  struct ShortString
  {
    char data[SHORT_STRING_CAPACTY];
    byte len;
  };
  struct ShortStringArray { //trick, if size and flag == 0 size return 11 and the last but is use as null caracter
    char data[SHORT_STRING_CAPACTY+1];
  };

  union Buffer
  {
    ShortString shortString;
    LongString longString;
    ShortStringArray shortStringArray;
  };
  Buffer buffer;

protected:
  inline void init(void);
  void setLen( byte len );
  bool bufferIsLongString() const { return buffer.shortString.len & LONG_STRING_FLAG; }
  void setLongShortStringFlag(bool flag);
  void invalidate(void);
  unsigned char changeBuffer(unsigned int maxStrLen);
  unsigned char concat(const char *cstr, unsigned int length);

  MyString & copy(const char *cstr, unsigned int length);
  MyString & copy(const __FlashStringHelper *pstr, unsigned int length); //to implement
     #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
	void move(MyString &rhs);
	#endif
  unsigned int shortToLong( MyString & str, int newLen );
  
};


class MyStringSumHelper : public MyString
{
public:
	MyStringSumHelper(const MyString &s) : MyString(s) {}
	MyStringSumHelper(const char *p) : MyString(p) {}
	MyStringSumHelper(char c) : MyString(c) {}
	MyStringSumHelper(unsigned char num) : MyString(num) {}
	MyStringSumHelper(int num) : MyString(num) {}
	MyStringSumHelper(unsigned int num) : MyString(num) {}
	MyStringSumHelper(long num) : MyString(num) {}
	MyStringSumHelper(unsigned long num) : MyString(num) {}
	MyStringSumHelper(float num) : MyString(num) {}
	MyStringSumHelper(double num) : MyString(num) {}
};

#endif  // __cplusplus
#endif  // String_class_h


WString_OP.cpp




/*
  WString.cpp - String library for Wiring & Arduino
  ...mostly rewritten by Paul Stoffregen...
  Copyright (c) 2009-10 Hernando Barragan.  All rights reserved.
  Copyright 2011, Paul Stoffregen, paul@pjrc.com

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "WString_OP.h"

/*********************************************/
/*  Constructors                             */
/*********************************************/

MyString::MyString( const char* cstr ) {
  init();
  if ( cstr ) {
    int len = strlen( cstr );
    copy( cstr, len );
  }
  /* old version
init();
if (cstr) copy(cstr, strlen(cstr));
  */
  
}

MyString::MyString(const MyString &value)
{
    init();
    *this = value;
}
/*
MyString::MyString(const __FlashStringHelper *pstr)
{
	init();
	*this = pstr;
}
*/
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
MyString::MyString(MyString &&rval)
{
	init();
	move(rval);
}
/*MyString::MyString(MyStringSumHelper &&rval)
{
	init();
	move(rval);
}*/
#endif
MyString::MyString(char c)
{
  init();
  buffer.shortStringArray.data[0] = c;
  buffer.shortStringArray.data[1] = 0;
  //setLen(1);
  /*old version
	init();
	char buf[2];
	buf[0] = c;
	buf[1] = 0;
	*this = buf;*/
}

MyString::MyString(unsigned char value, unsigned char base)
{
  init();
  utoa(value, buffer.shortStringArray.data , base);
  /* old version
	init();
	char buf[1 + 8 * sizeof(unsigned char)];
	utoa(value, buf, base);
	*this = buf;
  */
}

MyString::MyString(int value, unsigned char base)
{ 
	init();
	char buf[2 + 8 * sizeof(int)];
	itoa(value, buf, base);
	*this = buf;
}

MyString::MyString(unsigned int value, unsigned char base)
{
	init();
	char buf[1 + 8 * sizeof(unsigned int)];
	utoa(value, buf, base);
	*this = buf;
}

MyString::MyString(long value, unsigned char base)
{
	init();
	char buf[2 + 8 * sizeof(long)];
	ltoa(value, buf, base);
	*this = buf;
}

MyString::MyString(unsigned long value, unsigned char base)
{
	init();
	char buf[1 + 8 * sizeof(unsigned long)];
	ultoa(value, buf, base);
	*this = buf;
}

MyString::MyString(float value, unsigned char decimalPlaces)
{
	init();
	char buf[33];
	*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
}

MyString::MyString(double value, unsigned char decimalPlaces)
{
	init();
	char buf[33];
	*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
}

MyString::~MyString()
{
  if ( bufferIsLongString() && buffer.longString.data )
  {
    free( buffer.longString.data);
  }
  //free(buffer); //old version
}

/*********************************************/
/*  Memory Management                        */
/*********************************************/

void MyString::setLen( byte len )
{
  if ( !bufferIsLongString() ) {
    buffer.shortString.len = len | ( buffer.shortString.len & LONG_STRING_FLAG ); 
  }
  else {
    buffer.longString.len = len | ( buffer.longString.len & LONG_STRING_FLAG_TO_LONG );
  }
  
}

const unsigned int MyString::length() const
{
  if ( bufferIsLongString() ) {
    return buffer.longString.len & ~LONG_STRING_FLAG_TO_LONG;
  } else {
    if ( buffer.shortStringArray.data[0] == 0){
      return 0;
    } 
    else if ( buffer.shortString.len ) return buffer.shortString.len;
    else  return 11; //trick to use all 12 bytes, use "len" as null character
  }
}

void MyString::init(void)
{
///set to 0 all struct. good for both long and short string
  buffer.longString.data = nullptr;
  buffer.longString.len = 0;
  buffer.longString.capacity = 0;
}
  
void MyString::setLongShortStringFlag(bool flag) 
{
  buffer.shortString.len = buffer.shortString.len & ~LONG_STRING_FLAG; //reset flag
  buffer.shortString.len = buffer.shortString.len | flag << 7; //set flag
}

unsigned char MyString::reserve(unsigned int size)
{
  if (length() && buffer.longString.capacity >= size) return 1;
  if (changeBuffer(size)) {
    if (length() == 0) buffer.longString.data[0] = 0;
    return 1;
  }
  return 0;
  /* old version:
  if (buffer && capacity >= size) return 1;
  if (changeBuffer(size)) {
    if (len == 0) buffer[0] = 0;
    return 1;
  }
  return 0;
  */
}

const char * MyString::operator()() const {
  //Serial.println(buffer.longString.data);
  if ( bufferIsLongString() ) {
    return buffer.longString.data;
  }
  else return buffer.shortStringArray.data;
}

void MyString::invalidate(void)
{
  if (buffer.longString.data) free(buffer.longString.data);
  init();
  //buffer.longString.data = nullptr;
  //buffer.longString.capacity = buffer.longString.len = 0;
  /*
  if (buffer) free(buffer);
  buffer = NULL;
  capacity = len = 0;
  */
}

unsigned char MyString::changeBuffer(unsigned int maxStrLen)
{
  Serial.print("change buffer sixe: "); Serial.println(maxStrLen);
  char *newbuffer = (char *)realloc(buffer.longString.data, maxStrLen + 1);
  if (newbuffer) {
    Serial.println("alocation succed");
    buffer.longString.data = newbuffer;
    buffer.longString.capacity = maxStrLen;
    return 1;
  }
  return 0;
  /* old version:
  char *newbuffer = (char *)realloc(buffer, maxStrLen + 1);
  if (newbuffer) {
    buffer = newbuffer;
    capacity = maxStrLen;
    return 1;
  }
  return 0;
  */
}

/*********************************************/
/*  Copy and Move                            */
/*********************************************/


MyString &  MyString::copy(const char *cstr, unsigned int length)
{
  if ( !bufferIsLongString() ) init();
  setLongShortStringFlag ( (length <= SHORT_STRING_CAPACTY) ? 0 : 1);

  if ( bufferIsLongString() ) {
        Serial.print("copy ftion check long string: ");    Serial.println( cstr );
    if (!reserve(length)) {
      Serial.println( length );
      Serial.println( "invalidate" );
      invalidate();
      return *this;
    }
  setLen(length);
  strcpy(buffer.longString.data, cstr);
  return *this;
  } else {  //shortString
    if ( length == SHORT_STRING_CAPACTY ) buffer.shortString.len = 0; //trick to use all 12 byte for buffer
    else buffer.shortString.len = length;
    strcpy( buffer.shortStringArray.data, cstr);
    //Serial.println("copy short");
    
    return *this;
  }

}

MyString & MyString::copy(const __FlashStringHelper *pstr, unsigned int length)
{
/*	if (!reserve(length)) {
		invalidate();
		return *this;
	}
	len = length;
	strcpy_P(buffer, (PGM_P)pstr);*/
	return *this;
}

#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
void MyString::move(MyString &rhs)
{ 
  //handle to short string
  if ( rhs.length() <= SHORT_STRING_CAPACTY ) { //handle from long string
    if ( bufferIsLongString() && buffer.longString.data ) {
      free( buffer.longString.data );
    }
    init();
    setLen(rhs.length());
    strcpy(buffer.shortStringArray.data, rhs());
  } else { // handle to long string
    init();
    setLongShortStringFlag(LONG_STRING);
    setLen(rhs.length());
    buffer.longString.data = rhs.buffer.longString.data;
    rhs.buffer.longString.data = nullptr;
  }
  /*
	if (buffer) {
		if (rhs && capacity >= rhs.len) {
			strcpy(buffer, rhs.buffer);
			len = rhs.len;
			rhs.len = 0;
			return;
		} else {
			free(buffer);
		}
	}
	buffer = rhs.buffer;
	capacity = rhs.capacity;
	len = rhs.len;
	rhs.buffer = NULL;
	rhs.capacity = 0;
	rhs.len = 0;*/
}
#endif

unsigned int MyString::shortToLong( MyString & str, int newLen ) {
  if ( str.bufferIsLongString() ) return 1;
  char * buf =  new char[newLen +1];
  strcpy ( buf, str() );
  init();
  str.buffer.longString.data = buf;
  str.buffer.longString.capacity = newLen;
  str.buffer.longString.len = newLen;
  return 1;
}

MyString & MyString::operator = (const MyString &rhs) 
{
  if (this == &rhs) return *this;
  Serial.print("check copyable: ");
  Serial.println(rhs());

  if ( !rhs.bufferIsLongString() ) {
    if ( buffer.longString.data ) free( buffer.longString.data );
    Serial.println("copy short to short");
    buffer = rhs.buffer;
  } else {
    if ( !bufferIsLongString() ){
      init();
      Serial.println("init");
    } 
    setLongShortStringFlag( LONG_STRING );
    Serial.println("copy long string");
    copy(rhs.buffer.longString.data, rhs.length());
  }
  return *this;
  /*if ( bufferIsLongString() && !rhs.bufferIsLongString() ) {
    free(buffer.longString.data); //clear buffer if switching from long to short
    setLongShortStringFlag(SHORT_STRING);
  }	
	if (!bufferIsLongString() && rhs.bufferIsLongString()) {    
    if (rhs.buffer.longString.data) {
      init();
      setLongShortStringFlag(LONG_STRING);
      copy(rhs.buffer.longString.data, rhs.length());
    }
	  else invalidate();
  } else {
    this->buffer = rhs.buffer;
  }
  return *this;*/
 /*old version

	if (this == &rhs) return *this;
	
	if (rhs.buffer) copy(rhs.buffer, rhs.len);
	else invalidate();
	
	return *this;
*/	
}

MyString & MyString::operator = (const char *cstr)
{ 
  if ( cstr ) {
    int len = strlen(cstr);
    if ( bufferIsLongString() && len <= SHORT_STRING_CAPACTY) {
      free(buffer.longString.data); //clear buffer if switching from long to short
      setLongShortStringFlag(SHORT_STRING);
    }
    else if ( !bufferIsLongString() && len > SHORT_STRING_CAPACTY ) {
      init();
      setLongShortStringFlag(LONG_STRING);
    }
    copy(cstr, len);
    
  }
  return *this;
  /* old version
	if (cstr) copy(cstr, strlen(cstr));
	else invalidate();
	
	return *this;
  */
}

MyString & MyString::operator = (const __FlashStringHelper *pstr)
{
	if (pstr) copy(pstr, strlen_P((PGM_P)pstr));
	else invalidate();

	return *this;
}

#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
MyString & MyString::operator = (MyString &&rval)
{
   if (this != &rval) move(rval); 
  //old version
//	if (this != &rval) move(rval);
	return *this;
}
/*
String & String::operator = (StringSumHelper &&rval)
{
	if (this != &rval) move(rval);
	return *this;
}*/
#endif

/*********************************************/
/*  concat                                   */
/*********************************************/

unsigned char MyString::concat(const MyString &s)
{
  return concat( s(), s.length() );
	//return concat(s.buffer, s.len); //old version
}

unsigned char MyString::concat(const char *cstr, unsigned int clength)
{
  if ( clength == 0 ) return 1;
  unsigned int len = length();
  unsigned int newlen = len + clength;  
  if ( newlen > SHORT_STRING_CAPACTY ) { //handle long string
  //////////////need short to long routine....
    if ( !bufferIsLongString() && length() > 0) { //move short string to long string buffer      
      shortToLong( *this, newlen);
    }    
    else if (!reserve(newlen)) return 0;
    setLongShortStringFlag( LONG_STRING );      
    strcpy(buffer.longString.data + len, cstr);
    } else {// handle still fit short string
    strcpy(buffer.shortString.data + len, cstr);
  }
  setLen( newlen );
  return 1;
  //old version
/*	unsigned int newlen = len + length;
	if (!cstr) return 0;
	if (length == 0) return 1;
	if (!reserve(newlen)) return 0;
	strcpy(buffer + len, cstr);
	len = newlen;
	return 1;*/
}

unsigned char MyString::concat(const char *cstr)
{
	if (!cstr) return 0;
	return concat(cstr, strlen(cstr));
}

unsigned char MyString::concat(char c)
{
	char buf[2];
	buf[0] = c;
	buf[1] = 0;
	return concat(buf, 1);
}

unsigned char MyString::concat(unsigned char num)
{
	char buf[1 + 3 * sizeof(unsigned char)];
	itoa(num, buf, 10);
	return concat(buf, strlen(buf));
}

unsigned char MyString::concat(int num)
{
	char buf[2 + 3 * sizeof(int)];
	itoa(num, buf, 10);
	return concat(buf, strlen(buf));
}

unsigned char MyString::concat(unsigned int num)
{
	char buf[1 + 3 * sizeof(unsigned int)];
	utoa(num, buf, 10);
	return concat(buf, strlen(buf));
}

unsigned char MyString::concat(long num)
{
	char buf[2 + 3 * sizeof(long)];
	ltoa(num, buf, 10);
	return concat(buf, strlen(buf));
}

unsigned char MyString::concat(unsigned long num)
{
	char buf[1 + 3 * sizeof(unsigned long)];
	ultoa(num, buf, 10);
	return concat(buf, strlen(buf));
}

unsigned char MyString::concat(float num)
{
	char buf[20];
	char* string = dtostrf(num, 4, 2, buf);
	return concat(string, strlen(string));
}

unsigned char MyString::concat(double num)
{
	char buf[20];
	char* string = dtostrf(num, 4, 2, buf);
	return concat(string, strlen(string));
}

/*unsigned char String::concat(const __FlashStringHelper * str)
{
	if (!str) return 0;
	int length = strlen_P((const char *) str);
	if (length == 0) return 1;
	unsigned int newlen = len + length;
	if (!reserve(newlen)) return 0;
	strcpy_P(buffer + len, (const char *) str);
	len = newlen;
	return 1;
}*/


MyStringSumHelper & operator + (const MyStringSumHelper &lhs, const MyString &rhs)
{
	MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
	if (!a.concat(rhs(), rhs.length())) a.invalidate();
	return a;
}

MyStringSumHelper & operator + (const MyStringSumHelper &lhs, const char *cstr)
{
	MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
	if (!cstr || !a.concat(cstr, strlen(cstr))) a.invalidate();
	return a;
}

MyStringSumHelper & operator + (const MyStringSumHelper &lhs, char c)
{
	MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
	if (!a.concat(c)) a.invalidate();
  delay(1000);
	return a;
}

MyStringSumHelper & operator + (const MyStringSumHelper &lhs, unsigned char num)
{
	MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
	if (!a.concat(num)) a.invalidate();
	return a;
}
/*
MyStringSumHelper & operator + (const MyStringSumHelper &lhs, int num)
{
	MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
	if (!a.concat(num)) a.invalidate();
	return a;
}

MyStringSumHelper & operator + (const MyStringSumHelper &lhs, unsigned int num)
{
	MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
	if (!a.concat(num)) a.invalidate();
	return a;
}

MyStringSumHelper & operator + (const MyStringSumHelper &lhs, long num)
{
	MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
	if (!a.concat(num)) a.invalidate();
	return a;
}

MyStringSumHelper & operator + (const MyStringSumHelper &lhs, unsigned long num)
{
	MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
	if (!a.concat(num)) a.invalidate();
	return a;
}

MyStringSumHelper & operator + (const MyStringSumHelper &lhs, float num)
{
	MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
	if (!a.concat(num)) a.invalidate();
	return a;
}

MyStringSumHelper & operator + (const MyStringSumHelper &lhs, double num)
{
	MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
	if (!a.concat(num)) a.invalidate();
	return a;
}
*/

/*
MyStringSumHelper & operator + (const MyStringSumHelper &lhs, const __FlashStringHelper *rhs)
{
	MyStringSumHelper &a = const_cast<StringSumHelper&>(lhs);
	if (!a.concat(rhs))	a.invalidate();
	return a;
}
*/




Yeah. I don't even need to look at your code to know what's wrong. The essence of the problem is that those two things are mutually exclusive. You're using a union which makes you responsible for memory management; especially the poison pointer waiting in your Buffer.

But, like I said, I haven't looked at your code so I could be wrong.

You're trying to write a string library with Single Sign-on?

What is that?

Why are you doing that?

nop; SSO : Short string optimization.

Question: Is the data pointer in longString ever initialized to anything? Not clear to me it ever is. What does realloc() do if it is passed a bad/invalid/uninitialized pointer?

If the string is new, pointer is inilialized to nullptr ( init() function )

I must make sure that longstring pointer is nerver read when shortstring data is on. that will result on an invalid pointer.

Did you read the docs on realloc, in particular

Reallocates the given area of memory. It must be previously allocated by malloc(), calloc() or realloc() and not yet freed with a call to free or realloc . Otherwise, the results are undefined.

https://en.cppreference.com/w/c/memory/realloc

I can’t seem to find where you explicitly allocate memory in your code

This part is base on a copy of the original WString lib... ?

String( char*) > call init, then copy,
copy call reserve, call change buffer, that call realloc.
no malloc anywhere.

Strangely, it work until I use any short string... That what is strange.

in the sketch I provide, if you comment the first bloc with the 2 short string, the long string will print and is correctly allocated.

ok, i've try this:

unsigned char MyString::changeBuffer(unsigned int maxStrLen)
{
  Serial.print("change buffer sixe: "); Serial.println(maxStrLen);
  char *newbuffer = nullptr;
  if ( !buffer.longString.data ) {
    newbuffer = (char*) malloc (maxStrLen + 1);
    Serial.println("try malloc");
  }
  else newbuffer = (char *)realloc(buffer.longString.data, maxStrLen + 1);
  if (newbuffer) {
    Serial.println("alocation succed");
    buffer.longString.data = newbuffer;
    buffer.longString.capacity = maxStrLen;
    return 1;
  }
  return 0;

malloc have been tried, but failed to.

It failed on the newbuffer pointer... I really do not understand....

...highlights the mistake.

if you refer that I check for long string flag into shortString, it is because I put the flag into the last byte of the buffer union.

It append to be bit 31 of longString.len.

I admit it is a bit confusing

A small correction...

...flawed.

I suspect to make progress you are going to have to admit that and understand the reason it's flawed.

Pascal (variant record) and Rust (enum) both encourage correctly handling unions. In the case of Rust it's impossible to do the flawed thing.

Ho.

Strange that you could access data in both , if you shouldn't...

Found this explanation: docs.microsoft.com/en-us/cpp/cpp/unions

That complicate the design...

For the record: I have been able to make it work using new:

newbuffer = new char[maxStrLen +1];

but I understand it is not the proper way...

I will still work to find the best way...

one question: can I change, on the same object, the usage, or do I have to reconstruct it ?

From "man realloc":

If ptr is NULL, realloc() is identical to a call to malloc() for size bytes.

I'm guessing that the original code uses realloc(NULL, size) to do the initial allocation, but your SSO means that the initial short string is part of the union, rather than dynamically allocated, but your code is passing a pointer to it to realloc() anyway.

I think I've confirmed this. You have:


  //DATA structure
  struct LongString
  {
    char* data;
    unsigned long capacity;
    unsigned long len;
  };

  struct ShortString
  {
    char data[SHORT_STRING_CAPACTY];
    byte len;
  };

So if you realloc() an existing short string, the pointer address you're passing in will be some random value consisting of the first few characters of the old shortstring, rather than a valid pointer or null. That ... won't be good.

Before moving from short to long, I init() the buffer struct and Char * data is reset to nullptr:

MyString &  MyString::copy(const char *cstr, unsigned int length)
{
  if ( !bufferIsLongString() ) init();
  setLongShortStringFlag ( (length <= SHORT_STRING_CAPACTY) ? 0 : 1);
...

I can't figure why, but even if I try null to realloc, it fail on:

char * newbuffer = (char *)realloc(nullptr, maxStrLen + 1);
  if ( newbuffer == nullptr ) Serial.println (" allocation failed ");

anyway, Coding_Badly is right about the way I acces my data is wrong.

For what I read, it is probably cause by this. I will try to use an enum class instead of a inner flag to check the string length.

But don't you need to keep shortString.data intact so you can copy it into the long buffer?

not in that case, in this copy function, it is use to copy from a string literal or another string object,
on constructor and with operator=. so this copy overwrite the old content.

In concat, that was another story.