Trying to solve the problem with the buffer in printf(), I realised that I have to move to a different direction.
Instead of trying to encapsulate the C function and the callback inside Print Class (which actually does not have write() function, is only a virtual), it is better to write the printf() as native C++ member function of Print (aka Arduino style). The same approach is used anyway for the other functions of Print.
Now there is no C code, all printf functionality is in a function inside Print class, there is no reason to make callback to write something, it is very easy by calling the virtual function write() of Print. I also need a small internal function to calculate the digits of numerical values.
The total size of these 2 functions is 0x22+0x318 = 0x33A = 826 bytes (No buffers, no static variables)
To use add this code at the end of Print.cpp
//------------------------------------------------
#ifdef toupper
#undef toupper
#endif
#ifdef tolower
#undef tolower
#endif
#ifdef islower
#undef islower
#endif
#ifdef isdigit
#undef isdigit
#endif
#define toupper(c) ((c)&=0xDF)
#define tolower(c) ((c)|=0x20)
#define islower(c) ((unsigned char)c >= (unsigned char)'a' && (unsigned char)c <= (unsigned char)'z')
#define isdigit(c) ((unsigned char)c >= (unsigned char)'0' && (unsigned char)c <= (unsigned char)'9')
typedef union {
unsigned char byte[5];
long l;
unsigned long ul;
float f;
const char *ptr;
} value_t;
size_t Print::printDigit(unsigned char n, bool lower_case)
{
register unsigned char c = n + (unsigned char)'0';
if (c > (unsigned char)'9') {
c += (unsigned char)('A' - '0' - 10);
if (lower_case)
c += (unsigned char)('a' - 'A');
}
return write(c);
}
static void calculateDigit (value_t* value, unsigned char radix)
{
unsigned long ul = value->ul;
unsigned char* pb4 = &value->byte[4];
unsigned char i = 32;
do {
*pb4 = (*pb4 << 1) | ((ul >> 31) & 0x01);
ul <<= 1;
if (radix <= *pb4 ) {
*pb4 -= radix;
ul |= 1;
}
} while (--i);
value->ul = ul;
}
size_t Print::printf(const char *format, ...)
{
va_list ap;
bool left_justify;
bool zero_padding;
bool prefix_sign;
bool prefix_space;
bool signed_argument;
bool char_argument;
bool long_argument;
bool lower_case;
value_t value;
int charsOutputted;
bool lsd;
unsigned char radix;
unsigned char width;
signed char decimals;
unsigned char length;
char c;
// reset output chars
charsOutputted = 0;
va_start(ap, format);
while( c=*format++ ) {
if ( c=='%' ) {
left_justify = 0;
zero_padding = 0;
prefix_sign = 0;
prefix_space = 0;
signed_argument = 0;
char_argument = 0;
long_argument = 0;
radix = 0;
width = 0;
decimals = -1;
get_conversion_spec:
c = *format++;
if (c=='%') {
charsOutputted+=write(c);
continue;
}
if (isdigit(c)) {
if (decimals==-1) {
width = 10*width + c - '0';
if (width == 0) {
zero_padding = 1;
}
} else {
decimals = 10*decimals + c - '0';
}
goto get_conversion_spec;
}
if (c=='.') {
if (decimals==-1)
decimals=0;
else
; // duplicate, ignore
goto get_conversion_spec;
}
if (islower(c)) {
c = toupper(c);
lower_case = 1;
} else
lower_case = 0;
switch( c ) {
case '-':
left_justify = 1;
goto get_conversion_spec;
case '+':
prefix_sign = 1;
goto get_conversion_spec;
case ' ':
prefix_space = 1;
goto get_conversion_spec;
case 'B': /* byte */
char_argument = 1;
goto get_conversion_spec;
// case '#': /* not supported */
case 'H': /* short */
case 'J': /* intmax_t */
case 'T': /* ptrdiff_t */
case 'Z': /* size_t */
goto get_conversion_spec;
case 'L': /* long */
long_argument = 1;
goto get_conversion_spec;
case 'C':
if( char_argument )
c = va_arg(ap,char);
else
c = va_arg(ap,int);
charsOutputted+=write(c);
break;
case 'S':
value.ptr = va_arg(ap,const char *);
length = strlen(value.ptr);
if ( decimals == -1 ) {
decimals = length;
}
if ( ( !left_justify ) && (length < width) ) {
width -= length;
while( width-- != 0 ) {
charsOutputted+=write(' ');
}
}
while ( (c = *value.ptr) && (decimals-- > 0)) {
charsOutputted+=write(c);
value.ptr++;
}
if ( left_justify && (length < width)) {
width -= length;
while( width-- != 0 ) {
charsOutputted+=write(' ');
}
}
break;
case 'D':
case 'I':
signed_argument = 1;
radix = 10;
break;
case 'O':
radix = 8;
break;
case 'U':
radix = 10;
break;
case 'X':
radix = 16;
break;
default:
// nothing special, just output the character
charsOutputted+=write(c);
break;
}
if (radix != 0) {
unsigned char store[6];
unsigned char *pstore = &store[5];
if (char_argument) {
value.l = va_arg(ap, char);
if (!signed_argument) {
value.l &= 0xFF;
}
} else if (long_argument) {
value.l = va_arg(ap, long);
} else { // must be int
value.l = va_arg(ap, int);
if (!signed_argument) {
value.l &= 0xFFFF;
}
}
if ( signed_argument ) {
if (value.l < 0)
value.l = -value.l;
else
signed_argument = 0;
}
length=0;
lsd = 1;
do {
value.byte[4] = 0;
calculateDigit(&value, radix);
if (!lsd) {
*pstore = (value.byte[4] << 4) | (value.byte[4] >> 4) | *pstore;
pstore--;
} else {
*pstore = value.byte[4];
}
length++;
lsd = !lsd;
} while( value.ul );
if (width == 0) {
// default width. We set it to 1 to output
// at least one character in case the value itself
// is zero (i.e. length==0)
width = 1;
}
/* prepend spaces if needed */
if (!zero_padding && !left_justify) {
while ( width > (unsigned char) (length+1) ) {
charsOutputted+=write(' ');
width--;
}
}
if (signed_argument) { // this now means the original value was negative
charsOutputted+=write('-');
// adjust width to compensate for this character
width--;
} else if (length != 0) {
// value > 0
if (prefix_sign) {
charsOutputted+=write('+');
// adjust width to compensate for this character
width--;
} else if (prefix_space) {
charsOutputted+=write(' ');
// adjust width to compensate for this character
width--;
}
}
/* prepend zeroes/spaces if needed */
if (!left_justify) {
while ( width-- > length ) {
charsOutputted+=write( zero_padding ? '0' : ' ');
}
} else {
/* spaces are appended after the digits */
if (width > length)
width -= length;
else
width = 0;
}
/* output the digits */
while( length-- ) {
lsd = !lsd;
if (!lsd) {
pstore++;
value.byte[4] = *pstore >> 4;
} else {
value.byte[4] = *pstore & 0x0F;
}
charsOutputted+=printDigit(value.byte[4], lower_case);
}
}
} else {
charsOutputted+=write(c);
}
}
va_end(ap);
return (size_t)charsOutputted;
}