BLI_string: add BLI_string_elem_split_by_delim

Add a utility function to check if one string contains another
when split by a delimiter.
This commit is contained in:
Campbell Barton
2024-05-02 10:20:56 +10:00
parent 848f1d8792
commit 594ee2c766
3 changed files with 89 additions and 0 deletions

View File

@@ -562,6 +562,27 @@ int BLI_string_find_split_words(const char *str,
int r_words[][2],
int words_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 4);
/**
* A version of `STR_ELEM(..)` that treats `haystack` as multiple elements split by `delim`.
* Return true when `needle` is found in `haystack`.
*
* \param haystack: The string to search in.
* \param delim: The delimiter which divides haystack.
* \param needle: The string to search for
* (must not contain `delim` or the result will never be true).
*
* The following guarantees are made:
* - Only an exact match returns true, requiring the strings length and case be match.
* - Successive delimiters are supported.
* - An empty needle will match against:
* - A blank string.
* - Delimiters at the beginning or end of the string
* - Two successive delimiters.
*/
bool BLI_string_elem_split_by_delim(const char *haystack,
const char delim,
const char *needle) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 3);
/* -------------------------------------------------------------------- */
/** \name String Copy/Format Macros
* Avoid repeating destination with `sizeof(..)`.

View File

@@ -1117,6 +1117,24 @@ int BLI_string_find_split_words(
return n;
}
bool BLI_string_elem_split_by_delim(const char *haystack, const char delim, const char *needle)
{
/* May be zero, returns true when an empty span exists. */
const size_t needle_len = strlen(needle);
const char *p = haystack;
while (true) {
const char *p_next = BLI_strchr_or_end(p, delim);
if (((size_t)(p_next - p) == needle_len) && (memcmp(p, needle, needle_len) == 0)) {
return true;
}
if (*p_next == '\0') {
break;
}
p = p_next + 1;
}
return false;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@@ -1085,6 +1085,56 @@ TEST_F(StringFindSplitWords, LimitChars)
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Element
* \{ */
/* #BLI_string_elem_split_by_delim */
TEST(string, StringElemEmpty)
{
EXPECT_FALSE(BLI_string_elem_split_by_delim("A", ':', ""));
EXPECT_TRUE(BLI_string_elem_split_by_delim("", ':', ""));
EXPECT_TRUE(BLI_string_elem_split_by_delim(":", ':', ""));
EXPECT_TRUE(BLI_string_elem_split_by_delim("::", ':', ""));
EXPECT_TRUE(BLI_string_elem_split_by_delim("A:", ':', ""));
EXPECT_TRUE(BLI_string_elem_split_by_delim(":A", ':', ""));
}
TEST(string, StringElemSingle)
{
EXPECT_TRUE(BLI_string_elem_split_by_delim("A", ':', "A"));
EXPECT_FALSE(BLI_string_elem_split_by_delim("A", ':', "B"));
EXPECT_TRUE(BLI_string_elem_split_by_delim("B", 'A', "B"));
EXPECT_FALSE(BLI_string_elem_split_by_delim("A", 'A', "A"));
}
TEST(string, StringElemComplex)
{
EXPECT_TRUE(BLI_string_elem_split_by_delim("TEST", ':', "TEST"));
EXPECT_TRUE(BLI_string_elem_split_by_delim(":TEST", ':', "TEST"));
EXPECT_TRUE(BLI_string_elem_split_by_delim("TEST:", ':', "TEST"));
EXPECT_TRUE(BLI_string_elem_split_by_delim(":TEST:", ':', "TEST"));
EXPECT_TRUE(BLI_string_elem_split_by_delim("::TEST", ':', "TEST"));
EXPECT_TRUE(BLI_string_elem_split_by_delim("TEST::", ':', "TEST"));
EXPECT_TRUE(BLI_string_elem_split_by_delim("::TEST::", ':', "TEST"));
EXPECT_FALSE(BLI_string_elem_split_by_delim(":TEST ", ':', "TEST"));
EXPECT_FALSE(BLI_string_elem_split_by_delim(" TEST:", ':', "TEST"));
EXPECT_FALSE(BLI_string_elem_split_by_delim(": TEST :", ':', "TEST"));
EXPECT_TRUE(BLI_string_elem_split_by_delim("A:B:TEST", ':', "TEST"));
EXPECT_TRUE(BLI_string_elem_split_by_delim("TEST:A:B", ':', "TEST"));
EXPECT_TRUE(BLI_string_elem_split_by_delim("A:TEST:B", ':', "TEST"));
EXPECT_TRUE(BLI_string_elem_split_by_delim(":A:TEST:B:", ':', "TEST"));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name String Search (Case Insensitive)
* \{ */