From 5611cb6d476540e6a1c654c1f9acdce2787b3505 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 23 Apr 2012 16:19:39 -0400 Subject: Okay, imported the core-http library and got it compiling suspiciously easily. The unit/integration tests don't work yet as I'm still battling cmake/autobuild as usual but first milestone passed. --- indra/llcorehttp/bufferarray.cpp | 281 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 indra/llcorehttp/bufferarray.cpp (limited to 'indra/llcorehttp/bufferarray.cpp') diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp new file mode 100644 index 0000000000..4c20350b13 --- /dev/null +++ b/indra/llcorehttp/bufferarray.cpp @@ -0,0 +1,281 @@ +/** + * @file bufferarray.cpp + * @brief Implements the BufferArray scatter/gather buffer + * + * $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 "bufferarray.h" + + +namespace LLCore +{ + + +// ================================== +// BufferArray::Block Declaration +// ================================== + +class BufferArray::Block +{ +public: + ~Block(); + + void operator delete(void *); + void operator delete(void *, size_t len); + +protected: + Block(size_t len); + + Block(const Block &); // Not defined + void operator=(const Block &); // Not defined + + // Allocate the block with the additional space for the + // buffered data at the end of the object. + void * operator new(size_t len, size_t addl_len); + +public: + // Only public entry to get a block. + static Block * alloc(size_t len); + +public: + size_t mLen; + + // *NOTE: Must be last member of the object. We'll + // overallocate as requested via operator new and index + // into the array at will. + char mData[1]; +}; + + +// ================================== +// BufferArray Definitions +// ================================== + + +BufferArray::BufferArray() + : LLCoreInt::RefCounted(true), + mPos(0), + mLen(0) +{} + + +BufferArray::~BufferArray() +{ + for (container_t::iterator it(mBlocks.begin()); + it != mBlocks.end(); + ++it) + { + delete *it; + *it = NULL; + } + mBlocks.clear(); +} + + +size_t BufferArray::append(const char * src, size_t len) +{ + if (len) + { + if (mBlocks.size() >= mBlocks.capacity()) + { + mBlocks.reserve(mBlocks.size() + 5); + } + Block * block = Block::alloc(len); + memcpy(block->mData, src, len); + mBlocks.push_back(block); + mLen += len; + mPos = mLen; + } + return len; +} + + +char * BufferArray::appendBufferAlloc(size_t len) +{ + // If someone asks for zero-length, we give them a valid pointer. + if (mBlocks.size() >= mBlocks.capacity()) + { + mBlocks.reserve(mBlocks.size() + 5); + } + Block * block = Block::alloc(len); + mBlocks.push_back(block); + mLen += len; + mPos = mLen; + return block->mData; +} + + +size_t BufferArray::seek(size_t pos) +{ + if (pos > mLen) + pos = mLen; + mPos = pos; + return mPos; +} + + +size_t BufferArray::read(char * dst, size_t len) +{ + size_t result(0), offset(0); + size_t len_limit(mLen - mPos); + len = std::min(len, len_limit); + + if (mPos >= mLen || 0 == len) + return 0; + + const int block_limit(mBlocks.size()); + int block_start(findBlock(mPos, &offset)); + if (block_start < 0) + return 0; + + do + { + Block & block(*mBlocks[block_start]); + size_t block_limit(block.mLen - offset); + size_t block_len(std::min(block_limit, len)); + + memcpy(dst, &block.mData[offset], block_len); + result += block_len; + len -= block_len; + dst += block_len; + offset = 0; + ++block_start; + } + while (len && block_start < block_limit); + + mPos += result; + return result; +} + + +size_t BufferArray::write(const char * src, size_t len) +{ + size_t result(0), offset(0); + if (mPos > mLen || 0 == len) + return 0; + + const int block_limit(mBlocks.size()); + int block_start(findBlock(mPos, &offset)); + + if (block_start >= 0) + { + // Some or all of the write will be on top of + // existing data. + do + { + Block & block(*mBlocks[block_start]); + size_t block_limit(block.mLen - offset); + size_t block_len(std::min(block_limit, len)); + + memcpy(&block.mData[offset], src, block_len); + result += block_len; + len -= block_len; + src += block_len; + offset = 0; + ++block_start; + } + while (len && block_start < block_limit); + } + mPos += result; + + if (len) + { + // Some or all of the remaining write data will + // be an append. + result += append(src, len); + } + + return result; +} + + +int BufferArray::findBlock(size_t pos, size_t * ret_offset) +{ + *ret_offset = 0; + if (pos >= mLen) + return -1; // Doesn't exist + + const int block_limit(mBlocks.size()); + for (int i(0); i < block_limit; ++i) + { + if (pos < mBlocks[i]->mLen) + { + *ret_offset = pos; + return i; + } + pos -= mBlocks[i]->mLen; + } + + // Shouldn't get here but... + return -1; +} + + +// ================================== +// BufferArray::Block Definitions +// ================================== + + +BufferArray::Block::Block(size_t len) + : mLen(len) +{ + memset(mData, 0, len); +} + + +BufferArray::Block::~Block() +{ + mLen = 0; +} + + +void * BufferArray::Block::operator new(size_t len, size_t addl_len) +{ + void * mem = new char[len + addl_len + sizeof(void *)]; + return mem; +} + + +void BufferArray::Block::operator delete(void * mem) +{ + char * cmem = static_cast(mem); + delete [] cmem; +} + + +void BufferArray::Block::operator delete(void * mem, size_t) +{ + operator delete(mem); +} + + +BufferArray::Block * BufferArray::Block::alloc(size_t len) +{ + Block * block = new (len) Block(len); + return block; +} + + +} // end namespace LLCore + + -- cgit v1.3 From 4155301015525a242a79b9b3134e66d366bc0ebd Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 1 Jun 2012 21:30:45 -0400 Subject: Do some work on BufferArray to make it a bit less naive about chunking data. Remove the stateful use of a seek pointer so that shared read is possible (though maybe not interesting). --- indra/llcorehttp/_httpoprequest.cpp | 14 ++-- indra/llcorehttp/bufferarray.cpp | 126 +++++++++++++++++++--------- indra/llcorehttp/bufferarray.h | 53 ++---------- indra/llcorehttp/tests/test_bufferarray.hpp | 89 ++++++++------------ indra/newview/lltexturefetch.cpp | 3 +- 5 files changed, 133 insertions(+), 152 deletions(-) (limited to 'indra/llcorehttp/bufferarray.cpp') diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 791ee5f860..196011f953 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -340,7 +340,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) long data_size(0); if (mReqBody) { - mReqBody->seek(0); data_size = mReqBody->size(); } curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast(NULL)); @@ -356,7 +355,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) long data_size(0); if (mReqBody) { - mReqBody->seek(0); data_size = mReqBody->size(); } curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); @@ -423,10 +421,8 @@ size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void op->mReplyBody = new BufferArray(); } const size_t req_size(size * nmemb); - char * lump(op->mReplyBody->appendBufferAlloc(req_size)); - memcpy(lump, data, req_size); - - return req_size; + const size_t write_size(op->mReplyBody->append(static_cast(data), req_size)); + return write_size; } @@ -450,9 +446,9 @@ size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void } const size_t do_size((std::min)(req_size, body_size - op->mCurlBodyPos)); - op->mReqBody->read(static_cast(data), do_size); - op->mCurlBodyPos += do_size; - return do_size; + const size_t read_size(op->mReqBody->read(op->mCurlBodyPos, static_cast(data), do_size)); + op->mCurlBodyPos += read_size; + return read_size; } diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 4c20350b13..c13b1a3540 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -27,6 +27,18 @@ #include "bufferarray.h" +// BufferArray is a list of chunks, each a BufferArray::Block, of contiguous +// data presented as a single array. Chunks are at least BufferArray::BLOCK_ALLOC_SIZE +// in length and can be larger. Any chunk may be partially filled or even +// empty. +// +// The BufferArray itself is sharable as a RefCounted entity. As shared +// reads don't work with the concept of a current position/seek value, +// none is kept with the object. Instead, the read and write operations +// all take position arguments. Single write/shared read isn't supported +// directly and any such attempts have to be serialized outside of this +// implementation. + namespace LLCore { @@ -58,7 +70,8 @@ public: static Block * alloc(size_t len); public: - size_t mLen; + size_t mUsed; + size_t mAlloced; // *NOTE: Must be last member of the object. We'll // overallocate as requested via operator new and index @@ -74,7 +87,6 @@ public: BufferArray::BufferArray() : LLCoreInt::RefCounted(true), - mPos(0), mLen(0) {} @@ -94,19 +106,45 @@ BufferArray::~BufferArray() size_t BufferArray::append(const char * src, size_t len) { - if (len) + const size_t ret(len); + + // First, try to copy into the last block + if (len && ! mBlocks.empty()) + { + Block & last(*mBlocks.back()); + if (last.mUsed < last.mAlloced) + { + // Some will fit... + const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); + + memcpy(&last.mData[last.mUsed], src, copy_len); + last.mUsed += copy_len; + llassert_always(last.mUsed <= last.mAlloced); + mLen += copy_len; + src += copy_len; + len -= copy_len; + } + } + + // Then get new blocks as needed + while (len) { + const size_t copy_len((std::min)(len, BLOCK_ALLOC_SIZE)); + if (mBlocks.size() >= mBlocks.capacity()) { mBlocks.reserve(mBlocks.size() + 5); } - Block * block = Block::alloc(len); - memcpy(block->mData, src, len); + Block * block = Block::alloc(BLOCK_ALLOC_SIZE); + memcpy(block->mData, src, copy_len); + block->mUsed = copy_len; + llassert_always(block->mUsed <= block->mAlloced); mBlocks.push_back(block); - mLen += len; - mPos = mLen; + mLen += copy_len; + src += copy_len; + len -= copy_len; } - return len; + return ret; } @@ -117,41 +155,33 @@ char * BufferArray::appendBufferAlloc(size_t len) { mBlocks.reserve(mBlocks.size() + 5); } - Block * block = Block::alloc(len); + Block * block = Block::alloc((std::max)(BLOCK_ALLOC_SIZE, len)); + block->mUsed = len; mBlocks.push_back(block); mLen += len; - mPos = mLen; return block->mData; } -size_t BufferArray::seek(size_t pos) +size_t BufferArray::read(size_t pos, char * dst, size_t len) { - if (pos > mLen) - pos = mLen; - mPos = pos; - return mPos; -} - - -size_t BufferArray::read(char * dst, size_t len) -{ - size_t result(0), offset(0); - size_t len_limit(mLen - mPos); + if (pos >= mLen) + return 0; + size_t len_limit(mLen - pos); len = std::min(len, len_limit); - - if (mPos >= mLen || 0 == len) + if (0 == len) return 0; + size_t result(0), offset(0); const int block_limit(mBlocks.size()); - int block_start(findBlock(mPos, &offset)); + int block_start(findBlock(pos, &offset)); if (block_start < 0) return 0; do { Block & block(*mBlocks[block_start]); - size_t block_limit(block.mLen - offset); + size_t block_limit(block.mUsed - offset); size_t block_len(std::min(block_limit, len)); memcpy(dst, &block.mData[offset], block_len); @@ -163,19 +193,18 @@ size_t BufferArray::read(char * dst, size_t len) } while (len && block_start < block_limit); - mPos += result; return result; } -size_t BufferArray::write(const char * src, size_t len) +size_t BufferArray::write(size_t pos, const char * src, size_t len) { - size_t result(0), offset(0); - if (mPos > mLen || 0 == len) + if (pos > mLen || 0 == len) return 0; + size_t result(0), offset(0); const int block_limit(mBlocks.size()); - int block_start(findBlock(mPos, &offset)); + int block_start(findBlock(pos, &offset)); if (block_start >= 0) { @@ -184,20 +213,39 @@ size_t BufferArray::write(const char * src, size_t len) do { Block & block(*mBlocks[block_start]); - size_t block_limit(block.mLen - offset); + size_t block_limit(block.mUsed - offset); size_t block_len(std::min(block_limit, len)); memcpy(&block.mData[offset], src, block_len); result += block_len; - len -= block_len; src += block_len; + len -= block_len; offset = 0; ++block_start; } while (len && block_start < block_limit); } - mPos += result; + // Something left, see if it will fit in the free + // space of the last block. + if (len && ! mBlocks.empty()) + { + Block & last(*mBlocks.back()); + if (last.mUsed < last.mAlloced) + { + // Some will fit... + const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); + + memcpy(&last.mData[last.mUsed], src, copy_len); + last.mUsed += copy_len; + result += copy_len; + llassert_always(last.mUsed <= last.mAlloced); + mLen += copy_len; + src += copy_len; + len -= copy_len; + } + } + if (len) { // Some or all of the remaining write data will @@ -218,12 +266,12 @@ int BufferArray::findBlock(size_t pos, size_t * ret_offset) const int block_limit(mBlocks.size()); for (int i(0); i < block_limit; ++i) { - if (pos < mBlocks[i]->mLen) + if (pos < mBlocks[i]->mUsed) { *ret_offset = pos; return i; } - pos -= mBlocks[i]->mLen; + pos -= mBlocks[i]->mUsed; } // Shouldn't get here but... @@ -237,7 +285,8 @@ int BufferArray::findBlock(size_t pos, size_t * ret_offset) BufferArray::Block::Block(size_t len) - : mLen(len) + : mUsed(0), + mAlloced(len) { memset(mData, 0, len); } @@ -245,7 +294,8 @@ BufferArray::Block::Block(size_t len) BufferArray::Block::~Block() { - mLen = 0; + mUsed = 0; + mAlloced = 0; } diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index b26ad1b297..9ccd85d4f8 100644 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -73,6 +73,9 @@ private: void operator=(const BufferArray &); // Not defined public: + // Internal magic number, may be used by unit tests. + static const size_t BLOCK_ALLOC_SIZE = 1504; + /// Appends the indicated data to the BufferArray /// modifying current position and total size. New /// position is one beyond the final byte of the buffer. @@ -96,24 +99,16 @@ public: return mLen; } - /// Set the current position for subsequent read and - /// write operations. 'pos' values before the beginning - /// or greater than the size of the buffer are coerced - /// to a value within the buffer. - /// - /// @return Actual current position after seek. - size_t seek(size_t pos); - - /// Copies data from the current position in the instance + /// Copies data from the given position in the instance /// to the caller's buffer. Will return a short count of /// bytes copied if the 'len' extends beyond the data. - size_t read(char * dst, size_t len); + size_t read(size_t pos, char * dst, size_t len); /// Copies data from the caller's buffer to the instance /// at the current position. May overwrite existing data, /// append data when current position is equal to the /// size of the instance or do a mix of both. - size_t write(const char * src, size_t len); + size_t write(size_t pos, const char * src, size_t len); protected: int findBlock(size_t pos, size_t * ret_offset); @@ -123,46 +118,10 @@ protected: typedef std::vector container_t; container_t mBlocks; - size_t mPos; size_t mLen; }; // end class BufferArray -#if 0 - -// Conceptual for now. Another possibility is going with -// something like Boost::asio's buffers interface. They're -// trying to achieve the same thing above and below.... - -class BufferStream : public std::streambuf -{ -public: - BufferStream(BufferArray * buffer); - virtual ~BufferStream(); - -private: - BufferStream(const BufferStream &); // Not defined - void operator=(const BufferStream &); // Not defined - -public: - // Types - typedef std::streambuf::pos_type pos_type; - typedef std::streambuf::off_type off_type; - - virtual int underflow(); - - virtual int overflow(int c); - - virtual int sync(); - - virtual pos_type seekoff(off_type off, std::ios::seekdir way, std::ios::openmode which); - -protected: - BufferArray * mBufferArray; -}; // end class BufferStream - -#endif // 0 - } // end namespace LLCore #endif // _LLCORE_BUFFER_ARRAY_H_ diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp index ecbb5ef250..2ad9391d1c 100644 --- a/indra/llcorehttp/tests/test_bufferarray.hpp +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -67,7 +67,7 @@ void BufferArrayTestObjectType::test<1>() // Try to read char buffer[20]; - size_t read_len(ba->read(buffer, sizeof(buffer))); + size_t read_len(ba->read(0, buffer, sizeof(buffer))); ensure("Read returns empty", 0 == read_len); // release the implicit reference, causing the object to be released @@ -92,16 +92,13 @@ void BufferArrayTestObjectType::test<2>() char str1[] = "abcdefghij"; char buffer[256]; - size_t len = ba->write(str1, strlen(str1)); + size_t len = ba->write(0, str1, strlen(str1)); ensure("Wrote length correct", strlen(str1) == len); ensure("Recorded size correct", strlen(str1) == ba->size()); // read some data back - len = ba->seek(2); - ensure("Seek worked", 2 == len); - memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, 2); + len = ba->read(2, buffer, 2); ensure("Read length correct", 2 == len); ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); ensure("Read didn't overwrite", 'X' == buffer[2]); @@ -130,32 +127,26 @@ void BufferArrayTestObjectType::test<3>() size_t str1_len(strlen(str1)); char buffer[256]; - size_t len = ba->write(str1, str1_len); + size_t len = ba->write(0, str1, str1_len); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", str1_len == ba->size()); // again... - len = ba->write(str1, strlen(str1)); + len = ba->write(str1_len, str1, strlen(str1)); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", (2 * str1_len) == ba->size()); // read some data back - len = ba->seek(8); - ensure("Seek worked", 8 == len); - memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, 4); + len = ba->read(8, buffer, 4); ensure("Read length correct", 4 == len); ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); ensure("Read content correct", 'a' == buffer[2] && 'b' == buffer[3]); ensure("Read didn't overwrite", 'X' == buffer[4]); // Read whole thing - len = ba->seek(0); - ensure("Seek worked (2)", 0 == len); - memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); ensure("Read length correct", (2 * str1_len) == len); ensure("Read content correct (3)", 0 == strncmp(buffer, str1, str1_len)); ensure("Read content correct (4)", 0 == strncmp(&buffer[str1_len], str1, str1_len)); @@ -185,33 +176,29 @@ void BufferArrayTestObjectType::test<4>() char str2[] = "ABCDEFGHIJ"; char buffer[256]; - size_t len = ba->write(str1, str1_len); + size_t len = ba->write(0, str1, str1_len); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", str1_len == ba->size()); // again... - len = ba->write(str1, strlen(str1)); + len = ba->write(str1_len, str1, strlen(str1)); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", (2 * str1_len) == ba->size()); // reposition and overwrite - len = ba->seek(8); - ensure("Seek worked", 8 == len); - len = ba->write(str2, 4); + len = ba->write(8, str2, 4); ensure("Overwrite length correct", 4 == len); - // Leave position and read verifying content + // Leave position and read verifying content (stale really from seek() days) memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, 4); + len = ba->read(12, buffer, 4); ensure("Read length correct", 4 == len); ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); ensure("Read content correct.2", 'e' == buffer[2] && 'f' == buffer[3]); ensure("Read didn't overwrite", 'X' == buffer[4]); // reposition and check - len = ba->seek(6); - memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, 8); + len = ba->read(6, buffer, 8); ensure("Read length correct.2", 8 == len); ensure("Read content correct.3", 'g' == buffer[0] && 'h' == buffer[1]); ensure("Read content correct.4", 'A' == buffer[2] && 'B' == buffer[3]); @@ -242,21 +229,18 @@ void BufferArrayTestObjectType::test<5>() size_t str1_len(strlen(str1)); char buffer[256]; - size_t len = ba->write(str1, str1_len); + size_t len = ba->write(0, str1, str1_len); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", str1_len == ba->size()); // again... - len = ba->write(str1, strlen(str1)); + len = ba->write(str1_len, str1, str1_len); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", (2 * str1_len) == ba->size()); // read some data back - len = ba->seek(8); - ensure("Seek worked", 8 == len); - memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, 4); + len = ba->read(8, buffer, 4); ensure("Read length correct", 4 == len); ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); ensure("Read content correct.2", 'a' == buffer[2] && 'b' == buffer[3]); @@ -264,7 +248,7 @@ void BufferArrayTestObjectType::test<5>() // Read some more without repositioning memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, sizeof(buffer)); + len = ba->read(12, buffer, sizeof(buffer)); ensure("Read length correct", (str1_len - 2) == len); ensure("Read content correct.3", 0 == strncmp(buffer, str1+2, str1_len-2)); ensure("Read didn't overwrite.2", 'X' == buffer[str1_len-1]); @@ -294,31 +278,27 @@ void BufferArrayTestObjectType::test<6>() size_t str2_len(strlen(str2)); char buffer[256]; - size_t len = ba->write(str1, str1_len); + size_t len = ba->write(0, str1, str1_len); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", str1_len == ba->size()); // again... - len = ba->write(str1, strlen(str1)); + len = ba->write(str1_len, str1, strlen(str1)); ensure("Wrote length correct", str1_len == len); ensure("Recorded size correct", (2 * str1_len) == ba->size()); // reposition and overwrite - len = ba->seek(8); - ensure("Seek worked", 8 == len); - len = ba->write(str2, str2_len); + len = ba->write(8, str2, str2_len); ensure("Overwrite length correct", str2_len == len); // Leave position and read verifying content memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, 0); + len = ba->read(8 + str2_len, buffer, 0); ensure("Read length correct", 0 == len); ensure("Read didn't overwrite", 'X' == buffer[0]); // reposition and check - len = ba->seek(0); - memset(buffer, 'X', sizeof(buffer)); - len = ba->read(buffer, sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); ensure("Read length correct.2", (str1_len + str2_len - 2) == len); ensure("Read content correct", 0 == strncmp(buffer, str1, str1_len-2)); ensure("Read content correct.2", 0 == strncmp(buffer+str1_len-2, str2, str2_len)); @@ -350,18 +330,17 @@ void BufferArrayTestObjectType::test<7>() char buffer[256]; // 2x str1 - size_t len = ba->write(str1, str1_len); - len = ba->write(str1, strlen(str1)); + size_t len = ba->write(0, str1, str1_len); + len = ba->write(str1_len, str1, str1_len); // reposition and overwrite - len = ba->seek(6); - len = ba->write(str2, 2); + len = ba->write(6, str2, 2); ensure("Overwrite length correct", 2 == len); - len = ba->write(str2, 2); + len = ba->write(8, str2, 2); ensure("Overwrite length correct.2", 2 == len); - len = ba->write(str2, 2); + len = ba->write(10, str2, 2); ensure("Overwrite length correct.3", 2 == len); // append some data @@ -373,13 +352,12 @@ void BufferArrayTestObjectType::test<7>() memcpy(out_buf, str1, str1_len); // And some final writes - len = ba->write(str2, 2); + len = ba->write(3 * str1_len + str2_len, str2, 2); ensure("Write length correct.2", 2 == len); // Check contents memset(buffer, 'X', sizeof(buffer)); - ba->seek(0); - len = ba->read(buffer, sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); ensure("Final buffer length correct", (3 * str1_len + str2_len + 2) == len); ensure("Read content correct", 0 == strncmp(buffer, str1, 6)); ensure("Read content correct.2", 0 == strncmp(buffer + 6, str2, 2)); @@ -417,8 +395,8 @@ void BufferArrayTestObjectType::test<8>() char buffer[256]; // 2x str1 - size_t len = ba->write(str1, str1_len); - len = ba->write(str1, strlen(str1)); + size_t len = ba->write(0, str1, str1_len); + len = ba->write(str1_len, str1, str1_len); // zero-length allocate (we allow this with a valid pointer returned) char * out_buf(ba->appendBufferAlloc(0)); @@ -430,12 +408,11 @@ void BufferArrayTestObjectType::test<8>() ensure("Two zero-length appendBufferAlloc buffers distinct", out_buf != out_buf2); // And some final writes - len = ba->write(str2, str2_len); + len = ba->write(2 * str1_len, str2, str2_len); // Check contents memset(buffer, 'X', sizeof(buffer)); - ba->seek(0); - len = ba->read(buffer, sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); ensure("Final buffer length correct", (2 * str1_len + str2_len) == len); ensure("Read content correct.1", 0 == strncmp(buffer, str1, str1_len)); ensure("Read content correct.2", 0 == strncmp(buffer + str1_len, str1, str1_len)); diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index daad463e0d..e27f0aec1e 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1255,8 +1255,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { memcpy(buffer, mFormattedImage->getData(), cur_size); } - mHttpBufferArray->seek(0); - mHttpBufferArray->read((char *) buffer + cur_size, append_size); + mHttpBufferArray->read(0, (char *) buffer + cur_size, append_size); // NOTE: setData releases current data and owns new data (buffer) mFormattedImage->setData(buffer, total_size); -- cgit v1.3 From c7d5b0dced7f17f77fa00c816286cdbd7a0e9d4b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 2 Jun 2012 02:09:33 +0000 Subject: More platform fixes for linux/mac. --- indra/llcorehttp/bufferarray.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/llcorehttp/bufferarray.cpp') diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index c13b1a3540..47389da0be 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -85,6 +85,8 @@ public: // ================================== +const size_t BufferArray::BLOCK_ALLOC_SIZE; + BufferArray::BufferArray() : LLCoreInt::RefCounted(true), mLen(0) -- cgit v1.3 From 3a9c182d7fe3fdacf5923e3b1b51456472e60bb1 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 2 Jun 2012 02:22:07 +0000 Subject: Stupid windows tricks. --- indra/llcorehttp/bufferarray.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/llcorehttp/bufferarray.cpp') diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 47389da0be..36a8006bce 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -85,7 +85,9 @@ public: // ================================== +#if ! defined(WIN32) const size_t BufferArray::BLOCK_ALLOC_SIZE; +#endif // ! defined(WIN32) BufferArray::BufferArray() : LLCoreInt::RefCounted(true), -- cgit v1.3 From 267ab5b417eaef64a170d69ad83334df9d566ed9 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 11 Jun 2012 17:47:04 -0400 Subject: Convert BufferArray interfaces to void * (not char *). HttpRequest::update() honor time limit. Generally, opaque data operations are expected to be over 'void *' and have now converted interfaces to do that. Update() method honors millisecond limit to dwell time. Might want to homologate the millis/uSecs mix later.... --- indra/llcorehttp/bufferarray.cpp | 37 ++++++++++++++++------------- indra/llcorehttp/bufferarray.h | 8 +++---- indra/llcorehttp/httprequest.cpp | 13 ++++------ indra/llcorehttp/httprequest.h | 2 +- indra/llcorehttp/tests/test_bufferarray.hpp | 6 ++--- 5 files changed, 34 insertions(+), 32 deletions(-) (limited to 'indra/llcorehttp/bufferarray.cpp') diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 36a8006bce..6d5309a043 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -108,10 +108,11 @@ BufferArray::~BufferArray() } -size_t BufferArray::append(const char * src, size_t len) +size_t BufferArray::append(const void * src, size_t len) { const size_t ret(len); - + const char * c_src(static_cast(src)); + // First, try to copy into the last block if (len && ! mBlocks.empty()) { @@ -121,11 +122,11 @@ size_t BufferArray::append(const char * src, size_t len) // Some will fit... const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); - memcpy(&last.mData[last.mUsed], src, copy_len); + memcpy(&last.mData[last.mUsed], c_src, copy_len); last.mUsed += copy_len; llassert_always(last.mUsed <= last.mAlloced); mLen += copy_len; - src += copy_len; + c_src += copy_len; len -= copy_len; } } @@ -140,19 +141,19 @@ size_t BufferArray::append(const char * src, size_t len) mBlocks.reserve(mBlocks.size() + 5); } Block * block = Block::alloc(BLOCK_ALLOC_SIZE); - memcpy(block->mData, src, copy_len); + memcpy(block->mData, c_src, copy_len); block->mUsed = copy_len; llassert_always(block->mUsed <= block->mAlloced); mBlocks.push_back(block); mLen += copy_len; - src += copy_len; + c_src += copy_len; len -= copy_len; } return ret; } -char * BufferArray::appendBufferAlloc(size_t len) +void * BufferArray::appendBufferAlloc(size_t len) { // If someone asks for zero-length, we give them a valid pointer. if (mBlocks.size() >= mBlocks.capacity()) @@ -167,8 +168,10 @@ char * BufferArray::appendBufferAlloc(size_t len) } -size_t BufferArray::read(size_t pos, char * dst, size_t len) +size_t BufferArray::read(size_t pos, void * dst, size_t len) { + char * c_dst(static_cast(dst)); + if (pos >= mLen) return 0; size_t len_limit(mLen - pos); @@ -188,10 +191,10 @@ size_t BufferArray::read(size_t pos, char * dst, size_t len) size_t block_limit(block.mUsed - offset); size_t block_len(std::min(block_limit, len)); - memcpy(dst, &block.mData[offset], block_len); + memcpy(c_dst, &block.mData[offset], block_len); result += block_len; len -= block_len; - dst += block_len; + c_dst += block_len; offset = 0; ++block_start; } @@ -201,8 +204,10 @@ size_t BufferArray::read(size_t pos, char * dst, size_t len) } -size_t BufferArray::write(size_t pos, const char * src, size_t len) +size_t BufferArray::write(size_t pos, const void * src, size_t len) { + const char * c_src(static_cast(src)); + if (pos > mLen || 0 == len) return 0; @@ -220,9 +225,9 @@ size_t BufferArray::write(size_t pos, const char * src, size_t len) size_t block_limit(block.mUsed - offset); size_t block_len(std::min(block_limit, len)); - memcpy(&block.mData[offset], src, block_len); + memcpy(&block.mData[offset], c_src, block_len); result += block_len; - src += block_len; + c_src += block_len; len -= block_len; offset = 0; ++block_start; @@ -240,12 +245,12 @@ size_t BufferArray::write(size_t pos, const char * src, size_t len) // Some will fit... const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); - memcpy(&last.mData[last.mUsed], src, copy_len); + memcpy(&last.mData[last.mUsed], c_src, copy_len); last.mUsed += copy_len; result += copy_len; llassert_always(last.mUsed <= last.mAlloced); mLen += copy_len; - src += copy_len; + c_src += copy_len; len -= copy_len; } } @@ -254,7 +259,7 @@ size_t BufferArray::write(size_t pos, const char * src, size_t len) { // Some or all of the remaining write data will // be an append. - result += append(src, len); + result += append(c_src, len); } return result; diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index 9ccd85d4f8..72c3e1c669 100644 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -81,7 +81,7 @@ public: /// position is one beyond the final byte of the buffer. /// /// @return Count of bytes copied to BufferArray - size_t append(const char * src, size_t len); + size_t append(const void * src, size_t len); /// Similar to @see append(), this call guarantees a /// contiguous block of memory of requested size placed @@ -91,7 +91,7 @@ public: /// /// @return Pointer to contiguous region at end /// of BufferArray of 'len' size. - char * appendBufferAlloc(size_t len); + void * appendBufferAlloc(size_t len); /// Current count of bytes in BufferArray instance. size_t size() const @@ -102,13 +102,13 @@ public: /// Copies data from the given position in the instance /// to the caller's buffer. Will return a short count of /// bytes copied if the 'len' extends beyond the data. - size_t read(size_t pos, char * dst, size_t len); + size_t read(size_t pos, void * dst, size_t len); /// Copies data from the caller's buffer to the instance /// at the current position. May overwrite existing data, /// append data when current position is equal to the /// size of the instance or do a mix of both. - size_t write(size_t pos, const char * src, size_t len); + size_t write(size_t pos, const void * src, size_t len); protected: int findBlock(size_t pos, size_t * ret_offset); diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index baa0fe1a84..2f36168f8b 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -35,6 +35,8 @@ #include "_httpopsetpriority.h" #include "_httpopcancel.h" +#include "lltimer.h" + namespace { @@ -279,14 +281,9 @@ HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priori HttpStatus HttpRequest::update(long millis) { - HttpStatus status; - - // *FIXME: need timer stuff - // long now(getNow()); - // long limit(now + millis); - + const HttpTime limit(totalTime() + (1000 * HttpTime(millis))); HttpOperation * op(NULL); - while ((op = mReplyQueue->fetchOp())) + while (limit >= totalTime() && (op = mReplyQueue->fetchOp())) { // Process operation op->visitNotifier(this); @@ -295,7 +292,7 @@ HttpStatus HttpRequest::update(long millis) op->release(); } - return status; + return HttpStatus(); } diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 3592d5c6a3..01dbfba6dd 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -259,7 +259,7 @@ public: /// @param millis Maximum number of wallclock milliseconds to /// spend in the call. As hinted at above, this /// is partly a function of application code so it's - /// a soft limit. (And not currently implemented.) + /// a soft limit. /// /// @return Standard status code. HttpStatus update(long millis); diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp index 2ad9391d1c..3f947db967 100644 --- a/indra/llcorehttp/tests/test_bufferarray.hpp +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -348,7 +348,7 @@ void BufferArrayTestObjectType::test<7>() ensure("Append length correct", str2_len == len); // append some more - char * out_buf(ba->appendBufferAlloc(str1_len)); + void * out_buf(ba->appendBufferAlloc(str1_len)); memcpy(out_buf, str1, str1_len); // And some final writes @@ -399,11 +399,11 @@ void BufferArrayTestObjectType::test<8>() len = ba->write(str1_len, str1, str1_len); // zero-length allocate (we allow this with a valid pointer returned) - char * out_buf(ba->appendBufferAlloc(0)); + void * out_buf(ba->appendBufferAlloc(0)); ensure("Buffer from zero-length appendBufferAlloc non-NULL", NULL != out_buf); // Do it again - char * out_buf2(ba->appendBufferAlloc(0)); + void * out_buf2(ba->appendBufferAlloc(0)); ensure("Buffer from zero-length appendBufferAlloc non-NULL.2", NULL != out_buf2); ensure("Two zero-length appendBufferAlloc buffers distinct", out_buf != out_buf2); -- cgit v1.3 From 24e16e1632974057013b86300bb60954ea6f5684 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 12 Jun 2012 12:41:09 -0400 Subject: Convert _refcounted.h over to using LLAtomic32<>. Beware of bad documentation. operator--(int) does not return what the header claimed it did. --- indra/llcommon/llapr.h | 2 +- indra/llcorehttp/_refcounted.h | 52 ++++++++++++++-------------------------- indra/llcorehttp/bufferarray.cpp | 6 ++--- 3 files changed, 22 insertions(+), 38 deletions(-) (limited to 'indra/llcorehttp/bufferarray.cpp') diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index af33ce666f..034546c3f3 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -168,7 +168,7 @@ public: void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); } void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); } Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++ - Type operator --(int) { return apr_atomic_dec32(&mData); } // Type-- + Type operator --(int) { return apr_atomic_dec32(&mData); } // approximately --Type (0 if final is 0, non-zero otherwise) private: apr_uint32_t mData; diff --git a/indra/llcorehttp/_refcounted.h b/indra/llcorehttp/_refcounted.h index 72cef6b342..a96c65fb6b 100644 --- a/indra/llcorehttp/_refcounted.h +++ b/indra/llcorehttp/_refcounted.h @@ -28,9 +28,11 @@ #define LLCOREINT__REFCOUNTED_H_ +#include "linden_common.h" + #include -#include "linden_common.h" +#include "llapr.h" namespace LLCoreInt @@ -44,7 +46,7 @@ private: void operator=(const RefCounted &); // Not defined public: - explicit RefCounted(bool const implicit) + explicit RefCounted(bool const implicit) : mRefCount(implicit) {} @@ -52,42 +54,34 @@ public: void addRef() const; void release() const; bool isLastRef() const; - int getRefCount() const; + S32 getRefCount() const; void noRef() const; - static const int NOT_REF_COUNTED = -1; + static const S32 NOT_REF_COUNTED = -1; protected: virtual ~RefCounted(); virtual void destroySelf(); private: - mutable int mRefCount; - mutable boost::mutex mRefLock; + mutable LLAtomicS32 mRefCount; }; // end class RefCounted inline void RefCounted::addRef() const { - boost::mutex::scoped_lock lock(mRefLock); - llassert_always(mRefCount >= 0); - ++mRefCount; + S32 count(mRefCount++); + llassert_always(count >= 0); } inline void RefCounted::release() const { - int count(0); - { - // CRITICAL SECTION - boost::mutex::scoped_lock lock(mRefLock); - llassert_always(mRefCount != NOT_REF_COUNTED); - llassert_always(mRefCount > 0); - count = --mRefCount; - // CRITICAL SECTION - } - + S32 count(mRefCount); + llassert_always(count != NOT_REF_COUNTED); + llassert_always(count > 0); + count = mRefCount--; // clean ourselves up if that was the last reference if (0 == count) @@ -99,32 +93,22 @@ inline void RefCounted::release() const inline bool RefCounted::isLastRef() const { - int count(0); - { - // CRITICAL SECTION - boost::mutex::scoped_lock lock(mRefLock); - - llassert_always(mRefCount != NOT_REF_COUNTED); - llassert_always(mRefCount >= 1); - count = mRefCount; - // CRITICAL SECTION - } - + const S32 count(mRefCount); + llassert_always(count != NOT_REF_COUNTED); + llassert_always(count >= 1); return (1 == count); } -inline int RefCounted::getRefCount() const +inline S32 RefCounted::getRefCount() const { - boost::mutex::scoped_lock lock(mRefLock); - const int result(mRefCount); + const S32 result(mRefCount); return result; } inline void RefCounted::noRef() const { - boost::mutex::scoped_lock lock(mRefLock); llassert_always(mRefCount <= 1); mRefCount = NOT_REF_COUNTED; } diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 6d5309a043..ae92057df0 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -175,7 +175,7 @@ size_t BufferArray::read(size_t pos, void * dst, size_t len) if (pos >= mLen) return 0; size_t len_limit(mLen - pos); - len = std::min(len, len_limit); + len = (std::min)(len, len_limit); if (0 == len) return 0; @@ -189,7 +189,7 @@ size_t BufferArray::read(size_t pos, void * dst, size_t len) { Block & block(*mBlocks[block_start]); size_t block_limit(block.mUsed - offset); - size_t block_len(std::min(block_limit, len)); + size_t block_len((std::min)(block_limit, len)); memcpy(c_dst, &block.mData[offset], block_len); result += block_len; @@ -223,7 +223,7 @@ size_t BufferArray::write(size_t pos, const void * src, size_t len) { Block & block(*mBlocks[block_start]); size_t block_limit(block.mUsed - offset); - size_t block_len(std::min(block_limit, len)); + size_t block_len((std::min)(block_limit, len)); memcpy(&block.mData[offset], c_src, block_len); result += block_len; -- cgit v1.3 From a50944e078b98435685f04eda0ba93e95d4c61f2 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 19 Jun 2012 17:01:02 -0400 Subject: Cleanup: move magic nubmers to new _httpinternal.h header file. --- indra/llcorehttp/CMakeLists.txt | 1 + indra/llcorehttp/_httpinternal.h | 82 ++++++++++++++++++++++++++++++++++ indra/llcorehttp/_httplibcurl.cpp | 17 ++++--- indra/llcorehttp/_httplibcurl.h | 14 ++---- indra/llcorehttp/_httpoperation.cpp | 3 +- indra/llcorehttp/_httpoprequest.cpp | 62 ++++++++++++------------- indra/llcorehttp/_httpoprequest.h | 6 +++ indra/llcorehttp/_httppolicy.cpp | 4 +- indra/llcorehttp/_httppolicy.h | 3 +- indra/llcorehttp/_httppolicyglobal.cpp | 10 +++-- indra/llcorehttp/_httpservice.cpp | 9 ++-- indra/llcorehttp/_refcounted.cpp | 4 +- indra/llcorehttp/bufferarray.cpp | 4 +- indra/llcorehttp/httpcommon.cpp | 2 +- indra/llcorehttp/httpoptions.cpp | 8 ++-- indra/llcorehttp/httprequest.cpp | 1 + indra/llcorehttp/httprequest.h | 6 +-- indra/newview/lltextureview.cpp | 2 +- 18 files changed, 161 insertions(+), 77 deletions(-) create mode 100644 indra/llcorehttp/_httpinternal.h (limited to 'indra/llcorehttp/bufferarray.cpp') diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 9d8bae973e..acf4c0d6a3 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -53,6 +53,7 @@ set(llcorehttp_HEADER_FILES httpoptions.h httprequest.h httpresponse.h + _httpinternal.h _httplibcurl.h _httpopcancel.h _httpoperation.h diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h new file mode 100644 index 0000000000..bc0bd6a2ab --- /dev/null +++ b/indra/llcorehttp/_httpinternal.h @@ -0,0 +1,82 @@ +/** + * @file httpinternal.h + * @brief Implementation constants and magic numbers + * + * $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_HTTP_INTERNAL_H_ +#define _LLCORE_HTTP_INTERNAL_H_ + + +// If you find this included in a public interface header, +// something wrong is probably happening. + + +namespace LLCore +{ + +// Maxium number of policy classes that can be defined. +// *FIXME: Currently limited to the default class, extend. +const int POLICY_CLASS_LIMIT = 1; + +// Debug/informational tracing. Used both +// as a global option and in per-request traces. +const int TRACE_OFF = 0; +const int TRACE_LOW = 1; +const int TRACE_CURL_HEADERS = 2; +const int TRACE_CURL_BODIES = 3; + +const int TRACE_MIN = TRACE_OFF; +const int TRACE_MAX = TRACE_CURL_BODIES; + +// Request retry limits +const int DEFAULT_RETRY_COUNT = 5; +const int LIMIT_RETRY_MIN = 0; +const int LIMIT_RETRY_MAX = 100; + +const int DEFAULT_HTTP_REDIRECTS = 10; + +// Timeout value used for both connect and protocol exchange. +// Retries and time-on-queue are not included and aren't +// accounted for. +const long DEFAULT_TIMEOUT = 30L; +const long LIMIT_TIMEOUT_MIN = 0L; +const long LIMIT_TIMEOUT_MAX = 3600L; + +// Limits on connection counts +const int DEFAULT_CONNECTIONS = 8; +const int LIMIT_CONNECTIONS_MIN = 1; +const int LIMIT_CONNECTIONS_MAX = 256; + +// Tuning parameters + +// Time worker thread sleeps after a pass through the +// request, ready and active queues. +const int LOOP_SLEEP_NORMAL_MS = 2; + +// Block allocation size (a tuning parameter) is found +// in bufferarray.h. + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_INTERNAL_H_ diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index a176dd5b2a..65eb642056 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -31,6 +31,8 @@ #include "_httpoprequest.h" #include "_httppolicy.h" +#include "llhttpstatuscodes.h" + namespace LLCore { @@ -39,7 +41,8 @@ namespace LLCore HttpLibcurl::HttpLibcurl(HttpService * service) : mService(service) { - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + // *FIXME: Use active policy class count later + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) { mMultiHandles[policy_class] = 0; } @@ -61,7 +64,7 @@ HttpLibcurl::~HttpLibcurl() mActiveOps.erase(item); } - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) { if (mMultiHandles[policy_class]) { @@ -89,7 +92,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP); // Give libcurl some cycles to do I/O & callbacks - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) { if (! mMultiHandles[policy_class]) continue; @@ -144,7 +147,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() void HttpLibcurl::addOp(HttpOpRequest * op) { - llassert_always(op->mReqPolicy < HttpRequest::POLICY_CLASS_LIMIT); + llassert_always(op->mReqPolicy < POLICY_CLASS_LIMIT); llassert_always(mMultiHandles[op->mReqPolicy] != NULL); // Create standard handle @@ -159,7 +162,7 @@ void HttpLibcurl::addOp(HttpOpRequest * op) curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); op->mCurlActive = true; - if (op->mTracing > 0) + if (op->mTracing > TRACE_OFF) { HttpPolicy & policy(mService->getPolicy()); @@ -208,7 +211,7 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode } if (op->mStatus) { - int http_status(200); + int http_status(HTTP_OK); curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status); op->mStatus = LLCore::HttpStatus(http_status); @@ -220,7 +223,7 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode op->mCurlHandle = NULL; // Tracing - if (op->mTracing > 0) + if (op->mTracing > TRACE_OFF) { LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle: " << static_cast(op) diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 16b68bde43..0d0c4cad6d 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -36,6 +36,7 @@ #include "httprequest.h" #include "_httpservice.h" +#include "_httpinternal.h" namespace LLCore @@ -91,17 +92,8 @@ protected: protected: HttpService * mService; // Simple reference, not owner active_set_t mActiveOps; - CURLM * mMultiHandles[HttpRequest::POLICY_CLASS_LIMIT]; -}; // end class HttpLibcurl - - -// --------------------------------------- -// Free functions -// --------------------------------------- - - -curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); - + CURLM * mMultiHandles[POLICY_CLASS_LIMIT]; +}; // end class HttpLibcurl } // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index 5a31bf90e7..0d9553434e 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -33,6 +33,7 @@ #include "_httprequestqueue.h" #include "_httpreplyqueue.h" #include "_httpservice.h" +#include "_httpinternal.h" #include "lltimer.h" @@ -142,7 +143,7 @@ HttpStatus HttpOperation::cancel() void HttpOperation::addAsReply() { - if (mTracing > 0) + if (mTracing > TRACE_OFF) { LL_INFOS("CoreHttp") << "TRACE, ToReplyQueue, Handle: " << static_cast(this) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index ce41ebcce0..04a8e0baff 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -42,6 +42,7 @@ #include "_httppolicy.h" #include "_httppolicyglobal.h" #include "_httplibcurl.h" +#include "_httpinternal.h" #include "llhttpstatuscodes.h" #include "llproxy.h" @@ -73,13 +74,14 @@ void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line); -#if defined(WIN32) +#if LL_WINDOWS // Not available on windows where the legacy strtok interface // is thread-safe. char *strtok_r(char *str, const char *delim, char **saveptr); -#endif +#endif // LL_WINDOWS + } @@ -108,7 +110,7 @@ HttpOpRequest::HttpOpRequest() mReplyHeaders(NULL), mPolicyRetries(0), mPolicyRetryAt(HttpTime(0)), - mPolicyRetryLimit(5) + mPolicyRetryLimit(DEFAULT_RETRY_COUNT) { // *NOTE: As members are added, retry initialization/cleanup // may need to be extended in @prepareRequest(). @@ -334,8 +336,8 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, mProcFlags |= PF_SAVE_HEADERS; } mPolicyRetryLimit = options->getRetries(); - mPolicyRetryLimit = llclamp(mPolicyRetryLimit, 0, 100); - mTracing = (std::max)(mTracing, llclamp(options->getTrace(), 0, 3)); + mPolicyRetryLimit = llclamp(mPolicyRetryLimit, LIMIT_RETRY_MIN, LIMIT_RETRY_MAX); + mTracing = (std::max)(mTracing, llclamp(options->getTrace(), TRACE_MIN, TRACE_MAX)); } } @@ -384,7 +386,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, 10); // *FIXME: parameterize this later + curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, DEFAULT_HTTP_REDIRECTS); // *FIXME: parameterize this later curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle); curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback); @@ -452,8 +454,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); } break; @@ -463,7 +463,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } // Tracing - if (mTracing > 1) + if (mTracing >= TRACE_CURL_HEADERS) { curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, mCurlHandle); @@ -478,7 +478,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) char range_line[64]; -#if defined(WIN32) +#if LL_WINDOWS _snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1, (mReqLength ? fmt1 : fmt2), (unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1)); @@ -486,7 +486,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) snprintf(range_line, sizeof(range_line), (mReqLength ? fmt1 : fmt2), (unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1)); -#endif // defined(WIN32) +#endif // LL_WINDOWS range_line[sizeof(range_line) - 1] = '\0'; mCurlHeaders = curl_slist_append(mCurlHeaders, range_line); } @@ -494,11 +494,11 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); // Request options - long timeout(30); + long timeout(DEFAULT_TIMEOUT); if (mReqOptions) { timeout = mReqOptions->getTimeout(); - timeout = llclamp(timeout, 0L, 3600L); + timeout = llclamp(timeout, LIMIT_TIMEOUT_MIN, LIMIT_TIMEOUT_MAX); } curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, timeout); curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout); @@ -599,11 +599,11 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi memcpy(hdr_buffer, hdr_data, frag_size); hdr_buffer[frag_size] = '\0'; -#if defined(WIN32) +#if LL_WINDOWS if (! _strnicmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) #else if (! strncasecmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) -#endif +#endif // LL_WINDOWS { unsigned int first(0), last(0), length(0); int status; @@ -654,7 +654,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe switch (info) { case CURLINFO_TEXT: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "TEXT"; escape_libcurl_debug_data(buffer, len, true, safe_line); @@ -663,7 +663,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe break; case CURLINFO_HEADER_IN: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "HEADERIN"; escape_libcurl_debug_data(buffer, len, true, safe_line); @@ -672,20 +672,20 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe break; case CURLINFO_HEADER_OUT: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "HEADEROUT"; - escape_libcurl_debug_data(buffer, len, true, safe_line); + escape_libcurl_debug_data(buffer, 2 * len, true, safe_line); // Goes out as one line logit = true; } break; case CURLINFO_DATA_IN: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "DATAIN"; logit = true; - if (op->mTracing > 2) + if (op->mTracing >= TRACE_CURL_BODIES) { escape_libcurl_debug_data(buffer, len, false, safe_line); } @@ -699,11 +699,11 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe break; case CURLINFO_DATA_OUT: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "DATAOUT"; logit = true; - if (op->mTracing > 2) + if (op->mTracing >= TRACE_CURL_BODIES) { escape_libcurl_debug_data(buffer, len, false, safe_line); } @@ -755,22 +755,22 @@ int parse_content_range_header(char * buffer, if (! strtok_r(buffer, ": \t", &tok_state)) match = false; if (match && (tok = strtok_r(NULL, " \t", &tok_state))) -#if defined(WIN32) +#if LL_WINDOWS match = 0 == _stricmp("bytes", tok); #else match = 0 == strcasecmp("bytes", tok); -#endif +#endif // LL_WINDOWS if (match && ! (tok = strtok_r(NULL, " \t", &tok_state))) match = false; if (match) { unsigned int lcl_first(0), lcl_last(0), lcl_len(0); -#if defined(WIN32) +#if LL_WINDOWS if (3 == sscanf_s(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) #else if (3 == sscanf(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) -#endif +#endif // LL_WINDOWS { if (lcl_first > lcl_last || lcl_last >= lcl_len) return -1; @@ -779,11 +779,11 @@ int parse_content_range_header(char * buffer, *length = lcl_len; return 0; } -#if defined(WIN32) +#if LL_WINDOWS if (2 == sscanf_s(tok, "%u-%u/*", &lcl_first, &lcl_last)) #else if (2 == sscanf(tok, "%u-%u/*", &lcl_first, &lcl_last)) -#endif +#endif // LL_WINDOWS { if (lcl_first > lcl_last) return -1; @@ -798,14 +798,14 @@ int parse_content_range_header(char * buffer, return 1; } -#if defined(WIN32) +#if LL_WINDOWS char *strtok_r(char *str, const char *delim, char ** savestate) { return strtok_s(str, delim, savestate); } -#endif +#endif // LL_WINDOWS void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line) diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 9278445763..f2b709a3a2 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -172,6 +172,12 @@ public: }; // end class HttpOpRequestCompare +// --------------------------------------- +// Free functions +// --------------------------------------- + +curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); + } // end namespace LLCore #endif // _LLCORE_HTTP_OPREQUEST_H_ diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 0e08d88276..4be9f1d45f 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -126,7 +126,7 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) { int active(transport.getActiveCountInClass(policy_class)); - int needed(8 - active); + int needed(DEFAULT_CONNECTIONS - active); // *FIXME: move to policy class HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); @@ -242,7 +242,7 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) { - if (policy_class < HttpRequest::POLICY_CLASS_LIMIT) + if (policy_class < POLICY_CLASS_LIMIT) // *FIXME: use actual active class count { return (mState[policy_class].mReadyQueue.size() + mState[policy_class].mRetryQueue.size()); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 4114f64848..05de9303b5 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -33,6 +33,7 @@ #include "_httpreadyqueue.h" #include "_httpretryqueue.h" #include "_httppolicyglobal.h" +#include "_httpinternal.h" namespace LLCore @@ -108,7 +109,7 @@ protected: HttpRetryQueue mRetryQueue; }; - State mState[HttpRequest::POLICY_CLASS_LIMIT]; + State mState[POLICY_CLASS_LIMIT]; HttpService * mService; // Naked pointer, not refcounted, not owner HttpPolicyGlobal mGlobalOptions; diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index 6b1de38fd6..ca04839eaf 100644 --- a/indra/llcorehttp/_httppolicyglobal.cpp +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -26,6 +26,8 @@ #include "_httppolicyglobal.h" +#include "_httpinternal.h" + namespace LLCore { @@ -33,8 +35,8 @@ namespace LLCore HttpPolicyGlobal::HttpPolicyGlobal() : mSetMask(0UL), - mConnectionLimit(32L), - mTrace(0), + mConnectionLimit(DEFAULT_CONNECTIONS), + mTrace(TRACE_OFF), mUseLLProxy(0) {} @@ -64,11 +66,11 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) switch (opt) { case HttpRequest::GP_CONNECTION_LIMIT: - mConnectionLimit = value; + mConnectionLimit = llclamp(value, long(LIMIT_CONNECTIONS_MIN), long(LIMIT_CONNECTIONS_MAX)); break; case HttpRequest::GP_TRACE: - mTrace = llclamp(value, 0L, 3L); + mTrace = llclamp(value, long(TRACE_MIN), long(TRACE_MAX)); break; case HttpRequest::GP_LLPROXY: diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 87a78820f5..25f64acc42 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -34,15 +34,12 @@ #include "_httppolicy.h" #include "_httplibcurl.h" #include "_thread.h" +#include "_httpinternal.h" #include "lltimer.h" #include "llthread.h" -// Tuning parameters -static const int LOOP_SLEEP_NORMAL_MS = 2; // Normal per-loop sleep in milliseconds - - namespace LLCore { @@ -230,11 +227,11 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) if (! mExitRequested) { // Setup for subsequent tracing - long tracing(0); + long tracing(TRACE_OFF); mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing); op->mTracing = (std::max)(op->mTracing, int(tracing)); - if (op->mTracing > 0) + if (op->mTracing > TRACE_OFF) { LL_INFOS("CoreHttp") << "TRACE, FromRequestQueue, Handle: " << static_cast(op) diff --git a/indra/llcorehttp/_refcounted.cpp b/indra/llcorehttp/_refcounted.cpp index 11d75fdf97..e7d0b72741 100644 --- a/indra/llcorehttp/_refcounted.cpp +++ b/indra/llcorehttp/_refcounted.cpp @@ -30,11 +30,11 @@ namespace LLCoreInt { -#if ! defined(WIN32) +#if ! LL_WINDOWS const S32 RefCounted::NOT_REF_COUNTED; -#endif // ! defined(WIN32) +#endif // ! LL_WINDOWS RefCounted::~RefCounted() {} diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index ae92057df0..5eaa60c9ba 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -85,9 +85,9 @@ public: // ================================== -#if ! defined(WIN32) +#if ! LL_WINDOWS const size_t BufferArray::BLOCK_ALLOC_SIZE; -#endif // ! defined(WIN32) +#endif // ! LL_WINDOWS BufferArray::BufferArray() : LLCoreInt::RefCounted(true), diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index 9f17b5c842..1b18976359 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -54,9 +54,9 @@ std::string HttpStatus::toHex() const result.fill('0'); result << std::hex << operator unsigned long(); return result.str(); - } + std::string HttpStatus::toString() const { static const char * llcore_errors[] = diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp index c11d89e619..f2771c1f29 100644 --- a/indra/llcorehttp/httpoptions.cpp +++ b/indra/llcorehttp/httpoptions.cpp @@ -26,6 +26,8 @@ #include "httpoptions.h" +#include "_httpinternal.h" + namespace LLCore { @@ -34,9 +36,9 @@ namespace LLCore HttpOptions::HttpOptions() : RefCounted(true), mWantHeaders(false), - mTracing(0), - mTimeout(30), - mRetries(5) + mTracing(TRACE_OFF), + mTimeout(DEFAULT_TIMEOUT), + mRetries(DEFAULT_RETRY_COUNT) {} diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 2036ecfd1c..e906ff8a1e 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -108,6 +108,7 @@ HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::stri HttpRequest::policy_t HttpRequest::createPolicyClass() { + // *FIXME: Implement classes policy_t policy_id = 1; return policy_id; diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 4e78ed3719..24fff24b83 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -102,10 +102,6 @@ public: /// eventual service for any HTTP request. static const int DEFAULT_POLICY_ID = 0; - /// Maximum number of policies that may be defined. No policy - /// ID will equal or exceed this value. - static const int POLICY_CLASS_LIMIT = 1; - enum EGlobalPolicy { /// Maximum number of connections the library will use to @@ -163,7 +159,7 @@ public: /// Create a new policy class into which requests can be made. /// /// @return If positive, the policy_id used to reference - /// the class in other methods. If -1, an error + /// the class in other methods. If 0, an error /// occurred and @see getStatus() may provide more /// detail on the reason. static policy_t createPolicyClass(); diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index a4227ec2ff..ae2da84edd 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * 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 -- cgit v1.3 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/CMakeLists.txt | 3 + indra/llcorehttp/bufferarray.cpp | 14 ++ indra/llcorehttp/bufferarray.h | 8 + indra/llcorehttp/bufferstream.cpp | 285 +++++++++++++++++++++++++++ indra/llcorehttp/bufferstream.h | 94 +++++++++ indra/llcorehttp/tests/llcorehttp_test.cpp | 3 +- indra/llcorehttp/tests/test_bufferarray.hpp | 2 +- indra/llcorehttp/tests/test_bufferstream.hpp | 252 +++++++++++++++++++++++ 8 files changed, 659 insertions(+), 2 deletions(-) create mode 100644 indra/llcorehttp/bufferstream.cpp create mode 100644 indra/llcorehttp/bufferstream.h create mode 100644 indra/llcorehttp/tests/test_bufferstream.hpp (limited to 'indra/llcorehttp/bufferarray.cpp') 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 +#include + +#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 + +#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 052f985e7546aff2203773cf43c9a688c9c340ba Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 24 Jul 2012 15:21:28 -0400 Subject: Trivial change to tickle build. --- indra/llcorehttp/bufferarray.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'indra/llcorehttp/bufferarray.cpp') diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 5eb8f84c34..8eaaeed710 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -350,5 +350,3 @@ BufferArray::Block * BufferArray::Block::alloc(size_t len) } // end namespace LLCore - - -- cgit v1.3