// Common, group Buffers
// Copyright Alexander Liss

#include <string.h>
#include "commbuf.h"

	// CommBuffer

int CommBuffer::allocate(int size)
{

	if(size<=0) allocated=increment;
	else
	{
		allocated=size/increment*increment;
		if(allocated<size)
			allocated+=increment;
	}

	data=new unsigned char[allocated];
	if(!data) 
	{
		allocated=0;
		return 1;
	}

	memset(data,0,allocated);

	return 0;
}

int CommBuffer::copy(const CommBuffer& s)
{
	if(!data) return 1;

	int g=0;

	if(allocated<s.data_size+s.write_offset+1)
		g=reallocate(s.data_size+s.write_offset+1);
	if(g) return 1;

	memcpy(data,s.data,s.data_size+s.write_offset);
	data_size=s.data_size;
	write_offset=s.write_offset;
	encoding=s.encoding;
	
	return 0;
}


CommBuffer::~CommBuffer()
{
	if(data) memset(data,0,allocated);
	delete []data;
}

int CommBuffer::reallocate(int size)
{
	if(size<=0) size=increment;

	if(!data)
		return allocate(size);

	if(size<data_size+write_offset+1) return 1; 

	int new_size=size/increment*increment;
	if(new_size<size)
			new_size+=increment;

	unsigned char *old_data=data;
	int old_allocated=allocated;

	data=new unsigned char[new_size];
	if(!data) 
	{	
		// restore
		data=old_data;
		return 2;
	}

	allocated=new_size;
	memset(data,0,allocated);

	if(old_data)
	{
		memcpy(data,old_data,data_size+write_offset);

		memset(old_data,0,old_allocated);

		delete []old_data;
	}

	return 0;
}


int CommBuffer::set(const unsigned char * from,int size)
{
	data_size=0;

	if(size<=0 || from==0)
	{
		if(!data)
			allocate(increment);

		return 1;
	}

	if(!data)
		allocate(size+1);

	if(!data) return 2;

	return append(from,size);
}

int CommBuffer::set(int size, char s)
{

	data_size=0;

	if(size<=0 )
	{
		if(!data)
			allocate(increment);

		return 1;
	}

	if(!data)
		allocate(size+1);

	if(!data) return 2;

	return append(size,s);
}


int CommBuffer::set(const char * str)
{
	return set(str,strlen(str));
}


int CommBuffer::append(const unsigned char * from,int size)
{
	if(size<=0 || from==0) return 1;

	int g=0;

	if(data_size+size+1>back_allocated())
		g=reallocate(write_offset+data_size+size+1);
	if(g) return 2;

	memcpy(write_start()+data_size,from,size);
	data_size+=size;
	write_start()[data_size]=0;

	return 0;
}

int CommBuffer::append(int size, char s)
{
	if(size<=0) return 1;

	int g=0;

	if(data_size+size+1>back_allocated())
		g=reallocate(write_offset+data_size+size+1);
	if(g) return 2;

	memset(write_start()+data_size,s,size);
	data_size+=size;
	write_start()[data_size]=0;

	return 0;
}

int CommBuffer::append(const char * str)
{
	return append(str,strlen(str));
}


int CommBuffer::prepend(const unsigned char * from,int size)
{
	if(size<=0 || from==0) return 1;

	if(size>front_allocated())
		return 2;

	memcpy(write_start()-size,from,size);

	write_offset-=size;
	data_size+=size;

	return 0;
}

int CommBuffer::prepend(int size, char s)
{
	if(size<=0) return 1;

	if(size>front_allocated())
		return 2;

	memset(write_start()-size,s,size);

	write_offset-=size;
	data_size+=size;

	return 0;
}


// byte data[n+write_offset] becomes data[write_offset]
int CommBuffer::shift(int n)
{
	if(n<0) return 1;

	if(n==0) return 0;

	if(n>=data_size)
	{
		memset(write_start(),0,back_allocated());
		data_size=0;
		return 0;
	}

	// copy trailing zero
	memcpy(write_start(),write_start()+n,data_size-n+1);
	data_size-=n;

	return 0;

}