// (C) Copyright 2003-2004: Reece H. Dunn
//
//	Safe string operations
//
//	Original by Reece H. Dunn.
//	Modified by J. Nagle.
//
//
//	The templated class fixed_string<size> is provided for
//	declaring fixed size strings that do not use "new", and
//	the abstract base class fixed_string_base is provided
//	for references to fixed_string of any size.
//
//	These classes are intended to be safe against store-type buffer
//	overflows.
//	All storing subscripts must be checked.
//	Non-const char* pointers must not be returned.
//
//	The usual STL operations are to be provided for fixed_string,
//	along with the classic C string operations.
//
//	Direct replacement of C strings with fixed_string should
//	either work correctly or result in compile errors.
//
#ifndef BOOST_FIXED_STRING_HPP
#define BOOST_FIXED_STRING_HPP
#include <cstring>
#include <stddef.h>
#include <stdarg.h>
#include <stdexcept>
#include <string>
#ifdef _MSC_VER
namespace std
{
typedef ::size_t size_t;		// VC++ 6 problem
}
#endif // _MSC_VER
namespace boost
{
//	Abstract base class for templated strings.
//	This can be passed to functions, regardless of size.
template <class T>
class fixed_string_base
{
public:
    virtual std::size_t capacity() const = 0;
    virtual std::size_t size() = 0;
    virtual const T* c_str() const = 0;
    virtual T* data() = 0;
    virtual int vsnprintf(const T* fs, const va_list& args) = 0;
    virtual void copy(const T * s ) = 0;
	virtual void clear() = 0;
    virtual void append(const T* s) = 0;
    virtual T& operator[](std::size_t ix) = 0;
    virtual const T& operator[](std::size_t ix) const = 0;

	typedef typename T* iterator;
    typedef typename const T* const_iterator;
	//	Implemented functions without direct equivalents at the fixed_string level.
	fixed_string_base<T>& operator=(const fixed_string_base<T>& s) 
	{	copy(s.c_str()); return(*this); }
	fixed_string_base<T>& operator=(const T* s)
	{	copy(s2); return(*this); }
	int comp(fixed_string_base<T>& s1, 
			const fixed_string_base<T>& s2)
	{	s1.comp(s2.c_str()); return(s1); }
	bool operator==(const fixed_string_base<T>& s)
	{	return(comp(s) == 0); }
	bool operator==(const T* s)
	{	return(comp(s) == 0); }
};
//
//	class fixed_string
//
//	Generic template class for fixed size strings.
//
//	One extra space is always allocated for a trailing null.
//	But only calls to c_str guarantee that the trailing
//	null will be present.
//
template< class T, std::size_t N>
class fixed_string
            : public fixed_string_base<T>
{
private:
    T m_str[N+1];		// the data
    int m_len;			// length, not including trailing null
    // -1 if unknown
private:
    void update_len()	// update length if necessary
    {	if (m_len >= 0) return;	// length needs no update
        for (m_len = 0; m_len < N && m_str[m_len]; m_len++)
        {} // count
        m_str[m_len] = 0;
    }

    void invalidate_len()	// invalidate length. String has changed
    {	m_len = -1;
    }

public:
    fixed_string()			// default constructor
    :
    m_len(0)
    {
        m_str[0] = 0;		// initially null
        m_str[N] = 0;		// last must always be a null
    }

    fixed_string(const T* s)// constructor from string
    :
    m_len(0)
    {
        m_str[0] = 0;		// initially null
        m_str[N] = 0;		// last must always be a null
		copy(s);			// copy input
    }
	

    fixed_string(std::size_t cnt, T ch)	// constructor from char
    :
    m_len(0)
    {
        m_str[0] = 0;		// initially null
        m_str[N] = 0;		// last must always be a null
 		while (cnt--) this->+= ch;	// append N copies of char
    }

public: // access
    T* data() 				// access to raw data - unsafe
    {	invalidate_len();	// invalidates length count
        return(m_str);
    }
    const T* c_str() const
    {	//	"Virtual constness" - must update length info and trailing null
        fixed_string<T,N>* s =
            const_cast<fixed_string<T,N>* > (this);
        s->update_len();
        s->m_str[m_len] = 0;
        return( m_str );
    }

    std::size_t capacity() const
    {
        return(N);
    }

    std::size_t size()
    {
        update_len();
        return(m_len);
    }

    void clear()
    {
        m_len = 0;
        m_str[0] = 0;
    }

    void operator+=(const T& ch)
    {
        update_len();
        if (m_len >= N-1)
            throw(std::length_error("fixed_string +="));		// fails
        m_str[m_len] = ch;
        m_len++;
        m_str[m_len] = 0;
    }
	//	Subscripting.  
	//	Note that you can address the extra space for the trailing
	//	null, so that the classic "for (int i=0; s[i]; i++) will work.
public: 
    T& operator[](std::size_t ix)
    {
        invalidate_len();
        if (ix <= N)
            return(m_str[ix]);		// normal case
        throw(std::length_error("fixed_string []"));		// fails
    }

    const T& operator[](std::size_t ix) const
    {
        if (ix <= N)
            return(m_str[ix]);		// normal case
        throw(std::length_error("fixed_string []"));		// fails
    }

public:	// iterators, unchecked
	iterator begin()	{	return(&m_str[0]); }
	const iterator begin() const	{	return(&m_str[0]); }
	iterator end()		{	return(begin() + size() + 1); }
	const iterator end() const	{	return(begin() + size() + 1); }

public: // copy
    void copy(const fixed_string_base<T>& s)
    {
        copy(s.c_str());
    }

    void copy(const T* s)
    {
        clear();
        append(s);		// inefficient, test only
    }


public:  // append
    void append(const fixed_string_base<T>& s)
    {
        append(s.c_str());
    }

    void append(const T* s)
    {
        for (int i=0; s[i] !=0; i++)
        {
            *this += s[i];
        }
    }
public: // comparison
    inline int comp(const char* s) const
    {
        return(compare(c_str(),s));		// from char_traits
    }
    inline int comp(const fixed_string_base<T>& s) const
    {
        return(compare(c_str(),s,c_str())); // from char_traits
    }
};

//
//	char_string, char_string_base -- fixed string for "char"
//
template <std::size_t N>
class char_string
            : public fixed_string<char, N>
{	//	Functions which differ for different types go here
    int vsnprintf(const char* fs, const va_list& args)
    {
#ifdef _MSC_VER	
        return(::_vsnprintf(data(), N, fs, args ));
#else
		return(::vsnprintf(data(), N, fs, args ));
#endif
    }
};
typedef fixed_string_base<char> char_string_base;
//
//	wchar_string, wchar_string_base - fixed string for "wchar_t"
//
template <std::size_t N>
class wchar_string
            : public fixed_string<wchar_t, N>
{	//	Functions which differ for different types go here
    int vsnprintf(const wchar_t* fs, const va_list& args)
    {
#ifdef _MSC_VER
        return(::_vsnwprintf(data(), N, fs, args ));
#else
		return(::wvsnprintf(data(), N, fs, args ));
#endif
    }
};
typedef fixed_string_base<wchar_t> wchar_string_base;
}
#endif // BOOST_FIXED_STRING
