/*
 * Author:
 *         Kendy Kutzner <kutzner@ira.uka.de>
 *
 * (c) 2005 Research Group Dr. Fuhrmann, University of Karlsruhe, Germany
 * All rights reserved.
 *
 * $Id: buf.cc,v 1.18 2005/12/29 16:21:58 kutzner Exp $
 *
 */

/** \file
 * Implementation of class \link buf buf \endlink
 */

#include <malloc.h>
#include <string.h>
#include <assert.h>

#include "buf.h"
#include "igor.h"
#include "debug.h"
#include "utils.h"

/** \class buf
 * \brief .
 *
 * Internally, buffers allocate a piece of memory via malloc() and keep track
 * of allocated amount.  This implementation is not very efficient, because of
 * frequent calls to memcpy() and realloc().
 * \todo A better implementation could always allocate larger memory blocks and
 * leave some space at the front and at the end. Reallocations would only be
 * neccessary if the larger allocation is exhausted or most of the buffer is
 * not used any more. On the other hand, a good malloc()/free() is useful too.
 */


/**
 * Add an arbitrary byte string to the end of the buffer.
 * \param ptr The pointer to the bytes to be added.
 * \param size The number of octets to be added.
 */

void 
buf::put(const uint8 * ptr, int size){
	if(size == 0) return;
	assert(size>0);
	_buf = (uint8*)realloc(_buf, _size+size);
	assert(_buf);
	memcpy(_buf+_size, ptr, size);
	_size+=size;
	return;
}

/**
 * Copy one buffer to the end of another.
 * \param other The buffer to be added. This buffer is not modified.
 */

void
buf::put(buf * other){
	put(other->getraw(), other->size());
}

/**
 * Add a C++ string to the end of the buffer.
 * \param s The string to be added. The string is not modified.
 */

void
buf::put(string s){
	put((uint8*)s.c_str(), s.size());
}

/**
 * Get some bytes from the beginning of the buffer.
 * The bytes are removed from the buffer.
 * \param ptr Where the data is stored. If the pointer is NULL the data is sent to the bit bucket.
 * \param size How many bytes are copied.
 * \return The amount of octets transfered. This number may be smaller than len in case the buffer didn't contain enough bytes.
 */

int 
buf::get(uint8 * ptr, int size){
	assert(size>=0);
	int cpy = MIN(size, _size);
	if (cpy == 0) {
		return 0;
	}
	if (ptr)
		memcpy(ptr, _buf, cpy); // copy the data out
	memmove(_buf, _buf+cpy, _size-cpy); // move the buffer content
	_size -= cpy;
	assert(_size>=0);
	if (_size!=0){
		_buf = (uint8*)realloc(_buf, _size); // adjust allcd mem
		assert(_buf);
	} else {
		free(_buf);
		_buf = NULL;
	}
	return cpy;
}

/**
 * Get a line of text from the buffer.  A line of text is
 * characterized by an arbitrary number of arbitrary bytes
 * followed by a single newline (0x0a) character. The line
 * itself and the newline character are removed from the
 * buffer.
 * \return A newly allocated buf object containing the line without the trailing newline. If there was no complete line in the buffer (e.g. no newline found), NULL is returned.
 */

buf *
buf::getline(){
	dbg.print(dbg.BUF, 6, "getline: this=%p, _size=%i, _buf=%p\n", (void*)this, _size, (void*)_buf);
	int i=0;
	bool found = false;
	for (i = 0 ; i < _size; i++){
		dbg.print(dbg.BUF, 7, "checking byte %i\n", i);
		if (_buf[i]=='\n'){
			found = true;
			break;
		}
	}
	if (!found){
		dbg.print(dbg.BUF, 6, "getline: nothing found\n");
		return NULL;
	} else {
		uint8 * t = new uint8[i]; // temp storage, because copy buf->buf not available
		buf * b = new buf; // what we will return
		get(t, i); // string->temp
		b->put(t, i); // temp->result
		delete[] t;
		dbg.print(dbg.BUF, 6, "getline: something found\n");
		uint8 c;
		get(&c, 1); // eat newline
		return b;
	}
}

/**
 * Adds some bytes at the beginning of a buffer.
 * This function can undo a get() operation.
 * This function is similar to the add() method.
 * Contrary to add(), which puts new bytes at the end, this method adds the new bytes at the beginning of the buffer.
 * Calls can be expensive, since the function calls realloc() an memcpy()/memmove().
 * \param ptr The pointer to the bytes to be added.
 * \param size The number of octets to be added.
 */

void 
buf::unget(uint8 * ptr, int size){ // inefficient when the data was just removed by get()
	assert(size>0);
	_buf = (uint8*)realloc(_buf, _size+size);
	assert(_buf);
	memmove(_buf+size, _buf, _size); // move old data
	memcpy(_buf, ptr, size); // copy 'new' data in
	_size+=size;
	return;
}

/**
 * Deletes the content of the buffer, but not the buffer itself.
 */

void 
buf::clear(){
	assert(_size>=0);
	if (_size){
		free(_buf);
	}
	_size = 0;
	_buf = NULL;
	return;
}

/**
 * dtor
 */

buf::~buf(){
	clear();
	return;
}

