Skip to content

StrToU64

Description

Convert string to unsigned 64-bit integer

Parameters

Name Direction Description
str in String to convert
value out Where to store the result
config in Parse configuration (NULL for auto-detect base)

Success

Returns true and writes the parsed value into *value

Failure

Returns false when the input cannot be parsed; *value is left unchanged

Usage example (Cross-references)

Usage examples (Cross-references)
        // rejected a bare prefix with no digits.
        u64 val;
        if (!StrToU64(&temp, &val, NULL)) {
            LOG_ERROR("Failed to parse u8");
            StrDeinit(&temp);
        }
    
    _MAKE_READ_TXT_INT(u16, u16, u64, StrToU64, _U_BOUND(u16, UINT16_MAX))
    _MAKE_READ_TXT_INT(u32, u32, u64, StrToU64, _U_BOUND(u32, UINT32_MAX))
    _MAKE_READ_TXT_INT(u64, u64, u64, StrToU64, /* val is already u64; no bound */)
    
    _MAKE_READ_TXT_INT(u16, u16, u64, StrToU64, _U_BOUND(u16, UINT16_MAX))
    _MAKE_READ_TXT_INT(u32, u32, u64, StrToU64, _U_BOUND(u32, UINT32_MAX))
    _MAKE_READ_TXT_INT(u64, u64, u64, StrToU64, /* val is already u64; no bound */)
    _MAKE_READ_TXT_INT(i8, i8, i64, StrToI64, _I_BOUND(i8, INT8_MIN, INT8_MAX))
            u64            value;
            StrParseConfig config = {.base = 16};
            if (!StrToU64(&hex_str, &value, &config)) {
                LOG_ERROR("Failed to parse hex value");
                StrDeinit(&hex_str);
            u64            value;
            StrParseConfig config = {.base = 8};
            if (!StrToU64(&oct_str, &value, &config)) {
                LOG_ERROR("Failed to parse octal value");
                StrDeinit(&oct_str);
    }
    
    bool StrToU64(const Str *str, u64 *value, const StrParseConfig *config) {
        ValidateStr(str);
    
        u64 unsigned_value;
        if (!StrToU64(&temp_str, &unsigned_value, config)) {
            return false;
        }
    // Test StrToU64 function
    bool test_str_to_u64(void) {
        WriteFmt("Testing StrToU64\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str  s       = StrInitFromZstr("12345", &alloc);
        u64  value   = 0;
        bool success = StrToU64(&s, &value, NULL);
        bool result  = (success && value == 12345);
        s                     = StrInitFromZstr("ABCD", &alloc); // No 0x prefix when base is explicitly 16
        StrParseConfig config = {.base = 16};
        success               = StrToU64(&s, &value, &config);
        result                = result && (success && value == 0xABCD);
        StrDeinit(&s);
        s       = StrInitFromZstr("0xABCD", &alloc);
        success = StrToU64(&s, &value, NULL);
        result  = result && (success && value == 0xABCD);
        StrDeinit(&s);
        s       = StrInitFromZstr("0b101010", &alloc);
        success = StrToU64(&s, &value, NULL);
        result  = result && (success && value == 42);
        StrDeinit(&s);
        s       = StrInitFromZstr("0o52", &alloc);
        success = StrToU64(&s, &value, NULL);
        result  = result && (success && value == 42);
        StrDeinit(&s);
        s       = StrInitFromZstr("0", &alloc);
        success = StrToU64(&s, &value, NULL);
        result  = result && (success && value == 0);
        StrDeinit(&s);
        s       = StrInitFromZstr("not a number", &alloc);
        success = StrToU64(&s, &value, NULL);
        result  = result && (!success);
        StrDeinit(&s);
        s       = StrInitFromZstr("-123", &alloc);
        success = StrToU64(&s, &value, NULL);
        result  = result && (!success);
            StrFromU64(&s, u64_values[i], &config);
            u64  recovered_u64 = 0;
            bool success       = StrToU64(&s, &recovered_u64, NULL);
            result             = result && success && (recovered_u64 == u64_values[i]);
            StrFromU64(&s, u64_values[i], &config);
            recovered_u64 = 0;
            success       = StrToU64(&s, &recovered_u64, NULL); // Can now use explicit base 16 with 0x prefix
            result        = result && success && (recovered_u64 == u64_values[i]);
            // For bases with prefixes, use auto-detect. For others, use explicit base parsing
            StrParseConfig parse_config = {.base = base};
            bool           success      = StrToU64(&s, &recovered, config.use_prefix ? NULL : &parse_config);
            result                      = result && success && (recovered == base);
        StrFromU64(&s, UINT64_MAX, &config);
        u64  recovered_max = 0;
        bool success       = StrToU64(&s, &recovered_max, NULL);
        result             = result && success && (recovered_max == UINT64_MAX);
            u64            value    = 0;
            StrParseConfig config   = {.base = prefix_tests[i].base};
            bool           success  = StrToU64(&test_str, &value, prefix_tests[i].base == 0 ? NULL : &config);
            result                  = result && success && (value == prefix_tests[i].expected);
            StrDeinit(&test_str);
            u64            recovered = 0;
            StrParseConfig pconfig   = {.base = base};
            bool           success   = StrToU64(&s, &recovered, &pconfig); // Can now use explicit base with prefixes
            result                   = result && success && (recovered == large_value);
            u64            recovered    = 0;
            StrParseConfig parse_config = {.base = base};
            bool           success      = StrToU64(&s, &recovered, &parse_config);
    
            result = result && success && (recovered == test_value);
            u64            recovered    = 0;
            StrParseConfig parse_config = {.base = base};
            bool           success      = StrToU64(&s, &recovered, &parse_config);
    
            result = result && success && (recovered == test_value);
                u64            recovered    = 0;
                StrParseConfig parse_config = {.base = base};
                bool           success      = StrToU64(&s, &recovered, &parse_config);
    
                result = result && success && (recovered == test_values[i]);
    
            u64  recovered = 0;
            bool success   = StrToU64(&s, &recovered, NULL);
            result         = result && success && (recovered == test_value);
        Str  long_str   = StrInitFromZstr(long_number, &alloc);
        u64  long_value = 0;
        bool success    = StrToU64(&long_str, &long_value, NULL);
        // This might overflow, but should handle gracefully
        result = result && (success || !success); // Either succeeds or fails gracefully
    // (5 < 99). Real code rejects base 99; the mutant parses "5" as 5.
    static bool test_str_to_u64_invalid_base_rejected(void) {
        WriteFmt("Testing StrToU64 rejects an out-of-range base\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        u64            value  = 0;
        StrParseConfig config = {.base = 99};
        bool           ok     = StrToU64(&s, &value, &config);
        bool           result = (!ok);
    // parse of "ff" must succeed with value 255.
    static bool test_str_to_u64_valid_base_sixteen_accepted(void) {
        WriteFmt("Testing StrToU64 accepts explicit base 16\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        u64            value  = 0;
        StrParseConfig config = {.base = 16};
        bool           ok     = StrToU64(&s, &value, &config);
        bool           result = (ok && value == 255);
    // digit and is rejected. Real code parses 42.
    static bool test_str_to_u64_leading_space_skipped(void) {
        WriteFmt("Testing StrToU64 skips leading whitespace\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str  s      = StrInitFromZstr(" 42", &alloc);
        u64  value  = 0;
        bool ok     = StrToU64(&s, &value, NULL);
        bool result = (ok && value == 42);
    // falls into the empty-string path. Real code parses 42.
    static bool test_str_to_u64_leading_space_advance(void) {
        WriteFmt("Testing StrToU64 advances over leading whitespace\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str  s      = StrInitFromZstr(" 42", &alloc);
        u64  value  = 0;
        bool ok     = StrToU64(&s, &value, NULL);
        bool result = (ok && value == 42);
    // decimal 5. Mutant skips "0","5", finds no digits, returns false.
    static bool test_str_to_u64_zero_five_is_decimal(void) {
        WriteFmt("Testing StrToU64 parses 05 as decimal\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str  s      = StrInitFromZstr("05", &alloc);
        u64  value  = 0;
        bool ok     = StrToU64(&s, &value, NULL);
        bool result = (ok && value == 5);
    // digits and must be rejected. Mutant returns true with value 0.
    static bool test_str_to_u64_prefix_only_rejected(void) {
        WriteFmt("Testing StrToU64 rejects bare 0x prefix\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str  s      = StrInitFromZstr("0x", &alloc);
        u64  value  = 0;
        bool ok     = StrToU64(&s, &value, NULL);
        bool result = (!ok);
    // slip through. UINT64_MAX+1 == "18446744073709551616" must be rejected.
    static bool test_str_to_u64_overflow_rejected(void) {
        WriteFmt("Testing StrToU64 rejects u64 overflow\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str  s      = StrInitFromZstr("18446744073709551616", &alloc);
        u64  value  = 0;
        bool ok     = StrToU64(&s, &value, NULL);
        bool result = (!ok);
    // must parse to 42. Mutant returns false.
    static bool test_str_to_u64_have_digits_set(void) {
        WriteFmt("Testing StrToU64 marks digits consumed\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        Str  s      = StrInitFromZstr("42", &alloc);
        u64  value  = 0;
        bool ok     = StrToU64(&s, &value, NULL);
        bool result = (ok && value == 42);
    // wrongly reports extra characters for "42 ". Real code parses 42.
    static bool test_str_to_u64_trailing_space_skipped_strict(void) {
        WriteFmt("Testing StrToU64 skips trailing whitespace in strict mode\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        u64            value  = 0;
        StrParseConfig config = {.strict = true};
        bool           ok     = StrToU64(&s, &value, &config);
        bool           result = (ok && value == 42);
    // strict mode wrongly rejects "42  ". Real code parses 42.
    static bool test_str_to_u64_trailing_space_advance_strict(void) {
        WriteFmt("Testing StrToU64 advances over trailing whitespace in strict mode\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        u64            value  = 0;
        StrParseConfig config = {.strict = true};
        bool           ok     = StrToU64(&s, &value, &config);
        bool           result = (ok && value == 42);
    // trailing junk. "42x" in strict mode must be rejected. Mutant returns true.
    static bool test_str_to_u64_strict_rejects_trailing_junk(void) {
        WriteFmt("Testing StrToU64 strict mode rejects trailing junk\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        u64            value  = 0;
        StrParseConfig config = {.strict = true};
        bool           ok     = StrToU64(&s, &value, &config);
        bool           result = (!ok);
    // including a clean "42". Real code parses 42.
    static bool test_str_to_u64_strict_accepts_clean(void) {
        WriteFmt("Testing StrToU64 strict mode accepts a clean number\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
        u64            value  = 0;
        StrParseConfig config = {.strict = true};
        bool           ok     = StrToU64(&s, &value, &config);
        bool           result = (ok && value == 42);
    // mutant the barrier is gone and the corruption is not caught at entry.
    static bool test_str_to_u64_validate_barrier(void) {
        WriteFmt("Testing StrToU64 validate barrier aborts on corrupt Str\n");
    
        Str            bad    = {0}; // zeroed magic: ValidateStr must LOG_FATAL
        u64            value  = 0;
        StrParseConfig config = {.base = 10};
        StrToU64(&bad, &value, &config);
    
        return false; // unreachable on real code (aborts above)
        StrParseConfig   cfg   = {.base = 16};
        u64              out   = 0;
        bool             ok    = StrToU64(&s, &out, &cfg);
        bool             pass  = ok && out == 31;
        StrDeinit(&s);
        StrParseConfig   cfg   = {.base = 16};
        u64              out   = 0;
        bool             ok    = StrToU64(&s, &out, &cfg);
        bool             pass  = ok && out == 5;
        StrDeinit(&s);
        StrParseConfig   cfg   = {.base = 16};
        u64              out   = 0;
        bool             ok    = StrToU64(&s, &out, &cfg);
        bool             pass  = ok && out == 5;
        StrDeinit(&s);
        StrParseConfig   cfg   = {.base = 2};
        u64              out   = 0;
        bool             ok    = StrToU64(&s, &out, &cfg);
        bool             pass  = ok && out == 1;
        StrDeinit(&s);
        StrParseConfig   cfg   = {.base = 2};
        u64              out   = 0;
        bool             ok    = StrToU64(&s, &out, &cfg);
        bool             pass  = ok && out == 1;
        StrDeinit(&s);
        StrParseConfig   cfg   = {.base = 8};
        u64              out   = 0;
        bool             ok    = StrToU64(&s, &out, &cfg);
        bool             pass  = ok && out == 7;
        StrDeinit(&s);
        StrParseConfig   cfg   = {.base = 8};
        u64              out   = 0;
        bool             ok    = StrToU64(&s, &out, &cfg);
        bool             pass  = ok && out == 7;
        StrDeinit(&s);
        StrParseConfig   cfg   = {.base = 8};
        u64              out   = 0;
        bool             ok    = StrToU64(&s, &out, &cfg);
        bool             pass  = ok && out == 7;
        StrDeinit(&s);
        StrParseConfig   cfg   = {.base = 16};
        u64              out   = 0;
        bool             ok    = StrToU64(&s, &out, &cfg);
        bool             pass  = ok && out == 31;
        StrDeinit(&s);
        StrParseConfig   cfg   = {.base = 16};
        u64              out   = 0;
        bool             ok    = StrToU64(&s, &out, &cfg);
        bool             pass  = ok && out == 31;
        StrDeinit(&s);
        StrParseConfig   cfg   = {.base = 16};
        u64              out   = 0;
        bool             ok    = StrToU64(&s, &out, &cfg);
        bool             pass  = ok && out == 31;
        StrDeinit(&s);
        u64            value   = 999; // sentinel
        StrParseConfig config  = {.base = 2};
        bool           success = StrToU64(&s, &value, &config);
        // Real code: '2' is invalid in base 2 -> parse fails. The mutant accepts
        // digit value 2 and returns success.
    // and fails.
    static bool test_to_u64_base2_uppercase_prefix_skipped(void) {
        WriteFmt("Testing StrToU64 skips 0B prefix on explicit base 2 (533:51)\n");
    
        DefaultAllocator alloc  = DefaultAllocatorInit();
        StrParseConfig   config = {.base = 2};
        u64              value  = 0;
        bool             ok     = StrToU64(&s, &value, &config);
    
        // Real: uppercase prefix skipped, "101" base 2 == 5.
            test_str_conversion_invalid_input_failures,
            test_validate_str_guard,           // StrToF64 (Str.Mutants1)
            test_str_to_u64_validate_barrier,  // StrToU64 (Str.Mutants2)
            test_from_f64_null_aborts,         // StrFromF64 (Str.Mutants3)
            test_from_f64_null_aborts_high_precision,
Last updated on