//---------------------------------------------------------------------------
//	Greenplum Database
//	Copyright (C) 2010 Greenplum, Inc.
//
//	@filename:
//		CStringStatic.cpp
//
//	@doc:
//		Implementation of the ASCII-character String class
//		with static buffer allocation
//---------------------------------------------------------------------------

#include "gpos/string/CStringStatic.h"

#include "gpos/common/clibwrapper.h"
#include "gpos/string/CWStringStatic.h"



using namespace gpos;


//---------------------------------------------------------------------------
//	@function:
//		CStringStatic::CStringStatic
//
//	@doc:
//		Ctor - initializes with empty string
//
//---------------------------------------------------------------------------
CStringStatic::CStringStatic(CHAR buffer[], ULONG capacity)
	: m_buffer(buffer), m_length(0), m_capacity(capacity)
{
	GPOS_ASSERT(nullptr != buffer);
	GPOS_ASSERT(0 < m_capacity);

	m_buffer[0] = CHAR_EOS;
}


//---------------------------------------------------------------------------
//	@function:
//		CStringStatic::CStringStatic
//
//	@doc:
//		Ctor with string initialization
//
//---------------------------------------------------------------------------
CStringStatic::CStringStatic(CHAR buffer[], ULONG capacity,
							 const CHAR init_str[])
	: m_buffer(buffer), m_length(0), m_capacity(capacity)
{
	GPOS_ASSERT(nullptr != buffer);
	GPOS_ASSERT(0 < m_capacity);

	AppendBuffer(init_str);
}


//---------------------------------------------------------------------------
//	@function:
//		CStringStatic::Equals
//
//	@doc:
//		Checks whether the string is byte-wise equal to a given string literal
//
//---------------------------------------------------------------------------
BOOL
CStringStatic::Equals(const CHAR *buf) const
{
	GPOS_ASSERT(nullptr != buf);

	ULONG length = clib::Strlen(buf);
	return (m_length == length && 0 == clib::Strncmp(m_buffer, buf, length));
}


//---------------------------------------------------------------------------
//	@function:
//		CStringStatic::Append
//
//	@doc:
//		Appends a string
//
//---------------------------------------------------------------------------
void
CStringStatic::Append(const CStringStatic *str)
{
	AppendBuffer(str->Buffer());
}


//---------------------------------------------------------------------------
//	@function:
//		CStringStatic::AppendBuffer
//
//	@doc:
//		Appends the contents of a buffer to the current string
//
//---------------------------------------------------------------------------
void
CStringStatic::AppendBuffer(const CHAR *buf)
{
	GPOS_ASSERT(nullptr != buf);
	ULONG length = clib::Strlen(buf);
	if (0 == length || m_capacity == m_length)
	{
		return;
	}

	// check if new length exceeds capacity
	if (m_capacity <= length + m_length)
	{
		// truncate string
		length = m_capacity - m_length - 1;
	}

	GPOS_ASSERT(m_capacity > length + m_length);

	clib::Strncpy(m_buffer + m_length, buf, length + 1);
	m_length += length;

	// terminate string
	m_buffer[m_length] = CHAR_EOS;

	GPOS_ASSERT(IsValid());
}


//---------------------------------------------------------------------------
//	@function:
//		CStringStatic::AppendFormat
//
//	@doc:
//		Appends a formatted string
//
//---------------------------------------------------------------------------
void
CStringStatic::AppendFormat(const CHAR *format, ...)
{
	VA_LIST va_args;

	// get arguments
	VA_START(va_args, format);

	AppendFormatVA(format, va_args);

	// reset arguments
	VA_END(va_args);
}


//---------------------------------------------------------------------------
//	@function:
//		CStringStatic::AppendFormatVA
//
//	@doc:
//		Appends a formatted string based on passed va list
//
//---------------------------------------------------------------------------
void
CStringStatic::AppendFormatVA(const CHAR *format, VA_LIST va_args)
{
	GPOS_ASSERT(nullptr != format);

	// available space in buffer
	ULONG ulAvailable = m_capacity - m_length;

	// format string
	(void) clib::Vsnprintf(m_buffer + m_length, ulAvailable, format, va_args);

	// terminate string
	m_buffer[m_capacity - 1] = CHAR_EOS;
	m_length = clib::Strlen(m_buffer);

	GPOS_ASSERT(m_capacity > m_length);

	GPOS_ASSERT(IsValid());
}


//---------------------------------------------------------------------------
//	@function:
//		CStringStatic::AppendWsz
//
//	@doc:
//		Appends wide character string
//
//---------------------------------------------------------------------------
void
CStringStatic::AppendConvert(const WCHAR *wc_str)
{
	ULONG length_entry = GPOS_WSZ_LENGTH(wc_str);

	if (m_capacity - m_length < length_entry)
	{
		length_entry = m_capacity - m_length - 1;
	}

	for (ULONG i = 0; i < length_entry; i++)
	{
		CHAR str_convert[MB_LEN_MAX];

		/* convert wide character to multi-byte array */
		ULONG char_length = clib::Wctomb(str_convert, wc_str[i]);
		GPOS_ASSERT(0 < char_length);

		// check if wide character is ASCII-compatible
		if (1 == char_length)
		{
			// simple cast; works only for ASCII characters
			m_buffer[m_length] = CHAR(wc_str[i]);
		}
		else
		{
			// substitute wide character
			m_buffer[m_length] = GPOS_WCHAR_UNPRINTABLE;
		}
		m_length++;
	}

	m_buffer[m_length] = CHAR_EOS;
	GPOS_ASSERT(IsValid());
}


//---------------------------------------------------------------------------
//	@function:
//		CStringStatic::Reset
//
//	@doc:
//		Resets string
//
//---------------------------------------------------------------------------
void
CStringStatic::Reset()
{
	m_buffer[0] = CHAR_EOS;
	m_length = 0;
}


#ifdef GPOS_DEBUG

//---------------------------------------------------------------------------
//	@function:
//		CStringStatic::IsValid
//
//	@doc:
//		Checks whether a string is properly null-terminated
//
//---------------------------------------------------------------------------
bool
CStringStatic::IsValid() const
{
	return (m_length == clib::Strlen(m_buffer));
}

#endif	// GPOS_DEBUG


// EOF
