mirror of https://github.com/PCSX2/pcsx2.git
Changed the FastFormatString functions into nifty little classes that use TLS for their buffer workspaces. Result: a fully concurrent printf with zero malloc/free overhead. Use the pxsFmt macro for them -- which is a fully working alternative to wxsFormat().
(pxsFmt has been applied to console/logging only for now. Will apply it to more later, once the code is confirmed stable) git-svn-id: http://pcsx2.googlecode.com/svn/trunk@3596 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
c2e687ad27
commit
e5f87e068b
|
@ -410,6 +410,8 @@ protected:
|
|||
template< typename T, uint Alignment >
|
||||
class SafeAlignedArray : public SafeArray<T>
|
||||
{
|
||||
typedef SafeArray<T> _parent;
|
||||
|
||||
protected:
|
||||
T* _virtual_realloc( int newsize )
|
||||
{
|
||||
|
@ -423,6 +425,8 @@ protected:
|
|||
// Maybe useful,maybe not... no harm in attaching it. :D
|
||||
|
||||
public:
|
||||
using _parent::operator[];
|
||||
|
||||
virtual ~SafeAlignedArray()
|
||||
{
|
||||
safe_aligned_free( this->m_ptr );
|
||||
|
|
|
@ -155,17 +155,10 @@ public:
|
|||
m_valid_range = 0xffffffff;
|
||||
}
|
||||
|
||||
wxEXPLICIT ScopedArray( int size ) :
|
||||
m_array( pxAssertDev( size >= 0, "Invalid negative size specified." ) ? new T[size] : NULL )
|
||||
, m_valid_range( (uint)size )
|
||||
{
|
||||
}
|
||||
|
||||
// For breaking the 2gb barrier, lets provision this:
|
||||
wxEXPLICIT ScopedArray( s64 size ) :
|
||||
m_array( pxAssertDev( size >= 0 && (size < UINT_MAX), "Invalid negative size specified to ScopedArray." ) ? new T[size] : NULL )
|
||||
, m_valid_range( (uint)size )
|
||||
wxEXPLICIT ScopedArray( size_t size )
|
||||
{
|
||||
m_array = new T[size];
|
||||
m_valid_range = size;
|
||||
}
|
||||
|
||||
~ScopedArray() throw()
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "Dependencies.h"
|
||||
#include "SafeArray.h"
|
||||
|
||||
#include <wx/tokenzr.h>
|
||||
|
||||
|
@ -106,12 +107,66 @@ struct ParsedAssignmentString
|
|||
ParsedAssignmentString( const wxString& src );
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// FastFormatAscii
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Fast formatting of ASCII text. This class uses a process-wide format buffer that is
|
||||
// allocated only once and grown to accommodate string formatting needs. The buffer is
|
||||
// thread-safe. This technique reduces the overhead of formatting strings by eliminating
|
||||
// most or all heap allocation actions.
|
||||
//
|
||||
class FastFormatAscii
|
||||
{
|
||||
protected:
|
||||
SafeArray<char>* m_dest;
|
||||
bool m_deleteDest;
|
||||
|
||||
public:
|
||||
FastFormatAscii();
|
||||
~FastFormatAscii() throw();
|
||||
FastFormatAscii& Write( const char* fmt, ... );
|
||||
FastFormatAscii& WriteV( const char* fmt, va_list argptr );
|
||||
|
||||
const char* GetResult() const;
|
||||
operator const char*() const;
|
||||
|
||||
const wxString GetString() const;
|
||||
operator wxString() const;
|
||||
};
|
||||
|
||||
class FastFormatUnicode
|
||||
{
|
||||
protected:
|
||||
SafeArray<char>* m_dest;
|
||||
bool m_deleteDest;
|
||||
|
||||
public:
|
||||
FastFormatUnicode();
|
||||
~FastFormatUnicode() throw();
|
||||
|
||||
FastFormatUnicode& Write( const char* fmt, ... );
|
||||
FastFormatUnicode& Write( const wxChar* fmt, ... );
|
||||
FastFormatUnicode& WriteV( const char* fmt, va_list argptr );
|
||||
FastFormatUnicode& WriteV( const wxChar* fmt, va_list argptr );
|
||||
|
||||
const wxChar* GetResult() const;
|
||||
const wxString GetString() const;
|
||||
|
||||
operator const wxChar*() const
|
||||
{
|
||||
return (const wxChar*)m_dest->GetPtr();
|
||||
}
|
||||
|
||||
operator wxString() const
|
||||
{
|
||||
return (const wxChar*)m_dest->GetPtr();
|
||||
}
|
||||
};
|
||||
|
||||
extern bool pxParseAssignmentString( const wxString& src, wxString& ldest, wxString& rdest );
|
||||
|
||||
extern int FastFormatString_AsciiRaw(wxCharBuffer& dest, const char* fmt, va_list argptr);
|
||||
extern wxString FastFormatString_Ascii(const char* fmt, va_list argptr);
|
||||
extern wxString FastFormatString_Unicode(const wxChar* fmt, va_list argptr);
|
||||
|
||||
#define pxsFmt FastFormatUnicode().Write
|
||||
#define pxsFmtV FastFormatUnicode().WriteV
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Custom internal sprintf functions, which are ASCII only (even in UNICODE builds)
|
||||
|
|
|
@ -369,7 +369,7 @@ bool IConsoleWriter::Write( const char* fmt, ... ) const
|
|||
|
||||
va_list args;
|
||||
va_start(args,fmt);
|
||||
DoWrite( FastFormatString_Ascii(fmt, args) );
|
||||
DoWrite( pxsFmtV(fmt, args) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
@ -382,7 +382,7 @@ bool IConsoleWriter::Write( ConsoleColors color, const char* fmt, ... ) const
|
|||
va_list args;
|
||||
va_start(args,fmt);
|
||||
ConsoleColorScope cs( color );
|
||||
DoWrite( FastFormatString_Ascii(fmt, args) );
|
||||
DoWrite( pxsFmtV(fmt, args) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
@ -394,7 +394,7 @@ bool IConsoleWriter::WriteLn( const char* fmt, ... ) const
|
|||
|
||||
va_list args;
|
||||
va_start(args,fmt);
|
||||
DoWriteLn( _addIndentation( FastFormatString_Ascii(fmt, args), conlog_Indent ) );
|
||||
DoWriteLn( _addIndentation( pxsFmtV(fmt, args), conlog_Indent ) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
@ -406,7 +406,7 @@ bool IConsoleWriter::WriteLn( ConsoleColors color, const char* fmt, ... ) const
|
|||
va_list args;
|
||||
va_start(args,fmt);
|
||||
ConsoleColorScope cs( color );
|
||||
DoWriteLn( _addIndentation( FastFormatString_Ascii(fmt, args), conlog_Indent ) );
|
||||
DoWriteLn( _addIndentation( pxsFmtV(fmt, args), conlog_Indent ) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
@ -419,7 +419,7 @@ bool IConsoleWriter::Error( const char* fmt, ... ) const
|
|||
va_list args;
|
||||
va_start(args,fmt);
|
||||
ConsoleColorScope cs( Color_StrongRed );
|
||||
DoWriteLn( _addIndentation( FastFormatString_Ascii(fmt, args) ) );
|
||||
DoWriteLn( _addIndentation( pxsFmtV(fmt, args) ) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
@ -432,7 +432,7 @@ bool IConsoleWriter::Warning( const char* fmt, ... ) const
|
|||
va_list args;
|
||||
va_start(args,fmt);
|
||||
ConsoleColorScope cs( Color_StrongOrange );
|
||||
DoWriteLn( _addIndentation( FastFormatString_Ascii(fmt, args) ) );
|
||||
DoWriteLn( _addIndentation( pxsFmtV(fmt, args) ) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
@ -448,7 +448,7 @@ bool IConsoleWriter::Write( const wxChar* fmt, ... ) const
|
|||
|
||||
va_list args;
|
||||
va_start(args,fmt);
|
||||
DoWrite( FastFormatString_Unicode( fmt, args ) );
|
||||
DoWrite( pxsFmtV( fmt, args ) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
@ -461,7 +461,7 @@ bool IConsoleWriter::Write( ConsoleColors color, const wxChar* fmt, ... ) const
|
|||
va_list args;
|
||||
va_start(args,fmt);
|
||||
ConsoleColorScope cs( color );
|
||||
DoWrite( FastFormatString_Unicode( fmt, args ) );
|
||||
DoWrite( pxsFmtV( fmt, args ) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
@ -473,7 +473,7 @@ bool IConsoleWriter::WriteLn( const wxChar* fmt, ... ) const
|
|||
|
||||
va_list args;
|
||||
va_start(args,fmt);
|
||||
DoWriteLn( _addIndentation( FastFormatString_Unicode( fmt, args ), conlog_Indent ) );
|
||||
DoWriteLn( _addIndentation( pxsFmtV( fmt, args ), conlog_Indent ) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
@ -486,7 +486,7 @@ bool IConsoleWriter::WriteLn( ConsoleColors color, const wxChar* fmt, ... ) cons
|
|||
va_list args;
|
||||
va_start(args,fmt);
|
||||
ConsoleColorScope cs( color );
|
||||
DoWriteLn( _addIndentation( FastFormatString_Unicode( fmt, args ), conlog_Indent ) );
|
||||
DoWriteLn( _addIndentation( pxsFmtV( fmt, args ), conlog_Indent ) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
@ -499,7 +499,7 @@ bool IConsoleWriter::Error( const wxChar* fmt, ... ) const
|
|||
va_list args;
|
||||
va_start(args,fmt);
|
||||
ConsoleColorScope cs( Color_StrongRed );
|
||||
DoWriteLn( _addIndentation( FastFormatString_Unicode( fmt, args ) ) );
|
||||
DoWriteLn( _addIndentation( pxsFmtV( fmt, args ) ) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
@ -512,7 +512,7 @@ bool IConsoleWriter::Warning( const wxChar* fmt, ... ) const
|
|||
va_list args;
|
||||
va_start(args,fmt);
|
||||
ConsoleColorScope cs( Color_StrongOrange );
|
||||
DoWriteLn( _addIndentation( FastFormatString_Unicode( fmt, args ) ) );
|
||||
DoWriteLn( _addIndentation( pxsFmtV( fmt, args ) ) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
@ -528,7 +528,7 @@ bool IConsoleWriter::WriteFromStdout( const char* fmt, ... ) const
|
|||
|
||||
va_list args;
|
||||
va_start(args,fmt);
|
||||
DoWrite( FastFormatString_Ascii(fmt, args) );
|
||||
DoWrite( pxsFmtV(fmt, args) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
@ -541,7 +541,7 @@ bool IConsoleWriter::WriteFromStdout( ConsoleColors color, const char* fmt, ...
|
|||
va_list args;
|
||||
va_start(args,fmt);
|
||||
ConsoleColorScope cs( color );
|
||||
DoWriteFromStdout( FastFormatString_Ascii(fmt, args) );
|
||||
DoWriteFromStdout( pxsFmtV(fmt, args) );
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
|
|
|
@ -23,51 +23,120 @@ using namespace Threading;
|
|||
// system deadlock.
|
||||
static const int MaxFormattedStringLength = 0x80000;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// FormatBuffer
|
||||
// --------------------------------------------------------------------------------------
|
||||
// This helper class provides a "safe" way for us to check if the global handles for the
|
||||
// string format buffer have been initialized or destructed by the C++ global heap manager.
|
||||
// (C++ has no way to enforce init/destruct order on complex globals, thus the convoluted
|
||||
// use of booleans to check the status of the type initializers).
|
||||
//
|
||||
template< typename CharType >
|
||||
class FormatBuffer : public Mutex
|
||||
{
|
||||
public:
|
||||
bool& clearbit;
|
||||
SafeArray<CharType> buffer;
|
||||
wxMBConvUTF8 ConvUTF8;
|
||||
#include "TlsVariable.inl"
|
||||
|
||||
FormatBuffer( bool& bit_to_clear_on_destruction )
|
||||
: clearbit( bit_to_clear_on_destruction )
|
||||
, buffer( 4096, wxsFormat( L"%s Format Buffer", (sizeof(CharType)==1) ? "Ascii" : "Unicode" ) )
|
||||
// --------------------------------------------------------------------------------------
|
||||
// FastFormatBuffers
|
||||
// --------------------------------------------------------------------------------------
|
||||
template< typename CharType >
|
||||
class FastFormatBuffers
|
||||
{
|
||||
bit_to_clear_on_destruction = false;
|
||||
DeclareNoncopyableObject(FastFormatBuffers);
|
||||
|
||||
protected:
|
||||
typedef SafeAlignedArray<CharType,16> BufferType;
|
||||
|
||||
static const uint BufferCount = 3;
|
||||
|
||||
BufferType m_buffers[BufferCount];
|
||||
uint m_curslot;
|
||||
|
||||
public:
|
||||
FastFormatBuffers()
|
||||
{
|
||||
// This protects against potential recursive calls to our formatter, by forcing those
|
||||
// calls to use a dynamic buffer for formatting.
|
||||
m_curslot = BufferCount;
|
||||
|
||||
for (uint i=0; i<BufferCount; ++i)
|
||||
{
|
||||
//m_buffers[i].Name = wxsFormat(L"Ascii Formatting Buffer (slot%d)", i);
|
||||
m_buffers[i].Name = wxsFormat(L"%s Formatting Buffer (slot%d)",
|
||||
(sizeof(CharType)==1) ? L"Ascii" : L"Unicode", i);
|
||||
m_buffers[i].MakeRoomFor(1024);
|
||||
m_buffers[i].ChunkSize = 4096;
|
||||
}
|
||||
|
||||
virtual ~FormatBuffer() throw()
|
||||
m_curslot = 0;
|
||||
}
|
||||
|
||||
virtual ~FastFormatBuffers() throw()
|
||||
{
|
||||
clearbit = true;
|
||||
Wait(); // lock the mutex, just in case.
|
||||
pxAssumeDev(m_curslot==0, "Dangling Ascii formatting buffer detected!");
|
||||
}
|
||||
|
||||
bool HasFreeBuffer() const
|
||||
{
|
||||
return m_curslot < BufferCount;
|
||||
}
|
||||
|
||||
BufferType& GrabBuffer()
|
||||
{
|
||||
++m_curslot;
|
||||
pxAssume(m_curslot<BufferCount);
|
||||
return m_buffers[m_curslot];
|
||||
}
|
||||
|
||||
void ReleaseBuffer()
|
||||
{
|
||||
--m_curslot;
|
||||
pxAssume(m_curslot<BufferCount);
|
||||
}
|
||||
|
||||
BufferType& operator[](uint i)
|
||||
{
|
||||
pxAssume(i<BufferCount);
|
||||
return m_buffers[i];
|
||||
}
|
||||
};
|
||||
|
||||
// Assume the buffer is 'deleted' -- technically it's never existed on startup,
|
||||
// but "deleted" is well enough.
|
||||
// --------------------------------------------------------------------------------------
|
||||
// GlobalBufferManager
|
||||
// --------------------------------------------------------------------------------------
|
||||
// This local-scope class is needed in order to safely deal with C++ initializing and destroying
|
||||
// global objects in arbitrary order. The initbit is updated by the object when constructed and
|
||||
// destroyed; code using this class provides its own statically-initialized boolean (which MUST
|
||||
// default to false!) and then sets the boolean to true to indicate the object is ready for use.
|
||||
//
|
||||
template< typename T >
|
||||
class GlobalBufferManager
|
||||
{
|
||||
public:
|
||||
bool& initbit;
|
||||
T instance;
|
||||
|
||||
static bool ascii_buffer_is_deleted = true;
|
||||
static bool unicode_buffer_is_deleted = true;
|
||||
GlobalBufferManager( bool& globalBoolean )
|
||||
: initbit( globalBoolean )
|
||||
{
|
||||
initbit = true;
|
||||
}
|
||||
|
||||
static FormatBuffer<char> ascii_buffer( ascii_buffer_is_deleted );
|
||||
static FormatBuffer<wxChar> unicode_buffer( unicode_buffer_is_deleted );
|
||||
~GlobalBufferManager() throw()
|
||||
{
|
||||
initbit = false;
|
||||
instance.Dispose();
|
||||
}
|
||||
|
||||
static void format_that_ascii_mess( SafeArray<char>& buffer, const char* fmt, va_list argptr )
|
||||
T& Get()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
operator T&()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
static bool buffer_is_avail = false;
|
||||
static GlobalBufferManager< BaseTlsVariable< FastFormatBuffers< char > > > m_buffer_tls(buffer_is_avail);
|
||||
|
||||
static __releaseinline void format_that_ascii_mess( SafeArray<char>& buffer, uint writepos, const char* fmt, va_list argptr )
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
int size = buffer.GetLength();
|
||||
int len = vsnprintf(buffer.GetPtr(), size, fmt, argptr);
|
||||
int len = vsnprintf(buffer.GetPtr(writepos), size-writepos, fmt, argptr);
|
||||
|
||||
// some implementations of vsnprintf() don't NUL terminate
|
||||
// the string if there is not enough space for it so
|
||||
|
@ -83,25 +152,26 @@ static void format_that_ascii_mess( SafeArray<char>& buffer, const char* fmt, va
|
|||
if (len < 0)
|
||||
len = size + (size/4);
|
||||
|
||||
len += writepos;
|
||||
if (len < size) break;
|
||||
buffer.ExactAlloc( len + 1 );
|
||||
buffer.ExactAlloc( len + 31 );
|
||||
};
|
||||
|
||||
// performing an assertion or log of a truncated string is unsafe, so let's not; even
|
||||
// though it'd be kinda nice if we did.
|
||||
}
|
||||
|
||||
static void format_that_unicode_mess( SafeArray<wxChar>& buffer, const wxChar* fmt, va_list argptr)
|
||||
static __releaseinline void format_that_unicode_mess( SafeArray<char>& buffer, uint writepos, const wxChar* fmt, va_list argptr)
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
int size = buffer.GetLength();
|
||||
int len = wxVsnprintf(buffer.GetPtr(), size, fmt, argptr);
|
||||
int size = buffer.GetLength()/2;
|
||||
int len = wxVsnprintf((wxChar*)buffer.GetPtr(writepos), size-writepos, fmt, argptr);
|
||||
|
||||
// some implementations of vsnprintf() don't NUL terminate
|
||||
// the string if there is not enough space for it so
|
||||
// always do it manually
|
||||
buffer[size-1] = L'\0';
|
||||
((wxChar*)buffer.GetPtr())[size-1] = L'\0';
|
||||
|
||||
if( size >= MaxFormattedStringLength ) break;
|
||||
|
||||
|
@ -112,80 +182,134 @@ static void format_that_unicode_mess( SafeArray<wxChar>& buffer, const wxChar* f
|
|||
if ( len < 0 )
|
||||
len = size + (size/4);
|
||||
|
||||
len += writepos;
|
||||
if ( len < size ) break;
|
||||
buffer.ExactAlloc( len + 1 );
|
||||
buffer.ExactAlloc( (len + 31) * 2 );
|
||||
};
|
||||
|
||||
// performing an assertion or log of a truncated string is unsafe, so let's not; even
|
||||
// though it'd be kinda nice if we did.
|
||||
}
|
||||
|
||||
// returns the length of the string (not including the 0)
|
||||
int FastFormatString_AsciiRaw(wxCharBuffer& dest, const char* fmt, va_list argptr)
|
||||
SafeArray<char>* GetFormatBuffer( bool& deleteDest )
|
||||
{
|
||||
if( ascii_buffer_is_deleted )
|
||||
deleteDest = false;
|
||||
if (buffer_is_avail)
|
||||
{
|
||||
// This means that the program is shutting down and the C++ destructors are
|
||||
// running, randomly deallocating static variables from existence. We handle it
|
||||
// as gracefully as possible by allocating local vars to do our bidding (slow, but
|
||||
// ultimately necessary!)
|
||||
|
||||
SafeArray<char> localbuf( 4096, L"Temporary Ascii Formatting Buffer" );
|
||||
format_that_ascii_mess( localbuf, fmt, argptr );
|
||||
dest = localbuf.GetPtr();
|
||||
return strlen(dest);
|
||||
if (m_buffer_tls.Get()->HasFreeBuffer())
|
||||
return &m_buffer_tls.Get()->GrabBuffer();
|
||||
}
|
||||
|
||||
deleteDest = true;
|
||||
return new SafeArray<char>(2048, L"Temporary string formatting buffer");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// FastFormatUnicode (implementations)
|
||||
// --------------------------------------------------------------------------------------
|
||||
FastFormatUnicode::FastFormatUnicode()
|
||||
{
|
||||
m_dest = GetFormatBuffer(m_deleteDest);
|
||||
((wxChar*)m_dest->GetPtr())[0] = 0;
|
||||
}
|
||||
|
||||
FastFormatUnicode::~FastFormatUnicode() throw()
|
||||
{
|
||||
if (m_deleteDest)
|
||||
delete m_dest;
|
||||
else
|
||||
{
|
||||
// This is normal operation. The static buffers are available for use, and we use
|
||||
// them for sake of efficiency (fewer heap allocs, for sure!)
|
||||
|
||||
ScopedLock locker( ascii_buffer );
|
||||
format_that_ascii_mess( ascii_buffer.buffer, fmt, argptr );
|
||||
dest = ascii_buffer.buffer.GetPtr();
|
||||
return strlen(dest);
|
||||
m_buffer_tls.Get()->ReleaseBuffer();
|
||||
}
|
||||
|
||||
FastFormatUnicode& FastFormatUnicode::WriteV( const char* fmt, va_list argptr )
|
||||
{
|
||||
wxString converted( fromUTF8(FastFormatAscii().WriteV( fmt, argptr )) );
|
||||
|
||||
uint inspos = wxStrlen((wxChar*)m_dest->GetPtr());
|
||||
m_dest->MakeRoomFor((inspos + converted.Length() + 31)*2);
|
||||
wxStrcpy( &((wxChar*)m_dest->GetPtr())[inspos], converted );
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
wxString FastFormatString_Ascii(const char* fmt, va_list argptr)
|
||||
FastFormatUnicode& FastFormatUnicode::WriteV( const wxChar* fmt, va_list argptr )
|
||||
{
|
||||
if( ascii_buffer_is_deleted )
|
||||
{
|
||||
// This means that the program is shutting down and the C++ destructors are
|
||||
// running, randomly deallocating static variables from existence. We handle it
|
||||
// as gracefully as possible by allocating local vars to do our bidding (slow, but
|
||||
// ultimately necessary!)
|
||||
|
||||
SafeArray<char> localbuf( 4096, L"Temporary Ascii Formatting Buffer" );
|
||||
format_that_ascii_mess( localbuf, fmt, argptr );
|
||||
return fromUTF8( localbuf.GetPtr() );
|
||||
format_that_unicode_mess( *m_dest, wxStrlen((wxChar*)m_dest->GetPtr()), fmt, argptr );
|
||||
return *this;
|
||||
}
|
||||
|
||||
FastFormatUnicode& FastFormatUnicode::Write( const char* fmt, ... )
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, fmt);
|
||||
WriteV(fmt,list);
|
||||
va_end(list);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FastFormatUnicode& FastFormatUnicode::Write( const wxChar* fmt, ... )
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, fmt);
|
||||
WriteV(fmt,list);
|
||||
va_end(list);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const wxChar* FastFormatUnicode::GetResult() const
|
||||
{
|
||||
return (wxChar*)m_dest->GetPtr();
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// FastFormatAscii (implementations)
|
||||
// --------------------------------------------------------------------------------------
|
||||
FastFormatAscii::FastFormatAscii()
|
||||
{
|
||||
m_dest = GetFormatBuffer(m_deleteDest);
|
||||
m_dest->GetPtr()[0] = 0;
|
||||
}
|
||||
|
||||
FastFormatAscii::~FastFormatAscii() throw()
|
||||
{
|
||||
if (m_deleteDest)
|
||||
delete m_dest;
|
||||
else
|
||||
{
|
||||
// This is normal operation. The static buffers are available for use, and we use
|
||||
// them for sake of efficiency (fewer heap allocs, for sure!)
|
||||
|
||||
ScopedLock locker( ascii_buffer );
|
||||
format_that_ascii_mess( ascii_buffer.buffer, fmt, argptr );
|
||||
return fromUTF8( ascii_buffer.buffer.GetPtr() );
|
||||
}
|
||||
m_buffer_tls.Get()->ReleaseBuffer();
|
||||
}
|
||||
|
||||
wxString FastFormatString_Unicode(const wxChar* fmt, va_list argptr)
|
||||
const wxString FastFormatAscii::GetString() const
|
||||
{
|
||||
// See above for the explanation on the _is_deleted flags.
|
||||
return fromAscii(m_dest->GetPtr());
|
||||
}
|
||||
|
||||
if( unicode_buffer_is_deleted )
|
||||
FastFormatAscii::operator wxString() const
|
||||
{
|
||||
SafeArray<wxChar> localbuf( 4096, L"Temporary Unicode Formatting Buffer" );
|
||||
format_that_unicode_mess( localbuf, fmt, argptr );
|
||||
return localbuf.GetPtr();
|
||||
return fromAscii(m_dest->GetPtr());
|
||||
}
|
||||
else
|
||||
|
||||
FastFormatAscii& FastFormatAscii::WriteV( const char* fmt, va_list argptr )
|
||||
{
|
||||
ScopedLock locker( unicode_buffer );
|
||||
format_that_unicode_mess( unicode_buffer.buffer, fmt, argptr );
|
||||
return unicode_buffer.buffer.GetPtr();
|
||||
format_that_ascii_mess( *m_dest, strlen(m_dest->GetPtr()), fmt, argptr );
|
||||
return *this;
|
||||
}
|
||||
|
||||
FastFormatAscii& FastFormatAscii::Write( const char* fmt, ... )
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, fmt);
|
||||
WriteV(fmt,list);
|
||||
va_end(list);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const char* FastFormatAscii::GetResult() const
|
||||
{
|
||||
return m_dest->GetPtr();
|
||||
}
|
||||
|
||||
FastFormatAscii::operator const char*() const
|
||||
{
|
||||
return m_dest->GetPtr();
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@ pxWindowTextWriter& pxWindowTextWriter::FormatLn( const wxChar* fmt, ... )
|
|||
{
|
||||
va_list args;
|
||||
va_start(args,fmt);
|
||||
_DoWrite( FastFormatString_Unicode(fmt, args) );
|
||||
_DoWrite( pxsFmtV(fmt, args) );
|
||||
va_end(args);
|
||||
return *this;
|
||||
}
|
||||
|
|
|
@ -20,8 +20,8 @@ void AsciiFile::Printf( const char* fmt, ... )
|
|||
{
|
||||
va_list list;
|
||||
va_start( list, fmt );
|
||||
//std::string writeme; vssprintf( writeme, fmt, list );
|
||||
wxCharBuffer result; int reslen = FastFormatString_AsciiRaw(result, fmt, list);
|
||||
FastFormatAscii ascii;
|
||||
ascii.WriteV(fmt,list);
|
||||
va_end( list );
|
||||
Write( result.data(), reslen );
|
||||
Write( ascii, strlen(ascii) );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue