Tue, 04 Oct 2022 18:49:14 +0200
add zero-termination guarantees
src/cx/string.h | file | annotate | diff | comparison | revisions | |
test/test_string.cpp | file | annotate | diff | comparison | revisions |
--- a/src/cx/string.h Tue Sep 20 10:37:29 2022 +0200 +++ b/src/cx/string.h Tue Oct 04 18:49:14 2022 +0200 @@ -241,6 +241,7 @@ * So developers \em must pass the return value to cx_strfree() eventually. * * \note It is guaranteed that there is only one allocation. + * It is also guaranteed that the returned string is zero-terminated. * * @param alloc the allocator to use * @param count the total number of strings to concatenate @@ -260,6 +261,9 @@ * The resulting string will be allocated by standard \c malloc(). * So developers \em must pass the return value to cx_strfree() eventually. * + * \note It is guaranteed that there is only one allocation. + * It is also guaranteed that the returned string is zero-terminated. + * * @param count the total number of strings to concatenate * @param ... all strings * @return the concatenated string @@ -608,8 +612,7 @@ * * The new string will contain a copy allocated by \p allocator. * - * \note The returned string is guaranteed to be zero-terminated and can safely - * be passed to other APIs. + * \note The returned string is guaranteed to be zero-terminated. * * @param allocator the allocator to use * @param string the string to duplicate @@ -628,8 +631,7 @@ * The new string will contain a copy allocated by standard * \c malloc(). So developers \em must pass the return value to cx_strfree(). * - * \note The returned string is guaranteed to be zero-terminated and can safely - * be passed to other APIs. + * \note The returned string is guaranteed to be zero-terminated. * * @param string the string to duplicate * @return a duplicate of the string @@ -743,7 +745,8 @@ * The pattern is taken literally and is no regular expression. * Replaces at most \p replmax occurrences. * - * The returned string will be allocated by \p allocator. + * The returned string will be allocated by \p allocator and is guaranteed + * to be zero-terminated. * * If allocation fails, or the input string is empty, * the returned string will be empty. @@ -770,8 +773,8 @@ * The pattern is taken literally and is no regular expression. * Replaces at most \p replmax occurrences. * - * The returned string will be allocated by \c malloc() and \em must be passed - * to cx_strfree() eventually. + * The returned string will be allocated by \c malloc() and is guaranteed + * to be zero-terminated. * * If allocation fails, or the input string is empty, * the returned string will be empty. @@ -790,7 +793,8 @@ * * The pattern is taken literally and is no regular expression. * - * The returned string will be allocated by \p allocator. + * The returned string will be allocated by \p allocator and is guaranteed + * to be zero-terminated. * * If allocation fails, or the input string is empty, * the returned string will be empty. @@ -810,8 +814,8 @@ * The pattern is taken literally and is no regular expression. * Replaces at most \p replmax occurrences. * - * The returned string will be allocated by \c malloc() and \em must be passed - * to cx_strfree() eventually. + * The returned string will be allocated by \c malloc() and is guaranteed + * to be zero-terminated. * * If allocation fails, or the input string is empty, * the returned string will be empty.
--- a/test/test_string.cpp Tue Sep 20 10:37:29 2022 +0200 +++ b/test/test_string.cpp Tue Oct 04 18:49:14 2022 +0200 @@ -31,6 +31,8 @@ #include <gtest/gtest.h> +#define EXPECT_ZERO_TERMINATED(str) EXPECT_EQ((str).ptr[(str).length], '\0') + TEST(String, construct) { cxstring s1 = cx_str("1234"); cxstring s2 = cx_strn("abcd", 2); @@ -55,6 +57,22 @@ EXPECT_TRUE(alloc.verify()); } +TEST(String, strdup) { + cxstring str = CX_STR("test"); + cxmutstr dup = cx_strdup(str); + ASSERT_EQ(dup.length, str.length); + EXPECT_STREQ(dup.ptr, str.ptr); + EXPECT_ZERO_TERMINATED(dup); + cx_strfree(&dup); + + str.length = 2; + dup = cx_strdup(str); + ASSERT_EQ(dup.length, str.length); + EXPECT_STREQ(dup.ptr, "te"); + EXPECT_ZERO_TERMINATED(dup); + cx_strfree(&dup); +} + TEST(String, strlen) { cxstring s1 = CX_STR("1234"); cxstring s2 = CX_STR(".:.:."); @@ -210,7 +228,6 @@ EXPECT_GT(cx_strcasecmp(str, cx_str("compare")), 0); } - TEST(String, strcat) { cxstring s1 = CX_STR("12"); cxstring s2 = CX_STR("34"); @@ -221,18 +238,22 @@ cxmutstr t1 = cx_strcat_a(&alloc, 2, s1, s2); EXPECT_EQ(cx_strcmp(cx_strcast(t1), cx_str("1234")), 0); + EXPECT_ZERO_TERMINATED(t1); cx_strfree_a(&alloc, &t1); cxmutstr t2 = cx_strcat_a(&alloc, 3, s1, s2, s3); EXPECT_EQ(cx_strcmp(cx_strcast(t2), cx_str("123456")), 0); + EXPECT_ZERO_TERMINATED(t2); cx_strfree_a(&alloc, &t2); cxmutstr t3 = cx_strcat_a(&alloc, 6, s1, sn, s2, sn, s3, sn); EXPECT_EQ(cx_strcmp(cx_strcast(t3), cx_str("123456")), 0); + EXPECT_ZERO_TERMINATED(t3); cx_strfree_a(&alloc, &t3); cxmutstr t4 = cx_strcat_a(&alloc, 2, sn, sn); EXPECT_EQ(cx_strcmp(cx_strcast(t4), cx_str("")), 0); + EXPECT_ZERO_TERMINATED(t4); cx_strfree_a(&alloc, &t4); EXPECT_TRUE(alloc.verify()); @@ -524,63 +545,75 @@ cxstring csstr = CX_STR("test AB ab TEST xyz"); cxmutstr repl = cx_strreplace(str, cx_str("abab"), cx_str("muchlonger")); - cxstring expected = CX_STR("test muchlongerab string aba"); + auto expected = "test muchlongerab string aba"; cxmutstr repln = cx_strreplacen(str, cx_str("ab"), cx_str("c"), 2); - cxstring expectedn = CX_STR("test ccab string aba"); + auto expectedn = "test ccab string aba"; cxmutstr longrepl = cx_strreplace(longstr, cx_str("a"), cx_str("z")); - cxstring longexpect = CX_STR( - "xyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzcd"); + auto longexpect = "xyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzcd"; cxmutstr replnotrail = cx_strreplace(notrail, cx_str("ab"), cx_str("z")); - cxstring notrailexpect = CX_STR("test zz"); + auto notrailexpect = "test zz"; cxmutstr repleq = cx_strreplace(str, str, cx_str("hello")); - cxstring eqexpect = CX_STR("hello"); + auto eqexpect = "hello"; cxmutstr replempty1 = cx_strreplace(empty, cx_str("ab"), cx_str("c")); // expect: empty cxmutstr replempty2 = cx_strreplace(str, cx_str("abab"), empty); - cxstring emptyexpect2 = CX_STR("test ab string aba"); + auto emptyexpect2 = "test ab string aba"; cxmutstr replpre = cx_strreplace(str, cx_str("test "), cx_str("TEST ")); - cxstring preexpected = CX_STR("TEST ababab string aba"); + auto preexpected = "TEST ababab string aba"; cxmutstr replan1 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 1); - cxstring an1expected = CX_STR("xaaaaaaaaa"); + auto an1expected = "xaaaaaaaaa"; cxmutstr replan4 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 4); - cxstring an4expected = CX_STR("xxxxaaaaaa"); + auto an4expected = "xxxxaaaaaa"; cxmutstr replan9 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 9); - cxstring an9expected = CX_STR("xxxxxxxxxa"); + auto an9expected = "xxxxxxxxxa"; cxmutstr replan10 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 10); - cxstring an10expected = CX_STR("xxxxxxxxxx"); + auto an10expected = "xxxxxxxxxx"; cxmutstr replcs1 = cx_strreplace(csstr, cx_str("AB"), cx_str("*")); - cxstring cs1expected = CX_STR("test * ab TEST xyz"); + auto cs1expected = "test * ab TEST xyz"; cxmutstr replcs2 = cx_strreplace(csstr, cx_str("test"), cx_str("TEST")); - cxstring cs2expected = CX_STR("TEST AB ab TEST xyz"); + auto cs2expected = "TEST AB ab TEST xyz"; EXPECT_NE(repl.ptr, str.ptr); - EXPECT_EQ(cx_strcmp(cx_strcast(repl), expected), 0); - EXPECT_NE(repln.ptr, str.ptr); - EXPECT_EQ(cx_strcmp(cx_strcast(repln), expectedn), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(longrepl), longexpect), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(replnotrail), notrailexpect), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(repleq), eqexpect), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(replempty1), empty), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(replempty2), emptyexpect2), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(replpre), preexpected), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(replan1), an1expected), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(replan4), an4expected), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(replan9), an9expected), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(replan10), an10expected), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(replcs1), cs1expected), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(replcs2), cs2expected), 0); + EXPECT_ZERO_TERMINATED(repl); + EXPECT_STREQ(repl.ptr, expected); + EXPECT_ZERO_TERMINATED(repln); + EXPECT_STREQ(repln.ptr, expectedn); + EXPECT_ZERO_TERMINATED(longrepl); + EXPECT_STREQ(longrepl.ptr, longexpect); + EXPECT_ZERO_TERMINATED(replnotrail); + EXPECT_STREQ(replnotrail.ptr, notrailexpect); + EXPECT_ZERO_TERMINATED(repleq); + EXPECT_STREQ(repleq.ptr, eqexpect); + EXPECT_ZERO_TERMINATED(replempty1); + EXPECT_STREQ(replempty1.ptr, ""); + EXPECT_ZERO_TERMINATED(replempty2); + EXPECT_STREQ(replempty2.ptr, emptyexpect2); + EXPECT_ZERO_TERMINATED(replpre); + EXPECT_STREQ(replpre.ptr, preexpected); + EXPECT_ZERO_TERMINATED(replan1); + EXPECT_STREQ(replan1.ptr, an1expected); + EXPECT_ZERO_TERMINATED(replan4); + EXPECT_STREQ(replan4.ptr, an4expected); + EXPECT_ZERO_TERMINATED(replan9); + EXPECT_STREQ(replan9.ptr, an9expected); + EXPECT_ZERO_TERMINATED(replan10); + EXPECT_STREQ(replan10.ptr, an10expected); + EXPECT_ZERO_TERMINATED(replcs1); + EXPECT_STREQ(replcs1.ptr, cs1expected); + EXPECT_ZERO_TERMINATED(replcs2); + EXPECT_STREQ(replcs2.ptr, cs2expected); cx_strfree(&repl); cx_strfree(&repln);