diff options
| author | Monty Brandenberg <monty@lindenlab.com> | 2012-06-21 19:45:40 -0400 | 
|---|---|---|
| committer | Monty Brandenberg <monty@lindenlab.com> | 2012-06-21 19:45:40 -0400 | 
| commit | 4da93b6ad91dff1de98c3c8dd3674c0544f2958b (patch) | |
| tree | b3fc5f6b24ed52333de31296746724e6a3278088 | |
| parent | a066bc1994fccae7967921980332505aac97953f (diff) | |
SH-3177 Add streambuf/iostream adapters to BufferArray object.
Initial version that should have enough of the plumbing to produce
a working adapter.  Memory test is showing 8 bytes held after one
of the tests so I'm going to revisit that later.  But basic
functionality is there going by the unit tests.
| -rw-r--r-- | indra/llcorehttp/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | indra/llcorehttp/bufferarray.cpp | 14 | ||||
| -rw-r--r-- | indra/llcorehttp/bufferarray.h | 8 | ||||
| -rw-r--r-- | indra/llcorehttp/bufferstream.cpp | 285 | ||||
| -rw-r--r-- | indra/llcorehttp/bufferstream.h | 94 | ||||
| -rw-r--r-- | indra/llcorehttp/tests/llcorehttp_test.cpp | 3 | ||||
| -rw-r--r-- | indra/llcorehttp/tests/test_bufferarray.hpp | 2 | ||||
| -rw-r--r-- | indra/llcorehttp/tests/test_bufferstream.hpp | 252 | 
8 files changed, 659 insertions, 2 deletions
| diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index acf4c0d6a3..4c00cc04e9 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -24,6 +24,7 @@ include_directories(  set(llcorehttp_SOURCE_FILES      bufferarray.cpp +    bufferstream.cpp      httpcommon.cpp      httpheaders.cpp      httpoptions.cpp @@ -47,6 +48,7 @@ set(llcorehttp_HEADER_FILES      CMakeLists.txt      bufferarray.h +    bufferstream.h      httpcommon.h      httphandler.h      httpheaders.h @@ -105,6 +107,7 @@ if (LL_TESTS)        tests/test_httprequestqueue.hpp        tests/test_httpheaders.hpp        tests/test_bufferarray.hpp +      tests/test_bufferstream.hpp        )    set_source_files_properties(${llcorehttp_TEST_HEADER_FILES} diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 5eaa60c9ba..5eb8f84c34 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -288,6 +288,20 @@ int BufferArray::findBlock(size_t pos, size_t * ret_offset)  } +bool BufferArray::getBlockStartEnd(int block, const char ** start, const char ** end) +{ +	if (block < 0 || block >= mBlocks.size()) +	{ +		return false; +	} + +	const Block & b(*mBlocks[block]); +	*start = &b.mData[0]; +	*end = &b.mData[b.mUsed]; +	return true; +} + +  // ==================================  // BufferArray::Block Definitions  // ================================== diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index d0c51d3c73..1094a435b4 100644 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -37,6 +37,7 @@  namespace LLCore  { +class BufferArrayStreamBuf;  /// A very simple scatter/gather type map for bulk data.  The motivation  /// for this class is the writedata callback used by libcurl.  Response @@ -65,6 +66,11 @@ namespace LLCore  class BufferArray : public LLCoreInt::RefCounted  {  public: +	// BufferArrayStreamBuf has intimate knowledge of this +	// implementation to implement a buffer-free adapter. +	// Changes here will likely need to be reflected there. +	friend class BufferArrayStreamBuf; +	  	BufferArray();  protected: @@ -114,6 +120,8 @@ public:  protected:  	int findBlock(size_t pos, size_t * ret_offset); + +	bool getBlockStartEnd(int block, const char ** start, const char ** end);  protected:  	class Block; diff --git a/indra/llcorehttp/bufferstream.cpp b/indra/llcorehttp/bufferstream.cpp new file mode 100644 index 0000000000..6553900eef --- /dev/null +++ b/indra/llcorehttp/bufferstream.cpp @@ -0,0 +1,285 @@ +/** + * @file bufferstream.cpp + * @brief Implements the BufferStream adapter class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "bufferstream.h" + +#include "bufferarray.h" + + +namespace LLCore +{ + +BufferArrayStreamBuf::BufferArrayStreamBuf(BufferArray * array) +	: mBufferArray(array), +	  mReadCurPos(0), +	  mReadCurBlock(-1), +	  mReadBegin(NULL), +	  mReadCur(NULL), +	  mReadEnd(NULL), +	  mWriteCurPos(0) +{ +	if (array) +	{ +		array->addRef(); +		mWriteCurPos = array->mLen; +	} +} + + +BufferArrayStreamBuf::~BufferArrayStreamBuf() +{ +	if (mBufferArray) +	{ +		mBufferArray->release(); +		mBufferArray = NULL; +	} +} + +	 +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::underflow() +{ +	if (! mBufferArray) +	{ +		return traits_type::eof(); +	} + +	if (mReadCur == mReadEnd) +	{ +		// Find the next block with actual data or leave +		// mCurBlock/mCur/mEnd unchanged if we're at the end +		// of any block chain. +		const char * new_begin(NULL), * new_end(NULL); +		int new_cur_block(mReadCurBlock + 1); + +		while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end)) +		{ +			if (new_begin != new_end) +			{ +				break; +			} +			++new_cur_block; +		} +		if (new_begin == new_end) +		{ +			return traits_type::eof(); +		} + +		mReadCurBlock = new_cur_block; +		mReadBegin = mReadCur = new_begin; +		mReadEnd = new_end; +	} + +	return traits_type::to_int_type(*mReadCur); +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::uflow() +{ +	const int_type ret(underflow()); + +	if (traits_type::eof() != ret) +	{ +		++mReadCur; +		++mReadCurPos; +	} +	return ret; +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::pbackfail(int_type ch) +{ +	if (! mBufferArray) +	{ +		return traits_type::eof(); +	} + +	if (mReadCur == mReadBegin) +	{ +		// Find the previous block with actual data or leave +		// mCurBlock/mBegin/mCur/mEnd unchanged if we're at the +		// beginning of any block chain. +		const char * new_begin(NULL), * new_end(NULL); +		int new_cur_block(mReadCurBlock - 1); + +		while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end)) +		{ +			if (new_begin != new_end) +			{ +				break; +			} +			--new_cur_block; +		} +		if (new_begin == new_end) +		{ +			return traits_type::eof(); +		} + +		mReadCurBlock = new_cur_block; +		mReadBegin = new_begin; +		mReadEnd = mReadCur = new_end; +	} + +	if (traits_type::eof() != ch && mReadCur[-1] != ch) +	{ +		return traits_type::eof(); +	} +	--mReadCurPos; +	return traits_type::to_int_type(*--mReadCur); +} + + +std::streamsize BufferArrayStreamBuf::showmanyc() +{ +	if (! mBufferArray) +	{ +		return -1; +	} +	return mBufferArray->mLen - mReadCurPos; +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::overflow(int c) +{ +	if (! mBufferArray || mWriteCurPos > mBufferArray->mLen) +	{ +		return traits_type::eof(); +	} +	const size_t wrote(mBufferArray->write(mWriteCurPos, &c, 1)); +	mWriteCurPos += wrote; +	return wrote ? c : traits_type::eof(); +} + + +std::streamsize BufferArrayStreamBuf::xsputn(const char * src, std::streamsize count) +{ +	if (! mBufferArray || mWriteCurPos > mBufferArray->mLen) +	{ +		return 0; +	} +	const size_t wrote(mBufferArray->write(mWriteCurPos, src, count)); +	mWriteCurPos += wrote; +	return wrote; +} + + +std::streampos BufferArrayStreamBuf::seekoff(std::streamoff off, +											 std::ios_base::seekdir way, +											 std::ios_base::openmode which) +{ +	std::streampos ret(-1); + +	if (! mBufferArray) +	{ +		return ret; +	} +	 +	if (std::ios_base::in == which) +	{ +		size_t pos(0); + +		switch (way) +		{ +		case std::ios_base::beg: +			pos = off; +			break; +			 +		case std::ios_base::cur: +			pos = mReadCurPos += off; +			break; +			 +		case std::ios_base::end: +			pos = mBufferArray->mLen - off; +			break; + +		default: +			return ret; +		} + +		if (pos >= mBufferArray->size()) +		{ +			pos = (std::max)(size_t(0), mBufferArray->size() - 1); +		} +		size_t ba_offset(0); +		int block(mBufferArray->findBlock(pos, &ba_offset)); +		if (block < 0) +			return ret; +		const char * start(NULL), * end(NULL); +		if (! mBufferArray->getBlockStartEnd(block, &start, &end)) +			return ret; +		mReadCurBlock = block; +		mReadBegin = start; +		mReadCur = start + ba_offset; +		mReadEnd = end; +		ret = mReadCurPos = pos; +	} +	else if (std::ios_base::out == which) +	{ +		size_t pos(0); + +		switch (way) +		{ +		case std::ios_base::beg: +			pos = off; +			break; +			 +		case std::ios_base::cur: +			pos = mWriteCurPos += off; +			break; +			 +		case std::ios_base::end: +			pos = mBufferArray->mLen - off; +			break; + +		default: +			return ret; +		} + +		if (pos < 0) +			return ret; +		if (pos > mBufferArray->size()) +		{ +			pos = mBufferArray->size(); +		} +		ret = mWriteCurPos = pos; +	} + +	return ret; +} + + +BufferArrayStream::BufferArrayStream(BufferArray * ba) +	: std::iostream(&mStreamBuf), +	  mStreamBuf(ba) +{} +	 +			 +BufferArrayStream::~BufferArrayStream() +{} +	 + +}  // end namespace LLCore + + diff --git a/indra/llcorehttp/bufferstream.h b/indra/llcorehttp/bufferstream.h new file mode 100644 index 0000000000..60bda9ff9a --- /dev/null +++ b/indra/llcorehttp/bufferstream.h @@ -0,0 +1,94 @@ +/** + * @file bufferstream.h + * @brief Public-facing declaration for the BufferStream adapter class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_BUFFER_STREAM_H_ +#define	_LLCORE_BUFFER_STREAM_H_ + + +#include <sstream> +#include <cstdlib> + +#include "bufferarray.h" + + +namespace LLCore +{ + + +class BufferArrayStreamBuf : public std::streambuf +{ +public: +	BufferArrayStreamBuf(BufferArray * array); +	virtual ~BufferArrayStreamBuf(); + +private: +	BufferArrayStreamBuf(const BufferArrayStreamBuf &);	// Not defined +	void operator=(const BufferArrayStreamBuf &);		// Not defined + +public: +	// Input interfaces from std::streambuf +	int_type underflow(); +	int_type uflow(); +	int_type pbackfail(int_type ch); +	std::streamsize showmanyc(); + +	// Output interfaces from std::streambuf +	int_type overflow(int c); +	std::streamsize xsputn(const char * src, std::streamsize count); + +	// Common/misc interfaces from std::streambuf +	std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which); +	 +protected: +	BufferArray *		mBufferArray;			// Ref counted +	size_t				mReadCurPos; +	int					mReadCurBlock; +	const char *		mReadBegin; +	const char *		mReadCur; +	const char *		mReadEnd; +	size_t				mWriteCurPos; +	 +}; // end class BufferArrayStreamBuf + + +class BufferArrayStream : public std::iostream +{ +public: +	BufferArrayStream(BufferArray * ba); +	~BufferArrayStream(); + +protected: +	BufferArrayStream(const BufferArrayStream &); +	void operator=(const BufferArrayStream &); + +protected: +	BufferArrayStreamBuf		mStreamBuf; +}; // end class BufferArrayStream + + +}  // end namespace LLCore + +#endif	// _LLCORE_BUFFER_STREAM_H_ diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index 2b36d3a982..e863ddd13f 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -36,12 +36,13 @@  #include "../test/lltut.h"  // Pull in each of the test sets +#include "test_bufferarray.hpp" +#include "test_bufferstream.hpp"  #include "test_httpstatus.hpp"  #include "test_refcounted.hpp"  #include "test_httpoperation.hpp"  #include "test_httprequest.hpp"  #include "test_httpheaders.hpp" -#include "test_bufferarray.hpp"  #include "test_httprequestqueue.hpp"  #include "llproxy.h" diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp index 3f947db967..8a2a64d970 100644 --- a/indra/llcorehttp/tests/test_bufferarray.hpp +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -33,7 +33,7 @@  #include "test_allocator.h" -using namespace LLCoreInt; +using namespace LLCore; diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp new file mode 100644 index 0000000000..5afaba0966 --- /dev/null +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -0,0 +1,252 @@ +/**  + * @file test_bufferstream.hpp + * @brief unit tests for the LLCore::BufferArrayStreamBuf/BufferArrayStream classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_BUFFER_STREAM_H_ +#define TEST_LLCORE_BUFFER_STREAM_H_ + +#include "bufferstream.h" + +#include <iostream> + +#include "test_allocator.h" + + +using namespace LLCore; + + +namespace tut +{ + +struct BufferStreamTestData +{ +	// the test objects inherit from this so the member functions and variables +	// can be referenced directly inside of the test functions. +	size_t mMemTotal; +}; + +typedef test_group<BufferStreamTestData> BufferStreamTestGroupType; +typedef BufferStreamTestGroupType::object BufferStreamTestObjectType; +BufferStreamTestGroupType BufferStreamTestGroup("BufferStream Tests"); +typedef BufferArrayStreamBuf::traits_type tst_traits_t; + + +template <> template <> +void BufferStreamTestObjectType::test<1>() +{ +	set_test_name("BufferArrayStreamBuf construction with NULL BufferArray"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(NULL); +	ensure("Memory being used", mMemTotal < GetMemTotal()); + +	// Not much will work with a NULL +	ensure("underflow() on NULL fails", tst_traits_t::eof() == bsb->underflow()); +	ensure("uflow() on NULL fails", tst_traits_t::eof() == bsb->uflow()); +	ensure("pbackfail() on NULL fails", tst_traits_t::eof() == bsb->pbackfail('c')); +	ensure("showmanyc() on NULL fails", bsb->showmanyc() == -1); +	ensure("overflow() on NULL fails", tst_traits_t::eof() == bsb->overflow('c')); +	ensure("xsputn() on NULL fails", bsb->xsputn("blah", 4) == 0); +	ensure("seekoff() on NULL fails", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(-1)); +	 +	// release the implicit reference, causing the object to be released +	delete bsb; +	bsb = NULL; + +	// make sure we didn't leak any memory +	ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<2>() +{ +	set_test_name("BufferArrayStream construction with NULL BufferArray"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArrayStream * bas = new BufferArrayStream(NULL); +	ensure("Memory being used", mMemTotal < GetMemTotal()); + +	// Not much will work with a NULL here +	ensure("eof() is false on NULL", ! bas->eof()); +	ensure("fail() is false on NULL", ! bas->fail()); +	ensure("good() on NULL", bas->good()); +		    +	// release the implicit reference, causing the object to be released +	delete bas; +	bas = NULL; + +	// make sure we didn't leak any memory +	ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<3>() +{ +	set_test_name("BufferArrayStreamBuf construction with empty BufferArray"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted BufferArray with implicit reference +	BufferArray * ba = new BufferArray; +	BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); +	ensure("Memory being used", mMemTotal < GetMemTotal()); + +	// I can release my ref on the BA +	ba->release(); +	ba = NULL; +	 +	// release the implicit reference, causing the object to be released +	delete bsb; +	bsb = NULL; + +	// make sure we didn't leak any memory +	ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<4>() +{ +	set_test_name("BufferArrayStream construction with empty BufferArray"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted BufferArray with implicit reference +	BufferArray * ba = new BufferArray; + +	{ +		// create a new ref counted object with an implicit reference +		BufferArrayStream bas(ba); +		ensure("Memory being used", mMemTotal < GetMemTotal()); +	} + +	// release the implicit reference, causing the object to be released +	ba->release(); +	ba = NULL; +	 +	// make sure we didn't leak any memory +	ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<5>() +{ +	set_test_name("BufferArrayStreamBuf construction with real BufferArray"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted BufferArray with implicit reference +	BufferArray * ba = new BufferArray; +	const char * content("This is a string.  A fragment."); +	const size_t c_len(strlen(content)); +	ba->append(content, c_len); +	BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); +	ensure("Memory being used", mMemTotal < GetMemTotal()); + +	// I can release my ref on the BA +	ba->release(); +	ba = NULL; +	 +	// Various static state +	ensure("underflow() returns 'T'", bsb->underflow() == 'T'); +	ensure("underflow() returns 'T' again", bsb->underflow() == 'T'); +	ensure("uflow() returns 'T'", bsb->uflow() == 'T'); +	ensure("uflow() returns 'h'", bsb->uflow() == 'h'); +	ensure("pbackfail('i') fails", tst_traits_t::eof() == bsb->pbackfail('i')); +	ensure("pbackfail('T') fails", tst_traits_t::eof() == bsb->pbackfail('T')); +	ensure("pbackfail('h') succeeds", bsb->pbackfail('h') == 'h'); +	ensure("showmanyc() is everything but the 'T'", bsb->showmanyc() == (c_len - 1)); +	ensure("overflow() appends", bsb->overflow('c') == 'c'); +	ensure("showmanyc() reflects append", bsb->showmanyc() == (c_len - 1 + 1)); +	ensure("xsputn() appends some more", bsb->xsputn("bla!", 4) == 4); +	ensure("showmanyc() reflects 2nd append", bsb->showmanyc() == (c_len - 1 + 5)); +	ensure("seekoff() succeeds", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(0)); +	ensure("seekoff() succeeds 2", bsb->seekoff(4, std::ios_base::cur, std::ios_base::in) == std::streampos(4)); +	ensure("showmanyc() picks up seekoff", bsb->showmanyc() == (c_len + 5 - 4)); +	ensure("seekoff() succeeds 3", bsb->seekoff(0, std::ios_base::end, std::ios_base::in) == std::streampos(c_len + 4)); +	ensure("pbackfail('!') succeeds", tst_traits_t::eof() == bsb->pbackfail('!')); +	 +	// release the implicit reference, causing the object to be released +	delete bsb; +	bsb = NULL; + +	// make sure we didn't leak any memory +	ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<6>() +{ +	set_test_name("BufferArrayStream construction with real BufferArray"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted BufferArray with implicit reference +	BufferArray * ba = new BufferArray; +	//const char * content("This is a string.  A fragment."); +	//const size_t c_len(strlen(content)); +	//ba->append(content, strlen(content)); + +	{ +		// create a new ref counted object with an implicit reference +		BufferArrayStream bas(ba); +		ensure("Memory being used", mMemTotal < GetMemTotal()); + +		// Basic operations +		bas << "Hello" << 27 << "."; +		ensure("BA length 8", ba->size() == 8); + +		std::string str; +		bas >> str; +		ensure("reads correctly", str == "Hello27."); +	} + +	// release the implicit reference, causing the object to be released +	ba->release(); +	ba = NULL; + +	// make sure we didn't leak any memory +	// ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +	static U64 mem = GetMemTotal(); +} + + +}  // end namespace tut + + +#endif  // TEST_LLCORE_BUFFER_STREAM_H_ | 
