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



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>
class console_output_adapter
{
public:

    typedef __acrt_stdio_char_traits<Character> char_traits;

    bool validate() const throw()
    {
        return true;
    }

    void write_character(Character const c, int* const count_written) const throw()
    {
        if (char_traits::puttch_nolock(c) == char_traits::eof)
        {
            *count_written = -1;
        }
        else
        {
            ++*count_written;
        }
    }

    void write_string(
        Character const* const string,
        int              const length,
        int*             const count_written,
        errno_t*         const status
        ) const throw()
    {
        __crt_errno_guard const reset_errno{status};

        Character const* const string_last{string + length};
        for (Character const* it{string}; it != string_last; ++it)
        {
            // CRT_REFACTOR TODO This loses the value of count_written when a
            // conversion error occurs.
            write_character(*it, count_written);
            if (*count_written == -1)
            {
                // Only EILSEQ errors are recoverable here:
                if (*status != EILSEQ)
                    break;

                write_character('?', count_written);
            }
        }
    }
};



template <typename Character>
class stream_output_adapter
{
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());
    }

    void write_character(Character const c, int* const count_written) const throw()
    {
        if (_stream.is_string_backed() && _stream->_base == nullptr)
        {
            ++*count_written;
            return;
        }
   
        if (char_traits::puttc_nolock(c, _stream.public_stream()) == char_traits::eof)
        {
            *count_written = -1;
        }
        else
        {
            ++*count_written;
        }
    }

    void write_string(
        Character const* const string,
        int              const length,
        int*             const count_written,
        errno_t*         const status
        ) const throw()
    {
        if (_stream.is_string_backed() && _stream->_base == nullptr)
        {
            *count_written += length;
            return;
        }

        __crt_errno_guard const reset_errno{status};

        Character const* const string_last{string + length};
        for (Character const* it{string}; it != string_last; ++it)
        {
            write_character(*it, count_written);
            if (*count_written == -1)
            {
                // Only EILSEQ errors are recoverable here:
                if (*status != EILSEQ)
                    return;

                write_character('?', count_written);
            }
        }
    }

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,
        errno_t*         const status
        ) const throw()
    {
        __crt_errno_guard const reset_errno(status);

        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,
        maximum_precision  =  512
    };

    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()
    {
        _VALIDATE_RETURN_NOEXC(SIZE_MAX / count / 2 >= sizeof(T), 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(*buffer) != 'e')
    {
        do 
        {
            ++buffer;
        }
        while (isdigit(static_cast<unsigned char>(*buffer)));
    }

    // Check if the buffer is in hexadecimal format (cfr %a or %A and fp_format_a):
    if (tolower(*buffer) == '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
};

extern "C" extern __declspec(selectany) unsigned char const standard_lookup_table[]
{
    /* ' ' */  0x06,
    /* '!' */  0x00,
    /* '"' */  0x00,
    /* '#' */  0x06,
    /* '$' */  0x00,
    /* '%' */  0x01,
    /* '&' */  0x00,
    /* ''' */  0x00,
    /* '(' */  0x10,
    /* ')' */  0x00,
    /* '*' */  0x03,
    /* '+' */  0x06,
    /* ',' */  0x00,
    /* '-' */  0x06,
    /* '.' */  0x02,
    /* '/' */  0x10,
    /* '0' */  0x04,
    /* '1' */  0x45,
    /* '2' */  0x45,
    /* '3' */  0x45,
    /* '4' */  0x05,
    /* '5' */  0x05,
    /* '6' */  0x05,
    /* '7' */  0x05,
    /* '8' */  0x05,
    /* '9' */  0x35,
    /* ':' */  0x30,
    /* ';' */  0x00,
    /* '<' */  0x50,
    /* '=' */  0x00,
    /* '>' */  0x00,
    /* '?' */  0x00,
    /* '@' */  0x00,
    /* 'A' */  0x28,
    /* 'B' */  0x20,
    /* 'C' */  0x38,
    /* 'D' */  0x50,
    /* 'E' */  0x58,
    /* 'F' */  0x07,
    /* 'G' */  0x08,
    /* 'H' */  0x00,
    /* 'I' */  0x37,
    /* 'J' */  0x30,
    /* 'K' */  0x30,
    /* 'L' */  0x57,
    /* 'M' */  0x50,
    /* 'N' */  0x07,
    /* 'O' */  0x00,
    /* 'P' */  0x00,
    /* 'Q' */  0x20,
    /* 'R' */  0x20,
    /* 'S' */  0x08,
    /* 'T' */  0x07,
    /* 'U' */  0x00,
    /* 'V' */  0x00,
    /* 'W' */  0x00,
    /* 'X' */  0x08,
    /* 'Y' */  0x60,
    /* 'Z' */  0x68,
    /* '[' */  0x60,
    /* '\' */  0x60,
    /* ']' */  0x60,
    /* '^' */  0x60,
    /* '_' */  0x00,
    /* '`' */  0x00,
    /* 'a' */  0x78,
    /* 'b' */  0x70,
    /* 'c' */  0x78,
    /* 'd' */  0x78,
    /* 'e' */  0x78,
    /* 'f' */  0x78,
    /* 'g' */  0x08,
    /* 'h' */  0x07,
    /* 'i' */  0x08,
    /* 'j' */  0x07,
    /* 'k' */  0x00,
    /* 'l' */  0x07,
    /* 'm' */  0x00,
    /* 'n' */  0x08,
    /* 'o' */  0x08,
    /* 'p' */  0x08,
    /* 'q' */  0x00,
    /* 'r' */  0x00,
    /* 's' */  0x08,
    /* 't' */  0x07,
    /* 'u' */  0x08,
    /* 'v' */  0x00,
    /* 'w' */  0x07,
    /* 'x' */  0x08,
    /* 'y' */  0x00,
    /* 'z' */  0x07
};

extern "C" extern __declspec(selectany) unsigned char const format_validation_lookup_table[]
{
    /* ' ' */  0x06,
    /* '!' */  0x80,
    /* '"' */  0x80,
    /* '#' */  0x86,
    /* '$' */  0x80,
    /* '%' */  0x81,
    /* '&' */  0x80,
    /* ''' */  0x00,
    /* '(' */  0x00,
    /* ')' */  0x10,
    /* '*' */  0x03,
    /* '+' */  0x86,
    /* ',' */  0x80,
    /* '-' */  0x86,
    /* '.' */  0x82,
    /* '/' */  0x80,
    /* '0' */  0x14,
    /* '1' */  0x05,
    /* '2' */  0x05,
    /* '3' */  0x45,
    /* '4' */  0x45,
    /* '5' */  0x45,
    /* '6' */  0x85,
    /* '7' */  0x85,
    /* '8' */  0x85,
    /* '9' */  0x05,
    /* ':' */  0x00,
    /* ';' */  0x00,
    /* '<' */  0x30,
    /* '=' */  0x30,
    /* '>' */  0x80,
    /* '?' */  0x50,
    /* '@' */  0x80,
    /* 'A' */  0x88,
    /* 'B' */  0x00,
    /* 'C' */  0x08,
    /* 'D' */  0x00,
    /* 'E' */  0x28,
    /* 'F' */  0x27,
    /* 'G' */  0x38,
    /* 'H' */  0x50,
    /* 'I' */  0x57,
    /* 'J' */  0x80,
    /* 'K' */  0x00,
    /* 'L' */  0x07,
    /* 'M' */  0x00,
    /* 'N' */  0x37,
    /* 'O' */  0x30,
    /* 'P' */  0x30,
    /* 'Q' */  0x50,
    /* 'R' */  0x50,
    /* 'S' */  0x88,
    /* 'T' */  0x07,
    /* 'U' */  0x00,
    /* 'V' */  0x00,
    /* 'W' */  0x20,
    /* 'X' */  0x28,
    /* 'Y' */  0x80,
    /* 'Z' */  0x88,
    /* '[' */  0x80,
    /* '\' */  0x80,
    /* ']' */  0x00,
    /* '^' */  0x00,
    /* '_' */  0x00,
    /* '`' */  0x60,
    /* 'a' */  0x68,
    /* 'b' */  0x60,
    /* 'c' */  0x68,
    /* 'd' */  0x68,
    /* 'e' */  0x68,
    /* 'f' */  0x08,
    /* 'g' */  0x08,
    /* 'h' */  0x07,
    /* 'i' */  0x78,
    /* 'j' */  0x77,
    /* 'k' */  0x70,
    /* 'l' */  0x77,
    /* 'm' */  0x70,
    /* 'n' */  0x70,
    /* 'o' */  0x08,
    /* 'p' */  0x08,
    /* 'q' */  0x00,
    /* 'r' */  0x00,
    /* 's' */  0x08,
    /* 't' */  0x07,
    /* 'u' */  0x08,
    /* 'v' */  0x00,
    /* 'w' */  0x07,
    /* 'x' */  0x08,
    /* 'y' */  0x00,
    /* 'z' */  0x07
};



//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// 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      {nullptr      },
          _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.
    errno_t*          _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;
    };
    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{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 unsigned char const* state_transition_table() throw()
    {
        return standard_lookup_table;
    }

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{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 unsigned char const* state_transition_table() throw()
    {
        return format_validation_lookup_table;
    }
};



//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// 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{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}
    {
        _cached_errno = &errno;
    }

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

        Character* end{};
        *result = static_cast<int>(char_traits::tcstol(_format_it - 1, &end, 10));

        if (*_cached_errno == 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 suppres the
            // output operation but we do not fail the entire operation:
            errno_t const status{wctomb_s(&_string_length, _buffer.data<char>(), _buffer.count<char>(), wide_character)};
            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);

        // 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')
        {
            _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);
            if (_precision > formatting_buffer::maximum_precision)
                _precision = formatting_buffer::maximum_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);

        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 || _narrow_string[0] != '0'))
        {
            *--_narrow_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 a narrow string.  Note that we format the
        // buffer at the end of the narrow buffer, which allows us to perform
        // the formatting from least to greatest magnitude, which maps well to
        // the math.
        char* const last_digit{_buffer.data<char>() + _buffer.count<char>() - 1};
        
        _narrow_string = last_digit;
        while (_precision > 0 || number != 0)
        {
            --_precision;
            
            char digit{static_cast<char>(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(static_cast<Character>(digit), capital_hexits);
            }

            *_narrow_string-- = static_cast<char>(digit);
        }

        _string_length = static_cast<int>(last_digit - _narrow_string);
        ++_narrow_string;
    }

    // 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(&mbc_length, local_buffer, _countof(local_buffer), *p++)};
                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 - ' '] & 0x0f));

        auto const index = current_class * state_count() + static_cast<unsigned>(previous_state);
        return static_cast<state>(lookup_table[index] >> 4);
    }

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