Usermode/libc - Fix and unit test strtoull/* behavior (edge cases)
authorJohn Hodge <[email protected]>
Sat, 10 May 2014 05:11:44 +0000 (13:11 +0800)
committerJohn Hodge <[email protected]>
Sat, 10 May 2014 05:11:44 +0000 (13:11 +0800)
Usermode/Libraries/Makefile.tpl
Usermode/Libraries/libc.so_src/TEST_strtoi.c
Usermode/Libraries/libc.so_src/strtoi.c

index a99ff14..5317079 100644 (file)
@@ -115,10 +115,10 @@ $(OUTPUTDIR)Libs/%:
 
 obj-native/%.no: %.c
        @mkdir -p $(dir $@)
-       $(NCC) -c $< -o $@ -Wall -std=gnu99 -MD -MP -MF [email protected]
+       $(NCC) -g -c $< -o $@ -Wall -std=gnu99 -MD -MP -MF [email protected] '-D_SysDebug(f,v...)=fprintf(stderr,"DEBUG "f"\n",##v)' -include stdio.h
 
 TEST_%: obj-native/TEST_%.no obj-native/%.no
-       $(NCC) -o $@ $^
+       $(NCC) -g -o $@ $^
 
 -include $(UTESTS:%=obj-native/TEST_%.no.dep)
 -include $(UTESTS:%=obj-native/%.no.dep)
index 6d89224..62f2e69 100644 (file)
@@ -7,15 +7,69 @@
  */
 #include <stdio.h>
 #include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h>    // strerror
 
-#define TST(t, class, base, val, exp, fmt) do {\
-       t ret = strto##class(#val, NULL, base); \
+#define STR_(v)        #v
+#define STR(v) STR_(v)
+#define TST(t, class, base, val, exp, fmt, ofs, exp_errno) do {\
+       const char *in = val;\
+       char *end;\
+       errno = 0;\
+       t ret = strto##class(in, &end, base); \
        if( ret != exp ) \
