Usermode/libc - Actually fix strtoi (passing utest now)
[tpg/acess2.git] / Usermode / Libraries / libc.so_src / strtoi.c
index db6684f..84bf000 100644 (file)
@@ -12,7 +12,7 @@
 
 unsigned long long strtoull(const char *str, char **end, int base)
 {
-       long long       ret = 0;
+       unsigned long long      ret = 0;
        
        if( !str || base < 0 || base > 36 || base == 1 ) {
                if(end)
@@ -43,6 +43,10 @@ unsigned long long strtoull(const char *str, char **end, int base)
        if( base == 0 )
                base = 10;
 
+       // Value before getting within 1 digit of ULLONG_MAX
+       // - Used to avoid overflow in more accurate check
+       unsigned long long      max_before_ullong_max = ULLONG_MAX / base;
+       unsigned int    space_above = ULLONG_MAX - max_before_ullong_max * base;
        while( *str )
        {
                 int    next = -1;
@@ -58,22 +62,30 @@ unsigned long long strtoull(const char *str, char **end, int base)
                        if( 'a' <= *str && *str <= 'a'+base-10-1 )
                                next = *str - 'a' + 10;
                }
+               //_SysDebug("strtoull - ret=0x%llx,next=%i,str='%s'", ret, next, str);
                if( next < 0 )
                        break;
                
+               // If we're already out of range, keep eating
                if( ret == ULLONG_MAX ) {
                        errno = ERANGE;
                        str ++;
                        // Keep eating until first unrecognised character
                        continue;
                }
-               if( ret > (ULLONG_MAX-next)/base ) {
-                       //_SysDebug("strtoull - Out of range (0x%llx > 0x%llx)", ret, (ULLONG_MAX-next)/base);
-                       //_SysDebug("strtoull -              (%llu > %llu)", ret, (ULLONG_MAX-next)/base);
-                       ret = ULLONG_MAX;
-                       errno = ERANGE;
-                       str ++;
-                       continue;
+       
+               // Rough then accurate check against max value
+               if( ret >= max_before_ullong_max )
+               {
+                       //_SysDebug("strtoull - 0x%llx>0x%llx", ret, max_before_ullong_max);
+                       if( (ret - max_before_ullong_max) * base + next > space_above ) {
+                               //_SysDebug("strtoull - %u*%u+%u (%u) > %u",
+                               //      (unsigned int)(ret - max_before_ullong_max), base, next, space_above);
+                               ret = ULLONG_MAX;
+                               errno = ERANGE;
+                               str ++;
+                               continue;
+                       }
                }
                ret *= base;
                ret += next;
@@ -126,7 +138,8 @@ long long strtoll(const char *str, char **end, int base)
        //_SysDebug("strtoll - neg=%i,ret=%llu", neg, ret);
 
        if( neg ) {
-               if( -ret < LLONG_MIN ) {
+               // Abuses unsigned integer overflow
+               if( ret + LLONG_MIN < ret ) {
                        errno = ERANGE;
                        return LLONG_MIN;
                }

UCC git Repository :: git.ucc.asn.au