Skip to content

StrCmp

Description

Compare a Str against another string lexicographically.

Three call shapes via OVERLOAD + _Generic on other: StrCmp(s, other)other is Str * or Zstr. StrCmp(s, other, other_len)other is a fixed-length view (Zstr, size). The Str * overload is length-aware on both sides, so a string with an embedded NUL still compares correctly.

Parameters

Name Direction Description
s in Str to compare.
other in Other string (Str * / Zstr).
other_len in Length of other for the 3-arg fixed-length form.

Success

Returns 0 when equal, <0 when s < other, >0 when s > other. Neither string is modified.

Failure

Function cannot fail; the return value carries the order.

Usage example (Cross-references)

Usage examples (Cross-references)
    #define JR_STR_KV(si, k, str)                                                                                          \
        do {                                                                                                               \
            if (!StrCmp(&key, (k))) {                                                                                      \
                Str UNPL(my_str) = StrInit(&alloc);                                                                        \
                si               = JReadString((si), &UNPL(my_str));                                                       \
        });
    
        if (StrCmp(&obj.name, "test", 4) == 0 && obj.value == 42 && obj.flag == true) {
            WriteFmt(
                "[DEBUG] Whitespace variations test passed - name: {}, value: {}, flag: {}\n",
        }
    
        bool result = StrCmp(&output_clean, &expected_clean) == 0;
    
        if (!result) {
        }
    
        bool result = StrCmp(&output_clean, &expected_clean) == 0;
    
        if (!result) {
        }
    
        bool result = StrCmp(&output_clean, &expected_clean) == 0;
    
        if (!result) {
        }
    
        if (StrCmp(&data.name, "test", 4) != 0) {
            WriteFmt("[DEBUG] Name check failed: expected 'test', got '");
            for (size i = 0; i < StrLen(&data.name); i++) {
        }
    
        if (StrCmp(&data.user.profile.name, "Alice", 5) != 0) {
            WriteFmt("[DEBUG] Profile name check failed: expected 'Alice', got '");
            for (u64 i = 0; i < StrLen(&data.user.profile.name); i++) {
        }
    
        if (StrCmp(&data.status, "active", 6) != 0) {
            WriteFmt("[DEBUG] Status check failed: expected 'active', got '");
            for (size i = 0; i < StrLen(&data.status); i++) {
        });
    
        if (StrCmp(&data.company.departments.engineering.head, "John", 4) != 0) {
            WriteFmt("[DEBUG] Engineering head check failed: expected 'John', got '");
            for (size i = 0; i < StrLen(&data.company.departments.engineering.head); i++) {
        }
    
        if (StrCmp(&data.company.name, "TechCorp", 8) != 0) {
            WriteFmt("[DEBUG] Company name check failed: expected 'TechCorp', got '");
            for (size i = 0; i < StrLen(&data.company.name); i++) {
    
        // Debug message check
        if (StrCmp(&response.message, "Success", 7) != 0) {
            WriteFmt("[DEBUG] Message check failed: expected 'Success', got '");
            for (size i = 0; i < StrLen(&response.message); i++) {
            }
    
            if (StrCmp(&sym->analysis_name, "test_analysis", 13) != 0) {
                WriteFmt("[DEBUG] Analysis name check failed: expected 'test_analysis', got '");
                for (size i = 0; i < StrLen(&sym->analysis_name); i++) {
            }
    
            if (StrCmp(&sym->function_name, "main_func", 9) != 0) {
                WriteFmt(
                    "[DEBUG] Function name check failed: expected 'main_func', got string of length {}\n",
            }
    
            if (StrCmp(&sym->sha256, "abc123", 6) != 0) {
                WriteFmt("[DEBUG] SHA256 check failed: expected 'abc123', got '");
                for (size i = 0; i < StrLen(&sym->sha256); i++) {
            }
    
            if (StrCmp(&sym->function_mangled_name, "_Z4main", 7) != 0) {
                WriteFmt("[DEBUG] Mangled name check failed: expected '_Z4main', got '");
                for (size i = 0; i < StrLen(&sym->function_mangled_name); i++) {
        }
    
        if (StrCmp(&info.name, "test_func", 9) != 0) {
            WriteFmt("[DEBUG] Function name check failed: expected 'test_func', got '");
            for (size i = 0; i < StrLen(&info.name); i++) {
        }
    
        if (StrCmp(&info.name, "test_model", 10) != 0) {
            WriteFmt("[DEBUG] Model name check failed: expected 'test_model', got '");
            for (size i = 0; i < StrLen(&info.name); i++) {
        }
    
        if (StrCmp(&result.binary_name, "test_binary", 11) != 0) {
            WriteFmt("[DEBUG] Binary name check failed: expected 'test_binary', got '");
            for (size i = 0; i < StrLen(&result.binary_name); i++) {
        }
    
        if (StrCmp(&result.sha256, "abc123", 6) != 0) {
            WriteFmt("[DEBUG] SHA256 check failed: expected 'abc123', got '");
            for (size i = 0; i < StrLen(&result.sha256); i++) {
        }
    
        if (StrCmp(&result.model_name, "test_model", 10) != 0) {
            WriteFmt("[DEBUG] Model name check failed: expected 'test_model', got '");
            for (size i = 0; i < StrLen(&result.model_name); i++) {
        }
    
        if (StrCmp(&result.owned_by, "user1", 5) != 0) {
            WriteFmt("[DEBUG] Owned by check failed: expected 'user1', got '");
            for (size i = 0; i < StrLen(&result.owned_by); i++) {
        }
    
        if (StrCmp(&response.message, "Success", 7) != 0) {
            WriteFmt("[DEBUG] Message check failed: expected 'Success', got '");
            for (size i = 0; i < StrLen(&response.message); i++) {
            }
    
            if (StrCmp(&sym->analysis_name, "test_analysis", 13) != 0) {
                WriteFmt("[DEBUG] Analysis name check failed: expected 'test_analysis', got '");
                for (size i = 0; i < StrLen(&sym->analysis_name); i++) {
            }
    
            if (StrCmp(&sym->function_name, "main_func", 9) != 0) {
                WriteFmt(
                    "[DEBUG] Function name check failed: expected 'main_func', got string of length {}\n",
            }
    
            if (StrCmp(&sym->sha256, "abc123", 6) != 0) {
                WriteFmt("[DEBUG] SHA256 check failed: expected 'abc123', got '");
                for (size i = 0; i < StrLen(&sym->sha256); i++) {
            }
    
            if (StrCmp(&sym->function_mangled_name, "_Z4main", 7) != 0) {
                WriteFmt("[DEBUG] Mangled name check failed: expected '_Z4main', got '");
                for (size i = 0; i < StrLen(&sym->function_mangled_name); i++) {
        }
    
        if (StrCmp(&response.message, "Success", 7) != 0) {
            WriteFmt("[DEBUG] Message check failed: expected 'Success', got '");
            for (u64 i = 0; i < StrLen(&response.message); i++) {
            }
    
            if (StrCmp(&sym->analysis_name, "test_analysis", 13) != 0) {
                WriteFmt("[DEBUG] Analysis name check failed: expected 'test_analysis', got '");
                for (size i = 0; i < StrLen(&sym->analysis_name); i++) {
            }
    
            if (StrCmp(&sym->function_name, "main_func", 9) != 0) {
                WriteFmt("[DEBUG] Function name check failed: expected 'main_func', got '");
                for (size i = 0; i < StrLen(&sym->function_name); i++) {
            }
    
            if (StrCmp(&sym->sha256, "abc123", 6) != 0) {
                WriteFmt("[DEBUG] SHA256 check failed: expected 'abc123', got '");
                for (size i = 0; i < StrLen(&sym->sha256); i++) {
            }
    
            if (StrCmp(&sym->function_mangled_name, "_Z4main", 7) != 0) {
                WriteFmt("[DEBUG] Mangled name check failed: expected '_Z4main', got '");
                for (size i = 0; i < StrLen(&sym->function_mangled_name); i++) {
        });
    
        if (StrCmp(&name, "Alice", 5) != 0) {
            WriteFmt("[DEBUG] Name check failed: expected 'Alice', got '");
            for (size i = 0; i < StrLen(&name); i++) {
        }
    
        if (StrCmp(&city, "New York", 8) != 0) {
            WriteFmt("[DEBUG] City check failed: expected 'New York', got '");
            for (size i = 0; i < StrLen(&city); i++) {
        }
    
        if (StrCmp(&person.name, "Bob", 3) != 0) {
            WriteFmt("[DEBUG] Person name check failed: expected 'Bob', got '");
            for (size i = 0; i < StrLen(&person.name); i++) {
        }
    
        if (StrCmp(&config.log_level, "INFO", 4) != 0) {
            WriteFmt("[DEBUG] Log level check failed: expected 'INFO', got '");
            for (size i = 0; i < StrLen(&config.log_level); i++) {
            Str *lang3 = &VecAt(&languages, 2);
    
            if (StrCmp(lang1, "C", 1) != 0) {
                WriteFmt("[DEBUG] Language 1 check failed: expected 'C', got '");
                for (size i = 0; i < StrLen(lang1); i++) {
            }
    
            if (StrCmp(lang2, "Python", 6) != 0) {
                WriteFmt("[DEBUG] Language 2 check failed: expected 'Python', got '");
                for (size i = 0; i < StrLen(lang2); i++) {
            }
    
            if (StrCmp(lang3, "Rust", 4) != 0) {
                WriteFmt("[DEBUG] Language 3 check failed: expected 'Rust', got '");
                for (size i = 0; i < StrLen(lang3); i++) {
        });
    
        if (StrCmp(&data.user.name, "Charlie", 7) != 0) {
            WriteFmt("[DEBUG] User name check failed: expected 'Charlie', got '");
            for (size i = 0; i < StrLen(&data.user.name); i++) {
        }
    
        if (StrCmp(&data.user.email, "charlie@example.com", 19) != 0) {
            WriteFmt("[DEBUG] User email check failed: expected 'charlie@example.com', got '");
            for (size i = 0; i < StrLen(&data.user.email); i++) {
        }
    
        if (StrCmp(&product.name, "Laptop", 6) != 0) {
            WriteFmt("[DEBUG] Product name check failed: expected 'Laptop', got '");
            for (size i = 0; i < StrLen(&product.name); i++) {
            Str *tag3 = &VecAt(&product.tags, 2);
    
            if (StrCmp(tag1, "electronics", 11) != 0) {
                WriteFmt("[DEBUG] Tag 1 check failed: expected 'electronics', got '");
                for (size i = 0; i < StrLen(tag1); i++) {
            }
    
            if (StrCmp(tag2, "computers", 9) != 0) {
                WriteFmt("[DEBUG] Tag 2 check failed: expected 'computers', got '");
                for (size i = 0; i < StrLen(tag2); i++) {
            }
    
            if (StrCmp(tag3, "portable", 8) != 0) {
                WriteFmt("[DEBUG] Tag 3 check failed: expected 'portable', got '");
                for (size i = 0; i < StrLen(tag3); i++) {
    // Helper function to compare persons
    bool compare_persons(const TestPerson *a, const TestPerson *b) {
        return a->id == b->id && StrCmp((Str *)&a->name, (Str *)&b->name) == 0 && a->age == b->age &&
               a->is_active == b->is_active && a->salary == b->salary;
    }
    bool compare_configs(const TestConfig *a, const TestConfig *b) {
        if (a->debug_mode != b->debug_mode || a->timeout != b->timeout ||
            StrCmp((Str *)&a->log_level, (Str *)&b->log_level) != 0 ||
            VecLen(&a->features) != VecLen(&b->features)) {
            return false;
    
        for (size i = 0; i < VecLen(&a->features); i++) {
            if (StrCmp((Str *)&VecAt(&a->features, i), (Str *)&VecAt(&b->features, i)) != 0) {
                return false;
            }
        // Compare values
        if (original.count == parsed.count && original.temperature == parsed.temperature &&
            original.enabled == parsed.enabled && StrCmp(&original.message, &parsed.message) == 0) {
            WriteFmtLn("[DEBUG] Simple round-trip test passed");
        } else {
    
        // Compare values
        if (StrLen(&parsed.empty) == StrLen(&original.empty) && StrCmp(&original.simple, &parsed.simple) == 0 &&
            StrCmp(&original.with_spaces, &parsed.with_spaces) == 0 &&
            StrCmp(&original.with_special, &parsed.with_special) == 0) {
        // Compare values
        if (StrLen(&parsed.empty) == StrLen(&original.empty) && StrCmp(&original.simple, &parsed.simple) == 0 &&
            StrCmp(&original.with_spaces, &parsed.with_spaces) == 0 &&
            StrCmp(&original.with_special, &parsed.with_special) == 0) {
            WriteFmtLn("[DEBUG] String round-trip test passed");
        if (StrLen(&parsed.empty) == StrLen(&original.empty) && StrCmp(&original.simple, &parsed.simple) == 0 &&
            StrCmp(&original.with_spaces, &parsed.with_spaces) == 0 &&
            StrCmp(&original.with_special, &parsed.with_special) == 0) {
            WriteFmtLn("[DEBUG] String round-trip test passed");
        } else {
                if (StrLen(VecPtrAt(&original_strings, i)) != StrLen(VecPtrAt(&parsed_strings, i)) ||
                    (StrLen(VecPtrAt(&original_strings, i)) &&
                     StrCmp(VecPtrAt(&original_strings, i), VecPtrAt(&parsed_strings, i)) != 0)) {
                    strings_match = false;
                    break;
        result = result && (MapPairCount(&cfg) == 3);
        result = result && KvConfigContains(&cfg, "host");
        result = result && host && StrCmp(host, "localhost") == 0;
        result = result && KvConfigGetI64(&cfg, "port", &port) && (port == 8080);
        result = result && KvConfigGetBool(&cfg, "debug", &debug) && debug;
        result = result && (StrIterIndex(&si) == StrIterLength(&si));
        result = result && (MapPairCount(&cfg) == 4);
        result = result && path && StrCmp(path, "/srv/my app") == 0;
        result = result && user && StrCmp(user, "root") == 0;
        result = result && greet && StrCmp(greet, "hello world") == 0;
        result = result && (MapPairCount(&cfg) == 4);
        result = result && path && StrCmp(path, "/srv/my app") == 0;
        result = result && user && StrCmp(user, "root") == 0;
        result = result && greet && StrCmp(greet, "hello world") == 0;
        result = result && empty && (StrLen(empty) == 0);
        result = result && path && StrCmp(path, "/srv/my app") == 0;
        result = result && user && StrCmp(user, "root") == 0;
        result = result && greet && StrCmp(greet, "hello world") == 0;
        result = result && empty && (StrLen(empty) == 0);
        result = result && (StrLen(&host_copy) > 0);
        result = result && (StrBegin(&host_copy) != StrBegin(stored_host));
        result = result && (StrCmp(&host_copy, "localhost") == 0);
        result = result && (StrCmp(stored_host, "localhost") == 0);
        result = result && (StrBegin(&host_copy) != StrBegin(stored_host));
        result = result && (StrCmp(&host_copy, "localhost") == 0);
        result = result && (StrCmp(stored_host, "localhost") == 0);
    
        StrBegin(&host_copy)[0] = 'L';
        StrBegin(&host_copy)[0] = 'L';
    
        result = result && (StrCmp(&host_copy, "Localhost") == 0);
        result = result && (StrCmp(stored_host, "localhost") == 0);
    
        result = result && (StrCmp(&host_copy, "Localhost") == 0);
        result = result && (StrCmp(stored_host, "localhost") == 0);
    
        StrDeinit(&host_copy);
    // Test string comparison functions
    bool test_str_cmp(void) {
        WriteFmt("Testing StrCmp variants\n");
        DefaultAllocator alloc = DefaultAllocatorInit();
    
        // Test StrCmp with equal strings
        int  cmp1   = StrCmp(&s1, &s2);
        bool result = (cmp1 == 0);
    
        // Test StrCmp with different strings (H < W in ASCII)
        int cmp2 = StrCmp(&s1, &s3);
        result   = result && (cmp2 < 0);
    
        // Test StrCmp with string prefix - ZstrCompare compares the entire strings
        int cmp3 = StrCmp(&s1, &s4);
        result   = result && (cmp3 < 0); // "Hello" comes before "Hello World" lexicographically
    
        // Test StrCmp (Cstr key, key_len)
        int cmp4 = StrCmp(&s1, "Hello", 5);
        result   = result && (cmp4 == 0);
        result   = result && (cmp4 == 0);
    
        int cmp5 = StrCmp(&s1, "World", 5);
        result   = result && (cmp5 != 0);
    
        Str expected = StrInitFromZstr("Hello", &alloc);
        success      = success && (StrCmp(&s, &expected) == 0);
        StrDeinit(&expected);
        StrClear(&s);
    
        expected = StrInitFromZstr("Hello, World!", &alloc);
        success  = success && (StrCmp(&s, &expected) == 0);
        StrDeinit(&expected);
    
        Str expected = StrInitFromZstr("Alice", &alloc);
        success      = success && (StrCmp(&name, &expected) == 0);
        StrDeinit(&expected);
        StrClear(&name);
    
        expected = StrInitFromZstr("Bob", &alloc);
        success  = success && (StrCmp(&name, &expected) == 0);
        StrDeinit(&expected);
        StrReadFmt(z, "{c}", str_val);
        Str  expected = StrInitFromZstr("Hello", &alloc);
        bool str_pass = (StrCmp(&str_val, &expected) == 0);
        WriteFmt("str_val test: comparing with 'Hello', pass = {}\n", str_pass ? "true" : "false");
        success = success && str_pass;
        StrReadFmt(z, "{cs}", str_val);
        expected             = StrInitFromZstr("World", &alloc);
        bool quoted_str_pass = (StrCmp(&str_val, &expected) == 0);
        WriteFmt("quoted str_val test: comparing with 'World', pass = {}\n", quoted_str_pass ? "true" : "false");
        success = success && quoted_str_pass;
            // Should read "hello" (stops at first space)
            Str  expected   = StrInitFromZstr("hello world", &alloc);
            bool test1_pass = (StrCmp(&result, &expected) == 0);
            WriteFmt("Expected: 'hello', Pass: {}\n\n", test1_pass ? "true" : "false");
            success = success && test1_pass;
            // Should read "hello" (stops at first space)
            Str  expected   = StrInitFromZstr("hello", &alloc);
            bool test1_pass = (StrCmp(&result, &expected) == 0);
            WriteFmt("Expected: 'hello', Pass: {}\n\n", test1_pass ? "true" : "false");
            success = success && test1_pass;
            // Should read "HELLO" (stops at first space)
            Str  expected   = StrInitFromZstr("HELLO WORLD", &alloc);
            bool test2_pass = (StrCmp(&result, &expected) == 0);
            WriteFmt("Expected: 'HELLO', Pass: {}\n\n", test2_pass ? "true" : "false");
            success = success && test2_pass;
            WriteFmt("Input: '{}', Output: '{} {}'", in, result1, result2);
    
            bool test2_pass  = (StrCmp(&result1, "HELLO") == 0);
            test2_pass      &= (StrCmp(&result2, "WORLD") == 0);
            WriteFmt("Expected: 'HELLO WORLD', Pass: {}\n\n", test2_pass ? "true" : "false");
    
            bool test2_pass  = (StrCmp(&result1, "HELLO") == 0);
            test2_pass      &= (StrCmp(&result2, "WORLD") == 0);
            WriteFmt("Expected: 'HELLO WORLD', Pass: {}\n\n", test2_pass ? "true" : "false");
            success = success && test2_pass;
            WriteFmt("Input: '{}', Output: '{}{}'", in, result1, result2);
    
            bool test2_pass  = (StrCmp(&result1, "HELLO") == 0);
            test2_pass      &= (StrCmp(&result2, " WORLD MIGHTY MISRA") == 0); // notice the extra space
            WriteFmt("Expected: 'HELLO WORLD MIGHTY MISRA', Pass: {}\n\n", test2_pass ? "true" : "false");
    
            bool test2_pass  = (StrCmp(&result1, "HELLO") == 0);
            test2_pass      &= (StrCmp(&result2, " WORLD MIGHTY MISRA") == 0); // notice the extra space
            WriteFmt("Expected: 'HELLO WORLD MIGHTY MISRA', Pass: {}\n\n", test2_pass ? "true" : "false");
            success = success && test2_pass;
            // Should read "mixed case" (converts the entire quoted string)
            Str  expected   = StrInitFromZstr("mixed case", &alloc);
            bool test3_pass = (StrCmp(&result, &expected) == 0);
            WriteFmt("Expected: 'mixed case', Pass: {}\n\n", test3_pass ? "true" : "false");
            success = success && test3_pass;
            // Should read "ABC123XYZ" (only letters are converted, numbers unchanged)
            Str  expected   = StrInitFromZstr("ABC123XYZ", &alloc);
            bool test4_pass = (StrCmp(&result, &expected) == 0);
            WriteFmt("Expected: 'ABC123XYZ', Pass: {}\n\n", test4_pass ? "true" : "false");
            success = success && test4_pass;
            // Should read "Hello" (stops at first space, no case conversion)
            Str  expected   = StrInitFromZstr("Hello World", &alloc);
            bool test5_pass = (StrCmp(&result, &expected) == 0);
            WriteFmt("Expected: 'Hello World', Pass: {}\n\n", test5_pass ? "true" : "false");
            success = success && test5_pass;
        str_obj = BitVecToStr(&bv);
        result  = result && (StrLen(&str_obj) == 1);
        result  = result && (StrCmp(&str_obj, "1", 1) == 0);
        StrDeinit(&str_obj);
    #define JR_STR_KV(si, k, str)                                                                                          \
        do {                                                                                                               \
            if (!StrCmp(&key, (k))) {                                                                                      \
                Str UNPL(my_str) = StrInit();                                                                              \
                si               = JReadString((si), &UNPL(my_str));                                                       \
    #define JR_INT_KV(si, k, i)                                                                                            \
        do {                                                                                                               \
            if (!StrCmp(&key, (k))) {                                                                                      \
                i64 UNPL(my_int) = 0;                                                                                      \
                si               = JReadInteger((si), &UNPL(my_int));                                                      \
    #define JR_FLT_KV(si, k, f)                                                                                            \
        do {                                                                                                               \
            if (!StrCmp(&key, (k))) {                                                                                      \
                f64 UNPL(my_flt) = 0;                                                                                      \
                si               = JReadFloat((si), &UNPL(my_flt));                                                        \
    #define JR_BOOL_KV(si, k, b)                                                                                           \
        do {                                                                                                               \
            if (!StrCmp(&key, (k))) {                                                                                      \
                bool UNPL(my_b) = 0;                                                                                       \
                si              = JReadBool((si), &UNPL(my_b));                                                            \
    #define JR_OBJ_KV(si, k, reader)                                                                                       \
        do {                                                                                                               \
            if (!StrCmp(&key, (k))) {                                                                                      \
                JR_OBJ(si, reader);                                                                                        \
            }                                                                                                              \
    #define JR_ARR_KV(si, k, reader)                                                                                       \
        do {                                                                                                               \
            if (!StrCmp(&key, (k))) {                                                                                      \
                JR_ARR(si, reader);                                                                                        \
            }                                                                                                              \
Last updated on