| Server IP : 209.209.40.120 / Your IP : 216.73.217.112 Web Server : Microsoft-IIS/10.0 System : Windows NT NEWWWW 10.0 build 17763 (Windows Server 2019) i586 User : NEWWWW$ ( 0) PHP Version : 8.3.30 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /Program Files (x86)/Windows Kits/10/Source/10.0.19041.0/ucrt/inc/ |
Upload File : |
//
// corecrt_internal_stdio_output.h
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// This file defines the core implementation of the formatted output functions,
// including printf and its many variants (sprintf, fprintf, etc.).
//
#include <conio.h>
#include <ctype.h>
#include <corecrt_internal_fltintrn.h>
#include <corecrt_internal_stdio.h>
#include <locale.h>
#include <stdarg.h>
// Functions handling input/output should be optimized for speed
// to avoid performance regressions from previous CRT versions.
#pragma optimize("t", on)
namespace __crt_stdio_output {
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Argument Handling
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// These provide a very basic encapsulation over a va_list. The 'read' function
// reads the next argument of type T from the varargs list and updates the
// va_list, just like va_arg does. The 'peek' function returns the next argument
// of type T, but does not modify the va_list.
template <typename T>
T read_va_arg(va_list& arglist) throw()
{
return va_arg(arglist, T);
}
template <typename T>
T peek_va_arg(va_list arglist) throw()
{
return va_arg(arglist, T);
}
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Output Adapters
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// The actual write operations are different depending on whether the target is
// a stream or the console. We handle these differences via output adapters.
// The stream and console I/O functions pass an adapter to the core output
// function, and that function calls the various write members of the adapter to
// perform the write operations.
template <typename Character, typename Derived>
class output_adapter_common
{
public:
void write_character(Character const c, int* const count_written) const throw()
{
if (static_cast<Derived const*>(this)->write_character_without_count_update(c))
{
++*count_written;
}
else
{
*count_written = -1;
}
}
protected:
void write_string_impl(
Character const* const string,
int const length,
int* const count_written,
__crt_deferred_errno_cache& status
) const throw()
{
__crt_errno_guard const reset_errno{&status.get()};
Character const* const string_last{string + length};
for (Character const* it{string}; it != string_last; ++it)
{
if (static_cast<Derived const*>(this)->write_character_without_count_update(*it))
{
++*count_written;
}
else
{
// Non-standard extension: EILSEQ errors are recoverable.
// Standard behavior when we've encountered an 'illegal sequence' error
// (i.e. EILSEQ) is to set 'errno' to EILSEQ and return -1.
// Instead, we write '?' and continue writing the string.
if (status.get() != EILSEQ)
{
// *printf returns the number of characters written
// set count written to -1 to indicate an error occurred
*count_written = -1;
break;
}
write_character('?', count_written);
}
}
}
};
template <typename Character>
class console_output_adapter
: public output_adapter_common<Character, console_output_adapter<Character>>
{
public:
typedef __acrt_stdio_char_traits<Character> char_traits;
bool validate() const throw()
{
return true;
}
bool write_character_without_count_update(Character const c) const throw()
{
return char_traits::puttch_nolock(c) != char_traits::eof;
}
void write_string(
Character const* const string,
int const length,
int* const count_written,
__crt_deferred_errno_cache& status
) const throw()
{
write_string_impl(string, length, count_written, status);
}
};
template <typename Character>
class stream_output_adapter
: public output_adapter_common<Character, stream_output_adapter<Character>>
{
public:
typedef __acrt_stdio_char_traits<Character> char_traits;
stream_output_adapter(FILE* const public_stream) throw()
: _stream{public_stream}
{
}
bool validate() const throw()
{
_VALIDATE_RETURN(_stream.valid(), EINVAL, false);
return char_traits::validate_stream_is_ansi_if_required(_stream.public_stream());
}
bool write_character_without_count_update(Character const c) const throw()
{
if (_stream.is_string_backed() && _stream->_base == nullptr)
{
return true;
}
return char_traits::puttc_nolock(c, _stream.public_stream()) != char_traits::eof;
}
void write_string(
Character const* const string,
int const length,
int* const count_written,
__crt_deferred_errno_cache& status
) const throw()
{
if (_stream.is_string_backed() && _stream->_base == nullptr)
{
*count_written += length;
return;
}
write_string_impl(string, length, count_written, status);
}
private:
__crt_stdio_stream _stream;
};
template <typename Character>
struct string_output_adapter_context
{
Character* _buffer;
size_t _buffer_count;
size_t _buffer_used;
bool _continue_count;
};
template <typename Character>
class string_output_adapter
{
public:
typedef __acrt_stdio_char_traits<Character> char_traits;
typedef string_output_adapter_context<Character> context_type;
string_output_adapter(context_type* const context) throw()
: _context(context)
{
}
bool validate() const throw()
{
_VALIDATE_RETURN(_context != nullptr, EINVAL, false);
return true;
}
bool write_character(Character const c, int* const count_written) const throw()
{
if (_context->_buffer_used == _context->_buffer_count)
{
if (_context->_continue_count)
{
++*count_written;
}
else
{
*count_written = -1;
}
return _context->_continue_count;
}
++*count_written;
++_context->_buffer_used;
*_context->_buffer++ = c;
return true;
}
void write_string(
Character const* const string,
int const length,
int* const count_written,
__crt_deferred_errno_cache& status
) const throw()
{
// This function does not perform any operations that might reset errno,
// so we don't need to use __crt_errno_guard as we do in other output
// adapters.
UNREFERENCED_PARAMETER(status);
if (length == 0)
{
return;
}
if (_context->_buffer_used == _context->_buffer_count)
{
if (_context->_continue_count)
{
*count_written += length;
}
else
{
*count_written = -1;
}
return;
}
size_t const space_available = _context->_buffer_count - _context->_buffer_used;
size_t const elements_to_copy = __min(space_available, static_cast<size_t>(length));
// Performance note: This is hot code. Profiling has shown the extra
// validation done by memcpy_s to be quite expensive. The validation is
// unnecessary, so we call memcpy directly here:
memcpy(
_context->_buffer,
string,
elements_to_copy * sizeof(Character));
_context->_buffer += elements_to_copy;
_context->_buffer_used += elements_to_copy;
if (_context->_continue_count)
{
*count_written += length;
}
else if (elements_to_copy != static_cast<size_t>(length))
{
*count_written = -1;
}
else
{
*count_written += static_cast<int>(elements_to_copy);
}
}
private:
context_type* _context;
};
template <typename OutputAdapter, typename Character>
void write_multiple_characters(
OutputAdapter const& adapter,
Character const c,
int const count,
int* const count_written
) throw()
{
for (int i{0}; i < count; ++i)
{
adapter.write_character(c, count_written);
if (*count_written == -1)
break;
}
}
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Formatting Buffer
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// This type encapsulates the buffer to be used when formatting numbers. It
// contains as a data member a large, statically-sized buffer, which is used
// at first. If a larger buffer is required a new, larger buffer is dynamically
// allocated.
class formatting_buffer
{
public:
enum
{
member_buffer_size = 1024,
};
static_assert(member_buffer_size >= (_CVTBUFSIZE + 6) * 2, "Buffer is too small");
formatting_buffer() throw()
: _dynamic_buffer_size{0}
{
}
template <typename T>
bool ensure_buffer_is_big_enough(size_t const count) throw()
{
constexpr size_t max_count = SIZE_MAX / sizeof(T) / 2; // avoid runtime division
_VALIDATE_RETURN_NOEXC(max_count >= count, ENOMEM, false);
size_t const required_size{count * sizeof(T) * 2};
// Once we allocate a dynamic buffer, we no longer use the member buffer
if (!_dynamic_buffer && required_size <= member_buffer_size) {
return true;
}
if (required_size <= _dynamic_buffer_size) {
return true;
}
__crt_unique_heap_ptr<char> new_buffer{_malloc_crt_t(char, required_size)};
if (!new_buffer) {
return false;
}
_dynamic_buffer = static_cast<__crt_unique_heap_ptr<char>&&>(new_buffer);
_dynamic_buffer_size = required_size;
return true;
}
template <typename T>
T* data() throw()
{
if (!_dynamic_buffer)
return reinterpret_cast<T*>(_member_buffer);
return reinterpret_cast<T*>(_dynamic_buffer.get());
}
template <typename T>
T* scratch_data() throw()
{
if (!_dynamic_buffer)
return reinterpret_cast<T*>(_member_buffer) + count<T>();
return reinterpret_cast<T*>(_dynamic_buffer.get()) + count<T>();
}
template <typename T>
size_t count() const throw()
{
if (!_dynamic_buffer)
return member_buffer_size / sizeof(T) / 2;
return _dynamic_buffer_size / sizeof(T) / 2;
}
template <typename T>
size_t scratch_count() const throw()
{
return count<T>();
}
private:
char _member_buffer[member_buffer_size];
size_t _dynamic_buffer_size;
__crt_unique_heap_ptr<char> _dynamic_buffer;
};
// This function forces a decimal point in floating point output. It is called
// if '#' flag is given and precision is 0, so we know the number has no '.' in
// its current representation. We insert the '.' and move everything back one
// position until '\0' is seen. This function updates the buffer in place.
inline void __cdecl force_decimal_point(_Inout_z_ char* buffer, _locale_t const locale) throw()
{
if (_tolower_fast_internal(static_cast<unsigned char>(*buffer), locale) != 'e')
{
do
{
++buffer;
}
while (_isdigit_fast_internal(static_cast<unsigned char>(*buffer), locale));
}
// Check if the buffer is in hexadecimal format (cfr %a or %A and fp_format_a):
if (_tolower_fast_internal(*buffer, locale) == 'x')
{
// The buffer is in the form: [-]0xhP+d, and buffer points to the 'x':
// we want to put the decimal point after the h digit: [-]0xh.P+d
buffer += 2; // REVIEW This can skip terminal nul?
}
char holdchar = *buffer;
*buffer++ = *locale->locinfo->lconv->decimal_point;
do
{
char const nextchar = *buffer;
*buffer = holdchar;
holdchar = nextchar;
}
while (*buffer++);
}
// This function removes trailing zeroes (after the '.') from a floating point
// number. This function is called only when formatting in the %g mode when
// there is no '#' flag and precision is nonzero. This function updates the
// buffer in-place.
//
// This function changes the contents of the buffer from:
// [-] digit [digit...] [ . [digits...] [0...] ] [(exponent part)]
// to:
// [-] digit [digit...] [ . digit [digits...] ] [(exponent part)]
// or:
// [-] digit [digit...] [(exponent part)]
inline void __cdecl crop_zeroes(_Inout_z_ char* buffer, _locale_t const locale) throw()
{
char const decimal_point = *locale->locinfo->lconv->decimal_point;
while (*buffer && *buffer != decimal_point)
++buffer;
if (*buffer++)
{
while (*buffer && *buffer != 'e' && *buffer != 'E')
++buffer;
char* stop = buffer--;
while (*buffer == '0')
--buffer;
if (*buffer == decimal_point)
--buffer;
while((*++buffer = *stop++) != '\0') { }
}
}
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// States and the State Transition Tables
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// These tables hold the state transition data for the format processor. The
// lower nibble of each byte is the character class of the character. The upper
// nibble of each byte gives the next state to enter.
// The states of the format parsing state machine. These are encoded into the
// transition tables and are referenced throughout the format processor.
enum class state : unsigned
{
normal, // Normal state; outputting literal chars
percent, // Just read '%'
flag, // Just read flag character
width, // Just read width specifier
dot, // Just read '.'
precision, // Just read precision specifier
size, // Just read size specifier
type, // Just read type specifier
invalid // Invalid format
};
// Manifest constants to represent the different types of characters that may
// be encountered in the format string. These are not referenced anywhere in
// the source anymore, but their values are encoded into the state table, so
// we have retained the definition for reference.
enum class character_type : unsigned
{
other, // character with no special meaning
percent, // '%'
dot, // '.'
star, // '*'
zero, // '0'
digit, // '1'..'9'
flag, // ' ', '+', '-', '#'
size, // 'h', 'l', 'L', 'N', 'F', 'w'
type // type specifying character
};
struct state_transition_pair
{
state next_state;
character_type current_class;
};
template <typename T, size_t Size>
class spectre_mitigated_lookup_table
{
public:
static size_t const mask = Size - 1;
static_assert((Size & mask) == 0, "Size must be a power of two.");
// Instead of using an lfence mitigation, we can fill the table to a power of two,
// then bitwise-and all values used to index into the array.
__declspec(spectre(nomitigation)) T const& operator[](size_t const index) const
{
return m_array[index & mask];
}
T m_array[Size];
};
using printf_state_transition_table = spectre_mitigated_lookup_table<state_transition_pair, 128>;
// Note, the state transition and character type are unrelated data - they just occupy the same table.
// This table skips the 'invalid' state.
extern __declspec(selectany) printf_state_transition_table const standard_lookup_table_spectre
{
/* prev state -> cur char -> new state character type */
/* normal -> other -> */ state::normal, /* ' ' */ character_type::flag,
/* percent -> other -> */ state::normal, /* '!' */ character_type::other,
/* flag -> other -> */ state::normal, /* '"' */ character_type::other,
/* width -> other -> */ state::normal, /* '#' */ character_type::flag,
/* dot -> other -> */ state::normal, /* '$' */ character_type::other,
/* precision -> other -> */ state::normal, /* '%' */ character_type::percent,
/* size -> other -> */ state::normal, /* '&' */ character_type::other,
/* type -> other -> */ state::normal, /* ''' */ character_type::other,
/* normal -> percent -> */ state::percent, /* '(' */ character_type::other,
/* percent -> percent -> */ state::normal, /* ')' */ character_type::other,
/* flag -> percent -> */ state::normal, /* '*' */ character_type::star,
/* width -> percent -> */ state::normal, /* '+' */ character_type::flag,
/* dot -> percent -> */ state::normal, /* ',' */ character_type::other,
/* precision -> percent -> */ state::normal, /* '-' */ character_type::flag,
/* size -> percent -> */ state::normal, /* '.' */ character_type::dot,
/* type -> percent -> */ state::percent, /* '/' */ character_type::other,
/* normal -> dot -> */ state::normal, /* '0' */ character_type::zero,
/* percent -> dot -> */ state::dot, /* '1' */ character_type::digit,
/* flag -> dot -> */ state::dot, /* '2' */ character_type::digit,
/* width -> dot -> */ state::dot, /* '3' */ character_type::digit,
/* dot -> dot -> */ state::normal, /* '4' */ character_type::digit,
/* precision -> dot -> */ state::normal, /* '5' */ character_type::digit,
/* size -> dot -> */ state::normal, /* '6' */ character_type::digit,
/* type -> dot -> */ state::normal, /* '7' */ character_type::digit,
/* normal -> star -> */ state::normal, /* '8' */ character_type::digit,
/* percent -> star -> */ state::width, /* '9' */ character_type::digit,
/* flag -> star -> */ state::width, /* ':' */ character_type::other,
/* width -> star -> */ state::normal, /* ';' */ character_type::other,
/* dot -> star -> */ state::precision, /* '<' */ character_type::other,
/* precision -> star -> */ state::normal, /* '=' */ character_type::other,
/* size -> star -> */ state::normal, /* '>' */ character_type::other,
/* type -> star -> */ state::normal, /* '?' */ character_type::other,
/* normal -> zero -> */ state::normal, /* '@' */ character_type::other,
/* percent -> zero -> */ state::flag, /* 'A' */ character_type::type,
/* flag -> zero -> */ state::flag, /* 'B' */ character_type::other,
/* width -> zero -> */ state::width, /* 'C' */ character_type::type,
/* dot -> zero -> */ state::precision, /* 'D' */ character_type::other,
/* precision -> zero -> */ state::precision, /* 'E' */ character_type::type,
/* size -> zero -> */ state::normal, /* 'F' */ character_type::size,
/* type -> zero -> */ state::normal, /* 'G' */ character_type::type,
/* normal -> digit -> */ state::normal, /* 'H' */ character_type::other,
/* percent -> digit -> */ state::width, /* 'I' */ character_type::size,
/* flag -> digit -> */ state::width, /* 'J' */ character_type::other,
/* width -> digit -> */ state::width, /* 'K' */ character_type::other,
/* dot -> digit -> */ state::precision, /* 'L' */ character_type::size,
/* precision -> digit -> */ state::precision, /* 'M' */ character_type::other,
/* size -> digit -> */ state::normal, /* 'N' */ character_type::size,
/* type -> digit -> */ state::normal, /* 'O' */ character_type::other,
/* normal -> flag -> */ state::normal, /* 'P' */ character_type::other,
/* percent -> flag -> */ state::flag, /* 'Q' */ character_type::other,
/* flag -> flag -> */ state::flag, /* 'R' */ character_type::other,
/* width -> flag -> */ state::normal, /* 'S' */ character_type::type,
/* dot -> flag -> */ state::normal, /* 'T' */ character_type::size,
/* precision -> flag -> */ state::normal, /* 'U' */ character_type::other,
/* size -> flag -> */ state::normal, /* 'V' */ character_type::other,
/* type -> flag -> */ state::normal, /* 'W' */ character_type::other,
/* normal -> size -> */ state::normal, /* 'X' */ character_type::type,
/* percent -> size -> */ state::size, /* 'Y' */ character_type::other,
/* flag -> size -> */ state::size, /* 'Z' */ character_type::type,
/* width -> size -> */ state::size, /* '[' */ character_type::other,
/* dot -> size -> */ state::size, /* '\' */ character_type::other,
/* precision -> size -> */ state::size, /* ']' */ character_type::other,
/* size -> size -> */ state::size, /* '^' */ character_type::other,
/* type -> size -> */ state::normal, /* '_' */ character_type::other,
/* normal -> type -> */ state::normal, /* '`' */ character_type::other,
/* percent -> type -> */ state::type, /* 'a' */ character_type::type,
/* flag -> type -> */ state::type, /* 'b' */ character_type::other,
/* width -> type -> */ state::type, /* 'c' */ character_type::type,
/* dot -> type -> */ state::type, /* 'd' */ character_type::type,
/* precision -> type -> */ state::type, /* 'e' */ character_type::type,
/* size -> type -> */ state::type, /* 'f' */ character_type::type,
/* type -> type -> */ state::normal, /* 'g' */ character_type::type,
/* unused */ state::normal, /* 'h' */ character_type::size,
/* unused */ state::normal, /* 'i' */ character_type::type,
/* unused */ state::normal, /* 'j' */ character_type::size,
/* unused */ state::normal, /* 'k' */ character_type::other,
/* unused */ state::normal, /* 'l' */ character_type::size,
/* unused */ state::normal, /* 'm' */ character_type::other,
/* unused */ state::normal, /* 'n' */ character_type::type,
/* unused */ state::normal, /* 'o' */ character_type::type,
/* unused */ state::normal, /* 'p' */ character_type::type,
/* unused */ state::normal, /* 'q' */ character_type::other,
/* unused */ state::normal, /* 'r' */ character_type::other,
/* unused */ state::normal, /* 's' */ character_type::type,
/* unused */ state::normal, /* 't' */ character_type::size,
/* unused */ state::normal, /* 'u' */ character_type::type,
/* unused */ state::normal, /* 'v' */ character_type::other,
/* unused */ state::normal, /* 'w' */ character_type::size,
/* unused */ state::normal, /* 'x' */ character_type::type,
/* unused */ state::normal, /* 'y' */ character_type::other,
/* unused */ state::normal, /* 'z' */ character_type::size
};
// Note, the state transition and character type are unrelated data - they just occupy the same table.
extern __declspec(selectany) printf_state_transition_table const format_validation_lookup_table_spectre
{
/* prev state -> cur char -> new state character type */
/* normal -> other -> */ state::normal, /* ' ' */ character_type::flag,
/* percent -> other -> */ state::invalid, /* '!' */ character_type::other,
/* flag -> other -> */ state::invalid, /* '"' */ character_type::other,
/* width -> other -> */ state::invalid, /* '#' */ character_type::flag,
/* dot -> other -> */ state::invalid, /* '$' */ character_type::other,
/* precision -> other -> */ state::invalid, /* '%' */ character_type::percent,
/* size -> other -> */ state::invalid, /* '&' */ character_type::other,
/* type -> other -> */ state::normal, /* ''' */ character_type::other,
/* invalid -> other -> */ state::normal, /* '(' */ character_type::other,
/* normal -> percent -> */ state::percent, /* ')' */ character_type::other,
/* percent -> percent -> */ state::normal, /* '*' */ character_type::star,
/* flag -> percent -> */ state::invalid, /* '+' */ character_type::flag,
/* width -> percent -> */ state::invalid, /* ',' */ character_type::other,
/* dot -> percent -> */ state::invalid, /* '-' */ character_type::flag,
/* precision -> percent -> */ state::invalid, /* '.' */ character_type::dot,
/* size -> percent -> */ state::invalid, /* '/' */ character_type::other,
/* type -> percent -> */ state::percent, /* '0' */ character_type::zero,
/* invalid -> percent -> */ state::normal, /* '1' */ character_type::digit,
/* normal -> dot -> */ state::normal, /* '2' */ character_type::digit,
/* percent -> dot -> */ state::dot, /* '3' */ character_type::digit,
/* flag -> dot -> */ state::dot, /* '4' */ character_type::digit,
/* width -> dot -> */ state::dot, /* '5' */ character_type::digit,
/* dot -> dot -> */ state::invalid, /* '6' */ character_type::digit,
/* precision -> dot -> */ state::invalid, /* '7' */ character_type::digit,
/* size -> dot -> */ state::invalid, /* '8' */ character_type::digit,
/* type -> dot -> */ state::normal, /* '9' */ character_type::digit,
/* invalid -> dot -> */ state::normal, /* ':' */ character_type::other,
/* normal -> star -> */ state::normal, /* ';' */ character_type::other,
/* percent -> star -> */ state::width, /* '<' */ character_type::other,
/* flag -> star -> */ state::width, /* '=' */ character_type::other,
/* width -> star -> */ state::invalid, /* '>' */ character_type::other,
/* dot -> star -> */ state::precision, /* '?' */ character_type::other,
/* precision -> star -> */ state::invalid, /* '@' */ character_type::other,
/* size -> star -> */ state::invalid, /* 'A' */ character_type::type,
/* type -> star -> */ state::normal, /* 'B' */ character_type::other,
/* invalid -> star -> */ state::normal, /* 'C' */ character_type::type,
/* normal -> zero -> */ state::normal, /* 'D' */ character_type::other,
/* percent -> zero -> */ state::flag, /* 'E' */ character_type::type,
/* flag -> zero -> */ state::flag, /* 'F' */ character_type::size,
/* width -> zero -> */ state::width, /* 'G' */ character_type::type,
/* dot -> zero -> */ state::precision, /* 'H' */ character_type::other,
/* precision -> zero -> */ state::precision, /* 'I' */ character_type::size,
/* size -> zero -> */ state::invalid, /* 'J' */ character_type::other,
/* type -> zero -> */ state::normal, /* 'K' */ character_type::other,
/* invalid -> zero -> */ state::normal, /* 'L' */ character_type::size,
/* normal -> digit -> */ state::normal, /* 'M' */ character_type::other,
/* percent -> digit -> */ state::width, /* 'N' */ character_type::size,
/* flag -> digit -> */ state::width, /* 'O' */ character_type::other,
/* width -> digit -> */ state::width, /* 'P' */ character_type::other,
/* dot -> digit -> */ state::precision, /* 'Q' */ character_type::other,
/* precision -> digit -> */ state::precision, /* 'R' */ character_type::other,
/* size -> digit -> */ state::invalid, /* 'S' */ character_type::type,
/* type -> digit -> */ state::normal, /* 'T' */ character_type::size,
/* invalid -> digit -> */ state::normal, /* 'U' */ character_type::other,
/* normal -> flag -> */ state::normal, /* 'V' */ character_type::other,
/* percent -> flag -> */ state::flag, /* 'W' */ character_type::other,
/* flag -> flag -> */ state::flag, /* 'X' */ character_type::type,
/* width -> flag -> */ state::invalid, /* 'Y' */ character_type::other,
/* dot -> flag -> */ state::invalid, /* 'Z' */ character_type::type,
/* precision -> flag -> */ state::invalid, /* '[' */ character_type::other,
/* size -> flag -> */ state::invalid, /* '\' */ character_type::other,
/* type -> flag -> */ state::normal, /* ']' */ character_type::other,
/* invalid -> flag -> */ state::normal, /* '^' */ character_type::other,
/* normal -> size -> */ state::normal, /* '_' */ character_type::other,
/* percent -> size -> */ state::size, /* '`' */ character_type::other,
/* flag -> size -> */ state::size, /* 'a' */ character_type::type,
/* width -> size -> */ state::size, /* 'b' */ character_type::other,
/* dot -> size -> */ state::size, /* 'c' */ character_type::type,
/* precision -> size -> */ state::size, /* 'd' */ character_type::type,
/* size -> size -> */ state::size, /* 'e' */ character_type::type,
/* type -> size -> */ state::normal, /* 'f' */ character_type::type,
/* invalid -> size -> */ state::normal, /* 'g' */ character_type::type,
/* normal -> type -> */ state::normal, /* 'h' */ character_type::size,
/* percent -> type -> */ state::type, /* 'i' */ character_type::type,
/* flag -> type -> */ state::type, /* 'j' */ character_type::size,
/* width -> type -> */ state::type, /* 'k' */ character_type::other,
/* dot -> type -> */ state::type, /* 'l' */ character_type::size,
/* precision -> type -> */ state::type, /* 'm' */ character_type::other,
/* size -> type -> */ state::type, /* 'n' */ character_type::other,
/* type -> type -> */ state::normal, /* 'o' */ character_type::type,
/* invalid -> type -> */ state::normal, /* 'p' */ character_type::type,
/* unused */ state::normal, /* 'q' */ character_type::other,
/* unused */ state::normal, /* 'r' */ character_type::other,
/* unused */ state::normal, /* 's' */ character_type::type,
/* unused */ state::normal, /* 't' */ character_type::size,
/* unused */ state::normal, /* 'u' */ character_type::type,
/* unused */ state::normal, /* 'v' */ character_type::other,
/* unused */ state::normal, /* 'w' */ character_type::size,
/* unused */ state::normal, /* 'x' */ character_type::type,
/* unused */ state::normal, /* 'y' */ character_type::other,
/* unused */ state::normal, /* 'z' */ character_type::size
};
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Flags
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
enum FLAG : unsigned
{
FL_SIGN = 0x01, // Put plus or minus in front
FL_SIGNSP = 0x02, // Put space or minus in front
FL_LEFT = 0x04, // Left justify
FL_LEADZERO = 0x08, // Pad with leading zeros
FL_SIGNED = 0x10, // Signed data given
FL_ALTERNATE = 0x20, // Alternate form requested
FL_NEGATIVE = 0x40, // Value is negative
FL_FORCEOCTAL = 0x80, // Force leading '0' for octals
};
enum class length_modifier
{
none,
hh,
h,
l,
ll,
j,
z,
t,
L,
I,
I32,
I64,
w,
T,
enumerator_count
};
inline size_t __cdecl to_integer_size(length_modifier const length) throw()
{
switch (length)
{
case length_modifier::none: return sizeof(int );
case length_modifier::hh: return sizeof(char );
case length_modifier::h: return sizeof(short );
case length_modifier::l: return sizeof(long );
case length_modifier::ll: return sizeof(long long);
case length_modifier::j: return sizeof(intmax_t );
case length_modifier::z: return sizeof(size_t );
case length_modifier::t: return sizeof(ptrdiff_t);
case length_modifier::I: return sizeof(void* );
case length_modifier::I32: return sizeof(int32_t );
case length_modifier::I64: return sizeof(int64_t );
default: return 0;
}
}
template <typename Character>
bool __cdecl is_wide_character_specifier(
uint64_t const options,
Character const format_type,
length_modifier const length
) throw()
{
UNREFERENCED_PARAMETER(options);
// If a length specifier was used, use that width:
switch (length)
{
case length_modifier::l: return true;
case length_modifier::w: return true;
case length_modifier::h: return false;
}
if (length == length_modifier::T)
{
return sizeof(Character) == sizeof(wchar_t);
}
bool const is_naturally_wide{
sizeof(Character) == sizeof(wchar_t) &&
(options & _CRT_INTERNAL_PRINTF_LEGACY_WIDE_SPECIFIERS) != 0
};
bool const is_natural_width{
format_type == 'c' ||
format_type == 's'
};
return is_naturally_wide == is_natural_width;
}
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Core Base Classes
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// The format processor consists of a set of base classes that form a single
// inheritance hierarchy that looks like so:
//
// * common_data
// * output_adapter_data
// * standard_base [1]
// * format_validation_base [2]
// * positional_parameter_base [3]
//
// There is a single class template, output_processor, which may be instantiated
// to derive from any of the three bottom-most classes in the aforementioned
// base class hierarchy (these are marked with the numbers 1-3). When it derives
// from standard_base, it provides the standard formatted output functionality
// as defined in the C Standard Library specification. When it derives from the
// format_validation_base, it provides that same functionality, but also checks
// the format string for validity, and invokes the invalid parameter handler if
// the format string is determined to be invalid. Finally, if it derives from
// the positional_parameter_base, it supports positional parameters.
//
// These three derivable bases allow us to implement the three forms of public
// functions using a common implementation: the standard, unsuffixed functions,
// the format validating _s-suffixed functions, and the _p-suffixed functions
// that permit positional parameter usage.
//
// There are no virtual functions used here: each of the three functionality
// bases "overrides" base class functionality by hiding it. We must therefore
// be cautious to ensure that functions are called in such a way that the right
// implementation is called.
template <typename Character>
class common_data
{
protected:
common_data()
: _options {0 },
_locale {nullptr },
_cached_errno { },
_format_it {nullptr },
_valist_it {nullptr },
_characters_written{0 },
_state {state::normal},
_flags {0 },
_field_width {0 },
_precision {0 },
_suppress_output {false },
_format_char {'\0' },
_string_length {0 },
_string_is_wide {false }
{
}
uint64_t _options;
_locale_t _locale;
// We cache a pointer to errno to avoid having to query thread-local storage
// for every character write that we perform.
__crt_deferred_errno_cache _cached_errno;
// These two iterators control the formatting operation. The format iterator
// iterates over the format string, and the va_list argument pointer iterates
// over the varargs arguments.
Character const* _format_it;
va_list _valist_it;
// This stores the number of characters that have been written so far. It is
// initialized to zero and is incremented as characters are written. If an
// I/O error occurs, it is set to -1 to indicate the I/O failure.
int _characters_written;
// These represent the state for the current format specifier. The suppress
// output flag is set when output should be suppressed for the current format
// specifier (note that this is distinct from the global suppression that we
// use during the first pass of positional parameter handling).
state _state;
unsigned _flags;
int _field_width;
int _precision;
length_modifier _length;
bool _suppress_output;
// This is the character from the format string that was used to compute the
// current state. We need to store this separately because we advance the
// format string iterator at various points during processing.
Character _format_char;
// These pointers are used in various places to point to strings that either
// [1] are being formatted into, or [2] contain formatted data that is ready
// to be printed. At any given time, we may have either a narrow string or
// a wide string, but never both. The string length is the length of which-
// -ever string is currently present. The wide flag is set if the wide string
// is currently in use.
union
{
char* _narrow_string;
wchar_t* _wide_string;
};
char*& tchar_string(char ) throw() { return _narrow_string; }
wchar_t*& tchar_string(wchar_t) throw() { return _wide_string; }
Character*& tchar_string() throw() { return tchar_string(Character()); }
int _string_length;
bool _string_is_wide;
// The formatting buffer. This buffer is used to store the result of various
// formatting operations--notably, numbers get formatted into strings in this
// buffer.
formatting_buffer _buffer;
};
// This data base is split out from the common data base only so that we can
// more easily value-initialize all of the members of the common data base.
template <typename Character, typename OutputAdapter>
class output_adapter_data
: protected common_data<Character>
{
protected:
output_adapter_data(
OutputAdapter const& output_adapter,
uint64_t const options,
Character const* const format,
_locale_t const locale,
va_list const arglist
) throw()
: common_data{},
_output_adapter(output_adapter)
{
// We initialize several base class data members here, so that we can
// value initialize the entire base class before we get to this point.
_options = options;
_locale = locale;
_format_it = format;
_valist_it = arglist;
}
OutputAdapter _output_adapter;
};
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// standard_base
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// This is the base class with which the format processor is instantiated to
// provide "standard" printf formatting.
template <typename Character, typename OutputAdapter>
class standard_base
: protected output_adapter_data<Character, OutputAdapter>
{
protected:
template <typename... Ts>
standard_base(Ts&&... arguments) throw()
: output_adapter_data<Character, OutputAdapter>{arguments... },
_current_pass {pass::not_started}
{
}
bool advance_to_next_pass() throw()
{
_current_pass = static_cast<pass>(static_cast<unsigned>(_current_pass) + 1);
return _current_pass != pass::finished;
}
bool validate_and_update_state_at_end_of_format_string() const throw()
{
// No validation is performed in the standard output implementation:
return true;
}
bool should_format() throw()
{
return true;
}
template <typename RequestedParameterType, typename ActualParameterType>
bool extract_argument_from_va_list(ActualParameterType& result) throw()
{
result = static_cast<ActualParameterType>(read_va_arg<RequestedParameterType>(_valist_it));
return true;
}
bool update_field_width() throw()
{
_field_width = read_va_arg<int>(_valist_it);
return true;
}
bool update_precision() throw()
{
_precision = read_va_arg<int>(_valist_it);
return true;
}
bool validate_state_for_type_case_a() const throw()
{
return true;
}
bool should_skip_normal_state_processing() throw()
{
return false;
}
bool validate_and_update_state_at_beginning_of_format_character() throw()
{
return true;
}
bool should_skip_type_state_output() const throw()
{
return false;
}
static unsigned state_count() throw()
{
return static_cast<unsigned>(state::type) + 1;
}
static printf_state_transition_table const& state_transition_table() throw()
{
return standard_lookup_table_spectre;
}
private:
// In the standard format string processing, there is only one state, in
// which we both evaluate the format specifiers and format the parameters.
enum class pass : unsigned
{
not_started,
output,
finished
};
pass _current_pass;
};
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// format_validation_base
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// This is the base class with which the format processor is instantiated to
// provide "standard" printf formatting with additional validation of the format
// string.
template <typename Character, typename OutputAdapter>
class format_validation_base
: protected standard_base<Character, OutputAdapter>
{
protected:
template <typename... Ts>
format_validation_base(Ts&&... arguments) throw()
: standard_base<Character, OutputAdapter>{arguments...}
{
}
bool validate_and_update_state_at_end_of_format_string() const throw()
{
// When we reach the end of the format string, we ensure that the format
// string is not incomplete. I.e., when we are finished, the lsat thing
// that we should have encountered is a regular character to be written
// or a type specifier. Otherwise, the format string was incomplete.
_VALIDATE_RETURN(_state == state::normal || _state == state::type, EINVAL, false);
return true;
}
static unsigned state_count() throw()
{
return static_cast<unsigned>(state::invalid) + 1;
}
static printf_state_transition_table const& state_transition_table() throw()
{
return format_validation_lookup_table_spectre;
}
};
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// positional_parameter_base
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// This is the base class with which the format processor is instantiated to
// provide support for the formatted output functions that permit positional
// parameter references in the format string. Note that when this base is used,
// it also pulls in the format validation functionality.
template <typename Character, typename OutputAdapter>
class positional_parameter_base
: protected format_validation_base<Character, OutputAdapter>
{
protected:
typedef positional_parameter_base self_type;
typedef format_validation_base base_type;
typedef __crt_char_traits<Character> char_traits;
template <typename... Ts>
positional_parameter_base(Ts&&... arguments) throw()
: format_validation_base<Character, OutputAdapter>{arguments... },
_current_pass {pass::not_started},
_format_mode {mode::unknown },
_format {_format_it },
_type_index {-1 },
_maximum_index {-1 }
{
// Note that we do not zero-initialize the parameter data table until
// the first positional parameter is encountered in the format string.
}
bool advance_to_next_pass() throw()
{
_current_pass = static_cast<pass>(static_cast<unsigned>(_current_pass) + 1);
if (_current_pass == pass::finished)
return false;
// If we are in the output pass but the format string is a non-positional,
// ordinary format string, then we do not need a second pass:
if (_current_pass == pass::output && _format_mode == mode::nonpositional)
return false;
// All characters before the first format specifier are output in the
// first pass. We reset the format mode to 'unknown' to ensure that
// they are not output again during the second pass.
_format_mode = mode::unknown;
_maximum_index = -1;
_type_index = -1;
_field_width = 0;
_precision = 0;
_format_it = _format;
return true;
}
bool validate_and_update_state_at_end_of_format_string() throw()
{
if (!base_type::validate_and_update_state_at_end_of_format_string())
return false;
if (_format_mode != mode::positional || _current_pass != pass::position_scan)
return true;
// At the end of the first pass, we have the types filled into the
// arg_type member for each positional parameter. We now need to get
// the argument pointer for each positional parameter and store it
// into the arg_ptr member.
parameter_data* const first{_parameters};
parameter_data* const last {_parameters + _maximum_index + 1};
for (parameter_data* it{first}; it != last; ++it)
{
it->_valist_it = _valist_it;
switch (it->_actual_type)
{
case parameter_type::int32: read_va_arg<int >(_valist_it); break;
case parameter_type::int64: read_va_arg<__int64 >(_valist_it); break;
case parameter_type::pointer: read_va_arg<void* >(_valist_it); break;
case parameter_type::real64: read_va_arg<_CRT_DOUBLE>(_valist_it); break;
default:
// We should never reach this point:
_VALIDATE_RETURN(("Missing position in the format string", 0), EINVAL, false);
break;
}
}
return true;
}
bool should_format() throw()
{
return _current_pass != pass::position_scan || _format_mode == mode::nonpositional;
}
template <typename RequestedParameterType, typename ActualParameterType>
bool extract_argument_from_va_list(ActualParameterType& result) throw()
{
if (_format_mode == mode::nonpositional)
return base_type::extract_argument_from_va_list<RequestedParameterType>(result);
_VALIDATE_RETURN(_type_index >= 0 && _type_index < _ARGMAX, EINVAL, false);
if (_current_pass == pass::position_scan)
{
return validate_and_store_parameter_data(
_parameters[_type_index],
get_parameter_type(RequestedParameterType()),
_format_char,
_length
);
}
else
{
result = static_cast<ActualParameterType>(peek_va_arg<RequestedParameterType>(_parameters[_type_index]._valist_it));
return true;
}
}
bool update_field_width() throw()
{
if (_format_mode == mode::nonpositional)
return base_type::update_field_width();
Character* end_pointer{nullptr};
int const width_index{char_traits::tcstol(_format_it, &end_pointer, 10) - 1};
_format_it = end_pointer + 1;
if (_current_pass == pass::position_scan)
{
_VALIDATE_RETURN(width_index >= 0 && *end_pointer == '$' && width_index < _ARGMAX, EINVAL, false);
_maximum_index = width_index > _maximum_index
? width_index
: _maximum_index;
return validate_and_store_parameter_data(
_parameters[width_index],
parameter_type::int32,
_format_char,
_length
);
}
else
{
_field_width = peek_va_arg<int>(_parameters[width_index]._valist_it);
}
return true;
}
bool update_precision() throw()
{
if (_format_mode == mode::nonpositional)
return base_type::update_precision();
Character* end_pointer{nullptr};
int const precision_index{char_traits::tcstol(_format_it, &end_pointer, 10) - 1};
_format_it = end_pointer + 1;
if (_current_pass == pass::position_scan)
{
_VALIDATE_RETURN(precision_index >= 0 && *end_pointer == '$' && precision_index < _ARGMAX, EINVAL, false);
_maximum_index = precision_index > _maximum_index
? precision_index
: _maximum_index;
return validate_and_store_parameter_data(
_parameters[precision_index],
parameter_type::int32,
_format_char,
_length
);
}
else
{
_precision = peek_va_arg<int>(_parameters[precision_index]._valist_it);
}
return true;
}
bool validate_state_for_type_case_a() throw()
{
if (_format_mode == mode::positional && _current_pass == pass::position_scan)
{
_VALIDATE_RETURN(_type_index >= 0 && _type_index < _ARGMAX, EINVAL, false);
return validate_and_store_parameter_data(
_parameters[_type_index],
parameter_type::real64,
_format_char,
_length
);
}
return true;
}
bool should_skip_normal_state_processing() throw()
{
if (_current_pass == pass::position_scan && _format_mode == mode::positional)
return true;
if (_current_pass == pass::output && _format_mode == mode::unknown)
return true;
// We do not output during the first pass if we have already come across
// a positional format specifier. All characters before the first format
// specifier are output in the first pass. We need to check the format
// type during the second pass to ensure that they are not output a second
// time.
return false;
}
bool validate_and_update_state_at_beginning_of_format_character() throw()
{
// We're looking for a format specifier, so we'll have just seen a '%'
// and the next character is not a '%':
if (_state != state::percent || *_format_it == '%')
return true;
// When we encounter the first format specifier, we determine whether
// the format string is a positional format string or a standard format
// string.
if (_format_mode == mode::unknown)
{
Character* end_pointer{nullptr};
// The tcstol conversion will eat leading spaces and sign character
// if they are present. Only digits are permitted between the % and
// the $ in the positional format specifier.
if (*_format_it < '0' || *_format_it > '9')
{
_format_mode = mode::nonpositional;
}
else if (char_traits::tcstol(_format_it, &end_pointer, 10) > 0 && *end_pointer == '$')
{
if (_current_pass == pass::position_scan)
{
memset(_parameters, 0, sizeof(_parameters));
}
_format_mode = mode::positional;
}
else
{
_format_mode = mode::nonpositional;
}
}
if (_format_mode != mode::positional)
return true;
Character* end_pointer{nullptr};
_type_index = char_traits::tcstol(_format_it, &end_pointer, 10) - 1;
_format_it = end_pointer + 1;
if (_current_pass != pass::position_scan)
return true;
// We do not re-perform the type validations during the second pass...
_VALIDATE_RETURN(_type_index >= 0 && *end_pointer == '$' && _type_index < _ARGMAX, EINVAL, false);
_maximum_index = _type_index > _maximum_index
? _type_index
: _maximum_index;
return true;
}
bool should_skip_type_state_output() const throw()
{
return _format_mode == mode::positional && _current_pass == pass::position_scan;
}
private:
// Positional parameter processing occurs in two passes. In the first pass,
// we scan the format string to accumulate type information for the
// positional parameters. If the format string is a standard format string
// (that does not use positional parameters), then this pass also does the
// output. Otherwise, if the format string uses positional parameters,
// the actual output operation takes place in the second pass, after we've
// aggregated all of the information about the positional parameters.
enum class pass : unsigned
{
not_started,
position_scan,
output,
finished
};
// These represent the two modes of formatting. The processor starts off in
// the unknown mode and can transition to either nonpositional (indicating
// standard format string processing) or positional mode.
enum class mode : unsigned
{
unknown,
nonpositional,
positional
};
// These represent the different types of parameters that may be present in
// the argument list (remember that integral types narrower than int and
// real types narrower than double are promoted for the varargs call).
enum class parameter_type : unsigned
{
unused,
int32,
int64,
pointer,
real64
};
// This represents a parameter in the varargs list, along with information
// we have about the parameter from the format string. We store an array
// of these structures internally, and the structures are updated in several
// steps. They start off uninitialized, with the "unused" actual type. If
// the format string uses positional parameters, then the actual type, format
// type, and flags are updated during the positional scan of the format
// string. At the end of that scan, we iterate over the parameters and the
// varargs array to fill in the argptr for each of the positional parameters.
struct parameter_data
{
parameter_type _actual_type;
Character _format_type;
va_list _valist_it;
length_modifier _length;
};
// These provide transformations from the source parameter types to their
// underlying representation enumerators (from the parameter_type enum).
template <typename T>
static parameter_type __cdecl get_parameter_type(T* ) throw() { return parameter_type::pointer; }
static parameter_type __cdecl get_parameter_type(short ) throw() { return parameter_type::int32; }
static parameter_type __cdecl get_parameter_type(unsigned short ) throw() { return parameter_type::int32; }
static parameter_type __cdecl get_parameter_type(wchar_t ) throw() { return parameter_type::int32; }
static parameter_type __cdecl get_parameter_type(int ) throw() { return parameter_type::int32; }
static parameter_type __cdecl get_parameter_type(unsigned int ) throw() { return parameter_type::int32; }
static parameter_type __cdecl get_parameter_type(__int64 ) throw() { return parameter_type::int64; }
static parameter_type __cdecl get_parameter_type(unsigned __int64) throw() { return parameter_type::int64; }
static parameter_type __cdecl get_parameter_type(_CRT_DOUBLE ) throw() { return parameter_type::real64; }
// With positional parameters, a given parameter may be used multiple times
// in a format string. All appearances of a given parameter must be
// consistent (they must match in type and width). The first time a given
// parameter appears, it is accepted as-is. Each subsequent time that it
// reappears, this function is called to ensure that the reappearance is
// consistent with the previously processed appearance.
//
// Returns true if the reappearance is consistent (and valid). Returns false
// otherwise. This function does not invoke invalid parameter handling.
bool is_positional_parameter_reappearance_consistent(
parameter_data const& parameter,
parameter_type const actual_type,
Character const format_type,
length_modifier const length
) throw()
{
// Pointer format specifiers are exclusive; a parameter that previously
// appeared as a pointer may not reappear as a non-pointer, and vice-
// versa.
bool const old_is_pointer{is_pointer_specifier(parameter._format_type)};
bool const new_is_pointer{is_pointer_specifier(format_type)};
if (old_is_pointer || new_is_pointer)
{
return old_is_pointer == new_is_pointer;
}
// String format specifiers are exclusive, just like pointer specifiers.
// We must also ensure that the two appearances match in string type:
// either both must be wide or both must be narrow.
bool const old_is_string{is_string_specifier(parameter._format_type)};
bool const new_is_string{is_string_specifier(format_type)};
bool const old_is_character{is_character_specifier(parameter._format_type)};
bool const new_is_character{is_character_specifier(format_type)};
if (old_is_string || new_is_string || old_is_character || new_is_character)
{
if (old_is_string != new_is_string || old_is_character != new_is_character)
return false;
bool const old_is_wide{is_wide_character_specifier(_options, parameter._format_type, parameter._length)};
bool const new_is_wide{is_wide_character_specifier(_options, format_type, length)};
if (old_is_wide != new_is_wide)
return false;
return true;
}
// Numeric specifiers are exclusive: either both appearances must be
// numeric specifiers or neither appearance may be a numeric specifier.
// Additionally, if both appearances are numeric specifiers, they must
// both have the same width.
bool const old_is_integral{is_integral_specifier(parameter._format_type)};
bool const new_is_integral{is_integral_specifier(format_type)};
if (old_is_integral || new_is_integral)
{
if (old_is_integral != new_is_integral)
return false;
if ((parameter._length == length_modifier::I) != (length == length_modifier::I))
return false;
return to_integer_size(parameter._length) == to_integer_size(length);
}
return parameter._actual_type == actual_type;
}
bool validate_and_store_parameter_data(
parameter_data & parameter,
parameter_type const actual_type,
Character const format_type,
length_modifier const length
) throw()
{
if (parameter._actual_type == parameter_type::unused)
{
parameter._actual_type = actual_type;
parameter._format_type = format_type;
parameter._length = length;
}
else
{
_VALIDATE_RETURN(is_positional_parameter_reappearance_consistent(
parameter, actual_type, format_type, length
), EINVAL, false);
}
return true;
}
template <typename Character>
static bool __cdecl is_pointer_specifier(Character const specifier) throw()
{
return specifier == 'p';
}
template <typename Character>
static bool __cdecl is_string_specifier(Character const specifier) throw()
{
return specifier == 's' || specifier == 'S';
}
template <typename Character>
static bool __cdecl is_character_specifier(Character const specifier) throw()
{
return specifier == 'c' || specifier == 'C';
}
template <typename Character>
static bool __cdecl is_integral_specifier(Character const specifier) throw()
{
return specifier == 'd' || specifier == 'i' || specifier == 'o'
|| specifier == 'u' || specifier == 'x' || specifier == 'X'
|| specifier == '*';
}
pass _current_pass;
mode _format_mode;
// The original format pointer, which allows us to reset the format iterator
// between the two passes used in positional parameter processing.
Character const* _format;
// The array of positional parameters used in the format string.
parameter_data _parameters[_ARGMAX];
// These are indices into the _parameters array. They hold the maximum
// positional parameter index that has been seen so far and the index of the
// current format specifier. Until a positional parameter is encountered in
// the format string, these have the value -1.
int _maximum_index;
int _type_index;
};
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// output_processor
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// This is the main output processor class, which performs the format string
// processing and formats the varargs arguments appropriately. It delegates to
// the provided ProcessorBase for certain parts of the functionality. It is
// expected that the ProcessorBase argument is one of the base class types
// defined above.
template <typename Character, typename OutputAdapter, typename ProcessorBase>
class output_processor
: private ProcessorBase
{
public:
typedef __acrt_stdio_char_traits<Character> char_traits;
output_processor(
OutputAdapter const& output_adapter,
uint64_t const options,
Character const* const format,
_locale_t const locale,
va_list const arglist
) throw()
: ProcessorBase{output_adapter, options, format, locale, arglist}
{
}
// After construction, this function is called to evaluate the formatted
// output operation. This function must be called exactly once, immediately
// after constructing the object.
int process() throw()
{
if (!_output_adapter.validate())
return -1;
_VALIDATE_RETURN(_format_it != nullptr, EINVAL, -1);
while (advance_to_next_pass())
{
// At the start of each pass, we have no buffered string and we are
// in the normal state:
_string_length = 0;
_state = state::normal;
// Iterate over the format string until we reach the end, encounter
// an I/O error, or fail due to some other error:
while ((_format_char = *_format_it++) != '\0' && _characters_written >= 0)
{
_state = find_next_state(_format_char, _state);
if (!validate_and_update_state_at_beginning_of_format_character())
return -1;
if (_state == state::invalid)
{
_VALIDATE_RETURN(("Incorrect format specifier", 0), EINVAL, -1);
}
bool result = false;
switch (_state)
{
case state::normal: result = state_case_normal (); break;
case state::percent: result = state_case_percent (); break;
case state::flag: result = state_case_flag (); break;
case state::width: result = state_case_width (); break;
case state::dot: result = state_case_dot (); break;
case state::precision: result = state_case_precision(); break;
case state::size: result = state_case_size (); break;
case state::type: result = state_case_type (); break;
}
// If the state-specific operation failed, return immediately.
// The individual state cases are responsible for invoking the
// invalid parameter handler if the failure is due to an invalid
// parameter.
if (!result)
return -1;
}
if (!validate_and_update_state_at_end_of_format_string())
return -1;
}
return _characters_written;
}
private:
// The normal state is entered when a character that is not part of a format
// specifier is encountered in the format string. We simply write the
// character. There are four parts to the normal state: the state_case_normal
// function is called when the state is entered. It tests whether the actual
// output operation should take place. The state_case_normal_common function
// performs the actual output operation and is called from elsewhere in this
// class.
bool state_case_normal() throw()
{
if (should_skip_normal_state_processing())
return true;
_VALIDATE_RETURN(state_case_normal_common(), EINVAL, false);
return true;
}
bool state_case_normal_common() throw()
{
if (!state_case_normal_tchar(Character()))
return false;
_output_adapter.write_character(_format_char, &_characters_written);
return true;
}
bool state_case_normal_tchar(char) throw()
{
_string_is_wide = false;
if (__acrt_isleadbyte_l_noupdate(_format_char, _locale))
{
_output_adapter.write_character(_format_char, &_characters_written);
_format_char = *_format_it++;
// Ensure that we do not fall off the end of the format string:
_VALIDATE_RETURN(_format_char != '\0', EINVAL, false);
}
return true;
}
bool state_case_normal_tchar(wchar_t) throw()
{
_string_is_wide = true;
return true;
}
// We enter the percent state when we read a '%' from the format string. The
// percent sign begins a format specifier, so we reset our internal state for
// the new format specifier.
bool state_case_percent() throw()
{
_field_width = 0;
_suppress_output = false;
_flags = 0;
_precision = -1;
_length = length_modifier::none;
_string_is_wide = false;
return true;
}
// We enter the flag state when we are reading a format specifier and we
// encounter one of the optional flags. We update our state to account for
// the flag.
bool state_case_flag() throw()
{
// Set the flag based on which flag character:
switch (_format_char)
{
case '-': set_flag(FL_LEFT ); break; // '-' => left justify
case '+': set_flag(FL_SIGN ); break; // '+' => force sign indicator
case ' ': set_flag(FL_SIGNSP ); break; // ' ' => force sign or space
case '#': set_flag(FL_ALTERNATE); break; // '#' => alternate form
case '0': set_flag(FL_LEADZERO ); break; // '0' => pad with leading zeros
}
return true;
}
// Parses an integer from the format string. It is expected in this function
// that it is called _after_ the format string iterator has been advanced to
// the next character, so it starts parsing from _format_it - 1 (i.e., from
// the character currently being processed, a copy of which is stored in
// the _format_char data member).
bool parse_int_from_format_string(int* const result) throw()
{
__crt_errno_guard const reset_errno{&_cached_errno.get()};
Character* end{};
*result = static_cast<int>(char_traits::tcstol(_format_it - 1, &end, 10));
if (_cached_errno.get() == ERANGE)
return false;
if (end < _format_it)
return false;
_format_it = end;
return true;
}
// We enter the width state when we are reading a format specifier and we
// encounter either an asterisk (indicating the width should be read from
// the varargs) or a digit (indicating that we are in the process of reading
// the width from the format string.
bool state_case_width() throw()
{
if (_format_char != '*')
{
return parse_int_from_format_string(&_field_width);
}
// If the format character is an asterisk, we read the width from the
// varargs. If we read a negative value, we treat it as the '-' flag
// followed by a positive width (per the C Standard Library spec).
if (!update_field_width())
return false;
if (!should_format())
return true;
if (_field_width < 0)
{
set_flag(FL_LEFT);
_field_width = -_field_width;
}
return true;
}
// We enter the dot state when we read a '.' from the format string. This
// '.' introduces the precision part of the format specifier.
bool state_case_dot() throw()
{
// Reset the precision to zero. If the dot is not followed by a number,
// it means a precision of zero, not the default precision (per the C
// Standard Library specification). (Note: We represent the default
// precision with -1.)
_precision = 0;
return true;
}
// We enter the precision state after we read a ',' from the format string.
// At this point, we read the precision, in a manner similar to how we read
// the width.
bool state_case_precision() throw()
{
if (_format_char != '*')
{
return parse_int_from_format_string(&_precision);
}
// If the format character is an asterisk, we read the width from the
// varargs. If we read a negative value, we treat it as indicating the
// default precision.
if (!update_precision())
return false;
if (!should_format())
return true;
if (_precision < 0)
_precision = -1;
return true;
}
// We enter the size state when we have read one of the size characters from
// the format string.
bool state_case_size() throw()
{
if (_format_char == 'F')
{
// We hand the 'F' character as a length modifier because the length
// modifier occurs before the type specifier. If we find an 'F' and
// we are in the legacy compatibility mode that supports the 'F' length
// modifier, we just ignore it (it has no meaning). Otherwise we are
// not in compatibility mode so we switch out to the type case to handle
// the 'F' as a %F format specifier:
if ((_options & _CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY) == 0)
{
_state = state::type;
return state_case_type();
}
return true;
}
if (_format_char == 'N')
{
// If we find an 'N' and we are in the legacy compatibility mode that
// supports the 'N' length modifier, we just ignore it (it has no
// meaning). Otherwise, we are not in compatibility mode, so we
// invoke the invalid parameter handler and return failure.
if ((_options & _CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY) == 0)
{
_state = state::invalid;
#pragma warning(suppress: __WARNING_IGNOREDBYCOMMA) // 6319 comma operator
_VALIDATE_RETURN(("N length modifier not specifier", false), EINVAL, false);
return false;
}
return true;
}
_VALIDATE_RETURN(_length == length_modifier::none, EINVAL, false);
// We just read a size specifier; set the flags based on it:
switch (_format_char)
{
case 'h':
{
if (*_format_it == 'h')
{
++_format_it;
_length = length_modifier::hh;
}
else
{
_length = length_modifier::h;
}
return true;
}
case 'I':
{
// The I32, I64, and I length modifiers are Microsoft extensions.
if (*_format_it == '3' && *(_format_it + 1) == '2')
{
_format_it += 2;
_length = length_modifier::I32;
}
else if (*_format_it == '6' && *(_format_it + 1) == '4')
{
_format_it += 2;
_length = length_modifier::I64;
}
else if (*_format_it == 'd' ||
*_format_it == 'i' ||
*_format_it == 'o' ||
*_format_it == 'u' ||
*_format_it == 'x' ||
*_format_it == 'X')
{
// If we support positional parameters, then %I without a following
// 32 or 64 is platform-dependent:
_length = length_modifier::I;
}
return true;
}
case 'l':
{
if (*_format_it == 'l')
{
++_format_it;
_length = length_modifier::ll;
}
else
{
_length = length_modifier::l;
}
return true;
}
case 'L':
{
_length = length_modifier::L;
return true;
}
case 'j':
{
_length = length_modifier::j;
return true;
}
case 't':
{
_length = length_modifier::t;
return true;
}
case 'z':
{
_length = length_modifier::z;
return true;
}
case 'w':
{
_length = length_modifier::w;
return true;
}
case 'T':
{
_length = length_modifier::T;
return true;
}
}
return true;
}
// We enter the type case when we read the type part of the format specifier.
// At this point, we have read the entire format specifier and we can extract
// the value to be formatted from the varargs, format it, then output it.
// This state is broken up into subfunctions, one per type category.
bool state_case_type() throw()
{
// Each of the subfunctions is responsible for [1] extracting the next
// argument from the varargs, [2] formatting that argument into the
// internal buffer, and [3] updating the string state variables with
// the correct data for the format operation (either _narrow_string or
// _wide_string must be set correctly, the _string_length must be set
// correctly, and _string_is_wide must be true if the wide string
// should be used.
bool result{false};
switch (_format_char)
{
// Individual character output:
case 'C':
case 'c': result = type_case_c(); break;
// String output:
case 'Z': result = type_case_Z(); break;
case 'S':
case 's': result = type_case_s(); break;
// Floating-point output:
case 'A':
case 'E':
case 'F':
case 'G':
case 'a':
case 'e':
case 'f':
case 'g': result = type_case_a(); break;
// Integer and pointer output:
case 'd':
case 'i': result = type_case_d(); break;
case 'u': result = type_case_u(); break;
case 'o': result = type_case_o(); break;
case 'X': result = type_case_X(); break;
case 'x': result = type_case_x(); break;
case 'p': result = type_case_p(); break;
// State reporting (no output):
case 'n': result = type_case_n(); break;
}
// If the case-specific logic failed, return immediately. The case-
// specific function is responsible for invoking the invalid parameter
// handler if it needs to do so.
if (!result)
return false;
// Check to see whether the output operation should be skipped (we skip
// the output part e.g. during the positional scan pass when positional
// formatting is used).
if (should_skip_type_state_output())
return true;
// At this point, we've completed the bulk of the formatting operation
// and the string is ready to be printed. We now justify the string,
// pre-pend any required prefix and leading zeroes, then print it. Well,
// unless output is suppressed, that is... :-)
if (_suppress_output)
return true;
// Compute the prefix, if one is required...
Character prefix[3]{};
size_t prefix_length{0};
if (has_flag(FL_SIGNED))
{
if (has_flag(FL_NEGATIVE))
{
prefix[prefix_length++] = '-';
}
else if (has_flag(FL_SIGN))
{
prefix[prefix_length++] = '+';
}
else if (has_flag(FL_SIGNSP))
{
prefix[prefix_length++] = ' ';
}
}
bool const print_integer_0x{(_format_char == 'x' || _format_char == 'X') && has_flag(FL_ALTERNATE)};
bool const print_floating_point_0x{_format_char == 'a' || _format_char == 'A'};
if (print_integer_0x || print_floating_point_0x)
{
prefix[prefix_length++] = '0';
prefix[prefix_length++] = adjust_hexit('x' - 'a' + '9' + 1, _format_char == 'X' || _format_char == 'A');
}
// Compute the amount of padding required to get to the desired field
// width, then output the left padding, prefix, leading zeroes, the
// string, and right padding, in that order.
int const padding = static_cast<int>(_field_width - _string_length - prefix_length);
if (!has_flag(FL_LEFT | FL_LEADZERO))
{
// Left-pad with spaces
write_multiple_characters(_output_adapter, ' ', padding, &_characters_written);
}
// Write the prefix
_output_adapter.write_string(prefix, static_cast<int>(prefix_length), &_characters_written, _cached_errno);
if (has_flag(FL_LEADZERO) && !has_flag(FL_LEFT))
{
// Write leading zeroes
write_multiple_characters(_output_adapter, '0', padding, &_characters_written);
}
// Write the string
write_stored_string_tchar(Character());
if (_characters_written >= 0 && has_flag(FL_LEFT))
{
// Right-pad with spaces
write_multiple_characters(_output_adapter, ' ', padding, &_characters_written);
}
return true;
}
// Individual character output: The 'C' and 'c' format specifiers output an
// individual wide or narrow character, respectively. They both delegate to
// the type_case_c_tchar() overloads, which handle the output appropriately
// for the character type of the output adapter.
bool type_case_c() throw()
{
return type_case_c_tchar(Character());
}
bool type_case_c_tchar(char) throw()
{
// If the character is a wide character, we translate it to multibyte
// to be output, storing the multibyte string in the internal buffer:
if (is_wide_character_specifier(_options, _format_char, _length))
{
wchar_t wide_character{};
if (!extract_argument_from_va_list<wchar_t>(wide_character))
return false;
if (!should_format())
return true;
// Convert to multibyte. If the conversion fails, we suppress the
// output operation but we do not fail the entire operation:
errno_t const status{_wctomb_s_l(&_string_length, _buffer.data<char>(), _buffer.count<char>(), wide_character, _locale)};
if (status != 0)
_suppress_output = true;
}
// If the character is a narrow character, we can just write it directly
// to the output, as-is.
else
{
if (!extract_argument_from_va_list<unsigned short>(_buffer.data<char>()[0]))
return false;
if (!should_format())
return true;
_string_length = 1;
}
_narrow_string = _buffer.data<char>();
return true;
}
bool type_case_c_tchar(wchar_t) throw()
{
// If the output adapter accepts wide characters, then we must transform
// the character into a wide character to be output.
_string_is_wide = true;
wchar_t wide_character{};
if (!extract_argument_from_va_list<wchar_t>(wide_character))
return false;
if (!should_format())
return true;
if (!is_wide_character_specifier(_options, _format_char, _length))
{
// If the character is actually a multibyte character, then we must
// transform it into the equivalent wide character. If the translation
// is unsuccessful, we ignore this character but do not fail the entire
// output operation.
char const local_buffer[2]{ static_cast<char>(wide_character & 0x00ff), '\0' };
if (_mbtowc_l(_buffer.data<wchar_t>(), local_buffer, _locale->locinfo->_public._locale_mb_cur_max, _locale) < 0)
_suppress_output = true;
}
else
{
_buffer.data<wchar_t>()[0] = wide_character;
}
_wide_string = _buffer.data<wchar_t>();
_string_length = 1;
return true;
}
// String Output: The Z, S, and s format specifiers output a string. The Z
// format specifier is an extension, used to print a Windows SDK ANSI_STRING
// or UNICODE_STRING counted string. The S and s format specifiers output a
// wide or narrow C string, respectively. If a null pointer is passed, we
// replace it with a special sentinel string, with the contents "(null)".
bool type_case_Z() throw()
{
// This matches the representation of the Windows SDK types ANSI_STRING
// and UNICODE_STRING, which represent a counted string.
struct ansi_string
{
unsigned short _length;
unsigned short _maximum_length;
char* _buffer;
};
ansi_string* string{};
if (!extract_argument_from_va_list<ansi_string*>(string))
return false;
if (!should_format())
return true;
if (!string || string->_buffer == nullptr)
{
_narrow_string = narrow_null_string();
_string_length = static_cast<int>(strlen(_narrow_string));
_string_is_wide = false;
}
else if (is_wide_character_specifier(_options, _format_char, _length))
{
_wide_string = reinterpret_cast<wchar_t*>(string->_buffer);
_string_length = string->_length / static_cast<int>(sizeof(wchar_t));
_string_is_wide = true;
}
else
{
_narrow_string = string->_buffer;
_string_length = string->_length;
_string_is_wide = false;
}
return true;
}
bool type_case_s() throw()
{
// If this format specifier has the default precision, then the entire
// string is output. If a precision is given, then we output the minimum
// of the length of the C string and the given precision. Note that the
// string needs not be null-terminated if a precision is given, so we
// cannot call strlen to compute the length of the string.
if (!extract_argument_from_va_list<char*>(_narrow_string))
return false;
if (!should_format())
return true;
int const maximum_length{(_precision == -1) ? INT_MAX : _precision};
if (is_wide_character_specifier(_options, _format_char, _length))
{
if (!_wide_string)
_wide_string = wide_null_string();
_string_is_wide = true;
_string_length = static_cast<int>(wcsnlen(_wide_string, maximum_length));
}
else
{
if (!_narrow_string)
_narrow_string = narrow_null_string();
_string_length = type_case_s_compute_narrow_string_length(maximum_length, Character());
}
return true;
}
// We have two different implementations of the 's' type case, to handle
// narrow and wide strings, since computation of the length is subtly
// different depending on whether we are outputting narrow or wide
// characters. These functions just update the string state appropriately
// for the string that has just been read from the varargs.
int type_case_s_compute_narrow_string_length(int const maximum_length, char) const throw()
{
return static_cast<int>(strnlen(_narrow_string, maximum_length));
}
int type_case_s_compute_narrow_string_length(int const maximum_length, wchar_t) const throw()
{
int string_length{0};
for (char const* p{_narrow_string}; string_length < maximum_length && *p; ++string_length)
{
if (__acrt_isleadbyte_l_noupdate(static_cast<unsigned char>(*p), _locale))
{
++p;
}
++p;
}
return string_length;
}
// Floating-point output: The A case handles the A, E, F, and G format specifiers.
// The a case handles the a, e, f, and g format specifiers. A capital format
// specifier causes us to output capital hexits, whereas a lowercase format
// specifier causes us to output lowercase hexits.
bool type_case_a() throw()
{
// The double type is signed:
set_flag(FL_SIGNED);
if (!validate_state_for_type_case_a())
return false;
if (!should_format())
return true;
// First, we need to compute the actual precision to use, limited by
// both the maximum precision and the size of the buffer that we can
// allocate.
if (_precision < 0)
{
// The default precision depends on the format specifier used. For
// %e, %f, and %g, C specifies that the default precision is 6. For
// %a, C specifies that "if the precision is missing and FLT_RADIX
// is a power of 2, then the precision is sufficient for an exact
// representation of the value" (C11 7.21.6.1/8).
//
// The 64-bit double has 53 bits of precision. When printing in
// hexadecimal form, we print one bit of precision to the left of the
// radix point and the remaining 52 bits of precision to the right.
// Thus, the default precision is 13 (13 * 4 == 52).
if (_format_char == 'a' || _format_char == 'A')
{
_precision = 13;
}
else
{
_precision = 6;
}
}
else if (_precision == 0 && (_format_char == 'g' || _format_char == 'G'))
{
_precision = 1; // Per C Standard Library specification.
}
if (!_buffer.ensure_buffer_is_big_enough<char>(_CVTBUFSIZE + _precision))
{
// If we fail to enlarge the buffer, cap precision so that the
// statically-sized buffer may be used for the formatting:
_precision = static_cast<int>(_buffer.count<char>() - _CVTBUFSIZE);
}
_narrow_string = _buffer.data<char>();
// Note that we separately handle the FORMAT_POSSCAN_PASS above.
_CRT_DOUBLE tmp{};
if (!extract_argument_from_va_list<_CRT_DOUBLE>(tmp))
return false;
// Format the number into the buffer:
__acrt_fp_format(
&tmp.x,
_buffer.data<char>(),
_buffer.count<char>(),
_buffer.scratch_data<char>(),
_buffer.scratch_count<char>(),
static_cast<char>(_format_char),
_precision,
_options,
_locale,
__acrt_rounding_mode::standard);
// If the precision is zero but the '#' flag is part of the specifier,
// we force a decimal point:
if (has_flag(FL_ALTERNATE) && _precision == 0)
{
force_decimal_point(_narrow_string, _locale);
}
// The 'g' format specifier indicates that zeroes should be cropped
// unless the '#' flag is part of the specifier.
if ((_format_char == 'g' || _format_char == 'G') && !has_flag(FL_ALTERNATE))
{
crop_zeroes(_narrow_string, _locale);
}
// If the result was negative, we save the '-' for later and advance past
// the negative sign (we handle the '-' separately, in code shared with
// the integer formatting, to correctly handle flags).
if (*_narrow_string == '-')
{
set_flag(FL_NEGATIVE);
++_narrow_string;
}
// If the result was a special infinity or a nan string, suppress output
// of the "0x" prefix by treating the special string as just a string:
if (*_narrow_string == 'i' || *_narrow_string == 'I' ||
*_narrow_string == 'n' || *_narrow_string == 'N')
{
unset_flag(FL_LEADZERO); // padded with spaces, not zeros.
_format_char = 's';
}
_string_length = static_cast<int>(strlen(_narrow_string));
return true;
}
// Integer output: These functions handle the formatting of integer values.
// There are a number of format specifiers that handle integer formatting;
// we handle them separately, but they all end up calling the common function
// type_case_integer(), defined last.
bool type_case_d() throw()
{
set_flag(FL_SIGNED);
return type_case_integer(10);
}
bool type_case_u() throw()
{
return type_case_integer(10);
}
bool type_case_o() throw()
{
// If the alternate flag is set, we force a leading 0:
if (has_flag(FL_ALTERNATE))
set_flag(FL_FORCEOCTAL);
return type_case_integer(8);
}
bool type_case_X() throw()
{
return type_case_integer(16, true);
}
bool type_case_x() throw()
{
return type_case_integer(16);
}
// The 'p' format specifier writes a pointer, which is simply treated as a
// hexadecimal integer with lowercase hexits.
bool type_case_p() throw()
{
// We force the precision to be 2 * sizeof(void*), which is the number
// of hexits required to represent the pointer, so that it is zero-
// padded.
_precision = 2 * sizeof(void*);
// Ensure that we read a 32-bit integer on 32-bit architectures, and
// a 64-bit integer on 64-bit platforms:
_length = sizeof(void*) == 4
? length_modifier::I32
: length_modifier::I64;
return type_case_integer(16, true);
}
// This is the first half of the common integer formatting routine. It
// extracts the integer of the specified type from the varargs and does
// pre-processing common to all integer processing.
bool type_case_integer(unsigned const radix, bool const capital_hexits = false) throw()
{
size_t const integer_size = to_integer_size(_length);
// First, extract the argument of the required type from the varargs:
__int64 original_number {};
bool extraction_result{};
switch (integer_size)
{
case sizeof(int8_t):
extraction_result = has_flag(FL_SIGNED)
? extract_argument_from_va_list<int8_t >(original_number)
: extract_argument_from_va_list<uint8_t>(original_number);
break;
case sizeof(int16_t):
extraction_result = has_flag(FL_SIGNED)
? extract_argument_from_va_list<int16_t >(original_number)
: extract_argument_from_va_list<uint16_t>(original_number);
break;
case sizeof(int32_t):
extraction_result = has_flag(FL_SIGNED)
? extract_argument_from_va_list<int32_t >(original_number)
: extract_argument_from_va_list<uint32_t>(original_number);
break;
case sizeof(int64_t):
extraction_result = has_flag(FL_SIGNED)
? extract_argument_from_va_list<int64_t >(original_number)
: extract_argument_from_va_list<uint64_t>(original_number);
break;
default:
_VALIDATE_RETURN(("Invalid integer length modifier", 0), EINVAL, false);
break;
}
if (!extraction_result)
return false;
// If we're not formatting, then we're done; we just needed to read the
// argument from the varargs.
if (!should_format())
return true;
// Check the sign of the number. If it is negative, convert it to
// positive for formatting. We'll handle the minus sign later (after
// we return from this function).
unsigned __int64 number{};
if (has_flag(FL_SIGNED) && original_number < 0)
{
number = static_cast<unsigned __int64>(-original_number);
set_flag(FL_NEGATIVE);
}
else
{
number = static_cast<unsigned __int64>(original_number);
}
// Check the precision to see if the default precision was specified. If
// a non-default precision was specified, we turn off the zero flag, per
// the C Standard Library specification.
if (_precision < 0)
{
_precision = 1; // Default precision
}
else
{
unset_flag(FL_LEADZERO);
_buffer.ensure_buffer_is_big_enough<Character>(_precision);
}
// If the number is zero, we do not want to print the hex prefix ("0x"),
// even if it was requested:
if (number == 0)
{
unset_flag(FL_ALTERNATE);
}
_string_is_wide = sizeof(Character) == sizeof(wchar_t);
if (integer_size == sizeof(int64_t))
{
type_case_integer_parse_into_buffer<uint64_t>(number, radix, capital_hexits);
}
else
{
type_case_integer_parse_into_buffer<uint32_t>(static_cast<uint32_t>(number), radix, capital_hexits);
}
// If the FORCEOCTAL flag is set, then we output a leading zero, unless
// the formatted string already has a leading zero:
if (has_flag(FL_FORCEOCTAL) && (_string_length == 0 || tchar_string()[0] != '0'))
{
*--tchar_string() = '0';
++_string_length;
}
return true;
}
// This is the second half of the common integer formatting routine. It
// handles the actual formatting of the number. This logic has been split
// out from the first part so that we only use 64-bit arithmetic when
// absolutely required (on x86, 64-bit division is Slow-with-a-capital-S).
template <typename UnsignedInteger>
void type_case_integer_parse_into_buffer(
UnsignedInteger number,
unsigned const radix,
bool const capital_hexits
) throw()
{
// Format the number into the formatting buffer. Note that we format the
// buffer at the end of the formatting buffer, which allows us to perform
// the formatting from least to greatest magnitude, which maps well to
// the math.
Character* const last_digit{_buffer.data<Character>() + _buffer.count<Character>() - 1};
Character*& string_pointer = tchar_string();
string_pointer = last_digit;
while (_precision > 0 || number != 0)
{
--_precision;
Character digit{static_cast<Character>(number % radix + '0')};
number /= radix;
// If the digit is greater than 9, we need to convert it to the
// corresponding letter hexit in the required case:
if (digit > '9')
{
digit = adjust_hexit(digit, capital_hexits);
}
*string_pointer-- = static_cast<char>(digit);
}
_string_length = static_cast<int>(last_digit - string_pointer);
++string_pointer;
}
// The 'n' type specifier is special: We read a short* or int* from the
// varargs and write the number of characters that we've written so far to
// the pointed-to integer.
bool type_case_n() throw()
{
void* p{nullptr};
if (!extract_argument_from_va_list<void*>(p))
return false;
if (!should_format())
return true;
if (!_get_printf_count_output())
{
_VALIDATE_RETURN(("'n' format specifier disabled", 0), EINVAL, false);
return false; // Unreachable
}
switch (to_integer_size(_length))
{
case sizeof(int8_t): *static_cast<int8_t *>(p) = static_cast<int8_t >(_characters_written); break;
case sizeof(int16_t): *static_cast<int16_t*>(p) = static_cast<int16_t>(_characters_written); break;
case sizeof(int32_t): *static_cast<int32_t*>(p) = static_cast<int32_t>(_characters_written); break;
case sizeof(int64_t): *static_cast<int64_t*>(p) = static_cast<int64_t>(_characters_written); break;
default: _VALIDATE_RETURN(("Invalid integer length modifier", 0), EINVAL, false); break;
}
// This format specifier never corresponds to an output operation:
_suppress_output = true;
return true;
}
// After we have completed the formatting of the string to be output, we
// perform the output operation, which is handled by these two functions.
bool write_stored_string_tchar(char) throw()
{
if (_string_is_wide && _string_length > 0)
{
wchar_t* p{_wide_string};
for (int i{0}; i != _string_length; ++i)
{
char local_buffer[MB_LEN_MAX + 1];
int mbc_length{0};
errno_t const status{_wctomb_s_l(&mbc_length, local_buffer, _countof(local_buffer), *p++, _locale)};
if (status != 0 || mbc_length == 0)
{
_characters_written = -1;
return true;
}
_output_adapter.write_string(local_buffer, mbc_length, &_characters_written, _cached_errno);
}
}
else
{
_output_adapter.write_string(_narrow_string, _string_length, &_characters_written, _cached_errno);
}
return true;
}
bool write_stored_string_tchar(wchar_t) throw()
{
if (!_string_is_wide && _string_length > 0)
{
char* p{_narrow_string};
for (int i{0}; i != _string_length; ++i)
{
wchar_t wide_character{};
int const mbc_length{_mbtowc_l(&wide_character, p, _locale->locinfo->_public._locale_mb_cur_max, _locale)};
if (mbc_length <= 0)
{
_characters_written = -1;
return true;
}
_output_adapter.write_character(wide_character, &_characters_written);
p += mbc_length;
}
}
else
{
_output_adapter.write_string(_wide_string, _string_length, &_characters_written, _cached_errno);
}
return true;
}
// These functions are utilities for working with the flags, and performing
// state transitions.
bool has_flag (unsigned const f) const throw() { return (_flags & f) != 0; }
void set_flag (unsigned const f) throw() { _flags |= f; }
void unset_flag(unsigned const f) throw() { _flags &= ~f; }
state find_next_state(Character const c, state const previous_state) const throw()
{
auto const& lookup_table = state_transition_table();
unsigned const current_class = static_cast<unsigned>((c < ' ' || c > 'z')
? character_type::other
: static_cast<character_type>(lookup_table[c - ' '].current_class));
auto const index = current_class * state_count() + static_cast<unsigned>(previous_state);
return static_cast<state>(lookup_table[index].next_state);
}
// Adjusts an out-of-range hexit character to be a lowercase or capital
// letter. This function is called when formatting a number as hexadecimal
// and a hexit is above 9. The normal formatting will not correctly handle
// this case. Here, we adjust it by the required offset to yield a letter,
// in the range [A, F], as either a lowercase or capital letter.
static char __cdecl adjust_hexit(int const value, bool const capitalize) throw()
{
int const base {capitalize ? 'A' : 'a'};
int const offset{base - '9' - 1 };
return static_cast<char>(offset + value);
}
// When a null pointer is passed, we print this string as a placeholder.
static char * __cdecl narrow_null_string() throw() { return "(null)"; }
static wchar_t* __cdecl wide_null_string () throw() { return L"(null)"; }
};
} // namespace __crt_stdio_output
// Reset to build-defined optimizations.
#pragma optimize("", on)