// Copyright (c) 2002 David Muse
// See the COPYING file for more information

#include <rudiments/variablebuffer.h>
#include <rudiments/charstring.h>
#include <rudiments/rawbuffer.h>

#include <stdio.h>

#ifdef RUDIMENTS_NAMESPACE
namespace rudiments {
#endif

class variablebufferprivate {
	friend class variablebuffer;
	private:
		unsigned char	*_buffer;
		size_t		_buffersize;
		size_t		_initialsize;
		size_t		_increment;
		size_t		_position;
		size_t		_endofbuffer;
};

variablebuffer::variablebuffer(size_t initialsize, size_t increment) {
	init(new unsigned char[initialsize],initialsize,increment);
}

variablebuffer::variablebuffer(unsigned char *initialcontents,
				size_t initialsize, size_t increment) {
	init(initialcontents,initialsize,increment);
}

void variablebuffer::init(unsigned char *initialcontents,
				size_t initialsize, size_t increment) {
	pvt=new variablebufferprivate;
	pvt->_buffer=initialcontents;
	pvt->_buffersize=initialsize;
	pvt->_initialsize=initialsize;
	pvt->_increment=increment;
	pvt->_position=0;
	pvt->_endofbuffer=0;
}

variablebuffer::variablebuffer(const variablebuffer &v) {
	variablebufferClone(v);
}

variablebuffer &variablebuffer::operator=(const variablebuffer &v) {
	if (this!=&v) {
		delete[] pvt->_buffer;
		delete pvt;
		variablebufferClone(v);
	}
	return *this;
}

variablebuffer::~variablebuffer() {
	delete[] pvt->_buffer;
	delete pvt;
}

void variablebuffer::variablebufferClone(const variablebuffer &v) {
	pvt=new variablebufferprivate;
	pvt->_initialsize=v.pvt->_initialsize;
	pvt->_increment=v.pvt->_increment;
	pvt->_buffersize=v.pvt->_buffersize;
	pvt->_position=v.pvt->_position;
	pvt->_endofbuffer=v.pvt->_endofbuffer;
	pvt->_buffer=new unsigned char[v.pvt->_buffersize];
	rawbuffer::copy(pvt->_buffer,v.pvt->_buffer,pvt->_buffersize);
}

ssize_t variablebuffer::read(unsigned char *data, size_t size) {

	size_t	bytestoread=size;
	if (pvt->_position>pvt->_endofbuffer) {
		bytestoread=0;
	} else if (pvt->_position+size>pvt->_endofbuffer) {
		bytestoread=pvt->_endofbuffer-pvt->_position;
	}

	rawbuffer::copy(data,pvt->_buffer+pvt->_position,bytestoread);
	pvt->_position=pvt->_position+bytestoread;

	return bytestoread;
}

variablebuffer *variablebuffer::write(const unsigned char *data, size_t size) {

	// if the buffer is too small, extend it
	if (pvt->_position>=pvt->_buffersize) {
		extend(pvt->_position-pvt->_buffersize+size);
	} else if (size>=pvt->_buffersize-pvt->_position) {
		extend(pvt->_buffersize-pvt->_position+size);
	}

	// copy the data into the buffer
	rawbuffer::copy(pvt->_buffer+pvt->_position,data,size);

	// increment the position indices
	pvt->_position=pvt->_position+size;
	if (pvt->_position>pvt->_endofbuffer) {
		pvt->_endofbuffer=pvt->_position;
	}
	return this;
}

variablebuffer *variablebuffer::write(const char *string) {
	return write(reinterpret_cast<const unsigned char *>(string),
					charstring::length(string));
}

variablebuffer *variablebuffer::write(const char *string, size_t size) {
	return write(reinterpret_cast<const unsigned char *>(string),size);
}

variablebuffer *variablebuffer::write(char character) {
	return write(reinterpret_cast<const unsigned char *>(&character),
								sizeof(char));
}

variablebuffer *variablebuffer::write(int16_t number) {
	return write(reinterpret_cast<const unsigned char *>(&number),
							sizeof(int16_t));
}

variablebuffer *variablebuffer::write(int32_t number) {
	return write(reinterpret_cast<const unsigned char *>(&number),
							sizeof(int32_t));
}

variablebuffer *variablebuffer::write(int64_t number) {
	return write(reinterpret_cast<const unsigned char *>(&number),
							sizeof(int64_t));
}

variablebuffer *variablebuffer::write(unsigned char character) {
	return write(reinterpret_cast<const unsigned char *>(&character),
							sizeof(unsigned char));
}

variablebuffer *variablebuffer::write(uint16_t number) {
	return write(reinterpret_cast<const unsigned char *>(&number),
							sizeof(uint16_t));
}

variablebuffer *variablebuffer::write(uint32_t number) {
	return write(reinterpret_cast<const unsigned char *>(&number),
							sizeof(uint32_t));
}

variablebuffer *variablebuffer::write(uint64_t number) {
	return write(reinterpret_cast<const unsigned char *>(&number),
							sizeof(uint64_t));
}

variablebuffer *variablebuffer::write(float number) {
	return write(reinterpret_cast<const unsigned char *>(&number),
								sizeof(float));
}

variablebuffer *variablebuffer::write(double number) {
	return write(reinterpret_cast<const unsigned char *>(&number),
								sizeof(double));
}

void variablebuffer::clear() {
	delete[] pvt->_buffer;
	pvt->_buffer=new unsigned char[pvt->_initialsize];
	pvt->_buffersize=pvt->_initialsize;
	pvt->_position=0;
	pvt->_endofbuffer=0;
}

void variablebuffer::extend(size_t size) {
	size_t	newbuffersize=pvt->_buffersize+
				((size/pvt->_increment)*pvt->_increment)+
				(((size%pvt->_increment)>0)*pvt->_increment);
	unsigned char	*newbuffer=new unsigned char[newbuffersize];
	rawbuffer::copy(newbuffer,pvt->_buffer,pvt->_buffersize);
	delete[] pvt->_buffer;
	pvt->_buffer=newbuffer;
	pvt->_buffersize=newbuffersize;
}

const unsigned char *variablebuffer::getBuffer() {
	return pvt->_buffer;
}

unsigned char *variablebuffer::detachBuffer() {
	unsigned char	*retval=pvt->_buffer;
	pvt->_buffer=new unsigned char[pvt->_initialsize];
	pvt->_buffersize=pvt->_initialsize;
	pvt->_position=0;
	pvt->_endofbuffer=0;
	return retval;
}

size_t variablebuffer::getSize() {
	return pvt->_endofbuffer;
}

size_t variablebuffer::getPosition() {
	return pvt->_position;
}

size_t variablebuffer::getEnd() {
	return pvt->_endofbuffer;
}

size_t variablebuffer::getActualSize() {
	return pvt->_buffersize;
}

void variablebuffer::setPosition(size_t pos) {
	pvt->_position=pos;
}

variablebuffer *variablebuffer::append(const unsigned char *data, size_t size) {
	pvt->_position=pvt->_endofbuffer;
	return write(data,size);
}

variablebuffer *variablebuffer::append(const char *string) {
	return append(reinterpret_cast<const unsigned char *>(string),
						charstring::length(string));
}

variablebuffer *variablebuffer::append(const char *string, size_t size) {
	return append(reinterpret_cast<const unsigned char *>(string),size);
}

variablebuffer *variablebuffer::append(char character) {
	return append(reinterpret_cast<const unsigned char *>(&character),
								sizeof(char));
}

variablebuffer *variablebuffer::append(int16_t number) {
	return append(reinterpret_cast<const unsigned char *>(&number),
							sizeof(int16_t));
}

variablebuffer *variablebuffer::append(int32_t number) {
	return append(reinterpret_cast<const unsigned char *>(&number),
							sizeof(int32_t));
}

variablebuffer *variablebuffer::append(int64_t number) {
	return append(reinterpret_cast<const unsigned char *>(&number),
							sizeof(int64_t));
}

variablebuffer *variablebuffer::append(unsigned char character) {
	return append(reinterpret_cast<const unsigned char *>(&character),
							sizeof(unsigned char));
}

variablebuffer *variablebuffer::append(uint16_t number) {
	return append(reinterpret_cast<const unsigned char *>(&number),
							sizeof(uint16_t));
}

variablebuffer *variablebuffer::append(uint32_t number) {
	return append(reinterpret_cast<const unsigned char *>(&number),
							sizeof(uint32_t));
}

variablebuffer *variablebuffer::append(uint64_t number) {
	return append(reinterpret_cast<const unsigned char *>(&number),
							sizeof(uint64_t));
}

variablebuffer *variablebuffer::append(float number) {
	return append(reinterpret_cast<const unsigned char *>(&number),
								sizeof(float));
}

variablebuffer *variablebuffer::append(double number) {
	return append(reinterpret_cast<const unsigned char *>(&number),
								sizeof(double));
}

unsigned char *variablebuffer::_buffer() {
	return pvt->_buffer;
}

size_t variablebuffer::_endofbuffer() {
	return pvt->_endofbuffer;
}

void variablebuffer::_endofbuffer(size_t eob) {
	pvt->_endofbuffer=eob;
}

size_t variablebuffer::_position() {
	return pvt->_position;
}

void variablebuffer::_position(size_t pos) {
	pvt->_position=pos;
}

#ifdef RUDIMENTS_NAMESPACE
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1