diff options
Diffstat (limited to 'lib/libUPnP/Neptune/Source/Core/NptBufferedStreams.cpp')
-rw-r--r-- | lib/libUPnP/Neptune/Source/Core/NptBufferedStreams.cpp | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/lib/libUPnP/Neptune/Source/Core/NptBufferedStreams.cpp b/lib/libUPnP/Neptune/Source/Core/NptBufferedStreams.cpp new file mode 100644 index 0000000..157a5bc --- /dev/null +++ b/lib/libUPnP/Neptune/Source/Core/NptBufferedStreams.cpp @@ -0,0 +1,471 @@ +/***************************************************************** +| +| Neptune - Buffered Streams +| +| Copyright (c) 2002-2008, Axiomatic Systems, LLC. +| All rights reserved. +| +| Redistribution and use in source and binary forms, with or without +| modification, are permitted provided that the following conditions are met: +| * Redistributions of source code must retain the above copyright +| notice, this list of conditions and the following disclaimer. +| * Redistributions in binary form must reproduce the above copyright +| notice, this list of conditions and the following disclaimer in the +| documentation and/or other materials provided with the distribution. +| * Neither the name of Axiomatic Systems nor the +| names of its contributors may be used to endorse or promote products +| derived from this software without specific prior written permission. +| +| THIS SOFTWARE IS PROVIDED BY AXIOMATIC SYSTEMS ''AS IS'' AND ANY +| EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +| DISCLAIMED. IN NO EVENT SHALL AXIOMATIC SYSTEMS BE LIABLE FOR ANY +| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +| + ****************************************************************/ + +/*---------------------------------------------------------------------- +| includes ++---------------------------------------------------------------------*/ +#include "NptTypes.h" +#include "NptInterfaces.h" +#include "NptConstants.h" +#include "NptBufferedStreams.h" +#include "NptUtils.h" +#include "NptLogging.h" + +/*---------------------------------------------------------------------- +| logging ++---------------------------------------------------------------------*/ +NPT_SET_LOCAL_LOGGER("neptune.bufferedstreams") + +#define NPT_CHECK_NOLOGTIMEOUT(_x) \ +do { \ + NPT_Result __result = (_x); \ + if (__result != NPT_SUCCESS) { \ + if (__result != NPT_ERROR_TIMEOUT && __result != NPT_ERROR_EOS) { \ + NPT_CHECK_WARNING(__result); \ + } \ + return __result; \ + } \ +} while(0) + +/*---------------------------------------------------------------------- +| NPT_BufferedInputStream::NPT_BufferedInputStream ++---------------------------------------------------------------------*/ +NPT_BufferedInputStream::NPT_BufferedInputStream(NPT_InputStreamReference& source, NPT_Size buffer_size) : + m_Source(source), + m_Position(0), + m_SkipNewline(false), + m_Eos(false) +{ + // setup the read buffer + m_Buffer.data = NULL; + m_Buffer.offset = 0; + m_Buffer.valid = 0; + m_Buffer.size = buffer_size; +} + +/*---------------------------------------------------------------------- +| NPT_BufferedInputStream::~NPT_BufferedInputStream ++---------------------------------------------------------------------*/ +NPT_BufferedInputStream::~NPT_BufferedInputStream() +{ + // release the buffer + delete[] m_Buffer.data; +} + +/*---------------------------------------------------------------------- +| NPT_BufferedInputStream::SetBufferSize ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BufferedInputStream::SetBufferSize(NPT_Size size, bool force /* = false */) +{ + if (m_Buffer.data != NULL) { + // we already have a buffer + if (m_Buffer.size < size || force) { + // the current buffer is too small or we want to move + // existing data to the beginning of the buffer, reallocate + NPT_Byte* buffer = new NPT_Byte[size]; + if (buffer == NULL) return NPT_ERROR_OUT_OF_MEMORY; + + // copy existing data + NPT_Size need_to_copy = m_Buffer.valid - m_Buffer.offset; + if (need_to_copy) { + NPT_CopyMemory((void*)buffer, + m_Buffer.data+m_Buffer.offset, + need_to_copy); + } + + // use the new buffer + delete[] m_Buffer.data; + m_Buffer.data = buffer; + m_Buffer.valid -= m_Buffer.offset; + m_Buffer.offset = 0; + } + } + m_Buffer.size = size; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BufferedInputStream::FillBuffer ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BufferedInputStream::FillBuffer() +{ + // shortcut + if (m_Eos) return NPT_ERROR_EOS; + + // check that there is nothing left in the buffer and the buffer + // size is not 0 + NPT_ASSERT(m_Buffer.valid == m_Buffer.offset); + NPT_ASSERT(m_Buffer.size != 0); + + // allocate the read buffer if it has not been done yet + if (m_Buffer.data == NULL) { + m_Buffer.data = new NPT_Byte[m_Buffer.size]; + if (m_Buffer.data == NULL) return NPT_ERROR_OUT_OF_MEMORY; + } + + // refill the buffer + m_Buffer.offset = 0; + NPT_Result result = m_Source->Read(m_Buffer.data, m_Buffer.size, &m_Buffer.valid); + if (NPT_FAILED(result)) m_Buffer.valid = 0; + return result; +} + +/*---------------------------------------------------------------------- +| NPT_BufferedInputStream::ReleaseBuffer ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BufferedInputStream::ReleaseBuffer() +{ + NPT_ASSERT(m_Buffer.size == 0); + NPT_ASSERT(m_Buffer.offset == m_Buffer.valid); + + delete[] m_Buffer.data; + m_Buffer.data = NULL; + m_Buffer.offset = 0; + m_Buffer.valid = 0; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BufferedInputStream::ReadLine ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BufferedInputStream::ReadLine(char* buffer, + NPT_Size size, + NPT_Size* chars_read, + bool break_on_cr) +{ + NPT_Result result = NPT_SUCCESS; + char* buffer_start = buffer; + char* buffer_end = buffer_start+size-1; + bool skip_newline = false; + + // check parameters + if (buffer == NULL || size < 1) { + if (chars_read) *chars_read = 0; + return NPT_ERROR_INVALID_PARAMETERS; + } + + // read until EOF or newline + for (;;) { + while (m_Buffer.offset != m_Buffer.valid) { + // there is some data left in the buffer + NPT_Byte c = m_Buffer.data[m_Buffer.offset++]; + if (c == '\r') { + if (break_on_cr) { + skip_newline = true; + goto done; + } + } else if (c == '\n') { + if (m_SkipNewline && (buffer == buffer_start)) { + continue; + } + goto done; + } else { + if (buffer == buffer_end) { + result = NPT_ERROR_NOT_ENOUGH_SPACE; + goto done; + } + *buffer++ = c; + } + } + + if (m_Buffer.size == 0 && !m_Eos) { + // unbuffered mode + if (m_Buffer.data != NULL) ReleaseBuffer(); + while (NPT_SUCCEEDED(result = m_Source->Read(buffer, 1, NULL))) { + if (*buffer == '\r') { + if (break_on_cr) { + skip_newline = true; + goto done; + } + } else if (*buffer == '\n') { + goto done; + } else { + if (buffer == buffer_end) { + result = NPT_ERROR_NOT_ENOUGH_SPACE; + goto done; + } + ++buffer; + } + } + } else { + // refill the buffer + result = FillBuffer(); + } + if (NPT_FAILED(result)) goto done; + } + +done: + // update the newline skipping state + m_SkipNewline = skip_newline; + + // NULL-terminate the line + *buffer = '\0'; + + // return what we have + m_Position += (NPT_Size)(buffer-buffer_start); + if (chars_read) *chars_read = (NPT_Size)(buffer-buffer_start); + if (result == NPT_ERROR_EOS) { + m_Eos = true; + if (buffer != buffer_start) { + // we have reached the end of the stream, but we have read + // some chars, so do not return EOS now + return NPT_SUCCESS; + } + } + return result; +} + +/*---------------------------------------------------------------------- +| NPT_BufferedInputStream::ReadLine ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BufferedInputStream::ReadLine(NPT_String& line, + NPT_Size max_chars, + bool break_on_cr) +{ + // clear the line + line.SetLength(0); + + // reserve space for the chars + line.Reserve(max_chars); + + // read the line + NPT_Size chars_read = 0; + NPT_Result result = ReadLine(line.UseChars(), max_chars, &chars_read, break_on_cr); + NPT_CHECK_NOLOGTIMEOUT(result); + + // adjust the length of the string object + line.SetLength(chars_read); + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BufferedInputStream::Read ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BufferedInputStream::Read(void* buffer, + NPT_Size bytes_to_read, + NPT_Size* bytes_read) +{ + NPT_Result result = NPT_SUCCESS; + NPT_Size total_read = 0; + NPT_Size buffered; + + // check for a possible shortcut + if (bytes_to_read == 0) return NPT_SUCCESS; + + // skip a newline char if needed + if (m_SkipNewline) { + m_SkipNewline = false; + result = Read(buffer, 1, NULL); + if (NPT_FAILED(result)) goto done; + NPT_Byte c = *(NPT_Byte*)buffer; + if (c != '\n') { + buffer = (void*)((NPT_Byte*)buffer+1); + --bytes_to_read; + total_read = 1; + } + } + + // compute how much is buffered + buffered = m_Buffer.valid-m_Buffer.offset; + if (bytes_to_read > buffered) { + // there is not enough in the buffer, take what's there + if (buffered) { + NPT_CopyMemory(buffer, + m_Buffer.data + m_Buffer.offset, + buffered); + buffer = (void*)((NPT_Byte*)buffer+buffered); + m_Buffer.offset += buffered; + bytes_to_read -= buffered; + total_read += buffered; + goto done; + } + + // read the rest from the source + if (m_Buffer.size == 0) { + // unbuffered mode, read directly into the supplied buffer + if (m_Buffer.data != NULL) ReleaseBuffer(); // cleanup if necessary + NPT_Size local_read = 0; + result = m_Source->Read(buffer, bytes_to_read, &local_read); + if (NPT_SUCCEEDED(result)) { + total_read += local_read; + } + goto done; + } else { + // refill the buffer + result = FillBuffer(); + if (NPT_FAILED(result)) goto done; + buffered = m_Buffer.valid; + if (bytes_to_read > buffered) bytes_to_read = buffered; + } + } + + // get what we can from the buffer + if (bytes_to_read) { + NPT_CopyMemory(buffer, + m_Buffer.data + m_Buffer.offset, + bytes_to_read); + m_Buffer.offset += bytes_to_read; + total_read += bytes_to_read; + } + +done: + m_Position += total_read; + if (bytes_read) *bytes_read = total_read; + if (result == NPT_ERROR_EOS) { + m_Eos = true; + if (total_read != 0) { + // we have reached the end of the stream, but we have read + // some chars, so do not return EOS now + return NPT_SUCCESS; + } + } + return result; +} + +/*---------------------------------------------------------------------- +| NPT_BufferedInputStream::Peek ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BufferedInputStream::Peek(void* buffer, + NPT_Size bytes_to_read, + NPT_Size* bytes_read) +{ + NPT_Result result = NPT_SUCCESS; + NPT_Size buffered; + NPT_Size new_size = m_Buffer.size?m_Buffer.size:NPT_BUFFERED_BYTE_STREAM_DEFAULT_SIZE; + + // check for a possible shortcut + if (bytes_to_read == 0) return NPT_SUCCESS; + + // compute how much is buffered + buffered = m_Buffer.valid-m_Buffer.offset; + if (bytes_to_read > buffered && buffered < new_size && !m_Eos) { + // we need more data than what we have + // switch to unbuffered mode and resize to force relocation + // of data to the beginning of the buffer + SetBufferSize(new_size, true); + // fill up the end of the buffer + result = FillBuffer(); + // continue even if it failed + buffered = m_Buffer.valid; + } + + // make sure we're returning what we can + if (bytes_to_read > buffered) bytes_to_read = buffered; + + // get what we can from the buffer + NPT_CopyMemory(buffer, + m_Buffer.data + m_Buffer.offset, + bytes_to_read); + + if (bytes_read) *bytes_read = bytes_to_read; + if (result == NPT_ERROR_EOS) { + m_Eos = true; + if (bytes_to_read != 0) { + // we have reached the end of the stream, but we have read + // some chars, so do not return EOS now + return NPT_SUCCESS; + } + } + return result; +} + +/*---------------------------------------------------------------------- +| NPT_BufferedInputStream::Seek ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BufferedInputStream::Seek(NPT_Position offset) +{ + NPT_Result result; + + if (offset >= m_Position && + offset - m_Position < m_Buffer.valid - m_Buffer.offset) { + NPT_Position diff = offset - m_Position; + m_Buffer.offset += ((NPT_Size)diff); + m_Position = offset; + return NPT_SUCCESS; + } + + result = m_Source->Seek(offset); + if (NPT_FAILED(result)) return result; + + m_Buffer.offset = 0; + m_Buffer.valid = 0; + m_Eos = false; + m_Position = offset; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BufferedInputStream::Tell ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BufferedInputStream::Tell(NPT_Position& offset) +{ + offset = m_Position; + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_BufferedInputStream::GetSize ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BufferedInputStream::GetSize(NPT_LargeSize& size) +{ + return m_Source->GetSize(size); +} + +/*---------------------------------------------------------------------- +| NPT_BufferedInputStream::GetAvailable ++---------------------------------------------------------------------*/ +NPT_Result +NPT_BufferedInputStream::GetAvailable(NPT_LargeSize& available) +{ + NPT_LargeSize source_available = 0; + NPT_Result result = m_Source->GetAvailable(source_available); + if (NPT_SUCCEEDED(result)) { + available = m_Buffer.valid-m_Buffer.offset + source_available; + return NPT_SUCCESS; + } else { + available = m_Buffer.valid-m_Buffer.offset; + return available?NPT_SUCCESS:result; + } +} |