/*
 * Copyright 1995, 1996 Perforce Software.  All rights reserved.
 *
 * This file is part of Perforce - the FAST SCM System.
 */

/*
 * StrBuf.h - multipurpose buffers
 *
 * StrPtr, StrRef, and StrBuf are used throughout the system, as buffers
 * for storing just about any variable length byte data. 
 *
 * StrPtr is a low-cost (no constructor, no destructor, 8 byte) 
 * pointer/length pair to mutable data.  It has a variety of methods
 * to mangle it.
 *
 * StrRef is a kind-of StrPtr that allows the buffer pointer to be set.
 * As StrPtr doesn't allow this, a StrPtr object itself isn't useful.
 *
 * StrNum is a kind-of StrPtr with a temporary buffer whose only purpose
 * is to hold the string representation of an int.
 *
 * StrBuf is a kind-of StrPtr that allocates and extends it own buffer.
 *
 * StrFixed is a kind-of StrPtr that points to a character array that
 * is fixed at construction.
 *
 * Classes:
 *
 *	StrPtr - a pointer/length for arbitrary data
 *	StrRef - StrPtr that can be set
 *	StrBuf - StrPtr of privately allocated data
 *	StrFixed - StrPtr to a fixed length char buffer
 *	StrNum - StrPtr that holds a string of an int
 *	StrHuman - StrPtr that holds a "human-readable" string of an int
 *
 * Methods:
 *
 *	StrPtr::Clear() - set length = 0
 *	StrPtr::Text() - return buffer pointer
 *	StrPtr::Value() - return buffer pointer (old name)
 *	StrPtr::Length() - return buffer length
 *	StrPtr::GetEnd() - return pointer to character past end
 *	StrPtr::Atoi() - convert to integer and return
 *	StrPtr::Atoi64() - convert to P4INT64 and return
 *	StrPtr::Atoi64( char*, P4INT64* ) - Atoi64() with error checking
 *	StrPtr::Itoa() - format an int given the end of a buffer
 *	StrPtr::Itoa64() - format a P4INT64 given the end of a buffer
 *	StrPtr::SetLength() - set only length
 *	StrPtr::SetEnd() - set length by calculating from start
 *	StrPtr::[] - get a single character
 *	StrPtr::XCompare() - case exact string compare
 *	StrPtr::CCompare() - case folding string compare
 *	StrPtr::CCompareN() - case folding string compare, length-limited
 *	StrPtr::SCompare() - case aware string compare -- see strbuf.cc
 *	StrPtr::SEqual() - case aware character compare -- see strbuf.cc
 *	StrPtr::Contains() - finds a substring
 *	StrPtr::== - compare contents with buffer
 *	StrPtr::!= - compare contents with buffer
 *	StrPtr::< - compare contents with buffer
 *	StrPtr::<= - compare contents with buffer
 *	StrPtr::> - compare contents with buffer
 *	StrPtr::>= - compare contents with buffer
 *	StrPtr::StrCpy() - copy string out to a buffer
 *	StrPtr::StrCat() - copy string out to end of a buffer
 *	StrPtr::CaseFolding() - (static) SCompare sorts A < a, a < B
 *	StrPtr::CaseIgnored() - (static) SCompare sorts A == a, a < B
 *	StrPtr::CaseHybrid() - (static) SCompare sorts Ax < ax, aa < AX
 *	StrPtr::SetCaseFolding() - (static) 0=UNIX, 1=NT, 2=HYBRID
 *
 *	---
 *
 *	StrRef::Set() - set pointer/length
 *	StrRef::+= - move pointer/length
 *
 *	---
 *
 *	StrBuf::StringInit() - mimic actions of constructor
 *	StrBuf::Set() - allocate and fill from buffer
 *	StrBuf::Append() - extend and terminate from buffer
 *	StrBuf::Extend() - append contents from buffer
 *	StrBuf::Terminate() - terminate buffer
 *	StrBuf::Alloc() - allocate space in buffer and return pointer
 *	StrBuf::<< - Append contents from buffer or number
 *	StrBuf::Indent() - fill by indenting contents of another buffer
 *	StrBuf::Expand() - expand a string doing %var substitutions
 *	
 */

