/****************************************************************************
** $Id: qbuffer.cpp,v 2.0 1996/09/24 10:16:08 agulbra Exp $
**
** Implementation of QBuffer class
**
** Created : 930812
**
** Copyright (C) 1992-1996 Troll Tech AS.  All rights reserved.
**
** This file is part of the non-commercial distribution of Qt 1.0
** see the file LICENSE included in the distribution for the usage
** and distribution terms, or http://www.troll.no/qt/license.html.
**
** (This file differs from the commercial version of Qt only by this comment.)
**
*****************************************************************************/

#include "qbuffer.h"
#include <stdlib.h>

RCSTAG("$Id: qbuffer.cpp,v 2.0 1996/09/24 10:16:08 agulbra Exp $");


/*!
  \class QBuffer qbuffer.h
  \brief The QBuffer class is an I/O device that operates on a QByteArray

  \ingroup tools
  \ingroup stream

  QBuffer is an I/O device for reading and writing a memory buffer. A
  QFile may be used by itself (readBlock and writeBlock) or by more
  conveniently using QDataStream or QTextStream.  Most of its behavior is
  inherited from QIODevice.

  A QBuffer has an associated QByteArray which holds the buffer data.
  Writing data at the end (i.e. size()) of the buffer expands the byte
  array.

  The byte stream classes QDataStream and QTextStream have an constructor
  that creates an internal QBuffer that operates on a byte array.

  Example:
  \code
    QString str;
    QTextStream ts( str, IO_WriteOnly );
    ts << "pi = " << 3.14;			// str == "pi = 3.14"
  \endcode

  \sa QFile, QDataStream, QTextStream
*/


/*!
  Constructs an empty buffer.
*/

QBuffer::QBuffer()
{
    setFlags( IO_Direct );
    a_inc = 512;				// initial increment
    a_len = 0;
}


/*!
  Constructs a buffer and sets the buffer contents to \e buf.
  \sa setBuffer()
*/

QBuffer::QBuffer( QByteArray buf ) : a(buf)
{
    setFlags( IO_Direct );
    a_inc = 512;				// initial increment
    a_len = a.size();
}

/*!
  Destroys the buffer.
*/

QBuffer::~QBuffer()
{
}


/*!
  Replaces the buffer's contents with \e buf.

  This may not be done while the buffer is \link open() open\endlink.

  Note that if you open the buffer in write mode (\c IO_WriteOnly or
  IO_ReadWrite) and write something into the buffer, \e buf is also
  modified because QByteArray is an explicitly shared class.

  Example:
  \code
    QString str = "abc";
    QBuffer b( str );
    b.open( IO_WriteOnly );
    b.at( 3 );					// position at \0
    b.writeBlock( "def", 4 );			// write including \0
    b.close();
      // Now, str == "abcdef"
  \endcode

  \sa open, \link shclass.html Shared Classes\endlink
*/

bool QBuffer::setBuffer( QByteArray buf )
{
    if ( isOpen() ) {
#if defined(CHECK_STATE)
	warning( "QBuffer::setBuffer: Buffer is open");
#endif
	return FALSE;
    }
    a = buf;
    a_len = a.size();
    return TRUE;
}


/*!
  Opens the file specified by the file name currently set, using the mode \e m.
  Returns TRUE if successful, otherwise FALSE.

  The mode parameter \e m must be a combination of the following flags.
  <ul>
  <li>\c IO_ReadOnly opens a buffer in read-only mode.
  <li>\c IO_WriteOnly opens a buffer in write-only mode.
  <li>\c IO_ReadWrite opens a buffer in read/write mode.
  <li>\c IO_Append sets the buffer index to the end of the buffer.
  <li>\c IO_Truncate truncates the buffer.
  </ul>

  \sa close(), isOpen()
*/

bool QBuffer::open( int m  )
{
    if ( isOpen() ) {				// buffer already open
#if defined(CHECK_STATE)
	warning( "QBuffer::open: Buffer already open" );
#endif
	return FALSE;
    }
    setMode( m );
    if ( m & IO_Truncate )			// truncate buffer
	a.resize( 0 );
    if ( m & IO_Append )			// append to end of buffer
	index = a.size();
    else
	index = 0;
    setState( IO_Open );
    setStatus( 0 );
    return TRUE;
}

/*!
  Closes an open buffer.
  \sa open()
*/

void QBuffer::close()
{
    if ( isOpen() ) {
	setFlags( IO_Direct );
	index = 0;
    }
}

/*!
  The flush function does nothing.
*/

void QBuffer::flush()
{
    return;
}


/*!
  \fn int QBuffer::at() const
  Returns the buffer index.
  \sa size()
*/

/*!
  Sets the buffer index to \e pos. Returns TRUE if successful, otherwise FALSE.
  \sa size()
*/

bool QBuffer::at( int pos )
{
#if defined(CHECK_STATE)
    if ( !isOpen() ) {
	warning( "QBuffer::at: Buffer is not open" );
	return FALSE;
    }
#endif
    if ( (uint)pos > a_len ) {
#if defined(CHECK_RANGE)
	warning( "QBuffer::at: Index %d out of range", pos );
#endif
	return FALSE;
    }
    index = pos;
    return TRUE;
}


/*!
  Reads at most \e len bytes from the buffer into \e p and returns the
  number of bytes actually read.

  Returns -1 if a serious error occurred.

  \sa writeBlock()
*/

