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:
Vadim Zeitlin
2006-09-05 21:14:38 +00:00
parent 21d124e03c
commit 412a5c570d
6 changed files with 577 additions and 164 deletions

View File

@@ -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}

View File

@@ -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

View File

@@ -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;
} }

View 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;
}

View File

@@ -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

View File

@@ -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>