-               printf("FAIL strto"#class"('"#val"') != "#val" (act 0x"fmt")\n", ret);\
+               fprintf(stderr, "FAIL strto"#class"('%s') != "#exp" (act "fmt")\n", in, ret);\
+       if( end != in+ofs ) \
+               fprintf(stderr, "FAIL strto"#class"('%s') returned wrong end: %p (+%zi) instead of %p (+%zi)\n",\
+                       in,end,end-in,in+ofs,(size_t)ofs);\
+       if( exp_errno != errno ) \
+               fprintf(stderr, "FAIL strto"#class"('%s') returned wrong errno, exp '%s', got '%s'\n",\
+                       in, strerror(exp_errno), strerror(errno));\
 }while(0)
 
+#define PRIMEBUF(fmt, val)     buf_len = snprintf(buf, sizeof(buf), fmt, val)
+
 int main(int argc, char *argv[])
 {
-       TST(unsigned long, ul, 0, 0x10ec, 0x10ec, "%lx");
-       TST(unsigned long long, ull, 0, 0xffeed10ec, 0xffeed10ec, "%llx");
+       char buf[64];
+       size_t  buf_len;
+       
+       // Success cases
+       TST(unsigned long, ul, 0, "0x10ec", 0x10ec, "%lx", 2+4, 0);
+       TST(unsigned long long, ull, 0, "0xffeed10ec", 0xffeed10ec, "%llx", 2+9, 0);
+       TST(unsigned long long, ull, 0, "01234567", 01234567, "%llo", 8, 0);
+       TST(unsigned long long, ull, 0, "1234567", 1234567, "%lld", 7, 0);
+       TST(long long, ll, 0, "-1", -1, "%lld", 2, 0);  // -1
+       
+       // Invalid strings
+       TST(unsigned long long, ull, 0, "0x",  0, "%llx", 1, 0);        // Single 0
+       TST(unsigned long long, ull, 0, "0xg", 0, "%llx", 1, 0);        // Single 0
+       TST(unsigned long long, ull, 0, "-a", 0, "%llx", 0, 0); // Nothing
+       TST(long long, ll, 0, "-a", 0, "%lld", 0, 0);   // Nothing
+       TST(long long, ll, 0, "-1aaatg", -1, "%lld", 2, 0);     // -1 (with traling junk)
+       TST(long long, ll, 0, "-+1aaatg", 0, "%lld", 0, 0);     // Nothing
+       TST(long long, ll, 0, "-  1", 0, "%lld", 0, 0); // Nothing
+       TST(long long, ll, 0, "01278  1", 0127, "%lld", 4, 0);  // 0127 with junk
+       
+       // Range edges
+       PRIMEBUF("0x%llx", ULLONG_MAX);
+       TST(unsigned long long, ull, 0, buf, ULLONG_MAX, "0x%llx", buf_len, 0);
+       PRIMEBUF("%llu", ULLONG_MAX);
+       TST(unsigned long long, ull, 0, buf, ULLONG_MAX, "%llu", buf_len, 0);
+       PRIMEBUF("-%llu", (long long)LONG_MAX);
+       TST(long, l, 0, buf, -LONG_MAX, "%ld", buf_len, 0);
+       
+       // Out of range
+       // - When the range limit is hit, valid characters should still be consumed (just not used)
+       TST(unsigned long long, ull, 0, "0x10000FFFF0000FFFF", ULLONG_MAX, "%llx", strlen(in), ERANGE);
+       TST(unsigned long, ul, 0, "0x10000FFFF0000FFFF", ULONG_MAX, "%lx", strlen(in), ERANGE);
+       TST(long, l, 0, "0x10000FFFF0000FFFF", LONG_MAX, "%ld", strlen(in), ERANGE);
+       TST(long, l, 0, "-0x10000FFFF0000FFFF", LONG_MIN, "%ld", strlen(in), ERANGE);
+       if( LONG_MIN < -LONG_MAX )
+       {
+               // Ensure that if -LONG_MIN is greater than LONG_MAX, that converting it leaves a range error
+               PRIMEBUF("%ld", LONG_MIN);
+               TST(long, l, 0, buf+1, LONG_MAX, "%ld", buf_len-1, ERANGE);
+       }
 }
index de7f725..db6684f 100644 (file)
@@ -27,7 +27,7 @@ unsigned long long strtoull(const char *str, char **end, int base)
        
        // Handle base detection for hex
        if( base == 0 || base == 16 ) {
-               if( *str == '0' && str[1] == 'x' ) {
+               if( *str == '0' && (str[1] == 'x' || str[1] == 'X') && isxdigit(str[2]) ) {
                        str += 2;
                        base = 16;
                }
@@ -60,6 +60,21 @@ unsigned long long strtoull(const char *str, char **end, int base)
                }
                if( next < 0 )
                        break;
+               
+               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;
+               }
                ret *= base;
                ret += next;
                str ++;
@@ -85,7 +100,6 @@ unsigned long strtoul(const char *ptr, char **end, int base)
 long long strtoll(const char *str, char **end, int base)
 {
         int    neg = 0;
-       unsigned long long      ret;
 
        if( !str ) {
                errno = EINVAL;
@@ -96,17 +110,36 @@ long long strtoll(const char *str, char **end, int base)
                str++;
        
        // Check for negative (or positive) sign
-       if(*str == '-' || *str == '+') {
+       if(*str == '-' || *str == '+')
+       {
+               //_SysDebug("strtoll - str[0:1] = '%.2s'", str);
+               if( !isdigit(str[1]) ) {
+                       // Non-digit, invalid string
+                       if(end) *end = (char*)str;
+                       return 0;
+               }
                neg = (*str == '-');
                str++;
        }
 
-       ret = strtoull(str, end, base); 
+       unsigned long long ret = strtoull(str, end, base);      
+       //_SysDebug("strtoll - neg=%i,ret=%llu", neg, ret);
 
-       if( neg )
+       if( neg ) {
+               if( -ret < LLONG_MIN ) {
+                       errno = ERANGE;
+                       return LLONG_MIN;
+               }
                return -ret;
+       }
        else
+       {
+               if( ret > LLONG_MAX ) {
+                       errno = ERANGE;
+                       return LLONG_MAX;
+               }
                return ret;
+       }
 }
 
 long strtol(const char *str, char **end, int base)

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