Bug 743056 - nsPrintfCString is consistently misused because people don't understand its length-truncation behavior. Make it behave the way people expect by expanding the string as necessary (and use nsTSubstring::AppendPrintf to unify the codepath), r=jlebar

--HG--
extra : rebase_source : 8d638515f4ef5b89f0ecaca5a4cc2974982d7092
This commit is contained in:
Benjamin Smedberg 2012-04-13 12:58:59 -04:00
parent a0b742a231
commit e4e9885e88
6 changed files with 69 additions and 138 deletions

View File

@ -56,6 +56,7 @@
#endif /* !NS_DISABLE_LITERAL_TEMPLATE */
#include <string.h>
#include <stdarg.h>
#define kNotFound -1

View File

@ -42,46 +42,44 @@
#include "nsString.h"
#endif
/**
* |nsPrintfCString| lets you use a formated |printf| string as an |const nsACString|.
*
* myCStr += nsPrintfCString("%f", 13.917);
* // ...a general purpose substitute for |AppendFloat|
*
* For longer patterns, you'll want to use the constructor that takes a length
*
* nsPrintfCString(128, "%f, %f, %f, %f, %f, %f, %f, %i, %f", x, y, z, 3.2, j, k, l, 3, 3.1);
*
* Exceding the default size (which you must specify in the constructor, it is not determined)
* causes an allocation, so avoid that. If your formatted string exceeds the allocated space, it is
* cut off at the size of the buffer, no error is reported (and no out-of-bounds writing occurs).
* This class is intended to be useful for numbers and short
* strings, not arbitrary formatting of other strings (e.g., with %s). There is currently no
* wide version of this class, since wide |printf| is not generally available. That means
* to get a wide version of your formatted data, you must, e.g.,
*
* CopyASCIItoUTF16(nsPrintfCString("%f", 13.917"), myStr);
*
* That's another good reason to avoid this class for anything but numbers ... as strings can be
* much more efficiently handled with |NS_LITERAL_[C]STRING| and |nsLiteral[C]String|.
*/
class nsPrintfCString : public nsCString
/**
* |nsPrintfCString| is syntactic sugar for a printf used as a
* |const nsACString| with a small builtin autobuffer. In almost all cases
* it is better to just use AppendPrintf. Example usage:
*
* NS_WARNING(nsPrintfCString("Unexpected value: %f", 13.917).get());
*/
class nsPrintfCString : public nsFixedCString
{
typedef nsCString string_type;
enum { kLocalBufferSize=15 };
public:
explicit nsPrintfCString( const char_type* format, ... )
: nsFixedCString(mLocalBuffer, kLocalBufferSize, 0)
{
va_list ap;
va_start(ap, format);
AppendPrintf(format, ap);
va_end(ap);
}
// Obsolete form required you to specify a length in advance. The length is now
// unused.
nsPrintfCString( size_type n, const char_type* format, ...)
: nsFixedCString(mLocalBuffer, kLocalBufferSize, 0)
{
va_list ap;
va_start(ap, format);
AppendPrintf(format, ap);
va_end(ap);
}
private:
enum { kLocalBufferSize=15 };
// ought to be large enough for most things ... a |long long| needs at most 20 (so you'd better ask)
// pinkerton suggests 7. We should measure and decide what's appropriate
public:
// XXX don't these need to be declared CDECL ??
explicit nsPrintfCString( const char_type* format, ... );
nsPrintfCString( size_type n, const char_type* format, ...);
private:
char_type mLocalBuffer[ kLocalBufferSize + 1 ];
char_type mLocalBuffer[ kLocalBufferSize ];
};
#endif // !defined(nsPrintfCString_h___)

View File

