summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libadhocutil/buffer.cpp258
-rw-r--r--libadhocutil/buffer.h87
-rw-r--r--libadhocutil/intrusivePtrBase.h42
-rw-r--r--libadhocutil/unittests/Jamfile.jam10
-rw-r--r--libadhocutil/unittests/testBuffer.cpp119
5 files changed, 516 insertions, 0 deletions
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 <string.h>
+#include <stdio.h>
+
+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<boost::format>
+Buffer::getFormat(const std::string & msgfmt)
+{
+ return boost::shared_ptr<boost::format>(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 <string>
+#include <vector>
+#include <stdarg.h>
+#include <boost/format.hpp>
+#include <boost/shared_ptr.hpp>
+#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<Buffer> Ptr;
+ typedef boost::intrusive_ptr<const Buffer> 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 <typename ... Params>
+ Buffer & appendbf(const std::string & fmtstr, const Params & ... params)
+ {
+ return appendbf(*getFormat(fmtstr), params...);
+ }
+ template <typename Param, typename ... Params>
+ 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<boost::format> getFormat(const std::string & msgfmt);
+
+ private:
+ void DLL_PRIVATE flatten() const;
+
+ class DLL_PRIVATE Fragment : public virtual IntrusivePtrBase {
+ public:
+ typedef boost::intrusive_ptr<Fragment> Ptr;
+ typedef boost::intrusive_ptr<const Fragment> 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<Fragment::Ptr> 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 <boost/intrusive_ptr.hpp>
+
+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
+ : : :
+ <define>BOOST_TEST_DYN_LINK
+ <library>..//adhocutil
+ <library>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 <boost/test/unit_test.hpp>
+
+#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);
+}
+