/** * @file llbufferstream.cpp * @author Phoenix * @date 2005-10-10 * @brief Implementation of the buffer iostream classes * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, 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 "linden_common.h" #include "llbufferstream.h" #include "llbuffer.h" #include "llthread.h" #include "llmutex.h" static const 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() { //LL_DEBUGS() << "LLBufferStreamBuf::underflow()" << LL_ENDL; 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 != *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))) { ++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(0 == sync()) { return 1; } else { return EOF; } } // since we got here, we have a buffer, and we have a character to // put on it. LLBufferArray::segment_iterator_t it; LLMutexLock lock(mBuffer->getMutex()); 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. //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 if address is not NULL. // Need to think about that. LLMutexLock lock(mBuffer->getMutex()); address = mBuffer->seek(mChannels.out(), address, -1); if(address) { LLBufferArray::segment_iterator_t it; 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 #if( LL_WINDOWS || __GNUC__ > 2) LLBufferStreamBuf::pos_type LLBufferStreamBuf::seekoff( LLBufferStreamBuf::off_type off, std::ios::seekdir way, std::ios::openmode which) #else streampos LLBufferStreamBuf::seekoff( streamoff off, std::ios::seekdir way, std::ios::openmode which) #endif { 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: // get 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, (S32)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, (S32)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) { } LLBufferStream::~LLBufferStream() { }