class StrBuf;
class StrNum;

// On 64 bit platforms, the base 'size_t' type is 64 bits, which is much
// more than we need, or can handle. So we use our own size_t type instead;
// it's "p4size_t", defined in stdhdrs.h

// General String Buffer Sizes
# define SIZE_LINESTR     256
# define SIZE_SMALLSTR   1024
# define SIZE_MEDSTR     4096

class StrPtr {

    public:
	// Setting, getting

	char *	Text() const
		{ return buffer; }

	char *	Value() const
		{ return buffer; }

	unsigned char *UText() const
		{ return (unsigned char *)Text(); }

	p4size_t 	Length() const
		{ return length; }

	char *	End() const
		{ return Text() + length; }

	unsigned char *UEnd() const
		{ return UText() + length; }

	int	Atoi() const
		{ return Atoi( buffer ); }

	bool	IsNumeric( bool allowDouble = false ) const;

	int	StartsWith( const char *s, int l ) const;

	int	EndsWith( const char *s, int l ) const;

	P4INT64	Atoi64() const
		{ return Atoi64( buffer ); }

	static bool Atoi64( const char *p, P4INT64 *result );
	static bool Atoi64Lite( const char *p, P4INT64 *result );

	bool	Atoi64( P4INT64 *result ) const
		{ return Atoi64( buffer, result ); }

	void	SetLength() 
		{ length = (p4size_t)strlen( buffer ); }

	void	SetLength( p4size_t len )
		{ length = len; }

	void	SetEnd( char *p ) 
		{ length = (p4size_t)(p - buffer); }

	char	operator[]( p4size_t x ) const
		{ return buffer[x]; }

	// Compare -- p4ftp legacy

	int	Compare( const StrPtr &s ) const
		{ return SCompare( s ); }

	// CCompare/SCompare/XCompare

	int	CCompare( const StrPtr &s ) const
		{ return CCompare( buffer, s.buffer ); }

	int	CCompareN( const StrPtr &s, p4size_t len ) const
		{ return CCompareN( buffer, s.buffer, len ); }

	int	SCompare( const StrPtr &s ) const
		{ return SCompare( buffer, s.buffer ); }

	int	NCompare( const StrPtr &s ) const
		{ return NCompare( buffer, s.buffer ); }

	static int CCompare( const char *a, const char *b );
	static int CCompareN( const char *a, const char *b, p4size_t len );
	static int SCompare( const char *a, const char *b );
	static int NCompare( const char *a, const char *b );

	static int SCompare( unsigned char a, unsigned char b )
		{
		    return a==b ? 0 : SCompareF( a, b );
		}

	static int SEqual( unsigned char a, unsigned char b )
		{ 
		    switch( a^b ) 
		    { 
		    default: return 0;
		    case 0: return 1;
		    case 'A'^'a': return SEqualF( a, b );
		    }
		}

	int	SCompareN( const StrPtr &s ) const;

	int	XCompare( const StrPtr &s ) const
		{ return strcmp( buffer, s.buffer ); }

	static int XCompare( const char *a, const char *b )
		{ return strcmp( a, b ); }

	int	XCompareN( const StrPtr &s ) const
		{ return strncmp( buffer, s.buffer, length ); }

	// binary equality
	bool	BEqual( const StrPtr &s ) const
		{
		    return length == s.length
		        && memcmp( buffer, s.buffer, length ) == 0;
		}

	// More comparing

	const char *Contains( const StrPtr &s ) const
		{ return strstr( Text(), s.Text() ); }

	bool	operator ==( const char *buf ) const
		{ return strcmp( buffer, buf ) == 0; }

	bool	operator !=( const char *buf ) const
		{ return strcmp( buffer, buf ) != 0; }

	bool	operator <( const char *buf ) const
		{ return strcmp( buffer, buf ) < 0; }

	bool	operator <=( const char *buf ) const
		{ return strcmp( buffer, buf ) <= 0; }

	bool	operator >( const char *buf ) const
		{ return strcmp( buffer, buf ) > 0; }

