/*
								+----------------------------------+
								|                                  |
								|  ***   Data buffer struct   ***  |
								|                                  |
								|   Copyright © -tHE SWINe- 2009   |
								|                                  |
								|            Buffer.cpp            |
								|                                  |
								+----------------------------------+
*/

/**
 *	@file Buffer.cpp
 *	@author -tHE SWINe-
 *	@date 2009
 *	@brief Simple data buffer class.
 */

#include "NewFix.h"

#include "CallStack.h"
#include <vector>
#include <algorithm>
#include <limits.h>
#include <string.h> // memcpy()
//#include "Thread.h"
#include "MinMax.h"
#include "StlUtils.h"
#include "Buffer.h"

/*
 *								=== TBuffer ===
 */

/*
 *	TBuffer::TBuffer()
 *		- default constructor, initializes empty buffer
 */
TBuffer::TBuffer()
	:p_data(0), n_size(0), n_capacity(0)
{}

/*
 *	TBuffer::TBuffer(size_t _n_size)
 *		- creates a new buffer with size _n_size bytes
 *		- note buffer is going to be empty in case there was not enough memory
 */
TBuffer::TBuffer(size_t _n_size)
{
	n_capacity = n_size = (p_data = new(std::nothrow) uint8_t[_n_size])? _n_size : 0;
}

/*
 *	TBuffer::TBuffer(const TBuffer &r_t_buffer)
 *		- copy-constructor
 *		- note buffer is going to be empty in case there was not enough memory
 */
TBuffer::TBuffer(const TBuffer &r_t_buffer)
{
	n_capacity = n_size = (p_data = new(std::nothrow) uint8_t[r_t_buffer.n_size])? r_t_buffer.n_size : 0;
	memcpy(p_data, r_t_buffer.p_data, n_size * sizeof(uint8_t));
}

/*
 *	TBuffer &TBuffer::operator =(const TBuffer &r_t_buffer)
 *		- copy-operator
 *		- note buffer is going to be empty in case there was not enough memory
 */
TBuffer &TBuffer::operator =(const TBuffer &r_t_buffer)
{
	if(&r_t_buffer == this)
		return *this;
	// handle self-assignment

	if(p_data)
		delete[] p_data;
	n_capacity = n_size = (p_data = new(std::nothrow) uint8_t[r_t_buffer.n_size])? r_t_buffer.n_size : 0;
	memcpy(p_data, r_t_buffer.p_data, n_size * sizeof(uint8_t));
	return *this;
}

/*
 *	TBuffer::~TBuffer()
 *		- destructor
 *		- deletes buffer data
 */
TBuffer::~TBuffer()
{
	if(p_data)
		delete[] p_data;
}

/*
 *	void TBuffer::Swap(TBuffer &r_t_buffer)
 *		- swaps contents of this and r_t_buffer
 */
void TBuffer::Swap(TBuffer &r_t_buffer)
{
	size_t n_tmp_size = n_size;
	n_size = r_t_buffer.n_size;
	r_t_buffer.n_size = n_tmp_size;
	size_t n_tmp_capa = n_capacity;
	n_capacity = r_t_buffer.n_capacity;
	r_t_buffer.n_capacity = n_tmp_capa;
	uint8_t *p_tmp_data = p_data;
	p_data = r_t_buffer.p_data;
	r_t_buffer.p_data = p_tmp_data;
}

/*
 *	bool TBuffer::Resize(size_t n_new_size, bool b_data_important = true)
 *		- changes size of the buffer
 *		- note that resizing below buffer capacity never causes reallocations
 *		- if b_data_important is set, buffer data are copied when reallocating,
 *		  otherwise they're not (speed optimization)
 *		- returns true on success, false on failure (data aren't lost on failure,
 *		  but buffer size remains unchaged)
 */
bool TBuffer::Resize(size_t n_new_size, bool b_data_important)
{
	if(n_new_size <= n_capacity) {
		n_size = n_new_size;
		return true;
	}
	// don't have to resize

	size_t n_new_capacity = n_capacity;
	n_new_capacity *= 2;
	if(n_new_capacity < n_new_size) // overflow / too large resize step
		n_new_capacity = n_new_size;
	// calculate new capacity

	uint8_t *p_new_data;
	if(!(p_new_data = new(std::nothrow) uint8_t[n_new_capacity]))
		return false;
	// alloc

	if(p_data) {
		if(b_data_important)
			memcpy(p_new_data, p_data, n_size * sizeof(uint8_t));
		delete[] p_data;
	}
	// dealloc, copy data if required

	p_data = p_new_data;
	n_size = n_new_size;
	n_capacity = n_new_capacity;
	// set new buffer

	return true;
}

/*
 *	bool TBuffer::Grow(size_t n_add)
 *		- resizes the buffer so it contains at least n_add more free bytes
 *		- returns true on success, false on failure (data aren't lost on failure,
 *		  but buffer size remains unchaged)
 */
bool TBuffer::Grow(size_t n_add)
{
	if(n_size > SIZE_MAX - n_add)
		return false; // overflow
	size_t n_required_size = n_size + n_add;
	if(!Resize(n_required_size))
		return false;
	_ASSERTE(n_size == n_required_size);
	// resize to max

	return true;
}

/*
 *	void TBuffer::Clear()
 *		- clears contents of the buffer (both size and capacity will be 0)
 */
void TBuffer::Clear()
{
	if(p_data)
		delete[] p_data;
	p_data = 0;
	n_size = 0;
	n_capacity = 0;
	// clears the buffer
}

/*
 *	size_t static TBuffer::n_Max_Size()
 *		- returns the greatest size possible buffer can be allocated to
 */
/*size_t TBuffer::n_Max_Size()
{
	return size_t(CMaxIntValue<size_t>::result());
}*/

/*
 *								=== ~TBuffer ===
 */
