mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-27 23:30:47 +01:00
FFstrbuf: improves empty-string handling
for ffStrbufMatchSeparatedNS and ffStrbufSeparatedContainNS Fixes #2585
This commit is contained in:
+23
-16
@@ -723,16 +723,17 @@ bool ffStrbufRemoveDupWhitespaces(FFstrbuf* strbuf) {
|
||||
/// @param compLength The length of the separated string to check.
|
||||
/// @param comp The separated string to check.
|
||||
/// @param separator The separator character.
|
||||
///
|
||||
/// Empty-string handling:
|
||||
/// - If `strbuf` is empty, it matches only if `comp` contains an empty segment
|
||||
/// (e.g. "abc::def", "abc:", ":abc") or `comp` itself is empty.
|
||||
/// - If `comp` is empty (compLength == 0), it matches only if `strbuf` is also empty.
|
||||
bool ffStrbufMatchSeparatedNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator) {
|
||||
if (strbuf->length == 0) {
|
||||
return true;
|
||||
if (__builtin_expect(compLength == 0, false)) {
|
||||
return strbuf->length == 0;
|
||||
}
|
||||
|
||||
if (compLength == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const char* p = comp; p < comp + compLength;) {
|
||||
for (const char* p = comp; p <= comp + compLength;) {
|
||||
const char* colon = memchr(p, separator, (size_t) (comp + compLength - p));
|
||||
if (colon == nullptr) {
|
||||
uint32_t remainingLen = (uint32_t) (comp + compLength - p);
|
||||
@@ -752,15 +753,11 @@ bool ffStrbufMatchSeparatedNS(const FFstrbuf* strbuf, uint32_t compLength, const
|
||||
|
||||
/// @brief Case insensitive version of ffStrbufMatchSeparatedNS.
|
||||
bool ffStrbufMatchSeparatedIgnCaseNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator) {
|
||||
if (strbuf->length == 0) {
|
||||
return true;
|
||||
if (__builtin_expect(compLength == 0, false)) {
|
||||
return strbuf->length == 0;
|
||||
}
|
||||
|
||||
if (compLength == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const char* p = comp; p < comp + compLength;) {
|
||||
for (const char* p = comp; p <= comp + compLength;) {
|
||||
const char* colon = memchr(p, separator, (size_t) (comp + compLength - p));
|
||||
if (colon == nullptr) {
|
||||
uint32_t remainingLen = (uint32_t) (comp + compLength - p);
|
||||
@@ -802,9 +799,15 @@ int ffStrbufAppendUtf32CodePoint(FFstrbuf* strbuf, uint32_t codepoint) {
|
||||
/// @param compLength The length of the separated string to check.
|
||||
/// @param comp The substring to check.
|
||||
/// @param separator The separator character.
|
||||
///
|
||||
/// Empty-string handling is symmetric to ffStrbufMatchSeparatedNS
|
||||
bool ffStrbufSeparatedContainNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator) {
|
||||
if (__builtin_expect(strbuf->length == 0, false)) {
|
||||
return compLength == 0; // An empty list contains only the empty segment
|
||||
}
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
while (startIndex < strbuf->length) {
|
||||
while (startIndex <= strbuf->length) { // `<=` so a trailing empty segment (e.g. "abc:") is checked
|
||||
uint32_t colonIndex = ffStrbufNextIndexC(strbuf, startIndex, separator);
|
||||
|
||||
uint32_t folderLength = colonIndex - startIndex;
|
||||
@@ -819,8 +822,12 @@ bool ffStrbufSeparatedContainNS(const FFstrbuf* strbuf, uint32_t compLength, con
|
||||
}
|
||||
|
||||
bool ffStrbufSeparatedContainIgnCaseNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator) {
|
||||
if (__builtin_expect(strbuf->length == 0, false)) {
|
||||
return compLength == 0; // An empty list contains only the empty segment
|
||||
}
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
while (startIndex < strbuf->length) {
|
||||
while (startIndex <= strbuf->length) { // `<=` so a trailing empty segment (e.g. "abc:") is checked
|
||||
uint32_t colonIndex = ffStrbufNextIndexC(strbuf, startIndex, separator);
|
||||
|
||||
uint32_t folderLength = colonIndex - startIndex;
|
||||
|
||||
@@ -681,6 +681,30 @@ int main(void) {
|
||||
VERIFY(ffStrbufMatchSeparatedS(&strbuf, ":abcdef", ':') == false);
|
||||
}
|
||||
|
||||
{
|
||||
// ffStrbufMatchSeparatedNS with explicit compLength: the length-bounded
|
||||
// contract is load-bearing for the Linux keyboard handler, which passes
|
||||
// a fixed-size buffer length rather than a NUL-terminated string.
|
||||
ffStrbufSetStatic(&strbuf, "abc");
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 11, "abc:def:ghi", ':') == true);
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 3, "abc:def:ghi", ':') == true);
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 7, "abc:def:ghi", ':') == true); // "abc:def"
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 4, "abc:def:ghi", ':') == true); // "abc:"
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 5, "abc:def:ghi", ':') == true); // "abc:d" truncated, but "abc" is a complete segment
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 3, "abd:def:ghi", ':') == false);
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 0, "abc:def:ghi", ':') == false);
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 0, "", ':') == false);
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 11, "abc:def:ghi", ' ') == false); // no separator in bounds
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 12, "abc:def:ghi\0j", ':') == true); // embedded NUL inside bounds
|
||||
|
||||
ffStrbufClear(&strbuf);
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 11, "abc:def:ghi", ':') == false); // no empty segment
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 8, "abc::def", ':') == true); // empty segment in the middle
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 4, "abc:", ':') == true); // trailing empty segment
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 4, ":abc", ':') == true); // leading empty segment
|
||||
VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 0, "abc", ':') == true); // empty separated string itself
|
||||
}
|
||||
|
||||
{
|
||||
ffStrbufSetStatic(&strbuf, "ABC");
|
||||
VERIFY(ffStrbufMatchSeparatedIgnCaseS(&strbuf, "abc:def:ghi", ' ') == false);
|
||||
@@ -721,6 +745,27 @@ int main(void) {
|
||||
VERIFY(ffStrbufSeparatedContainIgnCaseS(&strbuf, "i", ':') == false);
|
||||
}
|
||||
|
||||
{
|
||||
// ffStrbufSeparatedContainNS with empty strings: an empty list contains
|
||||
// only the empty segment (symmetric to ffStrbufMatchSeparatedNS); an
|
||||
// empty comp matches only an empty segment in the list.
|
||||
ffStrbufClear(&strbuf);
|
||||
VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "", ':') == true); // empty list contains the empty segment
|
||||
VERIFY(ffStrbufSeparatedContainNS(&strbuf, 3, "abc", ':') == false); // empty list contains no non-empty segment
|
||||
VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "abc", ':') == true); // compLength == 0 means an empty segment
|
||||
|
||||
ffStrbufSetStatic(&strbuf, "abc::def");
|
||||
VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "", ':') == true); // empty segment in the middle
|
||||
ffStrbufSetStatic(&strbuf, "abc:");
|
||||
VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "", ':') == true); // trailing empty segment
|
||||
ffStrbufSetStatic(&strbuf, ":abc");
|
||||
VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "", ':') == true); // leading empty segment
|
||||
ffStrbufSetStatic(&strbuf, "abc:def");
|
||||
VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "", ':') == false); // no empty segment
|
||||
ffStrbufSetStatic(&strbuf, "abc");
|
||||
VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "", ':') == false); // no empty segment
|
||||
}
|
||||
|
||||
{
|
||||
ffStrbufSetStatic(&strbuf, "abc");
|
||||
ffStrbufSubstr(&strbuf, 0, 1); // start, end
|
||||
|
||||
Reference in New Issue
Block a user