	bool	operator >=( const char *buf ) const
		{ return strcmp( buffer, buf ) >= 0; }

	bool	operator ==( const StrPtr &s ) const
		{ return strcmp( buffer, s.buffer ) == 0; }

	bool	operator !=( const StrPtr &s ) const
		{ return strcmp( buffer, s.buffer ) != 0; }

	bool	operator <( const StrPtr &s ) const
		{ return strcmp( buffer, s.buffer ) < 0; }

	bool	operator <=( const StrPtr &s ) const
		{ return strcmp( buffer, s.buffer ) <= 0; }

	bool	operator >( const StrPtr &s ) const
		{ return strcmp( buffer, s.buffer ) > 0; }

	bool	operator >=( const StrPtr &s ) const
		{ return strcmp( buffer, s.buffer ) >= 0; }

	// Copying out
	// Includes EOS

	void	StrCpy( char *b ) const
		{ memcpy( b, buffer, length + 1 ); }

	void	StrCat( char *b ) const
		{ memcpy( b + strlen( b ), buffer, length + 1 ); }

	// check for identical underlying objects or overlaps

	bool	CheckSame( const StrPtr *a ) const
	    { return Text() == a->Text() && Length() == a->Length(); }
	bool	CheckOverlap( const StrPtr *a ) const
	    { return End() > a->Text() && a->End() > Text(); }

	// Formatting and parsing numbers as strings

	static int Atoi( const char *b ) { return atoi( b ); }
	static char *Itoa( int v, char *e ) { return Itoa64( v, e ); }

	static P4INT64 Atoi64( const char *buffer );
	static char *Itoa64( P4INT64 v, char *endbuf );
	static char *Itox( unsigned int v, char *endbuf );
	static char *Itox64( P4UINT64 v, char *endbuf );        

    friend class StrBuf;
    friend class StrRef;

    protected:
	char	*buffer;
	p4size_t	length;

    public:

	// Case sensitive server?

	static bool CaseFolding()
		{ return caseUse != ST_UNIX; }

	static bool CaseIgnored()
		{ return caseUse == ST_WINDOWS; }

	static bool CaseHybrid()
		{ return caseUse == ST_HYBRID; }

	static void SetCaseFolding( int c )
		{ caseUse = (CaseUse)c; foldingSet = true; }

	static bool CaseFoldingAlreadySet()
		{ return foldingSet; }

	enum CaseUse { ST_UNIX, ST_WINDOWS, ST_HYBRID };

	static CaseUse CaseUsage() { return caseUse; }

    private:

	static CaseUse caseUse;
	static bool foldingSet;

	static int SEqualF( unsigned char a, unsigned char b );
	static int SCompareF( unsigned char a, unsigned char b );

	static int NCompareLeft( const unsigned char *a, 
	                         const unsigned char *b );
	static int NCompareRight( const unsigned char *a, 
	                          const unsigned char *b );
} ;

class StrRef : public StrPtr {

    public:

		StrRef() {}

		StrRef( const StrRef &s )
		{ Set( &s ); }

		StrRef( const StrPtr &s )
		{ Set( &s ); }

		StrRef( const char *buf )
		{ Set( (char *)buf ); }

		StrRef( const char *buf, p4size_t len )
		{ Set( (char *)buf, len ); }

	static const StrPtr &Null()
		{ return null; }

	const StrRef & operator =(const StrRef &s)
		{ Set( &s ); return *this; }

	const StrRef & operator =(const StrPtr &s)
		{ Set( &s ); return *this; }

	const StrRef & operator =(const char *buf)
		{ Set( (char *)buf ); return *this; }

	void	operator +=( int l )
		{ buffer += l; length -= l; }

	void 	Set( char *buf )
		{ Set( buf, (p4size_t)strlen( buf ) ); }
		 
	void	Set( char *buf, p4size_t len )
		{ buffer = buf; length = len; }

	void	Set( const StrPtr *s )
		{ Set( s->buffer, s->length ); }

	void	Set( const StrPtr &s )
		{ Set( s.buffer, s.length ); }

