]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
SBuf: add methods for comparision directly against a C-string
authorAmos Jeffries <squid3@treenet.co.nz>
Mon, 21 Apr 2014 17:14:44 +0000 (10:14 -0700)
committerAmos Jeffries <squid3@treenet.co.nz>
Mon, 21 Apr 2014 17:14:44 +0000 (10:14 -0700)
Based on GNU strncasecmp() logics.

Also,
 * improve const-correctness on all SBuf comparision methods
 * add unit tests for the new API methods.

src/SBuf.cc
src/SBuf.h
src/tests/testSBuf.cc

index 36d2491a40a45c598ee940619d096d911a6ff746..5755f990baad4c0ecd10c65c6ec6d50c67575709 100644 (file)
@@ -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);
index 93bc64a608dc6e3851677a5117f1a30c6d923b38..be1287a9fac11375395ff0e4af4df7b505e40831 100644 (file)
@@ -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;
index 8cf5e7b1d3211370917f7e0efcb3c4bffbb4362d..9b18a5d83e626373778915fb98c31c829b32283a 100644 (file)
@@ -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<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