From: Amos Jeffries Date: Mon, 21 Apr 2014 17:14:44 +0000 (-0700) Subject: SBuf: add methods for comparision directly against a C-string X-Git-Tag: SQUID_3_5_0_1~277 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1284f77cf771998e24dea99de24bf7edb37ba84a;p=thirdparty%2Fsquid.git SBuf: add methods for comparision directly against a C-string Based on GNU strncasecmp() logics. Also, * improve const-correctness on all SBuf comparision methods * add unit tests for the new API methods. --- diff --git a/src/SBuf.cc b/src/SBuf.cc index 36d2491a40..5755f990ba 100644 --- a/src/SBuf.cc +++ b/src/SBuf.cc @@ -377,12 +377,12 @@ memcasecmp(const char *b1, const char *b2, SBuf::size_type len) } int -SBuf::compare(const SBuf &S, SBufCaseSensitive isCaseSensitive, size_type n) const +SBuf::compare(const SBuf &S, const SBufCaseSensitive isCaseSensitive, const size_type n) const { if (n != npos) return substr(0,n).compare(S.substr(0,n),isCaseSensitive); - size_type byteCompareLen = min(S.length(), length()); + const size_type byteCompareLen = min(S.length(), length()); ++stats.compareSlow; int rv = 0; if (isCaseSensitive == caseSensitive) { @@ -399,8 +399,61 @@ SBuf::compare(const SBuf &S, SBufCaseSensitive isCaseSensitive, size_type n) con return -1; } +int +SBuf::compare(const char *s, const SBufCaseSensitive isCaseSensitive, const size_type n) const +{ + // 0-length comparison is always true regardless of buffer states + if (!n) { + ++stats.compareFast; + return 0; + } + + // N-length compare MUST provide a non-NULL C-string pointer + assert(s); + + // when this is a 0-length string, no need for any complexity. + if (!length()) { + ++stats.compareFast; + return '\0' - *s; + } + + // brute-force scan in order to avoid ever needing strlen() on a c-string. + ++stats.compareSlow; + const char *left = buf(); + const char *right = s; + int rv = 0; + // what area to scan. + // n may be npos, but we treat that as a huge positive value + size_type byteCount = min(length(), n); + + // loop until we find a difference, a '\0', or reach the end of area to scan + if (isCaseSensitive == caseSensitive) { + while ((rv = *left - *right++) == 0) { + if (*left++ == '\0' || --byteCount == 0) + break; + } + } else { + while ((rv = tolower(*left) - tolower(*right++)) == 0) { + if (*left++ == '\0' || --byteCount == 0) + break; + } + } + + // If we stopped scanning because we reached the end + // of buf() before we reached the end of s, + // pretend we have a 0-terminator there to compare. + // NP: the loop already incremented "right" ready for this comparison + if (!byteCount && length() < n) + return '\0' - *right; + + // If we found a difference within the scan area, + // or we found a '\0', + // or all n characters were identical (and none was \0). + return rv; +} + bool -SBuf::startsWith(const SBuf &S, SBufCaseSensitive isCaseSensitive) const +SBuf::startsWith(const SBuf &S, const SBufCaseSensitive isCaseSensitive) const { debugs(24, 8, id << " startsWith " << S.id << ", caseSensitive: " << isCaseSensitive); diff --git a/src/SBuf.h b/src/SBuf.h index 93bc64a608..be1287a9fa 100644 --- a/src/SBuf.h +++ b/src/SBuf.h @@ -255,15 +255,28 @@ public: * \retval <0 argument of the call is smaller than called SBuf * \retval 0 argument of the call has the same contents of called SBuf */ - int compare(const SBuf &S, SBufCaseSensitive isCaseSensitive, size_type n = npos) const; + int compare(const SBuf &S, const SBufCaseSensitive isCaseSensitive, const size_type n = npos) const; - /// shorthand version for compare - inline int cmp(const SBuf &S, size_type n = npos) const { + /// shorthand version for compare() + inline int cmp(const SBuf &S, const size_type n = npos) const { return compare(S,caseSensitive,n); } - /// shorthand version for case-insensitive comparison - inline int caseCmp(const SBuf &S, size_type n = npos) const { + /// shorthand version for case-insensitive compare() + inline int caseCmp(const SBuf &S, const size_type n = npos) const { + return compare(S,caseInsensitive,n); + } + + /// Comparison with a C-string. + int compare(const char *s, const SBufCaseSensitive isCaseSensitive, const size_type n = npos) const; + + /// Shorthand version for C-string compare(). + inline int cmp(const char *S, const size_type n = npos) const { + return compare(S,caseSensitive,n); + } + + /// Shorthand version for case-insensitive C-string compare(). + inline int caseCmp(const char *S, const size_type n = npos) const { return compare(S,caseInsensitive,n); } @@ -272,7 +285,7 @@ public: * \param isCaseSensitive one of caseSensitive or caseInsensitive * \retval true argument is a prefix of the SBuf */ - bool startsWith(const SBuf &S, SBufCaseSensitive isCaseSensitive = caseSensitive) const; + bool startsWith(const SBuf &S, const SBufCaseSensitive isCaseSensitive = caseSensitive) const; bool operator ==(const SBuf & S) const; bool operator !=(const SBuf & S) const; diff --git a/src/tests/testSBuf.cc b/src/tests/testSBuf.cc index 8cf5e7b1d3..9b18a5d83e 100644 --- a/src/tests/testSBuf.cc +++ b/src/tests/testSBuf.cc @@ -244,6 +244,63 @@ static int sign(int v) return 0; } +static void +testComparisonStdFull(const char *left, const char *right) +{ + if (sign(strcmp(left, right)) != sign(SBuf(left).cmp(SBuf(right)))) + std::cerr << std::endl << " cmp(SBuf) npos " << left << " ?= " << right << std::endl; + CPPUNIT_ASSERT_EQUAL(sign(strcmp(left, right)), sign(SBuf(left).cmp(SBuf(right)))); + + if (sign(strcmp(left, right)) != sign(SBuf(left).cmp(right))) + std::cerr << std::endl << " cmp(char*) npos " << left << " ?= " << right << std::endl; + CPPUNIT_ASSERT_EQUAL(sign(strcmp(left, right)), sign(SBuf(left).cmp(right))); + + if (sign(strcasecmp(left, right)) != sign(SBuf(left).caseCmp(SBuf(right)))) + std::cerr << std::endl << " caseCmp(SBuf) npos " << left << " ?= " << right << std::endl; + CPPUNIT_ASSERT_EQUAL(sign(strcasecmp(left, right)), sign(SBuf(left).caseCmp(SBuf(right)))); + + if (sign(strcasecmp(left, right)) != sign(SBuf(left).caseCmp(right))) + std::cerr << std::endl << " caseCmp(char*) npos " << left << " ?= " << right << std::endl; + CPPUNIT_ASSERT_EQUAL(sign(strcasecmp(left, right)), sign(SBuf(left).caseCmp(right))); +} + +static void +testComparisonStdN(const char *left, const char *right, const size_t n) +{ + if (sign(strncmp(left, right, n)) != sign(SBuf(left).cmp(SBuf(right), n))) + std::cerr << std::endl << " cmp(SBuf) " << n << ' ' << left << " ?= " << right << std::endl; + CPPUNIT_ASSERT_EQUAL(sign(strncmp(left, right, n)), sign(SBuf(left).cmp(SBuf(right), n))); + + if (sign(strncmp(left, right, n)) != sign(SBuf(left).cmp(right, n))) + std::cerr << std::endl << " cmp(char*) " << n << ' ' << SBuf(left) << " ?= " << right << std::endl; + CPPUNIT_ASSERT_EQUAL(sign(strncmp(left, right, n)), sign(SBuf(left).cmp(right, n))); + + if (sign(strncasecmp(left, right, n)) != sign(SBuf(left).caseCmp(SBuf(right), n))) + std::cerr << std::endl << " caseCmp(SBuf) " << n << ' ' << left << " ?= " << right << std::endl; + CPPUNIT_ASSERT_EQUAL(sign(strncasecmp(left, right, n)), sign(SBuf(left).caseCmp(SBuf(right), n))); + + if (sign(strncasecmp(left, right, n)) != sign(SBuf(left).caseCmp(right, n))) + std::cerr << std::endl << " caseCmp(char*) " << n << ' ' << SBuf(left) << " ?= " << right << std::endl; + CPPUNIT_ASSERT_EQUAL(sign(strncasecmp(left, right, n)), sign(SBuf(left).caseCmp(right, n))); +} + +static void +testComparisonStdOneWay(const char *left, const char *right) +{ + testComparisonStdFull(left, right); + const size_t maxN = 2 + min(strlen(left), strlen(right)); + for (size_t n = 0; n <= maxN; ++n) { + testComparisonStdN(left, right, n); + } +} + +static void +testComparisonStd(const char *s1, const char *s2) +{ + testComparisonStdOneWay(s1, s2); + testComparisonStdOneWay(s2, s1); +} + void testSBuf::testComparisons() { @@ -278,6 +335,41 @@ testSBuf::testComparisons() CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2,3)); CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2,2)); CPPUNIT_ASSERT_EQUAL(0,s1.cmp(s2,2)); + + testComparisonStd("foo", "fooz"); + testComparisonStd("foo", "foo"); + testComparisonStd("foo", "f"); + testComparisonStd("foo", "bar"); + + testComparisonStd("foo", "FOOZ"); + testComparisonStd("foo", "FOO"); + testComparisonStd("foo", "F"); + + testComparisonStdOneWay("", ""); + + // rare case C-string input matching SBuf with N>strlen(s) + { + char *right = xstrdup("foo34567890123456789012345678"); + SBuf left("fooZYXWVUTSRQPONMLKJIHGFEDCBA"); + // is 3 bytes in length. NEVER more. + right[3] = '\0'; + left.setAt(3, '\0'); + + // pick another spot to truncate at if something goes horribly wrong. + right[14] = '\0'; + left.setAt(14, '\0'); + + const SBuf::size_type maxN = 20 + min(left.length(), static_cast(strlen(right))); + for (SBuf::size_type n = 0; n <= maxN; ++n) { + if (sign(strncmp(left.rawContent(), right, n)) != sign(left.cmp(right, n)) ) + std::cerr << std::endl << " cmp(char*) " << n << ' ' << left << " ?= " << right; + CPPUNIT_ASSERT_EQUAL(sign(strncmp(left.rawContent(), right, n)), sign(left.cmp(right, n))); + if (sign(strncasecmp(left.rawContent(), right, n)) != sign(left.caseCmp(right, n))) + std::cerr << std::endl << " caseCmp(char*) " << n << ' ' << left << " ?= " << right; + CPPUNIT_ASSERT_EQUAL(sign(strncasecmp(left.rawContent(), right, n)), sign(left.caseCmp(right, n))); + } + xfree(right); + } } void