}
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) {
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);
* \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);
}
* \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;
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()
{
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<SBuf::size_type>(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