    private:
    	static	StrRef null;

} ;

class StrBuf : public StrPtr {

    public:
		StrBuf() 
		{ StringInit(); }

	void	StringInit()
		{ length = size = 0; buffer = nullStrBuf; }

		~StrBuf()
		{ if( buffer != nullStrBuf ) delete []buffer; }

	// copy constructor, assignment

		StrBuf( const StrBuf &s )
		{ StringInit(); Set( &s ); }

		StrBuf( const StrRef &s )
		{ StringInit(); Set( &s ); }

		StrBuf( const StrPtr &s )
		{ StringInit(); Set( &s ); }

		StrBuf( const char *buf )
		{ StringInit(); Set( buf ); }

	const StrBuf & operator =(const StrBuf &s)
		{ Set( &s ); return *this; }

	const StrBuf & operator =(const StrRef &s)
		{ Set( &s ); return *this; }

	const StrBuf & operator =(const StrPtr &s)
		{ Set( &s ); return *this; }

	const StrBuf & operator =(const char *buf)
		{ if( (const char*)this != buf ) Set( buf ); return *this; }

	// Setting, getting

	void 	Clear( void )
		{ length = 0; }

	void 	Reset( void )
		{ 
		    if( buffer != nullStrBuf ) 
		    {
	                delete []buffer; 
		
		        length = size = 0; 
		        buffer = nullStrBuf; 
		    }
		}

	void	Reset( const char *buf )
		{ Reset(); UAppend( buf ); }

	void	Reset( const StrPtr *s )
		{ Reset(); UAppend( s ); }

	void 	Reset( const StrPtr &s )
		{ Reset(); UAppend( &s ); }

	void	Set( const char *buf )
	    { if( buf == Text() ) SetLength(); else { Clear(); Append( buf ); } }

	void	Set( const StrPtr *s )
	    { if( s->Text() != Text() ) { Clear(); UAppend( s ); } }

	void	Set( const StrPtr &s )
	    { if( s.Text() != Text() ) { Clear(); UAppend( &s ); } }

	void	Set( const char *buf, p4size_t len )
	    { if( buf == Text() ) SetLength( len ); else { Clear(); Append( buf, len ); } }

	void	Extend( const char *buf, p4size_t len )
		{ memcpy( Alloc( len ), buf, len ); }

	void	Extend( char c )
		{ *Alloc(1) = c; }

	void 	Terminate() 
		{ Extend(0); --length; }

	void	TruncateBlanks();     // Removes blanks just from the end
	void	TrimBlanks();         // Removes blanks from start and end

	void	Append( const char *buf );     

	void	Append( const StrPtr *s );

	void	Append( const char *buf, p4size_t len );

	void	UAppend( const char *buf );     

	void	UAppend( const StrPtr *s );

	void	UAppend( const char *buf, p4size_t len );

	// large-block append
	void	BlockAppend( const char *buf );

	void	BlockAppend( const StrPtr *s );

	void	BlockAppend( const char *buf, p4size_t len );

	void	UBlockAppend( const char *buf );

	void	UBlockAppend( const StrPtr *s );

	void	UBlockAppend( const char *buf, p4size_t len );

	char *	Alloc( p4size_t len )
		{
		    p4size_t oldlen = length;

		    if( ( length += len ) > size )
			Grow( oldlen );

		    return buffer + oldlen;
		}

	// large block (>= 128KB) allocation; no extra space is reserved
	char *	BlockAlloc( p4size_t len )
		{
		    p4size_t oldlen = length;

		    if( ( length += len ) > size )
			Reserve( oldlen );

		    return buffer + oldlen;
		}

        void    Fill( const char *buf, p4size_t len );

        void    Fill( const char *buf )
                {
		    Fill( buf, Length() );
		}

	p4size_t 	BufSize() const
		{ return size; }

	// leading-string compression

	void	Compress( StrPtr *s );
	void	UnCompress( StrPtr *s );

	// trailing-string compression
	int	EncodeTail( StrPtr &s, const char *replaceBytes );
	int	DecodeTail( StrPtr &s, const char *replaceBytes );

	// string << -- append string/number

