From f182efe2ac0ab58b2af4c503ea7b49988f307b5a Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 28 Aug 2015 20:36:52 +0100 Subject: Support std::string fragment storage, reduce copy overheads, better control --- libadhocutil/buffer.cpp | 180 ++++++++++++++++++++++++---------- libadhocutil/buffer.h | 66 ++++++++++--- libadhocutil/unittests/testBuffer.cpp | 36 +++---- 3 files changed, 195 insertions(+), 87 deletions(-) diff --git a/libadhocutil/buffer.cpp b/libadhocutil/buffer.cpp index daf5be6..3468f27 100644 --- a/libadhocutil/buffer.cpp +++ b/libadhocutil/buffer.cpp @@ -2,52 +2,123 @@ #include #include -Buffer::Fragment::Fragment(const char * b) : - len(strlen(b)) +// +// CString Fragment +// + +Buffer::CStringFragment::CStringFragment(const char * b, CStringHandling h) : + len(strlen(b)), + buf(b), + handling(h) { - buf = (char*)malloc(len + 1); - memcpy(buf, b, len + 1); - *(buf + len) = '\0'; } -Buffer::Fragment::Fragment(const char * b, size_t l) : - len(l) +Buffer::CStringFragment::CStringFragment(const char * b, CStringHandling h, size_t l) : + len(l), + buf(b), + handling(h) { - buf = (char*)malloc(l + 1); - memcpy(buf, b, l + 1); - *(buf + len) = '\0'; } -Buffer::Fragment::Fragment(char * b, size_t l, bool copy) : - len(l) +Buffer::CStringFragment::CStringFragment(char * b, CStringHandling h) : + len(strlen(b)), + buf(b), + handling(h) { - if (copy) { - buf = (char*)malloc(l + 1); - memcpy(buf, b, l + 1); - } - else { - buf = b; +} + +Buffer::CStringFragment::CStringFragment(char * b, CStringHandling h, size_t l) : + len(l), + buf(b), + handling(h) +{ +} + +Buffer::CStringFragment::~CStringFragment() +{ + if (handling != Use) { // Copy or Free + free(const_cast(buf)); } - *(buf + len) = '\0'; } -Buffer::Fragment::~Fragment() +char +Buffer::CStringFragment::operator[](size_t x) const +{ + return buf[x]; +} + +size_t +Buffer::CStringFragment::length() const +{ + return len; +} + +const char * +Buffer::CStringFragment::c_str() const +{ + return buf; +} + +std::string +Buffer::CStringFragment::str() const +{ + return buf; +} + +// +// Std::String Fragment +// + +Buffer::StringFragment::StringFragment(const std::string & str) : + buf(str) +{ +} + +size_t +Buffer::StringFragment::length() const +{ + return buf.length(); +} + +const char * +Buffer::StringFragment::c_str() const +{ + return buf.c_str(); +} + +std::string +Buffer::StringFragment::str() const { - free(buf); + return buf; } +char +Buffer::StringFragment::operator[](size_t x) const +{ + return buf[x]; +} + +// +// Buffer :) +// + Buffer::Buffer() { } -Buffer::Buffer(const char * src) +Buffer::Buffer(const char * src, CStringHandling h) +{ + append(src, h); +} + +Buffer::Buffer(char * src, CStringHandling h) { - content.push_back(new Fragment(src)); + append(src, h); } -Buffer::Buffer(char * src, bool copy) +Buffer::Buffer(const std::string & str) { - content.push_back(new Fragment(src, strlen(src), copy)); + append(str); } Buffer::~Buffer() @@ -55,19 +126,29 @@ Buffer::~Buffer() } Buffer & -Buffer::append(const char * str) +Buffer::append(const char * str, CStringHandling h) { if (str && *str) { - content.push_back(new Fragment(str)); + if (h == Copy) { + content.push_back(new StringFragment(str)); + } + else { + content.push_back(new CStringFragment(str, h)); + } } return *this; } Buffer & -Buffer::append(char * str, bool copy) +Buffer::append(char * str, CStringHandling h) { if (str && *str) { - content.push_back(new Fragment(str, strlen(str), copy)); + if (h == Copy) { + content.push_back(new StringFragment(str)); + } + else { + content.push_back(new CStringFragment(str, h)); + } } return *this; } @@ -76,7 +157,7 @@ Buffer & Buffer::append(const std::string & str) { if (!str.empty()) { - content.push_back(new Fragment(str.c_str(), str.length())); + content.push_back(new StringFragment(str)); } return *this; } @@ -97,7 +178,7 @@ Buffer::vappendf(const char * fmt, va_list args) char * frag; size_t len = vasprintf(&frag, fmt, args); if (len > 0) { - content.push_back(new Fragment(frag, len, false)); + content.push_back(new CStringFragment(frag, Free, len)); } else { free(frag); @@ -129,13 +210,13 @@ void Buffer::writeto(char * buf, size_t bufSize, size_t off) const { Content::const_iterator f = content.begin(); - while (f != content.end() && (*f)->len < off) { - off -= (*f)->len; + while (f != content.end() && (*f)->length() < off) { + off -= (*f)->length(); ++f; } while (f != content.end() && bufSize) { - for (size_t c = 0; bufSize && c < (*f)->len; bufSize--) { - *buf++ = (*f)->buf[c++]; + for (size_t c = 0; bufSize && c < (*f)->length(); bufSize--) { + *buf++ = (**f)[c++]; } ++f; off = 0; @@ -148,13 +229,13 @@ Buffer::operator std::string() const if (content.size() > 1) { std::string res; res.reserve(length()); - for (const Fragment::Ptr & f : content) { - res.append(f->buf, f->len); + for (const auto & f : content) { + res.append(f->str()); } return res; } else if (content.size() == 1) { - return std::string(content.front()->buf, content.front()->len); + return std::string(content.front()->str()); } return std::string(); } @@ -165,23 +246,16 @@ Buffer::operator const char *() const return ""; } flatten(); - return content.front()->buf; + return content.front()->c_str(); } void Buffer::flatten() const { if (content.size() > 1) { - auto len = length(); - auto buf = (char*)malloc(len + 1); - auto f = new Fragment(buf, len, false); - for (const Fragment::Ptr & f : content) { - memcpy(buf, f->buf, f->len); - buf += f->len; - } - *buf = '\0'; - content.clear(); - content.push_back(f); + auto f = new StringFragment(str()); + content.resize(1); + content.front() = f; } } @@ -196,7 +270,7 @@ Buffer::length() const { size_t len = 0; for (const Content::value_type & c : content) { - len += c->len; + len += c->length(); } return len; } @@ -205,7 +279,7 @@ Buffer & Buffer::operator=(const char * str) { content.resize(1); - content[0] = new Fragment(str); + content.front() = new StringFragment(str); return *this; } @@ -213,7 +287,7 @@ Buffer & Buffer::operator=(const std::string & str) { content.resize(1); - content[0] = new Fragment(str.c_str(), str.length()); + content.front() = new StringFragment(str); return *this; } @@ -239,7 +313,7 @@ std::ostream & std::operator<<(std::ostream & os, const Buffer & b) { for (const auto & f : b.content) { - os.write(f->buf, f->len); + os.write(f->c_str(), f->length()); } return os; } diff --git a/libadhocutil/buffer.h b/libadhocutil/buffer.h index c435574..aa1560a 100644 --- a/libadhocutil/buffer.h +++ b/libadhocutil/buffer.h @@ -12,17 +12,20 @@ class DLL_PUBLIC Buffer; namespace std { - DLL_PUBLIC std::ostream & operator<<(std::ostream &, const Buffer &); + DLL_PUBLIC std::ostream & operator<<(std::ostream &, const Buffer &); } class DLL_PUBLIC Buffer : public virtual IntrusivePtrBase { public: + enum CStringHandling { Use, Copy, Free }; + typedef boost::intrusive_ptr Ptr; typedef boost::intrusive_ptr CPtr; Buffer(); - Buffer(const char * src); - Buffer(char * src, bool copy); + Buffer(const char * src, CStringHandling); + Buffer(char * src, CStringHandling); + Buffer(const std::string &); ~Buffer(); Buffer & operator+=(const char * str); @@ -39,8 +42,8 @@ class DLL_PUBLIC Buffer : public virtual IntrusivePtrBase { void writeto(char * buf, size_t bufSize, size_t off) const; friend std::ostream & std::operator<<(std::ostream &, const Buffer &); - Buffer & append(const char * str); - Buffer & append(char * str, bool copy); + Buffer & append(const char * str, CStringHandling h); + Buffer & append(char * str, CStringHandling h); Buffer & append(const std::string & str); Buffer & appendf(const char * fmt, ...) __attribute__((format (printf, 2, 3))); Buffer & vappendf(const char * fmt, va_list args); @@ -55,7 +58,6 @@ class DLL_PUBLIC Buffer : public virtual IntrusivePtrBase { fmt % param; return appendbf(fmt, params...); } - Buffer & appendbf(boost::format & fmt); Buffer & clear(); size_t length() const; @@ -64,24 +66,56 @@ class DLL_PUBLIC Buffer : public virtual IntrusivePtrBase { static boost::shared_ptr getFormat(const std::string & msgfmt); private: + Buffer & appendbf(boost::format & fmt); void DLL_PRIVATE flatten() const; - class DLL_PRIVATE Fragment : public virtual IntrusivePtrBase { + class DLL_PRIVATE FragmentBase : public virtual IntrusivePtrBase { public: - typedef boost::intrusive_ptr Ptr; - typedef boost::intrusive_ptr CPtr; + virtual ~FragmentBase() = 0; - Fragment(const char *, size_t); - Fragment(const char *); - Fragment(char *, size_t, bool); - ~Fragment(); + virtual size_t length() const = 0; + virtual char operator[](size_t) const = 0; + virtual const char * c_str() const = 0; + virtual std::string str() const = 0; + }; - size_t len; // Excluding NULL term - char * buf; + class DLL_PRIVATE CStringFragment : public FragmentBase { + public: + CStringFragment(const char *, CStringHandling); + CStringFragment(const char *, CStringHandling, size_t); + CStringFragment(char *, CStringHandling); + CStringFragment(char *, CStringHandling, size_t); + ~CStringFragment(); + + size_t length() const; + char operator[](size_t) const; + const char * c_str() const; + std::string str() const; + + private: + const size_t len; // Excluding NULL term + const char * buf; + const CStringHandling handling; }; - typedef std::vector Content; + + class DLL_PRIVATE StringFragment : public FragmentBase { + public: + StringFragment(const std::string &); + + size_t length() const; + char operator[](size_t) const; + const char * c_str() const; + std::string str() const; + + private: + const std::string buf; + }; + + typedef boost::intrusive_ptr FragmentPtr; + typedef std::vector Content; mutable Content content; }; +Buffer::FragmentBase::~FragmentBase() = default; #endif diff --git a/libadhocutil/unittests/testBuffer.cpp b/libadhocutil/unittests/testBuffer.cpp index 5d64642..9c1c956 100644 --- a/libadhocutil/unittests/testBuffer.cpp +++ b/libadhocutil/unittests/testBuffer.cpp @@ -5,32 +5,32 @@ BOOST_AUTO_TEST_CASE ( create ) { - Buffer a; - Buffer b("const"); + Buffer empty; + Buffer copy("const", Buffer::Copy); auto nonconst = (char*)malloc(9); strcpy(nonconst, "nonconst"); - Buffer c(nonconst, true); - Buffer d(nonconst, false); + Buffer use(nonconst, Buffer::Use); + Buffer fre(nonconst, Buffer::Free); - BOOST_REQUIRE_EQUAL(false, a); - BOOST_REQUIRE_EQUAL(true, !a); - BOOST_REQUIRE_EQUAL(0, a.length()); - BOOST_REQUIRE_EQUAL("", a.str()); + BOOST_REQUIRE_EQUAL(false, empty); + BOOST_REQUIRE_EQUAL(true, !empty); + BOOST_REQUIRE_EQUAL(0, empty.length()); + BOOST_REQUIRE_EQUAL("", empty.str()); - BOOST_REQUIRE_EQUAL(true, b); - BOOST_REQUIRE_EQUAL(false, !b); - BOOST_REQUIRE_EQUAL(5, b.length()); - BOOST_REQUIRE_EQUAL("const", b.str()); + BOOST_REQUIRE_EQUAL(true, copy); + BOOST_REQUIRE_EQUAL(false, !copy); + BOOST_REQUIRE_EQUAL(5, copy.length()); + BOOST_REQUIRE_EQUAL("const", copy.str()); - BOOST_REQUIRE_EQUAL(8, c.length()); - BOOST_REQUIRE_EQUAL("nonconst", c.str()); + BOOST_REQUIRE_EQUAL(8, use.length()); + BOOST_REQUIRE_EQUAL("nonconst", use.str()); - BOOST_REQUIRE_EQUAL(8, d.length()); - BOOST_REQUIRE_EQUAL("nonconst", d.str()); + BOOST_REQUIRE_EQUAL(8, fre.length()); + BOOST_REQUIRE_EQUAL("nonconst", fre.str()); nonconst[0] = 'N'; - BOOST_REQUIRE_EQUAL("nonconst", c.str()); - BOOST_REQUIRE_EQUAL("Nonconst", d.str()); + BOOST_REQUIRE_EQUAL("Nonconst", use.str()); + BOOST_REQUIRE_EQUAL("Nonconst", fre.str()); } BOOST_AUTO_TEST_CASE( writestream ) -- cgit v1.2.3