wx printf() implementation bug fixes ('%' handling, thread safety, ...) and optimisations (patch 1548750)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@41023 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -1746,6 +1746,12 @@ wxGetTranslation wouldn't find them.
|
|||||||
The same as \helpref{wxSnprintf}{wxsnprintf} but takes a {\tt va\_list }
|
The same as \helpref{wxSnprintf}{wxsnprintf} but takes a {\tt va\_list }
|
||||||
argument instead of arbitrary number of parameters.
|
argument instead of arbitrary number of parameters.
|
||||||
|
|
||||||
|
Note that if \texttt{wxUSE_PRINTF_POS_PARAMS} is set to 1, then this function supports
|
||||||
|
positional arguments (see \helpref{wxString::Printf}{wxstringprintf} for more information).
|
||||||
|
However other functions of the same family (wxPrintf, wxSprintf, wxFprintf, wxVfprintf,
|
||||||
|
wxVfprintf, wxVprintf, wxVsprintf) currently do not to support positional parameters
|
||||||
|
even when \texttt{wxUSE_PRINTF_POS_PARAMS} is 1.
|
||||||
|
|
||||||
\wxheading{See also}
|
\wxheading{See also}
|
||||||
|
|
||||||
\helpref{wxSnprintf}{wxsnprintf}, \helpref{wxString::PrintfV}{wxstringprintfv}
|
\helpref{wxSnprintf}{wxsnprintf}, \helpref{wxString::PrintfV}{wxstringprintfv}
|
||||||
|
@@ -897,6 +897,19 @@ Prepends {\it str} to this string, returning a reference to this string.
|
|||||||
Similar to the standard function {\it sprintf()}. Returns the number of
|
Similar to the standard function {\it sprintf()}. Returns the number of
|
||||||
characters written, or an integer less than zero on error.
|
characters written, or an integer less than zero on error.
|
||||||
|
|
||||||
|
Note that if {\tt wxUSE_PRINTF_POS_PARAMS} is set to 1, then this function supports
|
||||||
|
Unix98-style positional parameters:
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
wxString str;
|
||||||
|
|
||||||
|
str.Printf(wxT("%d %d %d"), 1, 2, 3);
|
||||||
|
// str now contains "1 2 3"
|
||||||
|
|
||||||
|
str.Printf(wxT("%2$d %3$d %1$d"), 1, 2, 3);
|
||||||
|
// str now contains "2 3 1"
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
{\bf NB:} This function will use a safe version of {\it vsprintf()} (usually called
|
{\bf NB:} This function will use a safe version of {\it vsprintf()} (usually called
|
||||||
{\it vsnprintf()}) whenever available to always allocate the buffer of correct
|
{\it vsnprintf()}) whenever available to always allocate the buffer of correct
|
||||||
size. Unfortunately, this function is not available on all platforms and the
|
size. Unfortunately, this function is not available on all platforms and the
|
||||||
|
@@ -183,7 +183,7 @@ bool WXDLLEXPORT wxOKlibc()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// some limits of our implementation
|
// some limits of our implementation
|
||||||
#define wxMAX_SVNPRINTF_ARGUMENTS 64
|
#define wxMAX_SVNPRINTF_ARGUMENTS 16
|
||||||
#define wxMAX_SVNPRINTF_FLAGBUFFER_LEN 32
|
#define wxMAX_SVNPRINTF_FLAGBUFFER_LEN 32
|
||||||
|
|
||||||
// the conversion specifiers accepted by wxVsnprintf_
|
// the conversion specifiers accepted by wxVsnprintf_
|
||||||
@@ -248,36 +248,37 @@ class wxPrintfConvSpec
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
// the position of the argument relative to this conversion specifier
|
// the position of the argument relative to this conversion specifier
|
||||||
size_t pos;
|
size_t m_pos;
|
||||||
|
|
||||||
// the type of this conversion specifier
|
// the type of this conversion specifier
|
||||||
wxPrintfArgType type;
|
wxPrintfArgType m_type;
|
||||||
|
|
||||||
// the minimum and maximum width
|
// the minimum and maximum width
|
||||||
// when one of this var is set to -1 it means: use the following argument
|
// when one of this var is set to -1 it means: use the following argument
|
||||||
// in the stack as minimum/maximum width for this conversion specifier
|
// in the stack as minimum/maximum width for this conversion specifier
|
||||||
int min_width, max_width;
|
int m_nMinWidth, m_nMaxWidth;
|
||||||
|
|
||||||
// does the argument need to the be aligned to left ?
|
// does the argument need to the be aligned to left ?
|
||||||
bool adj_left;
|
bool m_bAlignLeft;
|
||||||
|
|
||||||
// pointer to the '%' of this conversion specifier in the format string
|
// pointer to the '%' of this conversion specifier in the format string
|
||||||
// NOTE: this points somewhere in the string given to the Parse() function -
|
// NOTE: this points somewhere in the string given to the Parse() function -
|
||||||
// it's task of the caller ensure that memory is still valid !
|
// it's task of the caller ensure that memory is still valid !
|
||||||
const wxChar *argpos;
|
const wxChar *m_pArgPos;
|
||||||
|
|
||||||
// pointer to the last character of this conversion specifier in the
|
// pointer to the last character of this conversion specifier in the
|
||||||
// format string
|
// format string
|
||||||
// NOTE: this points somewhere in the string given to the Parse() function -
|
// NOTE: this points somewhere in the string given to the Parse() function -
|
||||||
// it's task of the caller ensure that memory is still valid !
|
// it's task of the caller ensure that memory is still valid !
|
||||||
const wxChar *argend;
|
const wxChar *m_pArgEnd;
|
||||||
|
|
||||||
// a little buffer where formatting flags like #+\.hlqLZ are stored by Parse()
|
// a little buffer where formatting flags like #+\.hlqLZ are stored by Parse()
|
||||||
// for use in Process()
|
// for use in Process()
|
||||||
// NB: this buffer can be safely a char buffer instead of a wchar_t buffer
|
// NB: even if this buffer is used only for numeric conversion specifiers and
|
||||||
// since it's used only for numeric conversion specifier and always
|
// thus could be safely declared as a char[] buffer, we want it to be wxChar
|
||||||
// with sprintf().
|
// so that in Unicode builds we can avoid to convert its contents to Unicode
|
||||||
char szFlags[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
|
// chars when copying it in user's buffer.
|
||||||
|
wxChar m_szFlags[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -307,16 +308,16 @@ private:
|
|||||||
|
|
||||||
void wxPrintfConvSpec::Init()
|
void wxPrintfConvSpec::Init()
|
||||||
{
|
{
|
||||||
min_width = 0;
|
m_nMinWidth = 0;
|
||||||
max_width = 0xFFFF;
|
m_nMaxWidth = 0xFFFF;
|
||||||
pos = 0;
|
m_pos = 0;
|
||||||
adj_left = false;
|
m_bAlignLeft = false;
|
||||||
argpos = argend = NULL;
|
m_pArgPos = m_pArgEnd = NULL;
|
||||||
type = wxPAT_INVALID;
|
m_type = wxPAT_INVALID;
|
||||||
|
|
||||||
// this character will never be removed from szFlags array and
|
// this character will never be removed from m_szFlags array and
|
||||||
// is important when calling sprintf() in wxPrintfConvSpec::Process() !
|
// is important when calling sprintf() in wxPrintfConvSpec::Process() !
|
||||||
szFlags[0] = '%';
|
m_szFlags[0] = wxT('%');
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wxPrintfConvSpec::Parse(const wxChar *format)
|
bool wxPrintfConvSpec::Parse(const wxChar *format)
|
||||||
@@ -328,19 +329,19 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
|
|||||||
bool in_prec, prec_dot;
|
bool in_prec, prec_dot;
|
||||||
int ilen = 0;
|
int ilen = 0;
|
||||||
|
|
||||||
adj_left = in_prec = prec_dot = false;
|
m_bAlignLeft = in_prec = prec_dot = false;
|
||||||
argpos = argend = format;
|
m_pArgPos = m_pArgEnd = format;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
#define CHECK_PREC \
|
#define CHECK_PREC \
|
||||||
if (in_prec && !prec_dot) \
|
if (in_prec && !prec_dot) \
|
||||||
{ \
|
{ \
|
||||||
szFlags[flagofs++] = (char)'.'; \
|
m_szFlags[flagofs++] = '.'; \
|
||||||
prec_dot = true; \
|
prec_dot = true; \
|
||||||
}
|
}
|
||||||
|
|
||||||
// what follows '%'?
|
// what follows '%'?
|
||||||
const wxChar ch = *(++argend);
|
const wxChar ch = *(++m_pArgEnd);
|
||||||
switch ( ch )
|
switch ( ch )
|
||||||
{
|
{
|
||||||
case wxT('\0'):
|
case wxT('\0'):
|
||||||
@@ -355,47 +356,51 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
|
|||||||
case wxT('+'):
|
case wxT('+'):
|
||||||
case wxT('\''):
|
case wxT('\''):
|
||||||
CHECK_PREC
|
CHECK_PREC
|
||||||
szFlags[flagofs++] = (char)ch;
|
m_szFlags[flagofs++] = ch;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxT('-'):
|
case wxT('-'):
|
||||||
CHECK_PREC
|
CHECK_PREC
|
||||||
adj_left = true;
|
m_bAlignLeft = true;
|
||||||
szFlags[flagofs++] = (char)ch;
|
m_szFlags[flagofs++] = ch;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxT('.'):
|
case wxT('.'):
|
||||||
CHECK_PREC
|
CHECK_PREC
|
||||||
in_prec = true;
|
in_prec = true;
|
||||||
prec_dot = false;
|
prec_dot = false;
|
||||||
max_width = 0;
|
m_nMaxWidth = 0;
|
||||||
// dot will be auto-added to szFlags if non-negative
|
// dot will be auto-added to m_szFlags if non-negative
|
||||||
// number follows
|
// number follows
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxT('h'):
|
case wxT('h'):
|
||||||
ilen = -1;
|
ilen = -1;
|
||||||
CHECK_PREC
|
CHECK_PREC
|
||||||
szFlags[flagofs++] = (char)ch;
|
m_szFlags[flagofs++] = ch;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxT('l'):
|
case wxT('l'):
|
||||||
|
// NB: it's safe to use flagofs-1 as flagofs always start from 1
|
||||||
|
if (m_szFlags[flagofs-1] == 'l') // 'll' modifier is the same as 'L' or 'q'
|
||||||
|
ilen = 2;
|
||||||
|
else
|
||||||
ilen = 1;
|
ilen = 1;
|
||||||
CHECK_PREC
|
CHECK_PREC
|
||||||
szFlags[flagofs++] = (char)ch;
|
m_szFlags[flagofs++] = ch;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxT('q'):
|
case wxT('q'):
|
||||||
case wxT('L'):
|
case wxT('L'):
|
||||||
ilen = 2;
|
ilen = 2;
|
||||||
CHECK_PREC
|
CHECK_PREC
|
||||||
szFlags[flagofs++] = (char)ch;
|
m_szFlags[flagofs++] = ch;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxT('Z'):
|
case wxT('Z'):
|
||||||
ilen = 3;
|
ilen = 3;
|
||||||
CHECK_PREC
|
CHECK_PREC
|
||||||
szFlags[flagofs++] = (char)ch;
|
m_szFlags[flagofs++] = ch;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxT('*'):
|
case wxT('*'):
|
||||||
@@ -405,18 +410,18 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
|
|||||||
|
|
||||||
// tell Process() to use the next argument
|
// tell Process() to use the next argument
|
||||||
// in the stack as maxwidth...
|
// in the stack as maxwidth...
|
||||||
max_width = -1;
|
m_nMaxWidth = -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// tell Process() to use the next argument
|
// tell Process() to use the next argument
|
||||||
// in the stack as minwidth...
|
// in the stack as minwidth...
|
||||||
min_width = -1;
|
m_nMinWidth = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the * in our formatting buffer...
|
// save the * in our formatting buffer...
|
||||||
// will be replaced later by Process()
|
// will be replaced later by Process()
|
||||||
szFlags[flagofs++] = (char)ch;
|
m_szFlags[flagofs++] = ch;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxT('1'): case wxT('2'): case wxT('3'):
|
case wxT('1'): case wxT('2'): case wxT('3'):
|
||||||
@@ -425,41 +430,41 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
|
|||||||
{
|
{
|
||||||
int len = 0;
|
int len = 0;
|
||||||
CHECK_PREC
|
CHECK_PREC
|
||||||
while ( (*argend >= wxT('0')) &&
|
while ( (*m_pArgEnd >= wxT('0')) &&
|
||||||
(*argend <= wxT('9')) )
|
(*m_pArgEnd <= wxT('9')) )
|
||||||
{
|
{
|
||||||
szFlags[flagofs++] = (char)(*argend);
|
m_szFlags[flagofs++] = (*m_pArgEnd);
|
||||||
len = len*10 + (*argend - wxT('0'));
|
len = len*10 + (*m_pArgEnd - wxT('0'));
|
||||||
argend++;
|
m_pArgEnd++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_prec)
|
if (in_prec)
|
||||||
max_width = len;
|
m_nMaxWidth = len;
|
||||||
else
|
else
|
||||||
min_width = len;
|
m_nMinWidth = len;
|
||||||
|
|
||||||
argend--; // the main loop pre-increments n again
|
m_pArgEnd--; // the main loop pre-increments n again
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxT('$'): // a positional parameter (e.g. %2$s) ?
|
case wxT('$'): // a positional parameter (e.g. %2$s) ?
|
||||||
{
|
{
|
||||||
if (min_width <= 0)
|
if (m_nMinWidth <= 0)
|
||||||
break; // ignore this formatting flag as no
|
break; // ignore this formatting flag as no
|
||||||
// numbers are preceding it
|
// numbers are preceding it
|
||||||
|
|
||||||
// remove from szFlags all digits previously added
|
// remove from m_szFlags all digits previously added
|
||||||
do {
|
do {
|
||||||
flagofs--;
|
flagofs--;
|
||||||
} while (szFlags[flagofs] >= '1' &&
|
} while (m_szFlags[flagofs] >= '1' &&
|
||||||
szFlags[flagofs] <= '9');
|
m_szFlags[flagofs] <= '9');
|
||||||
|
|
||||||
// re-adjust the offset making it point to the
|
// re-adjust the offset making it point to the
|
||||||
// next free char of szFlags
|
// next free char of m_szFlags
|
||||||
flagofs++;
|
flagofs++;
|
||||||
|
|
||||||
pos = min_width;
|
m_pos = m_nMinWidth;
|
||||||
min_width = 0;
|
m_nMinWidth = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -470,25 +475,25 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
|
|||||||
case wxT('x'):
|
case wxT('x'):
|
||||||
case wxT('X'):
|
case wxT('X'):
|
||||||
CHECK_PREC
|
CHECK_PREC
|
||||||
szFlags[flagofs++] = (char)ch;
|
m_szFlags[flagofs++] = ch;
|
||||||
szFlags[flagofs] = (char)'\0';
|
m_szFlags[flagofs] = '\0';
|
||||||
if (ilen == 0)
|
if (ilen == 0)
|
||||||
type = wxPAT_INT;
|
m_type = wxPAT_INT;
|
||||||
else if (ilen == -1)
|
else if (ilen == -1)
|
||||||
// NB: 'short int' value passed through '...'
|
// NB: 'short int' value passed through '...'
|
||||||
// is promoted to 'int', so we have to get
|
// is promoted to 'int', so we have to get
|
||||||
// an int from stack even if we need a short
|
// an int from stack even if we need a short
|
||||||
type = wxPAT_INT;
|
m_type = wxPAT_INT;
|
||||||
else if (ilen == 1)
|
else if (ilen == 1)
|
||||||
type = wxPAT_LONGINT;
|
m_type = wxPAT_LONGINT;
|
||||||
else if (ilen == 2)
|
else if (ilen == 2)
|
||||||
#if SIZEOF_LONG_LONG
|
#if SIZEOF_LONG_LONG
|
||||||
type = wxPAT_LONGLONGINT;
|
m_type = wxPAT_LONGLONGINT;
|
||||||
#else // !long long
|
#else // !long long
|
||||||
type = wxPAT_LONGINT;
|
m_type = wxPAT_LONGINT;
|
||||||
#endif // long long/!long long
|
#endif // long long/!long long
|
||||||
else if (ilen == 3)
|
else if (ilen == 3)
|
||||||
type = wxPAT_SIZET;
|
m_type = wxPAT_SIZET;
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -498,17 +503,17 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
|
|||||||
case wxT('g'):
|
case wxT('g'):
|
||||||
case wxT('G'):
|
case wxT('G'):
|
||||||
CHECK_PREC
|
CHECK_PREC
|
||||||
szFlags[flagofs++] = (char)ch;
|
m_szFlags[flagofs++] = ch;
|
||||||
szFlags[flagofs] = (char)'\0';
|
m_szFlags[flagofs] = '\0';
|
||||||
if (ilen == 2)
|
if (ilen == 2)
|
||||||
type = wxPAT_LONGDOUBLE;
|
m_type = wxPAT_LONGDOUBLE;
|
||||||
else
|
else
|
||||||
type = wxPAT_DOUBLE;
|
m_type = wxPAT_DOUBLE;
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxT('p'):
|
case wxT('p'):
|
||||||
type = wxPAT_POINTER;
|
m_type = wxPAT_POINTER;
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -517,22 +522,22 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
|
|||||||
{
|
{
|
||||||
// in Unicode mode %hc == ANSI character
|
// in Unicode mode %hc == ANSI character
|
||||||
// and in ANSI mode, %hc == %c == ANSI...
|
// and in ANSI mode, %hc == %c == ANSI...
|
||||||
type = wxPAT_CHAR;
|
m_type = wxPAT_CHAR;
|
||||||
}
|
}
|
||||||
else if (ilen == 1)
|
else if (ilen == 1)
|
||||||
{
|
{
|
||||||
// in ANSI mode %lc == Unicode character
|
// in ANSI mode %lc == Unicode character
|
||||||
// and in Unicode mode, %lc == %c == Unicode...
|
// and in Unicode mode, %lc == %c == Unicode...
|
||||||
type = wxPAT_WCHAR;
|
m_type = wxPAT_WCHAR;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#if wxUSE_UNICODE
|
#if wxUSE_UNICODE
|
||||||
// in Unicode mode, %c == Unicode character
|
// in Unicode mode, %c == Unicode character
|
||||||
type = wxPAT_WCHAR;
|
m_type = wxPAT_WCHAR;
|
||||||
#else
|
#else
|
||||||
// in ANSI mode, %c == ANSI character
|
// in ANSI mode, %c == ANSI character
|
||||||
type = wxPAT_CHAR;
|
m_type = wxPAT_CHAR;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
done = true;
|
done = true;
|
||||||
@@ -543,20 +548,20 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
|
|||||||
{
|
{
|
||||||
// Unicode mode wx extension: we'll let %hs mean non-Unicode
|
// Unicode mode wx extension: we'll let %hs mean non-Unicode
|
||||||
// strings (when in ANSI mode, %s == %hs == ANSI string)
|
// strings (when in ANSI mode, %s == %hs == ANSI string)
|
||||||
type = wxPAT_PCHAR;
|
m_type = wxPAT_PCHAR;
|
||||||
}
|
}
|
||||||
else if (ilen == 1)
|
else if (ilen == 1)
|
||||||
{
|
{
|
||||||
// in Unicode mode, %ls == %s == Unicode string
|
// in Unicode mode, %ls == %s == Unicode string
|
||||||
// in ANSI mode, %ls == Unicode string
|
// in ANSI mode, %ls == Unicode string
|
||||||
type = wxPAT_PWCHAR;
|
m_type = wxPAT_PWCHAR;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#if wxUSE_UNICODE
|
#if wxUSE_UNICODE
|
||||||
type = wxPAT_PWCHAR;
|
m_type = wxPAT_PWCHAR;
|
||||||
#else
|
#else
|
||||||
type = wxPAT_PCHAR;
|
m_type = wxPAT_PCHAR;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
done = true;
|
done = true;
|
||||||
@@ -564,11 +569,11 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
|
|||||||
|
|
||||||
case wxT('n'):
|
case wxT('n'):
|
||||||
if (ilen == 0)
|
if (ilen == 0)
|
||||||
type = wxPAT_NINT;
|
m_type = wxPAT_NINT;
|
||||||
else if (ilen == -1)
|
else if (ilen == -1)
|
||||||
type = wxPAT_NSHORTINT;
|
m_type = wxPAT_NSHORTINT;
|
||||||
else if (ilen >= 1)
|
else if (ilen >= 1)
|
||||||
type = wxPAT_NLONGINT;
|
m_type = wxPAT_NLONGINT;
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -577,6 +582,12 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
|
|||||||
// leave it unchanged
|
// leave it unchanged
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flagofs == wxMAX_SVNPRINTF_FLAGBUFFER_LEN)
|
||||||
|
{
|
||||||
|
wxLogDebug(wxT("Too many flags specified for a single conversion specifier!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
while (!done);
|
while (!done);
|
||||||
|
|
||||||
@@ -584,55 +595,61 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void wxPrintfConvSpec::ReplaceAsteriskWith(int w)
|
void wxPrintfConvSpec::ReplaceAsteriskWith(int width)
|
||||||
{
|
{
|
||||||
char temp[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
|
wxChar temp[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
|
||||||
|
|
||||||
// find the first * in our flag buffer
|
// find the first * in our flag buffer
|
||||||
char *pwidth = strchr(szFlags, '*');
|
wxChar *pwidth = wxStrchr(m_szFlags, wxT('*'));
|
||||||
wxASSERT(pwidth);
|
wxASSERT(pwidth);
|
||||||
|
|
||||||
// save what follows the * (the +1 is to skip it!)
|
// save what follows the * (the +1 is to skip the asterisk itself!)
|
||||||
strcpy(temp, pwidth+1);
|
wxStrcpy(temp, pwidth+1);
|
||||||
if (w < 0) {
|
if (width < 0)
|
||||||
pwidth[0] = '-';
|
{
|
||||||
|
pwidth[0] = wxT('-');
|
||||||
pwidth++;
|
pwidth++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace * with the actual integer given as width
|
// replace * with the actual integer given as width
|
||||||
int offset = ::sprintf(pwidth,"%d",abs(w));
|
#if wxUSE_UNICODE
|
||||||
|
int maxlen = (m_szFlags + wxMAX_SVNPRINTF_FLAGBUFFER_LEN - pwidth) / sizeof(wxChar);
|
||||||
|
int offset = ::swprintf(pwidth, maxlen, L"%d", abs(width));
|
||||||
|
#else
|
||||||
|
int offset = ::sprintf(pwidth, "%d", abs(width));
|
||||||
|
#endif
|
||||||
|
|
||||||
// restore after the expanded * what was following it
|
// restore after the expanded * what was following it
|
||||||
strcpy(pwidth+offset, temp);
|
wxStrcpy(pwidth+offset, temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr)
|
bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr)
|
||||||
{
|
{
|
||||||
// did the '*' width/precision specifier was used ?
|
// did the '*' width/precision specifier was used ?
|
||||||
if (max_width == -1)
|
if (m_nMaxWidth == -1)
|
||||||
{
|
{
|
||||||
// take the maxwidth specifier from the stack
|
// take the maxwidth specifier from the stack
|
||||||
max_width = va_arg(argptr, int);
|
m_nMaxWidth = va_arg(argptr, int);
|
||||||
if (max_width < 0)
|
if (m_nMaxWidth < 0)
|
||||||
max_width = 0;
|
m_nMaxWidth = 0;
|
||||||
else
|
else
|
||||||
ReplaceAsteriskWith(max_width);
|
ReplaceAsteriskWith(m_nMaxWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (min_width == -1)
|
if (m_nMinWidth == -1)
|
||||||
{
|
{
|
||||||
// take the minwidth specifier from the stack
|
// take the minwidth specifier from the stack
|
||||||
min_width = va_arg(argptr, int);
|
m_nMinWidth = va_arg(argptr, int);
|
||||||
|
|
||||||
ReplaceAsteriskWith(min_width);
|
ReplaceAsteriskWith(m_nMinWidth);
|
||||||
if (min_width < 0)
|
if (m_nMinWidth < 0)
|
||||||
{
|
{
|
||||||
adj_left = !adj_left;
|
m_bAlignLeft = !m_bAlignLeft;
|
||||||
min_width = -min_width;
|
m_nMinWidth = -m_nMinWidth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (m_type) {
|
||||||
case wxPAT_INT:
|
case wxPAT_INT:
|
||||||
p->pad_int = va_arg(argptr, int);
|
p->pad_int = va_arg(argptr, int);
|
||||||
break;
|
break;
|
||||||
@@ -658,7 +675,7 @@ bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case wxPAT_CHAR:
|
case wxPAT_CHAR:
|
||||||
p->pad_char = (char)va_arg(argptr, int); // char is promoted to int when passed through '...'
|
p->pad_char = va_arg(argptr, int); // char is promoted to int when passed through '...'
|
||||||
break;
|
break;
|
||||||
case wxPAT_WCHAR:
|
case wxPAT_WCHAR:
|
||||||
p->pad_wchar = (wchar_t)va_arg(argptr, int); // char is promoted to int when passed through '...'
|
p->pad_wchar = (wchar_t)va_arg(argptr, int); // char is promoted to int when passed through '...'
|
||||||
@@ -691,9 +708,19 @@ bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr)
|
|||||||
|
|
||||||
int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p)
|
int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p)
|
||||||
{
|
{
|
||||||
// buffer to avoid dynamic memory allocation each time for small strings
|
// buffer to avoid dynamic memory allocation each time for small strings;
|
||||||
static char szScratch[1024];
|
// note that this buffer is used only to hold results of number formatting,
|
||||||
size_t lenCur = 0;
|
// %s directly writes user's string in buf, without using szScratch
|
||||||
|
#define wxSCRATCH_BUFFER_SIZE 512
|
||||||
|
|
||||||
|
wxChar szScratch[wxSCRATCH_BUFFER_SIZE];
|
||||||
|
size_t lenScratch = 0, lenCur = 0;
|
||||||
|
|
||||||
|
#if wxUSE_UNICODE
|
||||||
|
#define system_sprintf(buff, flags, data) ::swprintf(buff, wxSCRATCH_BUFFER_SIZE, flags, data)
|
||||||
|
#else
|
||||||
|
#define system_sprintf ::sprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
#define APPEND_CH(ch) \
|
#define APPEND_CH(ch) \
|
||||||
{ \
|
{ \
|
||||||
@@ -711,36 +738,36 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p)
|
|||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ( type )
|
switch ( m_type )
|
||||||
{
|
{
|
||||||
case wxPAT_INT:
|
case wxPAT_INT:
|
||||||
::sprintf(szScratch, szFlags, p->pad_int);
|
lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_int);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxPAT_LONGINT:
|
case wxPAT_LONGINT:
|
||||||
::sprintf(szScratch, szFlags, p->pad_longint);
|
lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_longint);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#if SIZEOF_LONG_LONG
|
#if SIZEOF_LONG_LONG
|
||||||
case wxPAT_LONGLONGINT:
|
case wxPAT_LONGLONGINT:
|
||||||
::sprintf(szScratch, szFlags, p->pad_longlongint);
|
lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_longlongint);
|
||||||
break;
|
break;
|
||||||
#endif // SIZEOF_LONG_LONG
|
#endif // SIZEOF_LONG_LONG
|
||||||
|
|
||||||
case wxPAT_SIZET:
|
case wxPAT_SIZET:
|
||||||
::sprintf(szScratch, szFlags, p->pad_sizet);
|
lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_sizet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxPAT_LONGDOUBLE:
|
case wxPAT_LONGDOUBLE:
|
||||||
::sprintf(szScratch, szFlags, p->pad_longdouble);
|
lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_longdouble);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxPAT_DOUBLE:
|
case wxPAT_DOUBLE:
|
||||||
::sprintf(szScratch, szFlags, p->pad_double);
|
lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_double);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxPAT_POINTER:
|
case wxPAT_POINTER:
|
||||||
::sprintf(szScratch, szFlags, p->pad_pointer);
|
lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_pointer);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxPAT_CHAR:
|
case wxPAT_CHAR:
|
||||||
@@ -750,33 +777,39 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p)
|
|||||||
#if wxUSE_UNICODE
|
#if wxUSE_UNICODE
|
||||||
p->pad_wchar;
|
p->pad_wchar;
|
||||||
|
|
||||||
if (type == wxPAT_CHAR) {
|
if (m_type == wxPAT_CHAR)
|
||||||
|
{
|
||||||
// user passed a character explicitely indicated as ANSI...
|
// user passed a character explicitely indicated as ANSI...
|
||||||
const char buf[2] = { p->pad_char, 0 };
|
const char buf[2] = { p->pad_char, 0 };
|
||||||
val = wxString(buf, wxConvLibc)[0u];
|
val = wxString(buf, wxConvLibc)[0u];
|
||||||
|
|
||||||
|
//wprintf(L"converting ANSI=>Unicode"); // for debug
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
p->pad_char;
|
p->pad_char;
|
||||||
|
|
||||||
#if wxUSE_WCHAR_T
|
#if wxUSE_WCHAR_T
|
||||||
if (type == wxPAT_WCHAR) {
|
if (m_type == wxPAT_WCHAR)
|
||||||
|
{
|
||||||
// user passed a character explicitely indicated as Unicode...
|
// user passed a character explicitely indicated as Unicode...
|
||||||
const wchar_t buf[2] = { p->pad_wchar, 0 };
|
const wchar_t buf[2] = { p->pad_wchar, 0 };
|
||||||
val = wxString(buf, wxConvLibc)[0u];
|
val = wxString(buf, wxConvLibc)[0u];
|
||||||
|
|
||||||
|
//printf("converting Unicode=>ANSI"); // for debug
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
if (!adj_left)
|
if (!m_bAlignLeft)
|
||||||
for (i = 1; i < (size_t)min_width; i++)
|
for (i = 1; i < (size_t)m_nMinWidth; i++)
|
||||||
APPEND_CH(_T(' '));
|
APPEND_CH(_T(' '));
|
||||||
|
|
||||||
APPEND_CH(val);
|
APPEND_CH(val);
|
||||||
|
|
||||||
if (adj_left)
|
if (m_bAlignLeft)
|
||||||
for (i = 1; i < (size_t)min_width; i++)
|
for (i = 1; i < (size_t)m_nMinWidth; i++)
|
||||||
APPEND_CH(_T(' '));
|
APPEND_CH(_T(' '));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -789,17 +822,23 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p)
|
|||||||
#if wxUSE_UNICODE
|
#if wxUSE_UNICODE
|
||||||
p->pad_pwchar;
|
p->pad_pwchar;
|
||||||
|
|
||||||
if (type == wxPAT_PCHAR) {
|
if (m_type == wxPAT_PCHAR)
|
||||||
|
{
|
||||||
// user passed a string explicitely indicated as ANSI...
|
// user passed a string explicitely indicated as ANSI...
|
||||||
val = s = wxString(p->pad_pchar, wxConvLibc);
|
val = s = wxString(p->pad_pchar, wxConvLibc);
|
||||||
|
|
||||||
|
//wprintf(L"converting ANSI=>Unicode"); // for debug
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
p->pad_pchar;
|
p->pad_pchar;
|
||||||
|
|
||||||
#if wxUSE_WCHAR_T
|
#if wxUSE_WCHAR_T
|
||||||
if (type == wxPAT_PWCHAR) {
|
if (m_type == wxPAT_PWCHAR)
|
||||||
|
{
|
||||||
// user passed a string explicitely indicated as Unicode...
|
// user passed a string explicitely indicated as Unicode...
|
||||||
val = s = wxString(p->pad_pwchar, wxConvLibc);
|
val = s = wxString(p->pad_pwchar, wxConvLibc);
|
||||||
|
|
||||||
|
//printf("converting Unicode=>ANSI"); // for debug
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -808,15 +847,15 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p)
|
|||||||
if (val)
|
if (val)
|
||||||
{
|
{
|
||||||
#if wxUSE_STRUTILS
|
#if wxUSE_STRUTILS
|
||||||
// at this point we are sure that max_width is positive or null
|
// at this point we are sure that m_nMaxWidth is positive or null
|
||||||
// (see top of wxPrintfConvSpec::LoadArg)
|
// (see top of wxPrintfConvSpec::LoadArg)
|
||||||
len = wxMin((unsigned int)max_width, wxStrlen(val));
|
len = wxMin((unsigned int)m_nMaxWidth, wxStrlen(val));
|
||||||
#else
|
#else
|
||||||
for ( len = 0; val[len] && (len < max_width); len++ )
|
for ( len = 0; val[len] && (len < m_nMaxWidth); len++ )
|
||||||
;
|
;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else if (max_width >= 6)
|
else if (m_nMaxWidth >= 6)
|
||||||
{
|
{
|
||||||
val = wxT("(null)");
|
val = wxT("(null)");
|
||||||
len = 6;
|
len = 6;
|
||||||
@@ -829,15 +868,13 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p)
|
|||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!adj_left)
|
if (!m_bAlignLeft)
|
||||||
{
|
{
|
||||||
for (i = len; i < min_width; i++)
|
for (i = len; i < m_nMinWidth; i++)
|
||||||
APPEND_CH(_T(' '));
|
APPEND_CH(_T(' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if wxUSE_STRUTILS
|
#if wxUSE_STRUTILS
|
||||||
// at this point we are sure that max_width is positive or null
|
|
||||||
// (see top of wxPrintfConvSpec::LoadArg)
|
|
||||||
len = wxMin((unsigned int)len, lenMax-lenCur);
|
len = wxMin((unsigned int)len, lenMax-lenCur);
|
||||||
wxStrncpy(buf+lenCur, val, len);
|
wxStrncpy(buf+lenCur, val, len);
|
||||||
lenCur += len;
|
lenCur += len;
|
||||||
@@ -846,9 +883,9 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p)
|
|||||||
APPEND_CH(val[i]);
|
APPEND_CH(val[i]);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (adj_left)
|
if (m_bAlignLeft)
|
||||||
{
|
{
|
||||||
for (i = len; i < min_width; i++)
|
for (i = len; i < m_nMinWidth; i++)
|
||||||
APPEND_CH(_T(' '));
|
APPEND_CH(_T(' '));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -873,7 +910,7 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p)
|
|||||||
|
|
||||||
// if we used system's sprintf() then we now need to append the s_szScratch
|
// if we used system's sprintf() then we now need to append the s_szScratch
|
||||||
// buffer to the given one...
|
// buffer to the given one...
|
||||||
switch (type)
|
switch (m_type)
|
||||||
{
|
{
|
||||||
case wxPAT_INT:
|
case wxPAT_INT:
|
||||||
case wxPAT_LONGINT:
|
case wxPAT_LONGINT:
|
||||||
@@ -886,16 +923,19 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p)
|
|||||||
case wxPAT_POINTER:
|
case wxPAT_POINTER:
|
||||||
#if wxUSE_STRUTILS
|
#if wxUSE_STRUTILS
|
||||||
{
|
{
|
||||||
const wxMB2WXbuf tmp = wxConvLibc.cMB2WX(szScratch);
|
wxASSERT(lenScratch >= 0 && lenScratch < wxSCRATCH_BUFFER_SIZE);
|
||||||
size_t len = wxMin(lenMax, wxStrlen(tmp));
|
if (lenMax < lenScratch)
|
||||||
wxStrncpy(buf, tmp, len);
|
{
|
||||||
lenCur += len;
|
// fill output buffer and then return -1
|
||||||
|
wxStrncpy(buf, szScratch, lenMax);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
wxStrncpy(buf, szScratch, lenScratch);
|
||||||
|
lenCur += lenScratch;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
{
|
{
|
||||||
const wxMB2WXbuf tmp =
|
APPEND_STR(szScratch);
|
||||||
wxConvLibc.cMB2WX(szScratch);
|
|
||||||
APPEND_STR(tmp);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
@@ -940,10 +980,16 @@ static int wxCopyStrWithPercents(wxChar *dest, const wxChar *source, size_t n)
|
|||||||
int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax,
|
int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax,
|
||||||
const wxChar *format, va_list argptr)
|
const wxChar *format, va_list argptr)
|
||||||
{
|
{
|
||||||
// cached data
|
// useful for debugging, to understand if we are really using this function
|
||||||
static wxPrintfConvSpec arg[wxMAX_SVNPRINTF_ARGUMENTS];
|
// rather than the system implementation
|
||||||
static wxPrintfArg argdata[wxMAX_SVNPRINTF_ARGUMENTS];
|
#if 0
|
||||||
static wxPrintfConvSpec *pspec[wxMAX_SVNPRINTF_ARGUMENTS] = { NULL };
|
wprintf(L"Using wxVsnprintf_\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// required memory:
|
||||||
|
wxPrintfConvSpec arg[wxMAX_SVNPRINTF_ARGUMENTS];
|
||||||
|
wxPrintfArg argdata[wxMAX_SVNPRINTF_ARGUMENTS];
|
||||||
|
wxPrintfConvSpec *pspec[wxMAX_SVNPRINTF_ARGUMENTS] = { NULL };
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
@@ -968,24 +1014,38 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax,
|
|||||||
wxPrintfConvSpec *current = &arg[nargs];
|
wxPrintfConvSpec *current = &arg[nargs];
|
||||||
|
|
||||||
// make toparse point to the end of this specifier
|
// make toparse point to the end of this specifier
|
||||||
toparse = current->argend;
|
toparse = current->m_pArgEnd;
|
||||||
|
|
||||||
if (current->pos > 0) {
|
if (current->m_pos > 0)
|
||||||
|
{
|
||||||
// the positionals start from number 1... adjust the index
|
// the positionals start from number 1... adjust the index
|
||||||
current->pos--;
|
current->m_pos--;
|
||||||
posarg_present = true;
|
posarg_present = true;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// not a positional argument...
|
// not a positional argument...
|
||||||
current->pos = nargs;
|
current->m_pos = nargs;
|
||||||
nonposarg_present = true;
|
nonposarg_present = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this conversion specifier is tied to the pos-th argument...
|
// this conversion specifier is tied to the pos-th argument...
|
||||||
pspec[current->pos] = current;
|
pspec[current->m_pos] = current;
|
||||||
nargs++;
|
nargs++;
|
||||||
|
|
||||||
if (nargs == wxMAX_SVNPRINTF_ARGUMENTS)
|
if (nargs == wxMAX_SVNPRINTF_ARGUMENTS)
|
||||||
|
{
|
||||||
|
wxLogDebug(wxT("A single call to wxVsnprintf() has more than %d arguments; ")
|
||||||
|
wxT("ignoring all remaining arguments."), wxMAX_SVNPRINTF_ARGUMENTS);
|
||||||
break; // cannot handle any additional conv spec
|
break; // cannot handle any additional conv spec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// it's safe to look in the next character of toparse as at worst
|
||||||
|
// we'll hit its \0
|
||||||
|
if (*(toparse+1) == wxT('%'))
|
||||||
|
toparse++; // the Parse() returned false because we've found a %%
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1001,10 +1061,11 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax,
|
|||||||
wxVaCopy(ap, argptr);
|
wxVaCopy(ap, argptr);
|
||||||
|
|
||||||
// now load arguments from stack
|
// now load arguments from stack
|
||||||
for (i=0; i < nargs && ok; i++) {
|
for (i=0; i < nargs && ok; i++)
|
||||||
// !pspec[i] if user forgot a positional parameter (e.g. %$1s %$3s) ?
|
{
|
||||||
// or LoadArg false if wxPrintfConvSpec::Parse failed to set its 'type'
|
// !pspec[i] means that the user forgot a positional parameter (e.g. %$1s %$3s);
|
||||||
// to a valid value...
|
// LoadArg == false means that wxPrintfConvSpec::Parse failed to set the
|
||||||
|
// conversion specifier 'type' to a valid value...
|
||||||
ok = pspec[i] && pspec[i]->LoadArg(&argdata[i], ap);
|
ok = pspec[i] && pspec[i]->LoadArg(&argdata[i], ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1020,21 +1081,30 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax,
|
|||||||
{
|
{
|
||||||
// copy in the output buffer the portion of the format string between
|
// copy in the output buffer the portion of the format string between
|
||||||
// last specifier and the current one
|
// last specifier and the current one
|
||||||
size_t tocopy = ( arg[i].argpos - toparse );
|
size_t tocopy = ( arg[i].m_pArgPos - toparse );
|
||||||
if (lenCur+tocopy >= lenMax)
|
if (lenCur+tocopy >= lenMax)
|
||||||
return -1; // not enough space in the output buffer !
|
{
|
||||||
|
// not enough space in the output buffer !
|
||||||
|
// copy until the end of remaining space and then stop
|
||||||
|
wxCopyStrWithPercents(buf+lenCur, toparse, lenMax - lenCur - 1);
|
||||||
|
buf[lenMax-1] = wxT('\0');
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
lenCur += wxCopyStrWithPercents(buf+lenCur, toparse, tocopy);
|
lenCur += wxCopyStrWithPercents(buf+lenCur, toparse, tocopy);
|
||||||
|
|
||||||
// process this specifier directly in the output buffer
|
// process this specifier directly in the output buffer
|
||||||
int n = arg[i].Process(buf+lenCur, lenMax - lenCur, &argdata[arg[i].pos]);
|
int n = arg[i].Process(buf+lenCur, lenMax - lenCur, &argdata[arg[i].m_pos]);
|
||||||
if (n == -1)
|
if (n == -1)
|
||||||
|
{
|
||||||
|
buf[lenMax-1] = wxT('\0'); // be sure to always NUL-terminate the string
|
||||||
return -1; // not enough space in the output buffer !
|
return -1; // not enough space in the output buffer !
|
||||||
|
}
|
||||||
lenCur += n;
|
lenCur += n;
|
||||||
|
|
||||||
// the +1 is because wxPrintfConvSpec::argend points to the last character
|
// the +1 is because wxPrintfConvSpec::m_pArgEnd points to the last character
|
||||||
// of the format specifier, but we are not interested to it...
|
// of the format specifier, but we are not interested to it...
|
||||||
toparse = arg[i].argend + 1;
|
toparse = arg[i].m_pArgEnd + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy portion of the format string after last specifier
|
// copy portion of the format string after last specifier
|
||||||
@@ -1048,10 +1118,6 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax,
|
|||||||
// the -1 is because of the '\0'
|
// the -1 is because of the '\0'
|
||||||
lenCur += wxCopyStrWithPercents(buf+lenCur, toparse, tocopy) - 1;
|
lenCur += wxCopyStrWithPercents(buf+lenCur, toparse, tocopy) - 1;
|
||||||
|
|
||||||
// clean the static array portion used...
|
|
||||||
// NOTE: other arrays do not need cleanup!
|
|
||||||
memset(pspec, 0, sizeof(wxPrintfConvSpec*)*nargs);
|
|
||||||
|
|
||||||
wxASSERT(lenCur == wxStrlen(buf));
|
wxASSERT(lenCur == wxStrlen(buf));
|
||||||
return lenCur;
|
return lenCur;
|
||||||
}
|
}
|
||||||
|
271
tests/benchmarks/printfbench.cpp
Normal file
271
tests/benchmarks/printfbench.cpp
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Name: printfbench.cpp
|
||||||
|
// Purpose: A sample console app which benchmarks wxPrintf*() functions
|
||||||
|
// Author: Francesco Montorsi
|
||||||
|
// Modified by:
|
||||||
|
// Created: 27/3/2006
|
||||||
|
// RCS-ID: $Id$
|
||||||
|
// Copyright: (c) 2006 Francesco Montorsi
|
||||||
|
// Licence: wxWindows license
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// declarations
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// headers
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include <wx/string.h>
|
||||||
|
#include <wx/stopwatch.h>
|
||||||
|
#include <wx/utils.h>
|
||||||
|
#include <wx/cmdline.h>
|
||||||
|
#include <wx/app.h>
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// command line
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define HELP_SWITCH wxT("h")
|
||||||
|
#define NUMBENCHMARK_OPTION wxT("n")
|
||||||
|
|
||||||
|
static const wxCmdLineEntryDesc g_cmdLineDesc[] =
|
||||||
|
{
|
||||||
|
{ wxCMD_LINE_SWITCH, HELP_SWITCH, wxT("help"),
|
||||||
|
wxT("displays help on the command line parameters") },
|
||||||
|
|
||||||
|
{ wxCMD_LINE_OPTION, NUMBENCHMARK_OPTION, wxT("numtest"),
|
||||||
|
wxT("the number of wxPrintf() calls to benchmark"), wxCMD_LINE_VAL_NUMBER },
|
||||||
|
|
||||||
|
{ wxCMD_LINE_NONE }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// constants
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define DEFAULT_NUMBENCHMARKS 100000
|
||||||
|
#define BUFSIZE 10000
|
||||||
|
|
||||||
|
// set wxTEST_WX_ONLY to 1 when you want to profile wx's implementation only.
|
||||||
|
// A little reminder about profiling under Linux:
|
||||||
|
//
|
||||||
|
// 1) configure wxWidgets in release mode
|
||||||
|
// 2) make sure that HAVE_UNIX98_PRINTF is undefined (just #defining it to zero
|
||||||
|
// does not work; you must comment out the entire #define) in your setup.h;
|
||||||
|
// and also that wxUSE_PRINTF_POS_PARAMS is set to 1; this will force the
|
||||||
|
// use of wx's own implementation of wxVsnprintf()
|
||||||
|
// 3) compile wx
|
||||||
|
// 4) set wxTEST_WX_ONLY to 1 and compile tests as well
|
||||||
|
// 5) run "callgrind ./printfbench"
|
||||||
|
// 6) run "kcachegrind dump_file_generated_by_callgrind"
|
||||||
|
//
|
||||||
|
#define wxTEST_WX_ONLY 1
|
||||||
|
|
||||||
|
|
||||||
|
const wxString g_verylongString =
|
||||||
|
wxT("very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very ")
|
||||||
|
wxT("very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string!\n\n\n");
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// benchmarking helpers
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define DO_LONG_BENCHMARK(fnc) \
|
||||||
|
fnc(buffer, BUFSIZE, \
|
||||||
|
wxT("This is a reasonably long string with various %s arguments, exactly %d, ") \
|
||||||
|
wxT("and is used as benchmark for %s - %% %.2f %d %s"), \
|
||||||
|
wxT("(many!!)"), 6, wxT("this program"), 23.342f, 999, \
|
||||||
|
g_verylongString.c_str());
|
||||||
|
|
||||||
|
#define DO_LONG_POSITIONAL_BENCHMARK(fnc) \
|
||||||
|
fnc(buffer, BUFSIZE, \
|
||||||
|
wxT("This is a %2$s and thus is harder to parse... let's %1$s ") \
|
||||||
|
wxT("for our benchmarking aims - %% %3$f %5$d %4$s"), \
|
||||||
|
wxT("test it"), wxT("string with positional arguments"), 23.342f, \
|
||||||
|
g_verylongString.c_str(), 999);
|
||||||
|
|
||||||
|
#define DO_BENCHMARK(fnc) \
|
||||||
|
fnc(buffer, BUFSIZE, \
|
||||||
|
wxT("This is a short %s string with very few words"), wxT("test"));
|
||||||
|
|
||||||
|
#define DO_POSITIONAL_BENCHMARK(fnc) \
|
||||||
|
fnc(buffer, BUFSIZE, \
|
||||||
|
wxT("This is a %2$s and thus is harder to parse... nonetheless, %1$s !"), \
|
||||||
|
wxT("test it"), wxT("string with positional arguments"));
|
||||||
|
|
||||||
|
// the configure script of wxWidgets will define HAVE_UNIX98_PRINTF on those
|
||||||
|
// system with a *printf() family of functions conformant to Unix 98 standard;
|
||||||
|
// systems without the configure script as build system (e.g. Windows) do not
|
||||||
|
// have positional support anyway
|
||||||
|
#ifdef HAVE_UNIX98_PRINTF
|
||||||
|
#define wxSYSTEM_HAS_POSPARAM_SUPPORT 1
|
||||||
|
#else
|
||||||
|
#define wxSYSTEM_HAS_POSPARAM_SUPPORT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// we need to avoid the use of wxPrintf() here since it could have been mapped
|
||||||
|
// to wxWidgets' implementation of wxVsnPrintf() !
|
||||||
|
#if wxUSE_UNICODE
|
||||||
|
#define sys_printf swprintf
|
||||||
|
#else
|
||||||
|
#define sys_printf snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// the given stopwatch returns a time delta in milliseconds thus this macro
|
||||||
|
// returns the number of microseconds required for a single *printf() call
|
||||||
|
#define wxFMT(sw) ((double)((sw.Time()*1000.0)/tests))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// main
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program");
|
||||||
|
wxInitializer initializer;
|
||||||
|
if ( !initializer )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed to initialize the wxWidgets library, aborting.");
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the command line
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
wxCmdLineParser cmdParser(g_cmdLineDesc, argc, argv);
|
||||||
|
if (cmdParser.Parse() != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (cmdParser.Found(HELP_SWITCH))
|
||||||
|
{
|
||||||
|
cmdParser.Usage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
long tests;
|
||||||
|
if (!cmdParser.Found(NUMBENCHMARK_OPTION, &tests))
|
||||||
|
tests = DEFAULT_NUMBENCHMARKS;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// print some info useful to compare different benchmarks
|
||||||
|
// -------------------------------------------------------
|
||||||
|
|
||||||
|
wxPrintf(wxT("\nRunning on %s\n"), wxGetOsDescription().c_str());
|
||||||
|
wxPrintf(wxT("Compiled in %s-%s-%s mode...\n\n"),
|
||||||
|
#if wxUSE_UNICODE
|
||||||
|
wxT("unicode"),
|
||||||
|
#else
|
||||||
|
wxT("ansi"),
|
||||||
|
#endif
|
||||||
|
#ifdef __WXDEBUG__
|
||||||
|
wxT("debug"),
|
||||||
|
#else
|
||||||
|
wxT("release"),
|
||||||
|
#endif
|
||||||
|
#ifdef WXUSINGDLL
|
||||||
|
wxT("shared")
|
||||||
|
#else
|
||||||
|
wxT("static")
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
wxPrintf(wxT("Running %ld tests, for each configuration/implementation\n\n"), tests);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// start!
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
wxChar buffer[BUFSIZE];
|
||||||
|
|
||||||
|
#if !wxTEST_WX_ONLY
|
||||||
|
|
||||||
|
#if wxUSE_PRINTF_POS_PARAMS
|
||||||
|
wxStopWatch wxPos;
|
||||||
|
for (int i=0; i < tests; i++)
|
||||||
|
{
|
||||||
|
DO_LONG_POSITIONAL_BENCHMARK(wxSnprintf)
|
||||||
|
DO_POSITIONAL_BENCHMARK(wxSnprintf)
|
||||||
|
}
|
||||||
|
wxPos.Pause();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// benchmark system implementation of snprintf()
|
||||||
|
wxStopWatch sys;
|
||||||
|
for (int i=0; i < tests; i++)
|
||||||
|
{
|
||||||
|
DO_LONG_BENCHMARK(sys_printf)
|
||||||
|
DO_BENCHMARK(sys_printf)
|
||||||
|
}
|
||||||
|
sys.Pause();
|
||||||
|
|
||||||
|
#if wxSYSTEM_HAS_POSPARAM_SUPPORT
|
||||||
|
wxStopWatch sysPos;
|
||||||
|
for (int i=0; i < tests; i++)
|
||||||
|
{
|
||||||
|
DO_LONG_POSITIONAL_BENCHMARK(wxSnprintf)
|
||||||
|
DO_POSITIONAL_BENCHMARK(wxSnprintf)
|
||||||
|
}
|
||||||
|
sysPos.Pause();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else // !wxTEST_WX_ONLY
|
||||||
|
|
||||||
|
// fake stopwatches
|
||||||
|
wxStopWatch wxPos, sys, sysPos;
|
||||||
|
wxPos.Pause();
|
||||||
|
sys.Pause();
|
||||||
|
sysPos.Pause();
|
||||||
|
|
||||||
|
#endif // !wxTEST_WX_ONLY
|
||||||
|
|
||||||
|
// benchmark wxWidgets implementation of wxSnprintf()
|
||||||
|
wxStopWatch wx;
|
||||||
|
for (int i=0; i < tests; i++)
|
||||||
|
{
|
||||||
|
DO_LONG_BENCHMARK(wxSnprintf)
|
||||||
|
DO_BENCHMARK(wxSnprintf)
|
||||||
|
}
|
||||||
|
wx.Pause();
|
||||||
|
|
||||||
|
// print results
|
||||||
|
// ----------------------
|
||||||
|
|
||||||
|
wxPrintf(wxT("\n ============================== RESULTS ==============================\n"));
|
||||||
|
wxPrintf(wxT(" => Time for the system's snprintf(): %.5f microsec\n"), wxFMT(sys));
|
||||||
|
#if wxSYSTEM_HAS_POSPARAM_SUPPORT
|
||||||
|
wxPrintf(wxT(" => Time for the system's snprintf() with positionals: %.5f microsec\n"), wxFMT(sysPos));
|
||||||
|
#endif
|
||||||
|
wxPrintf(wxT(" => Time for wxSnprintf(): %.5f microsec\n"), wxFMT(wx));
|
||||||
|
#if wxUSE_PRINTF_POS_PARAMS
|
||||||
|
wxPrintf(wxT(" => Time for wxSnprintf() with positionals: %.5f microsec\n"), wxFMT(wxPos));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
double medium;
|
||||||
|
#if wxSYSTEM_HAS_POSPARAM_SUPPORT && wxUSE_PRINTF_POS_PARAMS
|
||||||
|
medium = ((double)wx.Time() / (double)sys.Time() + (double)wxPos.Time() / (double)sysPos.Time()) / 2;
|
||||||
|
#else
|
||||||
|
medium = (double)wx.Time() / (double)sys.Time();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (medium > 0.0)
|
||||||
|
{
|
||||||
|
// sometimes it happens that with a small number of tests, medium results zero;
|
||||||
|
// in that case doing the 1.0/medium will not be a wise thing!
|
||||||
|
wxPrintf(wxT("\nwxWidgets implementation is %.3f times slower\n")
|
||||||
|
wxT("(i.e. %.3f times faster) than system implementation.\n"), medium, 1.0/medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
wxPrintf(wxT("\n\n"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@@ -81,6 +81,8 @@ private:
|
|||||||
CPPUNIT_TEST( G );
|
CPPUNIT_TEST( G );
|
||||||
CPPUNIT_TEST( S );
|
CPPUNIT_TEST( S );
|
||||||
CPPUNIT_TEST( Asterisk );
|
CPPUNIT_TEST( Asterisk );
|
||||||
|
CPPUNIT_TEST( Percent );
|
||||||
|
CPPUNIT_TEST( LongLong );
|
||||||
|
|
||||||
CPPUNIT_TEST( BigToSmallBuffer );
|
CPPUNIT_TEST( BigToSmallBuffer );
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
@@ -90,6 +92,9 @@ private:
|
|||||||
void G();
|
void G();
|
||||||
void S();
|
void S();
|
||||||
void Asterisk();
|
void Asterisk();
|
||||||
|
void Percent();
|
||||||
|
void LongLong();
|
||||||
|
void Unicode();
|
||||||
|
|
||||||
void BigToSmallBuffer();
|
void BigToSmallBuffer();
|
||||||
void Misc(wxChar *buffer, int size);
|
void Misc(wxChar *buffer, int size);
|
||||||
@@ -179,15 +184,30 @@ void VsnprintfTestCase::S()
|
|||||||
|
|
||||||
CMP3("abcde", "%.5s", wxT("abcdefghi"));
|
CMP3("abcde", "%.5s", wxT("abcdefghi"));
|
||||||
|
|
||||||
// some tests without any argument passed through ...
|
// do the same tests but with Unicode characters:
|
||||||
CMP2("%", "%%");
|
#if wxUSE_UNICODE
|
||||||
CMP2("%%%", "%%%%%%");
|
#define ALPHA "\x3B1"
|
||||||
|
#define BETA "\x3B2"
|
||||||
|
#define GAMMA "\x3B3"
|
||||||
|
#define DELTA "\x3B4"
|
||||||
|
#define EPSILON "\x3B5"
|
||||||
|
#define ZETA "\x3B6"
|
||||||
|
#define ETA "\x3B7"
|
||||||
|
#define THETA "\x3B8"
|
||||||
|
#define IOTA "\x3B9"
|
||||||
|
|
||||||
// do not test odd number of '%' symbols as different implementations
|
#define ABC ALPHA BETA GAMMA
|
||||||
// of snprintf() give different outputs as this situation is not considered
|
#define ABCDE ALPHA BETA GAMMA DELTA EPSILON
|
||||||
// by any standard (in fact, GCC will also warn you about a spurious % if
|
#define ABCDEFGHI ALPHA BETA GAMMA DELTA EPSILON ZETA ETA THETA IOTA
|
||||||
// you write %%% as argument of some *printf function !)
|
|
||||||
// Compare(wxT("%"), wxT("%%%"));
|
CMP3(" " ABC, "%5s", wxT(ABC));
|
||||||
|
CMP3(" " ALPHA, "%5s", wxT(ALPHA));
|
||||||
|
CMP3(ABCDEFGHI, "%5s", wxT(ABCDEFGHI));
|
||||||
|
CMP3(ABC " ", "%-5s", wxT(ABC));
|
||||||
|
CMP3(ABCDEFGHI, "%-5s", wxT(ABCDEFGHI));
|
||||||
|
|
||||||
|
CMP3(ABCDE, "%.5s", wxT(ABCDEFGHI));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void VsnprintfTestCase::Asterisk()
|
void VsnprintfTestCase::Asterisk()
|
||||||
@@ -199,6 +219,30 @@ void VsnprintfTestCase::Asterisk()
|
|||||||
CMP4("%0.002", "%%%.*f", 3, 0.0023456789);
|
CMP4("%0.002", "%%%.*f", 3, 0.0023456789);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VsnprintfTestCase::Percent()
|
||||||
|
{
|
||||||
|
// some tests without any argument passed through ...
|
||||||
|
CMP2("%", "%%");
|
||||||
|
CMP2("%%%", "%%%%%%");
|
||||||
|
|
||||||
|
CMP3("% abc", "%%%5s", wxT("abc"));
|
||||||
|
CMP3("% abc%", "%%%5s%%", wxT("abc"));
|
||||||
|
|
||||||
|
// do not test odd number of '%' symbols as different implementations
|
||||||
|
// of snprintf() give different outputs as this situation is not considered
|
||||||
|
// by any standard (in fact, GCC will also warn you about a spurious % if
|
||||||
|
// you write %%% as argument of some *printf function !)
|
||||||
|
// Compare(wxT("%"), wxT("%%%"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VsnprintfTestCase::LongLong()
|
||||||
|
{
|
||||||
|
CMP3("123456789", "%lld", (long long int)123456789);
|
||||||
|
CMP3("-123456789", "%lld", (long long int)-123456789);
|
||||||
|
|
||||||
|
CMP3("123456789", "%llu", (unsigned long long int)123456789);
|
||||||
|
}
|
||||||
|
|
||||||
void VsnprintfTestCase::Misc(wxChar *buffer, int size)
|
void VsnprintfTestCase::Misc(wxChar *buffer, int size)
|
||||||
{
|
{
|
||||||
// NB: remember that wx*printf could be mapped either to system
|
// NB: remember that wx*printf could be mapped either to system
|
||||||
|
@@ -88,4 +88,17 @@
|
|||||||
<files>testdata.fc</files>
|
<files>testdata.fc</files>
|
||||||
</wx-data>
|
</wx-data>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- BENCHMARKS -->
|
||||||
|
|
||||||
|
<exe id="printfbench" template="wx_sample_console,wx_test"
|
||||||
|
template_append="wx_append_base">
|
||||||
|
<sources>
|
||||||
|
benchmarks/printfbench.cpp
|
||||||
|
</sources>
|
||||||
|
<wx-lib>base</wx-lib>
|
||||||
|
</exe>
|
||||||
|
|
||||||
|
|
||||||
</makefile>
|
</makefile>
|
||||||
|
Reference in New Issue
Block a user