.h for declarations, definitions, etc. - not function code nor data
Segment code into a libint128.h and libint128.c and minimum #include<> lines in the .h file.
Strange to test if( !isdigit( *p ) ) { twice in asctou128()
Once is sufficient.
Don't bother defining U128_MIN
It is zero.
// #define U128_MIN (uint128)0
// #define U128_MAX (uint128)( ~U128_MIN )
#define U128_MAX ((uint128)-1)
Style: Tabs vs. spaces
Using spaces is potential more profitable.
Undefined behavior
is...(some_negative_not_EOF) is undefined behavior. Access via unsigned char * for maximum portability. A char may be negative.
// const char *p = s;
// ...
// while( isspace( *p ) ) {
const unsigned char *p = (const unsigned char *) s;
...
while( isspace( *p ) ) {
Do not use errno = ERANGE for a non-range issue
if( !isdigit( *p ) ) {
errno = ERANGE; // Do not do this.
// Re-think error indication strategy.
Unneeded cast
// val = (uint128)( 10 * val ) + (uint128)( *p - '0' );
val = 10 * val + (uint128)( *p - '0' );
Use boolean assignments
neg is used as a boolean flag. Use boolean constants.
// bool neg = 0;
bool neg = false;
Detect buffer access
I'd expect u128toasc() to return with a failure indication if the supplied buffer is too small.
I recommend to use an internal buffer of sufficient size for all int128 and then instead of memmove( str, str + j, len - j ), check size requirements (maybe test len > j) and then memcpy(str, local_buffer + j, len - j).
Use 10 for base 10 conversions
// while( x < -9 ) {
while( x <= -10 ) {
Minor: simplify
// memmove( str, str + j, len - j );
// return( str );
return memmove( str, str + j, len - j );
Way too many comments