/* $Id: string.hpp,v 1.26 2005/09/02 08:36:10 uwe Exp $ */ #ifndef STRING_CLASS_INCLUDED #define STRING_CLASS_INCLUDED /********************************************************** * possible preprocessor settings as compiler flags: * STRING_BUFFER_GRANULARITY : see below "length and memory locking" * SUPPRESS_COLORED_OUTPUT : do not use color for error messages * DEBUG_VERBOSITY : enables error messages, also enabled * by _DEBUG_ or DBG_LEVEL > 0 * USE_SDL : respects the endianes using the functions * in serialize.hpp by Ch. Freundl ********************************************************** * * Class to encapsulate strings, i.e. zero-terminated arrays of type char []. * * Some concepts of the class: * - string length: * Whenever we talk about the length of the string, we do not include * its terminating zero, analogously to the C-function "strlen". * - buffer granularity: * Whenever the content of string is changed, the class observes, * whether the size of the needed buffer changes, too. In both cases, * if a smaller or a larger buffer is needed to perform the ordered * changes, a new buffer is allocated, even if the lengths of the * previous and the future string just differ by one byte, i.e. the * buffer is allocated and adapted in chunks of size one. This * buffer granularity can be set at compile time using the #defined * constant STRING_BUFFER_GRANULARITY, that can be fixed to any value * greater than zero (e.g. -DSTRING_BUFFER_GRANULARITY=10 ). * - length and memory locking: * Resizing of the char buffer can be avoided, by locking the length * using the function lockLength(int length). This function locks the * string length to the passed size. On the one hand this means that * the buffer will not be diminished, if it is not needed of this size * anymore, on the other hand the buffer will not be enlarged, even if * an operation would require it, and the resulting string of that * operation will be truncated to length "length". If you do not really * want to limit the length, but the buffer size, this might have the * effect (if STRING_BUFFER_GRANULARITY > 1) that not the full allocated * memory is used (e.g. STRING_BUFFER_GRANULARITY==10, a call of * lockLength(11) would cause an allocation of a buffer of length 20, * that could take a string of length 19. Nevertheless all strings will * be truncated to length 11. To avoid this effect, if not wanted, one * can call lockBuffer(11) instead. Then the allocated buffer will be * the same, but the length will be locked to the maximal possible * length (19 in this case), which is the return value of lockBuffer. * Both functions lock the length and buffer respectively, if they are * called without the parameter. Which one should be used, simple de- * pends from the indendet effect. If you really want to bound the * length of the string, use lockLength, if you want to bound the used * memory or just avoid reallocations, use lockBuffer. * - assignment and locking * Assignment of one string object to another or initilisation of a * new object with a present one does not assign the locking state. * Locking must be set for each object separately. * ********************************************************** * * String( const int lockedLength = -1 ) * Constructor that allocates memory without containing used data and * lockes the length, if a value >= 0 is passed. * String( const char* const string ); * Initialises the object with a passed string. If the NULL pointer is * passed, the object stays empty. * String( const String& string ); * Assignment constructor. * * char& operator [] ( const int i ) * Just the direct addressing of the char array, that contains the string * data. Only if DEBUG_VERBOSITY is defined, the range is checked. Note, * that a reference is returned so that a write access is possible with * this operator. If the string is cut to a certain length by simply * writing a terminating zero with this operator, the internally kept * string length will not be updated. For cutting strings use cut(int). * * operator char*() * permits typecast to char*, returns pointer to the data string * * assignment and addition (== concatenation) * String& operator = ( const char* const string ); * String& operator = ( const String& string ); * String& operator += ( const char* const string ); * String& operator += ( const String& string ); * String& operator += ( const char c ); * String operator + ( const char* const string ) const; * String operator + ( const String& string ) const; * String operator + ( const char c ); * // global operator, to permit "string1" + (String)("string2") * String operator + ( const char* const, const String& ); * * alphabetic comparison: * Supported are <, >, <=, >=, ==, !=; on both sides having either * a string object or char* . * * char* getString() * Returns the data string. Same effect as the type cast (char*). * int getLength() * Returns the length of the present string, not including the * terminating zero. (Just a member variable is returned, the length * is not calculated at this point.) * int getLockedLength() * Returns the locked length. -1, if neither length or buffer are locked. * int getBufferLength() * Returns the size of the locked buffer. Note that lockLength also locks * the buffer, and lockBuffer also locks the length. -1, if nothing is * locked. * * int lockLength( const int length = -1 ) * Locks the length to the passed value. If no size is passed (<0), the * length is locked to the current string length. Changes buffer size, if * necessary. Returns the successfully locked string length, -1, if the * length could not be locked. * int lockBuffer( const int length = -1 ) * Locks the buffer to the passed size, also respecting * STRING_BUFFER_GRANULARITY, adapts the implicitly locked string length * and returns it. For example with STRING_BUFFER_GRANULARITY == 10 * lockLength(12) return 19, if the call was successful. * void unlock(); * Unlock buffer or length. * * int format( const char* const formatstring, ... ); * Simply use like the C-function printf to assign a string. * int cut( int length ); * Cuts the string to the passed length. If the string is locked, the * buffer will not be resized. (see example below). * void reset(); * Resets the object. This is the ONLY function besides unlock (and the * destructor, of course) that can unlock the object or change the buffer * size in locked state. * * unsigned int getSerializeBufferSize() const * Returns the buffer size needed for the serialisation. * int serialize( void*& buffer, const int maxBytes = -1 ) const * int deserialize( void*& buffer ) * Serialies/ deserialises the object into/ from a passed buffer at * "buffer". If "maxBytes" >= 0, maximal "maxBytes" are written into * the buffer. * int serialize( FILE* file, const int maxBytes = -1 ) const * int deserialize( FILE* file ) * Serialisation/ deserialisation into/ from a file. * ********************************************************** * * some examples: * * (1) We have a string of a directory path and the names of some files * in this directory. Therefrom we successively want to create the * full paths of these files, avoiding buffer resizing. * * --8<-------8<-------8<------ * char FileNames* [] = { "bender.txt", "lila.jpg", "fry.html", NULL }; * char PathName [] = "/usr/home/futurama"; * * String FullPath( PathName + "/" ); * FullPath.lockBuffer( FullPath.getLength() + 20 ); * int PathLength = FullPath.getLength(); * for( int i = 0; FileNames[i]; i++ ) { * FullPath += FileNames[i]; * // do something with the full path * FullPath.cut( PathLength ); * } * --8<-------8<-------8<------ * **********************************************************/ #include #include #include #ifdef USE_SDL #include #else typedef unsigned char Uint8; #endif /**********************************************************/ // make shure that "DBG_LEVEL > 0" switches on "_DEBUG_" #ifdef DBG_LEVEL #if DBG_LEVEL > 0 #ifndef _DEBUG_ #define _DEBUG_ #endif // _DEBUG_ #endif // DBG_LEVEL > 0 #endif // DBG_LEVEL #ifdef _DEBUG_ #ifndef DEBUG_VERBOSITY #define DEBUG_VERBOSITY #endif // DEBUG_VERBOSITY #endif // _DEBUG_ using namespace std; /**********************************************************/ //! Class to encapsulate strings, i.e. zero-terminated arrays of type char []. /*! Some concepts of the class: * - string length: * Whenever we talk about the length of the string, we do not include * its terminating zero, analogously to the C-function "strlen". * - buffer granularity: * Whenever the content of string is changed, the class observes, * whether the size of the needed buffer changes, too. In both cases, * if a smaller or a larger buffer is needed to perform the ordered * changes, a new buffer is allocated, even if the lengths of the * previous and the future string just differ by one byte, i.e. the * buffer is allocated and adapted in chunks of size one. This * buffer granularity can be set at compile time using the #defined * constant STRING_BUFFER_GRANULARITY, that can be fixed to any value * greater than zero (e.g. -DSTRING_BUFFER_GRANULARITY=10 ). * - length and memory locking: * Resizing of the char buffer can be avoided, by locking the length * using the function lockLength(int length). This function locks the * string length to the passed size. On the one hand this means that * the buffer will not be diminished, if it is not needed of this size * anymore, on the other hand the buffer will not be enlarged, even if * an operation would require it, and the resulting string of that * operation will be truncated to length "length". If you do not really * want to limit the length, but the buffer size, this might have the * effect (if STRING_BUFFER_GRANULARITY > 1) that not the full allocated * memory is used (e.g. STRING_BUFFER_GRANULARITY==10, a call of * lockLength(11) would cause an allocation of a buffer of length 20, * that could take a string of length 19. Nevertheless all strings will * be truncated to length 11. To avoid this effect, if not wanted, one * can call lockBuffer(11) instead. Then the allocated buffer will be * the same, but the length will be locked to the maximal possible * length (19 in this case), which is the return value of lockBuffer. * Both functions lock the length and buffer respectively, if they are * called without the parameter. Which one should be used, simple de- * pends from the indendet effect. If you really want to bound the * length of the string, use lockLength, if you want to bound the used * memory or just avoid reallocations, use lockBuffer. * - assignment and locking * Assignment of one string object to another or initilisation of a * new object with a present one does not assign the locking state. * Locking must be set for each object separately. */ class String { public: /*! Constructor that allocates memory without containing used * data and lockes the length, if a value >= 0 is passed. */ String( const int lockedLength = -1 ); /*! Initialises the object with a passed string, and thereby * allows a usage of the string and additional, optional * arguments as format string and parameters just like in the * C-function printf. */ String( const char* const string ); //! Assignment constructor. String( const String& string ); ~String(); /*! Just the direct addressing of the char array, that contains * the string data. Only if DEBUG_VERBOSITY is defined, the * range is checked. Note, that a reference is returned so that * a write access is possible with this operator. If the string * is cut to a certain length by simply writing a terminating * zero with this operator, the internally kept string length * will not be updated. For cutting strings use cut(int). */ char& operator [] ( const int i ) #ifndef DEBUG_VERBOSITY { return m_String[i]; } #endif // DEBUG_VERBOSITY ; //! permits typecast to char*, returns pointer to the data string operator char*() const { return m_String; } String& operator = ( const char* const string ); String& operator = ( const String& string ); String& operator += ( const char* const string ); String& operator += ( const String& string ); String& operator += ( const char c ); String operator + ( const char* const string ) const; String operator + ( const String& string ) const; String operator + ( const char c ); //! \name comparison operators //@{ bool operator == ( const char* const string ) const { return 0 == saveCompare( m_String, string ); } bool operator == ( const String& string ) const { return 0 == saveCompare( m_String, string.m_String ); } bool operator != ( const char* const string ) const { return 0 != saveCompare( m_String, string ); } bool operator != ( const String& string ) const { return 0 != saveCompare( m_String, string.m_String ); } bool operator < ( const String& string ) const { return 0 > saveCompare( m_String, string.m_String ); } bool operator < ( const char* const string ) const { return 0 > saveCompare( m_String, string ); } bool operator > ( const String& string ) const { return 0 < saveCompare( m_String, string.m_String ); } bool operator > ( const char* const string ) const { return 0 < saveCompare( m_String, string ); } bool operator <= ( const String& string ) const { return 0 >= saveCompare( m_String, string.m_String ); } bool operator <= ( const char* const string ) const { return 0 >= saveCompare( m_String, string ); } bool operator >= ( const String& string ) const { return 0 <= saveCompare( m_String, string.m_String ); } bool operator >= ( const char* const string ) const { return 0 <= saveCompare( m_String, string ); } //! helping function for the comparison operators static int saveCompare( const char* const s1, const char* const s2 ); //@} //! \name access functions //@{ //! Returns the data string. Same effect as the type cast (char*) char* getString() const { return m_String; } int getLength() const { return m_Length; } int getLockedLength() const { return m_lockedLength; } int getBufferLength() const { return m_BufferLength; } bool isLocked() const { return m_lockedLength >= 0; } //@} //! \name locking mechanisms //@{ int lockLength( const int length = -1 ); int lockBuffer( const int length = -1 ); void unlock() { m_lockedLength = -1; } //@} //! \name change the contained string //@{ int format( const char* const formatstring, ... ); int vformat( const char* const formatstring, va_list& args ); int cut( int length ); bool crop( int first, int last ); void reset(); //@} //! \name serialise functions //@{ unsigned int getSerializeBufferSize() const; int serialize( Uint8*& buffer, const int maxBytes = -1 ) const; int deserialize( Uint8*& buffer ); int serialize( FILE* file, const int maxBytes = -1 ) const; int deserialize( FILE* file ); //@} protected: //! \name internal helping functions //@{ int calcLength(); void add( const char* const string, const int length ); void assign( const char* const string, const int length ); inline int getBufferLengthFromLength( const int length ) const; inline int getSerializedClassSize() const; //@} char *m_String; // pointer to the kept string int m_Length, // current size of the string == strlen(m_String) m_BufferLength, // current size of the buffer m_lockedLength; // locked string length }; /********************************************************** * some additional extern operators **********************************************************/ // output operator ostream& operator << ( ostream&, const String& ); // char* + String String operator + ( const char* const, const String& ); // extern comparation operators // These operators are not really necessary, but they // conversions from char* to String inline bool operator == ( const char* const s1, const String& s2 ) { return 0 == String::saveCompare( s1, s2.getString() ); } inline bool operator != ( const char* const s1, const String& s2 ) { return 0 != String::saveCompare( s1, s2.getString() ); } inline bool operator < ( const char* const s1, const String& s2 ) { return 0 > String::saveCompare( s1, s2.getString() ); } inline bool operator <= ( const char* const s1, const String& s2 ) { return 0 >= String::saveCompare( s1, s2.getString() ); } inline bool operator > ( const char* const s1, const String& s2 ) { return 0 < String::saveCompare( s1, s2.getString() ); } inline bool operator >= ( const char* const s1, const String& s2 ) { return 0 <= String::saveCompare( s1, s2.getString() ); } /**********************************************************/ #endif // STRING_CLASS_INCLUDED