/** * @file bufferstream.cpp * @brief Implements the BufferStream adapter class * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2012, 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 "bufferstream.h" #include "bufferarray.h" namespace LLCore { BufferArrayStreamBuf::BufferArrayStreamBuf(BufferArray * array) : mBufferArray(array), mReadCurPos(0), mReadCurBlock(-1), mReadBegin(NULL), mReadCur(NULL), mReadEnd(NULL), mWriteCurPos(0) { if (array) { array->addRef(); mWriteCurPos = array->mLen; } } BufferArrayStreamBuf::~BufferArrayStreamBuf() { if (mBufferArray) { mBufferArray->release(); mBufferArray = NULL; } } BufferArrayStreamBuf::int_type BufferArrayStreamBuf::underflow() { if (! mBufferArray) { return traits_type::eof(); } if (mReadCur == mReadEnd) { // Find the next block with actual data or leave // mCurBlock/mCur/mEnd unchanged if we're at the end // of any block chain. const char * new_begin(NULL), * new_end(NULL); int new_cur_block(mReadCurBlock + 1); while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end)) { if (new_begin != new_end) { break; } ++new_cur_block; } if (new_begin == new_end) { return traits_type::eof(); } mReadCurBlock = new_cur_block; mReadBegin = mReadCur = new_begin; mReadEnd = new_end; } return traits_type::to_int_type(*mReadCur); } BufferArrayStreamBuf::int_type BufferArrayStreamBuf::uflow() { const int_type ret(underflow()); if (traits_type::eof() != ret) { ++mReadCur; ++mReadCurPos; } return ret; } BufferArrayStreamBuf::int_type BufferArrayStreamBuf::pbackfail(int_type ch) { if (! mBufferArray) { return traits_type::eof(); } if (mReadCur == mReadBegin) { // Find the previous block with actual data or leave // mCurBlock/mBegin/mCur/mEnd unchanged if we're at the // beginning of any block chain. const char * new_begin(NULL), * new_end(NULL); int new_cur_block(mReadCurBlock - 1); while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end)) { if (new_begin != new_end) { break; } --new_cur_block; } if (new_begin == new_end) { return traits_type::eof(); } mReadCurBlock = new_cur_block; mReadBegin = new_begin; mReadEnd = mReadCur = new_end; } if (traits_type::eof() != ch && mReadCur[-1] != ch) { return traits_type::eof(); } --mReadCurPos; return traits_type::to_int_type(*--mReadCur); } std::streamsize BufferArrayStreamBuf::showmanyc() { if (! mBufferArray) { return -1; } return mBufferArray->mLen - mReadCurPos; } BufferArrayStreamBuf::int_type BufferArrayStreamBuf::overflow(int c) { if (! mBufferArray || mWriteCurPos > mBufferArray->mLen) { return traits_type::eof(); } const size_t wrote(mBufferArray->write(mWriteCurPos, &c, 1)); mWriteCurPos += wrote; return wrote ? c : traits_type::eof(); } std::streamsize BufferArrayStreamBuf::xsputn(const char * src, std::streamsize count) { if (! mBufferArray || mWriteCurPos > mBufferArray->mLen) { return 0; } const size_t wrote(mBufferArray->write(mWriteCurPos, src, count)); mWriteCurPos += wrote; return wrote; } std::streampos BufferArrayStreamBuf::seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which) { std::streampos ret(-1); if (! mBufferArray) { return ret; } if (std::ios_base::in == which) { size_t pos(0); switch (way) { case std::ios_base::beg: pos = off; break; case std::ios_base::cur: pos = mReadCurPos += off; break; case std::ios_base::end: pos = mBufferArray->mLen - off; break; default: return ret; } if (pos >= mBufferArray->size()) { pos = (std::max)(size_t(0), mBufferArray->size() - 1); } size_t ba_offset(0); int block(mBufferArray->findBlock(pos, &ba_offset)); if (block < 0) return ret; const char * start(NULL), * end(NULL); if (! mBufferArray->getBlockStartEnd(block, &start, &end)) return ret; mReadCurBlock = block; mReadBegin = start; mReadCur = start + ba_offset; mReadEnd = end; ret = mReadCurPos = pos; } else if (std::ios_base::out == which) { size_t pos(0); switch (way) { case std::ios_base::beg: pos = off; break; case std::ios_base::cur: pos = mWriteCurPos += off; break; case std::ios_base::end: pos = mBufferArray->mLen - off; break; default: return ret; } if (pos > mBufferArray->size()) { pos = mBufferArray->size(); } ret = mWriteCurPos = pos; } return ret; } BufferArrayStream::BufferArrayStream(BufferArray * ba) : std::iostream(&mStreamBuf), mStreamBuf(ba) {} BufferArrayStream::~BufferArrayStream() {} } // end namespace LLCore