123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- /**
- * @file llbufferstream.cpp
- * @author Phoenix
- * @date 2005-10-10
- * @brief Implementation of the buffer iostream classes
- *
- * $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 "llbufferstream.h"
- #include "llbuffer.h"
- #include "llthread.h"
- constexpr S32 DEFAULT_OUTPUT_SEGMENT_SIZE = 1024 * 4;
- /*
- * LLBufferStreamBuf
- */
- LLBufferStreamBuf::LLBufferStreamBuf(const LLChannelDescriptors& channels,
- LLBufferArray* buffer)
- : mChannels(channels),
- mBuffer(buffer)
- {
- }
- LLBufferStreamBuf::~LLBufferStreamBuf()
- {
- sync();
- }
- // virtual
- int LLBufferStreamBuf::underflow()
- {
- if (!mBuffer)
- {
- return EOF;
- }
- LLMutexLock lock(mBuffer->getMutex());
- 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 != *iter 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.size() == 0 || !segment.isOnChannel(mChannels.in()))
- {
- ++iter;
- if (iter == end)
- {
- return EOF;
- }
- 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();
- }
- // virtual
- int LLBufferStreamBuf::overflow(int c)
- {
- if (!mBuffer)
- {
- return EOF;
- }
- if (EOF == c)
- {
- // If someone puts an EOF, I suppose we should sync and return
- // success.
- if (sync() == 0)
- {
- return 1;
- }
- else
- {
- return EOF;
- }
- }
- // Since we got here, we have a buffer and a character to put on it
- LLMutexLock lock(mBuffer->getMutex());
- LLBufferArray::segment_iterator_t it =
- mBuffer->makeSegment(mChannels.out(), DEFAULT_OUTPUT_SEGMENT_SIZE);
- if (it != mBuffer->endSegment())
- {
- char* start = (char*)(*it).data();
- (*start) = (char)(c);
- setp(start + 1, start + (*it).size());
- return c;
- }
- else
- {
- return EOF;
- }
- }
- // virtual
- int LLBufferStreamBuf::sync()
- {
- int return_value = -1;
- if (!mBuffer)
- {
- 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.
- #if 0
- 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 = 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);
- }
- }
- #endif
- // Set the put pointer so that we force an overflow on the next write.
- U8* address = (U8*)pptr();
- setp(NULL, NULL);
- LLMutexLock lock(mBuffer->getMutex());
- // *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)
- {
- LLBufferArray::segment_iterator_t it = mBuffer->splitAfter(address);
- LLBufferArray::segment_iterator_t end = mBuffer->endSegment();
- if (it != end)
- {
- ++it;
- if (it != end)
- {
- mBuffer->eraseSegment(it);
- }
- return_value = 0;
- }
- }
- else
- {
- // Nothing was put on the buffer, so the sync() is a no-op.
- return_value = 0;
- }
- return return_value;
- }
- //virtual
- LLBufferStreamBuf::pos_type LLBufferStreamBuf::seekoff(LLBufferStreamBuf::off_type off,
- std::ios::seekdir way,
- std::ios::openmode which)
- {
- if (!mBuffer || (way == std::ios::beg && off < 0) ||
- (way == std::ios::end && off > 0))
- {
- return -1;
- }
- U8* address = NULL;
- if (which & std::ios::in)
- {
- U8* base_addr = NULL;
- switch (way)
- {
- case std::ios::end:
- base_addr = (U8*)LLBufferArray::npos;
- break;
- case std::ios::cur:
- // Het the current get pointer and adjust it for buffer array
- // semantics.
- base_addr = (U8*)gptr();
- break;
- case std::ios::beg:
- default:
- // NULL is fine
- break;
- }
- LLMutexLock lock(mBuffer->getMutex());
- address = mBuffer->seek(mChannels.in(), base_addr, off);
- if (address)
- {
- LLBufferArray::segment_iterator_t iter;
- iter = mBuffer->getSegment(address);
- char* start = (char*)(*iter).data();
- setg(start, (char*)address, start + (*iter).size());
- }
- else
- {
- address = (U8*)(-1);
- }
- }
- if (which & std::ios::out)
- {
- U8* base_addr = NULL;
- switch (way)
- {
- case std::ios::end:
- base_addr = (U8*)LLBufferArray::npos;
- break;
- case std::ios::cur:
- // Get the current put pointer and adjust it for buffer array
- //semantics.
- base_addr = (U8*)pptr();
- break;
- case std::ios::beg:
- default:
- // NULL is fine
- break;
- }
- LLMutexLock lock(mBuffer->getMutex());
- address = mBuffer->seek(mChannels.out(), base_addr, off);
- if (address)
- {
- LLBufferArray::segment_iterator_t iter;
- iter = mBuffer->getSegment(address);
- setp((char*)address, (char*)(*iter).data() + (*iter).size());
- }
- else
- {
- address = (U8*)(-1);
- }
- }
- #if (LL_WINDOWS || __GNUC__ > 2)
- S32 rv = (S32)(intptr_t)address;
- return (pos_type)rv;
- #else
- return (streampos)address;
- #endif
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLBufferStream
- ///////////////////////////////////////////////////////////////////////////////
- LLBufferStream::LLBufferStream(const LLChannelDescriptors& channels,
- LLBufferArray* buffer)
- : std::iostream(&mStreamBuf),
- mStreamBuf(channels, buffer)
- {
- }
|