@ -387,34 +387,38 @@ class nsTSubstring_CharT
void AppendASCII( const char* data, size_type length = size_type(-1) ) { ReplaceASCII(mLength, 0, data, length); }
/**
* Append a formatted string to the current string. Uses the format
* codes documented in prprf.h
*/
void AppendPrintf( const char* format, ... );
void AppendInt( PRInt32 aInteger )
{ AppendPrintf31( "%d", aInteger ); }
{ AppendPrintf( "%d", aInteger ); }
void AppendInt( PRInt32 aInteger, int aRadix )
{
const char *fmt = aRadix == 10 ? "%d" : aRadix == 8 ? "%o" : "%x";
AppendPrintf31( fmt, aInteger );
AppendPrintf( fmt, aInteger );
}
void AppendInt( PRUint32 aInteger )
{ AppendPrintf31( "%u", aInteger ); }
{ AppendPrintf( "%u", aInteger ); }
void AppendInt( PRUint32 aInteger, int aRadix )
{
const char *fmt = aRadix == 10 ? "%u" : aRadix == 8 ? "%o" : "%x";
AppendPrintf31( fmt, aInteger );
AppendPrintf( fmt, aInteger );
}
void AppendInt( PRInt64 aInteger )
{ AppendPrintf31( "%lld", aInteger ); }
{ AppendPrintf( "%lld", aInteger ); }
void AppendInt( PRInt64 aInteger, int aRadix )
{
const char *fmt = aRadix == 10 ? "%lld" : aRadix == 8 ? "%llo" : "%llx";
AppendPrintf31( fmt, aInteger );
AppendPrintf( fmt, aInteger );
}
void AppendInt( PRUint64 aInteger )
{ AppendPrintf31( "%llu", aInteger ); }
{ AppendPrintf( "%llu", aInteger ); }
void AppendInt( PRUint64 aInteger, int aRadix )
{
const char *fmt = aRadix == 10 ? "%llu" : aRadix == 8 ? "%llo" : "%llx";
AppendPrintf31( fmt, aInteger );
AppendPrintf( fmt, aInteger );
}
/**
@ -426,8 +430,6 @@ class nsTSubstring_CharT
{ DoAppendFloat(aFloat, 15); }
private:
void NS_FASTCALL DoAppendFloat( double aFloat, int digits );
// AppendPrintf31 truncates output to 31 ASCII characters
void AppendPrintf31( const char* format, ... );
public:
// AppendLiteral must ONLY be applied to an actual literal string.
@ -740,6 +742,9 @@ class nsTSubstring_CharT
mFlags = dataFlags | (mFlags & 0xFFFF0000);
}
static PRIntn AppendFunc( void* arg, const char* s, PRUint32 len);
void AppendPrintf( const char* format, va_list ap );
public:
// mFlags is a bitwise combination of the following flags. the meaning

View File

@ -53,7 +53,6 @@ MOZILLA_INTERNAL_API = 1
CPPSRCS = \
nsDependentString.cpp \
nsDependentSubstring.cpp \
nsPrintfCString.cpp \
nsPromiseFlatString.cpp \
nsReadableUtils.cpp \
nsSubstring.cpp \

View File

@ -1,82 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1994-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsPrintfCString.h"
#include <stdarg.h>
#include "prprf.h"
// though these classes define a fixed buffer, they do not set the F_FIXED
// flag. this is because they are not intended to be modified after they have
// been constructed. we could change this but it would require adding a new
// class to the hierarchy, one that both this class and nsCAutoString would
// inherit from. for now, we populate the fixed buffer, and then let the
// nsCString code treat the buffer as if it were a dependent buffer.
nsPrintfCString::nsPrintfCString( const char_type* format, ... )
: string_type(mLocalBuffer, 0, F_TERMINATED)
{
va_list ap;
size_type logical_capacity = kLocalBufferSize;
size_type physical_capacity = logical_capacity + 1;
va_start(ap, format);
mLength = PR_vsnprintf(mData, physical_capacity, format, ap);
va_end(ap);
}
nsPrintfCString::nsPrintfCString( size_type n, const char_type* format, ... )
: string_type(mLocalBuffer, 0, F_TERMINATED)
{
va_list ap;
// make sure there's at least |n| space
size_type logical_capacity = kLocalBufferSize;
if ( n > logical_capacity )
{
if (!SetCapacity(n))
return; // out of memory !!
logical_capacity = n;
}
size_type physical_capacity = logical_capacity + 1;
va_start(ap, format);
mLength = PR_vsnprintf(mData, physical_capacity, format, ap);
va_end(ap);
}

View File

@ -731,27 +731,37 @@ nsTSubstring_CharT::StripChars( const char_type* aChars, PRUint32 aOffset )
mLength = to - mData;
}
void nsTSubstring_CharT::AppendPrintf31( const char* format, ...)
PRIntn
nsTSubstring_CharT::AppendFunc(void* arg, const char* s, PRUint32 len)
{
char buf[32];
va_list ap;
va_start(ap, format);
PRUint32 len = PR_vsnprintf(buf, sizeof(buf), format, ap);
AppendASCII(buf, len);
va_end(ap);
self_type* self = static_cast<self_type*>(arg);
// NSPR sends us the final null terminator even though we don't want it
if (len && s[len - 1] == '\0') {
--len;
}
self->AppendASCII(s, len);
return len;
}
void nsTSubstring_CharT::AppendPrintf( const char* format, ...)
{
char *buf;
va_list ap;
va_start(ap, format);
buf = PR_vsmprintf(format, ap);
AppendASCII(buf);
PR_smprintf_free(buf);
PRUint32 r = PR_vsxprintf(AppendFunc, this, format, ap);
if (r == (PRUint32) -1)
NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf");
va_end(ap);
}
void nsTSubstring_CharT::AppendPrintf( const char* format, va_list ap )
{
PRUint32 r = PR_vsxprintf(AppendFunc, this, format, ap);
if (r == (PRUint32) -1)
NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf");
}
/* hack to make sure we define Modified_cnvtf only once */
#ifdef CharT_is_PRUnichar