Adds LL_COMPILE_TIME_MESSAGE support to Linux. llfinite -> std::isfinite llisnan -> std::isnan vector_shrink_to_fit -> vector.shrink_to_fit
333 lines
7.9 KiB
C++
333 lines
7.9 KiB
C++
/**
|
|
* @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"
|
|
|
|
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
|
|
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:
|
|
// 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, 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);
|
|
}
|
|
}
|
|
|
|
S32 rv = (S32)(intptr_t)address;
|
|
return (pos_type)rv;
|
|
}
|
|
|
|
|
|
/*
|
|
* LLBufferStream
|
|
*/
|
|
LLBufferStream::LLBufferStream(
|
|
const LLChannelDescriptors& channels,
|
|
LLBufferArray* buffer) :
|
|
std::iostream(&mStreamBuf),
|
|
mStreamBuf(channels, buffer)
|
|
{
|
|
}
|
|
|
|
LLBufferStream::~LLBufferStream()
|
|
{
|
|
}
|