Hi,
the attached patch expands on the already-merged CharacterSet, adding:
- operator+
- addRange
- range-based constructor
- a number of predefined sets, mostly taken from the HTTP specs
- unit test
It's a candidate for merging to trunk.
--
/kinkie
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: [email protected]
# target_branch: file:///home/kinkie/squid/workspace/trunk/
# testament_sha1: 8d4dd50d54986910c84248b7242fe15b22dbdb20
# timestamp: 2014-01-05 16:17:42 +0100
# base_revision_id: [email protected]\
# fo37ieq0a1td0ata
#
# Begin patch
=== modified file 'src/base/CharacterSet.cc'
--- src/base/CharacterSet.cc 2013-12-18 15:22:06 +0000
+++ src/base/CharacterSet.cc 2013-12-30 13:22:05 +0000
@@ -1,7 +1,7 @@
#include "squid.h"
#include "CharacterSet.h"
-const CharacterSet &
+CharacterSet &
CharacterSet::operator +=(const CharacterSet &src)
{
Storage::const_iterator s = src.chars_.begin();
@@ -16,6 +16,14 @@
return *this;
}
+CharacterSet
+CharacterSet::operator +(const CharacterSet &src) const
+{
+ CharacterSet rv(*this);
+ rv += src;
+ return rv;
+}
+
CharacterSet &
CharacterSet::add(const unsigned char c)
{
@@ -23,6 +31,16 @@
return *this;
}
+CharacterSet &
+CharacterSet::addRange(unsigned char low, unsigned char high)
+{
+ while (low <= high) {
+ chars_[static_cast<uint8_t>(low)] = 1;
+ ++low;
+ }
+ return *this;
+}
+
CharacterSet::CharacterSet(const char *label, const char * const c)
: name(label == NULL ? "anonymous" : label), chars_(Storage(256,0))
{
@@ -30,3 +48,28 @@
for (size_t i = 0; i < clen; ++i)
add(c[i]);
}
+
+CharacterSet::CharacterSet(const char *label, unsigned char low, unsigned char high)
+: name(label == NULL ? "anonymous" : label), chars_(Storage(256,0))
+{
+ addRange(low,high);
+}
+
+const CharacterSet
+CharacterSet::ALPHA("ALPHA", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"),
+CharacterSet::BIT("BIT","01"),
+CharacterSet::CR("CR","\r"),
+CharacterSet::LF("LF","\n"),
+CharacterSet::CRLF("CRLF","\r\n"),
+CharacterSet::DIGIT("DIGIT","0123456789"),
+CharacterSet::DQUOTE("DQUOTE","\""),
+CharacterSet::HTAB("HTAB","\t"),
+CharacterSet::HEXDIG("HEXDIG","0123456789aAbBcCdDeEfF"),
+CharacterSet::SP("SP"," "),
+CharacterSet::VCHAR("VCHAR", 0x21, 0x7e),
+CharacterSet::WSP("WSP"," \t"),
+CharacterSet::TCHAR("TCHAR","!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"),
+CharacterSet::SPECIAL("SPECIAL","()<>@,;:\\\"/[]?={}")
+//,CharacterSet::QDTEXT("QDTEXT",{{9,9},{0x20,0x21},{0x23,0x5b},{0x5d,0x7e},{0x80,0xff}})
+//,CharacterSet::OBSTEXT("OBSTEXT",0x80,0xff)
+;
=== modified file 'src/base/CharacterSet.h'
--- src/base/CharacterSet.h 2013-12-18 15:22:06 +0000
+++ src/base/CharacterSet.h 2013-12-30 13:22:05 +0000
@@ -13,18 +13,64 @@
/// with specified initial contents
CharacterSet(const char *label, const char * const initial);
+ /// define a character set with the given label ("anonymous" if NULL)
+ /// containing characters defined in the supplied ranges
+ /// \see addRange
+ CharacterSet(const char *label, unsigned char low, unsigned char high);
+
/// whether a given character exists in the set
bool operator[](unsigned char c) const {return chars_[static_cast<uint8_t>(c)] != 0;}
/// add a given character to the character set
CharacterSet & add(const unsigned char c);
+ /// add a list of character ranges, expressed as pairs [low,high], including both ends
+ CharacterSet & addRange(unsigned char low, unsigned char high);
+
/// add all characters from the given CharacterSet to this one
- const CharacterSet &operator +=(const CharacterSet &src);
+ CharacterSet &operator +=(const CharacterSet &src);
+
+ /// return a new CharacterSet containing the union of two sets
+ CharacterSet operator +(const CharacterSet &src) const;
/// optional set label for debugging (default: "anonymous")
const char * name;
+ // common character sets, insipired to RFC5234
+ // A-Za-z
+ static const CharacterSet ALPHA;
+ // 0-1
+ static const CharacterSet BIT;
+ // carriage return
+ static const CharacterSet CR;
+ // line feed
+ static const CharacterSet LF;
+ // CRLF
+ static const CharacterSet CRLF;
+ // double quote
+ static const CharacterSet DQUOTE;
+ // 0-9
+ static const CharacterSet DIGIT;
+ // 0-9aAbBcCdDeEfF
+ static const CharacterSet HEXDIG;
+ // horizontal tab
+ static const CharacterSet HTAB;
+ // white space
+ static const CharacterSet SP;
+ // visible (printable) characters
+ static const CharacterSet VCHAR;
+ // <space><tab>
+ static const CharacterSet WSP;
+ // character sets from draft httpbis
+ // any VCHAR except for SPECIAL
+ static const CharacterSet TCHAR;
+ // special VCHARs
+ static const CharacterSet SPECIAL;
+ // qdtext (ready but not enabled for now)
+ //static const CharacterSet QDTEXT;
+ // obs-text
+ //static const CharacterSet OBSTEXT;
+
private:
/** index of characters in this set
*
=== modified file 'src/base/Makefile.am'
--- src/base/Makefile.am 2013-12-17 10:19:44 +0000
+++ src/base/Makefile.am 2014-01-01 17:15:10 +0000
@@ -26,3 +26,27 @@
TextException.h \
Vector.cc \
Vector.h
+
+EXTRA_PROGRAMS = \
+ testCharacterSet
+
+check_PROGRAMS += testCharacterSet
+TESTS += testCharacterSet
+
+testCharacterSet_SOURCES = \
+ CharacterSet.h \
+ testCharacterSet.h \
+ testCharacterSet.cc
+nodist_testCharacterSet_SOURCES = \
+ $(top_srcdir)/src/tests/testMain.cc \
+ $(top_srcdir)/src/tests/stub_debug.cc \
+ $(top_srcdir)/src/tests/stub_MemBuf.cc \
+ $(top_srcdir)/src/tests/stub_cbdata.cc
+testCharacterSet_LDFLAGS = $(LIBADD_DL)
+testCharacterSet_LDADD=\
+ $(SQUID_CPPUNIT_LIBS) \
+ $(SQUID_CPPUNIT_LA) \
+ libbase.la \
+ $(COMPAT_LIB) \
+ $(XTRA_LIBS)
+testCharacterSet_DEPENDENCIES= $(SQUID_CPPUNIT_LA)
=== added file 'src/base/testCharacterSet.cc'
--- src/base/testCharacterSet.cc 1970-01-01 00:00:00 +0000
+++ src/base/testCharacterSet.cc 2013-12-30 13:22:05 +0000
@@ -0,0 +1,82 @@
+#define SQUID_UNIT_TEST 1
+
+#include "squid.h"
+#include "base/CharacterSet.h"
+#include "testCharacterSet.h"
+
+#include <string>
+
+CPPUNIT_TEST_SUITE_REGISTRATION( testCharacterSet );
+
+void
+testCharacterSet::CharacterSetConstruction()
+{
+ {
+ CharacterSet t(NULL,"");
+ CPPUNIT_ASSERT_EQUAL(std::string("anonymous"),std::string(t.name));
+ }
+ {
+ CharacterSet t("test","");
+ CPPUNIT_ASSERT_EQUAL(std::string("test"),std::string(t.name));
+ }
+ {
+ CharacterSet t("test","");
+ for (int j = 0; j < 255; ++j)
+ CPPUNIT_ASSERT_EQUAL(false,t[j]);
+ }
+ {
+ CharacterSet t("test","0");
+ CPPUNIT_ASSERT_EQUAL(true,t['0']);
+ for (int j = 0; j < 255; ++j)
+ if (j != '0')
+ CPPUNIT_ASSERT_EQUAL(false,t[j]);
+ }
+}
+
+void
+testCharacterSet::CharacterSetAdd()
+{
+ CharacterSet t("test","0");
+ t.add(0);
+ CPPUNIT_ASSERT_EQUAL(true,t['\0']);
+ CPPUNIT_ASSERT_EQUAL(true,t['0']);
+}
+
+void
+testCharacterSet::CharacterSetAddRange()
+{
+ CharacterSet t("test","");
+ t.addRange('0','9');
+ CPPUNIT_ASSERT_EQUAL(true,t['0']);
+ CPPUNIT_ASSERT_EQUAL(true,t['5']);
+ CPPUNIT_ASSERT_EQUAL(true,t['9']);
+ CPPUNIT_ASSERT_EQUAL(false,t['a']);
+}
+
+void
+testCharacterSet::CharacterSetConstants()
+{
+ CPPUNIT_ASSERT_EQUAL(true,CharacterSet::ALPHA['a']);
+ CPPUNIT_ASSERT_EQUAL(true,CharacterSet::ALPHA['z']);
+ CPPUNIT_ASSERT_EQUAL(true,CharacterSet::ALPHA['A']);
+ CPPUNIT_ASSERT_EQUAL(true,CharacterSet::ALPHA['Z']);
+ CPPUNIT_ASSERT_EQUAL(false,CharacterSet::ALPHA['5']);
+}
+
+void
+testCharacterSet::CharacterSetUnion()
+{
+ {
+ CharacterSet hex("hex","");
+ hex += CharacterSet::DIGIT;
+ hex += CharacterSet(NULL,"aAbBcCdDeEfF");
+ for (int j = 0; j < 255; ++j)
+ CPPUNIT_ASSERT_EQUAL(CharacterSet::HEXDIG[j],hex[j]);
+ }
+ {
+ CharacterSet hex(NULL,"");
+ hex = CharacterSet::DIGIT + CharacterSet(NULL,"aAbBcCdDeEfF");
+ for (int j = 0; j < 255; ++j)
+ CPPUNIT_ASSERT_EQUAL(CharacterSet::HEXDIG[j],hex[j]);
+ }
+}
=== added file 'src/base/testCharacterSet.h'
--- src/base/testCharacterSet.h 1970-01-01 00:00:00 +0000
+++ src/base/testCharacterSet.h 2013-12-30 09:44:52 +0000
@@ -0,0 +1,26 @@
+#ifndef SQUID_BASE_TESTCHARACTERSET_H
+#define SQUID_BASE_TESTCHARACTERSET_H
+
+#define SQUID_UNIT_TEST 1
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class testCharacterSet : public CPPUNIT_NS::TestFixture
+{
+ CPPUNIT_TEST_SUITE( testCharacterSet );
+ CPPUNIT_TEST( CharacterSetConstruction );
+ CPPUNIT_TEST( CharacterSetAdd );
+ CPPUNIT_TEST( CharacterSetAddRange );
+ CPPUNIT_TEST( CharacterSetConstants );
+ CPPUNIT_TEST( CharacterSetUnion );
+ CPPUNIT_TEST_SUITE_END();
+
+protected:
+ void CharacterSetConstruction();
+ void CharacterSetAdd();
+ void CharacterSetAddRange();
+ void CharacterSetConstants();
+ void CharacterSetUnion();
+};
+
+#endif /* SQUID_BASE_TESTCHARACTERSET_H */
# Begin bundle
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