/* This program is distributed under the terms of the 'MIT license'. The text of this licence follows... Copyright (c) 2007 J.D.Medhurst (a.k.a. Tixy) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** @file @brief String formatting utilities header file. */ #ifndef __STRINGF_H__ #define __STRINGF_H__ /** @addtogroup utils_stringf Utils - String formatting Extensible classes for producing formated C strings. This also contains implementations of the C standard library functions: #vsprintf, #sprintf, #vsnprintf and #snprintf @{ */ #include // for va_list /** Class for generating formatted strings in a similar way to the C standard library function sprintf. The produced text is passed to the pure virtual Out methods which are responsible for storing or outputing the string. Supported conversion specifiers are: - diouxX - Integers. May have optional length modifier. - c - Character argument. Any length modifier is ignored. - s - String argument. Any length modifier is ignored. - p - Pointer argument. - n - Number of characters written so far. May have optional length modifier. Valid length modifiers are: - hh - char - h - short - l - long - ll - long long - j - intmax_t - z - size_t - t - ptrdiff_t The default implementation correctly parses the following conversion specifiers (but produces no output) - aAeEfgG - Floating point number with optional 'L' length modifier. */ class StringFormatter { public: /** Produce a formatted string. @param formatString A string specifying the format of the string. This is specified in the same way as the C standard library sprintf function. @param args The arguments for the formatted string. @return The size of the resulting string. */ size_t VFormat(const char* formatString, va_list args); /** Produce a formatted string. @param formatString A string specifying the format of the string. This is specified in the same way as the C standard library sprintf function. @param ... The arguments for the formatted string. @return The size of the resulting string. */ size_t Format(const char* formatString, ...); /** Produce a single line of a hex dump. Each line represents the contents of up to 16 bytes. The format of the text is like:
	12345678  61 62 63 64  65 66 67 68  69 6a 6b 6c  6d 6e 6f 70  abcdefghijklmnop
	
