From 8f25c1c3f3a20b0b6dd7a617acd54b40c5649708 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Mon, 28 Sep 2026 09:21:04 +0000 Subject: [PATCH] feat(libxce-alloc-preason-equality): NULL-safe str_eq helper in libxce utils (task 1.1) Co-Authored-By: Claude Opus 5.5 (1M context) --- .../libxce-alloc-preason-equality/tasks.md | 7 +++- src/include/utils.h | 5 +++ src/lib/testsuite/test_utils.c | 40 +++++++++++++++++++ src/lib/utils.c | 6 +++ 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/.kiro/specs/libxce-alloc-preason-equality/tasks.md b/.kiro/specs/libxce-alloc-preason-equality/tasks.md index d8f48b56..99fda432 100644 --- a/.kiro/specs/libxce-alloc-preason-equality/tasks.md +++ b/.kiro/specs/libxce-alloc-preason-equality/tasks.md @@ -2,8 +2,8 @@ Test-first throughout (red → green → refactor). Every sub-task leaves the build and all test suites green: consumers move to the public API before the private comparators are deleted, test fixtures are brought in line before `from` starts being compared, and each change updates the tests it affects. -- [ ] 1. Foundation: shared NULL-safe string equality -- [ ] 1.1 Provide a NULL-safe string-equality helper in libxce +- [x] 1. Foundation: shared NULL-safe string equality +- [x] 1.1 Provide a NULL-safe string-equality helper in libxce - Write the tests first: two NULL strings are equal; one NULL and one non-NULL are not equal in either order; equal strings are equal; different strings are not. - Add the helper beside the existing floating-point comparison helper in the shared utilities, as described in design.md (Decision 5). - Done when the libxce utility tests include these cases and the libxce suite passes under ASAN. @@ -84,3 +84,6 @@ Test-first throughout (red → green → refactor). Every sub-task leaves the bu - Done when the test passes and the live replay reports zero mismatches; then mark bug-1881 fixed through the buglog skill with the fix commit. - _Requirements: 4.3, 5.4_ - _Depends: spec scheduler-sim-discrete-clock_ + +## Implementation Notes +- `xce-build.sh` resolves a relative build dir under `src/`: `bash src/tools/xce-build.sh lib asan xce.build.alloceq` builds into `src/xce.build.alloceq`, so iterate with `meson test -C src/xce.build.alloceq`. diff --git a/src/include/utils.h b/src/include/utils.h index f3723a32..96cde1b4 100644 --- a/src/include/utils.h +++ b/src/include/utils.h @@ -57,6 +57,11 @@ #define float_equal(a, b) (fabs((a) - (b)) < float_near_zero) +/* NULL-safe string equality: non-zero iff a and b are both NULL, or both + * non-NULL and strcmp(a, b) == 0; zero otherwise (exactly one NULL, or + * different contents). Reads only; never modifies either argument. */ +extern int str_eq(const char* a, const char* b); + #define STR(x) _STR(x) #define _STR(x) #x diff --git a/src/lib/testsuite/test_utils.c b/src/lib/testsuite/test_utils.c index e77a11b2..611d1e42 100644 --- a/src/lib/testsuite/test_utils.c +++ b/src/lib/testsuite/test_utils.c @@ -426,6 +426,42 @@ test_xce_format_units_value_both_mode_renders_raw_and_human(void) TEST_ASSERT_EQUAL_STRING(expected, buf); } +/* str_eq: NULL-safe string equality (libxce-alloc-preason-equality + * task 1.1, Requirements 1.5 / 2.3, design Decision 5). */ +static void test_str_eq_both_null_are_equal(void) +{ + TEST_ASSERT_TRUE(str_eq(NULL, NULL)); +} + +static void test_str_eq_one_null_is_not_equal_either_order(void) +{ + TEST_ASSERT_FALSE(str_eq(NULL, "cpu")); + TEST_ASSERT_FALSE(str_eq("cpu", NULL)); + /* An empty string is still non-NULL: it must not equal NULL. */ + TEST_ASSERT_FALSE(str_eq(NULL, "")); + TEST_ASSERT_FALSE(str_eq("", NULL)); +} + +static void test_str_eq_equal_strings_are_equal(void) +{ + /* Distinct buffers with the same content, so the result cannot come + * from pointer identity. */ + char a[] = "node01"; + char b[] = "node01"; + + TEST_ASSERT_TRUE(str_eq(a, b)); + TEST_ASSERT_TRUE(str_eq(a, a)); + TEST_ASSERT_TRUE(str_eq("", "")); +} + +static void test_str_eq_different_strings_are_not_equal(void) +{ + TEST_ASSERT_FALSE(str_eq("node01", "node02")); + TEST_ASSERT_FALSE(str_eq("node0", "node01")); + TEST_ASSERT_FALSE(str_eq("node01", "node0")); + TEST_ASSERT_FALSE(str_eq("", "x")); +} + void test_utils(void) { printf("\n>> utils test\n\n"); @@ -455,4 +491,8 @@ void test_utils(void) RUN_TEST(test_xce_format_units_value_raw_mode_renders_bare_value); RUN_TEST(test_xce_format_units_value_human_mode_renders_scaled_string_alone); RUN_TEST(test_xce_format_units_value_both_mode_renders_raw_and_human); + RUN_TEST(test_str_eq_both_null_are_equal); + RUN_TEST(test_str_eq_one_null_is_not_equal_either_order); + RUN_TEST(test_str_eq_equal_strings_are_equal); + RUN_TEST(test_str_eq_different_strings_are_not_equal); } diff --git a/src/lib/utils.c b/src/lib/utils.c index 61e2abf8..85fd6e28 100644 --- a/src/lib/utils.c +++ b/src/lib/utils.c @@ -146,6 +146,12 @@ void free_argv(char** argv, int argc) { FREE_ARGV(argv, argc); } int get_myname(char* name, size_t size) { return gethostname(name, size); } +int str_eq(const char* a, const char* b) +{ + if (a == NULL || b == NULL) return a == b; + return strcmp(a, b) == 0; +} + char* frame_symbol(int k) { #if defined(__linux__)