// Common, group Buffers
// Copyright Alexander Liss

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

// memory allocation in multiples of "increment"
int StrBuffer::allocate(int size)
{

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

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

	memset(data,0,allocated);

	return 0;
}

// copy function used by copy constructor and assignment operator
int StrBuffer::copy(const StrBuffer& s)
{
	if(!data) return 1;

	int g=0;

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

	memcpy(data,s.data,s.data_size);
	data_size=s.data_size;

	return 0;
}

// constructor, which allows direct setting of "increment"
StrBuffer::StrBuffer(int initial_allocation,int Increment):
	increment(Increment),data_size(0),allocated(0),data(0)
{
	if(increment<=0) increment=8;

	if(initial_allocation<=0) return;

	allocate(initial_allocation);
	
}

// destructor clears memory before deleting data
StrBuffer::~StrBuffer()
{
	if(data) memset(data,0,allocated);
	delete []data;
}

// memory reallocation in multiples of "increment"
int StrBuffer::reallocate(int size)
{
	if(size<=0) size=increment;

	if(!data)
		return allocate(size);

	if(size<data_size+1) return 1; 

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

	char *old_data=data;
	int old_allocated=allocated;

	data=new 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);

		memset(old_data,0,old_allocated);

		delete []old_data;
	}

	return 0;
}


int StrBuffer::set(const 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 StrBuffer::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 StrBuffer::set(const char * str)
{
	return set(str,strlen(str));
}

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

	int g=0;

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

	memcpy(data+data_size,from,size);
	data_size+=size;
	data[data_size]=0;
	return 0;
}

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

	int g=0;

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

	memset(data+data_size,s,size);
	data_size+=size;
	data[data_size]=0;
	return 0;
}


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


// byte data[n] becomes data[0], first n bytes are dropped
int StrBuffer::shift(int n)
{
	if(n<0) return 1;

	if(n==0) return 0;

	if(n>=data_size)
	{
		memset(data,0,allocated);
		data_size=0;
		return 0;
	}

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

	return 0;

}