Despite the intention is to use it in a C++ project I put the c tag also because there is nothing C++-only (except references :)) in the code (I did not compile it by a C compiler though).
Preliminaries
I'm working on an automation system the main function of which is to control positional table. The table has 5 degrees of freedom each of which has a stepper motor attached to. Also I have the Arduino Nano board to control the motors. The communication between Arduino and PC is carried out by means of commands. Some commands contain numbers. An example of a command :
mz>100
where
m- means movez- degree of freedom (motion along the z axis)>- means clockwise100- number of steps
These are some objectives useful to better understand the context:
- Other numeric fields are also positive so
uint32_t. - I want to be able to easily write and debug commands manually so ASCII.
- Also a number can only be at the end (the last token) of a command.
Signatures
Serialization :
bool sr( uint32_t number, uint8_t* buff, const uint8_t size, uint8_t& pos)
number- number to serializebuff- buffer to serialize tosize- size ofbuffpos- number of used bytes to store the result
The role of pos is to be able to use this function in a cascaded maner so having an overload for other types: sr( number, ..., pos ); sr( enum, ..., pos ); ...
Deserialization is similar :
bool dsr( uint32_t& number, const uint8_t* buff, const uint8_t size, uint8_t pos )
pos- initial character of the number inbuff
Review Context
This code is intended for a -std=c++17 compiler.
Code
#define MAX_UINT32 0xffffffffU
#define MAX_UINT32_STR "4294967295"
#define BUFF_SIZE 20
bool sr( uint32_t number,
uint8_t* buff,
const uint8_t size,
uint8_t& pos )
{
uint32_t maxDivisor = 1;
for( ; number / maxDivisor >= 10; maxDivisor *= 10 ) { }
for( uint32_t divisor = maxDivisor; divisor > 0; divisor /= 10 )
{
if( pos >= size )
return false;
uint8_t digit = (number / divisor);
number = number % divisor;
buff[pos] = digit + '0';
pos++;
}
return true;
}
bool dsr( uint32_t& number,
const uint8_t* buff,
const uint8_t size,
uint8_t pos )
{
number = 0;
uint32_t maxNumberOfTens = MAX_UINT32 / 10;
uint32_t maxLastDigit = MAX_UINT32 % 10;
for( ; pos < size; pos++ )
{
uint8_t digit = buff[pos];
if( digit < '0' or digit > '9' )
return false;
digit -= '0';
if( number > maxNumberOfTens )
return false;
if( number == maxNumberOfTens and digit > maxLastDigit )
return false;
number = number * 10 + digit;
}
return true;
}
Test
Tested both in C++ and Arduino :
bool compare( const char* string, const uint8_t* buff, const uint8_t size )
{
if( size != strlen( string ) )
return false;
for( uint8_t i = 0; i < size; ++i )
if( buff[i] != string[i] )
return false;
return true;
}
void testSR( uint32_t input, const char* output )
{
uint8_t buff[BUFF_SIZE];
uint8_t pos = 0;
assert( sr( input, buff, BUFF_SIZE, pos ) );
assert( pos == strlen(output) );
assert( compare( output, buff, pos ) );
}
void testDSR( const char* input, const uint32_t output, const bool expected )
{
uint32_t result;
uint8_t buff[BUFF_SIZE] = { 0 };
if( not expected )
{
assert( not dsr( result, buff, BUFF_SIZE, 0 ) );
}
else
{
uint8_t i = 0;
for( ; i < strlen( input ); ++i )
buff[i] = input[i];
assert( dsr( result, buff, i, 0 ) );
assert( result == output );
}
}
int main()
{
testSR( 0U, "0" );
testSR( MAX_UINT32, MAX_UINT32_STR );
testSR( 9, "9" );
testSR( 10, "10" );
testSR( 999, "999" );
testSR( 10000, "10000" );
testSR( 1234567890, "1234567890" );
testSR( 1234567890, "1234567890" );
testDSR( "0", 0, true );
testDSR( MAX_UINT32_STR, MAX_UINT32, true );
testDSR( "9", 9, true );
testDSR( "10", 10, true );
testDSR( "999", 999, true );
testDSR( "10000", 10000, true );
testDSR( "1234567890", 1234567890, true );
testDSR( "-1", 0, false );
testDSR( "100a100", 100100, false );
testDSR( "12345678901", 0, false );
testDSR( "4294967296", 4294967295, false );
}
The sr and dsr functions are compiled to ~ 1.5 KB so not so much :)
Any help will be appreciated ! Thanks in advance !
ctag :) \$\endgroup\$strto...familiy I think I could. What others standard functions did you mean ? Could you provide the names ? Note, I want to use it both at the MC and at the PC levels. More over, I want to serialize other data such as structs and therefore it is better IMHO to have similar signatures, for example. And one more thing, I want it be exactlyuint32_tnotlongorunsigned. \$\endgroup\$<charconv>, functions likefrom_charsandto_chars. \$\endgroup\$