	StrBuf& operator <<( const char *s )
		{ Append( s ); return *this; }

	StrBuf& operator <<( const StrPtr *s )
		{ Append( s ); return *this; }

	StrBuf& operator <<( const StrPtr &s )
		{ Append( &s ); return *this; }

	StrBuf& operator <<( const StrNum &s )
		{ UAppend( (const StrPtr *)&s ); return *this; }

	StrBuf& operator <<( P4INT64 v );

	StrBuf& operator <<( int v );

	StrBuf& operator <<( long int v ); /* time_t */

	StrBuf& operator <<( unsigned int v ); /* p4size_t */

	StrBuf& operator <<( long unsigned int v );

    private:
	p4size_t	size;

	void	Grow( p4size_t len );

	// reserve a large block of memory (>= 128 KB); don't over-allocate
	void	Reserve( p4size_t oldlen );

	// Some DbCompare funcs memcpy from this, so it has be be big
	// enough that we aren't reaching past valid memory.  The
	// largest one seems to be DbInt64 (8 bytes.)
	static char nullStrBuf[ 8 ];
} ;

class StrFixed : public StrPtr {

    public:

		StrFixed( p4size_t l )
		{ this->length = l; this->buffer = new char[ l ]; }

		~StrFixed()
		{ delete []buffer; }

	void	SetBufferSize( p4size_t l );
} ;


class StrNum : public StrPtr {

    public:
		StrNum() {} 

		StrNum( P4INT64 v ) 
		{ Set( v ); }

		StrNum( long int v ) 
		{ Set( ( P4INT64 ) v ); }

		StrNum( int v ) 
		{ Set( v ); }

		StrNum( int ok, P4INT64 v )
		{ if( ok ) Set( v ); else buffer = buf, length = 0; }

	void	Set( P4INT64 v )
		{
		    buffer = Itoa64( v, buf + sizeof( buf ) );
		    length = (p4size_t)(buf + sizeof( buf ) - buffer - 1);
		}

	void	Set( int v )
		{
		    buffer = Itoa( v, buf + sizeof( buf ) );
		    length = (p4size_t)(buf + sizeof( buf ) - buffer - 1);
		}

	void	SetHex( P4INT64 v )
		{
		    buffer = Itox64( v, buf + sizeof( buf ) );
		    length = (p4size_t)(buf + sizeof( buf ) - buffer - 1);
		}

	void	SetHex( int v )
		{
		    buffer = Itox( v, buf + sizeof( buf ) );
		    length = (p4size_t)(buf + sizeof( buf ) - buffer - 1);
		}

	void	Set( P4INT64 v, int digits )
		{
		    Set( v );

		    while( (int)length < digits )
			*--buffer = '0', ++length;
		}

    private:
		char buf[24];
} ;

class StrHuman : public StrPtr
{
	public:
	        StrHuman() {}
 
	        StrHuman( long v, int f = 1024 )
	        { Convert( (P4INT64)v, f ); }
 
	        StrHuman( P4INT64 v, int f = 1024 )
	        { Convert( v, f ); }
 
	        static char *Itoa64( P4INT64 v, char *endbuf, int f );
 
	private:
	        void	Convert( P4INT64 v, int f )
	        {
	            buffer = Itoa64( v, buf + sizeof( buf ), f );
	            length = (p4size_t)(buf + sizeof( buf ) - buffer - 1);
	        }

	    char buf[24];
} ;

inline StrBuf &
StrBuf::operator <<( P4INT64 v )
{
	return operator <<( StrNum( v ) );
}

inline StrBuf &
StrBuf::operator <<( int v )
{
	return operator <<( StrNum( v ) );
}

inline StrBuf &
StrBuf::operator <<( long int v )
{
	return operator <<( StrNum( ( P4INT64 ) v ) );
}

inline StrBuf &
StrBuf::operator <<( unsigned int v )
{
	return operator <<( StrNum( ( P4INT64 ) v ) );
}

inline StrBuf &
StrBuf::operator <<( long unsigned int v )
{
	return operator <<( StrNum( ( P4INT64 ) v ) );
}
