From 4da93b6ad91dff1de98c3c8dd3674c0544f2958b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 21 Jun 2012 19:45:40 -0400 Subject: 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. --- indra/llcorehttp/tests/test_bufferstream.hpp | 252 +++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 indra/llcorehttp/tests/test_bufferstream.hpp (limited to 'indra/llcorehttp/tests/test_bufferstream.hpp') 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 + +#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 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_ -- cgit v1.3 From eed28348f2668c93bc572cffd8a284e65228ed02 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 21 Jun 2012 20:02:24 -0400 Subject: Compiler warning fix on linux. --- indra/llcorehttp/tests/test_bufferstream.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcorehttp/tests/test_bufferstream.hpp') diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp index 5afaba0966..45ddb7fd80 100644 --- a/indra/llcorehttp/tests/test_bufferstream.hpp +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -242,7 +242,7 @@ void BufferStreamTestObjectType::test<6>() // make sure we didn't leak any memory // ensure("Allocated memory returned", mMemTotal == GetMemTotal()); - static U64 mem = GetMemTotal(); + // static U64 mem = GetMemTotal(); } -- cgit v1.3 From 5ff1758b633f1984f601aacbb7920c3c744b87f7 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 22 Jun 2012 14:41:08 -0400 Subject: SH-3177, SH-3180 std::iostream and LLSD serialization for BufferArray objects. Seems to be working correctly. Not certain this is the fastest possible way to provide a std::streambuf interface but it's visually acceptable. --- indra/llcorehttp/bufferstream.h | 59 ++++++++++++++++++++++++++++ indra/llcorehttp/tests/test_bufferstream.hpp | 54 ++++++++++++++++++++++++- indra/newview/lltexturefetch.cpp | 27 ++++++------- indra/newview/lltexturefetch.h | 8 ++++ 4 files changed, 133 insertions(+), 15 deletions(-) (limited to 'indra/llcorehttp/tests/test_bufferstream.hpp') diff --git a/indra/llcorehttp/bufferstream.h b/indra/llcorehttp/bufferstream.h index 60bda9ff9a..9327a798aa 100644 --- a/indra/llcorehttp/bufferstream.h +++ b/indra/llcorehttp/bufferstream.h @@ -34,13 +34,59 @@ #include "bufferarray.h" +/// @file bufferstream.h +/// +/// std::streambuf and std::iostream adapters for BufferArray +/// objects. +/// +/// BufferArrayStreamBuf inherits std::streambuf and implements +/// an unbuffered interface for streambuf. This may or may not +/// be the most time efficient implementation and it is a little +/// challenging. +/// +/// BufferArrayStream inherits std::iostream and will be the +/// adapter object most callers will be interested in (though +/// it uses BufferArrayStreamBuf internally). Instances allow +/// for the usual streaming operators ('<<', '>>') and serialization +/// methods. +/// +/// Example of LLSD serialization to a BufferArray: +/// +/// BufferArray * ba = new BufferArray; +/// BufferArrayStream bas(ba); +/// LLSDSerialize::toXML(llsd, bas); +/// operationOnBufferArray(ba); +/// ba->release(); +/// ba = NULL; +/// // operationOnBufferArray and bas are each holding +/// // references to the ba instance at this point. +/// + namespace LLCore { +// ===================================================== +// BufferArrayStreamBuf +// ===================================================== + +/// Adapter class to put a std::streambuf interface on a BufferArray +/// +/// Application developers will rarely be interested in anything +/// other than the constructor and even that will rarely be used +/// except indirectly via the @BufferArrayStream class. The +/// choice of interfaces implemented yields a bufferless adapter +/// that doesn't used either the input or output pointer triplets +/// of the more common buffered implementations. This may or may +/// not be faster and that question could stand to be looked at +/// sometime. +/// + class BufferArrayStreamBuf : public std::streambuf { public: + /// Constructor increments the reference count on the + /// BufferArray argument and calls release() on destruction. BufferArrayStreamBuf(BufferArray * array); virtual ~BufferArrayStreamBuf(); @@ -74,9 +120,22 @@ protected: }; // end class BufferArrayStreamBuf +// ===================================================== +// BufferArrayStream +// ===================================================== + +/// Adapter class that supplies streaming operators to BufferArray +/// +/// Provides a streaming adapter to an existing BufferArray +/// instance so that the convenient '<<' and '>>' conversions +/// can be applied to a BufferArray. Very convenient for LLSD +/// serialization and parsing as well. + class BufferArrayStream : public std::iostream { public: + /// Constructor increments the reference count on the + /// BufferArray argument and calls release() on destruction. BufferArrayStream(BufferArray * ba); ~BufferArrayStream(); diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp index 45ddb7fd80..831c901b9d 100644 --- a/indra/llcorehttp/tests/test_bufferstream.hpp +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -31,6 +31,8 @@ #include #include "test_allocator.h" +#include "llsd.h" +#include "llsdserialize.h" using namespace LLCore; @@ -173,6 +175,8 @@ void BufferStreamTestObjectType::test<5>() const char * content("This is a string. A fragment."); const size_t c_len(strlen(content)); ba->append(content, c_len); + + // Creat an adapter for the BufferArray BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); ensure("Memory being used", mMemTotal < GetMemTotal()); @@ -223,7 +227,7 @@ void BufferStreamTestObjectType::test<6>() //ba->append(content, strlen(content)); { - // create a new ref counted object with an implicit reference + // Creat an adapter for the BufferArray BufferArrayStream bas(ba); ensure("Memory being used", mMemTotal < GetMemTotal()); @@ -246,6 +250,54 @@ void BufferStreamTestObjectType::test<6>() } +template <> template <> +void BufferStreamTestObjectType::test<7>() +{ + set_test_name("BufferArrayStream with LLSD serialization"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + + { + // Creat an adapter for the BufferArray + BufferArrayStream bas(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // LLSD + LLSD llsd = LLSD::emptyMap(); + + llsd["int"] = LLSD::Integer(3); + llsd["float"] = LLSD::Real(923289.28992); + llsd["string"] = LLSD::String("aksjdl;ajsdgfjgfal;sdgjakl;sdfjkl;ajsdfkl;ajsdfkl;jaskl;dfj"); + + LLSD llsd_map = LLSD::emptyMap(); + llsd_map["int"] = LLSD::Integer(-2889); + llsd_map["float"] = LLSD::Real(2.37829e32); + llsd_map["string"] = LLSD::String("OHIGODHSPDGHOSDHGOPSHDGP"); + + llsd["map"] = llsd_map; + + // Serialize it + LLSDSerialize::toXML(llsd, bas); + + std::string str; + bas >> str; + // std::cout << "SERIALIZED LLSD: " << str << std::endl; + ensure("Extracted string has reasonable length", str.size() > 60); + } + + // 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()); +} + + } // end namespace tut diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 4995d9f5ea..6b186811f1 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -58,6 +58,7 @@ #include "httphandler.h" #include "httpresponse.h" #include "bufferarray.h" +#include "bufferstream.h" ////////////////////////////////////////////////////////////////////////////// @@ -2182,6 +2183,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpRequest(NULL), mHttpOptions(NULL), mHttpHeaders(NULL), + mHttpMetricsHeaders(NULL), mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER), mTotalCacheReadCount(0U), mTotalCacheWriteCount(0U), @@ -2194,6 +2196,8 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpOptions = new LLCore::HttpOptions; mHttpHeaders = new LLCore::HttpHeaders; mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); + mHttpMetricsHeaders = new LLCore::HttpHeaders; + mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml"); } LLTextureFetch::~LLTextureFetch() @@ -2219,6 +2223,12 @@ LLTextureFetch::~LLTextureFetch() mHttpHeaders = NULL; } + if (mHttpMetricsHeaders) + { + mHttpMetricsHeaders->release(); + mHttpMetricsHeaders = NULL; + } + mHttpWaitResource.clear(); delete mHttpRequest; @@ -3501,29 +3511,18 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) if (! mCapsURL.empty()) { - // *FIXME: This mess to get an llsd into a string though - // it's actually no worse than what we currently do... - std::stringstream body; - LLSDSerialize::toXML(merged_llsd, body); - std::string body_str(body.str()); - body.clear(); - - LLCore::HttpHeaders * headers = new LLCore::HttpHeaders; - headers->mHeaders.push_back("Content-Type: application/llsd+xml"); - LLCore::BufferArray * ba = new LLCore::BufferArray; - ba->append(body_str.c_str(), body_str.length()); - body_str.clear(); + LLCore::BufferArrayStream bas(ba); + LLSDSerialize::toXML(merged_llsd, bas); fetcher->getHttpRequest().requestPost(report_policy_class, report_priority, mCapsURL, ba, NULL, - headers, + fetcher->getMetricsHeaders(), handler); ba->release(); - headers->release(); LLTextureFetch::svMetricsDataBreak = false; } else diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index e17c71113a..4d762a0e05 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -157,6 +157,13 @@ public: // Threads: T* LLCore::HttpRequest & getHttpRequest() { return *mHttpRequest; } + // Return a pointer to the shared metrics headers definition. + // Does not increment the reference count, caller is required + // to do that to hold a reference for any length of time. + // + // Threads: T* + LLCore::HttpHeaders * getMetricsHeaders() const { return mHttpMetricsHeaders; } + bool isQAMode() const { return mQAMode; } // ---------------------------------- @@ -322,6 +329,7 @@ private: LLCore::HttpRequest * mHttpRequest; // Ttf LLCore::HttpOptions * mHttpOptions; // Ttf LLCore::HttpHeaders * mHttpHeaders; // Ttf + LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf // We use a resource semaphore to keep HTTP requests in // WAIT_HTTP_RESOURCE2 if there aren't sufficient slots in the -- cgit v1.3