summaryrefslogtreecommitdiffstats
path: root/lib/libUPnP/Neptune/Source/Core/NptBufferedStreams.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libUPnP/Neptune/Source/Core/NptBufferedStreams.cpp')
-rw-r--r--lib/libUPnP/Neptune/Source/Core/NptBufferedStreams.cpp471
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;
+ }
+}