/*
* 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::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::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() const;
int EndsWith( const char *s, int l ) const;
P4INT64 Atoi64() const
{ return Atoi64( buffer ); }
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 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 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 ); }
// 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 );
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 <<( 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( int v )
{ Set( v ); }
StrNum( int ok, int v )
{ if( ok ) Set( v ); else buffer = buf, length = 0; }
void Set( int v )
{
buffer = Itoa( 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( int v, int digits )
{
Set( v );
while( (int)length < digits )
*--buffer = '0', ++length;
}
# ifdef HAVE_INT64
StrNum( long v ) { Set( (P4INT64)v ); }
StrNum( P4INT64 v )
{ Set( v ); }
void Set( P4INT64 v )
{
buffer = Itoa64( v, buf + sizeof( buf ) );
length = (p4size_t)(buf + sizeof( buf ) - buffer - 1);
}
# endif
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 <<( int v )
{
return operator <<( StrNum( v ) );
}