view deps/Platinum/ThirdParty/Neptune/Source/Core/NptBufferedStreams.cpp @ 0:3425707ddbf6

Initial import (hopefully this mercurial stuff works...)
author fraserofthenight
date Mon, 06 Jul 2009 08:06:28 -0700
parents
children
line wrap: on
line source

/*****************************************************************
|
|   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"

/*----------------------------------------------------------------------
|   NPT_BufferedInputStream::NPT_BufferedInputStream
+---------------------------------------------------------------------*/
NPT_BufferedInputStream::NPT_BufferedInputStream(NPT_InputStreamReference& source, NPT_Size buffer_size) :
    m_Source(source),
    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)
{
    if (m_Buffer.data != NULL) {
        // we already have a buffer
        if (m_Buffer.size < size) {
            // the current buffer is too small, 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;
    bool       skip_newline = false;

    // check parameters
    if (buffer == NULL || size < 1) return NPT_ERROR_INVALID_PARAMETERS;

    // read until EOF or newline
    while (buffer-buffer_start < (long)(size-1)) {
        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 {
                *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 {
                    ++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
    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_CHECK(ReadLine(line.UseChars(), max_chars, &chars_read, break_on_cr));

    // 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:
    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::Seek
+---------------------------------------------------------------------*/
NPT_Result 
NPT_BufferedInputStream::Seek(NPT_Position /*offset*/)
{
    // not implemented yet
    return NPT_ERROR_NOT_IMPLEMENTED;
}

/*----------------------------------------------------------------------
|   NPT_BufferedInputStream::Tell
+---------------------------------------------------------------------*/
NPT_Result 
NPT_BufferedInputStream::Tell(NPT_Position& offset)
{
    // not implemented yet
    offset = 0;
    return NPT_ERROR_NOT_IMPLEMENTED;
}

/*----------------------------------------------------------------------
|   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;
    }
}