From dcb872dcbc6e69d5aa31eac026bd4d373a4f5351 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 26 Aug 2015 02:22:12 +0100 Subject: Add buffer, intrusivePtrBase (dep) and covering tests --- libadhocutil/buffer.cpp | 258 ++++++++++++++++++++++++++++++++++ libadhocutil/buffer.h | 87 ++++++++++++ libadhocutil/intrusivePtrBase.h | 42 ++++++ libadhocutil/unittests/Jamfile.jam | 10 ++ libadhocutil/unittests/testBuffer.cpp | 119 ++++++++++++++++ 5 files changed, 516 insertions(+) create mode 100644 libadhocutil/buffer.cpp create mode 100644 libadhocutil/buffer.h create mode 100644 libadhocutil/intrusivePtrBase.h create mode 100644 libadhocutil/unittests/testBuffer.cpp diff --git a/libadhocutil/buffer.cpp b/libadhocutil/buffer.cpp new file mode 100644 index 0000000..daf5be6 --- /dev/null +++ b/libadhocutil/buffer.cpp @@ -0,0 +1,258 @@ +#include "buffer.h" +#include +#include + +Buffer::Fragment::Fragment(const char * b) : + len(strlen(b)) +{ + buf = (char*)malloc(len + 1); + memcpy(buf, b, len + 1); + *(buf + len) = '\0'; +} + +Buffer::Fragment::Fragment(const char * b, size_t l) : + len(l) +{ + 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) +{ + if (copy) { + buf = (char*)malloc(l + 1); + memcpy(buf, b, l + 1); + } + else { + buf = b; + } + *(buf + len) = '\0'; +} + +Buffer::Fragment::~Fragment() +{ + free(buf); +} + +Buffer::Buffer() +{ +} + +Buffer::Buffer(const char * src) +{ + content.push_back(new Fragment(src)); +} + +Buffer::Buffer(char * src, bool copy) +{ + content.push_back(new Fragment(src, strlen(src), copy)); +} + +Buffer::~Buffer() +{ +} + +Buffer & +Buffer::append(const char * str) +{ + if (str && *str) { + content.push_back(new Fragment(str)); + } + return *this; +} + +Buffer & +Buffer::append(char * str, bool copy) +{ + if (str && *str) { + content.push_back(new Fragment(str, strlen(str), copy)); + } + return *this; +} + +Buffer & +Buffer::append(const std::string & str) +{ + if (!str.empty()) { + content.push_back(new Fragment(str.c_str(), str.length())); + } + return *this; +} + +Buffer & +Buffer::appendf(const char * fmt, ...) +{ + va_list v; + va_start(v, fmt); + vappendf(fmt, v); + va_end(v); + return *this; +} + +Buffer & +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)); + } + else { + free(frag); + } + return *this; +} + +Buffer & +Buffer::appendbf(boost::format & fmt) +{ + append(fmt.str()); + return *this; +} + +Buffer & +Buffer::clear() +{ + content.clear(); + return *this; +} + +boost::shared_ptr +Buffer::getFormat(const std::string & msgfmt) +{ + return boost::shared_ptr(new boost::format(msgfmt)); +} + +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; + ++f; + } + while (f != content.end() && bufSize) { + for (size_t c = 0; bufSize && c < (*f)->len; bufSize--) { + *buf++ = (*f)->buf[c++]; + } + ++f; + off = 0; + } + *buf = '\0'; +} + +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); + } + return res; + } + else if (content.size() == 1) { + return std::string(content.front()->buf, content.front()->len); + } + return std::string(); +} + +Buffer::operator const char *() const +{ + if (content.empty()) { + return ""; + } + flatten(); + return content.front()->buf; +} + +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); + } +} + +std::string +Buffer::str() const +{ + return *this; +} + +size_t +Buffer::length() const +{ + size_t len = 0; + for (const Content::value_type & c : content) { + len += c->len; + } + return len; +} + +Buffer & +Buffer::operator=(const char * str) +{ + content.resize(1); + content[0] = new Fragment(str); + return *this; +} + +Buffer & +Buffer::operator=(const std::string & str) +{ + content.resize(1); + content[0] = new Fragment(str.c_str(), str.length()); + return *this; +} + +Buffer & +Buffer::operator=(const Buffer & buf) +{ + content = buf.content; + return *this; +} + +Buffer::operator bool() const +{ + return !content.empty(); +} + +bool +Buffer::operator!() const +{ + return content.empty(); +} + +std::ostream & +std::operator<<(std::ostream & os, const Buffer & b) +{ + for (const auto & f : b.content) { + os.write(f->buf, f->len); + } + return os; +} + +Buffer & +Buffer::operator+=(const char * str) +{ + return append(str); +} + +Buffer & +Buffer::operator+=(const std::string & str) +{ + return append(str); +} + diff --git a/libadhocutil/buffer.h b/libadhocutil/buffer.h new file mode 100644 index 0000000..c435574 --- /dev/null +++ b/libadhocutil/buffer.h @@ -0,0 +1,87 @@ +#ifndef ADHOC_BUFFER_H +#define ADHOC_BUFFER_H + +#include "intrusivePtrBase.h" +#include +#include +#include +#include +#include +#include "visibility.h" + +class DLL_PUBLIC Buffer; + +namespace std { + DLL_PUBLIC std::ostream & operator<<(std::ostream &, const Buffer &); +} + +class DLL_PUBLIC Buffer : public virtual IntrusivePtrBase { + public: + typedef boost::intrusive_ptr Ptr; + typedef boost::intrusive_ptr CPtr; + + Buffer(); + Buffer(const char * src); + Buffer(char * src, bool copy); + ~Buffer(); + + Buffer & operator+=(const char * str); + Buffer & operator+=(const std::string & str); + Buffer & operator=(const char * str); + Buffer & operator=(const std::string & str); + Buffer & operator=(const Buffer & str); + bool operator!() const; + + operator bool() const; + operator std::string() const; + operator const char *() const; + + 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 std::string & str); + Buffer & appendf(const char * fmt, ...) __attribute__((format (printf, 2, 3))); + Buffer & vappendf(const char * fmt, va_list args); + template + Buffer & appendbf(const std::string & fmtstr, const Params & ... params) + { + return appendbf(*getFormat(fmtstr), params...); + } + template + Buffer & appendbf(boost::format & fmt, const Param & param, const Params & ... params) + { + fmt % param; + return appendbf(fmt, params...); + } + Buffer & appendbf(boost::format & fmt); + Buffer & clear(); + + size_t length() const; + std::string str() const; + + static boost::shared_ptr getFormat(const std::string & msgfmt); + + private: + void DLL_PRIVATE flatten() const; + + class DLL_PRIVATE Fragment : public virtual IntrusivePtrBase { + public: + typedef boost::intrusive_ptr Ptr; + typedef boost::intrusive_ptr CPtr; + + Fragment(const char *, size_t); + Fragment(const char *); + Fragment(char *, size_t, bool); + ~Fragment(); + + size_t len; // Excluding NULL term + char * buf; + }; + typedef std::vector Content; + mutable Content content; +}; + +#endif + diff --git a/libadhocutil/intrusivePtrBase.h b/libadhocutil/intrusivePtrBase.h new file mode 100644 index 0000000..e10e2f1 --- /dev/null +++ b/libadhocutil/intrusivePtrBase.h @@ -0,0 +1,42 @@ +#ifndef ADHOC_INTRUSIVEPTRBASE_H +#define ADHOC_INTRUSIVEPTRBASE_H + +#include + +class IntrusivePtrBase { + protected: + inline IntrusivePtrBase() : _refCount(0) { } + inline virtual ~IntrusivePtrBase() = 0; + + mutable unsigned int _refCount; + friend void intrusive_ptr_release(const IntrusivePtrBase * p); + friend void intrusive_ptr_add_ref(const IntrusivePtrBase * p); + private: + IntrusivePtrBase(const IntrusivePtrBase &) = delete; + void operator=(const IntrusivePtrBase &) = delete; +}; + +inline +IntrusivePtrBase::~IntrusivePtrBase() = default; + +inline +void +intrusive_ptr_release(const IntrusivePtrBase * p) +{ + if (p->_refCount == 1) { + delete p; + } + else { + p->_refCount -= 1; + } +} + +inline +void +intrusive_ptr_add_ref(const IntrusivePtrBase * p) +{ + p->_refCount += 1; +} + +#endif + diff --git a/libadhocutil/unittests/Jamfile.jam b/libadhocutil/unittests/Jamfile.jam index 33ff61b..8332544 100644 --- a/libadhocutil/unittests/Jamfile.jam +++ b/libadhocutil/unittests/Jamfile.jam @@ -30,3 +30,13 @@ run testCurlStream ; +run + testBuffer.cpp + : : : + BOOST_TEST_DYN_LINK + ..//adhocutil + boost_utf + : + testBuffer + ; + diff --git a/libadhocutil/unittests/testBuffer.cpp b/libadhocutil/unittests/testBuffer.cpp new file mode 100644 index 0000000..5d64642 --- /dev/null +++ b/libadhocutil/unittests/testBuffer.cpp @@ -0,0 +1,119 @@ +#define BOOST_TEST_MODULE Buffer +#include + +#include "buffer.h" + +BOOST_AUTO_TEST_CASE ( create ) +{ + Buffer a; + Buffer b("const"); + auto nonconst = (char*)malloc(9); + strcpy(nonconst, "nonconst"); + Buffer c(nonconst, true); + Buffer d(nonconst, false); + + BOOST_REQUIRE_EQUAL(false, a); + BOOST_REQUIRE_EQUAL(true, !a); + BOOST_REQUIRE_EQUAL(0, a.length()); + BOOST_REQUIRE_EQUAL("", a.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(8, c.length()); + BOOST_REQUIRE_EQUAL("nonconst", c.str()); + + BOOST_REQUIRE_EQUAL(8, d.length()); + BOOST_REQUIRE_EQUAL("nonconst", d.str()); + + nonconst[0] = 'N'; + BOOST_REQUIRE_EQUAL("nonconst", c.str()); + BOOST_REQUIRE_EQUAL("Nonconst", d.str()); +} + +BOOST_AUTO_TEST_CASE( writestream ) +{ + std::stringstream buf; + Buffer b; + b.append("Some text.").append("And then ").append("some more."); + buf << b; + BOOST_REQUIRE_EQUAL("Some text.And then some more.", buf.str()); +} + +BOOST_AUTO_TEST_CASE( appendempty ) +{ + Buffer b; + // These should not add content + b.append(""); + BOOST_REQUIRE(!b); + b.append(std::string()); + BOOST_REQUIRE(!b); + b.appendf("%s", ""); + BOOST_REQUIRE(!b); + b.appendbf("%s", ""); + BOOST_REQUIRE(!b); +} + +BOOST_AUTO_TEST_CASE( appendthings ) +{ + Buffer b; + b.append("string a") + .append(std::string(" b")) + .appendf(" num %d", 1) + .appendbf(" num %d", 2); + BOOST_REQUIRE_EQUAL(22, b.length()); + BOOST_REQUIRE_EQUAL("string a b num 1 num 2", b.str()); + const char * cstring = b; + BOOST_REQUIRE_EQUAL(22, strlen(cstring)); + BOOST_REQUIRE_EQUAL("string a b num 1 num 2", cstring); +} + +BOOST_AUTO_TEST_CASE( writeto ) +{ + Buffer b; + b.append("string a") + .append(std::string(" b")) + .appendf(" num %d", 1) + .appendbf(" num %d", 2); + char buf[23]; + b.writeto(buf, 23, 0); + BOOST_REQUIRE_EQUAL(0, memcmp(buf, "string a b num 1 num 2", 23)); +} + +BOOST_AUTO_TEST_CASE( operators ) +{ + auto expected = "cstringstd::string"; + Buffer a; + Buffer b; + a += "cstring"; + a += std::string("std::string"); + BOOST_REQUIRE_EQUAL(expected, a.str()); + b = a; + BOOST_REQUIRE_EQUAL(expected, a.str()); + BOOST_REQUIRE_EQUAL(expected, b.str()); + + Buffer c; + c = expected; + BOOST_REQUIRE_EQUAL(expected, c.str()); + + Buffer d; + d = std::string(expected); + BOOST_REQUIRE_EQUAL(expected, d.str()); +} + +BOOST_AUTO_TEST_CASE( clear ) +{ + Buffer b("some"); + BOOST_REQUIRE(b); + b.clear(); + BOOST_REQUIRE(!b); +} + +BOOST_AUTO_TEST_CASE( replacesstringbf ) +{ + auto str = Buffer().appendbf("something %d", 1234).str(); + BOOST_REQUIRE_EQUAL("something 1234", str); +} + -- cgit v1.2.3