Result of svn merge -r59958:60105 svn+ssh://svn/svn/linden/branches/discard-garbage into release

meow-7.2.2
Aaron Brashears 2007-04-05 20:11:55 +00:00
parent 8ffbffa33b
commit 2c29acbaf2
4 changed files with 388 additions and 45 deletions

View File

@ -65,24 +65,44 @@ S32 LLSegment::size() const
return mSize;
}
bool LLSegment::operator==(const LLSegment& rhs) const
{
if((mData != rhs.mData)||(mSize != rhs.mSize)||(mChannel != rhs.mChannel))
{
return false;
}
return true;
}
/**
* LLHeapBuffer
*/
LLHeapBuffer::LLHeapBuffer()
LLHeapBuffer::LLHeapBuffer() :
mBuffer(NULL),
mSize(0),
mNextFree(NULL),
mReclaimedBytes(0)
{
LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
const S32 DEFAULT_HEAP_BUFFER_SIZE = 16384;
allocate(DEFAULT_HEAP_BUFFER_SIZE);
}
LLHeapBuffer::LLHeapBuffer(S32 size)
LLHeapBuffer::LLHeapBuffer(S32 size) :
mBuffer(NULL),
mSize(0),
mNextFree(NULL),
mReclaimedBytes(0)
{
LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
allocate(size);
}
LLHeapBuffer::LLHeapBuffer(const U8* src, S32 len)
LLHeapBuffer::LLHeapBuffer(const U8* src, S32 len) :
mBuffer(NULL),
mSize(0),
mNextFree(NULL),
mReclaimedBytes(0)
{
LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
if((len > 0) && src)
@ -93,12 +113,6 @@ LLHeapBuffer::LLHeapBuffer(const U8* src, S32 len)
memcpy(mBuffer, src, len); /*Flawfinder: ignore*/
}
}
else
{
mBuffer = NULL;
mSize = 0;
mNextFree = NULL;
}
}
// virtual
@ -111,11 +125,10 @@ LLHeapBuffer::~LLHeapBuffer()
mNextFree = NULL;
}
// virtual
//S32 LLHeapBuffer::bytesLeft() const
//{
// return (mSize - (mNextFree - mBuffer));
//}
S32 LLHeapBuffer::bytesLeft() const
{
return (mSize - (mNextFree - mBuffer));
}
// virtual
bool LLHeapBuffer::createSegment(
@ -139,9 +152,47 @@ bool LLHeapBuffer::createSegment(
return true;
}
// virtual
bool LLHeapBuffer::reclaimSegment(const LLSegment& segment)
{
if(containsSegment(segment))
{
mReclaimedBytes += segment.size();
if(mReclaimedBytes == mSize)
{
// We have reclaimed all of the memory from this
// buffer. Therefore, we can reset the mNextFree to the
// start of the buffer, and reset the reclaimed bytes.
mReclaimedBytes = 0;
mNextFree = mBuffer;
}
else if(mReclaimedBytes > mSize)
{
llwarns << "LLHeapBuffer reclaimed more memory than allocated."
<< " This is probably programmer error." << llendl;
}
return true;
}
return false;
}
// virtual
bool LLHeapBuffer::containsSegment(const LLSegment& segment) const
{
// *NOTE: this check is fairly simple because heap buffers are
// simple contiguous chunks of heap memory.
if((mBuffer > segment.data())
|| ((mBuffer + mSize) < (segment.data() + segment.size())))
{
return false;
}
return true;
}
void LLHeapBuffer::allocate(S32 size)
{
LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
mReclaimedBytes = 0;
mBuffer = new U8[size];
if(mBuffer)
{
@ -180,6 +231,18 @@ LLChannelDescriptors LLBufferArray::nextChannel()
return rv;
}
S32 LLBufferArray::capacity() const
{
S32 total = 0;
const_buffer_iterator_t iter = mBuffers.begin();
const_buffer_iterator_t end = mBuffers.end();
for(; iter != end; ++iter)
{
total += (*iter)->capacity();
}
return total;
}
bool LLBufferArray::append(S32 channel, const U8* src, S32 len)
{
LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
@ -684,14 +747,31 @@ LLBufferArray::segment_iterator_t LLBufferArray::makeSegment(
return send;
}
bool LLBufferArray::eraseSegment(const segment_iterator_t& iter)
bool LLBufferArray::eraseSegment(const segment_iterator_t& erase_iter)
{
LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
// *FIX: in theory, we could reclaim the memory. We are leaking a
// bit of buffered memory into an unusable but still referenced
// location.
(void)mSegments.erase(iter);
return true;
// Find out which buffer contains the segment, and if it is found,
// ask it to reclaim the memory.
bool rv = false;
LLSegment segment(*erase_iter);
buffer_iterator_t iter = mBuffers.begin();
buffer_iterator_t end = mBuffers.end();
for(; iter != end; ++iter)
{
// We can safely call reclaimSegment on every buffer, and once
// it returns true, the segment was found.
if((*iter)->reclaimSegment(segment))
{
rv = true;
break;
}
}
// No need to get the return value since we are not interested in
// the interator retured by the call.
(void)mSegments.erase(erase_iter);
return rv;
}

View File

@ -13,7 +13,7 @@
/**
* Declaration of classes used for minimizing calls to new[],
* memcpy(), and delete[]. Typically, you would create an LLHeapArray,
* memcpy(), and delete[]. Typically, you would create an LLBufferArray,
* feed it data, modify and add segments as you process it, and feed
* it to a sink.
*/
@ -89,6 +89,16 @@ public:
*/
S32 size() const;
/**
* @brief Check if two segments are the same.
*
* Two segments are considered equal if they are on the same
* channel and cover the exact same address range.
* @param rhs the segment to compare with this segment.
* @return Returns true if they are equal.
*/
bool operator==(const LLSegment& rhs) const;
protected:
S32 mChannel;
U8* mData;
@ -126,6 +136,35 @@ public:
* @return Returns true if a segment was found.
*/
virtual bool createSegment(S32 channel, S32 size, LLSegment& segment) = 0;
/**
* @brief Reclaim a segment from this buffer.
*
* This method is called on a buffer object when a caller is done
* with a contiguous segment of memory inside this buffer. Since
* segments can be cut arbitrarily outside of the control of the
* buffer, this segment may not match any segment returned from
* <code>createSegment()</code>.
* @param segment The contiguous buffer segment to reclaim.
* @return Returns true if the call was successful.
*/
virtual bool reclaimSegment(const LLSegment& segment) = 0;
/**
* @brief Test if a segment is inside this buffer.
*
* @param segment The contiguous buffer segment to test.
* @return Returns true if the segment is in the bufffer.
*/
virtual bool containsSegment(const LLSegment& segment) const = 0;
/**
* @brief Return the current number of bytes allocated.
*
* This was implemented as a debugging tool, and it is not
* necessarily a good idea to use it for anything else.
*/
virtual S32 capacity() const = 0;
};
/**
@ -167,9 +206,11 @@ public:
/**
* @brief Get the number of bytes left in the buffer.
*
* Note that this is not a virtual function, and only available in
* the LLHeapBuffer as a debugging aid.
* @return Returns the number of bytes left.
*/
//virtual S32 bytesLeft() const;
S32 bytesLeft() const;
/**
* @brief Generate a segment for this buffer.
@ -186,10 +227,40 @@ public:
*/
virtual bool createSegment(S32 channel, S32 size, LLSegment& segment);
/**
* @brief reclaim a segment from this buffer.
*
* This method is called on a buffer object when a caller is done
* with a contiguous segment of memory inside this buffer. Since
* segments can be cut arbitrarily outside of the control of the
* buffer, this segment may not match any segment returned from
* <code>createSegment()</code>.
* This call will fail if the segment passed in is note completely
* inside the buffer, eg, if the segment starts before this buffer
* in memory or ends after it.
* @param segment The contiguous buffer segment to reclaim.
* @return Returns true if the call was successful.
*/
virtual bool reclaimSegment(const LLSegment& segment);
/**
* @brief Test if a segment is inside this buffer.
*
* @param segment The contiguous buffer segment to test.
* @return Returns true if the segment is in the bufffer.
*/
virtual bool containsSegment(const LLSegment& segment) const;
/**
* @brief Return the current number of bytes allocated.
*/
virtual S32 capacity() const { return mSize; }
protected:
U8* mBuffer;
S32 mSize;
U8* mNextFree;
S32 mReclaimedBytes;
private:
/**
@ -204,13 +275,14 @@ private:
* @brief Class to represent scattered memory buffers and in-order segments
* of that buffered data.
*
* NOTE: This class needs to have an iovec interface
* *NOTE: This class needs to have an iovec interface
*/
class LLBufferArray
{
public:
typedef std::vector<LLBuffer*> buffer_list_t;
typedef buffer_list_t::iterator buffer_iterator_t;
typedef buffer_list_t::const_iterator const_buffer_iterator_t;
typedef std::list<LLSegment> segment_list_t;
typedef segment_list_t::const_iterator const_segment_iterator_t;
typedef segment_list_t::iterator segment_iterator_t;
@ -241,11 +313,16 @@ public:
*/
LLChannelDescriptors nextChannel();
//@}
/* @name Data methods
*/
//@{
/**
* @brief Return the sum of all allocated bytes.
*/
S32 capacity() const;
// These methods will be useful once there is any kind of buffer
// besides a heap buffer.
//bool append(EBufferChannel channel, LLBuffer* data);
@ -275,7 +352,6 @@ public:
* new segment is created and put in the front of the array. This
* object will internally allocate new buffers if necessary.
* @param channel The channel for this data
* @param src The start of memory for the data to be copied
* @param len The number of bytes of data to copy
* @return Returns true if the method worked.
@ -359,7 +435,7 @@ public:
bool takeContents(LLBufferArray& source);
//@}
/* @name Segment methods
/* @name Segment methods
*/
//@{
/**
@ -449,7 +525,7 @@ public:
* endSegment() on failure.
*/
segment_iterator_t makeSegment(S32 channel, S32 length);
/**
* @brief Erase the segment if it is in the buffer array.
*

View File

@ -43,34 +43,64 @@ int LLBufferStreamBuf::underflow()
{
return EOF;
}
LLSegment segment;
LLBufferArray::segment_iterator_t it;
U8* last_pos = (U8*)gptr();
if(last_pos) --last_pos;
LLBufferArray::segment_iterator_t end = mBuffer->endSegment();
// Get iterator to full segment containing last_pos
// and construct sub-segment starting at last_pos.
// Note: segment may != *it at this point
it = mBuffer->constructSegmentAfter(last_pos, segment);
if(it == end)
LLBufferArray::segment_iterator_t iter;
LLBufferArray::segment_iterator_t end = mBuffer->endSegment();
U8* last_pos = (U8*)gptr();
LLSegment segment;
if(last_pos)
{
// Back up into a piece of memory we know that we have
// allocated so that calls for the next segment based on
// 'after' will succeed.
--last_pos;
iter = mBuffer->splitAfter(last_pos);
if(iter != end)
{
// We need to clear the read segment just in case we have
// an early exit in the function and never collect the
// next segment. Calling eraseSegment() with the same
// segment twice is just like double deleting -- nothing
// good comes from it.
mBuffer->eraseSegment(iter++);
if(iter != end) segment = (*iter);
}
else
{
// This should never really happen, but somehow, the
// istream is telling the buf that it just finished
// reading memory that is not in the buf. I think this
// would only happen if there were a bug in the c++ stream
// class. Just bail.
// *TODO: can we set the fail bit on the stream somehow?
return EOF;
}
}
else
{
// Get iterator to full segment containing last_pos
// and construct sub-segment starting at last_pos.
// Note: segment may != *it at this point
iter = mBuffer->constructSegmentAfter(last_pos, segment);
}
if(iter == end)
{
return EOF;
}
// Iterate through segments to find a non-empty segment on input channel.
while((!segment.isOnChannel(mChannels.in()) || (segment.size() == 0)))
{
++it;
if(it == end)
++iter;
if(iter == end)
{
return EOF;
}
segment = *it;
segment = *(iter);
}
// set up the stream to read from the next segment.
char* start = (char*)segment.data();
setg(start, start, start + segment.size());
return *gptr();
@ -125,12 +155,43 @@ int LLBufferStreamBuf::sync()
return return_value;
}
// This chunk of code is not necessary because typically, users of
// the stream will read until EOF. Therefore, underflow was called
// and the segment was discarded before the sync() was called in
// the destructor. Theoretically, we could keep some more data
// around and detect the rare case where an istream was deleted
// before reading to the end, but that will only leave behind some
// unavailable but still referenced memory. Also, if another
// istream is constructed, it will re-read that segment, and then
// discard it.
//U8* last_pos = (U8*)gptr();
//if(last_pos)
//{
// // Looks like we read something. Discard what we have read.
// // gptr() actually returns the currrent position, but we call
// // it last_pos because of how it is used in the split call
// // below.
// --last_pos;
// LLBufferArray::segment_iterator_t iter;
// iter = mBuffer->splitAfter(last_pos);
// if(iter != mBuffer->endSegment())
// {
// // We need to clear the read segment just in case we have
// // an early exit in the function and never collect the
// // next segment. Calling eraseSegment() with the same
// // segment twice is just like double deleting -- nothing
// // good comes from it.
// mBuffer->eraseSegment(iter);
// }
//}
// set the put pointer so that we force an overflow on the next
// write.
U8* address = (U8*)pptr();
setp(NULL, NULL);
// *NOTE: I bet we could just --address. Need to think about that.
// *NOTE: I bet we could just --address if address is not NULL.
// Need to think about that.
address = mBuffer->seek(mChannels.out(), address, -1);
if(address)
{

View File

@ -29,6 +29,101 @@
#include "lluuid.h"
#include "llinstantmessage.h"
namespace tut
{
struct heap_buffer_data
{
heap_buffer_data() : mBuffer(NULL) {}
~heap_buffer_data() { if(mBuffer) delete mBuffer; }
LLHeapBuffer* mBuffer;
};
typedef test_group<heap_buffer_data> heap_buffer_test;
typedef heap_buffer_test::object heap_buffer_object;
tut::heap_buffer_test thb("heap_buffer");
template<> template<>
void heap_buffer_object::test<1>()
{
const S32 BUF_SIZE = 100;
mBuffer = new LLHeapBuffer(BUF_SIZE);
ensure_equals("empty buffer capacity", mBuffer->capacity(), BUF_SIZE);
const S32 SEGMENT_SIZE = 50;
LLSegment segment;
mBuffer->createSegment(0, SEGMENT_SIZE, segment);
ensure_equals("used buffer capacity", mBuffer->capacity(), BUF_SIZE);
}
template<> template<>
void heap_buffer_object::test<2>()
{
const S32 BUF_SIZE = 10;
mBuffer = new LLHeapBuffer(BUF_SIZE);
LLSegment segment;
mBuffer->createSegment(0, BUF_SIZE, segment);
ensure("segment is in buffer", mBuffer->containsSegment(segment));
ensure_equals("buffer consumed", mBuffer->bytesLeft(), 0);
bool created;
created = mBuffer->createSegment(0, 0, segment);
ensure("Create zero size segment fails", !created);
created = mBuffer->createSegment(0, BUF_SIZE, segment);
ensure("Create segment fails", !created);
}
template<> template<>
void heap_buffer_object::test<3>()
{
const S32 BUF_SIZE = 10;
mBuffer = new LLHeapBuffer(BUF_SIZE);
LLSegment segment;
mBuffer->createSegment(0, BUF_SIZE, segment);
ensure("segment is in buffer", mBuffer->containsSegment(segment));
ensure_equals("buffer consumed", mBuffer->bytesLeft(), 0);
bool reclaimed = mBuffer->reclaimSegment(segment);
ensure("buffer reclaimed.", reclaimed);
ensure_equals("buffer available", mBuffer->bytesLeft(), BUF_SIZE);
bool created;
created = mBuffer->createSegment(0, 0, segment);
ensure("Create zero size segment fails", !created);
created = mBuffer->createSegment(0, BUF_SIZE, segment);
ensure("Create another segment succeeds", created);
}
template<> template<>
void heap_buffer_object::test<4>()
{
const S32 BUF_SIZE = 10;
const S32 SEGMENT_SIZE = 4;
mBuffer = new LLHeapBuffer(BUF_SIZE);
LLSegment seg1;
mBuffer->createSegment(0, SEGMENT_SIZE, seg1);
ensure("segment is in buffer", mBuffer->containsSegment(seg1));
LLSegment seg2;
mBuffer->createSegment(0, SEGMENT_SIZE, seg2);
ensure("segment is in buffer", mBuffer->containsSegment(seg2));
LLSegment seg3;
mBuffer->createSegment(0, SEGMENT_SIZE, seg3);
ensure("segment is in buffer", mBuffer->containsSegment(seg3));
ensure_equals("segment is truncated", seg3.size(), 2);
LLSegment seg4;
bool created;
created = mBuffer->createSegment(0, SEGMENT_SIZE, seg4);
ensure("Create segment fails", !created);
bool reclaimed;
reclaimed = mBuffer->reclaimSegment(seg1);
ensure("buffer reclaim succeed.", reclaimed);
ensure_equals("no buffer available", mBuffer->bytesLeft(), 0);
reclaimed = mBuffer->reclaimSegment(seg2);
ensure("buffer reclaim succeed.", reclaimed);
ensure_equals("buffer reclaimed", mBuffer->bytesLeft(), 0);
reclaimed = mBuffer->reclaimSegment(seg3);
ensure("buffer reclaim succeed.", reclaimed);
ensure_equals("buffer reclaimed", mBuffer->bytesLeft(), BUF_SIZE);
created = mBuffer->createSegment(0, SEGMENT_SIZE, seg1);
ensure("segment is in buffer", mBuffer->containsSegment(seg1));
ensure("Create segment succeds", created);
}
}
namespace tut
{
struct buffer_data
@ -209,6 +304,37 @@ namespace tut
delete[] temp;
}
template<> template<>
void buffer_object::test<9>()
{
LLChannelDescriptors ch = mBuffer.nextChannel();
mBuffer.append(ch.in(), (U8*)"1", 1);
S32 capacity = mBuffer.capacity();
ensure("has capacity", capacity > 0);
U8* temp = new U8[capacity - 1];
mBuffer.append(ch.in(), temp, capacity - 1);
capacity = mBuffer.capacity();
ensure("has capacity when full", capacity > 0);
S32 used = mBuffer.countAfter(ch.in(), NULL);
ensure_equals("used equals capacity", used, capacity);
LLBufferArray::segment_iterator_t iter = mBuffer.beginSegment();
while(iter != mBuffer.endSegment())
{
mBuffer.eraseSegment(iter++);
}
used = mBuffer.countAfter(ch.in(), NULL);
ensure_equals("used is zero", used, 0);
S32 capacity2 = mBuffer.capacity();
ensure_equals("capacity the same after erase", capacity2, capacity);
mBuffer.append(ch.in(), temp, capacity - 1);
capacity2 = mBuffer.capacity();
ensure_equals("capacity the same after append", capacity2, capacity);
delete[] temp;
}
#if 0
template<> template<>
void buffer_object::test<9>()
@ -268,7 +394,7 @@ namespace tut
void bas_object::test<1>()
{
const char HELLO_WORLD[] = "hello world";
const S32 str_len = strlen(HELLO_WORLD); /* Flawfinder: ignore */
const S32 str_len = strlen(HELLO_WORLD); /* Flawfinder: ignore */
LLChannelDescriptors ch = mBuffer.nextChannel();
LLBufferStream str(ch, &mBuffer);
mBuffer.append(ch.in(), (U8*)HELLO_WORLD, str_len);