@param data Address of data to be dumped. @param size Size of data to be dumped. If >16 then only 16 bytes are dumped. @param addressOffset Value to add to \a data when displaying address in dump. @return The number of bytes remaining to dump. I.e. \a size-16 if \a size was greater 16, else zero. */ size_t HexDumpLine(const void* data, size_t size, ptrdiff_t addressOffset=0); protected: /** Called to 'output' generated text. @param text Pointer to text. @param textSize Size of text. */ virtual void Out(const char* text, size_t textSize) =0; /** Called to 'output' a repeated single character. @param character The character to output. @param repeatCount Number of times \a character should be output. */ virtual void Out(char character, size_t repeatCount) =0; protected: /** Format flags. */ enum Flags { FlagMinus = 1<<0, ///< Format flag '-' is present. FlagPlus = 1<<1, ///< Format flag '+' is present. FlagSpace = 1<<2, ///< Format flag ' ' is present. FlagHash = 1<<3, ///< Format flag '#' is present. FlagZero = 1<<4, ///< Format flag '0' is present. LengthMod_hh = 1<<8, ///< Format length modifier 'hh' is present. I.e. argument is a char. LengthMod_h = 1<<9, ///< Format length modifier 'h' is present. I.e. argument is a short. LengthMod_l = 1<<10, ///< Format length modifier 'l' is present. I.e. argument is a long. LengthMod_ll = 1<<11, ///< Format length modifier 'll' is present. I.e. argument is a long long. LengthMod_L = 1<<12, ///< Format length modifier 'L' is present. I.e. argument is a double. LengthMod_unknown = 1<<13, ///< Used to indicate that the argument size is unkown. SignedInt = 1<<15 ///< Used to indicate that an integer argument is signed. }; /** Information about a single conversion specification. */ class ConvertionSpec { public: /** @param args The argument list for the formatted string. */ inline ConvertionSpec(va_list args) : Args(args) {} /** Decode the conversion specification \a format, and update this class members accordingly. @param format Pointer to conversion specification. @return Address of the character immediately after the decoded format specification in \a format. */ const char* Decode(const char* format); /** Get an integer argument, of a specified size, from the argument list. @param flags Bitmask of values from #Flags. Size of argument is detemined by one of: #LengthMod_ll, #LengthMod_l, #LengthMod_h or #LengthMod_hh; if none are present, 'int' is assumed. If #SignedInt bit is set, the integer argument is sign extended. @return The argument as type long long. */ longlong GetIntArg(unsigned flags); /** Get an integer argument from the argument list. @return The argument. */ inline int GetIntArg() { return va_arg(Args,int); } /** Get an pointer argument from the argument list. @return The argument. */ inline void* GetPointerArg() { return va_arg(Args,void*); } private: /** Read an decimal integer value from \a format, or if the first character is a '*', get an integer from Args. @param format Pointer to the text to read. @param[out] val The value of the integer read, or zero if \a format did not point to either a decimal number or a '*'. @return Address of character after the decoded decimal number (or after the '*'). */ const char* ReadInt(const char* format, int& val); public: va_list Args; ///< The argument list of the formatted string. uint16_t Flags; ///< Bitmask of flags from #Flags char CovertionSpecifier; ///< The character representing the format, e.g. 'd' if the format is "%d". int FieldWidth; ///< The field width, or zero if none specified int Precision; ///< The specified precision, or -1 if none specified. }; /** Called when an conversion specification is found which is not recognised. The implementation of this method should produce text corresponding to the specified format, or handle it as an error in an implementation specific manner. The default implementation of this method just calls DefaultUnkownFormat. If you wish to provide your own implementation, define the macro STRINGFORMATTER_UNKOWNFORMAT_DEFINED when compiling stringf.cpp, this will omit the implementation defined in that file. @param[in,out] dstEnd End of buffer where any generated text is stored. This is initialise to the end of a buffer of #FormatTextBufferSize bytes which this function can optionally make use of. @param spec The decoded conversion specification which is to be handed. @return The start of the generated text. */ virtual char* UnkownFormat(char*& dstEnd, ConvertionSpec& spec); /** Default implemenation for UnkownFormat. This returns an empty sting and also discards any float argument from \a args if the conversion specifier is any of 'a', 'A', 'e', 'E', 'f', 'F', 'g' or 'G'. @param[in,out] dstEnd End of buffer where any generated text is stored. This is left unchanged. @param spec The decoded conversion specification which is to be handed. @return dstEnd. */ char* DefaultUnkownFormat(char*& dstEnd, ConvertionSpec& spec); public: /** Convert an unsigned integer into a hexadecimal numeric string. The number is left padded with zeros if it contains less that \a precision digits, and is optionally prefixed by "0x". @param val The value to convert. @param dst The address of the character immediately after where the string is to be stored. I.e. this specifies where the string will end, not where it starts. @param precision Minimum number of digits to store. A -ve value signifies 'use as many as required'. @param x This must be either 'x' or 'X'. If 'x', all alphabetic characters are coverted in lower case, if 'X', they are coverted in upper case, @param prefix If true, the converted value is prefixed with "0x". @return Pointer to the first character in the produced string. */ static char* PushHex(ulonglong val, char* dst, int precision=-1, int x='x', int prefix=false); /** Convert an unsigned integer into a octal numeric string. The number is left padded with zeros if it contains less that \a precision digits. @param val The value to convert. @param dst The address of the character immediately after where the string is to be stored. I.e. this specifies where the string will end, not where it starts. @param precision Minimum number of digits to store. A -ve value signifies 'use as many as required'. @param prefix If true, the converted value is prefixed "0" if it doesn't already start with one. @return Pointer to the first character in the produced string. */ static char* PushOctal(ulonglong val, char* dst, int precision=-1, int prefix=false); /** Convert an unsigned integer into a decimal numeric string. The number is left padded with zeros if it contains less that \a precision digits. @param val The value to convert. @param dst The address of the character immediately after where the string is to be stored. I.e. this specifies where the string will end, not where it starts. @param precision Minimum number of digits to store. A -ve value signifies 'use as many as required'. @return Pointer to the first character in the produced string. */ static char* PushDecimal(ulonglong val, char* dst, int precision=-1); #if !defined(_MSC_VER) /** Size of internal buffer used for the storing the generated text for a single conversion specification. This sets a limit on the precision value for integers. */ static const size_t FormatTextBufferSize = 130; /** Size of buffer required for #HexDumpLine. */ static const size_t HexDumpLineSize = sizeof(void*)*2 // digits in address +3*16 // 16 hex bytes with leading space +16/sizeof(void*) // extra leading spaces used to group bytes +2 // two spaces +16 // 16 ascii characters +1+1; // '\n' + NUL #else // old versions of MSVC don't like static const members, so use an enums... enum { FormatTextBufferSize = 130 }; enum { HexDumpLineSize = sizeof(void*)*2+3*16+16/sizeof(void*)+2+16+1+1 }; #endif friend class StringFormatter::ConvertionSpec; }; /** A StringFormatter which stores text in a specified buffer. Important note: the produced string will only have a terminating null character if the produced text has a size less than the size of the buffer. */ class StringBufferFormatter : public StringFormatter { public: /** @param buffer Address of the buffer to store text in. @param size Size of buffer. */ StringBufferFormatter(char* buffer, size_t size); // Implement output methods for StringFormatter... virtual void Out(const char* text, size_t textSize); virtual void Out(char character, size_t repeatCount); /** Terminate text generation. This appends a null character after the end of the generated text (if there is space in the buffer) and sets the buffer state to be empty. @return End of generated text. This is the address of the terminating null character, or the address immediately after the end of the buffer if the buffer was full. */ char* End(); protected: char* BufferStart; ///< Start of buffer used to store text. char* BufferEnd; ///< End of buffer used to store text. (The address immediately after the last byte.) char* BufferPtr; ///< Address to store the next byte of text to be 'output'. }; /** Generate a formatted C string. @param str String where output is to be written. @param format Format string. @param ap Arguments for formatting. @return Number of bytes written to \a str, excluding the terminating null byte. */ extern "C" int vsprintf(char* str, const char* format, va_list ap); /** Generate a formatted C string. @param str String where output is to be written. @param format Format string. @param ... Arguments for formatting. @return Number of bytes written to \a str, excluding the terminating null byte. */ extern "C" int sprintf(char* str, const char* format, ...); /** Generate a formatted C string with a limited size. @param str String where output is to be written. @param size Size of the buffer refered to by \a str. @param format Format string. @param ap Arguments for formatting. @return Number of bytes that would have been written to \a str, excluding the terminating null byte if \a size was large enough. */ extern "C" int vsnprintf(char* str, size_t size, const char* format, va_list ap); /** Generate a formatted C string with a limited size. @param str String where output is to be written. @param size Size of the buffer refered to by \a str. @param format Format string. @param ... Arguments for formatting. @return Number of bytes that would have been written to \a str, excluding the terminating null byte if \a size was large enough. */ extern "C" int snprintf(char* str, size_t size, const char* format, ...); /** @} */ // End of group #endif // __STRINGF_H__