int QBuffer::readBlock( char *p, uint len )
{
#if defined(CHECK_STATE)
    CHECK_PTR( p );
    if ( !isOpen() ) {				// buffer not open
	warning( "QBuffer::readBlock: Buffer not open" );
	return -1;
    }
    if ( !isReadable() ) {			// reading not permitted
	warning( "QBuffer::readBlock: Read operation not permitted" );
	return -1;
    }
#endif
    if ( (uint)index + len > a.size() ) {	// overflow
	if ( (uint)index >= a.size() ) {
#if defined(CHECK_RANGE)
	    warning( "QBuffer::readBlock: Buffer read error" );
#endif
	    setStatus( IO_ReadError );
	    return -1;
	}
	else
	    len = a.size() - (uint)index - 1;
    }
    memcpy( p, a.data()+index, len );
    index += len;
    return len;
}

/*!
  Writes \e len bytes from \e p to the buffer and returns the number of
  bytes actually written.

  Returns -1 if a serious error occurred.

  \sa readBlock()
*/

int QBuffer::writeBlock( const char *p, uint len )
{
#if defined(CHECK_NULL)
    if ( p == 0 && len != 0 )
	warning( "QBuffer::writeBlock: Null pointer error" );
#endif
#if defined(CHECK_STATE)
    if ( !isOpen() ) {				// buffer not open
	warning( "QBuffer::writeBlock: Buffer not open" );
	return -1;
    }
    if ( !isWritable() ) {			// writing not permitted
	warning( "QBuffer::writeBlock: Write operation not permitted" );
	return -1;
    }
#endif
    if ( (uint)index + len >= a_len ) {		// overflow
	uint new_len = a_len + a_inc*(((uint)index+len-a_len)/a_inc+1);
	if ( !a.resize( new_len ) ) {		// could not resize
#if defined(CHECK_NULL)
	    warning( "QBuffer::writeBlock: Memory allocation error" );
#endif
	    setStatus( IO_ResourceError );
	    return -1;
	}
	a_inc *= 2;				// double increment
	a_len = new_len;
	a.shd->len = (uint)index + len;
    }
    memcpy( a.data()+index, p, len );
    index += len;
    if ( a.shd->len < (uint)index )
	a.shd->len = (uint)index;		// fake (not alloc'd) length
    return len;
}


/*!
  Reads a line of text.

  Reads bytes from the buffer until end-of-line is reached, or up to
  \e maxlen bytes.

  \sa readBlock()
*/

int QBuffer::readLine( char *p, uint maxlen )
{
#if defined(CHECK_STATE)
    CHECK_PTR( p );
    if ( !isOpen() ) {				// buffer not open
	warning( "QBuffer::readLine: Buffer not open" );
	return -1;
    }
    if ( !isReadable() ) {			// reading not permitted
	warning( "QBuffer::readLine: Read operation not permitted" );
	return -1;
    }
#endif
    if ( maxlen == 0 )
	return 0;
    uint start = (uint)index;
    char *d = a.data() + index;
    maxlen--;					// make room for 0-terminator
    if ( a.size() - (uint)index < maxlen )
	maxlen = a.size() - (uint)index;
    while ( maxlen-- ) {
	if ( (*p++ = *d++) == '\n' )
	    break;
    }
    *p = '\0';
    index = d - a.data();
    return (uint)index - start;
}


/*!
  Reads a single byte/character from the buffer.

  Returns the byte/character read, or -1 if the end of the buffer has been
  reached.

  \sa putch(), ungetch()
*/

int QBuffer::getch()
{
#if defined(CHECK_STATE)
    if ( !isOpen() ) {				// buffer not open
	warning( "QBuffer::getch: Buffer not open" );
	return -1;
    }
    if ( !isReadable() ) {			// reading not permitted
	warning( "QBuffer::getch: Read operation not permitted" );
	return -1;
    }
#endif
    if ( (uint)index+1 > a.size() ) {		// overflow
#if defined(CHECK_RANGE)
	warning( "QBuffer::getch: Buffer read error" );
#endif
	setStatus( IO_ReadError );
	return -1;
    }
    return *(a.data()+index++);
}

/*!
  Writes the character \e ch to the buffer.

  Returns \e ch, or -1 if some error occurred.

  \sa getch(), ungetch()
*/

int QBuffer::putch( int ch )
{
#if defined(CHECK_STATE)
    if ( !isOpen() ) {				// buffer not open
	warning( "QBuffer::putch: Buffer not open" );
	return -1;
    }
    if ( !isWritable() ) {			// writing not permitted
	warning( "QBuffer::putch: Write operation not permitted" );
	return -1;
    }
#endif
    if ( (uint)index + 1 >= a_len ) {		// overflow
	char buf[1];
	buf[0] = (char)ch;
	writeBlock( buf, 1 );
    }
    else {
	*(a.data() + index++) = (char)ch;
	if ( a.shd->len < (uint)index )
	    a.shd->len = (uint)index;
    }
    return ch;
}

/*!
  Puts the character \e ch back into the buffer and decrements the index if
  it is not zero.

  This function is normally called to "undo" a getch() operation.

  Returns \e ch, or -1 if some error occurred.

  \sa getch(), putch()
*/

int QBuffer::ungetch( int ch )
{
#if defined(CHECK_STATE)
    if ( !isOpen() ) {				// buffer not open
	warning( "QBuffer::ungetch: Buffer not open" );
	return -1;
    }
    if ( !isReadable() ) {			// reading not permitted
	warning( "QBuffer::ungetch: Read operation not permitted" );
	return -1;
    }
#endif
    if ( ch != -1 ) {
	if ( index )
	    index--;
	else
	    ch = -1;
    }
    return ch;
}
