PageRenderTime 58ms CodeModel.GetById 11ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llmessage/llbufferstream.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 352 lines | 228 code | 23 blank | 101 comment | 38 complexity | b30836f76442528922091a36097a2703 MD5 | raw file
  1/** 
  2 * @file llbufferstream.cpp
  3 * @author Phoenix
  4 * @date 2005-10-10
  5 * @brief Implementation of the buffer iostream classes
  6 *
  7 * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28
 29#include "linden_common.h"
 30#include "llbufferstream.h"
 31
 32#include "llbuffer.h"
 33#include "llmemtype.h"
 34#include "llthread.h"
 35
 36static const S32 DEFAULT_OUTPUT_SEGMENT_SIZE = 1024 * 4;
 37
 38/*
 39 * LLBufferStreamBuf
 40 */
 41LLBufferStreamBuf::LLBufferStreamBuf(
 42	const LLChannelDescriptors& channels,
 43	LLBufferArray* buffer) :
 44	mChannels(channels),
 45	mBuffer(buffer)
 46{
 47	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 48}
 49
 50LLBufferStreamBuf::~LLBufferStreamBuf()
 51{
 52	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 53	sync();
 54}
 55
 56// virtual
 57int LLBufferStreamBuf::underflow()
 58{
 59	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 60	//lldebugs << "LLBufferStreamBuf::underflow()" << llendl;
 61	if(!mBuffer)
 62	{
 63		return EOF;
 64	}
 65
 66	LLMutexLock lock(mBuffer->getMutex());
 67	LLBufferArray::segment_iterator_t iter;
 68	LLBufferArray::segment_iterator_t end = mBuffer->endSegment();
 69	U8* last_pos = (U8*)gptr();
 70	LLSegment segment;
 71	if(last_pos)
 72	{
 73		// Back up into a piece of memory we know that we have
 74		// allocated so that calls for the next segment based on
 75		// 'after' will succeed.
 76		--last_pos;
 77		iter = mBuffer->splitAfter(last_pos);
 78		if(iter != end)
 79		{
 80			// We need to clear the read segment just in case we have
 81			// an early exit in the function and never collect the
 82			// next segment. Calling eraseSegment() with the same
 83			// segment twice is just like double deleting -- nothing
 84			// good comes from it.
 85			mBuffer->eraseSegment(iter++);
 86			if(iter != end) segment = (*iter);
 87		}
 88		else
 89		{
 90			// This should never really happen, but somehow, the
 91			// istream is telling the buf that it just finished
 92			// reading memory that is not in the buf. I think this
 93			// would only happen if there were a bug in the c++ stream
 94			// class. Just bail.
 95			// *TODO: can we set the fail bit on the stream somehow?
 96			return EOF;
 97		}
 98	}
 99	else
100	{
101		// Get iterator to full segment containing last_pos
102		// and construct sub-segment starting at last_pos.
103		// Note: segment may != *it at this point
104		iter = mBuffer->constructSegmentAfter(last_pos, segment);
105	}
106	if(iter == end)
107	{
108		return EOF;
109	}
110
111	// Iterate through segments to find a non-empty segment on input channel.
112	while((!segment.isOnChannel(mChannels.in()) || (segment.size() == 0)))
113	{
114		++iter;
115		if(iter == end)
116		{
117			return EOF;
118		}
119
120		segment = *(iter);
121	}
122
123	// set up the stream to read from the next segment.
124	char* start = (char*)segment.data();
125	setg(start, start, start + segment.size());
126	return *gptr();
127}
128
129// virtual
130int LLBufferStreamBuf::overflow(int c)
131{
132	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
133	if(!mBuffer)
134	{
135		return EOF;
136	}
137	if(EOF == c)
138	{
139		// if someone puts an EOF, I suppose we should sync and return
140		// success.
141		if(0 == sync())
142		{
143			return 1;
144		}
145		else
146		{
147			return EOF;
148		}
149	}
150
151	// since we got here, we have a buffer, and we have a character to
152	// put on it.
153	LLBufferArray::segment_iterator_t it;
154	LLMutexLock lock(mBuffer->getMutex());
155	it = mBuffer->makeSegment(mChannels.out(), DEFAULT_OUTPUT_SEGMENT_SIZE);
156	if(it != mBuffer->endSegment())
157	{
158		char* start = (char*)(*it).data();
159		(*start) = (char)(c);
160		setp(start + 1, start + (*it).size());
161		return c;
162	}
163	else
164	{
165		return EOF;
166	}
167}
168
169// virtual
170int LLBufferStreamBuf::sync()
171{
172	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
173	int return_value = -1;
174	if(!mBuffer)
175	{
176		return return_value;
177	}
178
179	// This chunk of code is not necessary because typically, users of
180	// the stream will read until EOF. Therefore, underflow was called
181	// and the segment was discarded before the sync() was called in
182	// the destructor. Theoretically, we could keep some more data
183	// around and detect the rare case where an istream was deleted
184	// before reading to the end, but that will only leave behind some
185	// unavailable but still referenced memory. Also, if another
186	// istream is constructed, it will re-read that segment, and then
187	// discard it.
188	//U8* last_pos = (U8*)gptr();
189	//if(last_pos)
190	//{
191	//	// Looks like we read something. Discard what we have read.
192	//	// gptr() actually returns the currrent position, but we call
193	//	// it last_pos because of how it is used in the split call
194	//	// below.
195	//	--last_pos;
196	//	LLBufferArray::segment_iterator_t iter;
197	//	iter = mBuffer->splitAfter(last_pos);
198	//	if(iter != mBuffer->endSegment())
199	//	{
200	//		// We need to clear the read segment just in case we have
201	//		// an early exit in the function and never collect the
202	//		// next segment. Calling eraseSegment() with the same
203	//		// segment twice is just like double deleting -- nothing
204	//		// good comes from it.
205	//		mBuffer->eraseSegment(iter);
206	//	}
207	//}
208
209	// set the put pointer so that we force an overflow on the next
210	// write.
211	U8* address = (U8*)pptr();
212	setp(NULL, NULL);
213
214	// *NOTE: I bet we could just --address if address is not NULL.
215	// Need to think about that.
216	LLMutexLock lock(mBuffer->getMutex());
217	address = mBuffer->seek(mChannels.out(), address, -1);
218	if(address)
219	{
220		LLBufferArray::segment_iterator_t it;
221		it = mBuffer->splitAfter(address);
222		LLBufferArray::segment_iterator_t end = mBuffer->endSegment();
223		if(it != end)
224		{
225			++it;
226			if(it != end)
227			{
228				mBuffer->eraseSegment(it);
229			}
230			return_value = 0;
231		}
232	}
233	else
234	{
235		// nothing was put on the buffer, so the sync() is a no-op.
236		return_value = 0;
237	}
238	return return_value;
239}
240
241// virtual
242#if( LL_WINDOWS || __GNUC__ > 2)
243LLBufferStreamBuf::pos_type LLBufferStreamBuf::seekoff(
244	LLBufferStreamBuf::off_type off,
245	std::ios::seekdir way,
246	std::ios::openmode which)
247#else
248streampos LLBufferStreamBuf::seekoff(
249	streamoff off,
250	std::ios::seekdir way,
251	std::ios::openmode which)
252#endif
253{
254	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
255	if(!mBuffer
256	   || ((way == std::ios::beg) && (off < 0))
257	   || ((way == std::ios::end) && (off > 0)))
258	{
259		return -1;
260	}
261	U8* address = NULL;
262	if(which & std::ios::in)
263	{
264		U8* base_addr = NULL;
265		switch(way)
266		{
267		case std::ios::end:
268			base_addr = (U8*)LLBufferArray::npos;
269			break;
270		case std::ios::cur:
271			// get the current get pointer and adjust it for buffer
272			// array semantics.
273			base_addr = (U8*)gptr();
274			break;
275		case std::ios::beg:
276		default:
277			// NULL is fine
278			break;
279		}
280
281		LLMutexLock lock(mBuffer->getMutex());
282		address = mBuffer->seek(mChannels.in(), base_addr, off);
283		if(address)
284		{
285			LLBufferArray::segment_iterator_t iter;
286			iter = mBuffer->getSegment(address);
287			char* start = (char*)(*iter).data();
288			setg(start, (char*)address, start + (*iter).size());
289		}
290		else
291		{
292			address = (U8*)(-1);
293		}
294	}
295	if(which & std::ios::out)
296	{
297		U8* base_addr = NULL;
298		switch(way)
299		{
300		case std::ios::end:
301			base_addr = (U8*)LLBufferArray::npos;
302			break;
303		case std::ios::cur:
304			// get the current put pointer and adjust it for buffer
305			// array semantics.
306			base_addr = (U8*)pptr();
307			break;
308		case std::ios::beg:
309		default:
310			// NULL is fine
311			break;
312		}
313
314		LLMutexLock lock(mBuffer->getMutex());
315		address = mBuffer->seek(mChannels.out(), base_addr, off);
316		if(address)
317		{
318			LLBufferArray::segment_iterator_t iter;
319			iter = mBuffer->getSegment(address);
320			setp((char*)address, (char*)(*iter).data() + (*iter).size());
321		}
322		else
323		{
324			address = (U8*)(-1);
325		}
326	}
327
328#if( LL_WINDOWS || __GNUC__ > 2 )
329	S32 rv = (S32)(intptr_t)address;
330	return (pos_type)rv;
331#else
332	return (streampos)address;
333#endif
334}
335
336
337/*
338 * LLBufferStream
339 */
340LLBufferStream::LLBufferStream(
341	const LLChannelDescriptors& channels,
342	LLBufferArray* buffer) :
343	std::iostream(&mStreamBuf),
344	mStreamBuf(channels, buffer)
345{
346	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
347}
348
349LLBufferStream::~LLBufferStream()
350{
351	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
352}