diff options
| author | Dan Goodliffe <dan@randomdan.homeip.net> | 2015-08-26 02:22:12 +0100 | 
|---|---|---|
| committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2015-08-28 00:37:56 +0100 | 
| commit | dcb872dcbc6e69d5aa31eac026bd4d373a4f5351 (patch) | |
| tree | ac43598013724dac9329743ba9edf509de7340be | |
| parent | Add CurlStreamSource for reading a cURL request as a std::istream (diff) | |
| download | libadhocutil-dcb872dcbc6e69d5aa31eac026bd4d373a4f5351.tar.bz2 libadhocutil-dcb872dcbc6e69d5aa31eac026bd4d373a4f5351.tar.xz libadhocutil-dcb872dcbc6e69d5aa31eac026bd4d373a4f5351.zip | |
Add buffer, intrusivePtrBase (dep) and covering tests
| -rw-r--r-- | libadhocutil/buffer.cpp | 258 | ||||
| -rw-r--r-- | libadhocutil/buffer.h | 87 | ||||
| -rw-r--r-- | libadhocutil/intrusivePtrBase.h | 42 | ||||
| -rw-r--r-- | libadhocutil/unittests/Jamfile.jam | 10 | ||||
| -rw-r--r-- | libadhocutil/unittests/testBuffer.cpp | 119 | 
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); +} + | 
