123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787 |
- /**
- * @file llbuffer.cpp
- * @author Phoenix
- * @date 2005-09-20
- * @brief Implementation of the segments, buffers, and buffer arrays.
- *
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include <iterator> // for VS2010
- #include "llbuffer.h"
- #include "llmath.h"
- #include "llstl.h"
- ///////////////////////////////////////////////////////////////////////////////
- // LLHeapBuffer class
- ///////////////////////////////////////////////////////////////////////////////
- LLHeapBuffer::LLHeapBuffer()
- : mBuffer(NULL),
- mSize(0),
- mNextFree(NULL),
- mReclaimedBytes(0)
- {
- constexpr S32 DEFAULT_HEAP_BUFFER_SIZE = 16384;
- allocate(DEFAULT_HEAP_BUFFER_SIZE);
- }
- LLHeapBuffer::LLHeapBuffer(S32 size)
- : mBuffer(NULL),
- mSize(0),
- mNextFree(NULL),
- mReclaimedBytes(0)
- {
- allocate(size);
- }
- LLHeapBuffer::LLHeapBuffer(const U8* src, S32 len)
- : mBuffer(NULL),
- mSize(0),
- mNextFree(NULL),
- mReclaimedBytes(0)
- {
- if (len > 0 && src)
- {
- allocate(len);
- if (mBuffer)
- {
- memcpy(mBuffer, src, len);
- }
- }
- }
- // virtual
- LLHeapBuffer::~LLHeapBuffer()
- {
- delete[] mBuffer;
- mBuffer = NULL;
- mSize = 0;
- mNextFree = NULL;
- }
- S32 LLHeapBuffer::bytesLeft() const
- {
- return (mSize - (mNextFree - mBuffer));
- }
- // virtual
- bool LLHeapBuffer::createSegment(S32 channel, S32 size, LLSegment& segment)
- {
- // get actual size of the segment.
- S32 actual_size = llmin(size, (mSize - S32(mNextFree - mBuffer)));
- // bail if we cannot build a valid segment
- if (actual_size <= 0)
- {
- return false;
- }
- // Yay, we're done.
- segment = LLSegment(channel, mNextFree, actual_size);
- mNextFree += actual_size;
- 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)
- {
- mReclaimedBytes = 0;
- mBuffer = new U8[size];
- if (mBuffer)
- {
- mSize = size;
- mNextFree = mBuffer;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLBufferArray class
- ///////////////////////////////////////////////////////////////////////////////
- LLBufferArray::LLBufferArray()
- : mNextBaseChannel(0),
- mMutexp(NULL)
- {
- }
- LLBufferArray::~LLBufferArray()
- {
- std::for_each(mBuffers.begin(), mBuffers.end(), DeletePointer());
- mBuffers.clear();
- if (mMutexp)
- {
- delete mMutexp;
- }
- }
- // static
- LLChannelDescriptors LLBufferArray::makeChannelConsumer(const LLChannelDescriptors& channels)
- {
- LLChannelDescriptors rv(channels.out());
- return rv;
- }
- void LLBufferArray::setThreaded(bool threaded)
- {
- if (threaded)
- {
- if (!mMutexp)
- {
- mMutexp = new LLMutex();
- }
- }
- else if (mMutexp)
- {
- delete mMutexp;
- mMutexp = NULL;
- }
- }
- LLChannelDescriptors LLBufferArray::nextChannel()
- {
- LLChannelDescriptors rv(mNextBaseChannel++);
- return rv;
- }
- // mMutexp should be locked before calling this.
- S32 LLBufferArray::capacity() const
- {
- S32 total = 0;
- for (const_buffer_iterator_t iter = mBuffers.begin(), end = mBuffers.end();
- iter != end; ++iter)
- {
- total += (*iter)->capacity();
- }
- return total;
- }
- bool LLBufferArray::append(S32 channel, const U8* src, S32 len)
- {
- LLMutexLock lock(mMutexp);
- std::vector<LLSegment> segments;
- if (copyIntoBuffers(channel, src, len, segments))
- {
- mSegments.insert(mSegments.end(), segments.begin(), segments.end());
- return true;
- }
- return false;
- }
- // mMutexp should be locked before calling this.
- bool LLBufferArray::prepend(S32 channel, const U8* src, S32 len)
- {
- std::vector<LLSegment> segments;
- if (copyIntoBuffers(channel, src, len, segments))
- {
- mSegments.insert(mSegments.begin(), segments.begin(), segments.end());
- return true;
- }
- return false;
- }
- bool LLBufferArray::insertAfter(segment_iterator_t segment, S32 channel,
- const U8* src, S32 len)
- {
- std::vector<LLSegment> segments;
- LLMutexLock lock(mMutexp);
- if (mSegments.end() != segment)
- {
- ++segment;
- }
- if (copyIntoBuffers(channel, src, len, segments))
- {
- mSegments.insert(segment, segments.begin(), segments.end());
- return true;
- }
- return false;
- }
- // mMutexp should be locked before calling this.
- LLBufferArray::segment_iterator_t LLBufferArray::splitAfter(U8* address)
- {
- segment_iterator_t end = mSegments.end();
- segment_iterator_t it = getSegment(address);
- if (it == end)
- {
- return end;
- }
- // We have the location and the segment.
- U8* base = it->data();
- S32 size = it->size();
- if (address == base + size)
- {
- // No need to split, since this is the last byte of the segment. We do
- // not want to have zero length segments, since that will only incur
- // processing overhead with no advantage.
- return it;
- }
- S32 channel = it->getChannel();
- LLSegment segment1(channel, base, address - base + 1);
- *it = segment1;
- segment_iterator_t rv = it++;
- LLSegment segment2(channel, address + 1, size - (address - base) - 1);
- mSegments.insert(it, segment2);
- return rv;
- }
- // mMutexp should be locked before calling this.
- LLBufferArray::segment_iterator_t LLBufferArray::beginSegment()
- {
- return mSegments.begin();
- }
- // mMutexp should be locked before calling this.
- LLBufferArray::segment_iterator_t LLBufferArray::endSegment()
- {
- return mSegments.end();
- }
- // mMutexp should be locked before calling this.
- LLBufferArray::segment_iterator_t LLBufferArray::constructSegmentAfter(U8* address,
- LLSegment& segment)
- {
- segment_iterator_t rv = mSegments.begin();
- segment_iterator_t end = mSegments.end();
- if (!address)
- {
- if (rv != end)
- {
- segment = *rv;
- }
- }
- else
- {
- // we have an address - find the segment it is in.
- for ( ; rv != end; ++rv)
- {
- if (address >= rv->data() && address < rv->data() + rv->size())
- {
- if (++address < rv->data() + rv->size())
- {
- // it's in this segment - construct an appropriate
- // sub-segment.
- segment = LLSegment(rv->getChannel(), address,
- rv->size() - (address - rv->data()));
- }
- else
- {
- ++rv;
- if (rv != end)
- {
- segment = *rv;
- }
- }
- break;
- }
- }
- }
- if (rv == end)
- {
- segment = LLSegment();
- }
- return rv;
- }
- // mMutexp should be locked before calling this.
- LLBufferArray::segment_iterator_t LLBufferArray::getSegment(U8* address)
- {
- segment_iterator_t end = mSegments.end();
- if (!address)
- {
- return end;
- }
- segment_iterator_t it = mSegments.begin();
- for ( ; it != end; ++it)
- {
- if (address >= it->data() && address < it->data() + it->size())
- {
- // found it.
- return it;
- }
- }
- return end;
- }
- // mMutexp should be locked before calling this.
- LLBufferArray::const_segment_iterator_t LLBufferArray::getSegment(U8* address) const
- {
- const_segment_iterator_t end = mSegments.end();
- if (!address)
- {
- return end;
- }
- const_segment_iterator_t it = mSegments.begin();
- for ( ; it != end; ++it)
- {
- if (address >= it->data() && address < it->data() + it->size())
- {
- // found it.
- return it;
- }
- }
- return end;
- }
- #if 0
- U8* LLBufferArray::getAddressAfter(U8* address)
- {
- U8* rv = NULL;
- segment_iterator_t it = getSegment(address);
- segment_iterator_t end = mSegments.end();
- if (it != end)
- {
- if (++address < it->data() + it->size())
- {
- // it's in the same segment
- rv = address;
- }
- else
- {
- // it's in the next segment
- if (++it != end)
- {
- rv = it->data();
- }
- }
- }
- return rv;
- }
- #endif
- S32 LLBufferArray::countAfter(S32 channel, U8* start) const
- {
- S32 count = 0;
- S32 offset = 0;
- const_segment_iterator_t it;
- LLMutexLock lock(mMutexp);
- const_segment_iterator_t end = mSegments.end();
- if (start)
- {
- it = getSegment(start);
- if (it == end)
- {
- return count;
- }
- if (++start < it->data() + it->size())
- {
- // it's in the same segment
- offset = start - it->data();
- }
- else if (++it == end)
- {
- // it's in the next segment
- return count;
- }
- }
- else
- {
- it = mSegments.begin();
- }
- while (it != end)
- {
- if (it->isOnChannel(channel))
- {
- count += it->size() - offset;
- }
- offset = 0;
- ++it;
- }
- return count;
- }
- U8* LLBufferArray::readAfter(S32 channel, U8* start, U8* dest, S32& len) const
- {
- U8* rv = start;
- if (!dest || len <= 0)
- {
- return rv;
- }
- S32 bytes_left = len;
- len = 0;
- S32 bytes_to_copy = 0;
- const_segment_iterator_t it;
- LLMutexLock lock(mMutexp);
- const_segment_iterator_t end = mSegments.end();
- if (start)
- {
- it = getSegment(start);
- if (it == end)
- {
- return rv;
- }
- if (++start < it->data() + it->size() && it->isOnChannel(channel))
- {
- // copy the data out of this segment
- S32 bytes_in_segment = it->size() - (start - it->data());
- bytes_to_copy = llmin(bytes_left, bytes_in_segment);
- memcpy(dest, start, bytes_to_copy);
- len += bytes_to_copy;
- bytes_left -= bytes_to_copy;
- rv = start + bytes_to_copy - 1;
- ++it;
- }
- else
- {
- ++it;
- }
- }
- else
- {
- it = mSegments.begin();
- }
- while (bytes_left && it != end)
- {
- if (!it->isOnChannel(channel))
- {
- ++it;
- continue;
- }
- bytes_to_copy = llmin(bytes_left, it->size());
- memcpy(dest + len, it->data(), bytes_to_copy);
- len += bytes_to_copy;
- bytes_left -= bytes_to_copy;
- rv = it->data() + bytes_to_copy - 1;
- ++it;
- }
- return rv;
- }
- U8* LLBufferArray::seek(S32 channel, U8* start, S32 delta) const
- {
- const_segment_iterator_t it;
- const_segment_iterator_t end = mSegments.end();
- U8* rv = start;
- if (0 == delta)
- {
- if ((U8*)npos == start)
- {
- // someone is looking for end of data.
- segment_list_t::const_reverse_iterator rit = mSegments.rbegin();
- segment_list_t::const_reverse_iterator rend = mSegments.rend();
- while (rit != rend)
- {
- if (!rit->isOnChannel(channel))
- {
- ++rit;
- continue;
- }
- rv = rit->data() + rit->size();
- break;
- }
- }
- else if (start)
- {
- // This is sort of a weird case - check if zero bytes away from
- // current position is on channel and return start if that is true.
- // Otherwise, return NULL.
- it = getSegment(start);
- if (it == end || !it->isOnChannel(channel))
- {
- rv = NULL;
- }
- }
- else
- {
- // Start is NULL, so return the very first byte on the channel, or
- // NULL.
- it = mSegments.begin();
- while (it != end && !it->isOnChannel(channel))
- {
- ++it;
- }
- if (it != end)
- {
- rv = it->data();
- }
- }
- return rv;
- }
- if (start)
- {
- it = getSegment(start);
- if (it != end && it->isOnChannel(channel))
- {
- if (delta > 0)
- {
- S32 bytes_in_segment = it->size() - (start - it->data());
- S32 local_delta = llmin(delta, bytes_in_segment);
- rv += local_delta;
- delta -= local_delta;
- ++it;
- }
- else
- {
- S32 bytes_in_segment = start - it->data();
- S32 local_delta = llmin(abs(delta), bytes_in_segment);
- rv -= local_delta;
- delta += local_delta;
- }
- }
- }
- else if (delta < 0)
- {
- // start is NULL, and delta indicates seeking backwards - return NULL.
- return NULL;
- }
- else
- {
- // start is NULL and delta > 0
- it = mSegments.begin();
- }
- if (delta > 0)
- {
- // At this point, we have an iterator into the segments, and are
- // seeking forward until delta is zero or we run out
- while (delta && it != end)
- {
- if (!it->isOnChannel(channel))
- {
- ++it;
- continue;
- }
- if (delta <= it->size())
- {
- // it's in this segment
- rv = it->data() + delta;
- }
- delta -= it->size();
- ++it;
- }
- if (delta && it == end)
- {
- // Whoops - sought past end.
- rv = NULL;
- }
- }
- else //if (delta < 0)
- {
- // We are at the beginning of a segment, and need to search backwards.
- segment_list_t::const_reverse_iterator rit(it);
- segment_list_t::const_reverse_iterator rend = mSegments.rend();
- while (delta && rit != rend)
- {
- if (!rit->isOnChannel(channel))
- {
- ++rit;
- continue;
- }
- if (abs(delta) <= rit->size())
- {
- // it's in this segment.
- rv = rit->data() + rit->size() + delta;
- delta = 0;
- }
- else
- {
- delta += rit->size();
- }
- ++rit;
- }
- if (delta && rit == rend)
- {
- // sought past the beginning.
- rv = NULL;
- }
- }
- return rv;
- }
- // test use only
- bool LLBufferArray::takeContents(LLBufferArray& source)
- {
- LLMutexLock lock(mMutexp);
- source.lock();
- std::copy(source.mBuffers.begin(), source.mBuffers.end(),
- std::back_insert_iterator<buffer_list_t>(mBuffers));
- source.mBuffers.clear();
- std::copy(source.mSegments.begin(), source.mSegments.end(),
- std::back_insert_iterator<segment_list_t>(mSegments));
- source.mSegments.clear();
- source.mNextBaseChannel = 0;
- source.unlock();
- return true;
- }
- // mMutexp should be locked before calling this.
- LLBufferArray::segment_iterator_t LLBufferArray::makeSegment(S32 channel,
- S32 len)
- {
- // Start at the end of the buffers, because it is the most likely to have
- // free space.
- LLSegment segment;
- buffer_list_t::reverse_iterator it = mBuffers.rbegin();
- buffer_list_t::reverse_iterator end = mBuffers.rend();
- bool made_segment = false;
- for ( ; it != end; ++it)
- {
- if ((*it)->createSegment(channel, len, segment))
- {
- made_segment = true;
- break;
- }
- }
- segment_iterator_t send = mSegments.end();
- if (!made_segment)
- {
- LLBuffer* buf = new LLHeapBuffer;
- mBuffers.push_back(buf);
- if (!buf->createSegment(channel, len, segment))
- {
- // failed. this should never happen.
- return send;
- }
- }
- // store and return the newly made segment
- mSegments.insert(send, segment);
- std::list<LLSegment>::reverse_iterator rv = mSegments.rbegin();
- ++rv;
- send = rv.base();
- return send;
- }
- // mMutexp should be locked before calling this.
- bool LLBufferArray::eraseSegment(const segment_iterator_t& erase_iter)
- {
- // 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;
- }
- // mMutexp should be locked before calling this.
- bool LLBufferArray::copyIntoBuffers(S32 channel, const U8* src, S32 len,
- std::vector<LLSegment>& segments)
- {
- if (!src || !len) return false;
- S32 copied = 0;
- LLSegment segment;
- buffer_iterator_t it = mBuffers.begin();
- buffer_iterator_t end = mBuffers.end();
- for ( ; it != end;)
- {
- if (!(*it)->createSegment(channel, len, segment))
- {
- ++it;
- continue;
- }
- segments.push_back(segment);
- S32 bytes = llmin(segment.size(), len);
- memcpy(segment.data(), src + copied, bytes);
- copied += bytes;
- len -= bytes;
- if (0 == len)
- {
- break;
- }
- }
- while (len)
- {
- LLBuffer* buf = new LLHeapBuffer;
- mBuffers.push_back(buf);
- if (!buf->createSegment(channel, len, segment))
- {
- // This totally failed - bail. This is the weird corner case were
- // we 'leak' memory. No worries about an actual leak - we will
- // still reclaim the memory later, but this particular buffer array
- // is hosed for some reason. This should never happen.
- return false;
- }
- segments.push_back(segment);
- memcpy(segment.data(), src + copied, segment.size());
- copied += segment.size();
- len -= segment.size();
- }
- return true;
- }
|