------------------------------------------------------------ revno: 13368 revision-id: squid3@treenet.co.nz-20140421171444-8l40fprn07c74bfu parent: squid3@treenet.co.nz-20140421150731-9djwk4mhy4npf30y committer: Amos Jeffries branch nick: trunk timestamp: Mon 2014-04-21 10:14:44 -0700 message: 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. ------------------------------------------------------------ # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: squid3@treenet.co.nz-20140421171444-8l40fprn07c74bfu # target_branch: http://bzr.squid-cache.org/bzr/squid3/trunk/ # testament_sha1: 4a9000e6d1661286741e0d7861a4d3d2fa9af10d # timestamp: 2014-04-21 17:53:48 +0000 # source_branch: http://bzr.squid-cache.org/bzr/squid3/trunk/ # base_revision_id: squid3@treenet.co.nz-20140421150731-\ # 9djwk4mhy4npf30y # # Begin patch === modified file 'src/SBuf.cc' --- src/SBuf.cc 2014-04-06 07:08:04 +0000 +++ src/SBuf.cc 2014-04-21 17:14:44 +0000 @@ -377,12 +377,12 @@ } 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 @@ 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); === modified file 'src/SBuf.h' --- src/SBuf.h 2014-04-06 07:08:04 +0000 +++ src/SBuf.h 2014-04-21 17:14:44 +0000 @@ -255,15 +255,28 @@ * \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; - - /// shorthand version for compare - inline int cmp(const SBuf &S, 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 { + int compare(const SBuf &S, const SBufCaseSensitive isCaseSensitive, const 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 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 @@ * \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; === modified file 'src/tests/testSBuf.cc' --- src/tests/testSBuf.cc 2014-02-08 13:36:42 +0000 +++ src/tests/testSBuf.cc 2014-04-21 17:14:44 +0000 @@ -244,6 +244,63 @@ 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 @@ 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