early-access version 1262

This commit is contained in:
pineappleEA 2020-12-30 00:04:04 +00:00
parent a5ba8b4937
commit ac593731e0
118 changed files with 8331 additions and 4646 deletions

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 1261. This is the source code for early-access 1262.
## Legal Notice ## Legal Notice

File diff suppressed because it is too large Load diff

View file

@ -15,6 +15,7 @@ bin/
/CMakeScripts /CMakeScripts
/doc/doxyxml /doc/doxyxml
/doc/html /doc/html
/doc/node_modules
virtualenv virtualenv
/Testing /Testing
/install_manifest.txt /install_manifest.txt

View file

@ -24,15 +24,23 @@ function(join result_var)
set(${result_var} "${result}" PARENT_SCOPE) set(${result_var} "${result}" PARENT_SCOPE)
endfunction() endfunction()
include(CMakeParseArguments)
# Sets a cache variable with a docstring joined from multiple arguments: # Sets a cache variable with a docstring joined from multiple arguments:
# set(<variable> <value>... CACHE <type> <docstring>...) # set(<variable> <value>... CACHE <type> <docstring>...)
# This allows splitting a long docstring for readability. # This allows splitting a long docstring for readability.
function(set_verbose) function(set_verbose)
cmake_parse_arguments(SET_VERBOSE "" "" "CACHE" ${ARGN}) # cmake_parse_arguments is broken in CMake 3.4 (cannot parse CACHE) so use
list(GET SET_VERBOSE_CACHE 0 type) # list instead.
list(REMOVE_AT SET_VERBOSE_CACHE 0) list(GET ARGN 0 var)
join(doc ${SET_VERBOSE_CACHE}) list(REMOVE_AT ARGN 0)
set(${SET_VERBOSE_UNPARSED_ARGUMENTS} CACHE ${type} ${doc}) list(GET ARGN 0 val)
list(REMOVE_AT ARGN 0)
list(REMOVE_AT ARGN 0)
list(GET ARGN 0 type)
list(REMOVE_AT ARGN 0)
join(doc ${ARGN})
set(${var} ${val} CACHE ${type} ${doc})
endfunction() endfunction()
# Set the default CMAKE_BUILD_TYPE to Release. # Set the default CMAKE_BUILD_TYPE to Release.
@ -44,6 +52,12 @@ if (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") "CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
endif () endif ()
project(FMT CXX)
include(GNUInstallDirs)
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING
"Installation directory for include files, a relative path "
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF) option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
option(FMT_WERROR "Halt the compilation with an error on compiler warnings." option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF) OFF)
@ -54,8 +68,7 @@ option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT}) option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
option(FMT_FUZZ "Generate the fuzz target." OFF) option(FMT_FUZZ "Generate the fuzz target." OFF)
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF) option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
project(FMT CXX)
# Get version from core.h # Get version from core.h
file(READ include/fmt/core.h core_h) file(READ include/fmt/core.h core_h)
@ -81,6 +94,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
include(cxx14) include(cxx14)
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
include(JoinPaths)
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index) list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
if (${index} GREATER -1) if (${index} GREATER -1)
@ -173,7 +187,11 @@ endfunction()
# Define the fmt library, its includes and the needed defines. # Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h
locale.h os.h ostream.h posix.h printf.h ranges.h) locale.h os.h ostream.h posix.h printf.h ranges.h)
set(FMT_SOURCES src/format.cc src/os.cc) if (FMT_OS)
set(FMT_SOURCES src/format.cc src/os.cc)
else()
set(FMT_SOURCES src/format.cc)
endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst) add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt) add_library(fmt::fmt ALIAS fmt)
@ -182,6 +200,10 @@ if (HAVE_STRTOD_L)
target_compile_definitions(fmt PUBLIC FMT_LOCALE) target_compile_definitions(fmt PUBLIC FMT_LOCALE)
endif () endif ()
if (MINGW)
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
endif ()
if (FMT_WERROR) if (FMT_WERROR)
target_compile_options(fmt PRIVATE ${WERROR_FLAG}) target_compile_options(fmt PRIVATE ${WERROR_FLAG})
endif () endif ()
@ -193,7 +215,7 @@ target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt PUBLIC target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>) $<INSTALL_INTERFACE:${FMT_INC_DIR}>)
set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.") set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
@ -209,7 +231,7 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug")
endif () endif ()
if (BUILD_SHARED_LIBS) if (BUILD_SHARED_LIBS)
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS") if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" AND NOT EMSCRIPTEN)
# Fix rpmlint warning: # Fix rpmlint warning:
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6. # unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
target_link_libraries(fmt -Wl,--as-needed) target_link_libraries(fmt -Wl,--as-needed)
@ -228,42 +250,36 @@ target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt-header-only INTERFACE target_include_directories(fmt-header-only INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>) $<INSTALL_INTERFACE:${FMT_INC_DIR}>)
# Install targets. # Install targets.
if (FMT_INSTALL) if (FMT_INSTALL)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
"Installation directory for cmake files, relative to " "Installation directory for cmake files, a relative path "
"${CMAKE_INSTALL_PREFIX}.") "that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake) set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake) set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc) set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
set(targets_export_name fmt-targets) set(targets_export_name fmt-targets)
set (INSTALL_TARGETS fmt)
if (TARGET fmt-header-only)
set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only)
endif ()
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
"Installation directory for libraries, relative to " "Installation directory for libraries, a relative path "
"${CMAKE_INSTALL_PREFIX}.") "that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
"Installation directory for include files, relative to "
"${CMAKE_INSTALL_PREFIX}.")
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
"Installation directory for pkgconfig (.pc) files, relative to " "Installation directory for pkgconfig (.pc) files, a relative path "
"${CMAKE_INSTALL_PREFIX}.") "that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
# Generate the version, config and target files into the build directory. # Generate the version, config and target files into the build directory.
write_basic_package_version_file( write_basic_package_version_file(
${version_config} ${version_config}
VERSION ${FMT_VERSION} VERSION ${FMT_VERSION}
COMPATIBILITY AnyNewerVersion) COMPATIBILITY AnyNewerVersion)
join_paths(libdir_for_pc_file "\${exec_prefix}" "${FMT_LIB_DIR}")
join_paths(includedir_for_pc_file "\${prefix}" "${FMT_INC_DIR}")
configure_file( configure_file(
"${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in" "${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in"
"${pkgconfig}" "${pkgconfig}"
@ -272,6 +288,8 @@ if (FMT_INSTALL)
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in ${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
${project_config} ${project_config}
INSTALL_DESTINATION ${FMT_CMAKE_DIR}) INSTALL_DESTINATION ${FMT_CMAKE_DIR})
set(INSTALL_TARGETS fmt fmt-header-only)
# Use a namespace because CMake provides better diagnostics for namespaced # Use a namespace because CMake provides better diagnostics for namespaced
# imported targets. # imported targets.
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt:: export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
@ -292,7 +310,7 @@ if (FMT_INSTALL)
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}> install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL) DESTINATION ${FMT_LIB_DIR} OPTIONAL)
install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR}) install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt")
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif () endif ()
@ -308,6 +326,7 @@ endif ()
# Control fuzzing independent of the unit tests. # Control fuzzing independent of the unit tests.
if (FMT_FUZZ) if (FMT_FUZZ)
add_subdirectory(test/fuzzing) add_subdirectory(test/fuzzing)
target_compile_definitions(fmt PUBLIC FMT_FUZZ)
endif () endif ()
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore) set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)

View file

@ -1,3 +1,349 @@
7.0.3 - 2020-08-06
------------------
* Worked around broken ``numeric_limits`` for 128-bit integers
(`#1787 <https://github.com/fmtlib/fmt/issues/1787>`_).
* Added error reporting on missing named arguments
(`#1796 <https://github.com/fmtlib/fmt/issues/1796>`_).
* Stopped using 128-bit integers with clang-cl
(`#1800 <https://github.com/fmtlib/fmt/pull/1800>`_).
Thanks `@Kingcom <https://github.com/Kingcom>`_.
* Fixed issues in locale-specific integer formatting
(`#1782 <https://github.com/fmtlib/fmt/issues/1782>`_,
`#1801 <https://github.com/fmtlib/fmt/issues/1801>`_).
7.0.2 - 2020-07-29
------------------
* Worked around broken ``numeric_limits`` for 128-bit integers
(`#1725 <https://github.com/fmtlib/fmt/issues/1725>`_).
* Fixed compatibility with CMake 3.4
(`#1779 <https://github.com/fmtlib/fmt/issues/1779>`_).
* Fixed handling of digit separators in locale-specific formatting
(`#1782 <https://github.com/fmtlib/fmt/issues/1782>`_).
7.0.1 - 2020-07-07
------------------
* Updated the inline version namespace name.
* Worked around a gcc bug in mangling of alias templates
(`#1753 <https://github.com/fmtlib/fmt/issues/1753>`_).
* Fixed a linkage error on Windows
(`#1757 <https://github.com/fmtlib/fmt/issues/1757>`_).
Thanks `@Kurkin (Dmitry Kurkin) <https://github.com/Kurkin>`_.
* Fixed minor issues with the documentation.
7.0.0 - 2020-07-05
------------------
* Reduced the library size. For example, on macOS a stripped test binary
statically linked with {fmt} `shrank from ~368k to less than 100k
<http://www.zverovich.net/2020/05/21/reducing-library-size.html>`_.
* Added a simpler and more efficient `format string compilation API
<https://fmt.dev/dev/api.html#compile-api>`_:
.. code:: c++
#include <fmt/compile.h>
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
The old ``fmt::compile`` API is now deprecated.
* Optimized integer formatting: ``format_to`` with format string compilation
and a stack-allocated buffer is now `faster than to_chars on both
libc++ and libstdc++
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_.
* Optimized handling of small format strings. For example,
.. code:: c++
fmt::format("Result: {}: ({},{},{},{})", str1, str2, str3, str4, str5)
is now ~40% faster (`#1685 <https://github.com/fmtlib/fmt/issues/1685>`_).
* Applied extern templates to improve compile times when using the core API
and ``fmt/format.h`` (`#1452 <https://github.com/fmtlib/fmt/issues/1452>`_).
For example, on macOS with clang the compile time of a test translation unit
dropped from 2.3s to 0.3s with ``-O2`` and from 0.6s to 0.3s with the default
settings (``-O0``).
Before (``-O2``)::
% time c++ -c test.cc -I include -std=c++17 -O2
c++ -c test.cc -I include -std=c++17 -O2 2.22s user 0.08s system 99% cpu 2.311 total
After (``-O2``)::
% time c++ -c test.cc -I include -std=c++17 -O2
c++ -c test.cc -I include -std=c++17 -O2 0.26s user 0.04s system 98% cpu 0.303 total
Before (default)::
% time c++ -c test.cc -I include -std=c++17
c++ -c test.cc -I include -std=c++17 0.53s user 0.06s system 98% cpu 0.601 total
After (default)::
% time c++ -c test.cc -I include -std=c++17
c++ -c test.cc -I include -std=c++17 0.24s user 0.06s system 98% cpu 0.301 total
It is still recommended to use ``fmt/core.h`` instead of ``fmt/format.h`` but
the compile time difference is now smaller. Thanks
`@alex3d <https://github.com/alex3d>`_ for the suggestion.
* Named arguments are now stored on stack (no dynamic memory allocations) and
the compiled code is more compact and efficient. For example
.. code:: c++
#include <fmt/core.h>
int main() {
fmt::print("The answer is {answer}\n", fmt::arg("answer", 42));
}
compiles to just (`godbolt <https://godbolt.org/z/NcfEp_>`__)
.. code:: asm
.LC0:
.string "answer"
.LC1:
.string "The answer is {answer}\n"
main:
sub rsp, 56
mov edi, OFFSET FLAT:.LC1
mov esi, 23
movabs rdx, 4611686018427387905
lea rax, [rsp+32]
lea rcx, [rsp+16]
mov QWORD PTR [rsp+8], 1
mov QWORD PTR [rsp], rax
mov DWORD PTR [rsp+16], 42
mov QWORD PTR [rsp+32], OFFSET FLAT:.LC0
mov DWORD PTR [rsp+40], 0
call fmt::v6::vprint(fmt::v6::basic_string_view<char>,
fmt::v6::format_args)
xor eax, eax
add rsp, 56
ret
.L.str.1:
.asciz "answer"
* Implemented compile-time checks for dynamic width and precision
(`#1614 <https://github.com/fmtlib/fmt/issues/1614>`_):
.. code:: c++
#include <fmt/format.h>
int main() {
fmt::print(FMT_STRING("{0:{1}}"), 42);
}
now gives a compilation error because argument 1 doesn't exist::
In file included from test.cc:1:
include/fmt/format.h:2726:27: error: constexpr variable 'invalid_format' must be
initialized by a constant expression
FMT_CONSTEXPR_DECL bool invalid_format =
^
...
include/fmt/core.h:569:26: note: in call to
'&checker(s, {}).context_->on_error(&"argument not found"[0])'
if (id >= num_args_) on_error("argument not found");
^
* Added sentinel support to ``fmt::join``
(`#1689 <https://github.com/fmtlib/fmt/pull/1689>`_)
.. code:: c++
struct zstring_sentinel {};
bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
struct zstring {
const char* p;
const char* begin() const { return p; }
zstring_sentinel end() const { return {}; }
};
auto s = fmt::format("{}", fmt::join(zstring{"hello"}, "_"));
// s == "h_e_l_l_o"
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
* Added support for named args, ``clear`` and ``reserve`` to
``dynamic_format_arg_store``
(`#1655 <https://github.com/fmtlib/fmt/issues/1655>`_,
`#1663 <https://github.com/fmtlib/fmt/pull/1663>`_,
`#1674 <https://github.com/fmtlib/fmt/pull/1674>`_,
`#1677 <https://github.com/fmtlib/fmt/pull/1677>`_).
Thanks `@vsolontsov-ll (Vladimir Solontsov)
<https://github.com/vsolontsov-ll>`_.
* Added support for the ``'c'`` format specifier to integral types for
compatibility with ``std::format``
(`#1652 <https://github.com/fmtlib/fmt/issues/1652>`_).
* Replaced the ``'n'`` format specifier with ``'L'`` for compatibility with
``std::format`` (`#1624 <https://github.com/fmtlib/fmt/issues/1624>`_).
The ``'n'`` specifier can be enabled via the ``FMT_DEPRECATED_N_SPECIFIER``
macro.
* The ``'='`` format specifier is now disabled by default for compatibility with
``std::format``. It can be enabled via the ``FMT_DEPRECATED_NUMERIC_ALIGN``
macro.
* Removed the following deprecated APIs:
* ``FMT_STRING_ALIAS`` and ``fmt`` macros - replaced by ``FMT_STRING``
* ``fmt::basic_string_view::char_type`` - replaced by
``fmt::basic_string_view::value_type``
* ``convert_to_int``
* ``format_arg_store::types``
* ``*parse_context`` - replaced by ``*format_parse_context``
* ``FMT_DEPRECATED_INCLUDE_OS``
* ``FMT_DEPRECATED_PERCENT`` - incompatible with ``std::format``
* ``*writer`` - replaced by compiled format API
* Renamed the ``internal`` namespace to ``detail``
(`#1538 <https://github.com/fmtlib/fmt/issues/1538>`_). The former is still
provided as an alias if the ``FMT_USE_INTERNAL`` macro is defined.
* Improved compatibility between ``fmt::printf`` with the standard specs
(`#1595 <https://github.com/fmtlib/fmt/issues/1595>`_,
`#1682 <https://github.com/fmtlib/fmt/pull/1682>`_,
`#1683 <https://github.com/fmtlib/fmt/pull/1683>`_,
`#1687 <https://github.com/fmtlib/fmt/pull/1687>`_,
`#1699 <https://github.com/fmtlib/fmt/pull/1699>`_).
Thanks `@rimathia <https://github.com/rimathia>`_.
* Fixed handling of ``operator<<`` overloads that use ``copyfmt``
(`#1666 <https://github.com/fmtlib/fmt/issues/1666>`_).
* Added the ``FMT_OS`` CMake option to control inclusion of OS-specific APIs
in the fmt target. This can be useful for embedded platforms
(`#1654 <https://github.com/fmtlib/fmt/issues/1654>`_,
`#1656 <https://github.com/fmtlib/fmt/pull/1656>`_).
Thanks `@kwesolowski (Krzysztof Wesolowski)
<https://github.com/kwesolowski>`_.
* Replaced ``FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION`` with the ``FMT_FUZZ``
macro to prevent interferring with fuzzing of projects using {fmt}
(`#1650 <https://github.com/fmtlib/fmt/pull/1650>`_).
Thanks `@asraa (Asra Ali) <https://github.com/asraa>`_.
* Fixed compatibility with emscripten
(`#1636 <https://github.com/fmtlib/fmt/issues/1636>`_,
`#1637 <https://github.com/fmtlib/fmt/pull/1637>`_).
Thanks `@ArthurSonzogni (Arthur Sonzogni)
<https://github.com/ArthurSonzogni>`_.
* Improved documentation
(`#704 <https://github.com/fmtlib/fmt/issues/704>`_,
`#1643 <https://github.com/fmtlib/fmt/pull/1643>`_,
`#1660 <https://github.com/fmtlib/fmt/pull/1660>`_,
`#1681 <https://github.com/fmtlib/fmt/pull/1681>`_,
`#1691 <https://github.com/fmtlib/fmt/pull/1691>`_,
`#1706 <https://github.com/fmtlib/fmt/pull/1706>`_,
`#1714 <https://github.com/fmtlib/fmt/pull/1714>`_,
`#1721 <https://github.com/fmtlib/fmt/pull/1721>`_,
`#1739 <https://github.com/fmtlib/fmt/pull/1739>`_,
`#1740 <https://github.com/fmtlib/fmt/pull/1740>`_,
`#1741 <https://github.com/fmtlib/fmt/pull/1741>`_,
`#1751 <https://github.com/fmtlib/fmt/pull/1751>`_).
Thanks `@senior7515 (Alexander Gallego) <https://github.com/senior7515>`_,
`@lsr0 (Lindsay Roberts) <https://github.com/lsr0>`_,
`@puetzk (Kevin Puetz) <https://github.com/puetzk>`_,
`@fpelliccioni (Fernando Pelliccioni) <https://github.com/fpelliccioni>`_,
Alexey Kuzmenko, `@jelly (jelle van der Waa) <https://github.com/jelly>`_,
`@claremacrae (Clare Macrae) <https://github.com/claremacrae>`_,
`@jiapengwen (文佳鹏) <https://github.com/jiapengwen>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@alexey-milovidov <https://github.com/alexey-milovidov>`_.
* Implemented various build configuration fixes and improvements
(`#1603 <https://github.com/fmtlib/fmt/pull/1603>`_,
`#1657 <https://github.com/fmtlib/fmt/pull/1657>`_,
`#1702 <https://github.com/fmtlib/fmt/pull/1702>`_,
`#1728 <https://github.com/fmtlib/fmt/pull/1728>`_).
Thanks `@scramsby (Scott Ramsby) <https://github.com/scramsby>`_,
`@jtojnar (Jan Tojnar) <https://github.com/jtojnar>`_,
`@orivej (Orivej Desh) <https://github.com/orivej>`_,
`@flagarde <https://github.com/flagarde>`_.
* Fixed various warnings and compilation issues
(`#1616 <https://github.com/fmtlib/fmt/pull/1616>`_,
`#1620 <https://github.com/fmtlib/fmt/issues/1620>`_,
`#1622 <https://github.com/fmtlib/fmt/issues/1622>`_,
`#1625 <https://github.com/fmtlib/fmt/issues/1625>`_,
`#1627 <https://github.com/fmtlib/fmt/pull/1627>`_,
`#1628 <https://github.com/fmtlib/fmt/issues/1628>`_,
`#1629 <https://github.com/fmtlib/fmt/pull/1629>`_,
`#1631 <https://github.com/fmtlib/fmt/issues/1631>`_,
`#1633 <https://github.com/fmtlib/fmt/pull/1633>`_,
`#1649 <https://github.com/fmtlib/fmt/pull/1649>`_,
`#1658 <https://github.com/fmtlib/fmt/issues/1658>`_,
`#1661 <https://github.com/fmtlib/fmt/pull/1661>`_,
`#1667 <https://github.com/fmtlib/fmt/pull/1667>`_,
`#1668 <https://github.com/fmtlib/fmt/issues/1668>`_,
`#1669 <https://github.com/fmtlib/fmt/pull/1669>`_,
`#1692 <https://github.com/fmtlib/fmt/issues/1692>`_,
`#1696 <https://github.com/fmtlib/fmt/pull/1696>`_,
`#1697 <https://github.com/fmtlib/fmt/pull/1697>`_,
`#1707 <https://github.com/fmtlib/fmt/issues/1707>`_,
`#1712 <https://github.com/fmtlib/fmt/pull/1712>`_,
`#1716 <https://github.com/fmtlib/fmt/pull/1716>`_,
`#1722 <https://github.com/fmtlib/fmt/pull/1722>`_,
`#1724 <https://github.com/fmtlib/fmt/issues/1724>`_,
`#1729 <https://github.com/fmtlib/fmt/pull/1729>`_,
`#1738 <https://github.com/fmtlib/fmt/pull/1738>`_,
`#1742 <https://github.com/fmtlib/fmt/issues/1742>`_,
`#1743 <https://github.com/fmtlib/fmt/issues/1743>`_,
`#1744 <https://github.com/fmtlib/fmt/pull/1744>`_,
`#1747 <https://github.com/fmtlib/fmt/issues/1747>`_,
`#1750 <https://github.com/fmtlib/fmt/pull/1750>`_).
Thanks `@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@gabime (Gabi Melman) <https://github.com/gabime>`_,
`@johnor (Johan) <https://github.com/johnor>`_,
`@Kurkin (Dmitry Kurkin) <https://github.com/Kurkin>`_,
`@invexed (James Beach) <https://github.com/invexed>`_,
`@peterbell10 <https://github.com/peterbell10>`_,
`@daixtrose (Markus Werle) <https://github.com/daixtrose>`_,
`@petrutlucian94 (Lucian Petrut) <https://github.com/petrutlucian94>`_,
`@Neargye (Daniil Goncharov) <https://github.com/Neargye>`_,
`@ambitslix (Attila M. Szilagyi) <https://github.com/ambitslix>`_,
`@gabime (Gabi Melman) <https://github.com/gabime>`_,
`@erthink (Leonid Yuriev) <https://github.com/erthink>`_,
`@tohammer (Tobias Hammer) <https://github.com/tohammer>`_,
`@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_.
6.2.1 - 2020-05-09
------------------
* Fixed ostream support in ``sprintf``
(`#1631 <https://github.com/fmtlib/fmt/issues/1631>`_).
* Fixed type detection when using implicit conversion to ``string_view`` and
ostream ``operator<<`` inconsistently
(`#1662 <https://github.com/fmtlib/fmt/issues/1662>`_).
6.2.0 - 2020-04-05 6.2.0 - 2020-04-05
------------------ ------------------
@ -24,7 +370,7 @@
if ``S`` is not formattable. if ``S`` is not formattable.
* Reduced library size by ~10%. * Reduced the library size by ~10%.
* Always print decimal point if ``#`` is specified * Always print decimal point if ``#`` is specified
(`#1476 <https://github.com/fmtlib/fmt/issues/1476>`_, (`#1476 <https://github.com/fmtlib/fmt/issues/1476>`_,
@ -587,16 +933,16 @@
#include <fmt/compile.h> #include <fmt/compile.h>
auto f = fmt::compile<int>("{}"); auto f = fmt::compile<int>("{}");
std::string s = fmt::format(f, 42); // can be called multiple times to format std::string s = fmt::format(f, 42); // can be called multiple times to
// different values // format different values
// s == "42" // s == "42"
It moves the cost of parsing a format string outside of the format function It moves the cost of parsing a format string outside of the format function
which can be beneficial when identically formatting many objects of the same which can be beneficial when identically formatting many objects of the same
types. Thanks `@stryku (Mateusz Janek) <https://github.com/stryku>`_. types. Thanks `@stryku (Mateusz Janek) <https://github.com/stryku>`_.
* Added the ``%`` format specifier that formats floating-point values as * Added experimental ``%`` format specifier that formats floating-point values
percentages (`#1060 <https://github.com/fmtlib/fmt/pull/1060>`_, as percentages (`#1060 <https://github.com/fmtlib/fmt/pull/1060>`_,
`#1069 <https://github.com/fmtlib/fmt/pull/1069>`_, `#1069 <https://github.com/fmtlib/fmt/pull/1069>`_,
`#1071 <https://github.com/fmtlib/fmt/pull/1071>`_): `#1071 <https://github.com/fmtlib/fmt/pull/1071>`_):

View file

@ -9,7 +9,9 @@
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg .. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
:alt: fmt is continuously fuzzed att oss-fuzz :alt: fmt is continuously fuzzed att oss-fuzz
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dlibfmt&can=1 :target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\
Summary&q=proj%3Dlibfmt&can=1
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg .. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
:alt: Ask questions at StackOverflow with the tag fmt :alt: Ask questions at StackOverflow with the tag fmt
@ -20,42 +22,46 @@ It can be used as a safe and fast alternative to (s)printf and iostreams.
`Documentation <https://fmt.dev/latest/>`__ `Documentation <https://fmt.dev/latest/>`__
Q&A: ask questions on `StackOverflow with the tag fmt <https://stackoverflow.com/questions/tagged/fmt>`_. Q&A: ask questions on `StackOverflow with the tag fmt
<https://stackoverflow.com/questions/tagged/fmt>`_.
Features Features
-------- --------
* Replacement-based `format API <https://fmt.dev/dev/api.html>`_ with * Simple `format API <https://fmt.dev/dev/api.html>`_ with positional arguments
positional arguments for localization. for localization
* Implementation of `C++20 std::format
<https://en.cppreference.com/w/cpp/utility/format>`__
* `Format string syntax <https://fmt.dev/dev/syntax.html>`_ similar to the one * `Format string syntax <https://fmt.dev/dev/syntax.html>`_ similar to the one
of `str.format <https://docs.python.org/3/library/stdtypes.html#str.format>`_ of Python's
in Python. `format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
* Safe `printf implementation * Safe `printf implementation
<https://fmt.dev/latest/api.html#printf-formatting>`_ including <https://fmt.dev/latest/api.html#printf-formatting>`_ including
the POSIX extension for positional arguments. the POSIX extension for positional arguments
* Implementation of `C++20 std::format <https://en.cppreference.com/w/cpp/utility/format>`__. * Extensibility: support for user-defined types
* Support for user-defined types.
* High performance: faster than common standard library implementations of * High performance: faster than common standard library implementations of
`printf <https://en.cppreference.com/w/cpp/io/c/fprintf>`_ and `printf <https://en.cppreference.com/w/cpp/io/c/fprintf>`_,
iostreams. See `Speed tests`_ and `Fast integer to string conversion in C++ iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_ and
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_. `Converting a hundred million integers to strings per second
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
* Small code size both in terms of source code (the minimum configuration * Small code size both in terms of source code (the minimum configuration
consists of just three header files, ``core.h``, ``format.h`` and consists of just three header files, ``core.h``, ``format.h`` and
``format-inl.h``) and compiled code. See `Compile time and code bloat`_. ``format-inl.h``) and compiled code. See `Compile time and code bloat`_
* Reliability: the library has an extensive set of `unit tests * Reliability: the library has an extensive set of `unit tests
<https://github.com/fmtlib/fmt/tree/master/test>`_ and is continuously fuzzed. <https://github.com/fmtlib/fmt/tree/master/test>`_ and is continuously fuzzed
* Safety: the library is fully type safe, errors in format strings can be * Safety: the library is fully type safe, errors in format strings can be
reported at compile time, automatic memory management prevents buffer overflow reported at compile time, automatic memory management prevents buffer overflow
errors. errors
* Ease of use: small self-contained code base, no external dependencies, * Ease of use: small self-contained code base, no external dependencies,
permissive MIT `license permissive MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_ <https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with * `Portability <https://fmt.dev/latest/index.html#portability>`_ with
consistent output across platforms and support for older compilers. consistent output across platforms and support for older compilers
* Clean warning-free codebase even on high warning levels * Clean warning-free codebase even on high warning levels
(``-Wall -Wextra -pedantic``). (``-Wall -Wextra -pedantic``)
* Support for wide strings. * Locale-independence by default
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro. * Support for wide strings
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro
See the `documentation <https://fmt.dev/latest/>`_ for more details. See the `documentation <https://fmt.dev/latest/>`_ for more details.
@ -66,37 +72,49 @@ Print ``Hello, world!`` to ``stdout``:
.. code:: c++ .. code:: c++
fmt::print("Hello, {}!", "world"); // Python-like format string syntax #include <fmt/core.h>
fmt::printf("Hello, %s!", "world"); // printf format string syntax
int main() {
fmt::print("Hello, world!\n");
}
Format a string and use positional arguments: Format a string:
.. code:: c++
std::string s = fmt::format("The answer is {}.", 42);
// s == "The answer is 42."
Format a string using positional arguments:
.. code:: c++ .. code:: c++
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
// s == "I'd rather be happy than right." // s == "I'd rather be happy than right."
Print a chrono duration:
.. code:: c++
#include <fmt/chrono.h>
int main() {
using namespace std::chrono_literals;
fmt::print("Elapsed time: {}", 42ms);
}
prints "Elapsed time: 42ms".
Check a format string at compile time: Check a format string at compile time:
.. code:: c++ .. code:: c++
// test.cc // test.cc
#include <fmt/format.h> #include <fmt/format.h>
std::string s = format(FMT_STRING("{2}"), 42); std::string s = format(FMT_STRING("{:d}"), "hello");
.. code:: gives a compile-time error because ``d`` is an invalid format specifier for a
string.
$ c++ -Iinclude -std=c++14 test.cc
...
test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
std::string s = format(FMT_STRING("{2}"), 42);
^
include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
ErrorHandler::on_error(message);
^
include/fmt/format.h:2226:16: note: in call to '&checker.context_->on_error(&"argument index out of range"[0])'
context_.on_error("argument index out of range");
^
Use {fmt} as a safe portable replacement for ``itoa`` Use {fmt} as a safe portable replacement for ``itoa``
(`godbolt <https://godbolt.org/g/NXmpU4>`_): (`godbolt <https://godbolt.org/g/NXmpU4>`_):
@ -113,7 +131,7 @@ Format objects of user-defined types via a simple `extension API
.. code:: c++ .. code:: c++
#include "fmt/format.h" #include <fmt/format.h>
struct date { struct date {
int year, month, day; int year, month, day;
@ -174,15 +192,15 @@ Folly Format folly::format 2.23
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``. {fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
The above results were generated by building ``tinyformat_test.cpp`` on macOS The above results were generated by building ``tinyformat_test.cpp`` on macOS
10.14.6 with ``clang++ -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of 10.14.6 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the
three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
further details refer to the `source further details refer to the `source
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_. <https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
{fmt} is 10x faster than ``std::ostringstream`` and ``sprintf`` on floating-point {fmt} is up to 10x faster than ``std::ostringstream`` and ``sprintf`` on
formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_) floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
and as fast as `double-conversion <https://github.com/google/double-conversion>`_: and faster than `double-conversion <https://github.com/google/double-conversion>`_:
.. image:: https://user-images.githubusercontent.com/576385/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png .. image:: https://user-images.githubusercontent.com/576385/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
:target: https://fmt.dev/unknown_mac64_clang10.0.html :target: https://fmt.dev/unknown_mac64_clang10.0.html
@ -264,12 +282,15 @@ or the bloat test::
Projects using this library Projects using this library
--------------------------- ---------------------------
* `0 A.D. <https://play0ad.com/>`_: A free, open-source, cross-platform real-time * `0 A.D. <https://play0ad.com/>`_: A free, open-source, cross-platform
strategy game real-time strategy game
* `AMPL/MP <https://github.com/ampl/mp>`_: * `AMPL/MP <https://github.com/ampl/mp>`_:
An open-source library for mathematical programming An open-source library for mathematical programming
* `Aseprite <https://github.com/aseprite/aseprite>`_:
Animated sprite editor & pixel art tool
* `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft * `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft
operations suite operations suite
@ -279,9 +300,21 @@ Projects using this library
* `ccache <https://ccache.dev/>`_: A compiler cache * `ccache <https://ccache.dev/>`_: A compiler cache
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database management system
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater * `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
vehicle vehicle
* `Drake <https://drake.mit.edu/>`_: A planning, control, and analysis toolbox
for nonlinear dynamical systems (MIT)
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
(Lyft)
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_: * `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
Player vs Player Gaming Network with tweaks Player vs Player Gaming Network with tweaks
@ -291,27 +324,25 @@ Projects using this library
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software * `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game * `Knuth <https://kth.cash/>`_: High-performance Bitcoin full-node
* `Drake <https://drake.mit.edu/>`_: A planning, control, and analysis toolbox * `Microsoft Verona <https://github.com/microsoft/verona>`_:
for nonlinear dynamical systems (MIT) Research programming language for concurrent ownership
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
(Lyft)
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
* `MongoDB <https://mongodb.com/>`_: Distributed document database * `MongoDB <https://mongodb.com/>`_: Distributed document database
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to * `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
generate randomized datasets generate randomized datasets
* `OpenSpace <https://openspaceproject.com/>`_: An open-source astrovisualization * `OpenSpace <https://openspaceproject.com/>`_: An open-source
framework astrovisualization framework
* `PenUltima Online (POL) <https://www.polserver.com/>`_: * `PenUltima Online (POL) <https://www.polserver.com/>`_:
An MMO server, compatible with most Ultima Online clients An MMO server, compatible with most Ultima Online clients
* `PyTorch <https://github.com/pytorch/pytorch>`_: An open-source machine
learning library
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance, * `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
associative database associative database
@ -320,13 +351,14 @@ Projects using this library
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster * `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster
proxy proxy
* `redpanda <https://vectorized.io/redpanda>`_: A 10x faster Kafka® replacement
for mission critical systems written in C++
* `rpclib <http://rpclib.net/>`_: A modern C++ msgpack-RPC server and client * `rpclib <http://rpclib.net/>`_: A modern C++ msgpack-RPC server and client
library library
* `Saddy <https://github.com/mamontov-cpp/saddy-graphics-engine-2d>`_: * `Salesforce Analytics Cloud
Small crossplatform 2D graphic engine <https://www.salesforce.com/analytics-cloud/overview/>`_:
* `Salesforce Analytics Cloud <https://www.salesforce.com/analytics-cloud/overview/>`_:
Business intelligence software Business intelligence software
* `Scylla <https://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store * `Scylla <https://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store
@ -344,6 +376,9 @@ Projects using this library
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source * `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
MMORPG framework MMORPG framework
* `Windows Terminal <https://github.com/microsoft/terminal>`_: The new Windows
Terminal
`More... <https://github.com/search?q=fmtlib&type=Code>`_ `More... <https://github.com/search?q=fmtlib&type=Code>`_
If you are aware of other projects using this library, please let me know If you are aware of other projects using this library, please let me know
@ -407,8 +442,8 @@ Format also has excessive build times and severe code bloat issues (see
FastFormat FastFormat
~~~~~~~~~~ ~~~~~~~~~~
This is an interesting library which is fast, safe and has positional This is an interesting library which is fast, safe and has positional arguments.
arguments. However it has significant limitations, citing its author: However, it has significant limitations, citing its author:
Three features that have no hope of being accommodated within the Three features that have no hope of being accommodated within the
current design are: current design are:
@ -417,8 +452,8 @@ arguments. However it has significant limitations, citing its author:
* Octal/hexadecimal encoding * Octal/hexadecimal encoding
* Runtime width/alignment specification * Runtime width/alignment specification
It is also quite big and has a heavy dependency, STLSoft, which might be It is also quite big and has a heavy dependency, STLSoft, which might be too
too restrictive for using it in some projects. restrictive for using it in some projects.
Boost Spirit.Karma Boost Spirit.Karma
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
@ -426,32 +461,9 @@ Boost Spirit.Karma
This is not really a formatting library but I decided to include it here for This is not really a formatting library but I decided to include it here for
completeness. As iostreams, it suffers from the problem of mixing verbatim text completeness. As iostreams, it suffers from the problem of mixing verbatim text
with arguments. The library is pretty fast, but slower on integer formatting with arguments. The library is pretty fast, but slower on integer formatting
than ``fmt::format_int`` on Karma's own benchmark, than ``fmt::format_to`` with format string compilation on Karma's own benchmark,
see `Fast integer to string conversion in C++ see `Converting a hundred million integers to strings per second
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_. <http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_.
FAQ
---
Q: how can I capture formatting arguments and format them later?
A: use ``std::tuple``:
.. code:: c++
template <typename... Args>
auto capture(const Args&... args) {
return std::make_tuple(args...);
}
auto print_message = [](const auto&... args) {
fmt::print(args...);
};
// Capture and store arguments:
auto args = capture("{} {}", 42, "foo");
// Do formatting:
std::apply(print_message, args);
License License
------- -------
@ -459,18 +471,19 @@ License
{fmt} is distributed under the MIT `license {fmt} is distributed under the MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_. <https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
The `Format String Syntax Documentation License
<https://fmt.dev/latest/syntax.html>`_ ---------------------
section in the documentation is based on the one from Python `string module
documentation <https://docs.python.org/3/library/string.html#module-string>`_
adapted for the current library. For this reason the documentation is
distributed under the Python Software Foundation license available in
`doc/python-license.txt
<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_.
It only applies if you distribute the documentation of fmt.
Acknowledgments The `Format String Syntax <https://fmt.dev/latest/syntax.html>`_
--------------- section in the documentation is based on the one from Python `string module
documentation <https://docs.python.org/3/library/string.html#module-string>`_.
For this reason the documentation is distributed under the Python Software
Foundation license available in `doc/python-license.txt
<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_.
It only applies if you distribute the documentation of {fmt}.
Maintainers
-----------
The {fmt} library is maintained by Victor Zverovich (`vitaut The {fmt} library is maintained by Victor Zverovich (`vitaut
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan <https://github.com/vitaut>`_) and Jonathan Müller (`foonathan
@ -479,23 +492,3 @@ See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names. `Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names.
Let us know if your contribution is not listed or mentioned incorrectly and Let us know if your contribution is not listed or mentioned incorrectly and
we'll make it right. we'll make it right.
The benchmark section of this readme file and the performance tests are taken
from the excellent `tinyformat <https://github.com/c42f/tinyformat>`_ library
written by Chris Foster. Boost Format library is acknowledged transitively
since it had some influence on tinyformat.
Some ideas used in the implementation are borrowed from `Loki
<http://loki-lib.sourceforge.net/>`_ SafeFormat and `Diagnostic API
<https://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in
`Clang <https://clang.llvm.org/>`_.
Format string syntax and the documentation are based on Python's `str.format
<https://docs.python.org/3/library/stdtypes.html#str.format>`_.
Thanks `Doug Turnbull <https://github.com/softwaredoug>`_ for his valuable
comments and contribution to the design of the type-safe API and
`Gregory Czajkowski <https://github.com/gcflymoto>`_ for implementing binary
formatting. Thanks `Ruslan Baratov <https://github.com/ruslo>`_ for comprehensive
`comparison of integer formatting algorithms <https://github.com/ruslo/int-dec-format-tests>`_
and useful comments regarding performance, `Boris Kaul <https://github.com/localvoid>`_ for
`C++ counting digits benchmark <https://github.com/localvoid/cxx-benchmark-count-digits>`_.
Thanks to `CarterLi <https://github.com/CarterLi>`_ for contributing various
improvements to the code.

View file

@ -9,4 +9,5 @@ add_custom_target(doc
SOURCES api.rst syntax.rst usage.rst build.py conf.py _templates/layout.html) SOURCES api.rst syntax.rst usage.rst build.py conf.py _templates/layout.html)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
DESTINATION share/doc/fmt OPTIONAL) DESTINATION share/doc/fmt OPTIONAL
PATTERN ".doctrees" EXCLUDE)

View file

@ -6,14 +6,13 @@
<meta name="author" content="Victor Zverovich"> <meta name="author" content="Victor Zverovich">
<link rel="stylesheet" href="_static/fmt.css"> <link rel="stylesheet" href="_static/fmt.css">
{# Google Analytics #} {# Google Analytics #}
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-20116650-4"></script>
<script> <script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ window.dataLayer = window.dataLayer || [];
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date(); function gtag(){dataLayer.push(arguments);}
a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1; gtag('js', new Date());
a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); gtag('config', 'UA-20116650-4');
ga('create', 'UA-20116650-4', 'fmtlib.net');
ga('send', 'pageview');
</script> </script>
{% endblock %} {% endblock %}

View file

@ -9,10 +9,12 @@ The {fmt} library API consists of the following parts:
* :ref:`fmt/core.h <core-api>`: the core API providing argument handling * :ref:`fmt/core.h <core-api>`: the core API providing argument handling
facilities and a lightweight subset of formatting functions facilities and a lightweight subset of formatting functions
* :ref:`fmt/format.h <format-api>`: the full format API providing compile-time * :ref:`fmt/format.h <format-api>`: the full format API providing compile-time
format string checks, output iterator and user-defined type support format string checks, wide string, output iterator and user-defined type
support
* :ref:`fmt/ranges.h <ranges-api>`: additional formatting support for ranges * :ref:`fmt/ranges.h <ranges-api>`: additional formatting support for ranges
and tuples and tuples
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting * :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
* :ref:`fmt/compile.h <compile-api>`: format string compilation
* :ref:`fmt/ostream.h <ostream-api>`: ``std::ostream`` support * :ref:`fmt/ostream.h <ostream-api>`: ``std::ostream`` support
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting * :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
@ -43,7 +45,7 @@ participate in an overload resolution if the latter is not a string.
.. _format: .. _format:
.. doxygenfunction:: format(const S&, Args&&...) .. doxygenfunction:: format(const S&, Args&&...)
.. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<Char>>) .. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<type_identity_t<Char>>>)
.. _print: .. _print:
@ -68,6 +70,9 @@ Argument Lists
.. doxygenclass:: fmt::format_arg_store .. doxygenclass:: fmt::format_arg_store
:members: :members:
.. doxygenclass:: fmt::dynamic_format_arg_store
:members:
.. doxygenclass:: fmt::basic_format_args .. doxygenclass:: fmt::basic_format_args
:members: :members:
@ -96,7 +101,7 @@ locale::
#include <locale> #include <locale>
std::locale::global(std::locale("en_US.UTF-8")); std::locale::global(std::locale("en_US.UTF-8"));
auto s = fmt::format("{:n}", 1000000); // s == "1,000,000" auto s = fmt::format("{:L}", 1000000); // s == "1,000,000"
.. _format-api: .. _format-api:
@ -104,7 +109,7 @@ Format API
========== ==========
``fmt/format.h`` defines the full format API providing compile-time format ``fmt/format.h`` defines the full format API providing compile-time format
string checks, output iterator and user-defined type support. string checks, wide string, output iterator and user-defined type support.
Compile-time Format String Checks Compile-time Format String Checks
--------------------------------- ---------------------------------
@ -132,6 +137,7 @@ template and implement ``parse`` and ``format`` methods::
// Parses format specifications of the form ['f' | 'e']. // Parses format specifications of the form ['f' | 'e'].
constexpr auto parse(format_parse_context& ctx) { constexpr auto parse(format_parse_context& ctx) {
// auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) // c++11
// [ctx.begin(), ctx.end()) is a character range that contains a part of // [ctx.begin(), ctx.end()) is a character range that contains a part of
// the format string starting from the format specifications to be parsed, // the format string starting from the format specifications to be parsed,
// e.g. in // e.g. in
@ -159,6 +165,7 @@ template and implement ``parse`` and ``format`` methods::
// stored in this formatter. // stored in this formatter.
template <typename FormatContext> template <typename FormatContext>
auto format(const point& p, FormatContext& ctx) { auto format(const point& p, FormatContext& ctx) {
// auto format(const point &p, FormatContext &ctx) -> decltype(ctx.out()) // c++11
// ctx.out() is an output iterator to write to. // ctx.out() is an output iterator to write to.
return format_to( return format_to(
ctx.out(), ctx.out(),
@ -178,8 +185,7 @@ example::
enum class color {red, green, blue}; enum class color {red, green, blue};
template <> template <> struct fmt::formatter<color>: formatter<string_view> {
struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>. // parse is inherited from formatter<string_view>.
template <typename FormatContext> template <typename FormatContext>
auto format(color c, FormatContext& ctx) { auto format(color c, FormatContext& ctx) {
@ -193,6 +199,15 @@ example::
} }
}; };
Since ``parse`` is inherited from ``formatter<string_view>`` it will recognize
all string format specifications, for example
.. code-block:: c++
fmt::format("{:>10}", color::blue)
will return ``" blue"``.
You can also write a formatter for a hierarchy of classes:: You can also write a formatter for a hierarchy of classes::
#include <type_traits> #include <type_traits>
@ -229,7 +244,7 @@ Output Iterator Support
----------------------- -----------------------
.. doxygenfunction:: fmt::format_to(OutputIt, const S&, Args&&...) .. doxygenfunction:: fmt::format_to(OutputIt, const S&, Args&&...)
.. doxygenfunction:: fmt::format_to_n(OutputIt, std::size_t, string_view, Args&&...) .. doxygenfunction:: fmt::format_to_n(OutputIt, size_t, const S&, const Args&...)
.. doxygenstruct:: fmt::format_to_n_result .. doxygenstruct:: fmt::format_to_n_result
:members: :members:
@ -238,9 +253,9 @@ Literal-based API
The following user-defined literals are defined in ``fmt/format.h``. The following user-defined literals are defined in ``fmt/format.h``.
.. doxygenfunction:: operator""_format(const char *, std::size_t) .. doxygenfunction:: operator""_format(const char *, size_t)
.. doxygenfunction:: operator""_a(const char *, std::size_t) .. doxygenfunction:: operator""_a(const char *, size_t)
Utilities Utilities
--------- ---------
@ -259,7 +274,10 @@ Utilities
.. doxygenfunction:: fmt::join(const Range&, string_view) .. doxygenfunction:: fmt::join(const Range&, string_view)
.. doxygenfunction:: fmt::join(It, It, string_view) .. doxygenfunction:: fmt::join(It, Sentinel, string_view)
.. doxygenclass:: fmt::detail::buffer
:members:
.. doxygenclass:: fmt::basic_memory_buffer .. doxygenclass:: fmt::basic_memory_buffer
:protected-members: :protected-members:
@ -317,50 +335,6 @@ arguments, the container that stores pointers to them will be allocated using
the default allocator. Also floating-point formatting falls back on ``sprintf`` the default allocator. Also floating-point formatting falls back on ``sprintf``
which may do allocations. which may do allocations.
Custom Formatting of Built-in Types
-----------------------------------
It is possible to change the way arguments are formatted by providing a
custom argument formatter class::
using arg_formatter = fmt::arg_formatter<fmt::buffer_range<char>>;
// A custom argument formatter that formats negative integers as unsigned
// with the ``x`` format specifier.
class custom_arg_formatter : public arg_formatter {
public:
custom_arg_formatter(fmt::format_context& ctx,
fmt::format_parse_context* parse_ctx = nullptr,
fmt::format_specs* spec = nullptr)
: arg_formatter(ctx, parse_ctx, spec) {}
using arg_formatter::operator();
auto operator()(int value) {
if (specs() && specs()->type == 'x')
return (*this)(static_cast<unsigned>(value)); // convert to unsigned and format
return arg_formatter::operator()(value);
}
};
std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) {
fmt::memory_buffer buffer;
// Pass custom argument formatter as a template arg to vformat_to.
fmt::vformat_to<custom_arg_formatter>(buffer, format_str, args);
return fmt::to_string(buffer);
}
template <typename ...Args>
inline std::string custom_format(
fmt::string_view format_str, const Args&... args) {
return custom_vformat(format_str, fmt::make_format_args(args...));
}
std::string s = custom_format("{:x}", -42); // s == "ffffffd6"
.. doxygenclass:: fmt::arg_formatter
:members:
.. _ranges-api: .. _ranges-api:
Ranges and Tuple Formatting Ranges and Tuple Formatting
@ -399,11 +373,26 @@ formatting::
std::time_t t = std::time(nullptr); std::time_t t = std::time(nullptr);
// Prints "The date is 2016-04-29." (with the current date) // Prints "The date is 2016-04-29." (with the current date)
fmt::print("The date is {:%Y-%m-%d}.", *std::localtime(&t)); fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t));
The format string syntax is described in the documentation of The format string syntax is described in the documentation of
`strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_. `strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_.
.. _compile-api:
Format string compilation
=========================
``fmt/compile.h`` provides format string compilation support. Format strings
are parsed at compile time and converted into efficient formatting code. This
supports arguments of built-in and string types as well as user-defined types
with ``constexpr`` ``parse`` functions in their ``formatter`` specializations.
Format string compilation can generate more binary code compared to the default
API and is only recommended in places where formatting is a performance
bottleneck.
.. doxygendefine:: FMT_COMPILE
.. _ostream-api: .. _ostream-api:
``std::ostream`` Support ``std::ostream`` Support
@ -448,3 +437,19 @@ argument type doesn't match its format specification.
.. doxygenfunction:: fprintf(std::basic_ostream<Char>&, const S&, const Args&...) .. doxygenfunction:: fprintf(std::basic_ostream<Char>&, const S&, const Args&...)
.. doxygenfunction:: sprintf(const S&, const Args&...) .. doxygenfunction:: sprintf(const S&, const Args&...)
Compatibility with C++20 ``std::format``
========================================
{fmt} implements nearly all of the `C++20 formatting library
<https://en.cppreference.com/w/cpp/utility/format>`_ with the following
differences:
* Names are defined in the ``fmt`` namespace instead of ``std`` to avoid
collisions with standard library implementations.
* The ``'L'`` format specifier cannot be combined with presentation specifiers
yet.
* Width calculation doesn't use grapheme clusterization. The latter has been
implemented in a separate branch but hasn't been integrated yet.
* Chrono formatting doesn't support C++20 date types since they are not provided
by standard library implementations.

View file

@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
from distutils.version import LooseVersion from distutils.version import LooseVersion
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0'] versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3']
def pip_install(package, commit=None, **kwargs): def pip_install(package, commit=None, **kwargs):
"Install package using pip." "Install package using pip."
@ -74,8 +74,8 @@ def build_docs(version='dev', **kwargs):
GENERATE_MAN = NO GENERATE_MAN = NO
GENERATE_RTF = NO GENERATE_RTF = NO
CASE_SENSE_NAMES = NO CASE_SENSE_NAMES = NO
INPUT = {0}/core.h {0}/format.h {0}/os.h {0}/ostream.h \ INPUT = {0}/core.h {0}/compile.h {0}/format.h {0}/os.h \
{0}/printf.h {0}/time.h {0}/ostream.h {0}/printf.h {0}/time.h
QUIET = YES QUIET = YES
JAVADOC_AUTOBRIEF = YES JAVADOC_AUTOBRIEF = YES
AUTOLINK_SUPPORT = NO AUTOLINK_SUPPORT = NO

View file

@ -22,7 +22,7 @@ Format API
---------- ----------
The format API is similar in spirit to the C ``printf`` family of function but The format API is similar in spirit to the C ``printf`` family of function but
is safer, simpler and serveral times `faster is safer, simpler and several times `faster
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_ <http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_
than common standard library implementations. than common standard library implementations.
The `format string syntax <syntax.html>`_ is similar to the one used by The `format string syntax <syntax.html>`_ is similar to the one used by

View file

@ -79,8 +79,8 @@ The general form of a *standard format specifier* is:
fill: <a character other than '{' or '}'> fill: <a character other than '{' or '}'>
align: "<" | ">" | "^" align: "<" | ">" | "^"
sign: "+" | "-" | " " sign: "+" | "-" | " "
width: `integer` | "{" `arg_id` "}" width: `integer` | "{" [`arg_id`] "}"
precision: `integer` | "{" `arg_id` "}" precision: `integer` | "{" [`arg_id`] "}"
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "L" | "p" | "s" type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "L" | "p" | "s"
int_type: "b" | "B" | "d" | "o" | "x" | "X" int_type: "b" | "B" | "d" | "o" | "x" | "X"

View file

@ -48,7 +48,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
// From fits in To without any problem. // From fits in To without any problem.
} else { } else {
// From does not always fit in To, resort to a dynamic check. // From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) { if (from < (T::min)() || from > (T::max)()) {
// outside range. // outside range.
ec = 1; ec = 1;
return {}; return {};
@ -74,7 +74,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
if (F::is_signed && !T::is_signed) { if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed! // From may be negative, not allowed!
if (fmt::internal::is_negative(from)) { if (fmt::detail::is_negative(from)) {
ec = 1; ec = 1;
return {}; return {};
} }
@ -84,7 +84,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
// yes, From always fits in To. // yes, From always fits in To.
} else { } else {
// from may not fit in To, we have to do a dynamic check // from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) { if (from > static_cast<From>((T::max)())) {
ec = 1; ec = 1;
return {}; return {};
} }
@ -97,7 +97,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
// yes, From always fits in To. // yes, From always fits in To.
} else { } else {
// from may not fit in To, we have to do a dynamic check // from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) { if (from > static_cast<From>((T::max)())) {
// outside range. // outside range.
ec = 1; ec = 1;
return {}; return {};
@ -141,7 +141,7 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
// catch the only happy case // catch the only happy case
if (std::isfinite(from)) { if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) { if (from >= T::lowest() && from <= (T::max)()) {
return static_cast<To>(from); return static_cast<To>(from);
} }
// not within range. // not within range.
@ -195,12 +195,13 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
} }
// multiply with Factor::num without overflow or underflow // multiply with Factor::num without overflow or underflow
if (Factor::num != 1) { if (Factor::num != 1) {
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num; const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
if (count > max1) { if (count > max1) {
ec = 1; ec = 1;
return {}; return {};
} }
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num; const auto min1 =
(std::numeric_limits<IntermediateRep>::min)() / Factor::num;
if (count < min1) { if (count < min1) {
ec = 1; ec = 1;
return {}; return {};
@ -269,7 +270,7 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// multiply with Factor::num without overflow or underflow // multiply with Factor::num without overflow or underflow
if (Factor::num != 1) { if (Factor::num != 1) {
constexpr auto max1 = internal::max_value<IntermediateRep>() / constexpr auto max1 = detail::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num); static_cast<IntermediateRep>(Factor::num);
if (count > max1) { if (count > max1) {
ec = 1; ec = 1;
@ -306,12 +307,12 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// Usage: f FMT_NOMACRO() // Usage: f FMT_NOMACRO()
#define FMT_NOMACRO #define FMT_NOMACRO
namespace internal { namespace detail {
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); } inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); } inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); } inline null<> gmtime_s(...) { return null<>(); }
} // namespace internal } // namespace detail
// Thread-safe replacement for std::localtime // Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) { inline std::tm localtime(std::time_t time) {
@ -322,22 +323,22 @@ inline std::tm localtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {} dispatcher(std::time_t t) : time_(t) {}
bool run() { bool run() {
using namespace fmt::internal; using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_)); return handle(localtime_r(&time_, &tm_));
} }
bool handle(std::tm* tm) { return tm != nullptr; } bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) { bool handle(detail::null<>) {
using namespace fmt::internal; using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_)); return fallback(localtime_s(&tm_, &time_));
} }
bool fallback(int res) { return res == 0; } bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER #if !FMT_MSC_VER
bool fallback(internal::null<>) { bool fallback(detail::null<>) {
using namespace fmt::internal; using namespace fmt::detail;
std::tm* tm = std::localtime(&time_); std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm; if (tm) tm_ = *tm;
return tm != nullptr; return tm != nullptr;
@ -359,21 +360,21 @@ inline std::tm gmtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {} dispatcher(std::time_t t) : time_(t) {}
bool run() { bool run() {
using namespace fmt::internal; using namespace fmt::detail;
return handle(gmtime_r(&time_, &tm_)); return handle(gmtime_r(&time_, &tm_));
} }
bool handle(std::tm* tm) { return tm != nullptr; } bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) { bool handle(detail::null<>) {
using namespace fmt::internal; using namespace fmt::detail;
return fallback(gmtime_s(&tm_, &time_)); return fallback(gmtime_s(&tm_, &time_));
} }
bool fallback(int res) { return res == 0; } bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER #if !FMT_MSC_VER
bool fallback(internal::null<>) { bool fallback(detail::null<>) {
std::tm* tm = std::gmtime(&time_); std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm; if (tm) tm_ = *tm;
return tm != nullptr; return tm != nullptr;
@ -386,17 +387,17 @@ inline std::tm gmtime(std::time_t time) {
return gt.tm_; return gt.tm_;
} }
namespace internal { namespace detail {
inline std::size_t strftime(char* str, std::size_t count, const char* format, inline size_t strftime(char* str, size_t count, const char* format,
const std::tm* time) { const std::tm* time) {
return std::strftime(str, count, format, time); return std::strftime(str, count, format, time);
} }
inline std::size_t strftime(wchar_t* str, std::size_t count, inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
const wchar_t* format, const std::tm* time) { const std::tm* time) {
return std::wcsftime(str, count, format, time); return std::wcsftime(str, count, format, time);
} }
} // namespace internal } // namespace detail
template <typename Char> struct formatter<std::tm, Char> { template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext> template <typename ParseContext>
@ -405,7 +406,7 @@ template <typename Char> struct formatter<std::tm, Char> {
if (it != ctx.end() && *it == ':') ++it; if (it != ctx.end() && *it == ':') ++it;
auto end = it; auto end = it;
while (end != ctx.end() && *end != '}') ++end; while (end != ctx.end() && *end != '}') ++end;
tm_format.reserve(internal::to_unsigned(end - it + 1)); tm_format.reserve(detail::to_unsigned(end - it + 1));
tm_format.append(it, end); tm_format.append(it, end);
tm_format.push_back('\0'); tm_format.push_back('\0');
return end; return end;
@ -414,11 +415,10 @@ template <typename Char> struct formatter<std::tm, Char> {
template <typename FormatContext> template <typename FormatContext>
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
std::size_t start = buf.size(); size_t start = buf.size();
for (;;) { for (;;) {
std::size_t size = buf.capacity() - start; size_t size = buf.capacity() - start;
std::size_t count = size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm);
internal::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) { if (count != 0) {
buf.resize(start + count); buf.resize(start + count);
break; break;
@ -430,7 +430,7 @@ template <typename Char> struct formatter<std::tm, Char> {
// https://github.com/fmtlib/fmt/issues/367 // https://github.com/fmtlib/fmt/issues/367
break; break;
} }
const std::size_t MIN_GROWTH = 10; const size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
} }
return std::copy(buf.begin(), buf.end(), ctx.out()); return std::copy(buf.begin(), buf.end(), ctx.out());
@ -439,7 +439,7 @@ template <typename Char> struct formatter<std::tm, Char> {
basic_memory_buffer<Char> tm_format; basic_memory_buffer<Char> tm_format;
}; };
namespace internal { namespace detail {
template <typename Period> FMT_CONSTEXPR const char* get_units() { template <typename Period> FMT_CONSTEXPR const char* get_units() {
return nullptr; return nullptr;
} }
@ -768,19 +768,25 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format, return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
val); val);
} }
template <typename Char, typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
return std::copy(unit.begin(), unit.end(), out);
}
template <typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
// This works when wchar_t is UTF-32 because units only contain characters
// that have the same representation in UTF-16 and UTF-32.
utf8_to_utf16 u(unit);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
template <typename Char, typename Period, typename OutputIt> template <typename Char, typename Period, typename OutputIt>
OutputIt format_duration_unit(OutputIt out) { OutputIt format_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) { if (const char* unit = get_units<Period>())
string_view s(unit); return copy_unit(string_view(unit), out, Char());
if (const_check(std::is_same<Char, wchar_t>())) {
utf8_to_utf16 u(s);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
return std::copy(s.begin(), s.end(), out);
}
const Char num_f[] = {'[', '{', '}', ']', 's', 0}; const Char num_f[] = {'[', '{', '}', ']', 's', 0};
if (Period::den == 1) return format_to(out, num_f, Period::num); if (const_check(Period::den == 1)) return format_to(out, num_f, Period::num);
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
return format_to(out, num_def_f, Period::num, Period::den); return format_to(out, num_def_f, Period::num, Period::den);
} }
@ -874,9 +880,9 @@ struct chrono_formatter {
if (isnan(value)) return write_nan(); if (isnan(value)) return write_nan();
uint32_or_64_or_128_t<int> n = uint32_or_64_or_128_t<int> n =
to_unsigned(to_nonnegative_int(value, max_value<int>())); to_unsigned(to_nonnegative_int(value, max_value<int>()));
int num_digits = internal::count_digits(n); int num_digits = detail::count_digits(n);
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits); out = format_decimal<char_type>(out, n, num_digits).end;
} }
void write_nan() { std::copy_n("nan", 3, out); } void write_nan() { std::copy_n("nan", 3, out); }
@ -1004,14 +1010,14 @@ struct chrono_formatter {
out = format_duration_unit<char_type, Period>(out); out = format_duration_unit<char_type, Period>(out);
} }
}; };
} // namespace internal } // namespace detail
template <typename Rep, typename Period, typename Char> template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> { struct formatter<std::chrono::duration<Rep, Period>, Char> {
private: private:
basic_format_specs<Char> specs; basic_format_specs<Char> specs;
int precision; int precision;
using arg_ref_type = internal::arg_ref<Char>; using arg_ref_type = detail::arg_ref<Char>;
arg_ref_type width_ref; arg_ref_type width_ref;
arg_ref_type precision_ref; arg_ref_type precision_ref;
mutable basic_string_view<Char> format_str; mutable basic_string_view<Char> format_str;
@ -1032,7 +1038,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
return arg_ref_type(arg_id); return arg_ref_type(arg_id);
} }
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) { FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
return arg_ref_type(context.next_arg_id()); return arg_ref_type(context.next_arg_id());
} }
@ -1062,17 +1068,17 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
auto begin = ctx.begin(), end = ctx.end(); auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin}; if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str}; spec_handler handler{*this, ctx, format_str};
begin = internal::parse_align(begin, end, handler); begin = detail::parse_align(begin, end, handler);
if (begin == end) return {begin, begin}; if (begin == end) return {begin, begin};
begin = internal::parse_width(begin, end, handler); begin = detail::parse_width(begin, end, handler);
if (begin == end) return {begin, begin}; if (begin == end) return {begin, begin};
if (*begin == '.') { if (*begin == '.') {
if (std::is_floating_point<Rep>::value) if (std::is_floating_point<Rep>::value)
begin = internal::parse_precision(begin, end, handler); begin = detail::parse_precision(begin, end, handler);
else else
handler.on_error("precision not allowed for this argument type"); handler.on_error("precision not allowed for this argument type");
} }
end = parse_chrono_format(begin, end, internal::chrono_format_checker()); end = parse_chrono_format(begin, end, detail::chrono_format_checker());
return {begin, end}; return {begin, end};
} }
@ -1083,7 +1089,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
-> decltype(ctx.begin()) { -> decltype(ctx.begin()) {
auto range = do_parse(ctx); auto range = do_parse(ctx);
format_str = basic_string_view<Char>( format_str = basic_string_view<Char>(
&*range.begin, internal::to_unsigned(range.end - range.begin)); &*range.begin, detail::to_unsigned(range.end - range.begin));
return range.end; return range.end;
} }
@ -1094,23 +1100,21 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
// is not specified. // is not specified.
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf); auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>; detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref,
internal::basic_writer<range> w(range(ctx.out())); ctx);
internal::handle_dynamic_spec<internal::width_checker>(specs.width, detail::handle_dynamic_spec<detail::precision_checker>(precision,
width_ref, ctx); precision_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx);
if (begin == end || *begin == '}') { if (begin == end || *begin == '}') {
out = internal::format_duration_value<Char>(out, d.count(), precision); out = detail::format_duration_value<Char>(out, d.count(), precision);
internal::format_duration_unit<Char, Period>(out); detail::format_duration_unit<Char, Period>(out);
} else { } else {
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f( detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d); ctx, out, d);
f.precision = precision; f.precision = precision;
parse_chrono_format(begin, end, f); parse_chrono_format(begin, end, f);
} }
w.write(buf.data(), buf.size(), specs); return detail::write(
return w.out(); ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
} }
}; };

View file

@ -198,7 +198,7 @@ struct rgb {
uint8_t b; uint8_t b;
}; };
namespace internal { namespace detail {
// color is a struct of either a rgb color or a terminal color. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type {
@ -221,7 +221,7 @@ struct color_type {
uint32_t rgb_color; uint32_t rgb_color;
} value; } value;
}; };
} // namespace internal } // namespace detail
// Experimental text formatting support. // Experimental text formatting support.
class text_style { class text_style {
@ -298,11 +298,11 @@ class text_style {
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
return static_cast<uint8_t>(ems) != 0; return static_cast<uint8_t>(ems) != 0;
} }
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT { FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
FMT_ASSERT(has_foreground(), "no foreground specified for this style"); FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color; return foreground_color;
} }
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT { FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
FMT_ASSERT(has_background(), "no background specified for this style"); FMT_ASSERT(has_background(), "no background specified for this style");
return background_color; return background_color;
} }
@ -313,7 +313,7 @@ class text_style {
private: private:
FMT_CONSTEXPR text_style(bool is_foreground, FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT detail::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(), : set_foreground_color(),
set_background_color(), set_background_color(),
ems() { ems() {
@ -326,23 +326,23 @@ class text_style {
} }
} }
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground) friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
FMT_NOEXCEPT; FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background) friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
FMT_NOEXCEPT; FMT_NOEXCEPT;
internal::color_type foreground_color; detail::color_type foreground_color;
internal::color_type background_color; detail::color_type background_color;
bool set_foreground_color; bool set_foreground_color;
bool set_background_color; bool set_background_color;
emphasis ems; emphasis ems;
}; };
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT { FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground); return text_style(/*is_foreground=*/true, foreground);
} }
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT { FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background); return text_style(/*is_foreground=*/false, background);
} }
@ -350,21 +350,21 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs; return text_style(lhs) | rhs;
} }
namespace internal { namespace detail {
template <typename Char> struct ansi_color_escape { template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color, FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
const char* esc) FMT_NOEXCEPT { const char* esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code // If we have a terminal color, we need to output another escape code
// sequence. // sequence.
if (!text_color.is_rgb) { if (!text_color.is_rgb) {
bool is_background = esc == internal::data::background_color; bool is_background = esc == detail::data::background_color;
uint32_t value = text_color.value.term_color; uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with // Background ASCII codes are the same as the foreground ones but with
// 10 more. // 10 more.
if (is_background) value += 10u; if (is_background) value += 10u;
std::size_t index = 0; size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('['); buffer[index++] = static_cast<Char>('[');
@ -398,7 +398,7 @@ template <typename Char> struct ansi_color_escape {
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough)) if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9; em_codes[3] = 9;
std::size_t index = 0; size_t index = 0;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) continue; if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
@ -429,14 +429,14 @@ template <typename Char> struct ansi_color_escape {
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
internal::color_type foreground) FMT_NOEXCEPT { detail::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::foreground_color); return ansi_color_escape<Char>(foreground, detail::data::foreground_color);
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
internal::color_type background) FMT_NOEXCEPT { detail::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::background_color); return ansi_color_escape<Char>(background, detail::data::background_color);
} }
template <typename Char> template <typename Char>
@ -455,11 +455,11 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
} }
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT { template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::reset_color, stream); fputs(detail::data::reset_color, stream);
} }
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT { template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::wreset_color, stream); fputs(detail::data::wreset_color, stream);
} }
template <typename Char> template <typename Char>
@ -476,33 +476,31 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
bool has_style = false; bool has_style = false;
if (ts.has_emphasis()) { if (ts.has_emphasis()) {
has_style = true; has_style = true;
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis()); auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end()); buf.append(emphasis.begin(), emphasis.end());
} }
if (ts.has_foreground()) { if (ts.has_foreground()) {
has_style = true; has_style = true;
auto foreground = auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
internal::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end()); buf.append(foreground.begin(), foreground.end());
} }
if (ts.has_background()) { if (ts.has_background()) {
has_style = true; has_style = true;
auto background = auto background = detail::make_background_color<Char>(ts.get_background());
internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end()); buf.append(background.begin(), background.end());
} }
internal::vformat_to(buf, format_str, args); detail::vformat_to(buf, format_str, args);
if (has_style) internal::reset_color<Char>(buf); if (has_style) detail::reset_color<Char>(buf);
} }
} // namespace internal } // namespace detail
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format, void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char>> args) { basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format), args); detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0)); buf.push_back(Char(0));
internal::fputs(buf.data(), f); detail::fputs(buf.data(), f);
} }
/** /**
@ -513,10 +511,10 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)> FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str, void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
internal::check_format_string<Args...>(format_str); detail::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S>>; using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...}; format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as)); vprint(f, ts, format_str, basic_format_args<context>(as));
@ -530,7 +528,7 @@ void print(std::FILE* f, const text_style& ts, const S& format_str,
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)> FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) { void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...); return print(stdout, ts, format_str, args...);
} }
@ -540,7 +538,7 @@ inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str, const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args); detail::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf); return fmt::to_string(buf);
} }
@ -560,7 +558,7 @@ template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
return vformat(ts, to_string_view(format_str), return vformat(ts, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...)); detail::make_args_checked<Args...>(format_str, args...));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View file

@ -13,7 +13,33 @@
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace detail {
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/**
\rst
Converts a string literal *s* into a format string that will be parsed at
compile time and converted into efficient formatting code. Requires C++17
``constexpr if`` compiler support.
**Example**::
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
return value;
}
// Part of a compiled format string. It can be either literal text or a // Part of a compiled format string. It can be either literal text or a
// replacement field. // replacement field.
@ -62,13 +88,15 @@ template <typename Char> struct part_counter {
if (begin != end) ++num_parts; if (begin != end) ++num_parts;
} }
FMT_CONSTEXPR void on_arg_id() { ++num_parts; } FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; } FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; } FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
return ++num_parts, 0;
}
FMT_CONSTEXPR void on_replacement_field(const Char*) {} FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) { const Char* end) {
// Find the matching brace. // Find the matching brace.
unsigned brace_counter = 0; unsigned brace_counter = 0;
@ -116,25 +144,28 @@ class format_string_compiler : public error_handler {
handler_(part::make_text({begin, to_unsigned(end - begin)})); handler_(part::make_text({begin, to_unsigned(end - begin)}));
} }
FMT_CONSTEXPR void on_arg_id() { FMT_CONSTEXPR int on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id()); part_ = part::make_arg_index(parse_context_.next_arg_id());
return 0;
} }
FMT_CONSTEXPR void on_arg_id(int id) { FMT_CONSTEXPR int on_arg_id(int id) {
parse_context_.check_arg_id(id); parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id); part_ = part::make_arg_index(id);
return 0;
} }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) { FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id); part_ = part::make_arg_name(id);
return 0;
} }
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) { FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
part_.arg_id_end = ptr; part_.arg_id_end = ptr;
handler_(part_); handler_(part_);
} }
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) { const Char* end) {
auto repl = typename part::replacement(); auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler( dynamic_specs_handler<basic_format_parse_context<Char>> handler(
@ -160,23 +191,24 @@ FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
format_string_compiler<Char, PartHandler>(format_str, handler)); format_string_compiler<Char, PartHandler>(format_str, handler));
} }
template <typename Range, typename Context, typename Id> template <typename OutputIt, typename Context, typename Id>
void format_arg( void format_arg(
basic_format_parse_context<typename Range::value_type>& parse_ctx, basic_format_parse_context<typename Context::char_type>& parse_ctx,
Context& ctx, Id arg_id) { Context& ctx, Id arg_id) {
ctx.advance_to( ctx.advance_to(visit_format_arg(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id))); arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
ctx.arg(arg_id)));
} }
// vformat_to is defined in a subnamespace to prevent ADL. // vformat_to is defined in a subnamespace to prevent ADL.
namespace cf { namespace cf {
template <typename Context, typename Range, typename CompiledFormat> template <typename Context, typename OutputIt, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args) auto vformat_to(OutputIt out, CompiledFormat& cf,
-> typename Context::iterator { basic_format_args<Context> args) -> typename Context::iterator {
using char_type = typename Context::char_type; using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx( basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_)); to_string_view(cf.format_str_));
Context ctx(out.begin(), args); Context ctx(out, args);
const auto& parts = cf.parts(); const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts); for (auto part_it = std::begin(parts); part_it != std::end(parts);
@ -197,12 +229,12 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
case format_part_t::kind::arg_index: case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end); advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index); detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
break; break;
case format_part_t::kind::arg_name: case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end); advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.str); detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
break; break;
case format_part_t::kind::replacement: { case format_part_t::kind::replacement: {
@ -226,7 +258,9 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
advance_to(parse_ctx, part.arg_id_end); advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to( ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg)); visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
ctx, nullptr, &specs),
arg));
break; break;
} }
} }
@ -240,7 +274,7 @@ struct basic_compiled_format {};
template <typename S, typename = void> template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format { struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>; using char_type = char_t<S>;
using parts_container = std::vector<internal::format_part<char_type>>; using parts_container = std::vector<detail::format_part<char_type>>;
parts_container compiled_parts; parts_container compiled_parts;
@ -305,7 +339,7 @@ struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
const parts_container& parts() const { const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts = static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>( compile_to_parts<char_type, num_format_parts>(
internal::to_string_view(S())); detail::to_string_view(S()));
return compiled_parts.data; return compiled_parts.data;
} }
}; };
@ -318,8 +352,8 @@ class compiled_format : private compiled_format_base<S> {
private: private:
basic_string_view<char_type> format_str_; basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat> template <typename Context, typename OutputIt, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf, friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) -> basic_format_args<Context> args) ->
typename Context::iterator; typename Context::iterator;
@ -359,8 +393,7 @@ template <typename Char> struct text {
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const { OutputIt format(OutputIt out, const Args&...) const {
// TODO: reserve return write<Char>(out, data);
return copy_str<Char>(data.begin(), data.end(), out);
} }
}; };
@ -373,33 +406,6 @@ constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
return {{&s[pos], size}}; return {{&s[pos], size}};
} }
template <typename Char, typename OutputIt, typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
OutputIt format_default(OutputIt out, T value) {
// TODO: reserve
format_int fi(value);
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
writer w(out);
w.write(value);
return w.out();
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) {
*out++ = value;
return out;
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, const Char* value) {
auto length = std::char_traits<Char>::length(value);
return copy_str<Char>(value, value + length, out);
}
// A replacement field that refers to argument N. // A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field { template <typename Char, typename T, int N> struct field {
using char_type = Char; using char_type = Char;
@ -408,13 +414,30 @@ template <typename Char, typename T, int N> struct field {
OutputIt format(OutputIt out, const Args&... args) const { OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`. // This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...); const T& arg = get<N>(args...);
return format_default<Char>(out, arg); return write<Char>(out, arg);
} }
}; };
template <typename Char, typename T, int N> template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {}; struct is_compiled_format<field<Char, T, N>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
using char_type = Char;
mutable formatter<T, Char> fmt;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
basic_format_context<OutputIt, Char> ctx(out, {});
return fmt.format(arg, ctx);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat { template <typename L, typename R> struct concat {
L lhs; L lhs;
R rhs; R rhs;
@ -450,7 +473,8 @@ constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S> template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) { constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS != to_string_view(format_str).size()) { if constexpr (POS !=
basic_string_view<typename S::char_type>(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str); constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>, if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>()) unknown_format>())
@ -462,6 +486,21 @@ constexpr auto parse_tail(T head, S format_str) {
} }
} }
template <typename T, typename Char> struct parse_specs_result {
formatter<T, Char> fmt;
size_t end;
};
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos) {
str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + (end - str.data()) + 1};
}
// Compiles a non-empty format string and returns the compiled representation // Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input. // or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S> template <typename Args, size_t POS, int ID, typename S>
@ -475,12 +514,13 @@ constexpr auto compile_format_string(S format_str) {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') { } else if constexpr (str[POS + 1] == '}') {
using type = get_type<ID, Args>; using type = get_type<ID, Args>;
if constexpr (std::is_same<type, int>::value) { return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(), format_str);
format_str); } else if constexpr (str[POS + 1] == ':') {
} else { using type = get_type<ID, Args>;
return unknown_format(); constexpr auto result = parse_specs<type>(str, POS + 2);
} return parse_tail<Args, result.end, ID + 1>(
spec_field<char_type, type, ID>{result.fmt}, format_str);
} else { } else {
return unknown_format(); return unknown_format();
} }
@ -494,100 +534,130 @@ constexpr auto compile_format_string(S format_str) {
format_str); format_str);
} }
} }
#endif // __cpp_if_constexpr
} // namespace internal
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename... Args, typename S, template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)> FMT_ENABLE_IF(is_compile_string<S>::value ||
detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) { constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str; constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) { if constexpr (str.size() == 0) {
return internal::make_text(str, 0, 0); return detail::make_text(str, 0, 0);
} else { } else {
constexpr auto result = constexpr auto result =
internal::compile_format_string<internal::type_list<Args...>, 0, 0>( detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str); format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>, if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
internal::unknown_format>()) { detail::unknown_format>()) {
return internal::compiled_format<S, Args...>(to_string_view(format_str)); return detail::compiled_format<S, Args...>(to_string_view(format_str));
} else { } else {
return result; return result;
} }
} }
} }
#else
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
cf.format(std::back_inserter(buffer), args...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# else
template <typename... Args, typename S, template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)> FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> { constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
return internal::compiled_format<S, Args...>(to_string_view(format_str)); return detail::compiled_format<S, Args...>(to_string_view(format_str));
} }
# endif // __cpp_if_constexpr #endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
// Compiles the format string which must be a string literal. // Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N> template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N]) auto compile(const Char (&format_str)[N])
-> internal::compiled_format<const Char*, Args...> { -> detail::compiled_format<const Char*, Args...> {
return internal::compiled_format<const Char*, Args...>( return detail::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1)); basic_string_view<Char>(format_str, N - 1));
} }
} // namespace detail
// DEPRECATED! use FMT_COMPILE instead.
template <typename... Args>
FMT_DEPRECATED auto compile(const Args&... args)
-> decltype(detail::compile(args...)) {
return detail::compile(args...);
}
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename CompiledFormat, typename... Args, template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type, typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format, FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
CompiledFormat>::value)> FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) { const Args&... args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>; detail::buffer<Char>& base = buffer;
using context = buffer_context<Char>; cf.format(std::back_inserter(base), args...);
internal::cf::vformat_to<context>(range(buffer), cf,
make_format_args<context>(args...));
return to_string(buffer); return to_string(buffer);
} }
template <typename OutputIt, typename CompiledFormat, typename... Args, template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format, FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using context = buffer_context<Char>;
detail::buffer<Char>& base = buffer;
detail::cf::vformat_to<context>(std::back_inserter(base), cf,
make_format_args<context>(args...));
return to_string(buffer);
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
constexpr basic_string_view<typename S::char_type> str = S();
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
return fmt::to_string(detail::first(args...));
constexpr auto compiled = detail::compile<Args...>(S());
return format(compiled, std::forward<Args>(args)...);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)> CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf, OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) { const Args&... args) {
using char_type = typename CompiledFormat::char_type; using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>; using context = format_context_t<OutputIt, char_type>;
return internal::cf::vformat_to<context>(range(out), cf, return detail::cf::vformat_to<context>(out, cf,
make_format_args<context>(args...)); make_format_args<context>(args...));
} }
template <typename OutputIt, typename CompiledFormat, typename... Args, template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
OutputIt format_to(OutputIt out, const S&, const Args&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
return format_to(out, compiled, args...);
}
template <
typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of<
detail::basic_compiled_format, CompiledFormat>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const CompiledFormat& cf, const CompiledFormat& cf,
const Args&... args) { const Args&... args) {
auto it = auto it =
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...); format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()}; return {it.base(), it.count()};
} }
template <typename CompiledFormat, typename... Args> template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) { size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(internal::counting_iterator(), cf, args...).count(); return format_to(detail::counting_iterator(), cf, args...).count();
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

File diff suppressed because it is too large Load diff

View file

@ -15,6 +15,7 @@
#include <cstdarg> #include <cstdarg>
#include <cstring> // for std::memmove #include <cstring> // for std::memmove
#include <cwchar> #include <cwchar>
#include <exception>
#include "format.h" #include "format.h"
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
@ -22,8 +23,16 @@
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
# if !defined(NOMINMAX) && !defined(WIN32_LEAN_AND_MEAN)
# define NOMINMAX
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# undef WIN32_LEAN_AND_MEAN
# undef NOMINMAX
# else
# include <windows.h>
# endif
# include <io.h> # include <io.h>
# include <windows.h>
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER
@ -33,15 +42,19 @@
// Dummy implementations of strerror_r and strerror_s called if corresponding // Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available. // system functions are not available.
inline fmt::internal::null<> strerror_r(int, char*, ...) { return {}; } inline fmt::detail::null<> strerror_r(int, char*, ...) { return {}; }
inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { return {}; } inline fmt::detail::null<> strerror_s(char*, size_t, ...) { return {}; }
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace detail {
FMT_FUNC void assert_fail(const char* file, int line, const char* message) { FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
print(stderr, "{}:{}: assertion failed: {}", file, line, message); // Use unchecked std::fprintf to avoid triggering another assertion when
std::abort(); // writing to stderr fails
std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
// Chosen instead of std::abort to satisfy Clang in CUDA mode during device
// code pass.
std::terminate();
} }
#ifndef _MSC_VER #ifndef _MSC_VER
@ -67,14 +80,14 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
// other - failure // other - failure
// Buffer should be at least of size 1. // Buffer should be at least of size 1.
FMT_FUNC int safe_strerror(int error_code, char*& buffer, FMT_FUNC int safe_strerror(int error_code, char*& buffer,
std::size_t buffer_size) FMT_NOEXCEPT { size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer"); FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer");
class dispatcher { class dispatcher {
private: private:
int error_code_; int error_code_;
char*& buffer_; char*& buffer_;
std::size_t buffer_size_; size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings. // A noop assignment operator to avoid bogus warnings.
void operator=(const dispatcher&) {} void operator=(const dispatcher&) {}
@ -97,7 +110,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
// Handle the case when strerror_r is not available. // Handle the case when strerror_r is not available.
FMT_MAYBE_UNUSED FMT_MAYBE_UNUSED
int handle(internal::null<>) { int handle(detail::null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_)); return fallback(strerror_s(buffer_, buffer_size_, error_code_));
} }
@ -111,7 +124,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
#if !FMT_MSC_VER #if !FMT_MSC_VER
// Fallback to strerror if strerror_r and strerror_s are not available. // Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::null<>) { int fallback(detail::null<>) {
errno = 0; errno = 0;
buffer_ = strerror(error_code_); buffer_ = strerror(error_code_);
return errno; return errno;
@ -119,7 +132,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
#endif #endif
public: public:
dispatcher(int err_code, char*& buf, std::size_t buf_size) dispatcher(int err_code, char*& buf, size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); } int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); }
@ -127,7 +140,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
return dispatcher(error_code, buffer, buffer_size).run(); return dispatcher(error_code, buffer, buffer_size).run();
} }
FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code, FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT { string_view message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into // Report error code making sure that the output fits into
// inline_buffer_size to avoid dynamic memory allocation and potential // inline_buffer_size to avoid dynamic memory allocation and potential
@ -136,20 +149,17 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
static const char SEP[] = ": "; static const char SEP[] = ": ";
static const char ERROR_STR[] = "error "; static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR. // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code); auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
if (internal::is_negative(error_code)) { if (detail::is_negative(error_code)) {
abs_value = 0 - abs_value; abs_value = 0 - abs_value;
++error_code_size; ++error_code_size;
} }
error_code_size += internal::to_unsigned(internal::count_digits(abs_value)); error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
internal::writer w(out); auto it = std::back_inserter(out);
if (message.size() <= inline_buffer_size - error_code_size) { if (message.size() <= inline_buffer_size - error_code_size)
w.write(message); format_to(it, "{}{}", message, SEP);
w.write(SEP); format_to(it, "{}{}", ERROR_STR, error_code);
}
w.write(ERROR_STR);
w.write(error_code);
assert(out.size() <= inline_buffer_size); assert(out.size() <= inline_buffer_size);
} }
@ -168,10 +178,10 @@ FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
size_t written = std::fwrite(ptr, size, count, stream); size_t written = std::fwrite(ptr, size, count, stream);
if (written < count) FMT_THROW(system_error(errno, "cannot write to file")); if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
} }
} // namespace internal } // namespace detail
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
namespace internal { namespace detail {
template <typename Locale> template <typename Locale>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
@ -194,18 +204,16 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()) return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
.decimal_point(); .decimal_point();
} }
} // namespace internal } // namespace detail
#else #else
template <typename Char> template <typename Char>
FMT_FUNC std::string internal::grouping_impl(locale_ref) { FMT_FUNC std::string detail::grouping_impl(locale_ref) {
return "\03"; return "\03";
} }
template <typename Char> template <typename Char> FMT_FUNC Char detail::thousands_sep_impl(locale_ref) {
FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
return FMT_STATIC_THOUSANDS_SEPARATOR; return FMT_STATIC_THOUSANDS_SEPARATOR;
} }
template <typename Char> template <typename Char> FMT_FUNC Char detail::decimal_point_impl(locale_ref) {
FMT_FUNC Char internal::decimal_point_impl(locale_ref) {
return '.'; return '.';
} }
#endif #endif
@ -222,9 +230,9 @@ FMT_FUNC void system_error::init(int err_code, string_view format_str,
base = std::runtime_error(to_string(buffer)); base = std::runtime_error(to_string(buffer));
} }
namespace internal { namespace detail {
template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) { template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) {
// fallback_uintptr is always stored in little endian. // fallback_uintptr is always stored in little endian.
int i = static_cast<int>(sizeof(void*)) - 1; int i = static_cast<int>(sizeof(void*)) - 1;
while (i > 0 && n.value[i] == 0) --i; while (i > 0 && n.value[i] == 0) --i;
@ -233,12 +241,27 @@ template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
} }
template <typename T> template <typename T>
const char basic_data<T>::digits[] = const typename basic_data<T>::digit_pair basic_data<T>::digits[] = {
"0001020304050607080910111213141516171819" {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'},
"2021222324252627282930313233343536373839" {'0', '5'}, {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'},
"4041424344454647484950515253545556575859" {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'},
"6061626364656667686970717273747576777879" {'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'},
"8081828384858687888990919293949596979899"; {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'},
{'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'},
{'3', '5'}, {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'},
{'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'},
{'4', '5'}, {'4', '6'}, {'4', '7'}, {'4', '8'}, {'4', '9'},
{'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'},
{'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'},
{'6', '5'}, {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'},
{'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'},
{'7', '5'}, {'7', '6'}, {'7', '7'}, {'7', '8'}, {'7', '9'},
{'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, {'8', '4'},
{'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'},
{'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
template <typename T> template <typename T>
const char basic_data<T>::hex_digits[] = "0123456789abcdef"; const char basic_data<T>::hex_digits[] = "0123456789abcdef";
@ -317,6 +340,10 @@ const char basic_data<T>::background_color[] = "\x1b[48;2;";
template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m"; template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m"; template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '}; template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
template <typename T>
const char basic_data<T>::left_padding_shifts[] = {31, 31, 0, 1, 0};
template <typename T>
const char basic_data<T>::right_padding_shifts[] = {0, 31, 0, 1, 0};
template <typename T> struct bits { template <typename T> struct bits {
static FMT_CONSTEXPR_DECL const int value = static FMT_CONSTEXPR_DECL const int value =
@ -576,9 +603,10 @@ class bigint {
void operator=(const bigint&) = delete; void operator=(const bigint&) = delete;
void assign(const bigint& other) { void assign(const bigint& other) {
bigits_.resize(other.bigits_.size()); auto size = other.bigits_.size();
bigits_.resize(size);
auto data = other.bigits_.data(); auto data = other.bigits_.data();
std::copy(data, data + other.bigits_.size(), bigits_.data()); std::copy(data, data + size, make_checked(bigits_.data(), size));
exp_ = other.exp_; exp_ = other.exp_;
} }
@ -594,7 +622,7 @@ class bigint {
int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; } int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
bigint& operator<<=(int shift) { FMT_NOINLINE bigint& operator<<=(int shift) {
assert(shift >= 0); assert(shift >= 0);
exp_ += shift / bigit_bits; exp_ += shift / bigit_bits;
shift %= bigit_bits; shift %= bigit_bits;
@ -1125,7 +1153,7 @@ int snprintf_float(T value, int precision, float_specs specs,
precision = (precision >= 0 ? precision : 6) - 1; precision = (precision >= 0 ? precision : 6) - 1;
// Build the format string. // Build the format string.
enum { max_format_size = 7 }; // Ths longest format is "%#.*Le". enum { max_format_size = 7 }; // The longest format is "%#.*Le".
char format[max_format_size]; char format[max_format_size];
char* format_ptr = format; char* format_ptr = format;
*format_ptr++ = '%'; *format_ptr++ = '%';
@ -1145,13 +1173,13 @@ int snprintf_float(T value, int precision, float_specs specs,
for (;;) { for (;;) {
auto begin = buf.data() + offset; auto begin = buf.data() + offset;
auto capacity = buf.capacity() - offset; auto capacity = buf.capacity() - offset;
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION #ifdef FMT_FUZZ
if (precision > 100000) if (precision > 100000)
throw std::runtime_error( throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf"); "fuzz mode - avoid large allocation inside snprintf");
#endif #endif
// Suppress the warning about a nonliteral format string. // Suppress the warning about a nonliteral format string.
// Cannot use auto becase of a bug in MinGW (#1532). // Cannot use auto because of a bug in MinGW (#1532).
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
int result = precision >= 0 int result = precision >= 0
? snprintf_ptr(begin, capacity, format, precision, value) ? snprintf_ptr(begin, capacity, format, precision, value)
@ -1268,14 +1296,14 @@ FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) {
return next; return next;
} }
} // namespace internal } // namespace detail
template <> struct formatter<internal::bigint> { template <> struct formatter<detail::bigint> {
format_parse_context::iterator parse(format_parse_context& ctx) { format_parse_context::iterator parse(format_parse_context& ctx) {
return ctx.begin(); return ctx.begin();
} }
format_context::iterator format(const internal::bigint& n, format_context::iterator format(const detail::bigint& n,
format_context& ctx) { format_context& ctx) {
auto out = ctx.out(); auto out = ctx.out();
bool first = true; bool first = true;
@ -1289,12 +1317,12 @@ template <> struct formatter<internal::bigint> {
out = format_to(out, "{:08x}", value); out = format_to(out, "{:08x}", value);
} }
if (n.exp_ > 0) if (n.exp_ > 0)
out = format_to(out, "p{}", n.exp_ * internal::bigint::bigit_bits); out = format_to(out, "p{}", n.exp_ * detail::bigint::bigit_bits);
return out; return out;
} }
}; };
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) { FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
auto transcode = [this](const char* p) { auto transcode = [this](const char* p) {
auto cp = uint32_t(); auto cp = uint32_t();
auto error = 0; auto error = 0;
@ -1325,7 +1353,7 @@ FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
buffer_.push_back(0); buffer_.push_back(0);
} }
FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code, FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT { string_view message) FMT_NOEXCEPT {
FMT_TRY { FMT_TRY {
memory_buffer buf; memory_buffer buf;
@ -1333,12 +1361,9 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
for (;;) { for (;;) {
char* system_message = &buf[0]; char* system_message = &buf[0];
int result = int result =
internal::safe_strerror(error_code, system_message, buf.size()); detail::safe_strerror(error_code, system_message, buf.size());
if (result == 0) { if (result == 0) {
internal::writer w(out); format_to(std::back_inserter(out), "{}: {}", message, system_message);
w.write(message);
w.write(": ");
w.write(system_message);
return; return;
} }
if (result != ERANGE) if (result != ERANGE)
@ -1350,7 +1375,7 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
format_error_code(out, error_code, message); format_error_code(out, error_code, message);
} }
FMT_FUNC void internal::error_handler::on_error(const char* message) { FMT_FUNC void detail::error_handler::on_error(const char* message) {
FMT_THROW(format_error(message)); FMT_THROW(format_error(message));
} }
@ -1359,14 +1384,39 @@ FMT_FUNC void report_system_error(int error_code,
report_error(format_system_error, error_code, message); report_error(format_system_error, error_code, message);
} }
struct stringifier {
template <typename T> FMT_INLINE std::string operator()(T value) const {
return to_string(value);
}
std::string operator()(basic_format_arg<format_context>::handle h) const {
memory_buffer buf;
detail::buffer<char>& base = buf;
format_parse_context parse_ctx({});
format_context format_ctx(std::back_inserter(base), {}, {});
h.format(parse_ctx, format_ctx);
return to_string(buf);
}
};
FMT_FUNC std::string detail::vformat(string_view format_str, format_args args) {
if (format_str.size() == 2 && equal2(format_str.data(), "{}")) {
auto arg = args.get(0);
if (!arg) error_handler().on_error("argument not found");
return visit_format_arg(stringifier(), arg);
}
memory_buffer buffer;
detail::vformat_to(buffer, format_str, args);
return to_string(buffer);
}
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
memory_buffer buffer; memory_buffer buffer;
internal::vformat_to(buffer, format_str, detail::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args)); basic_format_args<buffer_context<char>>(args));
#ifdef _WIN32 #ifdef _WIN32
auto fd = _fileno(f); auto fd = _fileno(f);
if (_isatty(fd)) { if (_isatty(fd)) {
internal::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size())); detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
auto written = DWORD(); auto written = DWORD();
if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)),
u16.c_str(), static_cast<DWORD>(u16.size()), &written, u16.c_str(), static_cast<DWORD>(u16.size()), &written,
@ -1376,16 +1426,16 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
return; return;
} }
#endif #endif
internal::fwrite_fully(buffer.data(), 1, buffer.size(), f); detail::fwrite_fully(buffer.data(), 1, buffer.size(), f);
} }
#ifdef _WIN32 #ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding. // Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void internal::vprint_mojibake(std::FILE* f, string_view format_str, FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str,
format_args args) { format_args args) {
memory_buffer buffer; memory_buffer buffer;
internal::vformat_to(buffer, format_str, detail::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args)); basic_format_args<buffer_context<char>>(args));
fwrite_fully(buffer.data(), 1, buffer.size(), f); fwrite_fully(buffer.data(), 1, buffer.size(), f);
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -14,15 +14,15 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace detail {
template <typename Char> template <typename Char>
typename buffer_context<Char>::iterator vformat_to( typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf, const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using range = buffer_range<Char>; using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args, return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
internal::locale_ref(loc)); args, detail::locale_ref(loc));
} }
template <typename Char> template <typename Char>
@ -30,43 +30,43 @@ std::basic_string<Char> vformat(
const std::locale& loc, basic_string_view<Char> format_str, const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args); detail::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer); return fmt::to_string(buffer);
} }
} // namespace internal } // namespace detail
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat( inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str, const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat(loc, to_string_view(format_str), args); return detail::vformat(loc, to_string_view(format_str), args);
} }
template <typename S, typename... Args, typename Char = char_t<S>> template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc, inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) { const S& format_str, Args&&... args) {
return internal::vformat( return detail::vformat(
loc, to_string_view(format_str), loc, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...)); detail::make_args_checked<Args...>(format_str, args...));
} }
template <typename S, typename OutputIt, typename... Args, template <typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t< typename Char = enable_if_t<
internal::is_output_iterator<OutputIt>::value, char_t<S>>> detail::is_output_iterator<OutputIt>::value, char_t<S>>>
inline OutputIt vformat_to( inline OutputIt vformat_to(
OutputIt out, const std::locale& loc, const S& format_str, OutputIt out, const std::locale& loc, const S& format_str,
format_args_t<type_identity_t<OutputIt>, Char> args) { format_args_t<type_identity_t<OutputIt>, Char> args) {
using range = internal::output_range<OutputIt, Char>; using af = detail::arg_formatter<OutputIt, Char>;
return vformat_to<arg_formatter<range>>( return vformat_to<af>(out, to_string_view(format_str), args,
range(out), to_string_view(format_str), args, internal::locale_ref(loc)); detail::locale_ref(loc));
} }
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value)> detail::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const std::locale& loc, inline OutputIt format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) { const S& format_str, Args&&... args) {
internal::check_format_string<Args...>(format_str); detail::check_format_string<Args...>(format_str);
using context = format_context_t<OutputIt, char_t<S>>; using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...}; format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str), return vformat_to(out, loc, to_string_view(format_str),

View file

@ -50,7 +50,7 @@
#ifdef FMT_SYSTEM #ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call) # define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else #else
# define FMT_SYSTEM(call) call # define FMT_SYSTEM(call) ::call
# ifdef _WIN32 # ifdef _WIN32
// Fix warnings about deprecated symbols. // Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call # define FMT_POSIX_CALL(call) ::_##call
@ -133,7 +133,7 @@ class error_code {
}; };
#ifdef _WIN32 #ifdef _WIN32
namespace internal { namespace detail {
// A converter from UTF-16 to UTF-8. // A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively. // It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 { class utf16_to_utf8 {
@ -156,7 +156,7 @@ class utf16_to_utf8 {
FMT_API void format_windows_error(buffer<char>& out, int error_code, FMT_API void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT; string_view message) FMT_NOEXCEPT;
} // namespace internal } // namespace detail
/** A Windows error. */ /** A Windows error. */
class windows_error : public system_error { class windows_error : public system_error {
@ -277,7 +277,8 @@ class file {
enum { enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
CREATE = FMT_POSIX(O_CREAT) // Create if the file doesn't exist.
}; };
// Constructs a file object which doesn't represent any file. // Constructs a file object which doesn't represent any file.
@ -313,10 +314,10 @@ class file {
FMT_API long long size() const; FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer. // Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void* buffer, std::size_t count); FMT_API size_t read(void* buffer, size_t count);
// Attempts to write count bytes from the specified buffer to the file. // Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void* buffer, std::size_t count); FMT_API size_t write(const void* buffer, size_t count);
// Duplicates a file descriptor with the dup function and returns // Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object. // the duplicate as a file object.
@ -341,6 +342,63 @@ class file {
// Returns the memory page size. // Returns the memory page size.
long getpagesize(); long getpagesize();
class direct_buffered_file;
template <typename S, typename... Args>
void print(direct_buffered_file& f, const S& format_str,
const Args&... args);
// A buffered file with a direct buffer access and no synchronization.
class direct_buffered_file {
private:
file file_;
enum { buffer_size = 4096 };
char buffer_[buffer_size];
int pos_;
void flush() {
if (pos_ == 0) return;
file_.write(buffer_, pos_);
pos_ = 0;
}
int free_capacity() const { return buffer_size - pos_; }
public:
direct_buffered_file(cstring_view path, int oflag)
: file_(path, oflag), pos_(0) {}
~direct_buffered_file() {
flush();
}
void close() {
flush();
file_.close();
}
template <typename S, typename... Args>
friend void print(direct_buffered_file& f, const S& format_str,
const Args&... args) {
// We could avoid double buffering.
auto buf = fmt::memory_buffer();
fmt::format_to(std::back_inserter(buf), format_str, args...);
auto remaining_pos = 0;
auto remaining_size = buf.size();
while (remaining_size > detail::to_unsigned(f.free_capacity())) {
auto size = f.free_capacity();
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, size);
f.pos_ += size;
f.flush();
remaining_pos += size;
remaining_size -= size;
}
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, remaining_size);
f.pos_ += static_cast<int>(remaining_size);
}
};
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE #ifdef FMT_LOCALE

View file

@ -9,10 +9,15 @@
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
#include <ostream> #include <ostream>
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Char> class basic_printf_parse_context;
template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail {
template <class Char> class formatbuf : public std::basic_streambuf<Char> { template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private: private:
@ -75,7 +80,7 @@ template <typename T, typename Char> class is_streamable {
// Write the content of buf to os. // Write the content of buf to os.
template <typename Char> template <typename Char>
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) { void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data(); const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size(); unsigned_streamsize size = buf.size();
@ -93,34 +98,53 @@ void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) { locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf); formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf); std::basic_ostream<Char> output(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>()); if (loc) output.imbue(loc.get<std::locale>());
#endif #endif
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value; output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.resize(buf.size()); buf.resize(buf.size());
} }
// Formats an object of type T that has an overloaded ostream operator<<. // Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char> template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: formatter<basic_string_view<Char>, Char> { : private formatter<basic_string_view<Char>, Char> {
template <typename Context> FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) { -> decltype(ctx.begin()) {
return formatter<basic_string_view<Char>, Char>::parse(ctx);
}
template <typename ParseCtx,
FMT_ENABLE_IF(std::is_same<
ParseCtx, basic_printf_parse_context<Char>>::value)>
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale()); format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size()); basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx); return formatter<basic_string_view<Char>, Char>::format(str, ctx);
} }
template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
}
}; };
} // namespace internal } // namespace detail
template <typename Char> template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args); detail::vformat_to(buffer, format_str, args);
internal::write(os, buffer); detail::write_buffer(os, buffer);
} }
/** /**
@ -133,10 +157,10 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) { void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str), vprint(os, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...)); detail::make_args_checked<Args...>(format_str, args...));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View file

@ -1,2 +1,2 @@
#include "os.h" #include "os.h"
#warning "fmt/posix.h is deprecated; use fmt/os.h instead" #warning "fmt/posix.h is deprecated; use fmt/os.h instead"

View file

@ -14,7 +14,7 @@
#include "ostream.h" #include "ostream.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
@ -90,11 +90,11 @@ template <typename T, typename Context> class arg_converter {
if (const_check(sizeof(target_type) <= sizeof(int))) { if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings. // Extra casts are used to silence warnings.
if (is_signed) { if (is_signed) {
arg_ = internal::make_arg<Context>( arg_ = detail::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value))); static_cast<int>(static_cast<target_type>(value)));
} else { } else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type; using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = internal::make_arg<Context>( arg_ = detail::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value))); static_cast<unsigned>(static_cast<unsigned_type>(value)));
} }
} else { } else {
@ -102,9 +102,9 @@ template <typename T, typename Context> class arg_converter {
// glibc's printf doesn't sign extend arguments of smaller types: // glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254" // std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB. // but we don't have to do the same because it's a UB.
arg_ = internal::make_arg<Context>(static_cast<long long>(value)); arg_ = detail::make_arg<Context>(static_cast<long long>(value));
} else { } else {
arg_ = internal::make_arg<Context>( arg_ = detail::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value)); static_cast<typename make_unsigned_or_bool<U>::type>(value));
} }
} }
@ -133,7 +133,7 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) { void operator()(T value) {
arg_ = internal::make_arg<Context>( arg_ = detail::make_arg<Context>(
static_cast<typename Context::char_type>(value)); static_cast<typename Context::char_type>(value));
} }
@ -141,6 +141,13 @@ template <typename Context> class char_converter {
void operator()(T) {} // No conversion needed for non-integral types. void operator()(T) {} // No conversion needed for non-integral types.
}; };
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; }
const Char* operator()(const Char* s) { return s; }
};
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative. // left alignment if it is negative.
template <typename Char> class printf_width_handler { template <typename Char> class printf_width_handler {
@ -155,7 +162,7 @@ template <typename Char> class printf_width_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) { unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value); auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (internal::is_negative(value)) { if (detail::is_negative(value)) {
specs_.align = align::left; specs_.align = align::left;
width = 0 - width; width = 0 - width;
} }
@ -172,23 +179,25 @@ template <typename Char> class printf_width_handler {
}; };
template <typename Char, typename Context> template <typename Char, typename Context>
void printf(buffer<Char>& buf, basic_string_view<Char> format, void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) { basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format(); Context(std::back_inserter(buf), format, args).format();
} }
} // namespace detail
template <typename OutputIt, typename Char, typename Context> // For printing into memory_buffer.
internal::truncating_iterator<OutputIt> printf( template <typename Char, typename Context>
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format, FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
basic_format_args<Context> args) { basic_string_view<Char> format,
return Context(it, format, args).format(); basic_format_args<Context> args) {
return detail::vprintf(buf, format, args);
} }
} // namespace internal using detail::vprintf;
using internal::printf; // For printing into memory_buffer.
template <typename Range> class printf_arg_formatter;
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context; template <typename OutputIt, typename Char> class basic_printf_context;
/** /**
@ -196,15 +205,15 @@ template <typename OutputIt, typename Char> class basic_printf_context;
The ``printf`` argument formatter. The ``printf`` argument formatter.
\endrst \endrst
*/ */
template <typename Range> template <typename OutputIt, typename Char>
class printf_arg_formatter : public internal::arg_formatter_base<Range> { class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
public: public:
using iterator = typename Range::iterator; using iterator = OutputIt;
private: private:
using char_type = typename Range::value_type; using char_type = Char;
using base = internal::arg_formatter_base<Range>; using base = detail::arg_formatter_base<OutputIt, Char>;
using context_type = basic_printf_context<iterator, char_type>; using context_type = basic_printf_context<OutputIt, Char>;
context_type& context_; context_type& context_;
@ -229,9 +238,9 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
\endrst \endrst
*/ */
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx) printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {} : base(iter, &specs, detail::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
iterator operator()(T value) { iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so // MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead. // use std::is_same instead.
@ -246,7 +255,11 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none; fmt_specs.sign = sign::none;
fmt_specs.alt = false; fmt_specs.alt = false;
fmt_specs.align = align::right; fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return base::operator()(value); return base::operator()(value);
} else { } else {
return base::operator()(value); return base::operator()(value);
@ -312,18 +325,21 @@ template <typename T> struct printf_formatter {
template <typename FormatContext> template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
internal::format_value(internal::get_container(ctx.out()), value); detail::format_value(detail::get_container(ctx.out()), value);
return ctx.out(); return ctx.out();
} }
}; };
/** This template formats data and writes the output to a writer. */ /**
This template formats data and writes the output through an output iterator.
*/
template <typename OutputIt, typename Char> class basic_printf_context { template <typename OutputIt, typename Char> class basic_printf_context {
public: public:
/** The character type for the output. */ /** The character type for the output. */
using char_type = Char; using char_type = Char;
using iterator = OutputIt; using iterator = OutputIt;
using format_arg = basic_format_arg<basic_printf_context>; using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>; template <typename T> using formatter_type = printf_formatter<T>;
private: private:
@ -331,7 +347,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out_; OutputIt out_;
basic_format_args<basic_printf_context> args_; basic_format_args<basic_printf_context> args_;
basic_format_parse_context<Char> parse_ctx_; parse_context_type parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it, static void parse_flags(format_specs& specs, const Char*& it,
const Char* end); const Char* end);
@ -346,9 +362,8 @@ template <typename OutputIt, typename Char> class basic_printf_context {
public: public:
/** /**
\rst \rst
Constructs a ``printf_context`` object. References to the arguments and Constructs a ``printf_context`` object. References to the arguments are
the writer are stored in the context object so make sure they have stored in the context object so make sure they have appropriate lifetimes.
appropriate lifetimes.
\endrst \endrst
*/ */
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str, basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
@ -358,18 +373,18 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out() { return out_; } OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; } void advance_to(OutputIt it) { out_ = it; }
internal::locale_ref locale() { return {}; } detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); } format_arg arg(int id) const { return args_.get(id); }
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; } parse_context_type& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) { FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message); parse_ctx_.on_error(message);
} }
/** Formats stored arguments and writes the output to the range. */ /** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>> template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
OutputIt format(); OutputIt format();
}; };
@ -389,7 +404,9 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
specs.fill[0] = '0'; specs.fill[0] = '0';
break; break;
case ' ': case ' ':
specs.sign = sign::space; if (specs.sign != sign::plus) {
specs.sign = sign::space;
}
break; break;
case '#': case '#':
specs.alt = true; specs.alt = true;
@ -407,7 +424,7 @@ basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
arg_index = parse_ctx_.next_arg_id(); arg_index = parse_ctx_.next_arg_id();
else else
parse_ctx_.check_arg_id(--arg_index); parse_ctx_.check_arg_id(--arg_index);
return internal::get_arg(*this, arg_index); return detail::get_arg(*this, arg_index);
} }
template <typename OutputIt, typename Char> template <typename OutputIt, typename Char>
@ -419,7 +436,7 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly // Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s). // preceded with '0' flag(s).
internal::error_handler eh; detail::error_handler eh;
int value = parse_nonnegative_int(it, end, eh); int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index if (it != end && *it == '$') { // value is an argument index
++it; ++it;
@ -438,12 +455,12 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
// Parse width. // Parse width.
if (it != end) { if (it != end) {
if (*it >= '0' && *it <= '9') { if (*it >= '0' && *it <= '9') {
internal::error_handler eh; detail::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh); specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') { } else if (*it == '*') {
++it; ++it;
specs.width = static_cast<int>(visit_format_arg( specs.width = static_cast<int>(visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg())); detail::printf_width_handler<char_type>(specs), get_arg()));
} }
} }
return arg_index; return arg_index;
@ -471,38 +488,52 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse argument index, flags and width. // Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs); int arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument index out of range"); if (arg_index == 0) on_error("argument not found");
// Parse precision. // Parse precision.
if (it != end && *it == '.') { if (it != end && *it == '.') {
++it; ++it;
c = it != end ? *it : 0; c = it != end ? *it : 0;
if ('0' <= c && c <= '9') { if ('0' <= c && c <= '9') {
internal::error_handler eh; detail::error_handler eh;
specs.precision = parse_nonnegative_int(it, end, eh); specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') { } else if (c == '*') {
++it; ++it;
specs.precision = static_cast<int>( specs.precision = static_cast<int>(
visit_format_arg(internal::printf_precision_handler(), get_arg())); visit_format_arg(detail::printf_precision_handler(), get_arg()));
} else { } else {
specs.precision = 0; specs.precision = 0;
} }
} }
format_arg arg = get_arg(arg_index); format_arg arg = get_arg(arg_index);
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg)) // For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral())
specs.fill[0] =
' '; // Ignore '0' flag for non-numeric types or if '-' present.
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>(
str,
detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
}
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false; specs.alt = false;
if (specs.fill[0] == '0') { if (specs.fill[0] == '0') {
if (arg.is_arithmetic()) if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric; specs.align = align::numeric;
else else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types. specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
} }
// Parse length and convert the argument to the required type. // Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0; c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0; char_type t = it != end ? *it : 0;
using internal::convert_arg; using detail::convert_arg;
switch (c) { switch (c) {
case 'h': case 'h':
if (t == 'h') { if (t == 'h') {
@ -526,7 +557,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
convert_arg<intmax_t>(arg, t); convert_arg<intmax_t>(arg, t);
break; break;
case 'z': case 'z':
convert_arg<std::size_t>(arg, t); convert_arg<size_t>(arg, t);
break; break;
case 't': case 't':
convert_arg<std::ptrdiff_t>(arg, t); convert_arg<std::ptrdiff_t>(arg, t);
@ -551,7 +582,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.type = 'd'; specs.type = 'd';
break; break;
case 'c': case 'c':
visit_format_arg(internal::char_converter<basic_printf_context>(arg), visit_format_arg(detail::char_converter<basic_printf_context>(arg),
arg); arg);
break; break;
} }
@ -560,15 +591,14 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
start = it; start = it;
// Format argument. // Format argument.
visit_format_arg(ArgFormatter(out, specs, *this), arg); out = visit_format_arg(ArgFormatter(out, specs, *this), arg);
} }
return std::copy(start, it, out); return std::copy(start, it, out);
} }
template <typename Char> template <typename Char>
using basic_printf_context_t = using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>, basic_printf_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
Char>;
using printf_context = basic_printf_context_t<char>; using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>; using wprintf_context = basic_printf_context_t<wchar_t>;
@ -605,7 +635,7 @@ inline std::basic_string<Char> vsprintf(
const S& format, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); vprintf(buffer, to_string_view(format), args);
return to_string(buffer); return to_string(buffer);
} }
@ -619,7 +649,7 @@ inline std::basic_string<Char> vsprintf(
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) { inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), make_format_args<context>(args...)); return vsprintf(to_string_view(format), make_format_args<context>(args...));
@ -630,8 +660,8 @@ inline int vfprintf(
std::FILE* f, const S& format, std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); vprintf(buffer, to_string_view(format), args);
std::size_t size = buffer.size(); size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1 ? -1
: static_cast<int>(size); : static_cast<int>(size);
@ -647,7 +677,7 @@ inline int vfprintf(
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline int fprintf(std::FILE* f, const S& format, const Args&... args) { inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format), return vfprintf(f, to_string_view(format),
@ -671,7 +701,7 @@ inline int vprintf(
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)> FMT_ENABLE_IF(detail::is_string<S>::value)>
inline int printf(const S& format_str, const Args&... args) { inline int printf(const S& format_str, const Args&... args) {
using context = basic_printf_context_t<char_t<S>>; using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str), return vprintf(to_string_view(format_str),
@ -683,8 +713,8 @@ inline int vfprintf(
std::basic_ostream<Char>& os, const S& format, std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); vprintf(buffer, to_string_view(format), args);
internal::write(os, buffer); detail::write_buffer(os, buffer);
return static_cast<int>(buffer.size()); return static_cast<int>(buffer.size());
} }
@ -693,7 +723,7 @@ template <typename ArgFormatter, typename Char,
typename Context = typename Context =
basic_printf_context<typename ArgFormatter::iterator, Char>> basic_printf_context<typename ArgFormatter::iterator, Char>>
typename ArgFormatter::iterator vprintf( typename ArgFormatter::iterator vprintf(
internal::buffer<Char>& out, basic_string_view<Char> format_str, detail::buffer<Char>& out, basic_string_view<Char> format_str,
basic_format_args<type_identity_t<Context>> args) { basic_format_args<type_identity_t<Context>> args) {
typename ArgFormatter::iterator iter(out); typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>(); Context(iter, format_str, args).template format<ArgFormatter>();

View file

@ -33,7 +33,7 @@ template <typename Char> struct formatting_base {
template <typename Char, typename Enable = void> template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> { struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = static FMT_CONSTEXPR_DECL const size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range. // range.
Char prefix; Char prefix;
@ -54,7 +54,7 @@ struct formatting_tuple : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
}; };
namespace internal { namespace detail {
template <typename RangeT, typename OutputIterator> template <typename RangeT, typename OutputIterator>
OutputIterator copy(const RangeT& range, OutputIterator out) { OutputIterator copy(const RangeT& range, OutputIterator out) {
@ -118,26 +118,24 @@ template <typename T> class is_tuple_like_ {
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N> template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>; using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N> using index_sequence = std::index_sequence<N...>; template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <std::size_t N> template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
using make_index_sequence = std::make_index_sequence<N>;
#else #else
template <typename T, T... N> struct integer_sequence { template <typename T, T... N> struct integer_sequence {
using value_type = T; using value_type = T;
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); } static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
}; };
template <std::size_t... N> template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
using index_sequence = integer_sequence<std::size_t, N...>;
template <typename T, std::size_t N, T... Ns> template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {}; struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns> template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {}; struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <std::size_t N> template <size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>; using make_index_sequence = make_integer_sequence<size_t, N>;
#endif #endif
template <class Tuple, class F, size_t... Is> template <class Tuple, class F, size_t... Is>
@ -185,11 +183,11 @@ FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'"; return add_space ? L" '{}'" : L"'{}'";
} }
} // namespace internal } // namespace detail
template <typename T> struct is_tuple_like { template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value; detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
}; };
template <typename TupleT, typename Char> template <typename TupleT, typename Char>
@ -202,17 +200,17 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) {
*out++ = ' '; *out++ = ' ';
} }
out = internal::copy(formatting.delimiter, out); out = detail::copy(formatting.delimiter, out);
} }
out = format_to(out, out = format_to(out,
internal::format_str_quoted( detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v), (formatting.add_delimiter_spaces && i > 0), v),
v); v);
++i; ++i;
} }
formatting_tuple<Char>& formatting; formatting_tuple<Char>& formatting;
std::size_t& i; size_t& i;
typename std::add_lvalue_reference<decltype( typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out; std::declval<FormatContext>().out())>::type out;
}; };
@ -228,14 +226,14 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename FormatContext = format_context> template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
std::size_t i = 0; size_t i = 0;
internal::copy(formatting.prefix, out); detail::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out}); detail::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) {
*out++ = ' '; *out++ = ' ';
} }
internal::copy(formatting.postfix, out); detail::copy(formatting.postfix, out);
return ctx.out(); return ctx.out();
} }
@ -243,10 +241,9 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename T, typename Char> struct is_range { template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value && detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
!internal::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value && !std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<internal::std_string_view<Char>, T>::value; !std::is_constructible<detail::std_string_view<Char>, T>::value;
}; };
template <typename RangeT, typename Char> template <typename RangeT, typename Char>
@ -262,15 +259,17 @@ struct formatter<RangeT, Char,
template <typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format(const RangeT& values, typename FormatContext::iterator format(const RangeT& values,
FormatContext& ctx) { FormatContext& ctx) {
auto out = internal::copy(formatting.prefix, ctx.out()); auto out = detail::copy(formatting.prefix, ctx.out());
std::size_t i = 0; size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) { auto it = values.begin();
auto end = values.end();
for (; it != end; ++it) {
if (i > 0) { if (i > 0) {
if (formatting.add_prepostfix_space) *out++ = ' '; if (formatting.add_prepostfix_space) *out++ = ' ';
out = internal::copy(formatting.delimiter, out); out = detail::copy(formatting.delimiter, out);
} }
out = format_to(out, out = format_to(out,
internal::format_str_quoted( detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it), (formatting.add_delimiter_spaces && i > 0), *it),
*it); *it);
if (++i > formatting.range_length_limit) { if (++i > formatting.range_length_limit) {
@ -279,11 +278,11 @@ struct formatter<RangeT, Char,
} }
} }
if (formatting.add_prepostfix_space) *out++ = ' '; if (formatting.add_prepostfix_space) *out++ = ' ';
return internal::copy(formatting.postfix, out); return detail::copy(formatting.postfix, out);
} }
}; };
template <typename Char, typename... T> struct tuple_arg_join : internal::view { template <typename Char, typename... T> struct tuple_arg_join : detail::view {
const std::tuple<T...>& tuple; const std::tuple<T...>& tuple;
basic_string_view<Char> sep; basic_string_view<Char> sep;
@ -301,14 +300,14 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format( typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) { const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{}); return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
} }
private: private:
template <typename FormatContext, size_t... N> template <typename FormatContext, size_t... N>
typename FormatContext::iterator format( typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx, const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
internal::index_sequence<N...>) { detail::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...); return format_args(value, ctx, std::get<N>(value.tuple)...);
} }
@ -371,14 +370,14 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
\endrst \endrst
*/ */
template <typename T> template <typename T>
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join( arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
std::initializer_list<T> list, string_view sep) { string_view sep) {
return join(std::begin(list), std::end(list), sep); return join(std::begin(list), std::end(list), sep);
} }
template <typename T> template <typename T>
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join( arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
std::initializer_list<T> list, wstring_view sep) { wstring_view sep) {
return join(std::begin(list), std::end(list), sep); return join(std::begin(list), std::end(list), sep);
} }

View file

@ -8,12 +8,12 @@
#include "fmt/format-inl.h" #include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace detail {
template <typename T> template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision, int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) { T value) {
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION #ifdef FMT_FUZZ
if (precision > 100000) if (precision > 100000)
throw std::runtime_error( throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf"); "fuzz mode - avoid large allocation inside snprintf");
@ -23,154 +23,47 @@ int format_float(char* buf, std::size_t size, const char* format, int precision,
return precision < 0 ? snprintf_ptr(buf, size, format, value) return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value); : snprintf_ptr(buf, size, format, precision, value);
} }
struct sprintf_specs { } // namespace detail
int precision;
char type;
bool alt : 1;
template <typename Char> template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
constexpr sprintf_specs(basic_format_specs<Char> specs)
: precision(specs.precision), type(specs.type), alt(specs.alt) {}
constexpr bool has_precision() const { return precision >= 0; }
};
// This is deprecated and is kept only to preserve ABI compatibility.
template <typename Double>
char* sprintf_format(Double value, internal::buffer<char>& buf,
sprintf_specs specs) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
// Build format string.
enum { max_format_size = 10 }; // longest format: %#-*.*Lg
char format[max_format_size];
char* format_ptr = format;
*format_ptr++ = '%';
if (specs.alt || !specs.type) *format_ptr++ = '#';
if (specs.precision >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
char type = specs.type;
if (type == '%')
type = 'f';
else if (type == 0 || type == 'n')
type = 'g';
#if FMT_MSC_VER
if (type == 'F') {
// MSVC's printf doesn't support 'F'.
type = 'f';
}
#endif
*format_ptr++ = type;
*format_ptr = '\0';
// Format using snprintf.
char* start = nullptr;
char* decimal_point_pos = nullptr;
for (;;) {
std::size_t buffer_size = buf.capacity();
start = &buf[0];
int result =
format_float(start, buffer_size, format, specs.precision, value);
if (result >= 0) {
unsigned n = internal::to_unsigned(result);
if (n < buf.capacity()) {
// Find the decimal point.
auto p = buf.data(), end = p + n;
if (*p == '+' || *p == '-') ++p;
if (specs.type != 'a' && specs.type != 'A') {
while (p < end && *p >= '0' && *p <= '9') ++p;
if (p < end && *p != 'e' && *p != 'E') {
decimal_point_pos = p;
if (!specs.type) {
// Keep only one trailing zero after the decimal point.
++p;
if (*p == '0') ++p;
while (p != end && *p >= '1' && *p <= '9') ++p;
char* where = p;
while (p != end && *p == '0') ++p;
if (p == end || *p < '0' || *p > '9') {
if (p != end) std::memmove(where, p, to_unsigned(end - p));
n -= static_cast<unsigned>(p - where);
}
}
}
}
buf.resize(n);
break; // The buffer is large enough - continue with formatting.
}
buf.reserve(n + 1);
} else {
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buf.reserve(buf.capacity() + 1);
}
}
return decimal_point_pos;
}
} // namespace internal
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
sprintf_specs);
template FMT_API char* internal::sprintf_format(long double,
internal::buffer<char>&,
sprintf_specs);
template struct FMT_INSTANTIATION_DEF_API internal::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float. // Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_specs, int (*instantiate_format_float)(double, int, detail::float_specs,
internal::buffer<char>&) = detail::buffer<char>&) = detail::format_float;
internal::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc); template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const; template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
#endif #endif
// Explicit instantiations for char. // Explicit instantiations for char.
template FMT_API std::string internal::grouping_impl<char>(locale_ref); template FMT_API std::string detail::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref); template FMT_API char detail::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref); template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char*, const char*); template FMT_API void detail::buffer<char>::append(const char*, const char*);
template FMT_API void internal::arg_map<format_context>::init( template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
const basic_format_args<format_context>& args); detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>);
template FMT_API std::string internal::vformat<char>( template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
string_view, basic_format_args<format_context>); detail::buffer<char>&);
template FMT_API int detail::snprintf_float(long double, int,
template FMT_API format_context::iterator internal::vformat_to( detail::float_specs,
internal::buffer<char>&, string_view, basic_format_args<format_context>); detail::buffer<char>&);
template FMT_API int detail::format_float(double, int, detail::float_specs,
template FMT_API int internal::snprintf_float(double, int, detail::buffer<char>&);
internal::float_specs, template FMT_API int detail::format_float(long double, int, detail::float_specs,
internal::buffer<char>&); detail::buffer<char>&);
template FMT_API int internal::snprintf_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(double, int, internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref); template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref); template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref); template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*, template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*); const wchar_t*);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE FMT_END_NAMESPACE

View file

@ -73,14 +73,14 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
#ifdef _WIN32 #ifdef _WIN32
internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) { detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
if (int error_code = convert(s)) { if (int error_code = convert(s)) {
FMT_THROW(windows_error(error_code, FMT_THROW(windows_error(error_code,
"cannot convert string from UTF-16 to UTF-8")); "cannot convert string from UTF-16 to UTF-8"));
} }
} }
int internal::utf16_to_utf8::convert(wstring_view s) { int detail::utf16_to_utf8::convert(wstring_view s) {
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size()); int s_size = static_cast<int>(s.size());
if (s_size == 0) { if (s_size == 0) {
@ -105,13 +105,13 @@ void windows_error::init(int err_code, string_view format_str,
format_args args) { format_args args) {
error_code_ = err_code; error_code_ = err_code;
memory_buffer buffer; memory_buffer buffer;
internal::format_windows_error(buffer, err_code, vformat(format_str, args)); detail::format_windows_error(buffer, err_code, vformat(format_str, args));
std::runtime_error& base = *this; std::runtime_error& base = *this;
base = std::runtime_error(to_string(buffer)); base = std::runtime_error(to_string(buffer));
} }
void internal::format_windows_error(internal::buffer<char>& out, int error_code, void detail::format_windows_error(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT { string_view message) FMT_NOEXCEPT {
FMT_TRY { FMT_TRY {
wmemory_buffer buf; wmemory_buffer buf;
buf.resize(inline_buffer_size); buf.resize(inline_buffer_size);
@ -124,10 +124,7 @@ void internal::format_windows_error(internal::buffer<char>& out, int error_code,
if (result != 0) { if (result != 0) {
utf16_to_utf8 utf8_message; utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) { if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
internal::writer w(out); format_to(std::back_inserter(out), "{}: {}", message, utf8_message);
w.write(message);
w.write(": ");
w.write(utf8_message);
return; return;
} }
break; break;
@ -143,7 +140,7 @@ void internal::format_windows_error(internal::buffer<char>& out, int error_code,
void report_windows_error(int error_code, void report_windows_error(int error_code,
fmt::string_view message) FMT_NOEXCEPT { fmt::string_view message) FMT_NOEXCEPT {
report_error(internal::format_windows_error, error_code, message); report_error(detail::format_windows_error, error_code, message);
} }
#endif // _WIN32 #endif // _WIN32
@ -234,14 +231,14 @@ std::size_t file::read(void* buffer, std::size_t count) {
RWResult result = 0; RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file")); if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
return internal::to_unsigned(result); return detail::to_unsigned(result);
} }
std::size_t file::write(const void* buffer, std::size_t count) { std::size_t file::write(const void* buffer, std::size_t count) {
RWResult result = 0; RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file")); if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
return internal::to_unsigned(result); return detail::to_unsigned(result);
} }
file file::dup(int fd) { file file::dup(int fd) {
@ -292,7 +289,11 @@ void file::pipe(file& read_end, file& write_end) {
buffered_file file::fdopen(const char* mode) { buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR. // Don't retry as fdopen doesn't return EINTR.
#if defined(__MINGW32__) && defined(_POSIX_)
FILE* f = ::fdopen(fd_, mode);
#else
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
#endif
if (!f) if (!f)
FMT_THROW( FMT_THROW(
system_error(errno, "cannot associate stream with file descriptor")); system_error(errno, "cannot associate stream with file descriptor"));

View file

@ -4,6 +4,7 @@
# A vagrant config for testing against gcc-4.8. # A vagrant config for testing against gcc-4.8.
Vagrant.configure("2") do |config| Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/xenial64" config.vm.box = "ubuntu/xenial64"
config.disksize.size = '15GB'
config.vm.provider "virtualbox" do |vb| config.vm.provider "virtualbox" do |vb|
vb.memory = "4096" vb.memory = "4096"

View file

@ -0,0 +1,26 @@
# This module provides function for joining paths
# known from from most languages
#
# Original license:
# SPDX-License-Identifier: (MIT OR CC0-1.0)
# Explicit permission given to distribute this module under
# the terms of the project as described in /LICENSE.rst.
# Copyright 2020 Jan Tojnar
# https://github.com/jtojnar/cmake-snips
#
# Modelled after Pythons os.path.join
# https://docs.python.org/3.7/library/os.path.html#os.path.join
# Windows not supported
function(join_paths joined_path first_path_segment)
set(temp_path "${first_path_segment}")
foreach(current_segment IN LISTS ARGN)
if(NOT ("${current_segment}" STREQUAL ""))
if(IS_ABSOLUTE "${current_segment}")
set(temp_path "${current_segment}")
else()
set(temp_path "${temp_path}/${current_segment}")
endif()
endif()
endforeach()
set(${joined_path} "${temp_path}" PARENT_SCOPE)
endfunction()

View file

@ -1,7 +1,7 @@
prefix=@CMAKE_INSTALL_PREFIX@ prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ libdir=@libdir_for_pc_file@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ includedir=@includedir_for_pc_file@
Name: fmt Name: fmt
Description: A modern formatting library Description: A modern formatting library

View file

@ -144,9 +144,33 @@ def update_site(env):
b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data) b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data)
b.data = b.data.replace('std::FILE*', 'std::FILE *') b.data = b.data.replace('std::FILE*', 'std::FILE *')
b.data = b.data.replace('unsigned int', 'unsigned') b.data = b.data.replace('unsigned int', 'unsigned')
b.data = b.data.replace('operator""_', 'operator"" _') #b.data = b.data.replace('operator""_', 'operator"" _')
b.data = b.data.replace(', size_t', ', std::size_t') b.data = b.data.replace(
'format_to_n(OutputIt, size_t, string_view, Args&&',
'format_to_n(OutputIt, size_t, const S&, const Args&')
b.data = b.data.replace(
'format_to_n(OutputIt, std::size_t, string_view, Args&&',
'format_to_n(OutputIt, std::size_t, const S&, const Args&')
if version == ('3.0.2'):
b.data = b.data.replace(
'fprintf(std::ostream&', 'fprintf(std::ostream &')
if version == ('5.3.0'):
b.data = b.data.replace(
'format_to(OutputIt, const S&, const Args&...)',
'format_to(OutputIt, const S &, const Args &...)')
if version.startswith('5.') or version.startswith('6.'):
b.data = b.data.replace(', size_t', ', std::size_t')
if version.startswith('7.'):
b.data = b.data.replace(', std::size_t', ', size_t')
b.data = b.data.replace('join(It, It', 'join(It, Sentinel')
b.data = b.data.replace('aa long', 'a long') b.data = b.data.replace('aa long', 'a long')
b.data = b.data.replace('serveral', 'several')
if version.startswith('6.2.'):
b.data = b.data.replace(
'vformat(const S&, basic_format_args<' +
'buffer_context<Char>>)',
'vformat(const S&, basic_format_args<' +
'buffer_context<type_identity_t<Char>>>)')
# Fix a broken link in index.rst. # Fix a broken link in index.rst.
index = os.path.join(target_doc_dir, 'index.rst') index = os.path.join(target_doc_dir, 'index.rst')
with rewrite(index) as b: with rewrite(index) as b:

View file

@ -10,10 +10,11 @@
#endif #endif
#include "fmt/chrono.h" #include "fmt/chrono.h"
#include "gtest-extra.h"
#include <iomanip> #include <iomanip>
#include "gtest-extra.h"
std::tm make_tm() { std::tm make_tm() {
auto time = std::tm(); auto time = std::tm();
time.tm_mday = 1; time.tm_mday = 1;

View file

@ -6,6 +6,7 @@
// For the license information refer to format.h. // For the license information refer to format.h.
#include "fmt/color.h" #include "fmt/color.h"
#include "gtest-extra.h" #include "gtest-extra.h"
TEST(ColorsTest, ColorsPrint) { TEST(ColorsTest, ColorsPrint) {

View file

@ -6,6 +6,7 @@
// For the license information refer to format.h. // For the license information refer to format.h.
#include <stdint.h> #include <stdint.h>
#include <cctype> #include <cctype>
#include <cfloat> #include <cfloat>
#include <climits> #include <climits>
@ -39,8 +40,8 @@ using testing::StrictMock;
#if FMT_USE_CONSTEXPR #if FMT_USE_CONSTEXPR
template <unsigned EXPECTED_PARTS_COUNT, typename Format> template <unsigned EXPECTED_PARTS_COUNT, typename Format>
void check_prepared_parts_type(Format format) { void check_prepared_parts_type(Format format) {
typedef fmt::internal::compiled_format_base<decltype(format)> provider; typedef fmt::detail::compiled_format_base<decltype(format)> provider;
typedef fmt::internal::format_part<char> typedef fmt::detail::format_part<char>
expected_parts_type[EXPECTED_PARTS_COUNT]; expected_parts_type[EXPECTED_PARTS_COUNT];
static_assert(std::is_same<typename provider::parts_container, static_assert(std::is_same<typename provider::parts_container,
expected_parts_type>::value, expected_parts_type>::value,
@ -66,46 +67,37 @@ TEST(CompileTest, CompileTimePreparedPartsTypeProvider) {
#endif #endif
TEST(CompileTest, PassStringLiteralFormat) { TEST(CompileTest, PassStringLiteralFormat) {
const auto prepared = fmt::compile<int>("test {}"); const auto prepared = fmt::detail::compile<int>("test {}");
EXPECT_EQ("test 42", fmt::format(prepared, 42)); EXPECT_EQ("test 42", fmt::format(prepared, 42));
const auto wprepared = fmt::compile<int>(L"test {}"); const auto wprepared = fmt::detail::compile<int>(L"test {}");
EXPECT_EQ(L"test 42", fmt::format(wprepared, 42)); EXPECT_EQ(L"test 42", fmt::format(wprepared, 42));
} }
#if FMT_USE_CONSTEXPR
TEST(CompileTest, PassCompileString) {
const auto prepared = fmt::compile<int>(FMT_STRING("test {}"));
EXPECT_EQ("test 42", fmt::format(prepared, 42));
const auto wprepared = fmt::compile<int>(FMT_STRING(L"test {}"));
EXPECT_EQ(L"test 42", fmt::format(wprepared, 42));
}
#endif
TEST(CompileTest, FormatToArrayOfChars) { TEST(CompileTest, FormatToArrayOfChars) {
char buffer[32] = {0}; char buffer[32] = {0};
const auto prepared = fmt::compile<int>("4{}"); const auto prepared = fmt::detail::compile<int>("4{}");
fmt::format_to(fmt::internal::make_checked(buffer, 32), prepared, 2); fmt::format_to(fmt::detail::make_checked(buffer, 32), prepared, 2);
EXPECT_EQ(std::string("42"), buffer); EXPECT_EQ(std::string("42"), buffer);
wchar_t wbuffer[32] = {0}; wchar_t wbuffer[32] = {0};
const auto wprepared = fmt::compile<int>(L"4{}"); const auto wprepared = fmt::detail::compile<int>(L"4{}");
fmt::format_to(fmt::internal::make_checked(wbuffer, 32), wprepared, 2); fmt::format_to(fmt::detail::make_checked(wbuffer, 32), wprepared, 2);
EXPECT_EQ(std::wstring(L"42"), wbuffer); EXPECT_EQ(std::wstring(L"42"), wbuffer);
} }
TEST(CompileTest, FormatToIterator) { TEST(CompileTest, FormatToIterator) {
std::string s(2, ' '); std::string s(2, ' ');
const auto prepared = fmt::compile<int>("4{}"); const auto prepared = fmt::detail::compile<int>("4{}");
fmt::format_to(s.begin(), prepared, 2); fmt::format_to(s.begin(), prepared, 2);
EXPECT_EQ("42", s); EXPECT_EQ("42", s);
std::wstring ws(2, L' '); std::wstring ws(2, L' ');
const auto wprepared = fmt::compile<int>(L"4{}"); const auto wprepared = fmt::detail::compile<int>(L"4{}");
fmt::format_to(ws.begin(), wprepared, 2); fmt::format_to(ws.begin(), wprepared, 2);
EXPECT_EQ(L"42", ws); EXPECT_EQ(L"42", ws);
} }
TEST(CompileTest, FormatToN) { TEST(CompileTest, FormatToN) {
char buf[5]; char buf[5];
auto f = fmt::compile<int>("{:10}"); auto f = fmt::detail::compile<int>("{:10}");
auto result = fmt::format_to_n(buf, 5, f, 42); auto result = fmt::format_to_n(buf, 5, f, 42);
EXPECT_EQ(result.size, 10); EXPECT_EQ(result.size, 10);
EXPECT_EQ(result.out, buf + 5); EXPECT_EQ(result.out, buf + 5);
@ -113,12 +105,12 @@ TEST(CompileTest, FormatToN) {
} }
TEST(CompileTest, FormattedSize) { TEST(CompileTest, FormattedSize) {
auto f = fmt::compile<int>("{:10}"); auto f = fmt::detail::compile<int>("{:10}");
EXPECT_EQ(fmt::formatted_size(f, 42), 10); EXPECT_EQ(fmt::formatted_size(f, 42), 10);
} }
TEST(CompileTest, MultipleTypes) { TEST(CompileTest, MultipleTypes) {
auto f = fmt::compile<int, int>("{} {}"); auto f = fmt::detail::compile<int, int>("{} {}");
EXPECT_EQ(fmt::format(f, 42, 42), "42 42"); EXPECT_EQ(fmt::format(f, 42, 42), "42 42");
} }
@ -126,18 +118,49 @@ struct formattable {};
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <> struct formatter<formattable> : formatter<const char*> { template <> struct formatter<formattable> : formatter<const char*> {
auto format(formattable, format_context& ctx) -> decltype(ctx.out()) { template <typename FormatContext>
auto format(formattable, FormatContext& ctx) -> decltype(ctx.out()) {
return formatter<const char*>::format("foo", ctx); return formatter<const char*>::format("foo", ctx);
} }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
TEST(CompileTest, FormatUserDefinedType) { TEST(CompileTest, FormatUserDefinedType) {
auto f = fmt::compile<formattable>("{}"); auto f = fmt::detail::compile<formattable>("{}");
EXPECT_EQ(fmt::format(f, formattable()), "foo"); EXPECT_EQ(fmt::format(f, formattable()), "foo");
} }
TEST(CompileTest, EmptyFormatString) { TEST(CompileTest, EmptyFormatString) {
auto f = fmt::compile<>(""); auto f = fmt::detail::compile<>("");
EXPECT_EQ(fmt::format(f), ""); EXPECT_EQ(fmt::format(f), "");
} }
#ifdef __cpp_if_constexpr
TEST(CompileTest, FormatDefault) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42u));
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42ll));
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42ull));
EXPECT_EQ("true", fmt::format(FMT_COMPILE("{}"), true));
EXPECT_EQ("x", fmt::format(FMT_COMPILE("{}"), 'x'));
EXPECT_EQ("4.2", fmt::format(FMT_COMPILE("{}"), 4.2));
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), "foo"));
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), std::string("foo")));
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), formattable()));
}
TEST(CompileTest, FormatSpecs) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42));
}
TEST(CompileTest, FormatTo) {
char buf[8];
auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42);
*end = '\0';
EXPECT_STREQ("42", buf);
}
TEST(CompileTest, TextAndArg) {
EXPECT_EQ(">>>42<<<", fmt::format(FMT_COMPILE(">>>{}<<<"), 42));
}
#endif

View file

@ -30,8 +30,8 @@
using fmt::basic_format_arg; using fmt::basic_format_arg;
using fmt::string_view; using fmt::string_view;
using fmt::internal::buffer; using fmt::detail::buffer;
using fmt::internal::value; using fmt::detail::value;
using testing::_; using testing::_;
using testing::StrictMock; using testing::StrictMock;
@ -42,7 +42,7 @@ struct test_struct {};
template <typename Context, typename T> template <typename Context, typename T>
basic_format_arg<Context> make_arg(const T& value) { basic_format_arg<Context> make_arg(const T& value) {
return fmt::internal::make_arg<Context>(value); return fmt::detail::make_arg<Context>(value);
} }
} // namespace } // namespace
@ -83,20 +83,20 @@ TEST(BufferTest, Nonmoveable) {
// A test buffer with a dummy grow method. // A test buffer with a dummy grow method.
template <typename T> struct test_buffer : buffer<T> { template <typename T> struct test_buffer : buffer<T> {
void grow(std::size_t capacity) { this->set(nullptr, capacity); } void grow(size_t capacity) { this->set(nullptr, capacity); }
}; };
template <typename T> struct mock_buffer : buffer<T> { template <typename T> struct mock_buffer : buffer<T> {
MOCK_METHOD1(do_grow, void(std::size_t capacity)); MOCK_METHOD1(do_grow, void(size_t capacity));
void grow(std::size_t capacity) { void grow(size_t capacity) {
this->set(this->data(), capacity); this->set(this->data(), capacity);
do_grow(capacity); do_grow(capacity);
} }
mock_buffer() {} mock_buffer() {}
mock_buffer(T* data) { this->set(data, 0); } mock_buffer(T* data) { this->set(data, 0); }
mock_buffer(T* data, std::size_t capacity) { this->set(data, capacity); } mock_buffer(T* data, size_t capacity) { this->set(data, capacity); }
}; };
TEST(BufferTest, Ctor) { TEST(BufferTest, Ctor) {
@ -115,7 +115,7 @@ TEST(BufferTest, Ctor) {
} }
{ {
int dummy; int dummy;
std::size_t capacity = std::numeric_limits<std::size_t>::max(); size_t capacity = std::numeric_limits<size_t>::max();
mock_buffer<int> buffer(&dummy, capacity); mock_buffer<int> buffer(&dummy, capacity);
EXPECT_EQ(&dummy, &buffer[0]); EXPECT_EQ(&dummy, &buffer[0]);
EXPECT_EQ(static_cast<size_t>(0), buffer.size()); EXPECT_EQ(static_cast<size_t>(0), buffer.size());
@ -148,7 +148,7 @@ TEST(BufferTest, Access) {
EXPECT_EQ(11, buffer[0]); EXPECT_EQ(11, buffer[0]);
buffer[3] = 42; buffer[3] = 42;
EXPECT_EQ(42, *(&buffer[0] + 3)); EXPECT_EQ(42, *(&buffer[0] + 3));
const fmt::internal::buffer<char>& const_buffer = buffer; const fmt::detail::buffer<char>& const_buffer = buffer;
EXPECT_EQ(42, const_buffer[3]); EXPECT_EQ(42, const_buffer[3]);
} }
@ -210,7 +210,8 @@ TEST(ArgTest, FormatArgs) {
} }
struct custom_context { struct custom_context {
typedef char char_type; using char_type = char;
using parse_context_type = fmt::format_parse_context;
template <typename T> struct formatter_type { template <typename T> struct formatter_type {
template <typename ParseContext> template <typename ParseContext>
@ -233,20 +234,20 @@ struct custom_context {
TEST(ArgTest, MakeValueWithCustomContext) { TEST(ArgTest, MakeValueWithCustomContext) {
test_struct t; test_struct t;
fmt::internal::value<custom_context> arg( fmt::detail::value<custom_context> arg(
fmt::internal::arg_mapper<custom_context>().map(t)); fmt::detail::arg_mapper<custom_context>().map(t));
custom_context ctx = {false, fmt::format_parse_context("")}; custom_context ctx = {false, fmt::format_parse_context("")};
arg.custom.format(&t, ctx.parse_context(), ctx); arg.custom.format(&t, ctx.parse_context(), ctx);
EXPECT_TRUE(ctx.called); EXPECT_TRUE(ctx.called);
} }
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace detail {
template <typename Char> template <typename Char>
bool operator==(custom_value<Char> lhs, custom_value<Char> rhs) { bool operator==(custom_value<Char> lhs, custom_value<Char> rhs) {
return lhs.value == rhs.value; return lhs.value == rhs.value;
} }
} // namespace internal } // namespace detail
FMT_END_NAMESPACE FMT_END_NAMESPACE
// Use a unique result type to make sure that there are no undesirable // Use a unique result type to make sure that there are no undesirable
@ -371,12 +372,12 @@ TEST(ArgTest, PointerArg) {
struct check_custom { struct check_custom {
test_result operator()( test_result operator()(
fmt::basic_format_arg<fmt::format_context>::handle h) const { fmt::basic_format_arg<fmt::format_context>::handle h) const {
struct test_buffer : fmt::internal::buffer<char> { struct test_buffer : fmt::detail::buffer<char> {
char data[10]; char data[10];
test_buffer() : fmt::internal::buffer<char>(data, 0, 10) {} test_buffer() : fmt::detail::buffer<char>(data, 0, 10) {}
void grow(std::size_t) {} void grow(size_t) {}
} buffer; } buffer;
fmt::internal::buffer<char>& base = buffer; fmt::detail::buffer<char>& base = buffer;
fmt::format_parse_context parse_ctx(""); fmt::format_parse_context parse_ctx("");
fmt::format_context ctx(std::back_inserter(base), fmt::format_args()); fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
h.format(parse_ctx, ctx); h.format(parse_ctx, ctx);
@ -455,6 +456,25 @@ TEST(FormatDynArgsTest, CustomFormat) {
EXPECT_EQ("cust=0 and cust=1 and cust=3", result); EXPECT_EQ("cust=0 and cust=1 and cust=3", result);
} }
TEST(FormatDynArgsTest, NamedInt) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(fmt::arg("a1", 42));
std::string result = fmt::vformat("{a1}", store);
EXPECT_EQ("42", result);
}
TEST(FormatDynArgsTest, NamedStrings) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
char str[]{"1234567890"};
store.push_back(fmt::arg("a1", str));
store.push_back(fmt::arg("a2", std::cref(str)));
str[0] = 'X';
std::string result = fmt::vformat("{a1} and {a2}", store);
EXPECT_EQ("1234567890 and X234567890", result);
}
TEST(FormatDynArgsTest, NamedArgByRef) { TEST(FormatDynArgsTest, NamedArgByRef) {
fmt::dynamic_format_arg_store<fmt::format_context> store; fmt::dynamic_format_arg_store<fmt::format_context> store;
@ -469,12 +489,53 @@ TEST(FormatDynArgsTest, NamedArgByRef) {
// storages. // storages.
int a1_val{42}; int a1_val{42};
auto a1 = fmt::arg("a1_", a1_val); auto a1 = fmt::arg("a1_", a1_val);
store.push_back("abc");
store.push_back(1.5f);
store.push_back(std::cref(a1)); store.push_back(std::cref(a1));
std::string result = fmt::vformat("{a1_}", // and {} and {}", std::string result = fmt::vformat("{a1_} and {} and {} and {}", store);
store);
EXPECT_EQ("42 and abc and 1.5 and 42", result);
}
TEST(FormatDynArgsTest, NamedCustomFormat) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
custom_type c{};
store.push_back(fmt::arg("c1", c));
++c.i;
store.push_back(fmt::arg("c2", c));
++c.i;
store.push_back(fmt::arg("c_ref", std::cref(c)));
++c.i;
std::string result = fmt::vformat("{c1} and {c2} and {c_ref}", store);
EXPECT_EQ("cust=0 and cust=1 and cust=3", result);
}
TEST(FormatDynArgsTest, Clear) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
std::string result = fmt::vformat("{}", store);
EXPECT_EQ("42", result); EXPECT_EQ("42", result);
store.push_back(43);
result = fmt::vformat("{} and {}", store);
EXPECT_EQ("42 and 43", result);
store.clear();
store.push_back(44);
result = fmt::vformat("{}", store);
EXPECT_EQ("44", result);
}
TEST(FormatDynArgsTest, Reserve) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.reserve(2, 1);
store.push_back(1.5f);
store.push_back(fmt::arg("a1", 42));
std::string result = fmt::vformat("{a1} and {}", store);
EXPECT_EQ("42 and 1.5", result);
} }
struct copy_throwable { struct copy_throwable {
@ -517,9 +578,9 @@ TEST(StringViewTest, Length) {
// Check string_view's comparison operator. // Check string_view's comparison operator.
template <template <typename> class Op> void check_op() { template <template <typename> class Op> void check_op() {
const char* inputs[] = {"foo", "fop", "fo"}; const char* inputs[] = {"foo", "fop", "fo"};
std::size_t num_inputs = sizeof(inputs) / sizeof(*inputs); size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
for (std::size_t i = 0; i < num_inputs; ++i) { for (size_t i = 0; i < num_inputs; ++i) {
for (std::size_t j = 0; j < num_inputs; ++j) { for (size_t j = 0; j < num_inputs; ++j) {
string_view lhs(inputs[i]), rhs(inputs[j]); string_view lhs(inputs[i]), rhs(inputs[j]);
EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs)); EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs));
} }
@ -605,7 +666,7 @@ template <typename Char> class my_string {
public: public:
my_string(const Char* s) : s_(s) {} my_string(const Char* s) : s_(s) {}
const Char* data() const FMT_NOEXCEPT { return s_.data(); } const Char* data() const FMT_NOEXCEPT { return s_.data(); }
std::size_t length() const FMT_NOEXCEPT { return s_.size(); } size_t length() const FMT_NOEXCEPT { return s_.size(); }
operator const Char*() const { return s_.c_str(); } operator const Char*() const { return s_.c_str(); }
private: private:
@ -621,23 +682,6 @@ inline fmt::basic_string_view<Char> to_string_view(const my_string<Char>& s)
struct non_string {}; struct non_string {};
} // namespace my_ns } // namespace my_ns
namespace FakeQt {
class QString {
public:
QString(const wchar_t* s) : s_(std::make_shared<std::wstring>(s)) {}
const wchar_t* utf16() const FMT_NOEXCEPT { return s_->data(); }
int size() const FMT_NOEXCEPT { return static_cast<int>(s_->size()); }
private:
std::shared_ptr<std::wstring> s_;
};
inline fmt::basic_string_view<wchar_t> to_string_view(const QString& s)
FMT_NOEXCEPT {
return {s.utf16(), static_cast<std::size_t>(s.size())};
}
} // namespace FakeQt
template <typename T> class IsStringTest : public testing::Test {}; template <typename T> class IsStringTest : public testing::Test {};
typedef ::testing::Types<char, wchar_t, char16_t, char32_t> StringCharTypes; typedef ::testing::Types<char, wchar_t, char16_t, char32_t> StringCharTypes;
@ -649,21 +693,19 @@ struct derived_from_string_view : fmt::basic_string_view<Char> {};
} // namespace } // namespace
TYPED_TEST(IsStringTest, IsString) { TYPED_TEST(IsStringTest, IsString) {
EXPECT_TRUE(fmt::internal::is_string<TypeParam*>::value); EXPECT_TRUE(fmt::detail::is_string<TypeParam*>::value);
EXPECT_TRUE(fmt::internal::is_string<const TypeParam*>::value); EXPECT_TRUE(fmt::detail::is_string<const TypeParam*>::value);
EXPECT_TRUE(fmt::internal::is_string<TypeParam[2]>::value); EXPECT_TRUE(fmt::detail::is_string<TypeParam[2]>::value);
EXPECT_TRUE(fmt::internal::is_string<const TypeParam[2]>::value); EXPECT_TRUE(fmt::detail::is_string<const TypeParam[2]>::value);
EXPECT_TRUE(fmt::internal::is_string<std::basic_string<TypeParam>>::value); EXPECT_TRUE(fmt::detail::is_string<std::basic_string<TypeParam>>::value);
EXPECT_TRUE(fmt::detail::is_string<fmt::basic_string_view<TypeParam>>::value);
EXPECT_TRUE( EXPECT_TRUE(
fmt::internal::is_string<fmt::basic_string_view<TypeParam>>::value); fmt::detail::is_string<derived_from_string_view<TypeParam>>::value);
EXPECT_TRUE( using string_view = fmt::detail::std_string_view<TypeParam>;
fmt::internal::is_string<derived_from_string_view<TypeParam>>::value);
using string_view = fmt::internal::std_string_view<TypeParam>;
EXPECT_TRUE(std::is_empty<string_view>::value != EXPECT_TRUE(std::is_empty<string_view>::value !=
fmt::internal::is_string<string_view>::value); fmt::detail::is_string<string_view>::value);
EXPECT_TRUE(fmt::internal::is_string<my_ns::my_string<TypeParam>>::value); EXPECT_TRUE(fmt::detail::is_string<my_ns::my_string<TypeParam>>::value);
EXPECT_FALSE(fmt::internal::is_string<my_ns::non_string>::value); EXPECT_FALSE(fmt::detail::is_string<my_ns::non_string>::value);
EXPECT_TRUE(fmt::internal::is_string<FakeQt::QString>::value);
} }
TEST(CoreTest, Format) { TEST(CoreTest, Format) {
@ -686,33 +728,16 @@ TEST(CoreTest, FormatTo) {
TEST(CoreTest, ToStringViewForeignStrings) { TEST(CoreTest, ToStringViewForeignStrings) {
using namespace my_ns; using namespace my_ns;
using namespace FakeQt;
EXPECT_EQ(to_string_view(my_string<char>("42")), "42"); EXPECT_EQ(to_string_view(my_string<char>("42")), "42");
EXPECT_EQ(to_string_view(my_string<wchar_t>(L"42")), L"42"); fmt::detail::type type =
EXPECT_EQ(to_string_view(QString(L"42")), L"42"); fmt::detail::mapped_type_constant<my_string<char>,
fmt::internal::type type = fmt::format_context>::value;
fmt::internal::mapped_type_constant<my_string<char>, EXPECT_EQ(type, fmt::detail::type::string_type);
fmt::format_context>::value;
EXPECT_EQ(type, fmt::internal::type::string_type);
type = fmt::internal::mapped_type_constant<my_string<wchar_t>,
fmt::wformat_context>::value;
EXPECT_EQ(type, fmt::internal::type::string_type);
type =
fmt::internal::mapped_type_constant<QString, fmt::wformat_context>::value;
EXPECT_EQ(type, fmt::internal::type::string_type);
// Does not compile: only wide format contexts are compatible with QString!
// type = fmt::internal::mapped_type_constant<QString,
// fmt::format_context>::value;
} }
TEST(CoreTest, FormatForeignStrings) { TEST(CoreTest, FormatForeignStrings) {
using namespace my_ns; using namespace my_ns;
using namespace FakeQt;
EXPECT_EQ(fmt::format(my_string<char>("{}"), 42), "42"); EXPECT_EQ(fmt::format(my_string<char>("{}"), 42), "42");
EXPECT_EQ(fmt::format(my_string<wchar_t>(L"{}"), 42), L"42");
EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42");
EXPECT_EQ(fmt::format(QString(L"{}"), my_string<wchar_t>(L"42")), L"42");
EXPECT_EQ(fmt::format(my_string<wchar_t>(L"{}"), QString(L"42")), L"42");
} }
struct implicitly_convertible_to_string { struct implicitly_convertible_to_string {
@ -747,15 +772,6 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
fmt::format("{}", explicitly_convertible_to_std_string_view())); fmt::format("{}", explicitly_convertible_to_std_string_view()));
} }
# endif # endif
struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
EXPECT_EQ(L"foo",
fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
}
#endif #endif
struct disabled_rvalue_conversion { struct disabled_rvalue_conversion {

View file

@ -18,14 +18,14 @@
// A custom argument formatter that doesn't print `-` for floating-point values // A custom argument formatter that doesn't print `-` for floating-point values
// rounded to 0. // rounded to 0.
class custom_arg_formatter class custom_arg_formatter
: public fmt::arg_formatter<fmt::buffer_range<char>> { : public fmt::detail::arg_formatter<fmt::format_context::iterator, char> {
public: public:
using range = fmt::buffer_range<char>; using base = fmt::detail::arg_formatter<fmt::format_context::iterator, char>;
typedef fmt::arg_formatter<range> base;
custom_arg_formatter(fmt::format_context& ctx, custom_arg_formatter(fmt::format_context& ctx,
fmt::format_parse_context* parse_ctx, fmt::format_parse_context* parse_ctx,
fmt::format_specs* s = nullptr) fmt::format_specs* s = nullptr,
const char* = nullptr)
: base(ctx, parse_ctx, s) {} : base(ctx, parse_ctx, s) {}
using base::operator(); using base::operator();
@ -39,8 +39,10 @@ class custom_arg_formatter
std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) { std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
fmt::detail::buffer<char>& base = buffer;
// Pass custom argument formatter as a template arg to vwrite. // Pass custom argument formatter as a template arg to vwrite.
fmt::vformat_to<custom_arg_formatter>(buffer, format_str, args); fmt::vformat_to<custom_arg_formatter>(std::back_inserter(base), format_str,
args);
return std::string(buffer.data(), buffer.size()); return std::string(buffer.data(), buffer.size());
} }

View file

@ -38,9 +38,9 @@ namespace std {
template<class Out, class charT> class basic_format_context; template<class Out, class charT> class basic_format_context;
using format_context = basic_format_context< using format_context = basic_format_context<
/* unspecified */ std::back_insert_iterator<fmt::internal::buffer<char>>, char>; /* unspecified */ std::back_insert_iterator<fmt::detail::buffer<char>>, char>;
using wformat_context = basic_format_context< using wformat_context = basic_format_context<
/* unspecified */ std::back_insert_iterator<fmt::internal::buffer<wchar_t>>, wchar_t>; /* unspecified */ std::back_insert_iterator<fmt::detail::buffer<wchar_t>>, wchar_t>;
template<class T, class charT = char> struct formatter { template<class T, class charT = char> struct formatter {
formatter() = delete; formatter() = delete;
@ -219,7 +219,7 @@ namespace std {
// Implementation details: // Implementation details:
using format_arg = basic_format_arg<basic_format_context>; using format_arg = basic_format_arg<basic_format_context>;
basic_format_context(Out out, basic_format_args<basic_format_context> args, fmt::internal::locale_ref) basic_format_context(Out out, basic_format_args<basic_format_context> args, fmt::detail::locale_ref)
: args_(args), out_(out) {} : args_(args), out_(out) {}
detail::error_handler error_handler() const { return {}; } detail::error_handler error_handler() const { return {}; }
basic_format_arg<basic_format_context> arg(fmt::basic_string_view<charT>) const { basic_format_arg<basic_format_context> arg(fmt::basic_string_view<charT>) const {
@ -488,22 +488,21 @@ template<class... Args>
namespace std { namespace std {
namespace detail { namespace detail {
template <typename Range> template <typename OutputIt, typename Char>
class arg_formatter class arg_formatter
: public fmt::internal::arg_formatter_base<Range, error_handler> { : public fmt::detail::arg_formatter_base<OutputIt, Char, error_handler> {
private: private:
using char_type = typename Range::value_type; using char_type = Char;
using base = fmt::internal::arg_formatter_base<Range, error_handler>; using base = fmt::detail::arg_formatter_base<OutputIt, Char, error_handler>;
using format_context = std::basic_format_context<typename base::iterator, char_type>; using format_context = std::basic_format_context<OutputIt, Char>;
using parse_context = basic_format_parse_context<char_type>; using parse_context = basic_format_parse_context<Char>;
parse_context* parse_ctx_; parse_context* parse_ctx_;
format_context& ctx_; format_context& ctx_;
public: public:
typedef Range range; using iterator = OutputIt;
typedef typename base::iterator iterator; using format_specs = typename base::format_specs;
typedef typename base::format_specs format_specs;
/** /**
\rst \rst
@ -513,7 +512,7 @@ class arg_formatter
\endrst \endrst
*/ */
arg_formatter(format_context& ctx, parse_context* parse_ctx = nullptr, fmt::format_specs* spec = nullptr) arg_formatter(format_context& ctx, parse_context* parse_ctx = nullptr, fmt::format_specs* spec = nullptr)
: base(Range(ctx.out()), spec, {}), parse_ctx_(parse_ctx), ctx_(ctx) {} : base(ctx.out(), spec, {}), parse_ctx_(parse_ctx), ctx_(ctx) {}
using base::operator(); using base::operator();
@ -529,36 +528,36 @@ class arg_formatter
}; };
template <typename Context> template <typename Context>
inline fmt::internal::type get_type(basic_format_arg<Context> arg) { inline fmt::detail::type get_type(basic_format_arg<Context> arg) {
return visit_format_arg([&] (auto val) { return visit_format_arg([&] (auto val) {
using char_type = typename Context::char_type; using char_type = typename Context::char_type;
using T = decltype(val); using T = decltype(val);
if (std::is_same_v<T, monostate>) if (std::is_same_v<T, monostate>)
return fmt::internal::type::none_type; return fmt::detail::type::none_type;
if (std::is_same_v<T, bool>) if (std::is_same_v<T, bool>)
return fmt::internal::type::bool_type; return fmt::detail::type::bool_type;
if (std::is_same_v<T, char_type>) if (std::is_same_v<T, char_type>)
return fmt::internal::type::char_type; return fmt::detail::type::char_type;
if (std::is_same_v<T, int>) if (std::is_same_v<T, int>)
return fmt::internal::type::int_type; return fmt::detail::type::int_type;
if (std::is_same_v<T, unsigned int>) if (std::is_same_v<T, unsigned int>)
return fmt::internal::type::uint_type; return fmt::detail::type::uint_type;
if (std::is_same_v<T, long long int>) if (std::is_same_v<T, long long int>)
return fmt::internal::type::long_long_type; return fmt::detail::type::long_long_type;
if (std::is_same_v<T, unsigned long long int>) if (std::is_same_v<T, unsigned long long int>)
return fmt::internal::type::ulong_long_type; return fmt::detail::type::ulong_long_type;
if (std::is_same_v<T, double>) if (std::is_same_v<T, double>)
return fmt::internal::type::double_type; return fmt::detail::type::double_type;
if (std::is_same_v<T, long double>) if (std::is_same_v<T, long double>)
return fmt::internal::type::long_double_type; return fmt::detail::type::long_double_type;
if (std::is_same_v<T, const char_type*>) if (std::is_same_v<T, const char_type*>)
return fmt::internal::type::cstring_type; return fmt::detail::type::cstring_type;
if (std::is_same_v<T, basic_string_view<char_type>>) if (std::is_same_v<T, basic_string_view<char_type>>)
return fmt::internal::type::string_type; return fmt::detail::type::string_type;
if (std::is_same_v<T, const void*>) if (std::is_same_v<T, const void*>)
return fmt::internal::type::pointer_type; return fmt::detail::type::pointer_type;
assert(get_value(arg).index() == 12); assert(get_value(arg).index() == 12);
return fmt::internal::type::custom_type; return fmt::detail::type::custom_type;
}, arg); }, arg);
} }
@ -582,45 +581,42 @@ class custom_formatter {
template <typename ArgFormatter, typename Char, typename Context> template <typename ArgFormatter, typename Char, typename Context>
struct format_handler : detail::error_handler { struct format_handler : detail::error_handler {
typedef typename ArgFormatter::range range; using iterator = typename ArgFormatter::iterator;
format_handler(range r, basic_string_view<Char> str, format_handler(iterator out, basic_string_view<Char> str,
basic_format_args<Context> format_args, basic_format_args<Context> format_args,
fmt::internal::locale_ref loc) fmt::detail::locale_ref loc)
: parse_ctx(str), context(r.begin(), format_args, loc) {} : parse_ctx(str), context(out, format_args, loc) {}
void on_text(const Char* begin, const Char* end) { void on_text(const Char* begin, const Char* end) {
auto size = fmt::internal::to_unsigned(end - begin); auto size = fmt::detail::to_unsigned(end - begin);
auto out = context.out(); auto out = context.out();
auto&& it = fmt::internal::reserve(out, size); auto&& it = fmt::detail::reserve(out, size);
it = std::copy_n(begin, size, it); it = std::copy_n(begin, size, it);
context.advance_to(out); context.advance_to(out);
} }
void on_arg_id() { int on_arg_id() { return parse_ctx.next_arg_id(); }
arg = context.arg(parse_ctx.next_arg_id()); int on_arg_id(unsigned id) { return parse_ctx.check_arg_id(id), id; }
} int on_arg_id(fmt::basic_string_view<Char>) { return 0; }
void on_arg_id(unsigned id) {
parse_ctx.check_arg_id(id);
arg = context.arg(id);
}
void on_arg_id(fmt::basic_string_view<Char>) {}
void on_replacement_field(const Char* p) { void on_replacement_field(int id, const Char* p) {
parse_ctx.advance_to(parse_ctx.begin() + (p - &*parse_ctx.begin())); auto arg = context.arg(id);
parse_ctx.advance_to(parse_ctx.begin() + (p - &*parse_ctx.begin()));
custom_formatter<Context> f(parse_ctx, context); custom_formatter<Context> f(parse_ctx, context);
if (!visit_format_arg(f, arg)) if (!visit_format_arg(f, arg))
context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx), arg)); context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx), arg));
} }
const Char* on_format_specs(const Char* begin, const Char* end) { const Char* on_format_specs(int id, const Char* begin, const Char* end) {
auto arg = context.arg(id);
parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin())); parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin()));
custom_formatter<Context> f(parse_ctx, context); custom_formatter<Context> f(parse_ctx, context);
if (visit_format_arg(f, arg)) return &*parse_ctx.begin(); if (visit_format_arg(f, arg)) return &*parse_ctx.begin();
fmt::basic_format_specs<Char> specs; fmt::basic_format_specs<Char> specs;
using fmt::internal::specs_handler; using fmt::detail::specs_handler;
using parse_context = basic_format_parse_context<Char>; using parse_context = basic_format_parse_context<Char>;
fmt::internal::specs_checker<specs_handler<parse_context, Context>> handler( fmt::detail::specs_checker<specs_handler<parse_context, Context>> handler(
specs_handler<parse_context, Context>(specs, parse_ctx, context), get_type(arg)); specs_handler<parse_context, Context>(specs, parse_ctx, context), get_type(arg));
begin = parse_format_specs(begin, end, handler); begin = parse_format_specs(begin, end, handler);
if (begin == end || *begin != '}') on_error("missing '}' in format string"); if (begin == end || *begin != '}') on_error("missing '}' in format string");
@ -631,7 +627,6 @@ struct format_handler : detail::error_handler {
basic_format_parse_context<Char> parse_ctx; basic_format_parse_context<Char> parse_ctx;
Context context; Context context;
basic_format_arg<Context> arg;
}; };
template <typename T, typename Char> template <typename T, typename Char>
@ -640,46 +635,45 @@ struct formatter {
// terminating '}'. // terminating '}'.
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext& ctx) { FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext& ctx) {
namespace internal = fmt::internal; namespace detail = fmt::detail;
typedef internal::dynamic_specs_handler<ParseContext> handler_type; typedef detail::dynamic_specs_handler<ParseContext> handler_type;
auto type = internal::mapped_type_constant<T, fmt::buffer_context<Char>>::value; auto type = detail::mapped_type_constant<T, fmt::buffer_context<Char>>::value;
internal::specs_checker<handler_type> handler(handler_type(specs_, ctx), detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
type); type);
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler); auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
auto type_spec = specs_.type; auto type_spec = specs_.type;
auto eh = ctx.error_handler(); auto eh = ctx.error_handler();
switch (type) { switch (type) {
case internal::type::none_type: case detail::type::none_type:
case internal::type::named_arg_type:
FMT_ASSERT(false, "invalid argument type"); FMT_ASSERT(false, "invalid argument type");
break; break;
case internal::type::int_type: case detail::type::int_type:
case internal::type::uint_type: case detail::type::uint_type:
case internal::type::long_long_type: case detail::type::long_long_type:
case internal::type::ulong_long_type: case detail::type::ulong_long_type:
case internal::type::bool_type: case detail::type::bool_type:
handle_int_type_spec(type_spec, handle_int_type_spec(type_spec,
internal::int_type_checker<decltype(eh)>(eh)); detail::int_type_checker<decltype(eh)>(eh));
break; break;
case internal::type::char_type: case detail::type::char_type:
handle_char_specs( handle_char_specs(
&specs_, internal::char_specs_checker<decltype(eh)>(type_spec, eh)); &specs_, detail::char_specs_checker<decltype(eh)>(type_spec, eh));
break; break;
case internal::type::double_type: case detail::type::double_type:
case internal::type::long_double_type: case detail::type::long_double_type:
internal::parse_float_type_spec(specs_, eh); detail::parse_float_type_spec(specs_, eh);
break; break;
case internal::type::cstring_type: case detail::type::cstring_type:
internal::handle_cstring_type_spec( detail::handle_cstring_type_spec(
type_spec, internal::cstring_type_checker<decltype(eh)>(eh)); type_spec, detail::cstring_type_checker<decltype(eh)>(eh));
break; break;
case internal::type::string_type: case detail::type::string_type:
internal::check_string_type_spec(type_spec, eh); detail::check_string_type_spec(type_spec, eh);
break; break;
case internal::type::pointer_type: case detail::type::pointer_type:
internal::check_pointer_type_spec(type_spec, eh); detail::check_pointer_type_spec(type_spec, eh);
break; break;
case internal::type::custom_type: case detail::type::custom_type:
// Custom format specifiers should be checked in parse functions of // Custom format specifiers should be checked in parse functions of
// formatter specializations. // formatter specializations.
break; break;
@ -689,18 +683,18 @@ struct formatter {
template <typename FormatContext> template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
fmt::internal::handle_dynamic_spec<fmt::internal::width_checker>( fmt::detail::handle_dynamic_spec<fmt::detail::width_checker>(
specs_.width, specs_.width_ref, ctx); specs_.width, specs_.width_ref, ctx);
fmt::internal::handle_dynamic_spec<fmt::internal::precision_checker>( fmt::detail::handle_dynamic_spec<fmt::detail::precision_checker>(
specs_.precision, specs_.precision_ref, ctx); specs_.precision, specs_.precision_ref, ctx);
using range_type = fmt::internal::output_range<typename FormatContext::iterator, using af = arg_formatter<typename FormatContext::iterator,
typename FormatContext::char_type>; typename FormatContext::char_type>;
return visit_format_arg(arg_formatter<range_type>(ctx, nullptr, &specs_), return visit_format_arg(af(ctx, nullptr, &specs_),
basic_format_arg<FormatContext>(val)); basic_format_arg<FormatContext>(val));
} }
private: private:
fmt::internal::dynamic_format_specs<Char> specs_; fmt::detail::dynamic_format_specs<Char> specs_;
}; };
} // namespace detail } // namespace detail
@ -717,11 +711,11 @@ template<class... Args>
string vformat(string_view fmt, format_args args) { string vformat(string_view fmt, format_args args) {
fmt::memory_buffer mbuf; fmt::memory_buffer mbuf;
fmt::internal::buffer<char>& buf = mbuf; fmt::detail::buffer<char>& buf = mbuf;
using range = fmt::buffer_range<char>; using af = detail::arg_formatter<fmt::format_context::iterator, char>;
detail::format_handler<detail::arg_formatter<range>, char, format_context> detail::format_handler<af, char, format_context>
h(range(std::back_inserter(buf)), fmt, args, {}); h(std::back_inserter(buf), fmt, args, {});
fmt::internal::parse_format_string<false>(fmt::to_string_view(fmt), h); fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h);
return to_string(mbuf); return to_string(mbuf);
} }
@ -741,10 +735,10 @@ template<class Out, class... Args>
template<class Out> template<class Out>
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args) { Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args) {
using range = fmt::internal::output_range<Out, char>; using af = detail::arg_formatter<Out, char>;
detail::format_handler<detail::arg_formatter<range>, char, basic_format_context<Out, char>> detail::format_handler<af, char, basic_format_context<Out, char>>
h(range(out), fmt, args, {}); h(out, fmt, args, {});
fmt::internal::parse_format_string<false>(fmt::to_string_view(fmt), h); fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h);
return h.context.out(); return h.context.out();
} }

View file

@ -21,9 +21,9 @@
#undef max #undef max
using fmt::internal::bigint; using fmt::detail::bigint;
using fmt::internal::fp; using fmt::detail::fp;
using fmt::internal::max_value; using fmt::detail::max_value;
static_assert(!std::is_copy_constructible<bigint>::value, ""); static_assert(!std::is_copy_constructible<bigint>::value, "");
static_assert(!std::is_copy_assignable<bigint>::value, ""); static_assert(!std::is_copy_assignable<bigint>::value, "");
@ -102,7 +102,7 @@ TEST(BigIntTest, Multiply) {
} }
TEST(BigIntTest, Accumulator) { TEST(BigIntTest, Accumulator) {
fmt::internal::accumulator acc; fmt::detail::accumulator acc;
EXPECT_EQ(acc.lower, 0); EXPECT_EQ(acc.lower, 0);
EXPECT_EQ(acc.upper, 0); EXPECT_EQ(acc.upper, 0);
acc.upper = 12; acc.upper = 12;
@ -110,7 +110,7 @@ TEST(BigIntTest, Accumulator) {
EXPECT_EQ(static_cast<uint32_t>(acc), 34); EXPECT_EQ(static_cast<uint32_t>(acc), 34);
acc += 56; acc += 56;
EXPECT_EQ(acc.lower, 90); EXPECT_EQ(acc.lower, 90);
acc += fmt::internal::max_value<uint64_t>(); acc += fmt::detail::max_value<uint64_t>();
EXPECT_EQ(acc.upper, 13); EXPECT_EQ(acc.upper, 13);
EXPECT_EQ(acc.lower, 89); EXPECT_EQ(acc.lower, 89);
acc >>= 32; acc >>= 32;
@ -262,7 +262,7 @@ TEST(FPTest, GetCachedPower) {
typedef std::numeric_limits<double> limits; typedef std::numeric_limits<double> limits;
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) { for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
int dec_exp = 0; int dec_exp = 0;
auto fp = fmt::internal::get_cached_power(exp, dec_exp); auto fp = fmt::detail::get_cached_power(exp, dec_exp);
EXPECT_LE(exp, fp.e); EXPECT_LE(exp, fp.e);
int dec_exp_step = 8; int dec_exp_step = 8;
EXPECT_LE(fp.e, exp + dec_exp_step * log2(10)); EXPECT_LE(fp.e, exp + dec_exp_step * log2(10));
@ -271,8 +271,8 @@ TEST(FPTest, GetCachedPower) {
} }
TEST(FPTest, GetRoundDirection) { TEST(FPTest, GetRoundDirection) {
using fmt::internal::get_round_direction; using fmt::detail::get_round_direction;
using fmt::internal::round_direction; using fmt::detail::round_direction;
EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0)); EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0));
EXPECT_EQ(round_direction::up, get_round_direction(100, 51, 0)); EXPECT_EQ(round_direction::up, get_round_direction(100, 51, 0));
EXPECT_EQ(round_direction::down, get_round_direction(100, 40, 10)); EXPECT_EQ(round_direction::down, get_round_direction(100, 40, 10));
@ -295,9 +295,9 @@ TEST(FPTest, GetRoundDirection) {
} }
TEST(FPTest, FixedHandler) { TEST(FPTest, FixedHandler) {
struct handler : fmt::internal::fixed_handler { struct handler : fmt::detail::fixed_handler {
char buffer[10]; char buffer[10];
handler(int prec = 0) : fmt::internal::fixed_handler() { handler(int prec = 0) : fmt::detail::fixed_handler() {
buf = buffer; buf = buffer;
precision = prec; precision = prec;
} }
@ -306,7 +306,7 @@ TEST(FPTest, FixedHandler) {
handler().on_digit('0', 100, 99, 0, exp, false); handler().on_digit('0', 100, 99, 0, exp, false);
EXPECT_THROW(handler().on_digit('0', 100, 100, 0, exp, false), EXPECT_THROW(handler().on_digit('0', 100, 100, 0, exp, false),
assertion_failure); assertion_failure);
namespace digits = fmt::internal::digits; namespace digits = fmt::detail::digits;
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, exp, false), digits::done); EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, exp, false), digits::done);
// Check that divisor - error doesn't overflow. // Check that divisor - error doesn't overflow.
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, exp, false), digits::error); EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, exp, false), digits::error);
@ -318,7 +318,7 @@ TEST(FPTest, FixedHandler) {
TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) { TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) {
fmt::memory_buffer buf; fmt::memory_buffer buf;
format_float(0.42, -1, fmt::internal::float_specs(), buf); format_float(0.42, -1, fmt::detail::float_specs(), buf);
} }
template <typename T> struct value_extractor { template <typename T> struct value_extractor {
@ -330,11 +330,11 @@ template <typename T> struct value_extractor {
#if FMT_USE_INT128 #if FMT_USE_INT128
// Apple Clang does not define typeid for __int128_t and __uint128_t. // Apple Clang does not define typeid for __int128_t and __uint128_t.
FMT_NORETURN T operator()(fmt::internal::int128_t) { FMT_NORETURN T operator()(fmt::detail::int128_t) {
throw std::runtime_error("invalid type __int128_t"); throw std::runtime_error("invalid type __int128_t");
} }
FMT_NORETURN T operator()(fmt::internal::uint128_t) { FMT_NORETURN T operator()(fmt::detail::uint128_t) {
throw std::runtime_error("invalid type __uint128_t"); throw std::runtime_error("invalid type __uint128_t");
} }
#endif #endif
@ -342,9 +342,9 @@ template <typename T> struct value_extractor {
TEST(FormatTest, ArgConverter) { TEST(FormatTest, ArgConverter) {
long long value = max_value<long long>(); long long value = max_value<long long>();
auto arg = fmt::internal::make_arg<fmt::format_context>(value); auto arg = fmt::detail::make_arg<fmt::format_context>(value);
fmt::visit_format_arg( fmt::visit_format_arg(
fmt::internal::arg_converter<long long, fmt::format_context>(arg, 'd'), fmt::detail::arg_converter<long long, fmt::format_context>(arg, 'd'),
arg); arg);
EXPECT_EQ(value, fmt::visit_format_arg(value_extractor<long long>(), arg)); EXPECT_EQ(value, fmt::visit_format_arg(value_extractor<long long>(), arg));
} }
@ -360,9 +360,9 @@ TEST(FormatTest, FormatNegativeNaN) {
TEST(FormatTest, StrError) { TEST(FormatTest, StrError) {
char* message = nullptr; char* message = nullptr;
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
EXPECT_ASSERT(fmt::internal::safe_strerror(EDOM, message = nullptr, 0), EXPECT_ASSERT(fmt::detail::safe_strerror(EDOM, message = nullptr, 0),
"invalid buffer"); "invalid buffer");
EXPECT_ASSERT(fmt::internal::safe_strerror(EDOM, message = buffer, 0), EXPECT_ASSERT(fmt::detail::safe_strerror(EDOM, message = buffer, 0),
"invalid buffer"); "invalid buffer");
buffer[0] = 'x'; buffer[0] = 'x';
#if defined(_GNU_SOURCE) && !defined(__COVERITY__) #if defined(_GNU_SOURCE) && !defined(__COVERITY__)
@ -374,18 +374,18 @@ TEST(FormatTest, StrError) {
#endif #endif
int result = int result =
fmt::internal::safe_strerror(error_code, message = buffer, BUFFER_SIZE); fmt::detail::safe_strerror(error_code, message = buffer, BUFFER_SIZE);
EXPECT_EQ(result, 0); EXPECT_EQ(result, 0);
std::size_t message_size = std::strlen(message); size_t message_size = std::strlen(message);
EXPECT_GE(BUFFER_SIZE - 1u, message_size); EXPECT_GE(BUFFER_SIZE - 1u, message_size);
EXPECT_EQ(get_system_error(error_code), message); EXPECT_EQ(get_system_error(error_code), message);
// safe_strerror never uses buffer on MinGW. // safe_strerror never uses buffer on MinGW.
#if !defined(__MINGW32__) && !defined(__sun) #if !defined(__MINGW32__) && !defined(__sun)
result = result =
fmt::internal::safe_strerror(error_code, message = buffer, message_size); fmt::detail::safe_strerror(error_code, message = buffer, message_size);
EXPECT_EQ(ERANGE, result); EXPECT_EQ(ERANGE, result);
result = fmt::internal::safe_strerror(error_code, message = buffer, 1); result = fmt::detail::safe_strerror(error_code, message = buffer, 1);
EXPECT_EQ(buffer, message); // Message should point to buffer. EXPECT_EQ(buffer, message); // Message should point to buffer.
EXPECT_EQ(ERANGE, result); EXPECT_EQ(ERANGE, result);
EXPECT_STREQ("", message); EXPECT_STREQ("", message);
@ -397,51 +397,48 @@ TEST(FormatTest, FormatErrorCode) {
{ {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
format_to(buffer, "garbage"); format_to(buffer, "garbage");
fmt::internal::format_error_code(buffer, 42, "test"); fmt::detail::format_error_code(buffer, 42, "test");
EXPECT_EQ("test: " + msg, to_string(buffer)); EXPECT_EQ("test: " + msg, to_string(buffer));
} }
{ {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size() + 1, std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size() + 1,
'x'); 'x');
fmt::internal::format_error_code(buffer, 42, prefix); fmt::detail::format_error_code(buffer, 42, prefix);
EXPECT_EQ(msg, to_string(buffer)); EXPECT_EQ(msg, to_string(buffer));
} }
int codes[] = {42, -1}; int codes[] = {42, -1};
for (std::size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) { for (size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) {
// Test maximum buffer size. // Test maximum buffer size.
msg = fmt::format("error {}", codes[i]); msg = fmt::format("error {}", codes[i]);
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size(), 'x'); std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size(), 'x');
fmt::internal::format_error_code(buffer, codes[i], prefix); fmt::detail::format_error_code(buffer, codes[i], prefix);
EXPECT_EQ(prefix + sep + msg, to_string(buffer)); EXPECT_EQ(prefix + sep + msg, to_string(buffer));
std::size_t size = fmt::inline_buffer_size; size_t size = fmt::inline_buffer_size;
EXPECT_EQ(size, buffer.size()); EXPECT_EQ(size, buffer.size());
buffer.resize(0); buffer.resize(0);
// Test with a message that doesn't fit into the buffer. // Test with a message that doesn't fit into the buffer.
prefix += 'x'; prefix += 'x';
fmt::internal::format_error_code(buffer, codes[i], prefix); fmt::detail::format_error_code(buffer, codes[i], prefix);
EXPECT_EQ(msg, to_string(buffer)); EXPECT_EQ(msg, to_string(buffer));
} }
} }
TEST(FormatTest, CountCodePoints) { TEST(FormatTest, CountCodePoints) {
#ifndef __cpp_char8_t EXPECT_EQ(4,
using fmt::char8_t; fmt::detail::count_code_points(
#endif fmt::basic_string_view<fmt::detail::char8_type>(
EXPECT_EQ( reinterpret_cast<const fmt::detail::char8_type*>("ёжик"))));
4, fmt::internal::count_code_points(
fmt::basic_string_view<fmt::internal::char8_type>(
reinterpret_cast<const fmt::internal::char8_type*>("ёжик"))));
} }
// Tests fmt::internal::count_digits for integer type Int. // Tests fmt::detail::count_digits for integer type Int.
template <typename Int> void test_count_digits() { template <typename Int> void test_count_digits() {
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::internal::count_digits(i)); for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::count_digits(i));
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) { for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) {
n *= 10; n *= 10;
EXPECT_EQ(i, fmt::internal::count_digits(n - 1)); EXPECT_EQ(i, fmt::detail::count_digits(n - 1));
EXPECT_EQ(i + 1, fmt::internal::count_digits(n)); EXPECT_EQ(i + 1, fmt::detail::count_digits(n));
} }
} }
@ -450,11 +447,10 @@ TEST(UtilTest, CountDigits) {
test_count_digits<uint64_t>(); test_count_digits<uint64_t>();
} }
TEST(UtilTest, WriteUIntPtr) { TEST(UtilTest, WriteFallbackUIntPtr) {
fmt::memory_buffer buf; std::string s;
fmt::internal::writer writer(buf); fmt::detail::write_ptr<char>(
writer.write_pointer( std::back_inserter(s),
fmt::internal::fallback_uintptr(reinterpret_cast<void*>(0xface)), fmt::detail::fallback_uintptr(reinterpret_cast<void*>(0xface)), nullptr);
nullptr); EXPECT_EQ(s, "0xface");
EXPECT_EQ("0xface", to_string(buf));
} }

View file

@ -35,10 +35,6 @@
#include "util.h" #include "util.h"
#undef ERROR #undef ERROR
#undef min
#undef max
using std::size_t;
using fmt::basic_memory_buffer; using fmt::basic_memory_buffer;
using fmt::format; using fmt::format;
@ -47,8 +43,7 @@ using fmt::memory_buffer;
using fmt::string_view; using fmt::string_view;
using fmt::wmemory_buffer; using fmt::wmemory_buffer;
using fmt::wstring_view; using fmt::wstring_view;
using fmt::internal::basic_writer; using fmt::detail::max_value;
using fmt::internal::max_value;
using testing::Return; using testing::Return;
using testing::StrictMock; using testing::StrictMock;
@ -102,47 +97,6 @@ void std_format(long double value, std::wstring& result) {
result = buffer; result = buffer;
} }
#endif #endif
// Checks if writing value to BasicWriter<Char> produces the same result
// as writing it to std::basic_ostringstream<Char>.
template <typename Char, typename T>
::testing::AssertionResult check_write(const T& value, const char* type) {
fmt::basic_memory_buffer<Char> buffer;
using range = fmt::buffer_range<Char>;
basic_writer<range> writer(buffer);
writer.write(value);
std::basic_string<Char> actual = to_string(buffer);
std::basic_string<Char> expected;
std_format(value, expected);
if (expected == actual) return ::testing::AssertionSuccess();
return ::testing::AssertionFailure()
<< "Value of: (Writer<" << type << ">() << value).str()\n"
<< " Actual: " << actual << "\n"
<< "Expected: " << expected << "\n";
}
struct AnyWriteChecker {
template <typename T>
::testing::AssertionResult operator()(const char*, const T& value) const {
::testing::AssertionResult result = check_write<char>(value, "char");
return result ? check_write<wchar_t>(value, "wchar_t") : result;
}
};
template <typename Char> struct WriteChecker {
template <typename T>
::testing::AssertionResult operator()(const char*, const T& value) const {
return check_write<Char>(value, "char");
}
};
// Checks if writing value to BasicWriter produces the same result
// as writing it to std::ostringstream both for char and wchar_t.
#define CHECK_WRITE(value) EXPECT_PRED_FORMAT1(AnyWriteChecker(), value)
#define CHECK_WRITE_CHAR(value) EXPECT_PRED_FORMAT1(WriteChecker<char>(), value)
#define CHECK_WRITE_WCHAR(value) \
EXPECT_PRED_FORMAT1(WriteChecker<wchar_t>(), value)
} // namespace } // namespace
struct uint32_pair { struct uint32_pair {
@ -150,10 +104,10 @@ struct uint32_pair {
}; };
TEST(UtilTest, BitCast) { TEST(UtilTest, BitCast) {
auto s = fmt::internal::bit_cast<uint32_pair>(uint64_t{42}); auto s = fmt::detail::bit_cast<uint32_pair>(uint64_t{42});
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), 42ull); EXPECT_EQ(fmt::detail::bit_cast<uint64_t>(s), 42ull);
s = fmt::internal::bit_cast<uint32_pair>(uint64_t(~0ull)); s = fmt::detail::bit_cast<uint32_pair>(uint64_t(~0ull));
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), ~0ull); EXPECT_EQ(fmt::detail::bit_cast<uint64_t>(s), ~0ull);
} }
TEST(UtilTest, Increment) { TEST(UtilTest, Increment) {
@ -178,18 +132,18 @@ TEST(UtilTest, ParseNonnegativeInt) {
fmt::string_view s = "10000000000"; fmt::string_view s = "10000000000";
auto begin = s.begin(), end = s.end(); auto begin = s.begin(), end = s.end();
EXPECT_THROW_MSG( EXPECT_THROW_MSG(
parse_nonnegative_int(begin, end, fmt::internal::error_handler()), parse_nonnegative_int(begin, end, fmt::detail::error_handler()),
fmt::format_error, "number is too big"); fmt::format_error, "number is too big");
s = "2147483649"; s = "2147483649";
begin = s.begin(); begin = s.begin();
end = s.end(); end = s.end();
EXPECT_THROW_MSG( EXPECT_THROW_MSG(
parse_nonnegative_int(begin, end, fmt::internal::error_handler()), parse_nonnegative_int(begin, end, fmt::detail::error_handler()),
fmt::format_error, "number is too big"); fmt::format_error, "number is too big");
} }
TEST(IteratorTest, CountingIterator) { TEST(IteratorTest, CountingIterator) {
fmt::internal::counting_iterator it; fmt::detail::counting_iterator it;
auto prev = it++; auto prev = it++;
EXPECT_EQ(prev.count(), 0); EXPECT_EQ(prev.count(), 0);
EXPECT_EQ(it.count(), 1); EXPECT_EQ(it.count(), 1);
@ -197,7 +151,7 @@ TEST(IteratorTest, CountingIterator) {
TEST(IteratorTest, TruncatingIterator) { TEST(IteratorTest, TruncatingIterator) {
char* p = nullptr; char* p = nullptr;
fmt::internal::truncating_iterator<char*> it(p, 3); fmt::detail::truncating_iterator<char*> it(p, 3);
auto prev = it++; auto prev = it++;
EXPECT_EQ(prev.base(), p); EXPECT_EQ(prev.base(), p);
EXPECT_EQ(it.base(), p + 1); EXPECT_EQ(it.base(), p + 1);
@ -206,7 +160,7 @@ TEST(IteratorTest, TruncatingIterator) {
TEST(IteratorTest, TruncatingBackInserter) { TEST(IteratorTest, TruncatingBackInserter) {
std::string buffer; std::string buffer;
auto bi = std::back_inserter(buffer); auto bi = std::back_inserter(buffer);
fmt::internal::truncating_iterator<decltype(bi)> it(bi, 2); fmt::detail::truncating_iterator<decltype(bi)> it(bi, 2);
*it++ = '4'; *it++ = '4';
*it++ = '2'; *it++ = '2';
*it++ = '1'; *it++ = '1';
@ -215,20 +169,20 @@ TEST(IteratorTest, TruncatingBackInserter) {
} }
TEST(IteratorTest, IsOutputIterator) { TEST(IteratorTest, IsOutputIterator) {
EXPECT_TRUE(fmt::internal::is_output_iterator<char*>::value); EXPECT_TRUE(fmt::detail::is_output_iterator<char*>::value);
EXPECT_FALSE(fmt::internal::is_output_iterator<const char*>::value); EXPECT_FALSE(fmt::detail::is_output_iterator<const char*>::value);
EXPECT_FALSE(fmt::internal::is_output_iterator<std::string>::value); EXPECT_FALSE(fmt::detail::is_output_iterator<std::string>::value);
EXPECT_TRUE(fmt::internal::is_output_iterator< EXPECT_TRUE(fmt::detail::is_output_iterator<
std::back_insert_iterator<std::string>>::value); std::back_insert_iterator<std::string>>::value);
EXPECT_TRUE(fmt::internal::is_output_iterator<std::string::iterator>::value); EXPECT_TRUE(fmt::detail::is_output_iterator<std::string::iterator>::value);
EXPECT_FALSE( EXPECT_FALSE(
fmt::internal::is_output_iterator<std::string::const_iterator>::value); fmt::detail::is_output_iterator<std::string::const_iterator>::value);
EXPECT_FALSE(fmt::internal::is_output_iterator<std::list<char>>::value); EXPECT_FALSE(fmt::detail::is_output_iterator<std::list<char>>::value);
EXPECT_TRUE( EXPECT_TRUE(
fmt::internal::is_output_iterator<std::list<char>::iterator>::value); fmt::detail::is_output_iterator<std::list<char>::iterator>::value);
EXPECT_FALSE(fmt::internal::is_output_iterator< EXPECT_FALSE(
std::list<char>::const_iterator>::value); fmt::detail::is_output_iterator<std::list<char>::const_iterator>::value);
EXPECT_FALSE(fmt::internal::is_output_iterator<uint32_pair>::value); EXPECT_FALSE(fmt::detail::is_output_iterator<uint32_pair>::value);
} }
TEST(MemoryBufferTest, Ctor) { TEST(MemoryBufferTest, Ctor) {
@ -343,10 +297,10 @@ TEST(MemoryBufferTest, Grow) {
mock_allocator<int> alloc; mock_allocator<int> alloc;
struct TestMemoryBuffer : Base { struct TestMemoryBuffer : Base {
TestMemoryBuffer(Allocator alloc) : Base(alloc) {} TestMemoryBuffer(Allocator alloc) : Base(alloc) {}
void grow(std::size_t size) { Base::grow(size); } void grow(size_t size) { Base::grow(size); }
} buffer((Allocator(&alloc))); } buffer((Allocator(&alloc)));
buffer.resize(7); buffer.resize(7);
using fmt::internal::to_unsigned; using fmt::detail::to_unsigned;
for (int i = 0; i < 7; ++i) buffer[to_unsigned(i)] = i * i; for (int i = 0; i < 7; ++i) buffer[to_unsigned(i)] = i * i;
EXPECT_EQ(10u, buffer.capacity()); EXPECT_EQ(10u, buffer.capacity());
int mem[20]; int mem[20];
@ -371,7 +325,7 @@ TEST(MemoryBufferTest, Allocator) {
basic_memory_buffer<char, 10, TestAllocator> buffer2( basic_memory_buffer<char, 10, TestAllocator> buffer2(
(TestAllocator(&alloc))); (TestAllocator(&alloc)));
EXPECT_EQ(&alloc, buffer2.get_allocator().get()); EXPECT_EQ(&alloc, buffer2.get_allocator().get());
std::size_t size = 2 * fmt::inline_buffer_size; size_t size = 2 * fmt::inline_buffer_size;
EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem)); EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem));
buffer2.reserve(size); buffer2.reserve(size);
EXPECT_CALL(alloc, deallocate(&mem, size)); EXPECT_CALL(alloc, deallocate(&mem, size));
@ -382,7 +336,7 @@ TEST(MemoryBufferTest, ExceptionInDeallocate) {
typedef allocator_ref<mock_allocator<char>> TestAllocator; typedef allocator_ref<mock_allocator<char>> TestAllocator;
StrictMock<mock_allocator<char>> alloc; StrictMock<mock_allocator<char>> alloc;
basic_memory_buffer<char, 10, TestAllocator> buffer((TestAllocator(&alloc))); basic_memory_buffer<char, 10, TestAllocator> buffer((TestAllocator(&alloc)));
std::size_t size = 2 * fmt::inline_buffer_size; size_t size = 2 * fmt::inline_buffer_size;
std::vector<char> mem(size); std::vector<char> mem(size);
{ {
EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem[0])); EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem[0]));
@ -397,27 +351,27 @@ TEST(MemoryBufferTest, ExceptionInDeallocate) {
EXPECT_THROW(buffer.reserve(2 * size), std::exception); EXPECT_THROW(buffer.reserve(2 * size), std::exception);
EXPECT_EQ(&mem2[0], &buffer[0]); EXPECT_EQ(&mem2[0], &buffer[0]);
// Check that the data has been copied. // Check that the data has been copied.
for (std::size_t i = 0; i < size; ++i) EXPECT_EQ('x', buffer[i]); for (size_t i = 0; i < size; ++i) EXPECT_EQ('x', buffer[i]);
} }
EXPECT_CALL(alloc, deallocate(&mem2[0], 2 * size)); EXPECT_CALL(alloc, deallocate(&mem2[0], 2 * size));
} }
TEST(UtilTest, UTF8ToUTF16) { TEST(UtilTest, UTF8ToUTF16) {
fmt::internal::utf8_to_utf16 u("лошадка"); fmt::detail::utf8_to_utf16 u("лошадка");
EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str()); EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str());
EXPECT_EQ(7, u.size()); EXPECT_EQ(7, u.size());
// U+10437 { DESERET SMALL LETTER YEE } // U+10437 { DESERET SMALL LETTER YEE }
EXPECT_EQ(L"\xD801\xDC37", fmt::internal::utf8_to_utf16("𐐷").str()); EXPECT_EQ(L"\xD801\xDC37", fmt::detail::utf8_to_utf16("𐐷").str());
EXPECT_THROW_MSG(fmt::internal::utf8_to_utf16("\xc3\x28"), std::runtime_error, EXPECT_THROW_MSG(fmt::detail::utf8_to_utf16("\xc3\x28"), std::runtime_error,
"invalid utf8"); "invalid utf8");
EXPECT_THROW_MSG(fmt::internal::utf8_to_utf16(fmt::string_view("л", 1)), EXPECT_THROW_MSG(fmt::detail::utf8_to_utf16(fmt::string_view("л", 1)),
std::runtime_error, "invalid utf8"); std::runtime_error, "invalid utf8");
EXPECT_EQ(L"123456", fmt::internal::utf8_to_utf16("123456").str()); EXPECT_EQ(L"123456", fmt::detail::utf8_to_utf16("123456").str());
} }
TEST(UtilTest, UTF8ToUTF16EmptyString) { TEST(UtilTest, UTF8ToUTF16EmptyString) {
std::string s = ""; std::string s = "";
fmt::internal::utf8_to_utf16 u(s.c_str()); fmt::detail::utf8_to_utf16 u(s.c_str());
EXPECT_EQ(L"", u.str()); EXPECT_EQ(L"", u.str());
EXPECT_EQ(s.size(), u.size()); EXPECT_EQ(s.size(), u.size());
} }
@ -479,101 +433,6 @@ TEST(StringViewTest, Ctor) {
EXPECT_EQ(4u, string_view(std::string("defg")).size()); EXPECT_EQ(4u, string_view(std::string("defg")).size());
} }
TEST(WriterTest, Data) {
memory_buffer buf;
fmt::internal::writer w(buf);
w.write(42);
EXPECT_EQ("42", to_string(buf));
}
TEST(WriterTest, WriteInt) {
CHECK_WRITE(42);
CHECK_WRITE(-42);
CHECK_WRITE(static_cast<short>(12));
CHECK_WRITE(34u);
CHECK_WRITE(std::numeric_limits<int>::min());
CHECK_WRITE(max_value<int>());
CHECK_WRITE(max_value<unsigned>());
}
TEST(WriterTest, WriteLong) {
CHECK_WRITE(56l);
CHECK_WRITE(78ul);
CHECK_WRITE(std::numeric_limits<long>::min());
CHECK_WRITE(max_value<long>());
CHECK_WRITE(max_value<unsigned long>());
}
TEST(WriterTest, WriteLongLong) {
CHECK_WRITE(56ll);
CHECK_WRITE(78ull);
CHECK_WRITE(std::numeric_limits<long long>::min());
CHECK_WRITE(max_value<long long>());
CHECK_WRITE(max_value<unsigned long long>());
}
TEST(WriterTest, WriteDouble) {
CHECK_WRITE(4.2);
CHECK_WRITE(-4.2);
auto min = std::numeric_limits<double>::min();
auto max = max_value<double>();
if (fmt::internal::use_grisu<double>()) {
EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min));
EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max));
} else {
CHECK_WRITE(min);
CHECK_WRITE(max);
}
}
TEST(WriterTest, WriteLongDouble) {
CHECK_WRITE(4.2l);
CHECK_WRITE_CHAR(-4.2l);
std::wstring str;
std_format(4.2l, str);
if (str[0] != '-')
CHECK_WRITE_WCHAR(-4.2l);
else
fmt::print("warning: long double formatting with std::swprintf is broken");
auto min = std::numeric_limits<long double>::min();
auto max = max_value<long double>();
if (fmt::internal::use_grisu<long double>()) {
EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min));
EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max));
} else {
CHECK_WRITE(min);
CHECK_WRITE(max);
}
}
TEST(WriterTest, WriteDoubleAtBufferBoundary) {
memory_buffer buf;
fmt::internal::writer writer(buf);
for (int i = 0; i < 100; ++i) writer.write(1.23456789);
}
TEST(WriterTest, WriteDoubleWithFilledBuffer) {
memory_buffer buf;
fmt::internal::writer writer(buf);
// Fill the buffer.
for (int i = 0; i < fmt::inline_buffer_size; ++i) writer.write(' ');
writer.write(1.2);
fmt::string_view sv(buf.data(), buf.size());
sv.remove_prefix(fmt::inline_buffer_size);
EXPECT_EQ("1.2", sv);
}
TEST(WriterTest, WriteChar) { CHECK_WRITE('a'); }
TEST(WriterTest, WriteWideChar) { CHECK_WRITE_WCHAR(L'a'); }
TEST(WriterTest, WriteString) {
CHECK_WRITE_CHAR("abc");
CHECK_WRITE_WCHAR("abc");
}
TEST(WriterTest, WriteWideString) { CHECK_WRITE_WCHAR(L"abc"); }
TEST(FormatToTest, FormatWithoutArgs) { TEST(FormatToTest, FormatWithoutArgs) {
std::string s; std::string s;
fmt::format_to(std::back_inserter(s), "test"); fmt::format_to(std::back_inserter(s), "test");
@ -640,15 +499,14 @@ TEST(FormatterTest, ArgErrors) {
EXPECT_THROW_MSG(format("{"), format_error, "invalid format string"); EXPECT_THROW_MSG(format("{"), format_error, "invalid format string");
EXPECT_THROW_MSG(format("{?}"), format_error, "invalid format string"); EXPECT_THROW_MSG(format("{?}"), format_error, "invalid format string");
EXPECT_THROW_MSG(format("{0"), format_error, "invalid format string"); EXPECT_THROW_MSG(format("{0"), format_error, "invalid format string");
EXPECT_THROW_MSG(format("{0}"), format_error, "argument index out of range"); EXPECT_THROW_MSG(format("{0}"), format_error, "argument not found");
EXPECT_THROW_MSG(format("{00}", 42), format_error, "invalid format string"); EXPECT_THROW_MSG(format("{00}", 42), format_error, "invalid format string");
char format_str[BUFFER_SIZE]; char format_str[BUFFER_SIZE];
safe_sprintf(format_str, "{%u", INT_MAX); safe_sprintf(format_str, "{%u", INT_MAX);
EXPECT_THROW_MSG(format(format_str), format_error, "invalid format string"); EXPECT_THROW_MSG(format(format_str), format_error, "invalid format string");
safe_sprintf(format_str, "{%u}", INT_MAX); safe_sprintf(format_str, "{%u}", INT_MAX);
EXPECT_THROW_MSG(format(format_str), format_error, EXPECT_THROW_MSG(format(format_str), format_error, "argument not found");
"argument index out of range");
safe_sprintf(format_str, "{%u", INT_MAX + 1u); safe_sprintf(format_str, "{%u", INT_MAX + 1u);
EXPECT_THROW_MSG(format(format_str), format_error, "number is too big"); EXPECT_THROW_MSG(format(format_str), format_error, "number is too big");
@ -673,19 +531,18 @@ template <> struct TestFormat<0> {
TEST(FormatterTest, ManyArgs) { TEST(FormatterTest, ManyArgs) {
EXPECT_EQ("19", TestFormat<20>::format("{19}")); EXPECT_EQ("19", TestFormat<20>::format("{19}"));
EXPECT_THROW_MSG(TestFormat<20>::format("{20}"), format_error, EXPECT_THROW_MSG(TestFormat<20>::format("{20}"), format_error,
"argument index out of range"); "argument not found");
EXPECT_THROW_MSG(TestFormat<21>::format("{21}"), format_error, EXPECT_THROW_MSG(TestFormat<21>::format("{21}"), format_error,
"argument index out of range"); "argument not found");
enum { max_packed_args = fmt::internal::max_packed_args }; enum { max_packed_args = fmt::detail::max_packed_args };
std::string format_str = fmt::format("{{{}}}", max_packed_args + 1); std::string format_str = fmt::format("{{{}}}", max_packed_args + 1);
EXPECT_THROW_MSG(TestFormat<max_packed_args>::format(format_str), EXPECT_THROW_MSG(TestFormat<max_packed_args>::format(format_str),
format_error, "argument index out of range"); format_error, "argument not found");
} }
TEST(FormatterTest, NamedArg) { TEST(FormatterTest, NamedArg) {
EXPECT_EQ("1/a/A", format("{_1}/{a_}/{A_}", fmt::arg("a_", 'a'), EXPECT_EQ("1/a/A", format("{_1}/{a_}/{A_}", fmt::arg("a_", 'a'),
fmt::arg("A_", "A"), fmt::arg("_1", 1))); fmt::arg("A_", "A"), fmt::arg("_1", 1)));
EXPECT_THROW_MSG(format("{a}"), format_error, "argument not found");
EXPECT_EQ(" -42", format("{0:{width}}", -42, fmt::arg("width", 4))); EXPECT_EQ(" -42", format("{0:{width}}", -42, fmt::arg("width", 4)));
EXPECT_EQ("st", format("{0:.{precision}}", "str", fmt::arg("precision", 2))); EXPECT_EQ("st", format("{0:.{precision}}", "str", fmt::arg("precision", 2)));
EXPECT_EQ("1 2", format("{} {two}", 1, fmt::arg("two", 2))); EXPECT_EQ("1 2", format("{} {two}", 1, fmt::arg("two", 2)));
@ -695,6 +552,8 @@ TEST(FormatterTest, NamedArg) {
fmt::arg("i", 0), fmt::arg("j", 0), fmt::arg("k", 0), fmt::arg("i", 0), fmt::arg("j", 0), fmt::arg("k", 0),
fmt::arg("l", 0), fmt::arg("m", 0), fmt::arg("n", 0), fmt::arg("l", 0), fmt::arg("m", 0), fmt::arg("n", 0),
fmt::arg("o", 0), fmt::arg("p", 0))); fmt::arg("o", 0), fmt::arg("p", 0)));
EXPECT_THROW_MSG(format("{a}"), format_error, "argument not found");
EXPECT_THROW_MSG(format("{a}", 42), format_error, "argument not found");
} }
TEST(FormatterTest, AutoArgIndex) { TEST(FormatterTest, AutoArgIndex) {
@ -708,7 +567,7 @@ TEST(FormatterTest, AutoArgIndex) {
"cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{:.{0}}", 1.2345, 2), format_error, EXPECT_THROW_MSG(format("{:.{0}}", 1.2345, 2), format_error,
"cannot switch from automatic to manual argument indexing"); "cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(format("{}"), format_error, "argument index out of range"); EXPECT_THROW_MSG(format("{}"), format_error, "argument not found");
} }
TEST(FormatterTest, EmptySpecs) { EXPECT_EQ("42", format("{0:}", 42)); } TEST(FormatterTest, EmptySpecs) { EXPECT_EQ("42", format("{0:}", 42)); }
@ -747,38 +606,8 @@ TEST(FormatterTest, RightAlign) {
EXPECT_EQ(" 0xface", format("{0:>8}", reinterpret_cast<void*>(0xface))); EXPECT_EQ(" 0xface", format("{0:>8}", reinterpret_cast<void*>(0xface)));
} }
#if FMT_NUMERIC_ALIGN #if FMT_DEPRECATED_NUMERIC_ALIGN
TEST(FormatterTest, NumericAlign) { TEST(FormatterTest, NumericAlign) { EXPECT_EQ("0042", format("{0:=4}", 42)); }
EXPECT_EQ(" 42", format("{0:=4}", 42));
EXPECT_EQ("+ 42", format("{0:=+4}", 42));
EXPECT_EQ(" 42", format("{0:=4o}", 042));
EXPECT_EQ("+ 42", format("{0:=+4o}", 042));
EXPECT_EQ(" 42", format("{0:=4x}", 0x42));
EXPECT_EQ("+ 42", format("{0:=+4x}", 0x42));
EXPECT_EQ("- 42", format("{0:=5}", -42));
EXPECT_EQ(" 42", format("{0:=5}", 42u));
EXPECT_EQ("- 42", format("{0:=5}", -42l));
EXPECT_EQ(" 42", format("{0:=5}", 42ul));
EXPECT_EQ("- 42", format("{0:=5}", -42ll));
EXPECT_EQ(" 42", format("{0:=5}", 42ull));
EXPECT_EQ("- 42.0", format("{0:=7}", -42.0));
EXPECT_EQ("- 42.0", format("{0:=7}", -42.0l));
EXPECT_THROW_MSG(format("{0:=5", 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG(format("{0:=5}", 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG(format("{0:=5}", "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:=8}", reinterpret_cast<void*>(0xface)),
format_error, "format specifier requires numeric argument");
EXPECT_EQ(" 1.0", fmt::format("{:= }", 1.0));
}
TEST(FormatToTest, FormatToNonbackInsertIteratorWithSignAndNumericAlignment) {
char buffer[16] = {};
fmt::format_to(fmt::internal::make_checked(buffer, 16), "{: =+}", 42.0);
EXPECT_STREQ("+42.0", buffer);
}
#endif #endif
TEST(FormatterTest, CenterAlign) { TEST(FormatterTest, CenterAlign) {
@ -969,7 +798,7 @@ TEST(FormatterTest, Width) {
safe_sprintf(format_str, "{0:%u", UINT_MAX); safe_sprintf(format_str, "{0:%u", UINT_MAX);
increment(format_str + 3); increment(format_str + 3);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big"); EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
std::size_t size = std::strlen(format_str); size_t size = std::strlen(format_str);
format_str[size] = '}'; format_str[size] = '}';
format_str[size + 1] = 0; format_str[size + 1] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big"); EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
@ -999,7 +828,7 @@ TEST(FormatterTest, RuntimeWidth) {
safe_sprintf(format_str, "{0:{%u", UINT_MAX); safe_sprintf(format_str, "{0:{%u", UINT_MAX);
increment(format_str + 4); increment(format_str + 4);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big"); EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
std::size_t size = std::strlen(format_str); size_t size = std::strlen(format_str);
format_str[size] = '}'; format_str[size] = '}';
format_str[size + 1] = 0; format_str[size + 1] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big"); EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
@ -1011,8 +840,7 @@ TEST(FormatterTest, RuntimeWidth) {
EXPECT_THROW_MSG(format("{0:{}", 0), format_error, EXPECT_THROW_MSG(format("{0:{}", 0), format_error,
"cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{0:{?}}", 0), format_error, "invalid format string"); EXPECT_THROW_MSG(format("{0:{?}}", 0), format_error, "invalid format string");
EXPECT_THROW_MSG(format("{0:{1}}", 0), format_error, EXPECT_THROW_MSG(format("{0:{1}}", 0), format_error, "argument not found");
"argument index out of range");
EXPECT_THROW_MSG(format("{0:{0:}}", 0), format_error, EXPECT_THROW_MSG(format("{0:{0:}}", 0), format_error,
"invalid format string"); "invalid format string");
@ -1053,7 +881,7 @@ TEST(FormatterTest, Precision) {
safe_sprintf(format_str, "{0:.%u", UINT_MAX); safe_sprintf(format_str, "{0:.%u", UINT_MAX);
increment(format_str + 4); increment(format_str + 4);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big"); EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
std::size_t size = std::strlen(format_str); size_t size = std::strlen(format_str);
format_str[size] = '}'; format_str[size] = '}';
format_str[size + 1] = 0; format_str[size + 1] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big"); EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
@ -1134,7 +962,7 @@ TEST(FormatterTest, Precision) {
EXPECT_THROW_MSG(format("{0:.2f}", reinterpret_cast<void*>(0xcafe)), EXPECT_THROW_MSG(format("{0:.2f}", reinterpret_cast<void*>(0xcafe)),
format_error, format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{:.{}e}", 42.0, fmt::internal::max_value<int>()), EXPECT_THROW_MSG(format("{:.{}e}", 42.0, fmt::detail::max_value<int>()),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_EQ("st", format("{0:.2}", "str")); EXPECT_EQ("st", format("{0:.2}", "str"));
@ -1145,7 +973,7 @@ TEST(FormatterTest, RuntimePrecision) {
safe_sprintf(format_str, "{0:.{%u", UINT_MAX); safe_sprintf(format_str, "{0:.{%u", UINT_MAX);
increment(format_str + 5); increment(format_str + 5);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big"); EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
std::size_t size = std::strlen(format_str); size_t size = std::strlen(format_str);
format_str[size] = '}'; format_str[size] = '}';
format_str[size + 1] = 0; format_str[size + 1] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big"); EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
@ -1160,8 +988,7 @@ TEST(FormatterTest, RuntimePrecision) {
"invalid format string"); "invalid format string");
EXPECT_THROW_MSG(format("{0:.{1}", 0, 0), format_error, EXPECT_THROW_MSG(format("{0:.{1}", 0, 0), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}}", 0), format_error, EXPECT_THROW_MSG(format("{0:.{1}}", 0), format_error, "argument not found");
"argument index out of range");
EXPECT_THROW_MSG(format("{0:.{0:}}", 0), format_error, EXPECT_THROW_MSG(format("{0:.{0:}}", 0), format_error,
"invalid format string"); "invalid format string");
@ -1256,7 +1083,8 @@ TEST(FormatterTest, FormatShort) {
TEST(FormatterTest, FormatInt) { TEST(FormatterTest, FormatInt) {
EXPECT_THROW_MSG(format("{0:v", 42), format_error, EXPECT_THROW_MSG(format("{0:v", 42), format_error,
"missing '}' in format string"); "missing '}' in format string");
check_unknown_types(42, "bBdoxXnL", "integer"); check_unknown_types(42, "bBdoxXnLc", "integer");
EXPECT_EQ("x", format("{:c}", static_cast<int>('x')));
} }
TEST(FormatterTest, FormatBin) { TEST(FormatterTest, FormatBin) {
@ -1396,7 +1224,6 @@ TEST(FormatterTest, FormatOct) {
} }
TEST(FormatterTest, FormatIntLocale) { TEST(FormatterTest, FormatIntLocale) {
EXPECT_EQ("1234", format("{:n}", 1234));
EXPECT_EQ("1234", format("{:L}", 1234)); EXPECT_EQ("1234", format("{:L}", 1234));
} }
@ -1413,7 +1240,7 @@ TEST(FormatterTest, FormatFloat) {
} }
TEST(FormatterTest, FormatDouble) { TEST(FormatterTest, FormatDouble) {
check_unknown_types(1.2, "eEfFgGaAn%", "double"); check_unknown_types(1.2, "eEfFgGaAnL%", "double");
EXPECT_EQ("0.0", format("{:}", 0.0)); EXPECT_EQ("0.0", format("{:}", 0.0));
EXPECT_EQ("0.000000", format("{:f}", 0.0)); EXPECT_EQ("0.000000", format("{:f}", 0.0));
EXPECT_EQ("0", format("{:g}", 0.0)); EXPECT_EQ("0", format("{:g}", 0.0));
@ -1422,6 +1249,7 @@ TEST(FormatterTest, FormatDouble) {
EXPECT_EQ("392.65", format("{:G}", 392.65)); EXPECT_EQ("392.65", format("{:G}", 392.65));
EXPECT_EQ("392.650000", format("{:f}", 392.65)); EXPECT_EQ("392.650000", format("{:f}", 392.65));
EXPECT_EQ("392.650000", format("{:F}", 392.65)); EXPECT_EQ("392.650000", format("{:F}", 392.65));
EXPECT_EQ("42", format("{:L}", 42.0));
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "%e", 392.65); safe_sprintf(buffer, "%e", 392.65);
EXPECT_EQ(buffer, format("{0:e}", 392.65)); EXPECT_EQ(buffer, format("{0:e}", 392.65));
@ -1492,7 +1320,7 @@ TEST(FormatterTest, FormatLongDouble) {
} }
TEST(FormatterTest, FormatChar) { TEST(FormatterTest, FormatChar) {
const char types[] = "cbBdoxXnL"; const char types[] = "cbBdoxXL";
check_unknown_types('a', types, "char"); check_unknown_types('a', types, "char");
EXPECT_EQ("a", format("{0}", 'a')); EXPECT_EQ("a", format("{0}", 'a'));
EXPECT_EQ("z", format("{0:c}", 'z')); EXPECT_EQ("z", format("{0:c}", 'z'));
@ -1608,12 +1436,46 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
} }
#endif #endif
// std::is_constructible is broken in MSVC until version 2015.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; }
};
TEST(FormatTest, FormatExplicitlyConvertibleToWStringView) {
EXPECT_EQ(L"foo",
fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
}
#endif
namespace fake_qt {
class QString {
public:
QString(const wchar_t* s) : s_(std::make_shared<std::wstring>(s)) {}
const wchar_t* utf16() const FMT_NOEXCEPT { return s_->data(); }
int size() const FMT_NOEXCEPT { return static_cast<int>(s_->size()); }
private:
std::shared_ptr<std::wstring> s_;
};
fmt::basic_string_view<wchar_t> to_string_view(const QString& s) FMT_NOEXCEPT {
return {s.utf16(), static_cast<size_t>(s.size())};
}
} // namespace fake_qt
TEST(FormatTest, FormatForeignStrings) {
using fake_qt::QString;
EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42");
EXPECT_EQ(fmt::format(QString(L"{}"), QString(L"42")), L"42");
}
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <> struct formatter<Date> { template <> struct formatter<Date> {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin(); auto it = ctx.begin();
if (*it == 'd') ++it; if (it != ctx.end() && *it == 'd') ++it;
return it; return it;
} }
@ -1649,7 +1511,7 @@ TEST(FormatterTest, CustomFormat) {
TEST(FormatterTest, CustomFormatTo) { TEST(FormatterTest, CustomFormatTo) {
char buf[10] = {}; char buf[10] = {};
auto end = auto end =
&*fmt::format_to(fmt::internal::make_checked(buf, 10), "{}", Answer()); &*fmt::format_to(fmt::detail::make_checked(buf, 10), "{}", Answer());
EXPECT_EQ(end, buf + 2); EXPECT_EQ(end, buf + 2);
EXPECT_STREQ(buf, "42"); EXPECT_STREQ(buf, "42");
} }
@ -1759,7 +1621,7 @@ TEST(FormatTest, Print) {
"Don't panic!"); "Don't panic!");
#endif #endif
// Check that the wide print overload compiles. // Check that the wide print overload compiles.
if (fmt::internal::const_check(false)) fmt::print(L"test"); if (fmt::detail::const_check(false)) fmt::print(L"test");
} }
TEST(FormatTest, Variadic) { TEST(FormatTest, Variadic) {
@ -1770,9 +1632,9 @@ TEST(FormatTest, Variadic) {
TEST(FormatTest, Dynamic) { TEST(FormatTest, Dynamic) {
typedef fmt::format_context ctx; typedef fmt::format_context ctx;
std::vector<fmt::basic_format_arg<ctx>> args; std::vector<fmt::basic_format_arg<ctx>> args;
args.emplace_back(fmt::internal::make_arg<ctx>(42)); args.emplace_back(fmt::detail::make_arg<ctx>(42));
args.emplace_back(fmt::internal::make_arg<ctx>("abc1")); args.emplace_back(fmt::detail::make_arg<ctx>("abc1"));
args.emplace_back(fmt::internal::make_arg<ctx>(1.5f)); args.emplace_back(fmt::detail::make_arg<ctx>(1.5f));
std::string result = fmt::vformat( std::string result = fmt::vformat(
"{} and {} and {}", "{} and {} and {}",
@ -1944,10 +1806,10 @@ TEST(FormatTest, StrongEnum) {
} }
#endif #endif
using buffer_range = fmt::buffer_range<char>; using buffer_iterator = fmt::format_context::iterator;
class mock_arg_formatter class mock_arg_formatter
: public fmt::internal::arg_formatter_base<buffer_range> { : public fmt::detail::arg_formatter_base<buffer_iterator, char> {
private: private:
#if FMT_USE_INT128 #if FMT_USE_INT128
MOCK_METHOD1(call, void(__int128_t value)); MOCK_METHOD1(call, void(__int128_t value));
@ -1956,24 +1818,23 @@ class mock_arg_formatter
#endif #endif
public: public:
typedef fmt::internal::arg_formatter_base<buffer_range> base; using base = fmt::detail::arg_formatter_base<buffer_iterator, char>;
typedef buffer_range range;
mock_arg_formatter(fmt::format_context& ctx, fmt::format_parse_context*, mock_arg_formatter(fmt::format_context& ctx, fmt::format_parse_context*,
fmt::format_specs* s = nullptr) fmt::format_specs* s = nullptr, const char* = nullptr)
: base(fmt::internal::get_container(ctx.out()), s, ctx.locale()) { : base(ctx.out(), s, ctx.locale()) {
EXPECT_CALL(*this, call(42)); EXPECT_CALL(*this, call(42));
} }
template <typename T> template <typename T>
typename std::enable_if<fmt::internal::is_integral<T>::value, iterator>::type typename std::enable_if<fmt::detail::is_integral<T>::value, iterator>::type
operator()(T value) { operator()(T value) {
call(value); call(value);
return base::operator()(value); return base::operator()(value);
} }
template <typename T> template <typename T>
typename std::enable_if<!fmt::internal::is_integral<T>::value, iterator>::type typename std::enable_if<!fmt::detail::is_integral<T>::value, iterator>::type
operator()(T value) { operator()(T value) {
return base::operator()(value); return base::operator()(value);
} }
@ -1985,7 +1846,9 @@ class mock_arg_formatter
static void custom_vformat(fmt::string_view format_str, fmt::format_args args) { static void custom_vformat(fmt::string_view format_str, fmt::format_args args) {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
fmt::vformat_to<mock_arg_formatter>(buffer, format_str, args); fmt::detail::buffer<char>& base = buffer;
fmt::vformat_to<mock_arg_formatter>(std::back_inserter(base), format_str,
args);
} }
template <typename... Args> template <typename... Args>
@ -2025,7 +1888,7 @@ TEST(FormatTest, DynamicFormatter) {
"cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{:{0}}", num), format_error, EXPECT_THROW_MSG(format("{:{0}}", num), format_error,
"cannot switch from automatic to manual argument indexing"); "cannot switch from automatic to manual argument indexing");
#if FMT_NUMERIC_ALIGN #if FMT_DEPRECATED_NUMERIC_ALIGN
EXPECT_THROW_MSG(format("{:=}", str), format_error, EXPECT_THROW_MSG(format("{:=}", str), format_error,
"format specifier requires numeric argument"); "format specifier requires numeric argument");
#endif #endif
@ -2043,9 +1906,30 @@ TEST(FormatTest, DynamicFormatter) {
"precision not allowed for this argument type"); "precision not allowed for this argument type");
} }
namespace adl_test {
namespace fmt {
namespace detail {
struct foo {};
template <typename, typename OutputIt> void write(OutputIt, foo) = delete;
} // namespace detail
} // namespace fmt
} // namespace adl_test
FMT_BEGIN_NAMESPACE
template <>
struct formatter<adl_test::fmt::detail::foo> : formatter<std::string> {
template <typename FormatContext>
auto format(adl_test::fmt::detail::foo, FormatContext& ctx)
-> decltype(ctx.out()) {
return formatter<std::string>::format("foo", ctx);
}
};
FMT_END_NAMESPACE
TEST(FormatTest, ToString) { TEST(FormatTest, ToString) {
EXPECT_EQ("42", fmt::to_string(42)); EXPECT_EQ("42", fmt::to_string(42));
EXPECT_EQ("0x1234", fmt::to_string(reinterpret_cast<void*>(0x1234))); EXPECT_EQ("0x1234", fmt::to_string(reinterpret_cast<void*>(0x1234)));
EXPECT_EQ("foo", fmt::to_string(adl_test::fmt::detail::foo()));
} }
TEST(FormatTest, ToWString) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } TEST(FormatTest, ToWString) { EXPECT_EQ(L"42", fmt::to_wstring(42)); }
@ -2159,7 +2043,7 @@ struct test_arg_id_handler {
template <size_t N> template <size_t N>
FMT_CONSTEXPR test_arg_id_handler parse_arg_id(const char (&s)[N]) { FMT_CONSTEXPR test_arg_id_handler parse_arg_id(const char (&s)[N]) {
test_arg_id_handler h; test_arg_id_handler h;
fmt::internal::parse_arg_id(s, s + N, h); fmt::detail::parse_arg_id(s, s + N, h);
return h; return h;
} }
@ -2180,9 +2064,9 @@ struct test_format_specs_handler {
fmt::align_t align = fmt::align::none; fmt::align_t align = fmt::align::none;
char fill = 0; char fill = 0;
int width = 0; int width = 0;
fmt::internal::arg_ref<char> width_ref; fmt::detail::arg_ref<char> width_ref;
int precision = 0; int precision = 0;
fmt::internal::arg_ref<char> precision_ref; fmt::detail::arg_ref<char> precision_ref;
char type = 0; char type = 0;
// Workaround for MSVC2017 bug that results in "expression did not evaluate // Workaround for MSVC2017 bug that results in "expression did not evaluate
@ -2208,12 +2092,12 @@ struct test_format_specs_handler {
FMT_CONSTEXPR void on_zero() { res = ZERO; } FMT_CONSTEXPR void on_zero() { res = ZERO; }
FMT_CONSTEXPR void on_width(int w) { width = w; } FMT_CONSTEXPR void on_width(int w) { width = w; }
FMT_CONSTEXPR void on_dynamic_width(fmt::internal::auto_id) {} FMT_CONSTEXPR void on_dynamic_width(fmt::detail::auto_id) {}
FMT_CONSTEXPR void on_dynamic_width(int index) { width_ref = index; } FMT_CONSTEXPR void on_dynamic_width(int index) { width_ref = index; }
FMT_CONSTEXPR void on_dynamic_width(string_view) {} FMT_CONSTEXPR void on_dynamic_width(string_view) {}
FMT_CONSTEXPR void on_precision(int p) { precision = p; } FMT_CONSTEXPR void on_precision(int p) { precision = p; }
FMT_CONSTEXPR void on_dynamic_precision(fmt::internal::auto_id) {} FMT_CONSTEXPR void on_dynamic_precision(fmt::detail::auto_id) {}
FMT_CONSTEXPR void on_dynamic_precision(int index) { precision_ref = index; } FMT_CONSTEXPR void on_dynamic_precision(int index) { precision_ref = index; }
FMT_CONSTEXPR void on_dynamic_precision(string_view) {} FMT_CONSTEXPR void on_dynamic_precision(string_view) {}
@ -2225,7 +2109,7 @@ struct test_format_specs_handler {
template <size_t N> template <size_t N>
FMT_CONSTEXPR test_format_specs_handler parse_test_specs(const char (&s)[N]) { FMT_CONSTEXPR test_format_specs_handler parse_test_specs(const char (&s)[N]) {
test_format_specs_handler h; test_format_specs_handler h;
fmt::internal::parse_format_specs(s, s + N, h); fmt::detail::parse_format_specs(s, s + N, h);
return h; return h;
} }
@ -2259,8 +2143,9 @@ struct test_parse_context {
}; };
struct test_context { struct test_context {
typedef char char_type; using char_type = char;
typedef fmt::basic_format_arg<test_context> format_arg; using format_arg = fmt::basic_format_arg<test_context>;
using parse_context_type = fmt::format_parse_context;
template <typename T> struct formatter_type { template <typename T> struct formatter_type {
typedef fmt::formatter<T, char_type> type; typedef fmt::formatter<T, char_type> type;
@ -2268,7 +2153,7 @@ struct test_context {
template <typename Id> template <typename Id>
FMT_CONSTEXPR fmt::basic_format_arg<test_context> arg(Id id) { FMT_CONSTEXPR fmt::basic_format_arg<test_context> arg(Id id) {
return fmt::internal::make_arg<test_context>(id); return fmt::detail::make_arg<test_context>(id);
} }
void on_error(const char*) {} void on_error(const char*) {}
@ -2281,7 +2166,7 @@ FMT_CONSTEXPR fmt::format_specs parse_specs(const char (&s)[N]) {
auto specs = fmt::format_specs(); auto specs = fmt::format_specs();
auto parse_ctx = test_parse_context(); auto parse_ctx = test_parse_context();
auto ctx = test_context(); auto ctx = test_context();
fmt::internal::specs_handler<test_parse_context, test_context> h( fmt::detail::specs_handler<test_parse_context, test_context> h(
specs, parse_ctx, ctx); specs, parse_ctx, ctx);
parse_format_specs(s, s + N, h); parse_format_specs(s, s + N, h);
return specs; return specs;
@ -2305,11 +2190,11 @@ TEST(FormatTest, ConstexprSpecsHandler) {
} }
template <size_t N> template <size_t N>
FMT_CONSTEXPR fmt::internal::dynamic_format_specs<char> parse_dynamic_specs( FMT_CONSTEXPR fmt::detail::dynamic_format_specs<char> parse_dynamic_specs(
const char (&s)[N]) { const char (&s)[N]) {
fmt::internal::dynamic_format_specs<char> specs; fmt::detail::dynamic_format_specs<char> specs;
test_parse_context ctx{}; test_parse_context ctx{};
fmt::internal::dynamic_specs_handler<test_parse_context> h(specs, ctx); fmt::detail::dynamic_specs_handler<test_parse_context> h(specs, ctx);
parse_format_specs(s, s + N, h); parse_format_specs(s, s + N, h);
return specs; return specs;
} }
@ -2333,8 +2218,8 @@ TEST(FormatTest, ConstexprDynamicSpecsHandler) {
template <size_t N> template <size_t N>
FMT_CONSTEXPR test_format_specs_handler check_specs(const char (&s)[N]) { FMT_CONSTEXPR test_format_specs_handler check_specs(const char (&s)[N]) {
fmt::internal::specs_checker<test_format_specs_handler> checker( fmt::detail::specs_checker<test_format_specs_handler> checker(
test_format_specs_handler(), fmt::internal::type::double_type); test_format_specs_handler(), fmt::detail::type::double_type);
parse_format_specs(s, s + N, checker); parse_format_specs(s, s + N, checker);
return checker; return checker;
} }
@ -2359,13 +2244,14 @@ TEST(FormatTest, ConstexprSpecsChecker) {
struct test_format_string_handler { struct test_format_string_handler {
FMT_CONSTEXPR void on_text(const char*, const char*) {} FMT_CONSTEXPR void on_text(const char*, const char*) {}
FMT_CONSTEXPR void on_arg_id() {} FMT_CONSTEXPR int on_arg_id() { return 0; }
template <typename T> FMT_CONSTEXPR void on_arg_id(T) {} template <typename T> FMT_CONSTEXPR int on_arg_id(T) { return 0; }
FMT_CONSTEXPR void on_replacement_field(const char*) {} FMT_CONSTEXPR void on_replacement_field(int, const char*) {}
FMT_CONSTEXPR const char* on_format_specs(const char* begin, const char*) { FMT_CONSTEXPR const char* on_format_specs(int, const char* begin,
const char*) {
return begin; return begin;
} }
@ -2376,7 +2262,7 @@ struct test_format_string_handler {
template <size_t N> FMT_CONSTEXPR bool parse_string(const char (&s)[N]) { template <size_t N> FMT_CONSTEXPR bool parse_string(const char (&s)[N]) {
test_format_string_handler h; test_format_string_handler h;
fmt::internal::parse_format_string<true>(fmt::string_view(s, N - 1), h); fmt::detail::parse_format_string<true>(fmt::string_view(s, N - 1), h);
return !h.error; return !h.error;
} }
@ -2420,8 +2306,10 @@ FMT_CONSTEXPR bool equal(const char* s1, const char* s2) {
template <typename... Args> template <typename... Args>
FMT_CONSTEXPR bool test_error(const char* fmt, const char* expected_error) { FMT_CONSTEXPR bool test_error(const char* fmt, const char* expected_error) {
const char* actual_error = nullptr; const char* actual_error = nullptr;
fmt::internal::do_check_format_string<char, test_error_handler, Args...>( string_view s(fmt, len(fmt));
string_view(fmt, len(fmt)), test_error_handler(actual_error)); fmt::detail::format_string_checker<char, test_error_handler, Args...> checker(
s, test_error_handler(actual_error));
fmt::detail::parse_format_string<true>(s, checker);
return equal(actual_error, expected_error); return equal(actual_error, expected_error);
} }
@ -2434,13 +2322,13 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR_NOARGS("foo", nullptr); EXPECT_ERROR_NOARGS("foo", nullptr);
EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string"); EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string");
EXPECT_ERROR("{0:s", "unknown format specifier", Date); EXPECT_ERROR("{0:s", "unknown format specifier", Date);
# if FMT_MSC_VER >= 1916 # if !FMT_MSC_VER || FMT_MSC_VER >= 1916
// This causes an internal compiler error in MSVC2017. // This causes an detail compiler error in MSVC2017.
EXPECT_ERROR("{:{<}", "invalid fill character '{'", int); EXPECT_ERROR("{:{<}", "invalid fill character '{'", int);
EXPECT_ERROR("{:10000000000}", "number is too big", int); EXPECT_ERROR("{:10000000000}", "number is too big", int);
EXPECT_ERROR("{:.10000000000}", "number is too big", int); EXPECT_ERROR("{:.10000000000}", "number is too big", int);
EXPECT_ERROR_NOARGS("{:x}", "argument index out of range"); EXPECT_ERROR_NOARGS("{:x}", "argument not found");
# if FMT_NUMERIC_ALIGN # if FMT_DEPRECATED_NUMERIC_ALIGN
EXPECT_ERROR("{0:=5", "unknown format specifier", int); EXPECT_ERROR("{0:=5", "unknown format specifier", int);
EXPECT_ERROR("{:=}", "format specifier requires numeric argument", EXPECT_ERROR("{:=}", "format specifier requires numeric argument",
const char*); const char*);
@ -2458,6 +2346,8 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{:+}", "format specifier requires signed argument", unsigned); EXPECT_ERROR("{:+}", "format specifier requires signed argument", unsigned);
EXPECT_ERROR("{:-}", "format specifier requires signed argument", unsigned); EXPECT_ERROR("{:-}", "format specifier requires signed argument", unsigned);
EXPECT_ERROR("{: }", "format specifier requires signed argument", unsigned); EXPECT_ERROR("{: }", "format specifier requires signed argument", unsigned);
EXPECT_ERROR("{:{}}", "argument not found", int);
EXPECT_ERROR("{:.{}}", "argument not found", double);
EXPECT_ERROR("{:.2}", "precision not allowed for this argument type", int); EXPECT_ERROR("{:.2}", "precision not allowed for this argument type", int);
EXPECT_ERROR("{:s}", "invalid type specifier", int); EXPECT_ERROR("{:s}", "invalid type specifier", int);
EXPECT_ERROR("{:s}", "invalid type specifier", bool); EXPECT_ERROR("{:s}", "invalid type specifier", bool);
@ -2480,8 +2370,8 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{:.{0x}}", "invalid format string", int); EXPECT_ERROR("{:.{0x}}", "invalid format string", int);
EXPECT_ERROR("{:.{-}}", "invalid format string", int); EXPECT_ERROR("{:.{-}}", "invalid format string", int);
EXPECT_ERROR("{:.x}", "missing precision specifier", int); EXPECT_ERROR("{:.x}", "missing precision specifier", int);
EXPECT_ERROR_NOARGS("{}", "argument index out of range"); EXPECT_ERROR_NOARGS("{}", "argument not found");
EXPECT_ERROR("{1}", "argument index out of range", int); EXPECT_ERROR("{1}", "argument not found", int);
EXPECT_ERROR("{1}{}", EXPECT_ERROR("{1}{}",
"cannot switch from manual to automatic argument indexing", int, "cannot switch from manual to automatic argument indexing", int,
int); int);
@ -2492,7 +2382,7 @@ TEST(FormatTest, FormatStringErrors) {
TEST(FormatTest, VFormatTo) { TEST(FormatTest, VFormatTo) {
typedef fmt::format_context context; typedef fmt::format_context context;
fmt::basic_format_arg<context> arg = fmt::internal::make_arg<context>(42); fmt::basic_format_arg<context> arg = fmt::detail::make_arg<context>(42);
fmt::basic_format_args<context> args(&arg, 1); fmt::basic_format_args<context> args(&arg, 1);
std::string s; std::string s;
fmt::vformat_to(std::back_inserter(s), "{}", args); fmt::vformat_to(std::back_inserter(s), "{}", args);
@ -2502,7 +2392,7 @@ TEST(FormatTest, VFormatTo) {
EXPECT_EQ("42", s); EXPECT_EQ("42", s);
typedef fmt::wformat_context wcontext; typedef fmt::wformat_context wcontext;
fmt::basic_format_arg<wcontext> warg = fmt::internal::make_arg<wcontext>(42); fmt::basic_format_arg<wcontext> warg = fmt::detail::make_arg<wcontext>(42);
fmt::basic_format_args<wcontext> wargs(&warg, 1); fmt::basic_format_args<wcontext> wargs(&warg, 1);
std::wstring w; std::wstring w;
fmt::vformat_to(std::back_inserter(w), L"{}", wargs); fmt::vformat_to(std::back_inserter(w), L"{}", wargs);
@ -2530,7 +2420,7 @@ TEST(FormatTest, EmphasisNonHeaderOnly) {
} }
TEST(FormatTest, CharTraitsIsNotAmbiguous) { TEST(FormatTest, CharTraitsIsNotAmbiguous) {
// Test that we don't inject internal names into the std namespace. // Test that we don't inject detail names into the std namespace.
using namespace std; using namespace std;
char_traits<char>::char_type c; char_traits<char>::char_type c;
(void)c; (void)c;
@ -2568,13 +2458,12 @@ template <typename S> std::string from_u8str(const S& str) {
} }
TEST(FormatTest, FormatUTF8Precision) { TEST(FormatTest, FormatUTF8Precision) {
using str_type = std::basic_string<fmt::internal::char8_type>; using str_type = std::basic_string<fmt::detail::char8_type>;
str_type format( str_type format(reinterpret_cast<const fmt::detail::char8_type*>(u8"{:.4}"));
reinterpret_cast<const fmt::internal::char8_type*>(u8"{:.4}")); str_type str(reinterpret_cast<const fmt::detail::char8_type*>(
str_type str(reinterpret_cast<const fmt::internal::char8_type*>(
u8"caf\u00e9s")); // cafés u8"caf\u00e9s")); // cafés
auto result = fmt::format(format, str); auto result = fmt::format(format, str);
EXPECT_EQ(fmt::internal::count_code_points(result), 4); EXPECT_EQ(fmt::detail::count_code_points(result), 4);
EXPECT_EQ(result.size(), 5); EXPECT_EQ(result.size(), 5);
EXPECT_EQ(from_u8str(result), from_u8str(str.substr(0, 5))); EXPECT_EQ(from_u8str(result), from_u8str(str.substr(0, 5)));
} }

View file

@ -7,14 +7,19 @@ in fmt. It is a part of the continous fuzzing at
The source code is modified to make the fuzzing possible without locking up on The source code is modified to make the fuzzing possible without locking up on
resource exhaustion: resource exhaustion:
```cpp ```cpp
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION #ifdef FMT_FUZZ
if(spec.precision>100000) { if(spec.precision>100000) {
throw std::runtime_error("fuzz mode - avoiding large precision"); throw std::runtime_error("fuzz mode - avoiding large precision");
} }
#endif #endif
``` ```
This macro is the defacto standard for making fuzzing practically possible, see This macro `FMT_FUZZ` is enabled on OSS-Fuzz builds and makes fuzzing
[the libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode). practically possible. It is used in fmt code to prevent resource exhaustion in
fuzzing mode.
The macro `FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION` is the
defacto standard for making fuzzing practically possible to disable certain
fuzzing-unfriendly features (for example, randomness), see [the libFuzzer
documentation](https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode).
## Running the fuzzers locally ## Running the fuzzers locally

View file

@ -25,7 +25,7 @@ void invoke_inner(fmt::string_view formatstring, const Item item) {
// Item is the underlying type for duration (int, long etc) // Item is the underlying type for duration (int, long etc)
template <typename Item> template <typename Item>
void invoke_outer(const uint8_t* Data, std::size_t Size, const int scaling) { void invoke_outer(const uint8_t* Data, size_t Size, const int scaling) {
// always use a fixed location of the data // always use a fixed location of the data
using fmt_fuzzer::Nfixed; using fmt_fuzzer::Nfixed;
@ -97,7 +97,7 @@ void invoke_outer(const uint8_t* Data, std::size_t Size, const int scaling) {
// doit_impl<Item,std::yotta>(buf.data(),item); // doit_impl<Item,std::yotta>(buf.data(),item);
} }
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 4) { if (Size <= 4) {
return 0; return 0;
} }

View file

@ -4,7 +4,7 @@
#include <vector> #include <vector>
#include "fuzzer_common.h" #include "fuzzer_common.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size); extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size);
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
std::ifstream in(argv[i]); std::ifstream in(argv[i]);
@ -13,7 +13,7 @@ int main(int argc, char* argv[]) {
const auto pos = in.tellg(); const auto pos = in.tellg();
assert(pos >= 0); assert(pos >= 0);
in.seekg(0, std::ios_base::beg); in.seekg(0, std::ios_base::beg);
std::vector<char> buf(static_cast<std::size_t>(pos)); std::vector<char> buf(static_cast<size_t>(pos));
in.read(buf.data(), static_cast<long>(buf.size())); in.read(buf.data(), static_cast<long>(buf.size()));
assert(in.gcount() == pos); assert(in.gcount() == pos);
LLVMFuzzerTestOneInput(fmt_fuzzer::as_bytes(buf.data()), buf.size()); LLVMFuzzerTestOneInput(fmt_fuzzer::as_bytes(buf.data()), buf.size());

View file

@ -10,7 +10,7 @@
#include "fuzzer_common.h" #include "fuzzer_common.h"
template <typename Item1> template <typename Item1>
void invoke_fmt(const uint8_t* Data, std::size_t Size, unsigned int argsize) { void invoke_fmt(const uint8_t* Data, size_t Size, unsigned int argsize) {
constexpr auto N1 = sizeof(Item1); constexpr auto N1 = sizeof(Item1);
static_assert(N1 <= fmt_fuzzer::Nfixed, "Nfixed too small"); static_assert(N1 <= fmt_fuzzer::Nfixed, "Nfixed too small");
if (Size <= fmt_fuzzer::Nfixed) { if (Size <= fmt_fuzzer::Nfixed) {
@ -29,11 +29,11 @@ void invoke_fmt(const uint8_t* Data, std::size_t Size, unsigned int argsize) {
// allocating buffers separately is slower, but increases chances // allocating buffers separately is slower, but increases chances
// of detecting memory errors // of detecting memory errors
#if FMT_FUZZ_SEPARATE_ALLOCATION #if FMT_FUZZ_SEPARATE_ALLOCATION
std::vector<char> argnamebuffer(argsize); std::vector<char> argnamebuffer(argsize + 1);
std::memcpy(argnamebuffer.data(), Data, argsize); std::memcpy(argnamebuffer.data(), Data, argsize);
auto argname = fmt::string_view(argnamebuffer.data(), argsize); auto argname = argnamebuffer.data();
#else #else
auto argname = fmt::string_view(fmt_fuzzer::as_chars(Data), argsize); auto argname = fmt_fuzzer::as_chars(Data);
#endif #endif
Data += argsize; Data += argsize;
Size -= argsize; Size -= argsize;
@ -105,7 +105,7 @@ template <typename Callback> void invoke(int index, Callback callback) {
} }
} }
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 3) { if (Size <= 3) {
return 0; return 0;
} }

View file

@ -13,7 +13,7 @@
using fmt_fuzzer::Nfixed; using fmt_fuzzer::Nfixed;
template <typename Item> template <typename Item>
void invoke_fmt(const uint8_t* Data, std::size_t Size) { void invoke_fmt(const uint8_t* Data, size_t Size) {
constexpr auto N = sizeof(Item); constexpr auto N = sizeof(Item);
static_assert(N <= Nfixed, "Nfixed is too small"); static_assert(N <= Nfixed, "Nfixed is too small");
if (Size <= Nfixed) { if (Size <= Nfixed) {
@ -40,7 +40,7 @@ void invoke_fmt(const uint8_t* Data, std::size_t Size) {
#endif #endif
} }
void invoke_fmt_time(const uint8_t* Data, std::size_t Size) { void invoke_fmt_time(const uint8_t* Data, size_t Size) {
using Item = std::time_t; using Item = std::time_t;
constexpr auto N = sizeof(Item); constexpr auto N = sizeof(Item);
static_assert(N <= Nfixed, "Nfixed too small"); static_assert(N <= Nfixed, "Nfixed too small");
@ -69,7 +69,7 @@ void invoke_fmt_time(const uint8_t* Data, std::size_t Size) {
} }
} }
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 3) { if (Size <= 3) {
return 0; return 0;
} }

View file

@ -10,7 +10,7 @@
using fmt_fuzzer::Nfixed; using fmt_fuzzer::Nfixed;
template <typename Item1, typename Item2> template <typename Item1, typename Item2>
void invoke_fmt(const uint8_t* Data, std::size_t Size) { void invoke_fmt(const uint8_t* Data, size_t Size) {
constexpr auto N1 = sizeof(Item1); constexpr auto N1 = sizeof(Item1);
constexpr auto N2 = sizeof(Item2); constexpr auto N2 = sizeof(Item2);
static_assert(N1 <= Nfixed, "size1 exceeded"); static_assert(N1 <= Nfixed, "size1 exceeded");
@ -90,7 +90,7 @@ template <typename Callback> void invoke(int index, Callback callback) {
} }
} }
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 3) { if (Size <= 3) {
return 0; return 0;
} }

View file

@ -10,7 +10,7 @@
constexpr auto Nfixed = fmt_fuzzer::Nfixed; constexpr auto Nfixed = fmt_fuzzer::Nfixed;
template <typename Item1, typename Item2> template <typename Item1, typename Item2>
void invoke_fmt(const uint8_t* Data, std::size_t Size) { void invoke_fmt(const uint8_t* Data, size_t Size) {
constexpr auto N1 = sizeof(Item1); constexpr auto N1 = sizeof(Item1);
constexpr auto N2 = sizeof(Item2); constexpr auto N2 = sizeof(Item2);
static_assert(N1 <= Nfixed, "size1 exceeded"); static_assert(N1 <= Nfixed, "size1 exceeded");
@ -86,7 +86,7 @@ template <typename Callback> void invoke(int index, Callback callback) {
} }
} }
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 3) { if (Size <= 3) {
return 0; return 0;
} }

View file

@ -344,6 +344,7 @@ class GTEST_API_ SingleFailureChecker {
# include <sys/mman.h> // NOLINT # include <sys/mman.h> // NOLINT
# include <sys/time.h> // NOLINT # include <sys/time.h> // NOLINT
# include <unistd.h> // NOLINT # include <unistd.h> // NOLINT
# include <string> # include <string>
#elif GTEST_OS_SYMBIAN #elif GTEST_OS_SYMBIAN
@ -7728,6 +7729,7 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() {
# include <sys/syslimits.h> # include <sys/syslimits.h>
#else #else
# include <limits.h> # include <limits.h>
# include <climits> // Some Linux distributions define PATH_MAX here. # include <climits> // Some Linux distributions define PATH_MAX here.
#endif // GTEST_OS_WINDOWS_MOBILE #endif // GTEST_OS_WINDOWS_MOBILE
@ -8899,6 +8901,7 @@ const char* StringFromGTestEnv(const char* flag, const char* default_value) {
#include <ctype.h> #include <ctype.h>
#include <stdio.h> #include <stdio.h>
#include <ostream> // NOLINT #include <ostream> // NOLINT
#include <string> #include <string>
@ -9494,6 +9497,7 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames(
// This file implements cardinalities. // This file implements cardinalities.
#include <limits.h> #include <limits.h>
#include <ostream> // NOLINT #include <ostream> // NOLINT
#include <sstream> #include <sstream>
#include <string> #include <string>
@ -9646,6 +9650,7 @@ GTEST_API_ Cardinality Exactly(int n) { return Between(n, n); }
// USE THEM IN USER CODE. // USE THEM IN USER CODE.
#include <ctype.h> #include <ctype.h>
#include <ostream> // NOLINT #include <ostream> // NOLINT
#include <string> #include <string>
@ -9808,6 +9813,7 @@ GTEST_API_ void Log(LogSeverity severity, const string& message,
// utilities for defining matchers. // utilities for defining matchers.
#include <string.h> #include <string.h>
#include <sstream> #include <sstream>
#include <string> #include <string>
@ -10291,6 +10297,7 @@ bool UnorderedElementsAreMatcherImplBase::
// EXPECT_CALL). // EXPECT_CALL).
#include <stdlib.h> #include <stdlib.h>
#include <iostream> // NOLINT #include <iostream> // NOLINT
#include <map> #include <map>
#include <set> #include <set>

View file

@ -8,6 +8,7 @@
#include "gtest-extra.h" #include "gtest-extra.h"
#include <gtest/gtest-spi.h> #include <gtest/gtest-spi.h>
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
@ -167,22 +168,24 @@ TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
// Tests that assertion arguments are evaluated exactly once. // Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, WriteTests) { TEST_F(SingleEvaluationTest, WriteTests) {
// successful EXPECT_WRITE // successful EXPECT_WRITE
EXPECT_WRITE(stdout, EXPECT_WRITE(
{ // NOLINT stdout,
a_++; { // NOLINT
std::printf("test"); a_++;
}, std::printf("test");
(b_++, "test")); },
(b_++, "test"));
EXPECT_EQ(1, a_); EXPECT_EQ(1, a_);
EXPECT_EQ(1, b_); EXPECT_EQ(1, b_);
// failed EXPECT_WRITE // failed EXPECT_WRITE
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(
{ // NOLINT stdout,
a_++; { // NOLINT
std::printf("test"); a_++;
}, std::printf("test");
(b_++, "other")), },
(b_++, "other")),
"Actual: test"); "Actual: test");
EXPECT_EQ(2, a_); EXPECT_EQ(2, a_);
EXPECT_EQ(2, b_); EXPECT_EQ(2, b_);
@ -417,16 +420,17 @@ TEST(OutputRedirectTest, ErrorInDtor) {
std::unique_ptr<OutputRedirect> redir(new OutputRedirect(f.get())); std::unique_ptr<OutputRedirect> redir(new OutputRedirect(f.get()));
// Put a character in a file buffer. // Put a character in a file buffer.
EXPECT_EQ('x', fputc('x', f.get())); EXPECT_EQ('x', fputc('x', f.get()));
EXPECT_WRITE(stderr, EXPECT_WRITE(
{ stderr,
// The close function must be called inside EXPECT_WRITE, {
// otherwise the system may recycle closed file descriptor when // The close function must be called inside EXPECT_WRITE,
// redirecting the output in EXPECT_STDERR and the second close // otherwise the system may recycle closed file descriptor when
// will break output redirection. // redirecting the output in EXPECT_STDERR and the second close
FMT_POSIX(close(write_fd)); // will break output redirection.
SUPPRESS_ASSERT(redir.reset(nullptr)); FMT_POSIX(close(write_fd));
}, SUPPRESS_ASSERT(redir.reset(nullptr));
format_system_error(EBADF, "cannot flush stream")); },
format_system_error(EBADF, "cannot flush stream"));
write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail
} }

View file

@ -57,7 +57,7 @@ std::string OutputRedirect::restore_and_read() {
if (read_end_.descriptor() == -1) return content; // Already read. if (read_end_.descriptor() == -1) return content; // Already read.
enum { BUFFER_SIZE = 4096 }; enum { BUFFER_SIZE = 4096 };
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
std::size_t count = 0; size_t count = 0;
do { do {
count = read_end_.read(buffer, BUFFER_SIZE); count = read_end_.read(buffer, BUFFER_SIZE);
content.append(buffer, count); content.append(buffer, count);
@ -66,9 +66,9 @@ std::string OutputRedirect::restore_and_read() {
return content; return content;
} }
std::string read(file& f, std::size_t count) { std::string read(file& f, size_t count) {
std::string buffer(count, '\0'); std::string buffer(count, '\0');
std::size_t n = 0, offset = 0; size_t n = 0, offset = 0;
do { do {
n = f.read(&buffer[offset], count - offset); n = f.read(&buffer[offset], count - offset);
// We can't read more than size_t bytes since count has type size_t. // We can't read more than size_t bytes since count has type size_t.

View file

@ -9,8 +9,9 @@
#define FMT_GTEST_EXTRA_H_ #define FMT_GTEST_EXTRA_H_
#include <string> #include <string>
#include "gmock.h"
#include "fmt/os.h" #include "fmt/os.h"
#include "gmock.h"
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \ #define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
@ -137,10 +138,11 @@ class SuppressAssert {
EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message) EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
// Attempts to read count characters from a file. // Attempts to read count characters from a file.
std::string read(fmt::file& f, std::size_t count); std::string read(fmt::file& f, size_t count);
# define EXPECT_READ(file, expected_content) \ # define EXPECT_READ(file, expected_content) \
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content))) EXPECT_EQ(expected_content, \
read(file, fmt::string_view(expected_content).size()))
#else #else
# define EXPECT_WRITE(file, statement, expected_output) SUCCEED() # define EXPECT_WRITE(file, statement, expected_output) SUCCEED()

View file

@ -6,9 +6,10 @@
// For the license information refer to format.h. // For the license information refer to format.h.
#include "fmt/locale.h" #include "fmt/locale.h"
#include "gmock.h" #include "gmock.h"
using fmt::internal::max_value; using fmt::detail::max_value;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Char> struct numpunct : std::numpunct<Char> { template <typename Char> struct numpunct : std::numpunct<Char> {
@ -41,49 +42,68 @@ template <typename Char> struct small_grouping : std::numpunct<Char> {
TEST(LocaleTest, DoubleDecimalPoint) { TEST(LocaleTest, DoubleDecimalPoint) {
std::locale loc(std::locale(), new numpunct<char>()); std::locale loc(std::locale(), new numpunct<char>());
EXPECT_EQ("1?23", fmt::format(loc, "{:n}", 1.23)); EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23));
} }
TEST(LocaleTest, Format) { TEST(LocaleTest, Format) {
std::locale loc(std::locale(), new numpunct<char>()); std::locale loc(std::locale(), new numpunct<char>());
EXPECT_EQ("1234567", fmt::format(std::locale(), "{:n}", 1234567)); EXPECT_EQ("1234567", fmt::format(std::locale(), "{:L}", 1234567));
EXPECT_EQ("1~234~567", fmt::format(loc, "{:n}", 1234567)); EXPECT_EQ("1~234~567", fmt::format(loc, "{:L}", 1234567));
EXPECT_EQ("-1~234~567", fmt::format(loc, "{:L}", -1234567));
fmt::format_arg_store<fmt::format_context, int> as{1234567}; fmt::format_arg_store<fmt::format_context, int> as{1234567};
EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:n}", fmt::format_args(as))); EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:L}", fmt::format_args(as)));
std::string s; std::string s;
fmt::format_to(std::back_inserter(s), loc, "{:n}", 1234567); fmt::format_to(std::back_inserter(s), loc, "{:L}", 1234567);
EXPECT_EQ("1~234~567", s); EXPECT_EQ("1~234~567", s);
std::locale no_grouping_loc(std::locale(), new no_grouping<char>()); std::locale no_grouping_loc(std::locale(), new no_grouping<char>());
EXPECT_EQ("1234567", fmt::format(no_grouping_loc, "{:n}", 1234567)); EXPECT_EQ("1234567", fmt::format(no_grouping_loc, "{:L}", 1234567));
std::locale special_grouping_loc(std::locale(), new special_grouping<char>()); std::locale special_grouping_loc(std::locale(), new special_grouping<char>());
EXPECT_EQ("1,23,45,678", fmt::format(special_grouping_loc, "{:n}", 12345678)); EXPECT_EQ("1,23,45,678", fmt::format(special_grouping_loc, "{:L}", 12345678));
EXPECT_EQ("12,345", fmt::format(special_grouping_loc, "{:L}", 12345));
std::locale small_grouping_loc(std::locale(), new small_grouping<char>()); std::locale small_grouping_loc(std::locale(), new small_grouping<char>());
EXPECT_EQ("4,2,9,4,9,6,7,2,9,5", EXPECT_EQ("4,2,9,4,9,6,7,2,9,5",
fmt::format(small_grouping_loc, "{:n}", max_value<uint32_t>())); fmt::format(small_grouping_loc, "{:L}", max_value<uint32_t>()));
}
TEST(LocaleTest, FormatDetaultAlign) {
std::locale special_grouping_loc(std::locale(), new special_grouping<char>());
EXPECT_EQ(" 12,345", fmt::format(special_grouping_loc, "{:8L}", 12345));
} }
TEST(LocaleTest, WFormat) { TEST(LocaleTest, WFormat) {
std::locale loc(std::locale(), new numpunct<wchar_t>()); std::locale loc(std::locale(), new numpunct<wchar_t>());
EXPECT_EQ(L"1234567", fmt::format(std::locale(), L"{:n}", 1234567)); EXPECT_EQ(L"1234567", fmt::format(std::locale(), L"{:L}", 1234567));
EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:n}", 1234567)); EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:L}", 1234567));
fmt::format_arg_store<fmt::wformat_context, int> as{1234567}; fmt::format_arg_store<fmt::wformat_context, int> as{1234567};
EXPECT_EQ(L"1~234~567", fmt::vformat(loc, L"{:n}", fmt::wformat_args(as))); EXPECT_EQ(L"1~234~567", fmt::vformat(loc, L"{:L}", fmt::wformat_args(as)));
EXPECT_EQ(L"1234567", fmt::format(std::locale("C"), L"{:n}", 1234567)); EXPECT_EQ(L"1234567", fmt::format(std::locale("C"), L"{:L}", 1234567));
std::locale no_grouping_loc(std::locale(), new no_grouping<wchar_t>()); std::locale no_grouping_loc(std::locale(), new no_grouping<wchar_t>());
EXPECT_EQ(L"1234567", fmt::format(no_grouping_loc, L"{:n}", 1234567)); EXPECT_EQ(L"1234567", fmt::format(no_grouping_loc, L"{:L}", 1234567));
std::locale special_grouping_loc(std::locale(), std::locale special_grouping_loc(std::locale(),
new special_grouping<wchar_t>()); new special_grouping<wchar_t>());
EXPECT_EQ(L"1,23,45,678", EXPECT_EQ(L"1,23,45,678",
fmt::format(special_grouping_loc, L"{:n}", 12345678)); fmt::format(special_grouping_loc, L"{:L}", 12345678));
std::locale small_grouping_loc(std::locale(), new small_grouping<wchar_t>()); std::locale small_grouping_loc(std::locale(), new small_grouping<wchar_t>());
EXPECT_EQ(L"4,2,9,4,9,6,7,2,9,5", EXPECT_EQ(L"4,2,9,4,9,6,7,2,9,5",
fmt::format(small_grouping_loc, L"{:n}", max_value<uint32_t>())); fmt::format(small_grouping_loc, L"{:L}", max_value<uint32_t>()));
}
TEST(LocaleTest, DoubleFormatter) {
auto loc = std::locale(std::locale(), new special_grouping<char>());
auto f = fmt::formatter<int>();
auto parse_ctx = fmt::format_parse_context("L");
f.parse(parse_ctx);
char buf[10] = {};
fmt::basic_format_context<char*, char> format_ctx(
buf, {}, fmt::detail::locale_ref(loc));
*f.format(12345, format_ctx) = 0;
EXPECT_STREQ("12,345", buf);
} }
#endif // FMT_STATIC_THOUSANDS_SEPARATOR #endif // FMT_STATIC_THOUSANDS_SEPARATOR

View file

@ -16,8 +16,8 @@ template <typename T> class mock_allocator {
mock_allocator() {} mock_allocator() {}
mock_allocator(const mock_allocator&) {} mock_allocator(const mock_allocator&) {}
typedef T value_type; typedef T value_type;
MOCK_METHOD1_T(allocate, T*(std::size_t n)); MOCK_METHOD1_T(allocate, T*(size_t n));
MOCK_METHOD2_T(deallocate, void(T* p, std::size_t n)); MOCK_METHOD2_T(deallocate, void(T* p, size_t n));
}; };
template <typename Allocator> class allocator_ref { template <typename Allocator> class allocator_ref {
@ -51,10 +51,10 @@ template <typename Allocator> class allocator_ref {
public: public:
Allocator* get() const { return alloc_; } Allocator* get() const { return alloc_; }
value_type* allocate(std::size_t n) { value_type* allocate(size_t n) {
return std::allocator_traits<Allocator>::allocate(*alloc_, n); return std::allocator_traits<Allocator>::allocate(*alloc_, n);
} }
void deallocate(value_type* p, std::size_t n) { alloc_->deallocate(p, n); } void deallocate(value_type* p, size_t n) { alloc_->deallocate(p, n); }
}; };
#endif // FMT_MOCK_ALLOCATOR_H_ #endif // FMT_MOCK_ALLOCATOR_H_

View file

@ -5,11 +5,12 @@
// //
// For the license information refer to format.h. // For the license information refer to format.h.
#include "fmt/os.h"
#include <cstdlib> // std::exit #include <cstdlib> // std::exit
#include <cstring> #include <cstring>
#include <memory> #include <memory>
#include "fmt/os.h"
#include "gtest-extra.h" #include "gtest-extra.h"
#include "util.h" #include "util.h"
@ -26,14 +27,14 @@ using fmt::error_code;
TEST(UtilTest, UTF16ToUTF8) { TEST(UtilTest, UTF16ToUTF8) {
std::string s = "ёжик"; std::string s = "ёжик";
fmt::internal::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A"); fmt::detail::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A");
EXPECT_EQ(s, u.str()); EXPECT_EQ(s, u.str());
EXPECT_EQ(s.size(), u.size()); EXPECT_EQ(s.size(), u.size());
} }
TEST(UtilTest, UTF16ToUTF8EmptyString) { TEST(UtilTest, UTF16ToUTF8EmptyString) {
std::string s = ""; std::string s = "";
fmt::internal::utf16_to_utf8 u(L""); fmt::detail::utf16_to_utf8 u(L"");
EXPECT_EQ(s, u.str()); EXPECT_EQ(s, u.str());
EXPECT_EQ(s.size(), u.size()); EXPECT_EQ(s.size(), u.size());
} }
@ -43,7 +44,7 @@ void check_utf_conversion_error(
const char* message, const char* message,
fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) { fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) {
fmt::memory_buffer out; fmt::memory_buffer out;
fmt::internal::format_windows_error(out, ERROR_INVALID_PARAMETER, message); fmt::detail::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
fmt::system_error error(0, ""); fmt::system_error error(0, "");
try { try {
(Converter)(str); (Converter)(str);
@ -55,12 +56,12 @@ void check_utf_conversion_error(
} }
TEST(UtilTest, UTF16ToUTF8Error) { TEST(UtilTest, UTF16ToUTF8Error) {
check_utf_conversion_error<fmt::internal::utf16_to_utf8, wchar_t>( check_utf_conversion_error<fmt::detail::utf16_to_utf8, wchar_t>(
"cannot convert string from UTF-16 to UTF-8"); "cannot convert string from UTF-16 to UTF-8");
} }
TEST(UtilTest, UTF16ToUTF8Convert) { TEST(UtilTest, UTF16ToUTF8Convert) {
fmt::internal::utf16_to_utf8 u; fmt::detail::utf16_to_utf8 u;
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(fmt::wstring_view(0, 1))); EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(fmt::wstring_view(0, 1)));
EXPECT_EQ(ERROR_INVALID_PARAMETER, EXPECT_EQ(ERROR_INVALID_PARAMETER,
u.convert(fmt::wstring_view(L"foo", INT_MAX + 1u))); u.convert(fmt::wstring_view(L"foo", INT_MAX + 1u)));
@ -73,17 +74,16 @@ TEST(UtilTest, FormatWindowsError) {
0, ERROR_FILE_EXISTS, 0, ERROR_FILE_EXISTS,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, 0); reinterpret_cast<LPWSTR>(&message), 0, 0);
fmt::internal::utf16_to_utf8 utf8_message(message); fmt::detail::utf16_to_utf8 utf8_message(message);
LocalFree(message); LocalFree(message);
fmt::memory_buffer actual_message; fmt::memory_buffer actual_message;
fmt::internal::format_windows_error(actual_message, ERROR_FILE_EXISTS, fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test");
"test");
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()), EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
fmt::to_string(actual_message)); fmt::to_string(actual_message));
actual_message.resize(0); actual_message.resize(0);
auto max_size = fmt::internal::max_value<size_t>(); auto max_size = fmt::detail::max_value<size_t>();
fmt::internal::format_windows_error(actual_message, ERROR_FILE_EXISTS, fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS,
fmt::string_view(0, max_size)); fmt::string_view(0, max_size));
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS), EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS),
fmt::to_string(actual_message)); fmt::to_string(actual_message));
} }
@ -103,11 +103,11 @@ TEST(UtilTest, FormatLongWindowsError) {
reinterpret_cast<LPWSTR>(&message), 0, 0) == 0) { reinterpret_cast<LPWSTR>(&message), 0, 0) == 0) {
return; return;
} }
fmt::internal::utf16_to_utf8 utf8_message(message); fmt::detail::utf16_to_utf8 utf8_message(message);
LocalFree(message); LocalFree(message);
fmt::memory_buffer actual_message; fmt::memory_buffer actual_message;
fmt::internal::format_windows_error(actual_message, provisioning_not_allowed, fmt::detail::format_windows_error(actual_message, provisioning_not_allowed,
"test"); "test");
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()), EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
fmt::to_string(actual_message)); fmt::to_string(actual_message));
} }
@ -120,14 +120,14 @@ TEST(UtilTest, WindowsError) {
error = e; error = e;
} }
fmt::memory_buffer message; fmt::memory_buffer message;
fmt::internal::format_windows_error(message, ERROR_FILE_EXISTS, "test error"); fmt::detail::format_windows_error(message, ERROR_FILE_EXISTS, "test error");
EXPECT_EQ(to_string(message), error.what()); EXPECT_EQ(to_string(message), error.what());
EXPECT_EQ(ERROR_FILE_EXISTS, error.error_code()); EXPECT_EQ(ERROR_FILE_EXISTS, error.error_code());
} }
TEST(UtilTest, ReportWindowsError) { TEST(UtilTest, ReportWindowsError) {
fmt::memory_buffer out; fmt::memory_buffer out;
fmt::internal::format_windows_error(out, ERROR_FILE_EXISTS, "test error"); fmt::detail::format_windows_error(out, ERROR_FILE_EXISTS, "test error");
out.push_back('\n'); out.push_back('\n');
EXPECT_WRITE(stderr, EXPECT_WRITE(stderr,
fmt::report_windows_error(ERROR_FILE_EXISTS, "test error"), fmt::report_windows_error(ERROR_FILE_EXISTS, "test error"),
@ -164,10 +164,10 @@ static file open_file() {
// Attempts to write a string to a file. // Attempts to write a string to a file.
static void write(file& f, fmt::string_view s) { static void write(file& f, fmt::string_view s) {
std::size_t num_chars_left = s.size(); size_t num_chars_left = s.size();
const char* ptr = s.data(); const char* ptr = s.data();
do { do {
std::size_t count = f.write(ptr, num_chars_left); size_t count = f.write(ptr, num_chars_left);
ptr += count; ptr += count;
// We can't write more than size_t bytes since num_chars_left // We can't write more than size_t bytes since num_chars_left
// has type size_t. // has type size_t.
@ -238,16 +238,17 @@ TEST(BufferedFileTest, CloseFileInDtor) {
TEST(BufferedFileTest, CloseErrorInDtor) { TEST(BufferedFileTest, CloseErrorInDtor) {
std::unique_ptr<buffered_file> f(new buffered_file(open_buffered_file())); std::unique_ptr<buffered_file> f(new buffered_file(open_buffered_file()));
EXPECT_WRITE(stderr, EXPECT_WRITE(
{ stderr,
// The close function must be called inside EXPECT_WRITE, {
// otherwise the system may recycle closed file descriptor when // The close function must be called inside EXPECT_WRITE,
// redirecting the output in EXPECT_STDERR and the second close // otherwise the system may recycle closed file descriptor when
// will break output redirection. // redirecting the output in EXPECT_STDERR and the second close
FMT_POSIX(close(f->fileno())); // will break output redirection.
SUPPRESS_ASSERT(f.reset(nullptr)); FMT_POSIX(close(f->fileno()));
}, SUPPRESS_ASSERT(f.reset(nullptr));
format_system_error(EBADF, "cannot close file") + "\n"); },
format_system_error(EBADF, "cannot close file") + "\n");
} }
TEST(BufferedFileTest, Close) { TEST(BufferedFileTest, Close) {
@ -286,6 +287,26 @@ TEST(BufferedFileTest, Fileno) {
EXPECT_READ(copy, FILE_CONTENT); EXPECT_READ(copy, FILE_CONTENT);
} }
TEST(DirectBufferedFileTest, Print) {
fmt::direct_buffered_file out(
"test-file", fmt::file::WRONLY | fmt::file::CREATE);
fmt::print(out, "The answer is {}.\n", 42);
out.close();
file in("test-file", file::RDONLY);
EXPECT_READ(in, "The answer is 42.\n");
}
TEST(DirectBufferedFileTest, BufferBoundary) {
auto str = std::string(4096, 'x');
fmt::direct_buffered_file out(
"test-file", fmt::file::WRONLY | fmt::file::CREATE);
fmt::print(out, "{}", str);
fmt::print(out, "{}", str);
out.close();
file in("test-file", file::RDONLY);
EXPECT_READ(in, str + str);
}
TEST(FileTest, DefaultCtor) { TEST(FileTest, DefaultCtor) {
file f; file f;
EXPECT_EQ(-1, f.descriptor()); EXPECT_EQ(-1, f.descriptor());
@ -369,16 +390,17 @@ TEST(FileTest, CloseFileInDtor) {
TEST(FileTest, CloseErrorInDtor) { TEST(FileTest, CloseErrorInDtor) {
std::unique_ptr<file> f(new file(open_file())); std::unique_ptr<file> f(new file(open_file()));
EXPECT_WRITE(stderr, EXPECT_WRITE(
{ stderr,
// The close function must be called inside EXPECT_WRITE, {
// otherwise the system may recycle closed file descriptor when // The close function must be called inside EXPECT_WRITE,
// redirecting the output in EXPECT_STDERR and the second close // otherwise the system may recycle closed file descriptor when
// will break output redirection. // redirecting the output in EXPECT_STDERR and the second close
FMT_POSIX(close(f->descriptor())); // will break output redirection.
SUPPRESS_ASSERT(f.reset(nullptr)); FMT_POSIX(close(f->descriptor()));
}, SUPPRESS_ASSERT(f.reset(nullptr));
format_system_error(EBADF, "cannot close file") + "\n"); },
format_system_error(EBADF, "cannot close file") + "\n");
} }
TEST(FileTest, Close) { TEST(FileTest, Close) {
@ -495,4 +517,4 @@ TEST(LocaleTest, Strtod) {
EXPECT_EQ(start + 3, ptr); EXPECT_EQ(start + 3, ptr);
} }
# endif # endif
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL

View file

@ -21,9 +21,9 @@ template <> struct formatter<test> : formatter<int> {
}; };
} // namespace fmt } // namespace fmt
#include "fmt/ostream.h"
#include <sstream> #include <sstream>
#include "fmt/ostream.h"
#include "gmock.h" #include "gmock.h"
#include "gtest-extra.h" #include "gtest-extra.h"
#include "util.h" #include "util.h"
@ -64,22 +64,23 @@ TEST(OStreamTest, Enum) {
EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum())); EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
} }
using range = fmt::buffer_range<char>; struct test_arg_formatter
: fmt::detail::arg_formatter<fmt::format_context::iterator, char> {
struct test_arg_formatter : fmt::arg_formatter<range> {
fmt::format_parse_context parse_ctx; fmt::format_parse_context parse_ctx;
test_arg_formatter(fmt::format_context& ctx, fmt::format_specs& s) test_arg_formatter(fmt::format_context& ctx, fmt::format_specs& s)
: fmt::arg_formatter<range>(ctx, &parse_ctx, &s), parse_ctx("") {} : fmt::detail::arg_formatter<fmt::format_context::iterator, char>(
ctx, &parse_ctx, &s),
parse_ctx("") {}
}; };
TEST(OStreamTest, CustomArg) { TEST(OStreamTest, CustomArg) {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
fmt::internal::buffer<char>& base = buffer; fmt::detail::buffer<char>& base = buffer;
fmt::format_context ctx(std::back_inserter(base), fmt::format_args()); fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
fmt::format_specs spec; fmt::format_specs spec;
test_arg_formatter af(ctx, spec); test_arg_formatter af(ctx, spec);
fmt::visit_format_arg( fmt::visit_format_arg(
af, fmt::internal::make_arg<fmt::format_context>(streamable_enum())); af, fmt::detail::make_arg<fmt::format_context>(streamable_enum()));
EXPECT_EQ("streamable_enum", std::string(buffer.data(), buffer.size())); EXPECT_EQ("streamable_enum", std::string(buffer.data(), buffer.size()));
} }
@ -95,7 +96,7 @@ TEST(OStreamTest, Format) {
TEST(OStreamTest, FormatSpecs) { TEST(OStreamTest, FormatSpecs) {
EXPECT_EQ("def ", format("{0:<5}", TestString("def"))); EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
EXPECT_EQ(" def", format("{0:>5}", TestString("def"))); EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
#if FMT_NUMERIC_ALIGN #if FMT_DEPRECATED_NUMERIC_ALIGN
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")), format_error, EXPECT_THROW_MSG(format("{0:=5}", TestString("def")), format_error,
"format specifier requires numeric argument"); "format specifier requires numeric argument");
#endif #endif
@ -140,18 +141,18 @@ TEST(OStreamTest, WriteToOStream) {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
const char* foo = "foo"; const char* foo = "foo";
buffer.append(foo, foo + std::strlen(foo)); buffer.append(foo, foo + std::strlen(foo));
fmt::internal::write(os, buffer); fmt::detail::write_buffer(os, buffer);
EXPECT_EQ("foo", os.str()); EXPECT_EQ("foo", os.str());
} }
TEST(OStreamTest, WriteToOStreamMaxSize) { TEST(OStreamTest, WriteToOStreamMaxSize) {
std::size_t max_size = fmt::internal::max_value<std::size_t>(); size_t max_size = fmt::detail::max_value<size_t>();
std::streamsize max_streamsize = fmt::internal::max_value<std::streamsize>(); std::streamsize max_streamsize = fmt::detail::max_value<std::streamsize>();
if (max_size <= fmt::internal::to_unsigned(max_streamsize)) return; if (max_size <= fmt::detail::to_unsigned(max_streamsize)) return;
struct test_buffer : fmt::internal::buffer<char> { struct test_buffer : fmt::detail::buffer<char> {
explicit test_buffer(std::size_t size) { resize(size); } explicit test_buffer(size_t size) { resize(size); }
void grow(std::size_t) {} void grow(size_t) {}
} buffer(max_size); } buffer(max_size);
struct mock_streambuf : std::streambuf { struct mock_streambuf : std::streambuf {
@ -171,13 +172,13 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
typedef std::make_unsigned<std::streamsize>::type ustreamsize; typedef std::make_unsigned<std::streamsize>::type ustreamsize;
ustreamsize size = max_size; ustreamsize size = max_size;
do { do {
auto n = std::min(size, fmt::internal::to_unsigned(max_streamsize)); auto n = std::min(size, fmt::detail::to_unsigned(max_streamsize));
EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n))) EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n)))
.WillOnce(testing::Return(max_streamsize)); .WillOnce(testing::Return(max_streamsize));
data += n; data += n;
size -= n; size -= n;
} while (size != 0); } while (size != 0);
fmt::internal::write(os, buffer); fmt::detail::write_buffer(os, buffer);
} }
TEST(OStreamTest, Join) { TEST(OStreamTest, Join) {
@ -258,7 +259,7 @@ TEST(OStreamTest, DisableBuiltinOStreamOperators) {
struct explicitly_convertible_to_string_like { struct explicitly_convertible_to_string_like {
template <typename String, template <typename String,
typename = typename std::enable_if<std::is_constructible< typename = typename std::enable_if<std::is_constructible<
String, const char*, std::size_t>::value>::type> String, const char*, size_t>::value>::type>
explicit operator String() const { explicit operator String() const {
return String("foo", 3u); return String("foo", 3u);
} }
@ -269,13 +270,13 @@ std::ostream& operator<<(std::ostream& os,
return os << "bar"; return os << "bar";
} }
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) { TEST(OStreamTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like())); EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
} }
#ifdef FMT_USE_STRING_VIEW #ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view { struct explicitly_convertible_to_std_string_view {
explicit operator fmt::internal::std_string_view<char>() const { explicit operator fmt::detail::std_string_view<char>() const {
return {"foo", 3u}; return {"foo", 3u};
} }
}; };
@ -285,8 +286,24 @@ std::ostream& operator<<(std::ostream& os,
return os << "bar"; return os << "bar";
} }
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) { TEST(OStreamTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like())); EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
} }
#endif // FMT_USE_STRING_VIEW #endif // FMT_USE_STRING_VIEW
struct copyfmt_test {};
std::ostream& operator<<(std::ostream& os, copyfmt_test) {
std::ios ios(nullptr);
ios.copyfmt(os);
return os << "foo";
}
TEST(OStreamTest, CopyFmt) {
EXPECT_EQ("foo", fmt::format("{}", copyfmt_test()));
}
TEST(OStreamTest, CompileTimeString) {
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
}

View file

@ -49,8 +49,8 @@ int pipe_count;
int fopen_count; int fopen_count;
int fclose_count; int fclose_count;
int fileno_count; int fileno_count;
std::size_t read_nbyte; size_t read_nbyte;
std::size_t write_nbyte; size_t write_nbyte;
bool sysconf_error; bool sysconf_error;
enum { NONE, MAX_SIZE, ERROR } fstat_sim; enum { NONE, MAX_SIZE, ERROR } fstat_sim;
@ -261,8 +261,8 @@ TEST(FileTest, Size) {
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size())); EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
# ifdef _WIN32 # ifdef _WIN32
fmt::memory_buffer message; fmt::memory_buffer message;
fmt::internal::format_windows_error(message, ERROR_ACCESS_DENIED, fmt::detail::format_windows_error(message, ERROR_ACCESS_DENIED,
"cannot get file size"); "cannot get file size");
fstat_sim = ERROR; fstat_sim = ERROR;
EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message)); EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message));
fstat_sim = NONE; fstat_sim = NONE;
@ -288,7 +288,7 @@ TEST(FileTest, ReadRetry) {
write_end.write("test", SIZE); write_end.write("test", SIZE);
write_end.close(); write_end.close();
char buffer[SIZE]; char buffer[SIZE];
std::size_t count = 0; size_t count = 0;
EXPECT_RETRY(count = read_end.read(buffer, SIZE), read, EXPECT_RETRY(count = read_end.read(buffer, SIZE), read,
"cannot read from file"); "cannot read from file");
EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count); EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
@ -298,7 +298,7 @@ TEST(FileTest, WriteRetry) {
file read_end, write_end; file read_end, write_end;
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
enum { SIZE = 4 }; enum { SIZE = 4 };
std::size_t count = 0; size_t count = 0;
EXPECT_RETRY(count = write_end.write("test", SIZE), write, EXPECT_RETRY(count = write_end.write("test", SIZE), write,
"cannot write to file"); "cannot write to file");
write_end.close(); write_end.close();
@ -316,8 +316,8 @@ TEST(FileTest, ConvertReadCount) {
file read_end, write_end; file read_end, write_end;
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
char c; char c;
std::size_t size = UINT_MAX; size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t)) ++size; if (sizeof(unsigned) != sizeof(size_t)) ++size;
read_count = 1; read_count = 1;
read_nbyte = 0; read_nbyte = 0;
EXPECT_THROW(read_end.read(&c, size), fmt::system_error); EXPECT_THROW(read_end.read(&c, size), fmt::system_error);
@ -329,8 +329,8 @@ TEST(FileTest, ConvertWriteCount) {
file read_end, write_end; file read_end, write_end;
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
char c; char c;
std::size_t size = UINT_MAX; size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t)) ++size; if (sizeof(unsigned) != sizeof(size_t)) ++size;
write_count = 1; write_count = 1;
write_nbyte = 0; write_nbyte = 0;
EXPECT_THROW(write_end.write(&c, size), fmt::system_error); EXPECT_THROW(write_end.write(&c, size), fmt::system_error);

View file

@ -17,7 +17,7 @@
using fmt::format; using fmt::format;
using fmt::format_error; using fmt::format_error;
using fmt::internal::max_value; using fmt::detail::max_value;
const unsigned BIG_NUM = INT_MAX + 1u; const unsigned BIG_NUM = INT_MAX + 1u;
@ -113,14 +113,13 @@ TEST(PrintfTest, SwitchArgIndexing) {
TEST(PrintfTest, InvalidArgIndex) { TEST(PrintfTest, InvalidArgIndex) {
EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error, EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error,
"argument index out of range"); "argument not found");
EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error, EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error,
"argument index out of range"); "argument not found");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42), format_error, EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42), format_error,
"argument index out of range"); "argument not found");
EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error, EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error, "argument not found");
"argument index out of range");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42), format_error, EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42), format_error,
"number is too big"); "number is too big");
} }
@ -140,8 +139,11 @@ TEST(PrintfTest, ZeroFlag) {
EXPECT_PRINTF("+00042", "%00+6d", 42); EXPECT_PRINTF("+00042", "%00+6d", 42);
EXPECT_PRINTF(" 42", "%05.d", 42);
EXPECT_PRINTF(" 0042", "%05.4d", 42);
// '0' flag is ignored for non-numeric types. // '0' flag is ignored for non-numeric types.
EXPECT_PRINTF("0000x", "%05c", 'x'); EXPECT_PRINTF(" x", "%05c", 'x');
} }
TEST(PrintfTest, PlusFlag) { TEST(PrintfTest, PlusFlag) {
@ -152,11 +154,38 @@ TEST(PrintfTest, PlusFlag) {
// '+' flag is ignored for non-numeric types. // '+' flag is ignored for non-numeric types.
EXPECT_PRINTF("x", "%+c", 'x'); EXPECT_PRINTF("x", "%+c", 'x');
// '+' flag wins over space flag
EXPECT_PRINTF("+42", "%+ d", 42);
EXPECT_PRINTF("-42", "%+ d", -42);
EXPECT_PRINTF("+42", "% +d", 42);
EXPECT_PRINTF("-42", "% +d", -42);
EXPECT_PRINTF("+0042", "% +05d", 42);
EXPECT_PRINTF("+0042", "%0+ 5d", 42);
// '+' flag and space flag are both ignored for non-numeric types.
EXPECT_PRINTF("x", "%+ c", 'x');
EXPECT_PRINTF("x", "% +c", 'x');
} }
TEST(PrintfTest, MinusFlag) { TEST(PrintfTest, MinusFlag) {
EXPECT_PRINTF("abc ", "%-5s", "abc"); EXPECT_PRINTF("abc ", "%-5s", "abc");
EXPECT_PRINTF("abc ", "%0--5s", "abc"); EXPECT_PRINTF("abc ", "%0--5s", "abc");
EXPECT_PRINTF("7 ", "%-5d", 7);
EXPECT_PRINTF("97 ", "%-5hhi", 'a');
EXPECT_PRINTF("a ", "%-5c", 'a');
// '0' flag is ignored if '-' flag is given
EXPECT_PRINTF("7 ", "%-05d", 7);
EXPECT_PRINTF("7 ", "%0-5d", 7);
EXPECT_PRINTF("a ", "%-05c", 'a');
EXPECT_PRINTF("a ", "%0-5c", 'a');
EXPECT_PRINTF("97 ", "%-05hhi", 'a');
EXPECT_PRINTF("97 ", "%0-5hhi", 'a');
// '-' and space flag don't interfere
EXPECT_PRINTF(" 42", "%- d", 42);
} }
TEST(PrintfTest, SpaceFlag) { TEST(PrintfTest, SpaceFlag) {
@ -222,8 +251,7 @@ TEST(PrintfTest, DynamicWidth) {
EXPECT_EQ("42 ", test_sprintf("%*d", -5, 42)); EXPECT_EQ("42 ", test_sprintf("%*d", -5, 42));
EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error,
"width is not integer"); "width is not integer");
EXPECT_THROW_MSG(test_sprintf("%*d"), format_error, EXPECT_THROW_MSG(test_sprintf("%*d"), format_error, "argument not found");
"argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%*d", BIG_NUM, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%*d", BIG_NUM, 42), format_error,
"number is too big"); "number is too big");
} }
@ -259,6 +287,11 @@ TEST(PrintfTest, FloatPrecision) {
EXPECT_PRINTF(buffer, "%.3a", 1234.5678); EXPECT_PRINTF(buffer, "%.3a", 1234.5678);
} }
TEST(PrintfTest, StringPrecision) {
char test[] = {'H', 'e', 'l', 'l', 'o'};
EXPECT_EQ(fmt::sprintf("%.4s", test), "Hell");
}
TEST(PrintfTest, IgnorePrecisionForNonNumericArg) { TEST(PrintfTest, IgnorePrecisionForNonNumericArg) {
EXPECT_PRINTF("abc", "%.5s", "abc"); EXPECT_PRINTF("abc", "%.5s", "abc");
} }
@ -268,8 +301,7 @@ TEST(PrintfTest, DynamicPrecision) {
EXPECT_EQ("42", test_sprintf("%.*d", -5, 42)); EXPECT_EQ("42", test_sprintf("%.*d", -5, 42));
EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error,
"precision is not integer"); "precision is not integer");
EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error, EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error, "argument not found");
"argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%.*d", BIG_NUM, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%.*d", BIG_NUM, 42), format_error,
"number is too big"); "number is too big");
if (sizeof(long long) != sizeof(int)) { if (sizeof(long long) != sizeof(int)) {
@ -298,7 +330,7 @@ void TestLength(const char* length_spec, U value) {
unsigned long long unsigned_value = 0; unsigned long long unsigned_value = 0;
// Apply integer promotion to the argument. // Apply integer promotion to the argument.
unsigned long long max = max_value<U>(); unsigned long long max = max_value<U>();
using fmt::internal::const_check; using fmt::detail::const_check;
if (const_check(max <= static_cast<unsigned>(max_value<int>()))) { if (const_check(max <= static_cast<unsigned>(max_value<int>()))) {
signed_value = static_cast<int>(value); signed_value = static_cast<int>(value);
unsigned_value = static_cast<unsigned long long>(value); unsigned_value = static_cast<unsigned long long>(value);
@ -367,7 +399,7 @@ TEST(PrintfTest, Length) {
TestLength<long long>("ll"); TestLength<long long>("ll");
TestLength<unsigned long long>("ll"); TestLength<unsigned long long>("ll");
TestLength<intmax_t>("j"); TestLength<intmax_t>("j");
TestLength<std::size_t>("z"); TestLength<size_t>("z");
TestLength<std::ptrdiff_t>("t"); TestLength<std::ptrdiff_t>("t");
long double max = max_value<long double>(); long double max = max_value<long double>();
EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max); EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max);
@ -447,6 +479,12 @@ TEST(PrintfTest, String) {
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr); EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
} }
TEST(PrintfTest, UCharString) {
unsigned char str[] = "test";
unsigned char* pstr = str;
EXPECT_EQ("test", fmt::sprintf("%s", pstr));
}
TEST(PrintfTest, Pointer) { TEST(PrintfTest, Pointer) {
int n; int n;
void* p = &n; void* p = &n;
@ -502,9 +540,7 @@ TEST(PrintfTest, PrintfError) {
TEST(PrintfTest, WideString) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); } TEST(PrintfTest, WideString) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); }
TEST(PrintfTest, PrintfCustom) { TEST(PrintfTest, PrintfCustom) {
// The test is disabled for now because it requires decoupling EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
// fallback_formatter::format from format_context.
//EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
} }
TEST(PrintfTest, OStream) { TEST(PrintfTest, OStream) {
@ -571,51 +607,22 @@ TEST(PrintfTest, VSPrintfMakeWArgsExample) {
#endif #endif
} }
typedef fmt::printf_arg_formatter<fmt::buffer_range<char>> formatter_t; TEST(PrintfTest, PrintfDetermineOutputSize) {
typedef fmt::basic_printf_context<formatter_t::iterator, char> context_t; using backit = std::back_insert_iterator<std::vector<char>>;
using truncated_printf_context =
fmt::basic_printf_context<fmt::detail::truncating_iterator<backit>, char>;
// A custom printf argument formatter that doesn't print `-` for floating-point auto v = std::vector<char>{};
// values rounded to 0. auto it = std::back_inserter(v);
class custom_printf_arg_formatter : public formatter_t {
public:
using formatter_t::iterator;
custom_printf_arg_formatter(formatter_t::iterator iter, const auto format_string = "%s";
formatter_t::format_specs& specs, context_t& ctx) const auto format_arg = "Hello";
: formatter_t(iter, specs, ctx) {} const auto expected_size = fmt::sprintf(format_string, format_arg).size();
using formatter_t::operator(); EXPECT_EQ((truncated_printf_context(
fmt::detail::truncating_iterator<backit>(it, 0), format_string,
#if FMT_MSC_VER > 0 && FMT_MSC_VER <= 1804 fmt::make_format_args<truncated_printf_context>(format_arg))
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> .format()
iterator operator()(T value){ .count()),
#else expected_size);
iterator operator()(double value) {
#endif
// Comparing a float to 0.0 is safe.
if (round(value * pow(10, specs()->precision)) == 0.0) value = 0;
return formatter_t::operator()(value);
}
}
;
typedef fmt::basic_format_args<context_t> format_args_t;
std::string custom_vformat(fmt::string_view format_str, format_args_t args) {
fmt::memory_buffer buffer;
fmt::vprintf<custom_printf_arg_formatter>(buffer, format_str, args);
return std::string(buffer.data(), buffer.size());
}
template <typename... Args>
std::string custom_format(const char* format_str, const Args&... args) {
auto va = fmt::make_printf_args(args...);
return custom_vformat(format_str, {va});
}
TEST(PrintfTest, CustomFormat) {
EXPECT_EQ("0.00", custom_format("%.2f", -.00001));
EXPECT_EQ("0.00", custom_format("%.2f", .00001));
EXPECT_EQ("1.00", custom_format("%.2f", 1.00001));
EXPECT_EQ("-1.00", custom_format("%.2f", -1.00001));
} }

View file

@ -79,7 +79,7 @@ TEST(RangesTest, JoinInitializerList) {
struct my_struct { struct my_struct {
int32_t i; int32_t i;
std::string str; // can throw std::string str; // can throw
template <std::size_t N> decltype(auto) get() const noexcept { template <size_t N> decltype(auto) get() const noexcept {
if constexpr (N == 0) if constexpr (N == 0)
return i; return i;
else if constexpr (N == 1) else if constexpr (N == 1)
@ -87,16 +87,15 @@ struct my_struct {
} }
}; };
template <std::size_t N> decltype(auto) get(const my_struct& s) noexcept { template <size_t N> decltype(auto) get(const my_struct& s) noexcept {
return s.get<N>(); return s.get<N>();
} }
namespace std { namespace std {
template <> template <> struct tuple_size<my_struct> : std::integral_constant<size_t, 2> {};
struct tuple_size<my_struct> : std::integral_constant<std::size_t, 2> {};
template <std::size_t N> struct tuple_element<N, my_struct> { template <size_t N> struct tuple_element<N, my_struct> {
using type = decltype(std::declval<my_struct>().get<N>()); using type = decltype(std::declval<my_struct>().get<N>());
}; };
@ -140,3 +139,17 @@ TEST(RangesTest, FormatStringLike) {
EXPECT_EQ("foo", fmt::format("{}", string_like())); EXPECT_EQ("foo", fmt::format("{}", string_like()));
} }
#endif // FMT_USE_STRING_VIEW #endif // FMT_USE_STRING_VIEW
struct zstring_sentinel {};
bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
struct zstring {
const char* p;
const char* begin() const { return p; }
zstring_sentinel end() const { return {}; }
};
TEST(RangesTest, JoinSentinel) {
zstring hello{"hello"};
EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello));
EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_")));
}

View file

@ -5,12 +5,14 @@
// //
// For the license information refer to format.h. // For the license information refer to format.h.
#include "scan.h"
#include <time.h> #include <time.h>
#include <climits> #include <climits>
#include "gmock.h" #include "gmock.h"
#include "gtest-extra.h" #include "gtest-extra.h"
#include "scan.h"
TEST(ScanTest, ReadText) { TEST(ScanTest, ReadText) {
fmt::string_view s = "foo"; fmt::string_view s = "foo";
@ -73,7 +75,7 @@ template <> struct scanner<tm> {
if (it != ctx.end() && *it == ':') ++it; if (it != ctx.end() && *it == ':') ++it;
auto end = it; auto end = it;
while (end != ctx.end() && *end != '}') ++end; while (end != ctx.end() && *end != '}') ++end;
format.reserve(internal::to_unsigned(end - it + 1)); format.reserve(detail::to_unsigned(end - it + 1));
format.append(it, end); format.append(it, end);
format.push_back('\0'); format.push_back('\0');
return end; return end;

View file

@ -31,7 +31,7 @@ class scan_parse_context {
FMT_CONSTEXPR iterator end() const { return format_.end(); } FMT_CONSTEXPR iterator end() const { return format_.end(); }
void advance_to(iterator it) { void advance_to(iterator it) {
format_.remove_prefix(internal::to_unsigned(it - begin())); format_.remove_prefix(detail::to_unsigned(it - begin()));
} }
}; };
@ -48,11 +48,11 @@ struct scan_context {
iterator end() const { return begin() + input_.size(); } iterator end() const { return begin() + input_.size(); }
void advance_to(iterator it) { void advance_to(iterator it) {
input_.remove_prefix(internal::to_unsigned(it - begin())); input_.remove_prefix(detail::to_unsigned(it - begin()));
} }
}; };
namespace internal { namespace detail {
enum class scan_type { enum class scan_type {
none_type, none_type,
int_type, int_type,
@ -107,20 +107,20 @@ class scan_arg {
ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx)); ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx));
} }
}; };
} // namespace internal } // namespace detail
struct scan_args { struct scan_args {
int size; int size;
const internal::scan_arg* data; const detail::scan_arg* data;
template <size_t N> template <size_t N>
scan_args(const std::array<internal::scan_arg, N>& store) scan_args(const std::array<detail::scan_arg, N>& store)
: size(N), data(store.data()) { : size(N), data(store.data()) {
static_assert(N < INT_MAX, "too many arguments"); static_assert(N < INT_MAX, "too many arguments");
} }
}; };
namespace internal { namespace detail {
struct scan_handler : error_handler { struct scan_handler : error_handler {
private: private:
@ -169,14 +169,15 @@ struct scan_handler : error_handler {
scan_ctx_.advance_to(it + size); scan_ctx_.advance_to(it + size);
} }
void on_arg_id() { on_arg_id(next_arg_id_++); } int on_arg_id() { return on_arg_id(next_arg_id_++); }
void on_arg_id(int id) { int on_arg_id(int id) {
if (id >= args_.size) on_error("argument index out of range"); if (id >= args_.size) on_error("argument index out of range");
arg_ = args_.data[id]; arg_ = args_.data[id];
return id;
} }
void on_arg_id(string_view) { on_error("invalid format"); } int on_arg_id(string_view) { return on_error("invalid format"), 0; }
void on_replacement_field(const char*) { void on_replacement_field(int, const char*) {
auto it = scan_ctx_.begin(), end = scan_ctx_.end(); auto it = scan_ctx_.begin(), end = scan_ctx_.end();
switch (arg_.type) { switch (arg_.type) {
case scan_type::int_type: case scan_type::int_type:
@ -208,24 +209,24 @@ struct scan_handler : error_handler {
} }
} }
const char* on_format_specs(const char* begin, const char*) { const char* on_format_specs(int, const char* begin, const char*) {
if (arg_.type != scan_type::custom_type) return begin; if (arg_.type != scan_type::custom_type) return begin;
parse_ctx_.advance_to(begin); parse_ctx_.advance_to(begin);
arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_); arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_);
return parse_ctx_.begin(); return parse_ctx_.begin();
} }
}; };
} // namespace internal } // namespace detail
template <typename... Args> template <typename... Args>
std::array<internal::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) { std::array<detail::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) {
return {{args...}}; return {{args...}};
} }
string_view::iterator vscan(string_view input, string_view format_str, string_view::iterator vscan(string_view input, string_view format_str,
scan_args args) { scan_args args) {
internal::scan_handler h(format_str, input, args); detail::scan_handler h(format_str, input, args);
internal::parse_format_string<false>(format_str, h); detail::parse_format_string<false>(format_str, h);
return input.begin() + (h.pos() - &*input.begin()); return input.begin() + (h.pos() - &*input.begin());
} }

View file

@ -1,4 +1,5 @@
#include <format> #include <format>
#include "gtest.h" #include "gtest.h"
TEST(StdFormatTest, Escaping) { TEST(StdFormatTest, Escaping) {
@ -61,7 +62,7 @@ TEST(StdFormatTest, Int) {
string s0 = format("{}", 42); // s0 == "42" string s0 = format("{}", 42); // s0 == "42"
string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); // s1 == "101010 42 52 2a" string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); // s1 == "101010 42 52 2a"
string s2 = format("{0:#x} {0:#X}", 42); // s2 == "0x2a 0X2A" string s2 = format("{0:#x} {0:#X}", 42); // s2 == "0x2a 0X2A"
string s3 = format("{:n}", 1234); // s3 == "1234" (depends on the locale) string s3 = format("{:L}", 1234); // s3 == "1234" (depends on the locale)
EXPECT_EQ(s0, "42"); EXPECT_EQ(s0, "42");
EXPECT_EQ(s1, "101010 42 52 2a"); EXPECT_EQ(s1, "101010 42 52 2a");
EXPECT_EQ(s2, "0x2a 0X2A"); EXPECT_EQ(s2, "0x2a 0X2A");
@ -123,7 +124,7 @@ template <> struct std::formatter<S> {
if constexpr (!is_integral_v<type> || is_same_v<type, bool>) if constexpr (!is_integral_v<type> || is_same_v<type, bool>)
throw format_error("width is not integral"); throw format_error("width is not integral");
// else if (value < 0 || value > numeric_limits<int>::max()) // else if (value < 0 || value > numeric_limits<int>::max())
else if (fmt::internal::is_negative(value) || else if (fmt::detail::is_negative(value) ||
value > numeric_limits<int>::max()) value > numeric_limits<int>::max())
throw format_error("invalid width"); throw format_error("invalid width");
else else

View file

@ -9,6 +9,7 @@
#define FMT_TEST_ASSERT_H_ #define FMT_TEST_ASSERT_H_
#include <stdexcept> #include <stdexcept>
#include "gtest.h" #include "gtest.h"
class assertion_failure : public std::logic_error { class assertion_failure : public std::logic_error {

View file

@ -6,6 +6,7 @@
// For the license information refer to format.h. // For the license information refer to format.h.
#include <cstdlib> #include <cstdlib>
#include "gtest.h" #include "gtest.h"
#ifdef _WIN32 #ifdef _WIN32

View file

@ -6,6 +6,7 @@
// For the license information refer to format.h. // For the license information refer to format.h.
#include "util.h" #include "util.h"
#include <cstring> #include <cstring>
void increment(char* s) { void increment(char* s) {

View file

@ -19,7 +19,7 @@ enum { BUFFER_SIZE = 256 };
# define FMT_VSNPRINTF vsnprintf # define FMT_VSNPRINTF vsnprintf
#endif #endif
template <std::size_t SIZE> template <size_t SIZE>
void safe_sprintf(char (&buffer)[SIZE], const char* format, ...) { void safe_sprintf(char (&buffer)[SIZE], const char* format, ...) {
std::va_list args; std::va_list args;
va_start(args, format); va_start(args, format);

View file

@ -0,0 +1,12 @@
sudo: true
dist: bionic
language: cpp
compiler:
- gcc
- clang
addons:
apt:
packages:
- nasm yasm g++-multilib tcsh
script:
- make test

View file

@ -1,4 +1,4 @@
PREFIX=/usr/local PREFIX?=/usr/local
INSTALL_DIR=$(PREFIX)/include/xbyak INSTALL_DIR=$(PREFIX)/include/xbyak
all: all:

View file

@ -23,7 +23,7 @@ void putOpmask(bool only64bit)
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
} tbl[] = { } tbl[] = {
{ "kadd", 0x4A }, { "kadd", 0x4A },
{ "kand", 0x41 }, { "kand", 0x41 },
@ -46,7 +46,7 @@ void putOpmask(bool only64bit)
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
} tbl[] = { } tbl[] = {
{ "knot", 0x44 }, { "knot", 0x44 },
{ "kortest", 0x98 }, { "kortest", 0x98 },
@ -63,23 +63,23 @@ void putOpmask(bool only64bit)
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
} tbl[] = { } tbl[] = {
{ "kshiftl", 0x32 }, { "kshiftl", 0x32 },
{ "kshiftr", 0x30 }, { "kshiftr", 0x30 },
}; };
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const Tbl& p = tbl[i]; const Tbl& p = tbl[i];
printf("void %sw(const Opmask& r1, const Opmask& r2, uint8 imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W1, 0x%02X, imm); }\n", p.name, p.code); printf("void %sw(const Opmask& r1, const Opmask& r2, uint8_t imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W1, 0x%02X, imm); }\n", p.name, p.code);
printf("void %sq(const Opmask& r1, const Opmask& r2, uint8 imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W1, 0x%02X, imm); }\n", p.name, p.code + 1); printf("void %sq(const Opmask& r1, const Opmask& r2, uint8_t imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W1, 0x%02X, imm); }\n", p.name, p.code + 1);
printf("void %sb(const Opmask& r1, const Opmask& r2, uint8 imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W0, 0x%02X, imm); }\n", p.name, p.code); printf("void %sb(const Opmask& r1, const Opmask& r2, uint8_t imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W0, 0x%02X, imm); }\n", p.name, p.code);
printf("void %sd(const Opmask& r1, const Opmask& r2, uint8 imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W0, 0x%02X, imm); }\n", p.name, p.code + 1); printf("void %sd(const Opmask& r1, const Opmask& r2, uint8_t imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W0, 0x%02X, imm); }\n", p.name, p.code + 1);
} }
} }
puts("void kmovw(const Opmask& k, const Operand& op) { opVex(k, 0, op, T_L0 | T_0F | T_W0, 0x90); }"); puts("void kmovw(const Opmask& k, const Operand& op) { if (!op.isMEM() && !op.isOPMASK()) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(k, 0, op, T_L0 | T_0F | T_W0, 0x90); }");
puts("void kmovq(const Opmask& k, const Operand& op) { opVex(k, 0, op, T_L0 | T_0F | T_W1, 0x90); }"); puts("void kmovq(const Opmask& k, const Operand& op) { if (!op.isMEM() && !op.isOPMASK()) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(k, 0, op, T_L0 | T_0F | T_W1, 0x90); }");
puts("void kmovb(const Opmask& k, const Operand& op) { opVex(k, 0, op, T_L0 | T_0F | T_66 | T_W0, 0x90); }"); puts("void kmovb(const Opmask& k, const Operand& op) { if (!op.isMEM() && !op.isOPMASK()) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(k, 0, op, T_L0 | T_0F | T_66 | T_W0, 0x90); }");
puts("void kmovd(const Opmask& k, const Operand& op) { opVex(k, 0, op, T_L0 | T_0F | T_66 | T_W1, 0x90); }"); puts("void kmovd(const Opmask& k, const Operand& op) { if (!op.isMEM() && !op.isOPMASK()) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(k, 0, op, T_L0 | T_0F | T_66 | T_W1, 0x90); }");
puts("void kmovw(const Address& addr, const Opmask& k) { opVex(k, 0, addr, T_L0 | T_0F | T_W0, 0x91); }"); puts("void kmovw(const Address& addr, const Opmask& k) { opVex(k, 0, addr, T_L0 | T_0F | T_W0, 0x91); }");
puts("void kmovq(const Address& addr, const Opmask& k) { opVex(k, 0, addr, T_L0 | T_0F | T_W1, 0x91); }"); puts("void kmovq(const Address& addr, const Opmask& k) { opVex(k, 0, addr, T_L0 | T_0F | T_W1, 0x91); }");
@ -98,13 +98,13 @@ void putOpmask(bool only64bit)
void putVcmp() void putVcmp()
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
int type; int type;
bool hasIMM; bool hasIMM;
} tbl[] = { } tbl[] = {
{ 0xC2, "vcmppd", T_0F | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_YMM | T_66, true }, { 0xC2, "vcmppd", T_0F | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_YMM | T_66 | T_B64, true },
{ 0xC2, "vcmpps", T_0F | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_YMM, true }, { 0xC2, "vcmpps", T_0F | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_YMM | T_B32, true },
{ 0xC2, "vcmpsd", T_0F | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_F2 | T_N8, true }, { 0xC2, "vcmpsd", T_0F | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_F2 | T_N8, true },
{ 0xC2, "vcmpss", T_0F | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_F3 | T_N4, true }, { 0xC2, "vcmpss", T_0F | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_F3 | T_N4, true },
@ -142,7 +142,7 @@ void putVcmp()
const Tbl *p = &tbl[i]; const Tbl *p = &tbl[i];
std::string type = type2String(p->type); std::string type = type2String(p->type);
printf("void %s(const Opmask& k, const Xmm& x, const Operand& op%s) { opAVX_K_X_XM(k, x, op, %s, 0x%02X%s); }\n" printf("void %s(const Opmask& k, const Xmm& x, const Operand& op%s) { opAVX_K_X_XM(k, x, op, %s, 0x%02X%s); }\n"
, p->name, p->hasIMM ? ", uint8 imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : ""); , p->name, p->hasIMM ? ", uint8_t imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : "");
} }
} }
@ -150,7 +150,7 @@ void putVcmp()
void putX_XM() void putX_XM()
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
int type; int type;
} tbl[] = { } tbl[] = {
@ -198,7 +198,7 @@ void putX_XM()
void putM_X() void putM_X()
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
int type; int type;
} tbl[] = { } tbl[] = {
@ -219,7 +219,7 @@ void putM_X()
void putXM_X() void putXM_X()
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
int type; int type;
} tbl[] = { } tbl[] = {
@ -242,7 +242,7 @@ void putXM_X()
void putX_X_XM_IMM() void putX_X_XM_IMM()
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
int type; int type;
bool hasIMM; bool hasIMM;
@ -368,12 +368,15 @@ void putX_X_XM_IMM()
{ 0x52, "vpdpwssd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false }, { 0x52, "vpdpwssd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
{ 0x53, "vpdpwssds", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false }, { 0x53, "vpdpwssds", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
{ 0x72, "vcvtne2ps2bf16", T_F2 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
{ 0x52, "vdpbf16ps", T_F3 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
}; };
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const Tbl *p = &tbl[i]; const Tbl *p = &tbl[i];
std::string type = type2String(p->type); std::string type = type2String(p->type);
printf("void %s(const Xmm& x1, const Xmm& x2, const Operand& op%s) { opAVX_X_X_XM(x1, x2, op, %s, 0x%02X%s); }\n" printf("void %s(const Xmm& x1, const Xmm& x2, const Operand& op%s) { opAVX_X_X_XM(x1, x2, op, %s, 0x%02X%s); }\n"
, p->name, p->hasIMM ? ", uint8 imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : ""); , p->name, p->hasIMM ? ", uint8_t imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : "");
} }
} }
@ -381,7 +384,7 @@ void putShift()
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
int idx; int idx;
int type; int type;
} tbl[] = { } tbl[] = {
@ -394,7 +397,7 @@ void putShift()
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const Tbl& p = tbl[i]; const Tbl& p = tbl[i];
std::string type = type2String(p.type); std::string type = type2String(p.type);
printf("void %s(const Xmm& x, const Operand& op, uint8 imm) { opAVX_X_X_XM(Xmm(x.getKind(), %d), x, op, %s, 0x%02X, imm); }\n", p.name, p.idx, type.c_str(), p.code); printf("void %s(const Xmm& x, const Operand& op, uint8_t imm) { opAVX_X_X_XM(Xmm(x.getKind(), %d), x, op, %s, 0x%02X, imm); }\n", p.name, p.idx, type.c_str(), p.code);
} }
} }
@ -403,7 +406,7 @@ void putExtractInsert()
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
int type; int type;
bool isZMM; bool isZMM;
} tbl[] = { } tbl[] = {
@ -421,13 +424,13 @@ void putExtractInsert()
const Tbl& p = tbl[i]; const Tbl& p = tbl[i];
std::string type = type2String(p.type); std::string type = type2String(p.type);
const char *kind = p.isZMM ? "Operand::MEM | Operand::YMM" : "Operand::MEM | Operand::XMM"; const char *kind = p.isZMM ? "Operand::MEM | Operand::YMM" : "Operand::MEM | Operand::XMM";
printf("void %s(const Operand& op, const %s& r, uint8 imm) { if (!op.is(%s)) throw Error(ERR_BAD_COMBINATION); opVex(r, 0, op, %s, 0x%2X, imm); }\n", p.name, p.isZMM ? "Zmm" : "Ymm", kind, type.c_str(), p.code); printf("void %s(const Operand& op, const %s& r, uint8_t imm) { if (!op.is(%s)) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(r, 0, op, %s, 0x%2X, imm); }\n", p.name, p.isZMM ? "Zmm" : "Ymm", kind, type.c_str(), p.code);
} }
} }
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
int type; int type;
bool isZMM; bool isZMM;
} tbl[] = { } tbl[] = {
@ -446,8 +449,8 @@ void putExtractInsert()
std::string type = type2String(p.type); std::string type = type2String(p.type);
const char *x = p.isZMM ? "Zmm" : "Ymm"; const char *x = p.isZMM ? "Zmm" : "Ymm";
const char *cond = p.isZMM ? "op.is(Operand::MEM | Operand::YMM)" : "(r1.getKind() == r2.getKind() && op.is(Operand::MEM | Operand::XMM))"; const char *cond = p.isZMM ? "op.is(Operand::MEM | Operand::YMM)" : "(r1.getKind() == r2.getKind() && op.is(Operand::MEM | Operand::XMM))";
printf("void %s(const %s& r1, const %s& r2, const Operand& op, uint8 imm) {" printf("void %s(const %s& r1, const %s& r2, const Operand& op, uint8_t imm) {"
"if (!%s) throw Error(ERR_BAD_COMBINATION); " "if (!%s) XBYAK_THROW(ERR_BAD_COMBINATION) "
"opVex(r1, &r2, op, %s, 0x%2X, imm); }\n", p.name, x, x, cond, type.c_str(), p.code); "opVex(r1, &r2, op, %s, 0x%2X, imm); }\n", p.name, x, x, cond, type.c_str(), p.code);
} }
} }
@ -457,7 +460,7 @@ void putBroadcast(bool only64bit)
{ {
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
int type; int type;
int reg; int reg;
@ -519,7 +522,7 @@ void putGather()
const struct Tbl { const struct Tbl {
const char *name; const char *name;
int type; int type;
uint8 code; uint8_t code;
int mode; int mode;
} tbl[] = { } tbl[] = {
{ "vpgatherdd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4, 0x90, xx_yy_zz }, { "vpgatherdd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4, 0x90, xx_yy_zz },
@ -542,7 +545,7 @@ void putScatter()
const struct Tbl { const struct Tbl {
const char *name; const char *name;
int type; int type;
uint8 code; uint8_t code;
int mode; // reverse of gather int mode; // reverse of gather
} tbl[] = { } tbl[] = {
{ "vpscatterdd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4 | T_M_K, 0xA0, xx_yy_zz }, { "vpscatterdd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4 | T_M_K, 0xA0, xx_yy_zz },
@ -564,10 +567,10 @@ void putScatter()
void putShuff() void putShuff()
{ {
puts("void vshuff32x4(const Ymm& y1, const Ymm& y2, const Operand& op, uint8 imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, 0x23, imm); }"); puts("void vshuff32x4(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, 0x23, imm); }");
puts("void vshuff64x2(const Ymm& y1, const Ymm& y2, const Operand& op, uint8 imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, 0x23, imm); }"); puts("void vshuff64x2(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, 0x23, imm); }");
puts("void vshufi32x4(const Ymm& y1, const Ymm& y2, const Operand& op, uint8 imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, 0x43, imm); }"); puts("void vshufi32x4(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, 0x43, imm); }");
puts("void vshufi64x2(const Ymm& y1, const Ymm& y2, const Operand& op, uint8 imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, 0x43, imm); }"); puts("void vshufi64x2(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, 0x43, imm); }");
} }
void putMov() void putMov()
@ -624,7 +627,7 @@ void putMov()
void putX_XM_IMM() void putX_XM_IMM()
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
int type; int type;
bool hasIMM; bool hasIMM;
@ -661,7 +664,7 @@ void putX_XM_IMM()
const Tbl *p = &tbl[i]; const Tbl *p = &tbl[i];
std::string type = type2String(p->type); std::string type = type2String(p->type);
printf("void %s(const Xmm& x, const Operand& op%s) { opAVX_X_XM_IMM(x, op, %s, 0x%02X%s); }\n" printf("void %s(const Xmm& x, const Operand& op%s) { opAVX_X_XM_IMM(x, op, %s, 0x%02X%s); }\n"
, p->name, p->hasIMM ? ", uint8 imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : ""); , p->name, p->hasIMM ? ", uint8_t imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : "");
} }
} }
@ -674,7 +677,7 @@ void putMisc()
const char *name; const char *name;
int zm; int zm;
int type; int type;
uint8 code; uint8_t code;
bool isZmm; bool isZmm;
} tbl[] = { } tbl[] = {
{ "vgatherpf0dps", 1, T_EW0 | T_N4, 0xC6, true }, { "vgatherpf0dps", 1, T_EW0 | T_N4, 0xC6, true },
@ -705,12 +708,16 @@ void putMisc()
} }
} }
puts("void vfpclasspd(const Opmask& k, const Operand& op, uint8 imm) { if (!op.isBit(128|256|512)) throw Error(ERR_BAD_MEM_SIZE); opVex(k.changeBit(op.getBit()), 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, 0x66, imm); }"); puts("void vfpclasspd(const Opmask& k, const Operand& op, uint8_t imm) { if (!op.isBit(128|256|512)) XBYAK_THROW(ERR_BAD_MEM_SIZE) opVex(k.changeBit(op.getBit()), 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, 0x66, imm); }");
puts("void vfpclassps(const Opmask& k, const Operand& op, uint8 imm) { if (!op.isBit(128|256|512)) throw Error(ERR_BAD_MEM_SIZE); opVex(k.changeBit(op.getBit()), 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, 0x66, imm); }"); puts("void vfpclassps(const Opmask& k, const Operand& op, uint8_t imm) { if (!op.isBit(128|256|512)) XBYAK_THROW(ERR_BAD_MEM_SIZE) opVex(k.changeBit(op.getBit()), 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, 0x66, imm); }");
puts("void vfpclasssd(const Opmask& k, const Operand& op, uint8 imm) { if (!op.isXMEM()) throw Error(ERR_BAD_MEM_SIZE); opVex(k, 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_N8, 0x67, imm); }"); puts("void vfpclasssd(const Opmask& k, const Operand& op, uint8_t imm) { if (!op.isXMEM()) XBYAK_THROW(ERR_BAD_MEM_SIZE) opVex(k, 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_N8, 0x67, imm); }");
puts("void vfpclassss(const Opmask& k, const Operand& op, uint8 imm) { if (!op.isXMEM()) throw Error(ERR_BAD_MEM_SIZE); opVex(k, 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_N4, 0x67, imm); }"); puts("void vfpclassss(const Opmask& k, const Operand& op, uint8_t imm) { if (!op.isXMEM()) XBYAK_THROW(ERR_BAD_MEM_SIZE) opVex(k, 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_N4, 0x67, imm); }");
puts("void vpshufbitqmb(const Opmask& k, const Xmm& x, const Operand& op) { opVex(k, &x, op, T_66 | T_0F38 | T_EW0 | T_YMM | T_MUST_EVEX, 0x8F); }"); puts("void vpshufbitqmb(const Opmask& k, const Xmm& x, const Operand& op) { opVex(k, &x, op, T_66 | T_0F38 | T_EW0 | T_YMM | T_MUST_EVEX, 0x8F); }");
puts("void vcvtneps2bf16(const Xmm& x, const Operand& op) { opCvt2(x, op, T_F3 | T_0F38 | T_EW0 | T_YMM | T_SAE_Z | T_MUST_EVEX | T_B32, 0x72); }");
puts("void vp2intersectd(const Opmask& k, const Xmm& x, const Operand& op) { if (k.getOpmaskIdx() != 0) XBYAK_THROW(ERR_OPMASK_IS_ALREADY_SET) opAVX_K_X_XM(k, x, op, T_F2 | T_0F38 | T_YMM | T_EVEX | T_EW0 | T_B32, 0x68); }");
puts("void vp2intersectq(const Opmask& k, const Xmm& x, const Operand& op) { if (k.getOpmaskIdx() != 0) XBYAK_THROW(ERR_OPMASK_IS_ALREADY_SET) opAVX_K_X_XM(k, x, op, T_F2 | T_0F38 | T_YMM | T_EVEX | T_EW1 | T_B64, 0x68); }");
} }
void putV4FMA() void putV4FMA()
@ -728,7 +735,9 @@ int main(int argc, char *[])
bool only64bit = argc == 2; bool only64bit = argc == 2;
putOpmask(only64bit); putOpmask(only64bit);
putBroadcast(only64bit); putBroadcast(only64bit);
if (only64bit) return 0; if (only64bit) {
return 0;
}
putVcmp(); putVcmp();
putX_XM(); putX_XM();
putM_X(); putM_X();

View file

@ -23,9 +23,9 @@ void put_jREGz(const char *reg, bool prefix)
struct GenericTbl { struct GenericTbl {
const char *name; const char *name;
uint8 code1; uint8_t code1;
uint8 code2; uint8_t code2;
uint8 code3; uint8_t code3;
}; };
void putGeneric(const GenericTbl *p, size_t n) void putGeneric(const GenericTbl *p, size_t n)
@ -44,7 +44,7 @@ void putX_X_XM(bool omitOnly)
// (x, x, x/m[, imm]) or (y, y, y/m[, imm]) // (x, x, x/m[, imm]) or (y, y, y/m[, imm])
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
int type; int type;
bool hasIMM; bool hasIMM;
@ -212,25 +212,37 @@ void putX_X_XM(bool omitOnly)
std::string type = type2String(p->type); std::string type = type2String(p->type);
if (omitOnly) { if (omitOnly) {
if (p->enableOmit) { if (p->enableOmit) {
printf("void v%s(const Xmm& x, const Operand& op%s) { v%s(x, x, op%s); }\n", p->name, p->hasIMM ? ", uint8 imm" : "", p->name, p->hasIMM ? ", imm" : ""); printf("void v%s(const Xmm& x, const Operand& op%s) { v%s(x, x, op%s); }\n", p->name, p->hasIMM ? ", uint8_t imm" : "", p->name, p->hasIMM ? ", imm" : "");
} }
} else { } else {
if (p->mode & 1) { if (p->mode & 1) {
if (p->hasIMM) { if (p->hasIMM) {
printf("void %s(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x%02X, 0x66, isXMM_XMMorMEM, static_cast<uint8>(imm), 0x3A); }\n", p->name, p->code); printf("void %s(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x%02X, 0x66, isXMM_XMMorMEM, static_cast<uint8_t>(imm), 0x3A); }\n", p->name, p->code);
} else { } else {
printf("void %s(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x%02X, 0x66, isXMM_XMMorMEM, NONE, 0x38); }\n", p->name, p->code); printf("void %s(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x%02X, 0x66, isXMM_XMMorMEM, NONE, 0x38); }\n", p->name, p->code);
} }
} }
if (p->mode & 2) { if (p->mode & 2) {
printf("void v%s(const Xmm& x1, const Xmm& x2, const Operand& op%s) { opAVX_X_X_XM(x1, x2, op, %s, 0x%02X%s); }\n" printf("void v%s(const Xmm& x1, const Xmm& x2, const Operand& op%s) { opAVX_X_X_XM(x1, x2, op, %s, 0x%02X%s); }\n"
, p->name, p->hasIMM ? ", uint8 imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : ""); , p->name, p->hasIMM ? ", uint8_t imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : "");
} }
} }
} }
} }
} }
void putMemOp(const char *name, uint8_t prefix, uint8_t ext, uint8_t code1, int code2, int bit = 32)
{
printf("void %s(const Address& addr) { ", name);
if (prefix) printf("db(0x%02X); ", prefix);
printf("opModM(addr, Reg%d(%d), 0x%02X, 0x%02X); }\n", bit, ext, code1, code2);
}
void putLoadSeg(const char *name, uint8_t code1, int code2 = NONE)
{
printf("void %s(const Reg& reg, const Address& addr) { opLoadSeg(addr, reg, 0x%02X, 0x%02X); }\n", name, code1, code2);
}
void put() void put()
{ {
const int NO = CodeGenerator::NONE; const int NO = CodeGenerator::NONE;
@ -250,7 +262,7 @@ void put()
const int Q = 1 << 3; const int Q = 1 << 3;
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
// MMX // MMX
@ -300,7 +312,7 @@ void put()
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
int mode; int mode;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
@ -334,7 +346,7 @@ void put()
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
int ext; int ext;
int mode; int mode;
const char *name; const char *name;
@ -364,8 +376,8 @@ void put()
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
uint8 pref; uint8_t pref;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
{ 0x70, 0, "pshufw" }, { 0x70, 0, "pshufw" },
@ -375,13 +387,13 @@ void put()
}; };
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const Tbl *p = &tbl[i]; const Tbl *p = &tbl[i];
printf("void %s(const Mmx& mmx, const Operand& op, uint8 imm8) { opMMX(mmx, op, 0x%02X, 0x%02X, imm8); }\n", p->name, p->code, p->pref); printf("void %s(const Mmx& mmx, const Operand& op, uint8_t imm8) { opMMX(mmx, op, 0x%02X, 0x%02X, imm8); }\n", p->name, p->code, p->pref);
} }
} }
{ {
const struct MmxTbl6 { const struct MmxTbl6 {
uint8 code; // for (reg, reg/[mem]) uint8_t code; // for (reg, reg/[mem])
uint8 code2; // for ([mem], reg) uint8_t code2; // for ([mem], reg)
int pref; int pref;
const char *name; const char *name;
} mmxTbl6[] = { } mmxTbl6[] = {
@ -420,7 +432,7 @@ void put()
{ 0xF2, "sd" }, { 0xF2, "sd" },
}; };
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
int mode; int mode;
const char *name; const char *name;
bool hasImm; bool hasImm;
@ -449,8 +461,8 @@ void put()
for (size_t j = 0; j < NUM_OF_ARRAY(sufTbl); j++) { for (size_t j = 0; j < NUM_OF_ARRAY(sufTbl); j++) {
if (!(p->mode & (1 << j))) continue; if (!(p->mode & (1 << j))) continue;
if (p->hasImm) { if (p->hasImm) {
// don't change uint8 to int because NO is not in byte // don't change uint8_t to int because NO is not in byte
printf("void %s%s(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0x%2X, 0x%02X, isXMM_XMMorMEM, imm8); }\n", p->name, sufTbl[j].name, p->code, sufTbl[j].code); printf("void %s%s(const Xmm& xmm, const Operand& op, uint8_t imm8) { opGen(xmm, op, 0x%2X, 0x%02X, isXMM_XMMorMEM, imm8); }\n", p->name, sufTbl[j].name, p->code, sufTbl[j].code);
} else { } else {
printf("void %s%s(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x%2X, 0x%02X, isXMM_XMMorMEM); }\n", p->name, sufTbl[j].name, p->code, sufTbl[j].code); printf("void %s%s(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x%2X, 0x%02X, isXMM_XMMorMEM); }\n", p->name, sufTbl[j].name, p->code, sufTbl[j].code);
} }
@ -460,8 +472,8 @@ void put()
{ {
// (XMM, XMM) // (XMM, XMM)
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
uint8 pref; uint8_t pref;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
{ 0xF7, 0x66, "maskmovdqu" }, { 0xF7, 0x66, "maskmovdqu" },
@ -478,7 +490,7 @@ void put()
{ {
// (XMM, XMM|MEM) // (XMM, XMM|MEM)
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
int pref; int pref;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
@ -510,7 +522,7 @@ void put()
{ {
// special type // special type
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
int pref; int pref;
const char *name; const char *name;
const char *cond; const char *cond;
@ -554,7 +566,7 @@ void put()
} }
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
int pref; int pref;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
@ -571,7 +583,7 @@ void put()
{ {
// cmov // cmov
const struct Tbl { const struct Tbl {
uint8 ext; uint8_t ext;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
{ 0, "o" }, { 0, "o" },
@ -616,6 +628,22 @@ void put()
printf("void set%s(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, 0x90 | %d); }%s\n", p->name, p->ext, msg); printf("void set%s(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, 0x90 | %d); }%s\n", p->name, p->ext, msg);
} }
} }
{
const struct Tbl {
const char *name;
uint8_t code;
} tbl[] = {
{ "loop", 0xE2 },
{ "loope", 0xE1 },
{ "loopne", 0xE0 },
};
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const Tbl *p = &tbl[i];
printf("void %s(std::string label) { opJmp(label, T_SHORT, 0x%02X, 0, 0); }\n", p->name, p->code);
printf("void %s(const Label& label) { opJmp(label, T_SHORT, 0x%02X, 0, 0); }\n", p->name, p->code);
printf("void %s(const char *label) { %s(std::string(label)); }\n", p->name, p->name);
}
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
{ {
const GenericTbl tbl[] = { const GenericTbl tbl[] = {
@ -633,16 +661,28 @@ void put()
{ "cmpsb", 0xA6 }, { "cmpsb", 0xA6 },
{ "cmpsw", 0x66, 0xA7 }, { "cmpsw", 0x66, 0xA7 },
{ "cmpsd", 0xA7 }, { "cmpsd", 0xA7 },
{ "int3", 0xCC },
{ "scasb", 0xAE }, { "scasb", 0xAE },
{ "scasw", 0x66, 0xAF }, { "scasw", 0x66, 0xAF },
{ "scasd", 0xAF }, { "scasd", 0xAF },
{ "movsb", 0xA4 }, { "movsb", 0xA4 },
{ "leave", 0xC9 },
{ "lodsb", 0xAC },
{ "lodsw", 0x66, 0xAD },
{ "lodsd", 0xAD },
{ "movsw", 0x66, 0xA5 }, { "movsw", 0x66, 0xA5 },
{ "movsd", 0xA5 }, { "movsd", 0xA5 },
{ "outsb", 0x6E },
{ "outsw", 0x66, 0x6F },
{ "outsd", 0x6F },
{ "stosb", 0xAA }, { "stosb", 0xAA },
{ "stosw", 0x66, 0xAB }, { "stosw", 0x66, 0xAB },
{ "stosd", 0xAB }, { "stosd", 0xAB },
{ "rep", 0xF3 }, { "rep", 0xF3 },
{ "repe", 0xF3 },
{ "repz", 0xF3 },
{ "repne", 0xF2 },
{ "repnz", 0xF2 },
{ "lahf", 0x9F }, { "lahf", 0x9F },
{ "lock", 0xF0 }, { "lock", 0xF0 },
@ -651,6 +691,8 @@ void put()
{ "stc", 0xF9 }, { "stc", 0xF9 },
{ "std", 0xFD }, { "std", 0xFD },
{ "sti", 0xFB }, { "sti", 0xFB },
{ "sysenter", 0x0F, 0x34 },
{ "sysexit", 0x0F, 0x35 },
{ "emms", 0x0F, 0x77 }, { "emms", 0x0F, 0x77 },
{ "pause", 0xF3, 0x90 }, { "pause", 0xF3, 0x90 },
@ -684,7 +726,8 @@ void put()
{ "fabs", 0xD9, 0xE1 }, { "fabs", 0xD9, 0xE1 },
{ "faddp", 0xDE, 0xC1 }, { "faddp", 0xDE, 0xC1 },
{ "fchs", 0xD9, 0xE0 }, { "fchs", 0xD9, 0xE0 },
{ "fclex", 0x9B, 0xDB, 0xE2 },
{ "fnclex", 0xDB, 0xE2 },
{ "fcom", 0xD8, 0xD1 }, { "fcom", 0xD8, 0xD1 },
{ "fcomp", 0xD8, 0xD9 }, { "fcomp", 0xD8, 0xD9 },
{ "fcompp", 0xDE, 0xD9 }, { "fcompp", 0xDE, 0xD9 },
@ -724,13 +767,23 @@ void put()
{ "fxtract", 0xD9, 0xF4 }, { "fxtract", 0xD9, 0xF4 },
{ "fyl2x", 0xD9, 0xF1 }, { "fyl2x", 0xD9, 0xF1 },
{ "fyl2xp1", 0xD9, 0xF9 }, { "fyl2xp1", 0xD9, 0xF9 },
// AMD Zen
{ "monitorx", 0x0F, 0x01, 0xFA },
{ "mwaitx", 0x0F, 0x01, 0xFB },
{ "clzero", 0x0F, 0x01, 0xFC },
}; };
putGeneric(tbl, NUM_OF_ARRAY(tbl)); putGeneric(tbl, NUM_OF_ARRAY(tbl));
puts("void enter(uint16_t x, uint8_t y) { db(0xC8); dw(x); db(y); }");
puts("void int_(uint8_t x) { db(0xCD); db(x); }");
putLoadSeg("lss", 0x0F, 0xB2);
putLoadSeg("lfs", 0x0F, 0xB4);
putLoadSeg("lgs", 0x0F, 0xB5);
} }
{ {
const struct Tbl { const struct Tbl {
uint8 code; // (reg, reg) uint8_t code; // (reg, reg)
uint8 ext; // (reg, imm) uint8_t ext; // (reg, imm)
const char *name; const char *name;
} tbl[] = { } tbl[] = {
{ 0x10, 2, "adc" }, { 0x10, 2, "adc" },
@ -745,14 +798,14 @@ void put()
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const Tbl *p = &tbl[i]; const Tbl *p = &tbl[i];
printf("void %s(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x%02X); }\n", p->name, p->code); printf("void %s(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x%02X); }\n", p->name, p->code);
printf("void %s(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x%02X, %d); }\n", p->name, p->code, p->ext); printf("void %s(const Operand& op, uint32_t imm) { opRM_I(op, imm, 0x%02X, %d); }\n", p->name, p->code, p->ext);
} }
} }
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
uint8 ext; uint8_t ext;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
{ 0x48, 1, "dec" }, { 0x48, 1, "dec" },
@ -765,8 +818,8 @@ void put()
} }
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
uint8 ext; uint8_t ext;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
{ 0xa3, 4, "bt" }, { 0xa3, 4, "bt" },
@ -777,13 +830,13 @@ void put()
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const Tbl *p = &tbl[i]; const Tbl *p = &tbl[i];
printf("void %s(const Operand& op, const Reg& reg) { opModRM(reg, op, op.isREG(16|32|64) && op.getBit() == reg.getBit(), op.isMEM(), 0x0f, 0x%02X); }\n", p->name, p->code); printf("void %s(const Operand& op, const Reg& reg) { opModRM(reg, op, op.isREG(16|32|64) && op.getBit() == reg.getBit(), op.isMEM(), 0x0f, 0x%02X); }\n", p->name, p->code);
printf("void %s(const Operand& op, uint8 imm) { opR_ModM(op, 16|32|64, %d, 0x0f, 0xba, NONE, false, 1); db(imm); }\n", p->name, p->ext); printf("void %s(const Operand& op, uint8_t imm) { opR_ModM(op, 16|32|64, %d, 0x0f, 0xba, NONE, false, 1); db(imm); }\n", p->name, p->ext);
} }
} }
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
uint8 ext; uint8_t ext;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
{ 0xF6, 6, "div" }, { 0xF6, 6, "div" },
@ -802,7 +855,7 @@ void put()
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 ext; uint8_t ext;
} tbl[] = { } tbl[] = {
{ "rcl", 2 }, { "rcl", 2 },
{ "rcr", 3 }, { "rcr", 3 },
@ -823,21 +876,21 @@ void put()
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
} tbl[] = { } tbl[] = {
{ "shld", 0xA4 }, { "shld", 0xA4 },
{ "shrd", 0xAC }, { "shrd", 0xAC },
}; };
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const Tbl *p = &tbl[i]; const Tbl *p = &tbl[i];
printf("void %s(const Operand& op, const Reg& reg, uint8 imm) { opShxd(op, reg, imm, 0x%02X); }\n", p->name, p->code); printf("void %s(const Operand& op, const Reg& reg, uint8_t imm) { opShxd(op, reg, imm, 0x%02X); }\n", p->name, p->code);
printf("void %s(const Operand& op, const Reg& reg, const Reg8& _cl) { opShxd(op, reg, 0, 0x%02X, &_cl); }\n", p->name, p->code); printf("void %s(const Operand& op, const Reg& reg, const Reg8& _cl) { opShxd(op, reg, 0, 0x%02X, &_cl); }\n", p->name, p->code);
} }
} }
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
} tbl[] = { } tbl[] = {
{ "bsf", 0xBC }, { "bsf", 0xBC },
{ "bsr", 0xBD }, { "bsr", 0xBD },
@ -850,7 +903,7 @@ void put()
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
} tbl[] = { } tbl[] = {
{ "popcnt", 0xB8 }, { "popcnt", 0xB8 },
{ "tzcnt", 0xBC }, { "tzcnt", 0xBC },
@ -864,7 +917,7 @@ void put()
// SSSE3 // SSSE3
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
{ 0x00, "pshufb" }, { 0x00, "pshufb" },
@ -887,12 +940,12 @@ void put()
const Tbl *p = &tbl[i]; const Tbl *p = &tbl[i];
printf("void %s(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x%02X, 0x66, NONE, 0x38); }\n", p->name, p->code); printf("void %s(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x%02X, 0x66, NONE, 0x38); }\n", p->name, p->code);
} }
printf("void palignr(const Mmx& mmx, const Operand& op, int imm) { opMMX(mmx, op, 0x0f, 0x66, static_cast<uint8>(imm), 0x3a); }\n"); printf("void palignr(const Mmx& mmx, const Operand& op, int imm) { opMMX(mmx, op, 0x0f, 0x66, static_cast<uint8_t>(imm), 0x3a); }\n");
} }
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
} tbl[] = { } tbl[] = {
{ "pclmullqlqdq", 0 }, { "pclmullqlqdq", 0 },
{ "pclmulhqlqdq", 1 }, { "pclmulhqlqdq", 1 },
@ -906,26 +959,41 @@ void put()
} }
{ {
const struct Tbl { const struct Tbl {
uint8 code1; uint8_t code1;
int code2; int code2;
uint8 ext; uint8_t ext;
const char *name; const char *name;
uint8_t prefix;
} tbl[] = { } tbl[] = {
{ 0x0F, 0xAE, 2, "ldmxcsr" }, { 0x0F, 0xAE, 2, "ldmxcsr", 0 },
{ 0x0F, 0xAE, 3, "stmxcsr" }, { 0x0F, 0xAE, 3, "stmxcsr", 0 },
{ 0x0F, 0xAE, 7, "clflush" }, // 0x80 is bug of nasm ? { 0x0F, 0xAE, 7, "clflush", 0 },
{ 0xD9, NONE, 5, "fldcw" }, { 0x0F, 0xAE, 7, "clflushopt", 0x66 },
// { 0x9B, 0xD9, 7, "fstcw" }, // not correct order for fstcw [eax] on 64bit OS { 0xDF, NONE, 4, "fbld", 0 },
{ 0xDF, NONE, 6, "fbstp", 0 },
{ 0xD9, NONE, 5, "fldcw", 0 },
{ 0xD9, NONE, 4, "fldenv", 0 },
{ 0xDD, NONE, 4, "frstor", 0 },
{ 0xDD, NONE, 6, "fsave", 0x9B },
{ 0xDD, NONE, 6, "fnsave", 0 },
{ 0xD9, NONE, 7, "fstcw", 0x9B },
{ 0xD9, NONE, 7, "fnstcw", 0 },
{ 0xD9, NONE, 6, "fstenv", 0x9B },
{ 0xD9, NONE, 6, "fnstenv", 0 },
{ 0xDD, NONE, 7, "fstsw", 0x9B },
{ 0xDD, NONE, 7, "fnstsw", 0 },
{ 0x0F, 0xAE, 1, "fxrstor", 0 },
}; };
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const Tbl *p = &tbl[i]; const Tbl *p = &tbl[i];
printf("void %s(const Address& addr) { opModM(addr, Reg32(%d), 0x%02X, 0x%02X); }\n", p->name, p->ext, p->code1, p->code2); putMemOp(p->name, p->prefix, p->ext, p->code1, p->code2);
} }
printf("void fstcw(const Address& addr) { db(0x9B); opModM(addr, Reg32(7), 0xD9, NONE); }\n"); puts("void fstsw(const Reg16& r) { if (r.getIdx() != Operand::AX) XBYAK_THROW(ERR_BAD_PARAMETER) db(0x9B); db(0xDF); db(0xE0); }");
puts("void fnstsw(const Reg16& r) { if (r.getIdx() != Operand::AX) XBYAK_THROW(ERR_BAD_PARAMETER) db(0xDF); db(0xE0); }");
} }
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
{ 0x2B, "movntpd" }, { 0x2B, "movntpd" },
@ -939,7 +1007,7 @@ void put()
} }
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
{ 0xBE, "movsx" }, { 0xBE, "movsx" },
@ -950,6 +1018,12 @@ void put()
printf("void %s(const Reg& reg, const Operand& op) { opMovxx(reg, op, 0x%02X); }\n", p->name, p->code); printf("void %s(const Reg& reg, const Operand& op) { opMovxx(reg, op, 0x%02X); }\n", p->name, p->code);
} }
} }
{ // in/out
puts("void in_(const Reg& a, uint8_t v) { opInOut(a, 0xE4, v); }");
puts("void in_(const Reg& a, const Reg& d) { opInOut(a, d, 0xEC); }");
puts("void out_(uint8_t v, const Reg& a) { opInOut(a, 0xE6, v); }");
puts("void out_(const Reg& d, const Reg& a) { opInOut(a, d, 0xEE); }");
}
// mpx // mpx
{ {
puts("void bndcl(const BoundsReg& bnd, const Operand& op) { db(0xF3); opR_ModM(op, i32e, bnd.getIdx(), 0x0F, 0x1A, NONE, !op.isMEM()); }"); puts("void bndcl(const BoundsReg& bnd, const Operand& op) { db(0xF3); opR_ModM(op, i32e, bnd.getIdx(), 0x0F, 0x1A, NONE, !op.isMEM()); }");
@ -963,7 +1037,7 @@ void put()
} }
// misc // misc
{ {
puts("void lea(const Reg& reg, const Address& addr) { if (!reg.isBit(16 | i32e)) throw Error(ERR_BAD_SIZE_OF_REGISTER); opModM(addr, reg, 0x8D); }"); puts("void lea(const Reg& reg, const Address& addr) { if (!reg.isBit(16 | i32e)) XBYAK_THROW(ERR_BAD_SIZE_OF_REGISTER) opModM(addr, reg, 0x8D); }");
puts("void bswap(const Reg32e& reg) { opModR(Reg32(1), reg, 0x0F); }"); puts("void bswap(const Reg32e& reg) { opModR(Reg32(1), reg, 0x0F); }");
puts("void ret(int imm = 0) { if (imm) { db(0xC2); dw(imm); } else { db(0xC3); } }"); puts("void ret(int imm = 0) { if (imm) { db(0xC2); dw(imm); } else { db(0xC3); } }");
@ -975,24 +1049,24 @@ void put()
puts("void adox(const Reg32e& reg, const Operand& op) { opGen(reg, op, 0xF6, 0xF3, isREG32_REG32orMEM, NONE, 0x38); }"); puts("void adox(const Reg32e& reg, const Operand& op) { opGen(reg, op, 0xF6, 0xF3, isREG32_REG32orMEM, NONE, 0x38); }");
puts("void cmpxchg8b(const Address& addr) { opModM(addr, Reg32(1), 0x0F, 0xC7); }"); puts("void cmpxchg8b(const Address& addr) { opModM(addr, Reg32(1), 0x0F, 0xC7); }");
puts("void pextrw(const Operand& op, const Mmx& xmm, uint8 imm) { opExt(op, xmm, 0x15, imm, true); }"); puts("void pextrw(const Operand& op, const Mmx& xmm, uint8_t imm) { opExt(op, xmm, 0x15, imm, true); }");
puts("void pextrb(const Operand& op, const Xmm& xmm, uint8 imm) { opExt(op, xmm, 0x14, imm); }"); puts("void pextrb(const Operand& op, const Xmm& xmm, uint8_t imm) { opExt(op, xmm, 0x14, imm); }");
puts("void pextrd(const Operand& op, const Xmm& xmm, uint8 imm) { opExt(op, xmm, 0x16, imm); }"); puts("void pextrd(const Operand& op, const Xmm& xmm, uint8_t imm) { opExt(op, xmm, 0x16, imm); }");
puts("void extractps(const Operand& op, const Xmm& xmm, uint8 imm) { opExt(op, xmm, 0x17, imm); }"); puts("void extractps(const Operand& op, const Xmm& xmm, uint8_t imm) { opExt(op, xmm, 0x17, imm); }");
puts("void pinsrw(const Mmx& mmx, const Operand& op, int imm) { if (!op.isREG(32) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opGen(mmx, op, 0xC4, mmx.isXMM() ? 0x66 : NONE, 0, imm); }"); puts("void pinsrw(const Mmx& mmx, const Operand& op, int imm) { if (!op.isREG(32) && !op.isMEM()) XBYAK_THROW(ERR_BAD_COMBINATION) opGen(mmx, op, 0xC4, mmx.isXMM() ? 0x66 : NONE, 0, imm); }");
puts("void insertps(const Xmm& xmm, const Operand& op, uint8 imm) { opGen(xmm, op, 0x21, 0x66, isXMM_XMMorMEM, imm, 0x3A); }"); puts("void insertps(const Xmm& xmm, const Operand& op, uint8_t imm) { opGen(xmm, op, 0x21, 0x66, isXMM_XMMorMEM, imm, 0x3A); }");
puts("void pinsrb(const Xmm& xmm, const Operand& op, uint8 imm) { opGen(xmm, op, 0x20, 0x66, isXMM_REG32orMEM, imm, 0x3A); }"); puts("void pinsrb(const Xmm& xmm, const Operand& op, uint8_t imm) { opGen(xmm, op, 0x20, 0x66, isXMM_REG32orMEM, imm, 0x3A); }");
puts("void pinsrd(const Xmm& xmm, const Operand& op, uint8 imm) { opGen(xmm, op, 0x22, 0x66, isXMM_REG32orMEM, imm, 0x3A); }"); puts("void pinsrd(const Xmm& xmm, const Operand& op, uint8_t imm) { opGen(xmm, op, 0x22, 0x66, isXMM_REG32orMEM, imm, 0x3A); }");
puts("void pmovmskb(const Reg32e& reg, const Mmx& mmx) { if (mmx.isXMM()) db(0x66); opModR(reg, mmx, 0x0F, 0xD7); }"); puts("void pmovmskb(const Reg32e& reg, const Mmx& mmx) { if (mmx.isXMM()) db(0x66); opModR(reg, mmx, 0x0F, 0xD7); }");
puts("void maskmovq(const Mmx& reg1, const Mmx& reg2) { if (!reg1.isMMX() || !reg2.isMMX()) throw Error(ERR_BAD_COMBINATION); opModR(reg1, reg2, 0x0F, 0xF7); }"); puts("void maskmovq(const Mmx& reg1, const Mmx& reg2) { if (!reg1.isMMX() || !reg2.isMMX()) XBYAK_THROW(ERR_BAD_COMBINATION) opModR(reg1, reg2, 0x0F, 0xF7); }");
puts("void movmskps(const Reg32e& reg, const Xmm& xmm) { opModR(reg, xmm, 0x0F, 0x50); }"); puts("void movmskps(const Reg32e& reg, const Xmm& xmm) { opModR(reg, xmm, 0x0F, 0x50); }");
puts("void movmskpd(const Reg32e& reg, const Xmm& xmm) { db(0x66); movmskps(reg, xmm); }"); puts("void movmskpd(const Reg32e& reg, const Xmm& xmm) { db(0x66); movmskps(reg, xmm); }");
puts("void movntps(const Address& addr, const Xmm& xmm) { opModM(addr, Mmx(xmm.getIdx()), 0x0F, 0x2B); }"); puts("void movntps(const Address& addr, const Xmm& xmm) { opModM(addr, Mmx(xmm.getIdx()), 0x0F, 0x2B); }");
puts("void movntdqa(const Xmm& xmm, const Address& addr) { db(0x66); opModM(addr, xmm, 0x0F, 0x38, 0x2A); }"); puts("void movntdqa(const Xmm& xmm, const Address& addr) { db(0x66); opModM(addr, xmm, 0x0F, 0x38, 0x2A); }");
puts("void lddqu(const Xmm& xmm, const Address& addr) { db(0xF2); opModM(addr, xmm, 0x0F, 0xF0); }"); puts("void lddqu(const Xmm& xmm, const Address& addr) { db(0xF2); opModM(addr, xmm, 0x0F, 0xF0); }");
puts("void movnti(const Address& addr, const Reg32e& reg) { opModM(addr, reg, 0x0F, 0xC3); }"); puts("void movnti(const Address& addr, const Reg32e& reg) { opModM(addr, reg, 0x0F, 0xC3); }");
puts("void movntq(const Address& addr, const Mmx& mmx) { if (!mmx.isMMX()) throw Error(ERR_BAD_COMBINATION); opModM(addr, mmx, 0x0F, 0xE7); }"); puts("void movntq(const Address& addr, const Mmx& mmx) { if (!mmx.isMMX()) XBYAK_THROW(ERR_BAD_COMBINATION) opModM(addr, mmx, 0x0F, 0xE7); }");
puts("void movd(const Address& addr, const Mmx& mmx) { if (mmx.isXMM()) db(0x66); opModM(addr, mmx, 0x0F, 0x7E); }"); puts("void movd(const Address& addr, const Mmx& mmx) { if (mmx.isXMM()) db(0x66); opModM(addr, mmx, 0x0F, 0x7E); }");
puts("void movd(const Reg32& reg, const Mmx& mmx) { if (mmx.isXMM()) db(0x66); opModR(mmx, reg, 0x0F, 0x7E); }"); puts("void movd(const Reg32& reg, const Mmx& mmx) { if (mmx.isXMM()) db(0x66); opModR(mmx, reg, 0x0F, 0x7E); }");
@ -1002,18 +1076,18 @@ void put()
puts("void movdq2q(const Mmx& mmx, const Xmm& xmm) { db(0xF2); opModR(mmx, xmm, 0x0F, 0xD6); }"); puts("void movdq2q(const Mmx& mmx, const Xmm& xmm) { db(0xF2); opModR(mmx, xmm, 0x0F, 0xD6); }");
puts("void movq(const Mmx& mmx, const Operand& op) { if (mmx.isXMM()) db(0xF3); opModRM(mmx, op, (mmx.getKind() == op.getKind()), op.isMEM(), 0x0F, mmx.isXMM() ? 0x7E : 0x6F); }"); puts("void movq(const Mmx& mmx, const Operand& op) { if (mmx.isXMM()) db(0xF3); opModRM(mmx, op, (mmx.getKind() == op.getKind()), op.isMEM(), 0x0F, mmx.isXMM() ? 0x7E : 0x6F); }");
puts("void movq(const Address& addr, const Mmx& mmx) { if (mmx.isXMM()) db(0x66); opModM(addr, mmx, 0x0F, mmx.isXMM() ? 0xD6 : 0x7F); }"); puts("void movq(const Address& addr, const Mmx& mmx) { if (mmx.isXMM()) db(0x66); opModM(addr, mmx, 0x0F, mmx.isXMM() ? 0xD6 : 0x7F); }");
puts("void rdrand(const Reg& r) { if (r.isBit(8)) throw Error(ERR_BAD_SIZE_OF_REGISTER); opModR(Reg(6, Operand::REG, r.getBit()), r, 0x0F, 0xC7); }"); puts("void rdrand(const Reg& r) { if (r.isBit(8)) XBYAK_THROW(ERR_BAD_SIZE_OF_REGISTER) opModR(Reg(6, Operand::REG, r.getBit()), r, 0x0F, 0xC7); }");
puts("void rdseed(const Reg& r) { if (r.isBit(8)) throw Error(ERR_BAD_SIZE_OF_REGISTER); opModR(Reg(7, Operand::REG, r.getBit()), r, 0x0F, 0xC7); }"); puts("void rdseed(const Reg& r) { if (r.isBit(8)) XBYAK_THROW(ERR_BAD_SIZE_OF_REGISTER) opModR(Reg(7, Operand::REG, r.getBit()), r, 0x0F, 0xC7); }");
puts("void crc32(const Reg32e& reg, const Operand& op) { if (reg.isBit(32) && op.isBit(16)) db(0x66); db(0xF2); opModRM(reg, op, op.isREG(), op.isMEM(), 0x0F, 0x38, 0xF0 | (op.isBit(8) ? 0 : 1)); }"); puts("void crc32(const Reg32e& reg, const Operand& op) { if (reg.isBit(32) && op.isBit(16)) db(0x66); db(0xF2); opModRM(reg, op, op.isREG(), op.isMEM(), 0x0F, 0x38, 0xF0 | (op.isBit(8) ? 0 : 1)); }");
} }
{ {
const struct Tbl { const struct Tbl {
uint8 m16; uint8_t m16;
uint8 m32; uint8_t m32;
uint8 m64; uint8_t m64;
uint8 ext; uint8_t ext;
const char *name; const char *name;
uint8 m64ext; uint8_t m64ext;
} tbl[] = { } tbl[] = {
{ 0x00, 0xD8, 0xDC, 0, "fadd" }, { 0x00, 0xD8, 0xDC, 0, "fadd" },
{ 0xDE, 0xDA, 0x00, 0, "fiadd" }, { 0xDE, 0xDA, 0x00, 0, "fiadd" },
@ -1046,8 +1120,8 @@ void put()
} }
{ {
const struct Tbl { const struct Tbl {
uint32 code1; uint32_t code1;
uint32 code2; uint32_t code2;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
{ 0xD8C0, 0xDCC0, "fadd" }, { 0xD8C0, 0xDCC0, "fadd" },
@ -1091,8 +1165,8 @@ void put()
} }
{ {
const struct Tbl { const struct Tbl {
uint8 code1; uint8_t code1;
uint8 code2; uint8_t code2;
const char *name; const char *name;
} tbl[] = { } tbl[] = {
{ 0xD8, 0xD0, "fcom" }, { 0xD8, 0xD0, "fcom" },
@ -1113,7 +1187,7 @@ void put()
// AVX // AVX
{ // pd, ps, sd, ss { // pd, ps, sd, ss
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
bool only_pd_ps; bool only_pd_ps;
} tbl[] = { } tbl[] = {
@ -1142,7 +1216,7 @@ void put()
// (x, x/m[, imm]) or (y, y/m[, imm]) // (x, x/m[, imm]) or (y, y/m[, imm])
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
int type; int type;
bool hasIMM; bool hasIMM;
@ -1223,7 +1297,7 @@ void put()
const Tbl *p = &tbl[i]; const Tbl *p = &tbl[i];
std::string type = type2String(p->type); std::string type = type2String(p->type);
if (p->mode & 1) { if (p->mode & 1) {
const char *immS1 = p->hasIMM ? ", uint8 imm" : ""; const char *immS1 = p->hasIMM ? ", uint8_t imm" : "";
const char *immS2 = p->hasIMM ? ", imm" : ", NONE"; const char *immS2 = p->hasIMM ? ", imm" : ", NONE";
const char *pref = p->type & T_66 ? "0x66" : p->type & T_F2 ? "0xF2" : p->type & T_F3 ? "0xF3" : "NONE"; const char *pref = p->type & T_66 ? "0x66" : p->type & T_F2 ? "0xF2" : p->type & T_F3 ? "0xF3" : "NONE";
const char *suf = p->type & T_0F38 ? "0x38" : p->type & T_0F3A ? "0x3A" : "NONE"; const char *suf = p->type & T_0F38 ? "0x38" : p->type & T_0F3A ? "0x3A" : "NONE";
@ -1231,14 +1305,14 @@ void put()
} }
if (p->mode & 2) { if (p->mode & 2) {
printf("void v%s(const Xmm& xm, const Operand& op%s) { opAVX_X_XM_IMM(xm, op, %s, 0x%02X%s); }\n" printf("void v%s(const Xmm& xm, const Operand& op%s) { opAVX_X_XM_IMM(xm, op, %s, 0x%02X%s); }\n"
, p->name, p->hasIMM ? ", uint8 imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : ""); , p->name, p->hasIMM ? ", uint8_t imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : "");
} }
} }
} }
// (m, x), (m, y) // (m, x), (m, y)
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
int type; int type;
} tbl[] = { } tbl[] = {
@ -1259,7 +1333,7 @@ void put()
// (x, x/m), (y, y/m), (x, x, x/m), (y, y, y/m) // (x, x/m), (y, y/m), (x, x, x/m), (y, y, y/m)
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
int type; int type;
int mode; // 1 : sse, 2 : avx, 3 : sse + avx int mode; // 1 : sse, 2 : avx, 3 : sse + avx
@ -1280,7 +1354,7 @@ void put()
const Tbl *p = &tbl[i]; const Tbl *p = &tbl[i];
std::string type = type2String(p->type); std::string type = type2String(p->type);
if (p->mode & 1) { if (p->mode & 1) {
uint8 pref = p->type & T_66 ? 0x66 : p->type & T_F2 ? 0xF2 : p->type & T_F3 ? 0xF3 : 0; uint8_t pref = p->type & T_66 ? 0x66 : p->type & T_F2 ? 0xF2 : p->type & T_F3 ? 0xF3 : 0;
printf("void %s(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x%02X, 0x%02X, isXMM_XMMorMEM%s); }\n", p->name, p->code, pref, p->type & T_0F38 ? ", NONE, 0x38" : ""); printf("void %s(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x%02X, 0x%02X, isXMM_XMMorMEM%s); }\n", p->name, p->code, pref, p->type & T_0F38 ? ", NONE, 0x38" : "");
} }
if (p->mode & 2) { if (p->mode & 2) {
@ -1308,7 +1382,7 @@ void put()
// vpermd, vpermps // vpermd, vpermps
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
int type; int type;
} tbl[] = { } tbl[] = {
@ -1326,7 +1400,7 @@ void put()
// vpermq, vpermpd // vpermq, vpermpd
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
int type; int type;
} tbl[] = { } tbl[] = {
@ -1336,7 +1410,7 @@ void put()
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const Tbl& p = tbl[i]; const Tbl& p = tbl[i];
std::string type = type2String(p.type); std::string type = type2String(p.type);
printf("void %s(const Ymm& y, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(y, op, %s, 0x%02X, imm); }\n", p.name, type.c_str(), p.code); printf("void %s(const Ymm& y, const Operand& op, uint8_t imm) { opAVX_X_XM_IMM(y, op, %s, 0x%02X, imm); }\n", p.name, type.c_str(), p.code);
} }
} }
// vcmpeqps // vcmpeqps
@ -1363,7 +1437,7 @@ void put()
const struct Tbl { const struct Tbl {
bool isH; bool isH;
bool isPd; bool isPd;
uint8 code; uint8_t code;
} tbl[] = { } tbl[] = {
{ true, true, 0x16 }, { true, true, 0x16 },
{ true, false, 0x16 }, { true, false, 0x16 },
@ -1375,7 +1449,7 @@ void put()
char c = p.isH ? 'h' : 'l'; char c = p.isH ? 'h' : 'l';
const char *suf = p.isPd ? "pd" : "ps"; const char *suf = p.isPd ? "pd" : "ps";
const char *type = p.isPd ? "T_0F | T_66 | T_EVEX | T_EW1 | T_N8" : "T_0F | T_EVEX | T_EW0 | T_N8"; const char *type = p.isPd ? "T_0F | T_66 | T_EVEX | T_EW1 | T_N8" : "T_0F | T_EVEX | T_EW0 | T_N8";
printf("void vmov%c%s(const Xmm& x, const Operand& op1, const Operand& op2 = Operand()) { if (!op2.isNone() && !op2.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x, op1, op2, %s, 0x%02X); }\n" printf("void vmov%c%s(const Xmm& x, const Operand& op1, const Operand& op2 = Operand()) { if (!op2.isNone() && !op2.isMEM()) XBYAK_THROW(ERR_BAD_COMBINATION) opAVX_X_X_XM(x, op1, op2, %s, 0x%02X); }\n"
, c, suf, type, p.code); , c, suf, type, p.code);
printf("void vmov%c%s(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, %s, 0x%02X); }\n" printf("void vmov%c%s(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, %s, 0x%02X); }\n"
, c, suf, type, p.code + 1); , c, suf, type, p.code + 1);
@ -1384,7 +1458,7 @@ void put()
// FMA // FMA
{ {
const struct Tbl { const struct Tbl {
uint8 code; uint8_t code;
const char *name; const char *name;
bool supportYMM; bool supportYMM;
} tbl[] = { } tbl[] = {
@ -1408,7 +1482,7 @@ void put()
for (int k = 0; k < 3; k++) { for (int k = 0; k < 3; k++) {
const struct Ord { const struct Ord {
const char *str; const char *str;
uint8 code; uint8_t code;
} ord[] = { } ord[] = {
{ "132", 0x90 }, { "132", 0x90 },
{ "213", 0xA0 }, { "213", 0xA0 },
@ -1438,10 +1512,10 @@ void put()
{ {
printf("void vbroadcastf128(const Ymm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, T_0F38 | T_66 | T_W0 | T_YMM, 0x1A); }\n"); printf("void vbroadcastf128(const Ymm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, T_0F38 | T_66 | T_W0 | T_YMM, 0x1A); }\n");
printf("void vbroadcasti128(const Ymm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, T_0F38 | T_66 | T_W0 | T_YMM, 0x5A); }\n"); printf("void vbroadcasti128(const Ymm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, T_0F38 | T_66 | T_W0 | T_YMM, 0x5A); }\n");
printf("void vbroadcastsd(const Ymm& y, const Operand& op) { if (!op.isMEM() && !(y.isYMM() && op.isXMM()) && !(y.isZMM() && op.isXMM())) throw Error(ERR_BAD_COMBINATION); opAVX_X_XM_IMM(y, op, T_0F38 | T_66 | T_W0 | T_YMM | T_EVEX | T_EW1 | T_N8, 0x19); }\n"); printf("void vbroadcastsd(const Ymm& y, const Operand& op) { if (!op.isMEM() && !(y.isYMM() && op.isXMM()) && !(y.isZMM() && op.isXMM())) XBYAK_THROW(ERR_BAD_COMBINATION) opAVX_X_XM_IMM(y, op, T_0F38 | T_66 | T_W0 | T_YMM | T_EVEX | T_EW1 | T_N8, 0x19); }\n");
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
int type; int type;
bool ew1; bool ew1;
} tbl[] = { } tbl[] = {
@ -1454,40 +1528,40 @@ void put()
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const Tbl& p = tbl[i]; const Tbl& p = tbl[i];
std::string type = type2String(p.type); std::string type = type2String(p.type);
printf("void %s(const Xmm& x, const Operand& op) { if (!(op.isXMM() || op.isMEM())) throw Error(ERR_BAD_COMBINATION); opAVX_X_XM_IMM(x, op, %s, 0x%02X); }\n", p.name, type.c_str(), p.code); printf("void %s(const Xmm& x, const Operand& op) { if (!(op.isXMM() || op.isMEM())) XBYAK_THROW(ERR_BAD_COMBINATION) opAVX_X_XM_IMM(x, op, %s, 0x%02X); }\n", p.name, type.c_str(), p.code);
} }
puts("void vextractf128(const Operand& op, const Ymm& y, uint8 imm) { if (!(op.isXMEM() && y.isYMM())) throw Error(ERR_BAD_COMBINATION); opVex(y, 0, op, T_0F3A | T_66 | T_W0 | T_YMM, 0x19, imm); }"); puts("void vextractf128(const Operand& op, const Ymm& y, uint8_t imm) { if (!(op.isXMEM() && y.isYMM())) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(y, 0, op, T_0F3A | T_66 | T_W0 | T_YMM, 0x19, imm); }");
puts("void vextracti128(const Operand& op, const Ymm& y, uint8 imm) { if (!(op.isXMEM() && y.isYMM())) throw Error(ERR_BAD_COMBINATION); opVex(y, 0, op, T_0F3A | T_66 | T_W0 | T_YMM, 0x39, imm); }"); puts("void vextracti128(const Operand& op, const Ymm& y, uint8_t imm) { if (!(op.isXMEM() && y.isYMM())) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(y, 0, op, T_0F3A | T_66 | T_W0 | T_YMM, 0x39, imm); }");
puts("void vextractps(const Operand& op, const Xmm& x, uint8 imm) { if (!((op.isREG(32) || op.isMEM()) && x.isXMM())) throw Error(ERR_BAD_COMBINATION); opVex(x, 0, op, T_0F3A | T_66 | T_W0 | T_EVEX | T_N4, 0x17, imm); }"); puts("void vextractps(const Operand& op, const Xmm& x, uint8_t imm) { if (!((op.isREG(32) || op.isMEM()) && x.isXMM())) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(x, 0, op, T_0F3A | T_66 | T_W0 | T_EVEX | T_N4, 0x17, imm); }");
puts("void vinsertf128(const Ymm& y1, const Ymm& y2, const Operand& op, uint8 imm) { if (!(y1.isYMM() && y2.isYMM() && op.isXMEM())) throw Error(ERR_BAD_COMBINATION); opVex(y1, &y2, op, T_0F3A | T_66 | T_W0 | T_YMM, 0x18, imm); }"); puts("void vinsertf128(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { if (!(y1.isYMM() && y2.isYMM() && op.isXMEM())) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(y1, &y2, op, T_0F3A | T_66 | T_W0 | T_YMM, 0x18, imm); }");
puts("void vinserti128(const Ymm& y1, const Ymm& y2, const Operand& op, uint8 imm) { if (!(y1.isYMM() && y2.isYMM() && op.isXMEM())) throw Error(ERR_BAD_COMBINATION); opVex(y1, &y2, op, T_0F3A | T_66 | T_W0 | T_YMM, 0x38, imm); }"); puts("void vinserti128(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { if (!(y1.isYMM() && y2.isYMM() && op.isXMEM())) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(y1, &y2, op, T_0F3A | T_66 | T_W0 | T_YMM, 0x38, imm); }");
puts("void vperm2f128(const Ymm& y1, const Ymm& y2, const Operand& op, uint8 imm) { if (!(y1.isYMM() && y2.isYMM() && op.isYMEM())) throw Error(ERR_BAD_COMBINATION); opVex(y1, &y2, op, T_0F3A | T_66 | T_W0 | T_YMM, 0x06, imm); }"); puts("void vperm2f128(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { if (!(y1.isYMM() && y2.isYMM() && op.isYMEM())) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(y1, &y2, op, T_0F3A | T_66 | T_W0 | T_YMM, 0x06, imm); }");
puts("void vperm2i128(const Ymm& y1, const Ymm& y2, const Operand& op, uint8 imm) { if (!(y1.isYMM() && y2.isYMM() && op.isYMEM())) throw Error(ERR_BAD_COMBINATION); opVex(y1, &y2, op, T_0F3A | T_66 | T_W0 | T_YMM, 0x46, imm); }"); puts("void vperm2i128(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { if (!(y1.isYMM() && y2.isYMM() && op.isYMEM())) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(y1, &y2, op, T_0F3A | T_66 | T_W0 | T_YMM, 0x46, imm); }");
puts("void vlddqu(const Xmm& x, const Address& addr) { opAVX_X_X_XM(x, cvtIdx0(x), addr, T_0F | T_F2 | T_W0 | T_YMM, 0xF0); }"); puts("void vlddqu(const Xmm& x, const Address& addr) { opAVX_X_X_XM(x, cvtIdx0(x), addr, T_0F | T_F2 | T_W0 | T_YMM, 0xF0); }");
puts("void vldmxcsr(const Address& addr) { opAVX_X_X_XM(xm2, xm0, addr, T_0F, 0xAE); }"); puts("void vldmxcsr(const Address& addr) { opAVX_X_X_XM(xm2, xm0, addr, T_0F, 0xAE); }");
puts("void vstmxcsr(const Address& addr) { opAVX_X_X_XM(xm3, xm0, addr, T_0F, 0xAE); }"); puts("void vstmxcsr(const Address& addr) { opAVX_X_X_XM(xm3, xm0, addr, T_0F, 0xAE); }");
puts("void vmaskmovdqu(const Xmm& x1, const Xmm& x2) { opAVX_X_X_XM(x1, xm0, x2, T_0F | T_66, 0xF7); }"); puts("void vmaskmovdqu(const Xmm& x1, const Xmm& x2) { opAVX_X_X_XM(x1, xm0, x2, T_0F | T_66, 0xF7); }");
puts("void vpextrb(const Operand& op, const Xmm& x, uint8 imm) { if (!((op.isREG(8|16|i32e) || op.isMEM()) && x.isXMM())) throw Error(ERR_BAD_COMBINATION); opVex(x, 0, op, T_0F3A | T_66 | T_EVEX | T_N1, 0x14, imm); }"); puts("void vpextrb(const Operand& op, const Xmm& x, uint8_t imm) { if (!((op.isREG(8|16|i32e) || op.isMEM()) && x.isXMM())) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(x, 0, op, T_0F3A | T_66 | T_EVEX | T_N1, 0x14, imm); }");
puts("void vpextrw(const Operand& op, const Xmm& x, uint8 imm) { if (!((op.isREG(16|i32e) || op.isMEM()) && x.isXMM())) throw Error(ERR_BAD_COMBINATION); if (op.isREG() && x.getIdx() < 16) { opAVX_X_X_XM(Xmm(op.getIdx()), xm0, x, T_0F | T_66, 0xC5, imm); } else { opVex(x, 0, op, T_0F3A | T_66 | T_EVEX | T_N2, 0x15, imm); } }"); puts("void vpextrw(const Operand& op, const Xmm& x, uint8_t imm) { if (!((op.isREG(16|i32e) || op.isMEM()) && x.isXMM())) XBYAK_THROW(ERR_BAD_COMBINATION) if (op.isREG() && x.getIdx() < 16) { opAVX_X_X_XM(Xmm(op.getIdx()), xm0, x, T_0F | T_66, 0xC5, imm); } else { opVex(x, 0, op, T_0F3A | T_66 | T_EVEX | T_N2, 0x15, imm); } }");
puts("void vpextrd(const Operand& op, const Xmm& x, uint8 imm) { if (!((op.isREG(32) || op.isMEM()) && x.isXMM())) throw Error(ERR_BAD_COMBINATION); opVex(x, 0, op, T_0F3A | T_66 | T_W0 | T_EVEX | T_EW0 | T_N4, 0x16, imm); }"); puts("void vpextrd(const Operand& op, const Xmm& x, uint8_t imm) { if (!((op.isREG(32) || op.isMEM()) && x.isXMM())) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(x, 0, op, T_0F3A | T_66 | T_W0 | T_EVEX | T_EW0 | T_N4, 0x16, imm); }");
puts("void vpextrq(const Operand& op, const Xmm& x, uint8 imm) { if (!((op.isREG(64) || op.isMEM()) && x.isXMM())) throw Error(ERR_BAD_COMBINATION); opVex(x, 0, op, T_0F3A | T_66 | T_W1 | T_EVEX | T_EW1 | T_N8, 0x16, imm); }"); puts("void vpextrq(const Operand& op, const Xmm& x, uint8_t imm) { if (!((op.isREG(64) || op.isMEM()) && x.isXMM())) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(x, 0, op, T_0F3A | T_66 | T_W1 | T_EVEX | T_EW1 | T_N8, 0x16, imm); }");
puts("void vpinsrb(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { if (!(x1.isXMM() && x2.isXMM() && (op.isREG(32) || op.isMEM()))) throw Error(ERR_BAD_COMBINATION); opVex(x1, &x2, op, T_0F3A | T_66 | T_EVEX | T_N1, 0x20, imm); }"); puts("void vpinsrb(const Xmm& x1, const Xmm& x2, const Operand& op, uint8_t imm) { if (!(x1.isXMM() && x2.isXMM() && (op.isREG(32) || op.isMEM()))) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(x1, &x2, op, T_0F3A | T_66 | T_EVEX | T_N1, 0x20, imm); }");
puts("void vpinsrw(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { if (!(x1.isXMM() && x2.isXMM() && (op.isREG(32) || op.isMEM()))) throw Error(ERR_BAD_COMBINATION); opVex(x1, &x2, op, T_0F | T_66 | T_EVEX | T_N2, 0xC4, imm); }"); puts("void vpinsrw(const Xmm& x1, const Xmm& x2, const Operand& op, uint8_t imm) { if (!(x1.isXMM() && x2.isXMM() && (op.isREG(32) || op.isMEM()))) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(x1, &x2, op, T_0F | T_66 | T_EVEX | T_N2, 0xC4, imm); }");
puts("void vpinsrd(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { if (!(x1.isXMM() && x2.isXMM() && (op.isREG(32) || op.isMEM()))) throw Error(ERR_BAD_COMBINATION); opVex(x1, &x2, op, T_0F3A | T_66 | T_W0 | T_EVEX | T_EW0 | T_N4, 0x22, imm); }"); puts("void vpinsrd(const Xmm& x1, const Xmm& x2, const Operand& op, uint8_t imm) { if (!(x1.isXMM() && x2.isXMM() && (op.isREG(32) || op.isMEM()))) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(x1, &x2, op, T_0F3A | T_66 | T_W0 | T_EVEX | T_EW0 | T_N4, 0x22, imm); }");
puts("void vpinsrq(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { if (!(x1.isXMM() && x2.isXMM() && (op.isREG(64) || op.isMEM()))) throw Error(ERR_BAD_COMBINATION); opVex(x1, &x2, op, T_0F3A | T_66 | T_W1 | T_EVEX | T_EW1 | T_N8, 0x22, imm); }"); puts("void vpinsrq(const Xmm& x1, const Xmm& x2, const Operand& op, uint8_t imm) { if (!(x1.isXMM() && x2.isXMM() && (op.isREG(64) || op.isMEM()))) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(x1, &x2, op, T_0F3A | T_66 | T_W1 | T_EVEX | T_EW1 | T_N8, 0x22, imm); }");
puts("void vpmovmskb(const Reg32e& r, const Xmm& x) { if (!x.is(Operand::XMM | Operand::YMM)) throw Error(ERR_BAD_COMBINATION); opVex(x.isYMM() ? Ymm(r.getIdx()) : Xmm(r.getIdx()), 0, x, T_0F | T_66 | T_YMM, 0xD7); }"); puts("void vpmovmskb(const Reg32e& r, const Xmm& x) { if (!x.is(Operand::XMM | Operand::YMM)) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(x.isYMM() ? Ymm(r.getIdx()) : Xmm(r.getIdx()), 0, x, T_0F | T_66 | T_YMM, 0xD7); }");
} }
// (x, x, imm), (x, imm) // (x, x, imm), (x, imm)
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
int idx; int idx;
int type; int type;
} tbl[] = { } tbl[] = {
@ -1505,14 +1579,14 @@ void put()
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const Tbl& p = tbl[i]; const Tbl& p = tbl[i];
std::string type = type2String(p.type); std::string type = type2String(p.type);
printf("void v%s(const Xmm& x, const Operand& op, uint8 imm) { opAVX_X_X_XM(Xmm(x.getKind(), %d), x, op, %s, 0x%02X, imm); }\n", p.name, p.idx, type.c_str(), p.code); printf("void v%s(const Xmm& x, const Operand& op, uint8_t imm) { opAVX_X_X_XM(Xmm(x.getKind(), %d), x, op, %s, 0x%02X, imm); }\n", p.name, p.idx, type.c_str(), p.code);
} }
} }
// 4-op // 4-op
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
} tbl[] = { } tbl[] = {
{ "vblendvpd", 0x4B }, { "vblendvpd", 0x4B },
{ "vblendvps", 0x4A }, { "vblendvps", 0x4A },
@ -1525,18 +1599,18 @@ void put()
} }
// mov // mov
{ {
printf("void vmovd(const Xmm& x, const Operand& op) { if (!op.isREG(32) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x, xm0, op, T_0F | T_66 | T_W0 | T_EVEX | T_N4, 0x6E); }\n"); printf("void vmovd(const Xmm& x, const Operand& op) { if (!op.isREG(32) && !op.isMEM()) XBYAK_THROW(ERR_BAD_COMBINATION) opAVX_X_X_XM(x, xm0, op, T_0F | T_66 | T_W0 | T_EVEX | T_N4, 0x6E); }\n");
printf("void vmovd(const Operand& op, const Xmm& x) { if (!op.isREG(32) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x, xm0, op, T_0F | T_66 | T_W0 | T_EVEX | T_N4, 0x7E); }\n"); printf("void vmovd(const Operand& op, const Xmm& x) { if (!op.isREG(32) && !op.isMEM()) XBYAK_THROW(ERR_BAD_COMBINATION) opAVX_X_X_XM(x, xm0, op, T_0F | T_66 | T_W0 | T_EVEX | T_N4, 0x7E); }\n");
printf("void vmovq(const Xmm& x, const Address& addr) { int type, code; if (x.getIdx() < 16) { type = T_0F | T_F3; code = 0x7E; } else { type = T_0F | T_66 | T_EVEX | T_EW1 | T_N8; code = 0x6E; } opAVX_X_X_XM(x, xm0, addr, type, code); }\n"); printf("void vmovq(const Xmm& x, const Address& addr) { int type, code; if (x.getIdx() < 16) { type = T_0F | T_F3; code = 0x7E; } else { type = T_0F | T_66 | T_EVEX | T_EW1 | T_N8; code = 0x6E; } opAVX_X_X_XM(x, xm0, addr, type, code); }\n");
printf("void vmovq(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, T_0F | T_66 | T_EVEX | T_EW1 | T_N8, x.getIdx() < 16 ? 0xD6 : 0x7E); }\n"); printf("void vmovq(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, T_0F | T_66 | T_EVEX | T_EW1 | T_N8, x.getIdx() < 16 ? 0xD6 : 0x7E); }\n");
printf("void vmovq(const Xmm& x1, const Xmm& x2) { opAVX_X_X_XM(x1, xm0, x2, T_0F | T_F3 | T_EVEX | T_EW1 | T_N8, 0x7E); }\n"); printf("void vmovq(const Xmm& x1, const Xmm& x2) { opAVX_X_X_XM(x1, xm0, x2, T_0F | T_F3 | T_EVEX | T_EW1 | T_N8, 0x7E); }\n");
printf("void vmovhlps(const Xmm& x1, const Xmm& x2, const Operand& op = Operand()) { if (!op.isNone() && !op.isXMM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x1, x2, op, T_0F | T_EVEX | T_EW0, 0x12); }\n"); printf("void vmovhlps(const Xmm& x1, const Xmm& x2, const Operand& op = Operand()) { if (!op.isNone() && !op.isXMM()) XBYAK_THROW(ERR_BAD_COMBINATION) opAVX_X_X_XM(x1, x2, op, T_0F | T_EVEX | T_EW0, 0x12); }\n");
printf("void vmovlhps(const Xmm& x1, const Xmm& x2, const Operand& op = Operand()) { if (!op.isNone() && !op.isXMM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x1, x2, op, T_0F | T_EVEX | T_EW0, 0x16); }\n"); printf("void vmovlhps(const Xmm& x1, const Xmm& x2, const Operand& op = Operand()) { if (!op.isNone() && !op.isXMM()) XBYAK_THROW(ERR_BAD_COMBINATION) opAVX_X_X_XM(x1, x2, op, T_0F | T_EVEX | T_EW0, 0x16); }\n");
printf("void vmovmskpd(const Reg& r, const Xmm& x) { if (!r.isBit(i32e)) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x.isXMM() ? Xmm(r.getIdx()) : Ymm(r.getIdx()), cvtIdx0(x), x, T_0F | T_66 | T_W0 | T_YMM, 0x50); }\n"); printf("void vmovmskpd(const Reg& r, const Xmm& x) { if (!r.isBit(i32e)) XBYAK_THROW(ERR_BAD_COMBINATION) opAVX_X_X_XM(x.isXMM() ? Xmm(r.getIdx()) : Ymm(r.getIdx()), cvtIdx0(x), x, T_0F | T_66 | T_W0 | T_YMM, 0x50); }\n");
printf("void vmovmskps(const Reg& r, const Xmm& x) { if (!r.isBit(i32e)) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x.isXMM() ? Xmm(r.getIdx()) : Ymm(r.getIdx()), cvtIdx0(x), x, T_0F | T_W0 | T_YMM, 0x50); }\n"); printf("void vmovmskps(const Reg& r, const Xmm& x) { if (!r.isBit(i32e)) XBYAK_THROW(ERR_BAD_COMBINATION) opAVX_X_X_XM(x.isXMM() ? Xmm(r.getIdx()) : Ymm(r.getIdx()), cvtIdx0(x), x, T_0F | T_W0 | T_YMM, 0x50); }\n");
puts("void vmovntdq(const Address& addr, const Xmm& x) { opVex(x, 0, addr, T_0F | T_66 | T_YMM | T_EVEX | T_EW0, 0xE7); }"); puts("void vmovntdq(const Address& addr, const Xmm& x) { opVex(x, 0, addr, T_0F | T_66 | T_YMM | T_EVEX | T_EW0, 0xE7); }");
puts("void vmovntpd(const Address& addr, const Xmm& x) { opVex(x, 0, addr, T_0F | T_66 | T_YMM | T_EVEX | T_EW1, 0x2B); }"); puts("void vmovntpd(const Address& addr, const Xmm& x) { opVex(x, 0, addr, T_0F | T_66 | T_YMM | T_EVEX | T_EW1, 0x2B); }");
@ -1549,7 +1623,7 @@ void put()
int type = T_0F | T_EVEX; int type = T_0F | T_EVEX;
type |= i == 0 ? (T_F2 | T_EW1 | T_N8) : (T_F3 | T_EW0 | T_N4); type |= i == 0 ? (T_F2 | T_EW1 | T_N8) : (T_F3 | T_EW0 | T_N4);
std::string s = type2String(type); std::string s = type2String(type);
printf("void vmovs%c(const Xmm& x1, const Xmm& x2, const Operand& op = Operand()) { if (!op.isNone() && !op.isXMM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x1, x2, op, %s, 0x10); }\n", c1, s.c_str()); printf("void vmovs%c(const Xmm& x1, const Xmm& x2, const Operand& op = Operand()) { if (!op.isNone() && !op.isXMM()) XBYAK_THROW(ERR_BAD_COMBINATION) opAVX_X_X_XM(x1, x2, op, %s, 0x10); }\n", c1, s.c_str());
printf("void vmovs%c(const Xmm& x, const Address& addr) { opAVX_X_X_XM(x, xm0, addr, %s, 0x10); }\n", c1, s.c_str()); printf("void vmovs%c(const Xmm& x, const Address& addr) { opAVX_X_X_XM(x, xm0, addr, %s, 0x10); }\n", c1, s.c_str());
printf("void vmovs%c(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, %s | T_M_K, 0x11); }\n", c1, s.c_str()); printf("void vmovs%c(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, %s | T_M_K, 0x11); }\n", c1, s.c_str());
} }
@ -1574,7 +1648,7 @@ void put()
puts("void vcvttpd2dq(const Xmm& x, const Operand& op) { opCvt2(x, op, T_66 | T_0F | T_YMM | T_EVEX |T_EW1 | T_B64 | T_ER_Z, 0xE6); }"); puts("void vcvttpd2dq(const Xmm& x, const Operand& op) { opCvt2(x, op, T_66 | T_0F | T_YMM | T_EVEX |T_EW1 | T_B64 | T_ER_Z, 0xE6); }");
puts("void vcvtph2ps(const Xmm& x, const Operand& op) { checkCvt1(x, op); opVex(x, 0, op, T_0F38 | T_66 | T_W0 | T_EVEX | T_EW0 | T_N8 | T_N_VL | T_SAE_Y, 0x13); }"); puts("void vcvtph2ps(const Xmm& x, const Operand& op) { checkCvt1(x, op); opVex(x, 0, op, T_0F38 | T_66 | T_W0 | T_EVEX | T_EW0 | T_N8 | T_N_VL | T_SAE_Y, 0x13); }");
puts("void vcvtps2ph(const Operand& op, const Xmm& x, uint8 imm) { checkCvt1(x, op); opVex(x, 0, op, T_0F3A | T_66 | T_W0 | T_EVEX | T_EW0 | T_N8 | T_N_VL | T_SAE_Y, 0x1D, imm); }"); puts("void vcvtps2ph(const Operand& op, const Xmm& x, uint8_t imm) { checkCvt1(x, op); opVex(x, 0, op, T_0F3A | T_66 | T_W0 | T_EVEX | T_EW0 | T_N8 | T_N_VL | T_SAE_Y, 0x1D, imm); }");
} }
// haswell gpr(reg, reg, r/m) // haswell gpr(reg, reg, r/m)
@ -1582,7 +1656,7 @@ void put()
const struct Tbl { const struct Tbl {
const char *name; const char *name;
int type; int type;
uint8 code; uint8_t code;
} tbl[] = { } tbl[] = {
{ "andn", T_0F38, 0xF2 }, { "andn", T_0F38, 0xF2 },
{ "mulx", T_F2 | T_0F38, 0xF6 }, { "mulx", T_F2 | T_0F38, 0xF6 },
@ -1599,7 +1673,7 @@ void put()
const struct Tbl { const struct Tbl {
const char *name; const char *name;
int type; int type;
uint8 code; uint8_t code;
} tbl[] = { } tbl[] = {
{ "bextr", T_0F38, 0xF7 }, { "bextr", T_0F38, 0xF7 },
{ "bzhi", T_0F38, 0xF5 }, { "bzhi", T_0F38, 0xF5 },
@ -1611,15 +1685,15 @@ void put()
const Tbl& p = tbl[i]; const Tbl& p = tbl[i];
printf("void %s(const Reg32e& r1, const Operand& op, const Reg32e& r2) { opGpr(r1, op, r2, %s, 0x%x, false); }\n", p.name, type2String(p.type).c_str(), p.code); printf("void %s(const Reg32e& r1, const Operand& op, const Reg32e& r2) { opGpr(r1, op, r2, %s, 0x%x, false); }\n", p.name, type2String(p.type).c_str(), p.code);
} }
puts("void rorx(const Reg32e& r, const Operand& op, uint8 imm) { opGpr(r, op, Reg32e(0, r.getBit()), T_0F3A | T_F2, 0xF0, false, imm); }"); puts("void rorx(const Reg32e& r, const Operand& op, uint8_t imm) { opGpr(r, op, Reg32e(0, r.getBit()), T_0F3A | T_F2, 0xF0, false, imm); }");
} }
// gpr(reg, r/m) // gpr(reg, r/m)
{ {
const struct Tbl { const struct Tbl {
const char *name; const char *name;
int type; int type;
uint8 code; uint8_t code;
uint8 idx; uint8_t idx;
} tbl[] = { } tbl[] = {
{ "blsi", T_0F38, 0xF3, 3 }, { "blsi", T_0F38, 0xF3, 3 },
{ "blsmsk", T_0F38, 0xF3, 2 }, { "blsmsk", T_0F38, 0xF3, 2 },
@ -1637,7 +1711,7 @@ void put()
const int x_vy_x = 2; const int x_vy_x = 2;
const struct Tbl { const struct Tbl {
const char *name; const char *name;
uint8 code; uint8_t code;
int w; int w;
int mode; int mode;
} tbl[] = { } tbl[] = {
@ -1669,6 +1743,7 @@ void put32()
{ "aas", 0x3F }, { "aas", 0x3F },
{ "daa", 0x27 }, { "daa", 0x27 },
{ "das", 0x2F }, { "das", 0x2F },
{ "into", 0xCE },
{ "popad", 0x61 }, { "popad", 0x61 },
{ "popfd", 0x9D }, { "popfd", 0x9D },
{ "pusha", 0x60 }, { "pusha", 0x60 },
@ -1677,6 +1752,8 @@ void put32()
{ "popa", 0x61 }, { "popa", 0x61 },
}; };
putGeneric(tbl, NUM_OF_ARRAY(tbl)); putGeneric(tbl, NUM_OF_ARRAY(tbl));
putLoadSeg("lds", 0xC5, NONE);
putLoadSeg("les", 0xC4, NONE);
} }
void put64() void put64()
@ -1688,18 +1765,24 @@ void put64()
{ "cdqe", 0x48, 0x98 }, { "cdqe", 0x48, 0x98 },
{ "cqo", 0x48, 0x99 }, { "cqo", 0x48, 0x99 },
{ "cmpsq", 0x48, 0xA7 }, { "cmpsq", 0x48, 0xA7 },
{ "popfq", 0x9D },
{ "pushfq", 0x9C },
{ "lodsq", 0x48, 0xAD },
{ "movsq", 0x48, 0xA5 }, { "movsq", 0x48, 0xA5 },
{ "scasq", 0x48, 0xAF }, { "scasq", 0x48, 0xAF },
{ "stosq", 0x48, 0xAB }, { "stosq", 0x48, 0xAB },
{ "syscall", 0x0F, 0x05 },
{ "sysret", 0x0F, 0x07 },
}; };
putGeneric(tbl, NUM_OF_ARRAY(tbl)); putGeneric(tbl, NUM_OF_ARRAY(tbl));
puts("void cmpxchg16b(const Address& addr) { opModM(addr, Reg64(1), 0x0F, 0xC7); }"); putMemOp("cmpxchg16b", 0, 1, 0x0F, 0xC7, 64);
putMemOp("fxrstor64", 0, 1, 0x0F, 0xAE, 64);
puts("void movq(const Reg64& reg, const Mmx& mmx) { if (mmx.isXMM()) db(0x66); opModR(mmx, reg, 0x0F, 0x7E); }"); puts("void movq(const Reg64& reg, const Mmx& mmx) { if (mmx.isXMM()) db(0x66); opModR(mmx, reg, 0x0F, 0x7E); }");
puts("void movq(const Mmx& mmx, const Reg64& reg) { if (mmx.isXMM()) db(0x66); opModR(mmx, reg, 0x0F, 0x6E); }"); puts("void movq(const Mmx& mmx, const Reg64& reg) { if (mmx.isXMM()) db(0x66); opModR(mmx, reg, 0x0F, 0x6E); }");
puts("void movsxd(const Reg64& reg, const Operand& op) { if (!op.isBit(32)) throw Error(ERR_BAD_COMBINATION); opModRM(reg, op, op.isREG(), op.isMEM(), 0x63); }"); puts("void movsxd(const Reg64& reg, const Operand& op) { if (!op.isBit(32)) XBYAK_THROW(ERR_BAD_COMBINATION) opModRM(reg, op, op.isREG(), op.isMEM(), 0x63); }");
puts("void pextrq(const Operand& op, const Xmm& xmm, uint8 imm) { if (!op.isREG(64) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opGen(Reg64(xmm.getIdx()), op, 0x16, 0x66, 0, imm, 0x3A); }"); puts("void pextrq(const Operand& op, const Xmm& xmm, uint8_t imm) { if (!op.isREG(64) && !op.isMEM()) XBYAK_THROW(ERR_BAD_COMBINATION) opGen(Reg64(xmm.getIdx()), op, 0x16, 0x66, 0, imm, 0x3A); }");
puts("void pinsrq(const Xmm& xmm, const Operand& op, uint8 imm) { if (!op.isREG(64) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opGen(Reg64(xmm.getIdx()), op, 0x22, 0x66, 0, imm, 0x3A); }"); puts("void pinsrq(const Xmm& xmm, const Operand& op, uint8_t imm) { if (!op.isREG(64) && !op.isMEM()) XBYAK_THROW(ERR_BAD_COMBINATION) opGen(Reg64(xmm.getIdx()), op, 0x22, 0x66, 0, imm, 0x3A); }");
puts("void vcvtss2si(const Reg64& r, const Operand& op) { opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, T_0F | T_F3 | T_W1 | T_EVEX | T_EW1 | T_ER_X | T_N8, 0x2D); }"); puts("void vcvtss2si(const Reg64& r, const Operand& op) { opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, T_0F | T_F3 | T_W1 | T_EVEX | T_EW1 | T_ER_X | T_N8, 0x2D); }");
puts("void vcvttss2si(const Reg64& r, const Operand& op) { opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, T_0F | T_F3 | T_W1 | T_EVEX | T_EW1 | T_SAE_X | T_N8, 0x2C); }"); puts("void vcvttss2si(const Reg64& r, const Operand& op) { opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, T_0F | T_F3 | T_W1 | T_EVEX | T_EW1 | T_SAE_X | T_N8, 0x2C); }");
@ -1710,10 +1793,35 @@ void put64()
puts("void vmovq(const Reg64& r, const Xmm& x) { opAVX_X_X_XM(x, xm0, Xmm(r.getIdx()), T_66 | T_0F | T_W1 | T_EVEX | T_EW1, 0x7E); }"); puts("void vmovq(const Reg64& r, const Xmm& x) { opAVX_X_X_XM(x, xm0, Xmm(r.getIdx()), T_66 | T_0F | T_W1 | T_EVEX | T_EW1, 0x7E); }");
} }
void putAMX_TILE()
{
puts("void ldtilecfg(const Address& addr) { opVex(tmm0, &tmm0, addr, T_0F38 | T_W0, 0x49); }");
puts("void sttilecfg(const Address& addr) { opVex(tmm0, &tmm0, addr, T_66 | T_0F38 | T_W0, 0x49); }");
puts("void tileloadd(const Tmm& tm, const Address& addr) { opAMX(tm, addr, T_F2 | T_0F38 | T_W0, 0x4b); }");
puts("void tileloaddt1(const Tmm& tm, const Address& addr) { opAMX(tm, addr, T_66 | T_0F38 | T_W0, 0x4b); }");
puts("void tilerelease() { db(0xc4); db(0xe2); db(0x78); db(0x49); db(0xc0); }");
puts("void tilestored(const Address& addr, const Tmm& tm) { opVex(tm, &tmm0, addr, T_F3 | T_0F38 | T_W0, 0x4b); }");
puts("void tilezero(const Tmm& Tmm) { opVex(Tmm, &tmm0, tmm0, T_F2 | T_0F38 | T_W0, 0x49); }");
}
void putAMX_INT8()
{
puts("void tdpbssd(const Tmm& x1, const Tmm& x2, const Tmm& x3) { opVex(x1, &x3, x2, T_F2 | T_0F38 | T_W0, 0x5e); }");
puts("void tdpbsud(const Tmm& x1, const Tmm& x2, const Tmm& x3) { opVex(x1, &x3, x2, T_F3 | T_0F38 | T_W0, 0x5e); }");
puts("void tdpbusd(const Tmm& x1, const Tmm& x2, const Tmm& x3) { opVex(x1, &x3, x2, T_66 | T_0F38 | T_W0, 0x5e); }");
puts("void tdpbuud(const Tmm& x1, const Tmm& x2, const Tmm& x3) { opVex(x1, &x3, x2, T_0F38 | T_W0, 0x5e); }");
}
void putAMX_BF16()
{
puts("void tdpbf16ps(const Tmm& x1, const Tmm& x2, const Tmm& x3) { opVex(x1, &x3, x2, T_F3 | T_0F38 | T_W0, 0x5c); }");
}
void putFixed() void putFixed()
{ {
puts("#ifdef XBYAK64"); puts("#ifdef XBYAK64");
put64(); put64();
putAMX_TILE();
putAMX_INT8();
putAMX_BF16();
puts("#else"); puts("#else");
put32(); put32();
puts("#endif"); puts("#endif");
@ -1724,7 +1832,7 @@ void putFixed()
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const char *name = tbl[i]; const char *name = tbl[i];
printf("void %s(const Operand& op1, const Operand& op2) { %s_(op1, op2); }\n", name, name); printf("void %s(const Operand& op1, const Operand& op2) { %s_(op1, op2); }\n", name, name);
printf("void %s(const Operand& op, uint32 imm) { %s_(op, imm); }\n", name, name); printf("void %s(const Operand& op, uint32_t imm) { %s_(op, imm); }\n", name, name);
} }
puts("void not(const Operand& op) { not_(op); }"); puts("void not(const Operand& op) { not_(op); }");
puts("#endif"); puts("#endif");
@ -1732,10 +1840,10 @@ void putFixed()
void putOmit() void putOmit()
{ {
puts("void vpinsrb(const Xmm& x, const Operand& op, uint8 imm) { vpinsrb(x, x, op, imm); }"); puts("void vpinsrb(const Xmm& x, const Operand& op, uint8_t imm) { vpinsrb(x, x, op, imm); }");
puts("void vpinsrd(const Xmm& x, const Operand& op, uint8 imm) { vpinsrd(x, x, op, imm); }"); puts("void vpinsrd(const Xmm& x, const Operand& op, uint8_t imm) { vpinsrd(x, x, op, imm); }");
puts("void vpinsrq(const Xmm& x, const Operand& op, uint8 imm) { vpinsrq(x, x, op, imm); }"); puts("void vpinsrq(const Xmm& x, const Operand& op, uint8_t imm) { vpinsrq(x, x, op, imm); }");
puts("void vpinsrw(const Xmm& x, const Operand& op, uint8 imm) { vpinsrw(x, x, op, imm); }"); puts("void vpinsrw(const Xmm& x, const Operand& op, uint8_t imm) { vpinsrw(x, x, op, imm); }");
puts("void vcvtsi2sd(const Xmm& x, const Operand& op) { vcvtsi2sd(x, x, op); }"); puts("void vcvtsi2sd(const Xmm& x, const Operand& op) { vcvtsi2sd(x, x, op); }");
puts("void vcvtsi2ss(const Xmm& x, const Operand& op) { vcvtsi2ss(x, x, op); }"); puts("void vcvtsi2ss(const Xmm& x, const Operand& op) { vcvtsi2ss(x, x, op); }");
@ -1769,7 +1877,7 @@ void putOmit()
}; };
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const char *name = tbl[i]; const char *name = tbl[i];
printf("void v%s(const Xmm& x, uint8 imm) { v%s(x, x, imm); }\n", name, name); printf("void v%s(const Xmm& x, uint8_t imm) { v%s(x, x, imm); }\n", name, name);
} }
} }
{ {

View file

@ -1,22 +1,25 @@
[![Build Status](https://travis-ci.org/herumi/xbyak.png)](https://travis-ci.org/herumi/xbyak)
# Xbyak 5.78 ; JIT assembler for x86(IA32), x64(AMD64, x86-64) by C++ # Xbyak 5.97 ; JIT assembler for x86(IA32), x64(AMD64, x86-64) by C++
## Abstract ## Abstract
This is a header file which enables dynamically to assemble x86(IA32), x64(AMD64, x86-64) mnemonic. Xbyak is a C++ header library that enables dynamically to assemble x86(IA32), x64(AMD64, x86-64) mnemonic.
## Feature ## Feature
* header file only * header file only
* Intel/MASM like syntax * Intel/MASM like syntax
* fully support AVX-512 * fully support AVX-512
**Note**: Xbyak uses and(), or(), xor(), not() functions, so `-fno-operator-names` option is necessary for gcc/clang. **Note**:
Use `and_()`, `or_()`, ... instead of `and()`, `or()`.
If you want to use them, then specify `-fno-operator-names` option to gcc/clang.
Or define `XBYAK_NO_OP_NAMES` before including `xbyak.h` and use and_(), or_(), xor_(), not_() instead of them. ### News
- (break backward compatibility) `push(byte, imm)` (resp. `push(word, imm)`) forces to cast `imm` to 8(resp. 16) bit.
and_(), or_(), xor_(), not_() are always available. - (Windows) `#include <winsock2.h>` has been removed from xbyak.h, so add it explicitly if you need it.
- support exception-less mode see. [Exception-less mode](#exception-less-mode)
`XBYAK_NO_OP_NAMES` will be defined in the feature version. - `XBYAK_USE_MMAP_ALLOCATOR` will be defined on Linux/macOS unless `XBYAK_DONT_USE_MMAP_ALLOCATOR` is defined.
### Supported OS ### Supported OS
@ -47,7 +50,6 @@ These files are copied into `/usr/local/include/xbyak`.
Inherit `Xbyak::CodeGenerator` class and make the class method. Inherit `Xbyak::CodeGenerator` class and make the class method.
``` ```
#define XBYAK_NO_OP_NAMES
#include <xbyak/xbyak.h> #include <xbyak/xbyak.h>
struct Code : Xbyak::CodeGenerator { struct Code : Xbyak::CodeGenerator {
@ -58,6 +60,15 @@ struct Code : Xbyak::CodeGenerator {
} }
}; };
``` ```
Or you can pass the instance of CodeGenerator without inheriting.
```
void genCode(Xbyak::CodeGenerator& code, int x) {
using namespace Xbyak::util;
code.mov(eax, x);
code.ret();
}
```
Make an instance of the class and get the function Make an instance of the class and get the function
pointer by calling `getCode()` and call it. pointer by calling `getCode()` and call it.
``` ```
@ -146,6 +157,8 @@ vfpclassps k5{k3}, [rax+64]{1to4}, 5 --> vfpclassps(k5|k3, yword_b [rax+64],
``` ```
### Remark ### Remark
* `k1`, ..., `k7` are opmask registers. * `k1`, ..., `k7` are opmask registers.
- `k0` is dealt as no mask.
- e.g. `vmovaps(zmm0|k0, ptr[rax]);` and `vmovaps(zmm0|T_z, ptr[rax]);` are same to `vmovaps(zmm0, ptr[rax]);`.
* use `| T_z`, `| T_sae`, `| T_rn_sae`, `| T_rd_sae`, `| T_ru_sae`, `| T_rz_sae` instead of `,{z}`, `,{sae}`, `,{rn-sae}`, `,{rd-sae}`, `,{ru-sae}`, `,{rz-sae}` respectively. * use `| T_z`, `| T_sae`, `| T_rn_sae`, `| T_rd_sae`, `| T_ru_sae`, `| T_rz_sae` instead of `,{z}`, `,{sae}`, `,{rn-sae}`, `,{rd-sae}`, `,{ru-sae}`, `,{rz-sae}` respectively.
* `k4 | k3` is different from `k3 | k4`. * `k4 | k3` is different from `k3 | k4`.
* use `ptr_b` for broadcast `{1toX}`. X is automatically determined. * use `ptr_b` for broadcast `{1toX}`. X is automatically determined.
@ -212,6 +225,32 @@ void func1()
} }
``` ```
### short and long jump
Xbyak deals with jump mnemonics of an undefined label as short jump if no type is specified.
So if the size between jmp and label is larger than 127 byte, then xbyak will cause an error.
```
jmp("short-jmp"); // short jmp
// small code
L("short-jmp");
jmp("long-jmp");
// long code
L("long-jmp"); // throw exception
```
Then specify T_NEAR for jmp.
```
jmp("long-jmp", T_NEAR); // long jmp
// long code
L("long-jmp");
```
Or call `setDefaultJmpNEAR(true);` once, then the default type is set to T_NEAR.
```
jmp("long-jmp"); // long jmp
// long code
L("long-jmp");
```
### Label class ### Label class
`L()` and `jxx()` support Label class. `L()` and `jxx()` support Label class.
@ -369,15 +408,22 @@ c.setProtectModeRE();
Call `readyRE()` instead of `ready()` when using `AutoGrow` mode. Call `readyRE()` instead of `ready()` when using `AutoGrow` mode.
See [protect-re.cpp](sample/protect-re.cpp). See [protect-re.cpp](sample/protect-re.cpp).
## Exception-less mode
If `XBYAK_NO_EXCEPTION` is defined, then gcc/clang can compile xbyak with `-fno-exceptions`.
In stead of throwing an exception, `Xbyak::GetError()` returns non-zero value (e.g. `ERR_BAD_ADDRESSING`) if there is something wrong.
The status will not be changed automatically, then you should reset it by `Xbyak::ClearError()`.
`CodeGenerator::reset()` calls `ClearError()`.
## Macro ## Macro
* **XBYAK32** is defined on 32bit. * **XBYAK32** is defined on 32bit.
* **XBYAK64** is defined on 64bit. * **XBYAK64** is defined on 64bit.
* **XBYAK64_WIN** is defined on 64bit Windows(VC) * **XBYAK64_WIN** is defined on 64bit Windows(VC).
* **XBYAK64_GCC** is defined on 64bit gcc, cygwin * **XBYAK64_GCC** is defined on 64bit gcc, cygwin.
* define **XBYAK_NO_OP_NAMES** on gcc without `-fno-operator-names` * define **XBYAK_USE_OP_NAMES** on gcc with `-fno-operator-names` if you want to use `and()`, ....
* define **XBYAK_ENABLE_OMITTED_OPERAND** if you use omitted destination such as `vaddps(xmm2, xmm3);`(deprecated in the future) * define **XBYAK_ENABLE_OMITTED_OPERAND** if you use omitted destination such as `vaddps(xmm2, xmm3);`(deprecated in the future).
* define **XBYAK_UNDEF_JNL** if Bessel function jnl is defined as macro * define **XBYAK_UNDEF_JNL** if Bessel function jnl is defined as macro.
* define **XBYAK_NO_EXCEPTION** for a compiler option `-fno-exceptions`.
## Sample ## Sample
@ -392,6 +438,31 @@ modified new BSD License
http://opensource.org/licenses/BSD-3-Clause http://opensource.org/licenses/BSD-3-Clause
## History ## History
* 2020/Sep/08 ver 5.97 replace uint32 with uint32_t etc.
* 2020/Aug/28 ver 5.95 some constructors of register classes support constexpr if C++14 or later
* 2020/Aug/04 ver 5.941 `CodeGenerator::reset()` calls `ClearError()`.
* 2020/Jul/28 ver 5.94 remove #include <winsock2.h> (only windows)
* 2020/Jul/21 ver 5.93 support exception-less mode
* 2020/Jun/30 ver 5.92 support Intel AMX instruction set (Thanks to nshustrov)
* 2020/Jun/22 ver 5.913 fix mov(r64, imm64) on 32-bit env with XBYAK64
* 2020/Jun/19 ver 5.912 define MAP_JIT on macOS regardless of Xcode version (Thanks to rsdubtso)
* 2020/May/10 ver 5.911 XBYAK_USE_MMAP_ALLOCATOR is defined unless XBYAK_DONT_USE_MMAP_ALLOCATOR is defined.
* 2020/Apr/20 ver 5.91 accept mask register k0 (it means no mask)
* 2020/Apr/09 ver 5.90 kmov{b,d,w,q} throws exception for an unsupported register
* 2020/Feb/26 ver 5.891 fix typo of type
* 2020/Jan/03 ver 5.89 fix error of vfpclasspd
* 2019/Dec/20 ver 5.88 fix compile error on Windows
* 2019/Dec/19 ver 5.87 add setDefaultJmpNEAR(), which deals with `jmp` of an undefined label as T_NEAR if no type is specified.
* 2019/Dec/13 ver 5.86 [changed] revert to the behavior before v5.84 if -fno-operator-names is defined (and() is available)
* 2019/Dec/07 ver 5.85 append MAP_JIT flag to mmap for macOS mojave or later
* 2019/Nov/29 ver 5.84 [changed] XBYAK_NO_OP_NAMES is defined unless XBYAK_USE_OP_NAMES is defined
* 2019/Oct/12 ver 5.83 exit(1) was removed
* 2019/Sep/23 ver 5.82 support monitorx, mwaitx, clzero (thanks to @MagurosanTeam)
* 2019/Sep/14 ver 5.81 support some generic mnemonics.
* 2019/Aug/01 ver 5.802 fix detection of AVX512_BF16 (thanks to vpirogov)
* 2019/May/27 support vp2intersectd, vp2intersectq (not tested)
* 2019/May/26 ver 5.80 support vcvtne2ps2bf16, vcvtneps2bf16, vdpbf16ps
* 2019/Apr/27 ver 5.79 vcmppd/vcmpps supports ptr_b(thanks to jkopinsky)
* 2019/Apr/15 ver 5.78 rewrite Reg::changeBit() (thanks to MerryMage) * 2019/Apr/15 ver 5.78 rewrite Reg::changeBit() (thanks to MerryMage)
* 2019/Mar/06 ver 5.77 fix number of cores that share LLC cache by densamoilov * 2019/Mar/06 ver 5.77 fix number of cores that share LLC cache by densamoilov
* 2019/Jan/17 ver 5.76 add Cpu::getNumCores() by shelleygoel * 2019/Jan/17 ver 5.76 add Cpu::getNumCores() by shelleygoel
@ -530,3 +601,5 @@ http://opensource.org/licenses/BSD-3-Clause
## Author ## Author
MITSUNARI Shigeo(herumi@nifty.com) MITSUNARI Shigeo(herumi@nifty.com)
## Sponsors welcome
[GitHub Sponsor](https://github.com/sponsors/herumi)

View file

@ -1,5 +1,5 @@
C++用x86(IA-32), x64(AMD64, x86-64) JITアセンブラ Xbyak 5.78 C++用x86(IA-32), x64(AMD64, x86-64) JITアセンブラ Xbyak 5.97
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
◎概要 ◎概要
@ -22,21 +22,21 @@
Intel Mac Intel Mac
などで動作確認をしています。 などで動作確認をしています。
※ Xbyakはデフォルトでand(), or(), xor(), not()関数を使います。 ※ and, orなどの代わりにand_, or_を使用してください。
gccではそれらを演算子として解釈してしまうため、-fno-operator-namesオプションを追加してコンパイルしてください。 and, orなどを使いたい場合は-fno-operator-namesをgcc/clangに指定してください。
あるいはXBYAK_NO_OP_NAMESを定義してand_(), or_(), xor_(), not_()を使ってください。
and_(), or_(), xor_(), not_()はXBYAK_NO_OP_NAMESされていないときでも使えます。
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
◎準備 ◎準備
xbyak.h xbyak.h
xbyak_bin2hex.h xbyak_bin2hex.h
xbyak_mnemonic.h
これらを同一のパスに入れてインクルードパスに追加してください。 これらを同一のパスに入れてインクルードパスに追加してください。
Linuxではmake installで/usr/local/include/xbyakにコピーされます。 Linuxではmake installで/usr/local/include/xbyakにコピーされます。
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
◎下位互換性の破れ ◎下位互換性の破れ
* push byte, immまたはpush word, immが下位8bit, 16bitにキャストした値を使うように変更。
* (Windows) `<winsock2.h>`をincludeしなくなったので必要なら明示的にincludeしてください。
* XBYAK_USE_MMAP_ALLOCATORがデフォルトで有効になりました。従来の方式にする場合はXBYAK_DONT_USE_MMAP_ALLOCATORを定義してください。
* Xbyak::Errorの型をenumからclassに変更 * Xbyak::Errorの型をenumからclassに変更
** 従来のenumの値をとるにはintにキャストしてください。 ** 従来のenumの値をとるにはintにキャストしてください。
* (古い)Reg32eクラスを(新しい)Reg32eとRegExpに分ける。 * (古い)Reg32eクラスを(新しい)Reg32eとRegExpに分ける。
@ -46,6 +46,13 @@ Linuxではmake installで/usr/local/include/xbyakにコピーされます。
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
◎新機能 ◎新機能
例外なしモード追加
XBYAK_NO_EXCEPTIONを定義してコンパイルするとgcc/clangで-fno-exceptionsオプションでコンパイルできます。
エラーは例外の代わりに`Xbyak::GetError()`で通達されます。
この値が0でなければ何か問題が発生しています。
この値は自動的に変更されないので`Xbyak::ClearError()`でリセットしてください。
`CodeGenerator::reset()`は`ClearError()`を呼びます。
MmapAllocator追加 MmapAllocator追加
これはUnix系OSでのみの仕様です。XBYAK_USE_MMAP_ALLOCATORを使うと利用できます。 これはUnix系OSでのみの仕様です。XBYAK_USE_MMAP_ALLOCATORを使うと利用できます。
デフォルトのAllocatorはメモリ確保時にposix_memalignを使います。 デフォルトのAllocatorはメモリ確保時にposix_memalignを使います。
@ -54,7 +61,6 @@ map countの最大値は/proc/sys/vm/max_map_countに書かれています。
デフォルトでは3万個ほどのXbyak::CodeGeneratorインスタンスを生成するとエラーになります。 デフォルトでは3万個ほどのXbyak::CodeGeneratorインスタンスを生成するとエラーになります。
test/mprotect_test.cppで確認できます。 test/mprotect_test.cppで確認できます。
これを避けるためにはmmapを使うMmapAllocatorを使ってください。 これを避けるためにはmmapを使うMmapAllocatorを使ってください。
将来この挙動がデフォルトになるかもしれません。
AutoGrowモード追加 AutoGrowモード追加
@ -373,6 +379,31 @@ sample/{echo,hello}.bfは http://www.kmonos.net/alang/etc/brainfuck.php から
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
◎履歴 ◎履歴
2020/09/08 ver 5.97 uint32などをuint32_tに置換
2020/08/28 ver 5.95 レジスタクラスのコンストラクタがconstexprに対応(C++14以降)
2020/08/04 ver 5.941 `CodeGenerator::reset()`が`ClearError()`を呼ぶように変更
2020/07/28 ver 5.94 #include <winsock2.h>の削除 (only windows)
2020/07/21 ver 5.93 例外なしモード追加
2020/06/30 ver 5.92 Intel AMX命令サポート (Thanks to nshustrov)
2020/06/19 ver 5.913 32ビット環境でXBYAK64を定義したときのmov(r64, imm64)を修正
2020/06/19 ver 5.912 macOSの古いXcodeでもMAP_JITを有効にする(Thanks to rsdubtso)
2020/05/10 ver 5.911 Linux/macOSでXBYAK_USE_MMAP_ALLOCATORがデフォルト有効になる
2020/04/20 ver 5.91 マスクレジスタk0を受け入れる(マスクをしない)
2020/04/09 ver 5.90 kmov{b,w,d,q}がサポートされないレジスタを受けると例外を投げる
2020/02/26 ver 5.891 zm0のtype修正
2020/01/03 ver 5.89 vfpclasspdの処理エラー修正
2019/12/20 ver 5.88 Windowsでのコンパイルエラー修正
2019/12/19 ver 5.87 未定義ラベルへのjmp命令のデフォルト挙動をT_NEARにするsetDefaultJmpNEAR()を追加
2019/12/13 ver 5.86 [変更] -fno-operator-namesが指定されたときは5.84以前の挙動に戻す
2019/12/07 ver 5.85 mmapにMAP_JITフラグを追加(macOS mojave以上)
2019/11/29 ver 5.84 [変更] XBYAK_USE_OP_NAMESが定義されていない限りXBYAK_NO_OP_NAMESが定義されるように変更
2019/10/12 ver 5.83 exit(1)の除去
2019/09/23 ver 5.82 monitorx, mwaitx, clzero対応 (thanks to MagurosanTeam)
2019/09/14 ver 5.81 いくつかの一般命令をサポート
2019/08/01 ver 5.802 AVX512_BF16判定修正 (thanks to vpirogov)
2019/05/27 support vp2intersectd, vp2intersectq (not tested)
2019/05/26 ver 5.80 support vcvtne2ps2bf16, vcvtneps2bf16, vdpbf16ps
2019/04/27 ver 5.79 vcmppd/vcmppsのptr_b対応忘れ(thanks to jkopinsky)
2019/04/15 ver 5.78 Reg::changeBit()のリファクタリング(thanks to MerryMage) 2019/04/15 ver 5.78 Reg::changeBit()のリファクタリング(thanks to MerryMage)
2019/03/06 ver 5.77 LLCキャッシュを共有数CPU数の修整(by densamoilov) 2019/03/06 ver 5.77 LLCキャッシュを共有数CPU数の修整(by densamoilov)
2019/01/17 ver 5.76 Cpu::getNumCores()追加(by shelleygoel) 2019/01/17 ver 5.76 Cpu::getNumCores()追加(by shelleygoel)

View file

@ -1,10 +1,12 @@
TARGET = test quantize bf toyvm test_util memfunc static_buf jmp_table
XBYAK_INC=../xbyak/xbyak.h XBYAK_INC=../xbyak/xbyak.h
BOOST_EXIST=$(shell echo "\#include <boost/spirit/core.hpp>" | (gcc -E - 2>/dev/null) | grep "boost/spirit/core.hpp" >/dev/null && echo "1") BOOST_EXIST=$(shell echo "\#include <boost/spirit/core.hpp>" | (gcc -E - 2>/dev/null) | grep "boost/spirit/core.hpp" >/dev/null && echo "1")
UNAME_M=$(shell uname -m) UNAME_M=$(shell uname -m)
ONLY_64BIT=0
ifeq ($(shell uname -s),Darwin) ifeq ($(shell uname -s),Darwin)
ONLY_64BIT=1
OS=mac
ifeq ($(UNAME_M),x86_64) ifeq ($(UNAME_M),x86_64)
BIT=64 BIT=64
endif endif
@ -27,19 +29,27 @@ else
endif endif
ifeq ($(BIT),64) ifeq ($(BIT),64)
TARGET += test64 bf64 memfunc64 test_util64 static_buf64 jmp_table64 TARGET += test64 bf64 memfunc64 test_util64 jmp_table64
ifeq ($(BOOST_EXIST),1) ifeq ($(BOOST_EXIST),1)
TARGET += calc64 #calc2_64 TARGET += calc64 #calc2_64
endif endif
endif endif
ifneq ($(OS),mac)
TARGET += static_buf64
endif
ifneq ($(ONLY_64BIT),1)
TARGET += test quantize bf toyvm test_util memfunc static_buf jmp_table
ifeq ($(BOOST_EXIST),1) ifeq ($(BOOST_EXIST),1)
TARGET += calc #calc2 TARGET += calc #calc2
endif
endif endif
all: $(TARGET) all: $(TARGET)
CFLAGS_WARN=-Wall -Wextra -Wformat=2 -Wcast-qual -Wcast-align -Wwrite-strings -Wfloat-equal -Wpointer-arith -pedantic CFLAGS_WARN=-Wall -Wextra -Wformat=2 -Wcast-qual -Wcast-align -Wwrite-strings -Wfloat-equal -Wpointer-arith #-pedantic
CFLAGS=-g -O2 -fomit-frame-pointer -Wall -I../ $(CFLAGS_WARN) CFLAGS=-g -O2 -fomit-frame-pointer -Wall -I../ $(CFLAGS_WARN)
@ -85,9 +95,13 @@ jmp_table:
$(CXX) $(CFLAGS) jmp_table.cpp -o $@ -m32 $(CXX) $(CFLAGS) jmp_table.cpp -o $@ -m32
jmp_table64: jmp_table64:
$(CXX) $(CFLAGS) jmp_table.cpp -o $@ -m64 $(CXX) $(CFLAGS) jmp_table.cpp -o $@ -m64
profiler: profiler.cpp ../xbyak/xbyak_util.h
$(CXX) $(CFLAGS) profiler.cpp -o $@
profiler-vtune: profiler.cpp ../xbyak/xbyak_util.h
$(CXX) $(CFLAGS) profiler.cpp -o $@ -DXBYAK_USE_VTUNE -I /opt/intel/vtune_amplifier/include/ -L /opt/intel/vtune_amplifier/lib64 -ljitprofiling -ldl
clean: clean:
rm -rf *.o $(TARGET) *.exe rm -rf *.o $(TARGET) *.exe profiler profiler-vtune
test : test0.cpp $(XBYAK_INC) test : test0.cpp $(XBYAK_INC)
test64: test0.cpp $(XBYAK_INC) test64: test0.cpp $(XBYAK_INC)

View file

@ -148,7 +148,7 @@ public:
} }
}; };
void dump(const Xbyak::uint8 *code, size_t size) void dump(const uint8_t *code, size_t size)
{ {
puts("#include <stdio.h>\nstatic int stack[128 * 1024];"); puts("#include <stdio.h>\nstatic int stack[128 * 1024];");
#ifdef _MSC_VER #ifdef _MSC_VER

View file

@ -0,0 +1,228 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{654BD79B-59D3-4B10-BBAA-158BAB272828}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>15.0.27924.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Midl>
<TypeLibraryName>.\Release/bf.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\Release/bf.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Midl>
<TypeLibraryName>.\Debug/bf.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderOutputFile>.\Debug/bf.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug/bf.pdb</ProgramDatabaseFile>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
<TypeLibraryName>.\Release/bf.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\Release/bf.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
<TypeLibraryName>.\Debug/bf.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderOutputFile>.\Debug/bf.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug/bf.pdb</ProgramDatabaseFile>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="bf.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -155,9 +155,9 @@ struct Grammar : public boost::spirit::classic::grammar<Grammar> {
void put(const std::vector<double>& x) void put(const std::vector<double>& x)
{ {
printf("%f", x[0]); for (size_t i = 0, n = x.size(); i < n; i++) {
for (size_t i = 1, n = x.size(); i < n; i++) { if (i > 0) printf(", ");
printf(", %f", x[i]); printf("%f", x[i]);
} }
} }

View file

@ -0,0 +1,228 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{5FDDFAA6-B947-491D-A17E-BBD863846579}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>15.0.27924.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Midl>
<TypeLibraryName>.\Release/calc.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\Release/calc.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Midl>
<TypeLibraryName>.\Debug/calc.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderOutputFile>.\Debug/calc.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug/calc.pdb</ProgramDatabaseFile>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
<TypeLibraryName>.\Release/calc.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\Release/calc.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
<TypeLibraryName>.\Debug/calc.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderOutputFile>.\Debug/calc.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug/calc.pdb</ProgramDatabaseFile>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="calc.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -102,7 +102,7 @@ private:
MAX_CONST_NUM = 32 MAX_CONST_NUM = 32
}; };
MIE_ALIGN(16) double constTbl_[MAX_CONST_NUM]; MIE_ALIGN(16) double constTbl_[MAX_CONST_NUM];
Xbyak::uint64 negConst_; Xbyak::uint64_t negConst_;
size_t constTblPos_; size_t constTblPos_;
#ifdef XBYAK32 #ifdef XBYAK32
const Xbyak::Reg32& varTbl_; const Xbyak::Reg32& varTbl_;
@ -118,7 +118,7 @@ public:
64bit: x [rcx](win), xmm0(gcc), return xmm0 64bit: x [rcx](win), xmm0(gcc), return xmm0
*/ */
Jit() Jit()
: negConst_(Xbyak::uint64(1) << 63) : negConst_(Xbyak::uint64_t(1) << 63)
, constTblPos_(0) , constTblPos_(0)
#ifdef XBYAK32 #ifdef XBYAK32
, varTbl_(eax) , varTbl_(eax)

View file

@ -0,0 +1,90 @@
/*
How to profile JIT-code with perf or VTune
sudo perf record ./profiler 1
amplxe-cl -collect hotspots -result-dir r001hs -quiet ./profiler-vtune 2
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <xbyak/xbyak_util.h>
const int N = 3000000;
struct Code : public Xbyak::CodeGenerator {
Code()
{
mov(eax, N);
Xbyak::Label lp = L();
for (int i = 0; i < 10; i++) {
sub(eax, 1);
}
jg(lp);
mov(eax, 1);
ret();
}
};
struct Code2 : public Xbyak::CodeGenerator {
Code2()
{
mov(eax, N);
Xbyak::Label lp = L();
for (int i = 0; i < 10; i++) {
xorps(xm0, xm0);
}
sub(eax, 1);
jg(lp);
mov(eax, 1);
ret();
}
};
double s1(int n)
{
double r = 0;
for (int i = 0; i < n; i++) {
r += 1.0 / (i + 1);
}
return r;
}
double s2(int n)
{
double r = 0;
for (int i = 0; i < n; i++) {
r += 1.0 / (i * i + 1) + 2.0 / (i + 3);
}
return r;
}
int main(int argc, char *argv[])
{
int mode = argc == 1 ? 0 : atoi(argv[1]);
Code c;
Code2 c2;
int (*f)() = (int (*)())c.getCode();
int (*g)() = (int (*)())c2.getCode();
printf("f:%p, %d\n", (const void*)f, (int)c.getSize());
printf("g:%p, %d\n", (const void*)g, (int)c2.getSize());
Xbyak::util::Profiler prof;
printf("mode=%d\n", mode);
prof.init(mode);
prof.set("f", (const void*)f, c.getSize());
prof.set("g", (const void*)g, c2.getSize());
double sum = 0;
for (int i = 0; i < 20000; i++) {
sum += s1(i);
sum += s2(i);
}
printf("sum=%f\n", sum);
for (int i = 0; i < 2000; i++) {
sum += f();
}
printf("f=%f\n", sum);
for (int i = 0; i < 2000; i++) {
sum += g();
}
printf("g=%f\n", sum);
puts("end");
}

View file

@ -5,12 +5,12 @@
This program generates a quantization routine by using fast division algorithm in run-time. This program generates a quantization routine by using fast division algorithm in run-time.
time(sec) time(sec)
quality 1(low) 10 50 100(high) quality 1(high) 10 50 100(low)
VC2005 8.0 8.0 8.0 8.0 VC2005 8.0 8.0 8.0 8.0
Xbyak 1.6 0.8 0.5 0.5 Xbyak 1.6 0.8 0.5 0.5
; generated code at q = 100 ; generated code at q = 1
push esi push esi
push edi push edi
mov edi,dword ptr [esp+0Ch] mov edi,dword ptr [esp+0Ch]
@ -48,9 +48,6 @@
#pragma warning(disable : 4996) // scanf #pragma warning(disable : 4996) // scanf
#endif #endif
typedef Xbyak::uint64 uint64;
typedef Xbyak::uint32 uint32;
const int N = 64; const int N = 64;
class Quantize : public Xbyak::CodeGenerator { class Quantize : public Xbyak::CodeGenerator {
@ -66,7 +63,7 @@ public:
output : eax = [esi+offset] / dividend output : eax = [esi+offset] / dividend
destroy : edx destroy : edx
*/ */
void udiv(uint32 dividend, int offset) void udiv(uint32_t dividend, int offset)
{ {
mov(eax, ptr[esi + offset]); mov(eax, ptr[esi + offset]);
@ -83,11 +80,11 @@ public:
return; return;
} }
uint64 mLow, mHigh; uint64_t mLow, mHigh;
int len = ilog2(odd) + 1; int len = ilog2(odd) + 1;
{ {
uint64 roundUp = uint64(1) << (32 + len); uint64_t roundUp = uint64_t(1) << (32 + len);
uint64 k = roundUp / (0xFFFFFFFFL - (0xFFFFFFFFL % odd)); uint64_t k = roundUp / (0xFFFFFFFFL - (0xFFFFFFFFL % odd));
mLow = roundUp / odd; mLow = roundUp / odd;
mHigh = (roundUp + k) / odd; mHigh = (roundUp + k) / odd;
} }
@ -96,12 +93,12 @@ public:
mLow >>= 1; mHigh >>= 1; len--; mLow >>= 1; mHigh >>= 1; len--;
} }
uint64 m; int a; uint64_t m; int a;
if ((mHigh >> 32) == 0) { if ((mHigh >> 32) == 0) {
m = mHigh; a = 0; m = mHigh; a = 0;
} else { } else {
len = ilog2(odd); len = ilog2(odd);
uint64 roundDown = uint64(1) << (32 + len); uint64_t roundDown = uint64_t(1) << (32 + len);
mLow = roundDown / odd; mLow = roundDown / odd;
int r = (int)(roundDown % odd); int r = (int)(roundDown % odd);
m = (r <= (odd >> 1)) ? mLow : mLow + 1; m = (r <= (odd >> 1)) ? mLow : mLow + 1;
@ -124,9 +121,9 @@ public:
mov(eax, edx); mov(eax, edx);
} }
/* /*
quantize(uint32 dest[64], const uint32 src[64]); quantize(uint32_t dest[64], const uint32_t src[64]);
*/ */
Quantize(const uint32 qTbl[64]) Quantize(const uint32_t qTbl[64])
{ {
push(esi); push(esi);
push(edi); push(edi);
@ -143,7 +140,7 @@ public:
} }
}; };
void quantize(uint32 dest[64], const uint32 src[64], const uint32 qTbl[64]) void quantize(uint32_t dest[64], const uint32_t src[64], const uint32_t qTbl[64])
{ {
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
dest[i] = src[i] / qTbl[i]; dest[i] = src[i] / qTbl[i];
@ -170,7 +167,7 @@ int main(int argc, char *argv[])
} }
} }
printf("q=%d\n", q); printf("q=%d\n", q);
uint32 qTbl[] = { uint32_t qTbl[] = {
16, 11, 10, 16, 24, 40, 51, 61, 16, 11, 10, 16, 24, 40, 51, 61,
12, 12, 14, 19, 26, 58, 60, 55, 12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56, 14, 13, 16, 24, 40, 57, 69, 56,
@ -187,16 +184,16 @@ int main(int argc, char *argv[])
} }
try { try {
uint32 src[N]; uint32_t src[N];
uint32 dest[N]; uint32_t dest[N];
uint32 dest2[N]; uint32_t dest2[N];
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
src[i] = rand() % 2048; src[i] = rand() % 2048;
} }
Quantize jit(qTbl); Quantize jit(qTbl);
//printf("jit size=%d, ptr=%p\n", jit.getSize(), jit.getCode()); //printf("jit size=%d, ptr=%p\n", jit.getSize(), jit.getCode());
void (*quantize2)(uint32*, const uint32*, const uint32 *) = jit.getCode<void (*)(uint32*, const uint32*, const uint32 *)>(); void (*quantize2)(uint32_t*, const uint32_t*, const uint32_t *) = jit.getCode<void (*)(uint32_t*, const uint32_t*, const uint32_t *)>();
quantize(dest, src, qTbl); quantize(dest, src, qTbl);
quantize2(dest2, src, qTbl); quantize2(dest2, src, qTbl);

View file

@ -0,0 +1,228 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{D06753BF-E1F3-4578-9B18-08673327F77C}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>15.0.27924.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Midl>
<TypeLibraryName>.\Debug/quantize.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderOutputFile>.\Debug/quantize.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug/quantize.pdb</ProgramDatabaseFile>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Midl>
<TypeLibraryName>.\Release/quantize.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\Release/quantize.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
<TypeLibraryName>.\Debug/quantize.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderOutputFile>.\Debug/quantize.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug/quantize.pdb</ProgramDatabaseFile>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
<TypeLibraryName>.\Release/quantize.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\Release/quantize.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="quantize.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -163,15 +163,15 @@ int main()
// use memory allocated by user // use memory allocated by user
using namespace Xbyak; using namespace Xbyak;
const size_t codeSize = 4096; const size_t codeSize = 4096;
uint8 buf[codeSize + 16]; uint8_t buf[codeSize + 16];
uint8 *p = CodeArray::getAlignedAddress(buf); uint8_t *p = CodeArray::getAlignedAddress(buf);
Sample s(p, codeSize); Sample s(p, codeSize);
if (!CodeArray::protect(p, codeSize, CodeArray::PROTECT_RWE)) { if (!CodeArray::protect(p, codeSize, CodeArray::PROTECT_RWE)) {
fprintf(stderr, "can't protect\n"); fprintf(stderr, "can't protect\n");
return 1; return 1;
} }
int (*func)(int) = s.getCode<int (*)(int)>(); int (*func)(int) = s.getCode<int (*)(int)>();
const uint8 *funcp = reinterpret_cast<const uint8*>(func); const uint8_t *funcp = reinterpret_cast<const uint8_t*>(func);
if (funcp != p) { if (funcp != p) {
fprintf(stderr, "internal error %p %p\n", p, funcp); fprintf(stderr, "internal error %p %p\n", p, funcp);
return 1; return 1;

View file

@ -0,0 +1,228 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{1CDE4D2A-BE3A-4B9B-B28F-524A23084A8E}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>15.0.27924.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Midl>
<TypeLibraryName>.\Debug/test0.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderOutputFile>.\Debug/test0.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug/test0.pdb</ProgramDatabaseFile>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Midl>
<TypeLibraryName>.\Release/test0.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\Release/test0.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
<TypeLibraryName>.\Debug/test0.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderOutputFile>.\Debug/test0.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug/test0.pdb</ProgramDatabaseFile>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
<TypeLibraryName>.\Release/test0.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\Release/test0.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="test0.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -78,6 +78,8 @@ void putCPUinfo()
{ Cpu::tAVX512_VNNI, "avx512_vnni" }, { Cpu::tAVX512_VNNI, "avx512_vnni" },
{ Cpu::tAVX512_BITALG, "avx512_bitalg" }, { Cpu::tAVX512_BITALG, "avx512_bitalg" },
{ Cpu::tAVX512_VPOPCNTDQ, "avx512_vpopcntdq" }, { Cpu::tAVX512_VPOPCNTDQ, "avx512_vpopcntdq" },
{ Cpu::tAVX512_BF16, "avx512_bf16" },
{ Cpu::tAVX512_VP2INTERSECT, "avx512_vp2intersect" },
}; };
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
if (cpu.has(tbl[i].type)) printf(" %s", tbl[i].str); if (cpu.has(tbl[i].type)) printf(" %s", tbl[i].str);

View file

@ -0,0 +1,228 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{CFC9B272-FDA1-4C87-B4EF-CDCA9B57F4DD}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>15.0.27924.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Midl>
<TypeLibraryName>.\Debug/test_util.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderOutputFile>.\Debug/test_util.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug/test_util.pdb</ProgramDatabaseFile>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
<TypeLibraryName>.\Debug/test_util.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderOutputFile>.\Debug/test_util.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug/test_util.pdb</ProgramDatabaseFile>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Midl>
<TypeLibraryName>.\Release/test_util.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\Release/test_util.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
<TypeLibraryName>.\Release/test_util.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\Release/test_util.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="test_util.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -39,7 +39,7 @@
using namespace Xbyak; using namespace Xbyak;
class ToyVm : public Xbyak::CodeGenerator { class ToyVm : public Xbyak::CodeGenerator {
typedef std::vector<uint32> Buffer; typedef std::vector<uint32_t> Buffer;
public: public:
enum Reg { enum Reg {
A, B A, B
@ -53,14 +53,14 @@ public:
{ {
::memset(mem_, 0, sizeof(mem_)); ::memset(mem_, 0, sizeof(mem_));
} }
void vldi(Reg r, uint16 imm) { encode(LDI, r, imm); } void vldi(Reg r, uint16_t imm) { encode(LDI, r, imm); }
void vld(Reg r, uint16 idx) { encode(LD, r, idx); } void vld(Reg r, uint16_t idx) { encode(LD, r, idx); }
void vst(Reg r, uint16 idx) { encode(ST, r, idx); } void vst(Reg r, uint16_t idx) { encode(ST, r, idx); }
void vadd(Reg r, uint16 idx) { encode(ADD, r, idx); } void vadd(Reg r, uint16_t idx) { encode(ADD, r, idx); }
void vaddi(Reg r, uint16 imm) { encode(ADDI, r, imm); } void vaddi(Reg r, uint16_t imm) { encode(ADDI, r, imm); }
void vsub(Reg r, uint16 idx) { encode(SUB, r, idx); } void vsub(Reg r, uint16_t idx) { encode(SUB, r, idx); }
void vsubi(Reg r, uint16 imm) { encode(SUBI, r, imm); } void vsubi(Reg r, uint16_t imm) { encode(SUBI, r, imm); }
void vjnz(Reg r, int offset) { encode(JNZ, r, static_cast<uint16>(offset)); } void vjnz(Reg r, int offset) { encode(JNZ, r, static_cast<uint16_t>(offset)); }
void vput(Reg r) { encode(PUT, r); } void vput(Reg r) { encode(PUT, r); }
void setMark() void setMark()
{ {
@ -73,12 +73,12 @@ public:
void run() void run()
{ {
bool debug = false;//true; bool debug = false;//true;
uint32 reg[2] = { 0, 0 }; uint32_t reg[2] = { 0, 0 };
const size_t end = code_.size(); const size_t end = code_.size();
uint32 pc = 0; uint32_t pc = 0;
for (;;) { for (;;) {
uint32 x = code_[pc]; uint32_t x = code_[pc];
uint32 code, r, imm; uint32_t code, r, imm;
decode(code, r, imm, x); decode(code, r, imm, x);
if (debug) { if (debug) {
printf("---\n"); printf("---\n");
@ -149,11 +149,11 @@ public:
xor_(edi, edi); xor_(edi, edi);
mov(mem, (size_t)mem_); mov(mem, (size_t)mem_);
const size_t end = code_.size(); const size_t end = code_.size();
uint32 pc = 0; uint32_t pc = 0;
uint32 labelNum = 0; uint32_t labelNum = 0;
for (;;) { for (;;) {
uint32 x = code_[pc]; uint32_t x = code_[pc];
uint32 code, r, imm; uint32_t code, r, imm;
decode(code, r, imm, x); decode(code, r, imm, x);
L(Label::toStr(labelNum++)); L(Label::toStr(labelNum++));
switch (code) { switch (code) {
@ -229,18 +229,18 @@ public:
ret(); ret();
} }
private: private:
uint32 mem_[65536]; uint32_t mem_[65536];
Buffer code_; Buffer code_;
int mark_; int mark_;
void decode(uint32& code, uint32& r, uint32& imm, uint32 x) void decode(uint32_t& code, uint32_t& r, uint32_t& imm, uint32_t x)
{ {
code = x >> 24; code = x >> 24;
r = (x >> 16) & 0xff; r = (x >> 16) & 0xff;
imm = x & 0xffff; imm = x & 0xffff;
} }
void encode(Code code, Reg r, uint16 imm = 0) void encode(Code code, Reg r, uint16_t imm = 0)
{ {
uint32 x = (code << 24) | (r << 16) | imm; uint32_t x = (code << 24) | (r << 16) | imm;
code_.push_back(x); code_.push_back(x);
} }
}; };
@ -262,7 +262,7 @@ public:
*/ */
vldi(A, 1); // c vldi(A, 1); // c
vst(A, 0); // p(1) vst(A, 0); // p(1)
vldi(B, static_cast<uint16>(n)); vldi(B, static_cast<uint16_t>(n));
vst(B, 2); // n vst(B, 2); // n
// lp // lp
setMark(); setMark();
@ -283,9 +283,9 @@ public:
} }
}; };
void fibC(uint32 n) void fibC(uint32_t n)
{ {
uint32 p, c, t; uint32_t p, c, t;
p = 1; p = 1;
c = 1; c = 1;
lp: lp:

View file

@ -0,0 +1,228 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{2E41C7AF-39FF-454C-B081-37445378DCB3}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>15.0.27924.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Midl>
<TypeLibraryName>.\Debug/toyvm.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderOutputFile>.\Debug/toyvm.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug/toyvm.pdb</ProgramDatabaseFile>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Midl>
<TypeLibraryName>.\Release/toyvm.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\Release/toyvm.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
<TypeLibraryName>.\Debug/toyvm.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderOutputFile>.\Debug/toyvm.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug/toyvm.pdb</ProgramDatabaseFile>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
<TypeLibraryName>.\Release/toyvm.tlb</TypeLibraryName>
<HeaderFileName />
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\Release/toyvm.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SuppressStartupBanner>true</SuppressStartupBanner>
<SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link>
<Bscmake>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Bscmake>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="toyvm.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -1,9 +1,18 @@
TARGET = make_nm normalize_prefix jmp address bad_address misc cvt_test cvt_test32 TARGET = make_nm normalize_prefix bad_address misc cvt_test cvt_test32 noexception
XBYAK_INC=../xbyak/xbyak.h XBYAK_INC=../xbyak/xbyak.h
UNAME_S=$(shell uname -s)
BIT=32 BIT=32
ifeq ($(shell uname -m),x86_64) ifeq ($(shell uname -m),x86_64)
BIT=64 BIT=64
endif endif
ONLY_64BIT=0
ifeq ($(UNAME_S),Darwin)
# 32-bit binary is not supported
ONLY_64BIT=1
endif
ifeq ($(ONLY_64BIT),0)
TARGET += jmp address
endif
ifeq ($(BIT),64) ifeq ($(BIT),64)
TARGET += jmp64 address64 TARGET += jmp64 address64
@ -36,18 +45,24 @@ cvt_test: cvt_test.cpp ../xbyak/xbyak.h
$(CXX) $(CFLAGS) $< -o $@ $(CXX) $(CFLAGS) $< -o $@
cvt_test32: cvt_test.cpp ../xbyak/xbyak.h cvt_test32: cvt_test.cpp ../xbyak/xbyak.h
$(CXX) $(CFLAGS) $< -o $@ -DXBYAK32 $(CXX) $(CFLAGS) $< -o $@ -DXBYAK32
noexception: noexception.cpp ../xbyak/xbyak.h
$(CXX) $(CFLAGS) $< -o $@ -fno-exceptions
test: normalize_prefix jmp bad_address $(TARGET) test_nm: normalize_prefix $(TARGET)
$(MAKE) -C ../gen $(MAKE) -C ../gen
ifneq ($(ONLY_64BIT),1)
./test_nm.sh ./test_nm.sh
./test_nm.sh noexcept
./noexception
./test_nm.sh Y ./test_nm.sh Y
./test_nm.sh avx512 ./test_nm.sh avx512
./test_address.sh ./test_address.sh
./jmp ./jmp
./cvt_test32
endif
./bad_address ./bad_address
./misc ./misc
./cvt_test ./cvt_test
./cvt_test32
ifeq ($(BIT),64) ifeq ($(BIT),64)
./test_address.sh 64 ./test_address.sh 64
./test_nm.sh 64 ./test_nm.sh 64
@ -56,8 +71,10 @@ ifeq ($(BIT),64)
endif endif
test_avx: normalize_prefix test_avx: normalize_prefix
ifneq ($(ONLY_64BIT),0)
./test_avx.sh ./test_avx.sh
./test_avx.sh Y ./test_avx.sh Y
endif
ifeq ($(BIT),64) ifeq ($(BIT),64)
./test_address.sh 64 ./test_address.sh 64
./test_avx.sh 64 ./test_avx.sh 64
@ -65,10 +82,18 @@ ifeq ($(BIT),64)
endif endif
test_avx512: normalize_prefix test_avx512: normalize_prefix
ifneq ($(ONLY_64BIT),0)
./test_avx512.sh ./test_avx512.sh
endif
ifeq ($(BIT),64) ifeq ($(BIT),64)
./test_avx512.sh 64 ./test_avx512.sh 64
endif endif
test:
$(MAKE) test_nm
$(MAKE) test_avx
$(MAKE) test_avx512
clean: clean:
rm -rf *.o $(TARGET) lib_run nm.cpp nm_frame make_512 rm -rf *.o $(TARGET) lib_run nm.cpp nm_frame make_512

View file

@ -1,47 +1,28 @@
#include <xbyak/xbyak.h> #include <xbyak/xbyak.h>
#include <cybozu/test.hpp>
#define TEST_EXCEPTION(state) \
{ \
num++; \
bool exception = false; \
try { \
state; \
} catch (...) { \
exception = true; \
} \
if (!exception) { \
printf("exception should arise for %s\n", #state); \
err++; \
} \
}
struct Code : Xbyak::CodeGenerator { struct Code : Xbyak::CodeGenerator {
Code() Code()
{ {
int err = 0; CYBOZU_TEST_EXCEPTION(mov(eax, ptr [esp + esp]), std::exception);
int num = 0; CYBOZU_TEST_EXCEPTION(mov(eax, ptr [ax]), std::exception); // not support
TEST_EXCEPTION(mov(eax, ptr [esp + esp])); CYBOZU_TEST_EXCEPTION(mov(eax, ptr [esp * 4]), std::exception);
TEST_EXCEPTION(mov(eax, ptr [ax])); // not support CYBOZU_TEST_EXCEPTION(mov(eax, ptr [eax * 16]), std::exception);
TEST_EXCEPTION(mov(eax, ptr [esp * 4])); CYBOZU_TEST_EXCEPTION(mov(eax, ptr [eax + eax + eax]), std::exception);
TEST_EXCEPTION(mov(eax, ptr [eax * 16])); CYBOZU_TEST_EXCEPTION(mov(eax, ptr [eax * 2 + ecx * 4]), std::exception);
TEST_EXCEPTION(mov(eax, ptr [eax + eax + eax])); CYBOZU_TEST_EXCEPTION(mov(eax, ptr [eax * 2 + ecx * 4]), std::exception);
TEST_EXCEPTION(mov(eax, ptr [eax * 2 + ecx * 4])); CYBOZU_TEST_EXCEPTION(mov(eax, ptr [xmm0]), std::exception);
TEST_EXCEPTION(mov(eax, ptr [eax * 2 + ecx * 4])); CYBOZU_TEST_EXCEPTION(fld(dword [xmm0]), std::exception);
TEST_EXCEPTION(mov(eax, ptr [xmm0])); CYBOZU_TEST_EXCEPTION(vgatherdpd(xmm0, ptr [eax * 2], ymm3), std::exception);
TEST_EXCEPTION(fld(dword [xmm0])); CYBOZU_TEST_EXCEPTION(vgatherdpd(xmm0, ptr [xmm0 + xmm1], ymm3), std::exception);
TEST_EXCEPTION(vgatherdpd(xmm0, ptr [eax * 2], ymm3));
TEST_EXCEPTION(vgatherdpd(xmm0, ptr [xmm0 + xmm1], ymm3));
#ifdef XBYAK64 #ifdef XBYAK64
TEST_EXCEPTION(mov(eax, ptr [rax + eax])); CYBOZU_TEST_EXCEPTION(mov(eax, ptr [rax + eax]), std::exception);
TEST_EXCEPTION(mov(eax, ptr [xmm0 + ymm0])); CYBOZU_TEST_EXCEPTION(mov(eax, ptr [xmm0 + ymm0]), std::exception);
#endif #endif
if (!err) {
printf("bad_address test %d ok\n", num);
}
} }
}; };
int main() CYBOZU_TEST_AUTO(exception)
{ {
Code c; Code c;
} }

View file

@ -117,7 +117,7 @@ CYBOZU_TEST_AUTO(test1)
int offset; int offset;
bool isBack; bool isBack;
bool isShort; bool isShort;
uint8 result[6]; uint8_t result[6];
int size; int size;
} tbl[] = { } tbl[] = {
{ 0, true, true, { 0xeb, 0xfe }, 2 }, { 0, true, true, { 0xeb, 0xfe }, 2 },
@ -133,7 +133,7 @@ CYBOZU_TEST_AUTO(test1)
const Tbl *p = &tbl[i]; const Tbl *p = &tbl[i];
for (int k = 0; k < 2; k++) { for (int k = 0; k < 2; k++) {
TestJmp jmp(p->offset, p->isBack, p->isShort, k == 0); TestJmp jmp(p->offset, p->isBack, p->isShort, k == 0);
const uint8 *q = (const uint8*)jmp.getCode(); const uint8_t *q = (const uint8_t*)jmp.getCode();
if (p->isBack) q += p->offset; /* skip nop */ if (p->isBack) q += p->offset; /* skip nop */
for (int j = 0; j < p->size; j++) { for (int j = 0; j < p->size; j++) {
CYBOZU_TEST_EQUAL(q[j], p->result[j]); CYBOZU_TEST_EQUAL(q[j], p->result[j]);
@ -205,6 +205,41 @@ CYBOZU_TEST_AUTO(testJmpCx)
} }
} }
CYBOZU_TEST_AUTO(loop)
{
const uint8_t ok[] = {
// lp:
0x31, 0xC0, // xor eax, eax
0xE2, 0xFC, // loop lp
0xE0, 0xFA, // loopne lp
0xE1, 0xF8, // loope lp
};
struct Code : CodeGenerator {
Code(bool useLabel)
{
if (useLabel) {
Xbyak::Label lp = L();
xor_(eax, eax);
loop(lp);
loopne(lp);
loope(lp);
} else {
L("@@");
xor_(eax, eax);
loop("@b");
loopne("@b");
loope("@b");
}
}
};
Code code1(false);
CYBOZU_TEST_EQUAL(code1.getSize(), sizeof(ok));
CYBOZU_TEST_EQUAL_ARRAY(code1.getCode(), ok, sizeof(ok));
Code code2(true);
CYBOZU_TEST_EQUAL(code2.getSize(), sizeof(ok));
CYBOZU_TEST_EQUAL_ARRAY(code2.getCode(), ok, sizeof(ok));
}
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(disable : 4310) #pragma warning(disable : 4310)
#endif #endif
@ -337,11 +372,11 @@ CYBOZU_TEST_AUTO(test3)
} }
#endif #endif
Xbyak::uint8 bufL[4096 * 32]; uint8_t bufL[4096 * 32];
Xbyak::uint8 bufS[4096 * 2]; uint8_t bufS[4096 * 2];
struct MyAllocator : Xbyak::Allocator { struct MyAllocator : Xbyak::Allocator {
Xbyak::uint8 *alloc(size_t size) uint8_t *alloc(size_t size)
{ {
if (size < sizeof(bufS)) { if (size < sizeof(bufS)) {
printf("test use bufS(%d)\n", (int)size); printf("test use bufS(%d)\n", (int)size);
@ -354,7 +389,7 @@ struct MyAllocator : Xbyak::Allocator {
fprintf(stderr, "no memory %d\n", (int)size); fprintf(stderr, "no memory %d\n", (int)size);
exit(1); exit(1);
} }
void free(Xbyak::uint8 *) void free(uint8_t *)
{ {
} }
} myAlloc; } myAlloc;
@ -393,6 +428,7 @@ CYBOZU_TEST_AUTO(test4)
} }
} }
#ifndef __APPLE__
CYBOZU_TEST_AUTO(test5) CYBOZU_TEST_AUTO(test5)
{ {
struct Test5 : Xbyak::CodeGenerator { struct Test5 : Xbyak::CodeGenerator {
@ -440,8 +476,9 @@ CYBOZU_TEST_AUTO(test5)
gm.assign((const char*)gc.getCode(), gc.getSize()); gm.assign((const char*)gc.getCode(), gc.getSize());
CYBOZU_TEST_EQUAL(fm, gm); CYBOZU_TEST_EQUAL(fm, gm);
} }
#endif
size_t getValue(const uint8* p) size_t getValue(const uint8_t* p)
{ {
size_t v = 0; size_t v = 0;
for (size_t i = 0; i < sizeof(size_t); i++) { for (size_t i = 0; i < sizeof(size_t); i++) {
@ -450,7 +487,7 @@ size_t getValue(const uint8* p)
return v; return v;
} }
void checkAddr(const uint8 *p, size_t offset, size_t expect) void checkAddr(const uint8_t *p, size_t offset, size_t expect)
{ {
size_t v = getValue(p + offset); size_t v = getValue(p + offset);
CYBOZU_TEST_EQUAL(v, size_t(p) + expect); CYBOZU_TEST_EQUAL(v, size_t(p) + expect);
@ -498,7 +535,7 @@ CYBOZU_TEST_AUTO(MovLabel)
const struct { const struct {
int pos; int pos;
uint8 ok; uint8_t ok;
} tbl[] = { } tbl[] = {
#ifdef XBYAK32 #ifdef XBYAK32
{ 0x00, 0x90 }, { 0x00, 0x90 },
@ -532,11 +569,11 @@ CYBOZU_TEST_AUTO(MovLabel)
const bool useNewLabel = k == 0; const bool useNewLabel = k == 0;
MovLabelCode code(grow, useNewLabel); MovLabelCode code(grow, useNewLabel);
if (grow) code.ready(); if (grow) code.ready();
const uint8* const p = code.getCode(); const uint8_t* const p = code.getCode();
for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(tbl); i++) {
int pos = tbl[i].pos; int pos = tbl[i].pos;
uint8 x = p[pos]; uint8_t x = p[pos];
uint8 ok = tbl[i].ok; uint8_t ok = tbl[i].ok;
CYBOZU_TEST_EQUAL(x, ok); CYBOZU_TEST_EQUAL(x, ok);
} }
#ifdef XBYAK32 #ifdef XBYAK32
@ -1182,11 +1219,11 @@ CYBOZU_TEST_AUTO(rip_jmp)
CYBOZU_TEST_EQUAL(ret, ret1234() + ret9999()); CYBOZU_TEST_EQUAL(ret, ret1234() + ret9999());
} }
#ifdef XBYAK64_GCC #if 0
CYBOZU_TEST_AUTO(rip_addr) CYBOZU_TEST_AUTO(rip_addr)
{ {
/* /*
assume |&x - &code| < 2GiB we can't assume |&x - &code| < 2GiB anymore
*/ */
static int x = 5; static int x = 5;
struct Code : Xbyak::CodeGenerator { struct Code : Xbyak::CodeGenerator {
@ -1201,6 +1238,8 @@ CYBOZU_TEST_AUTO(rip_addr)
CYBOZU_TEST_EQUAL(x, 123); CYBOZU_TEST_EQUAL(x, 123);
} }
#endif #endif
#ifndef __APPLE__
CYBOZU_TEST_AUTO(rip_addr_with_fixed_buf) CYBOZU_TEST_AUTO(rip_addr_with_fixed_buf)
{ {
MIE_ALIGN(4096) static char buf[8192]; MIE_ALIGN(4096) static char buf[8192];
@ -1225,6 +1264,7 @@ CYBOZU_TEST_AUTO(rip_addr_with_fixed_buf)
code.setProtectModeRW(); code.setProtectModeRW();
} }
#endif #endif
#endif
struct ReleaseTestCode : Xbyak::CodeGenerator { struct ReleaseTestCode : Xbyak::CodeGenerator {
ReleaseTestCode(Label& L1, Label& L2, Label& L3) ReleaseTestCode(Label& L1, Label& L2, Label& L3)
@ -1270,3 +1310,76 @@ CYBOZU_TEST_AUTO(release_label_after_code)
printf("id=%d %d %d %d %d\n", L1.getId(), L2.getId(), L3.getId(), L4.getId(), L5.getId()); printf("id=%d %d %d %d %d\n", L1.getId(), L2.getId(), L3.getId(), L4.getId(), L5.getId());
} }
} }
struct JmpTypeCode : Xbyak::CodeGenerator {
void nops()
{
for (int i = 0; i < 130; i++) {
nop();
}
}
// return jmp code size
size_t gen(bool pre, bool large, Xbyak::CodeGenerator::LabelType type)
{
Label label;
if (pre) {
L(label);
if (large) nops();
size_t pos = getSize();
jmp(label, type);
return getSize() - pos;
} else {
size_t pos = getSize();
jmp(label, type);
size_t size = getSize() - pos;
if (large) nops();
L(label);
return size;
}
}
};
CYBOZU_TEST_AUTO(setDefaultJmpNEAR)
{
const Xbyak::CodeGenerator::LabelType T_SHORT = Xbyak::CodeGenerator::T_SHORT;
const Xbyak::CodeGenerator::LabelType T_NEAR = Xbyak::CodeGenerator::T_NEAR;
const Xbyak::CodeGenerator::LabelType T_AUTO = Xbyak::CodeGenerator::T_AUTO;
const struct {
bool pre;
bool large;
Xbyak::CodeGenerator::LabelType type;
size_t expect1; // 0 means exception
size_t expect2;
} tbl[] = {
{ false, false, T_SHORT, 2, 2 },
{ false, false, T_NEAR, 5, 5 },
{ false, true, T_SHORT, 0, 0 },
{ false, true, T_NEAR, 5, 5 },
{ true, false, T_SHORT, 2, 2 },
{ true, false, T_NEAR, 5, 5 },
{ true, true, T_SHORT, 0, 0 },
{ true, true, T_NEAR, 5, 5 },
{ false, false, T_AUTO, 2, 5 },
{ false, true, T_AUTO, 0, 5 },
{ true, false, T_AUTO, 2, 2 },
{ true, true, T_AUTO, 5, 5 },
};
JmpTypeCode code1, code2;
code2.setDefaultJmpNEAR(true);
for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(tbl); i++) {
if (tbl[i].expect1) {
size_t size = code1.gen(tbl[i].pre, tbl[i].large, tbl[i].type);
CYBOZU_TEST_EQUAL(size, tbl[i].expect1);
} else {
CYBOZU_TEST_EXCEPTION(code1.gen(tbl[i].pre, tbl[i].large, tbl[i].type), std::exception);
}
if (tbl[i].expect2) {
size_t size = code2.gen(tbl[i].pre, tbl[i].large, tbl[i].type);
CYBOZU_TEST_EQUAL(size, tbl[i].expect2);
} else {
CYBOZU_TEST_EXCEPTION(code2.gen(tbl[i].pre, tbl[i].large, tbl[i].type), std::exception);
}
}
}

View file

@ -9,111 +9,111 @@ using namespace Xbyak;
const int bitEnd = 64; const int bitEnd = 64;
const uint64 YMM_SAE = 1ULL << 0; const uint64_t YMM_SAE = 1ULL << 0;
const uint64 _XMM = 1ULL << 1; const uint64_t _XMM = 1ULL << 1;
const uint64 _MEM = 1ULL << 2; const uint64_t _MEM = 1ULL << 2;
const uint64 _REG32 = 1ULL << 3; const uint64_t _REG32 = 1ULL << 3;
const uint64 EAX = 1ULL << 4; const uint64_t EAX = 1ULL << 4;
const uint64 IMM32 = 1ULL << 5; const uint64_t IMM32 = 1ULL << 5;
const uint64 IMM8 = 1ULL << 6; const uint64_t IMM8 = 1ULL << 6;
const uint64 _REG8 = 1ULL << 7; const uint64_t _REG8 = 1ULL << 7;
const uint64 _REG16 = 1ULL << 8; const uint64_t _REG16 = 1ULL << 8;
const uint64 XMM_K = 1ULL << 9; const uint64_t XMM_K = 1ULL << 9;
const uint64 YMM_K = 1ULL << 10; const uint64_t YMM_K = 1ULL << 10;
const uint64 ZMM_K = 1ULL << 11; const uint64_t ZMM_K = 1ULL << 11;
const uint64 AX = 1ULL << 12; const uint64_t AX = 1ULL << 12;
const uint64 AL = 1ULL << 13; const uint64_t AL = 1ULL << 13;
const uint64 IMM_1 = 1ULL << 14; const uint64_t IMM_1 = 1ULL << 14;
const uint64 MEM8 = 1ULL << 15; const uint64_t MEM8 = 1ULL << 15;
const uint64 MEM16 = 1ULL << 16; const uint64_t MEM16 = 1ULL << 16;
const uint64 MEM32 = 1ULL << 17; const uint64_t MEM32 = 1ULL << 17;
const uint64 VM32Z = 1ULL << 19; const uint64_t VM32Z = 1ULL << 19;
const uint64 K_K = 1ULL << 20; const uint64_t K_K = 1ULL << 20;
const uint64 MEM_ONLY_DISP = 1ULL << 21; const uint64_t MEM_ONLY_DISP = 1ULL << 21;
const uint64 VM32X_K = 1ULL << 23; const uint64_t VM32X_K = 1ULL << 23;
const uint64 _YMM = 1ULL << 24; const uint64_t _YMM = 1ULL << 24;
const uint64 VM32X_32 = 1ULL << 39; const uint64_t VM32X_32 = 1ULL << 39;
const uint64 VM32X_64 = 1ULL << 40; const uint64_t VM32X_64 = 1ULL << 40;
const uint64 VM32Y_32 = 1ULL << 41; const uint64_t VM32Y_32 = 1ULL << 41;
const uint64 VM32Y_64 = 1ULL << 42; const uint64_t VM32Y_64 = 1ULL << 42;
const uint64 VM32Z_K = 1ULL << 32; const uint64_t VM32Z_K = 1ULL << 32;
#ifdef XBYAK64 #ifdef XBYAK64
const uint64 _MEMe = 1ULL << 25; const uint64_t _MEMe = 1ULL << 25;
const uint64 REG32_2 = 1ULL << 26; // r8d, ... const uint64_t REG32_2 = 1ULL << 26; // r8d, ...
const uint64 REG16_2 = 1ULL << 27; // r8w, ... const uint64_t REG16_2 = 1ULL << 27; // r8w, ...
const uint64 REG8_2 = 1ULL << 28; // r8b, ... const uint64_t REG8_2 = 1ULL << 28; // r8b, ...
const uint64 REG8_3 = 1ULL << 29; // spl, ... const uint64_t REG8_3 = 1ULL << 29; // spl, ...
const uint64 _REG64 = 1ULL << 30; // rax, ... const uint64_t _REG64 = 1ULL << 30; // rax, ...
const uint64 _REG64_2 = 1ULL << 31; // r8, ... const uint64_t _REG64_2 = 1ULL << 31; // r8, ...
const uint64 _XMM2 = 1ULL << 33; const uint64_t _XMM2 = 1ULL << 33;
const uint64 _YMM2 = 1ULL << 34; const uint64_t _YMM2 = 1ULL << 34;
const uint64 VM32X = VM32X_32 | VM32X_64; const uint64_t VM32X = VM32X_32 | VM32X_64;
const uint64 VM32Y = VM32Y_32 | VM32Y_64; const uint64_t VM32Y = VM32Y_32 | VM32Y_64;
#else #else
const uint64 _MEMe = 0; const uint64_t _MEMe = 0;
const uint64 REG32_2 = 0; const uint64_t REG32_2 = 0;
const uint64 REG16_2 = 0; const uint64_t REG16_2 = 0;
const uint64 REG8_2 = 0; const uint64_t REG8_2 = 0;
const uint64 REG8_3 = 0; const uint64_t REG8_3 = 0;
const uint64 _REG64 = 0; const uint64_t _REG64 = 0;
const uint64 _REG64_2 = 0; const uint64_t _REG64_2 = 0;
const uint64 _XMM2 = 0; const uint64_t _XMM2 = 0;
const uint64 _YMM2 = 0; const uint64_t _YMM2 = 0;
const uint64 VM32X = VM32X_32; const uint64_t VM32X = VM32X_32;
const uint64 VM32Y = VM32Y_32; const uint64_t VM32Y = VM32Y_32;
#endif #endif
const uint64 REG64 = _REG64 | _REG64_2; const uint64_t REG64 = _REG64 | _REG64_2;
const uint64 REG32 = _REG32 | REG32_2 | EAX; const uint64_t REG32 = _REG32 | REG32_2 | EAX;
const uint64 REG16 = _REG16 | REG16_2 | AX; const uint64_t REG16 = _REG16 | REG16_2 | AX;
const uint64 REG32e = REG32 | REG64; const uint64_t REG32e = REG32 | REG64;
const uint64 REG8 = _REG8 | REG8_2|AL; const uint64_t REG8 = _REG8 | REG8_2|AL;
const uint64 MEM = _MEM | _MEMe; const uint64_t MEM = _MEM | _MEMe;
const uint64 MEM64 = 1ULL << 35; const uint64_t MEM64 = 1ULL << 35;
const uint64 YMM_ER = 1ULL << 36; const uint64_t YMM_ER = 1ULL << 36;
const uint64 VM32Y_K = 1ULL << 37; const uint64_t VM32Y_K = 1ULL << 37;
const uint64 IMM_2 = 1ULL << 38; const uint64_t IMM_2 = 1ULL << 38;
const uint64 IMM = IMM_1 | IMM_2; const uint64_t IMM = IMM_1 | IMM_2;
const uint64 YMM = _YMM | _YMM2; const uint64_t YMM = _YMM | _YMM2;
const uint64 K = 1ULL << 43; const uint64_t K = 1ULL << 43;
const uint64 _ZMM = 1ULL << 44; const uint64_t _ZMM = 1ULL << 44;
const uint64 _ZMM2 = 1ULL << 45; const uint64_t _ZMM2 = 1ULL << 45;
#ifdef XBYAK64 #ifdef XBYAK64
const uint64 ZMM = _ZMM | _ZMM2; const uint64_t ZMM = _ZMM | _ZMM2;
const uint64 _YMM3 = 1ULL << 46; const uint64_t _YMM3 = 1ULL << 46;
#else #else
const uint64 ZMM = _ZMM; const uint64_t ZMM = _ZMM;
const uint64 _YMM3 = 0; const uint64_t _YMM3 = 0;
#endif #endif
const uint64 K2 = 1ULL << 47; const uint64_t K2 = 1ULL << 47;
const uint64 ZMM_SAE = 1ULL << 48; const uint64_t ZMM_SAE = 1ULL << 48;
const uint64 ZMM_ER = 1ULL << 49; const uint64_t ZMM_ER = 1ULL << 49;
#ifdef XBYAK64 #ifdef XBYAK64
const uint64 _XMM3 = 1ULL << 50; const uint64_t _XMM3 = 1ULL << 50;
#else #else
const uint64 _XMM3 = 0; const uint64_t _XMM3 = 0;
#endif #endif
const uint64 XMM = _XMM | _XMM2 | _XMM3; const uint64_t XMM = _XMM | _XMM2 | _XMM3;
const uint64 XMM_SAE = 1ULL << 51; const uint64_t XMM_SAE = 1ULL << 51;
#ifdef XBYAK64 #ifdef XBYAK64
const uint64 XMM_KZ = 1ULL << 52; const uint64_t XMM_KZ = 1ULL << 52;
const uint64 YMM_KZ = 1ULL << 53; const uint64_t YMM_KZ = 1ULL << 53;
const uint64 ZMM_KZ = 1ULL << 54; const uint64_t ZMM_KZ = 1ULL << 54;
#else #else
const uint64 XMM_KZ = 0; const uint64_t XMM_KZ = 0;
const uint64 YMM_KZ = 0; const uint64_t YMM_KZ = 0;
const uint64 ZMM_KZ = 0; const uint64_t ZMM_KZ = 0;
#endif #endif
const uint64 MEM_K = 1ULL << 55; const uint64_t MEM_K = 1ULL << 55;
const uint64 M_1to2 = 1ULL << 56; const uint64_t M_1to2 = 1ULL << 56;
const uint64 M_1to4 = 1ULL << 57; const uint64_t M_1to4 = 1ULL << 57;
const uint64 M_1to8 = 1ULL << 58; const uint64_t M_1to8 = 1ULL << 58;
const uint64 M_1to16 = 1ULL << 59; const uint64_t M_1to16 = 1ULL << 59;
const uint64 XMM_ER = 1ULL << 60; const uint64_t XMM_ER = 1ULL << 60;
const uint64 M_xword = 1ULL << 61; const uint64_t M_xword = 1ULL << 61;
const uint64 M_yword = 1ULL << 62; const uint64_t M_yword = 1ULL << 62;
const uint64 MY_1to4 = 1ULL << 18; const uint64_t MY_1to4 = 1ULL << 18;
const uint64 NOPARA = 1ULL << (bitEnd - 1); const uint64_t NOPARA = 1ULL << (bitEnd - 1);
class Test { class Test {
Test(const Test&); Test(const Test&);
@ -121,7 +121,7 @@ class Test {
const bool isXbyak_; const bool isXbyak_;
int funcNum_; int funcNum_;
// check all op1, op2, op3 // check all op1, op2, op3
void put(const std::string& nm, uint64 op1 = NOPARA, uint64 op2 = NOPARA, uint64 op3 = NOPARA, uint64 op4 = NOPARA) const void put(const std::string& nm, uint64_t op1 = NOPARA, uint64_t op2 = NOPARA, uint64_t op3 = NOPARA, uint64_t op4 = NOPARA) const
{ {
for (int i = 0; i < bitEnd; i++) { for (int i = 0; i < bitEnd; i++) {
if ((op1 & (1ULL << i)) == 0) continue; if ((op1 & (1ULL << i)) == 0) continue;
@ -144,7 +144,7 @@ class Test {
} }
} }
} }
void put(const char *nm, uint64 op, const char *xbyak, const char *nasm) const void put(const char *nm, uint64_t op, const char *xbyak, const char *nasm) const
{ {
for (int i = 0; i < bitEnd; i++) { for (int i = 0; i < bitEnd; i++) {
if ((op & (1ULL << i)) == 0) continue; if ((op & (1ULL << i)) == 0) continue;
@ -156,7 +156,7 @@ class Test {
printf("\n"); printf("\n");
} }
} }
void put(const char *nm, const char *xbyak, const char *nasm = 0, uint64 op = NOPARA) const void put(const char *nm, const char *xbyak, const char *nasm = 0, uint64_t op = NOPARA) const
{ {
if (nasm == 0) nasm = xbyak; if (nasm == 0) nasm = xbyak;
for (int i = 0; i < bitEnd; i++) { for (int i = 0; i < bitEnd; i++) {
@ -169,7 +169,7 @@ class Test {
printf("\n"); printf("\n");
} }
} }
const char *get(uint64 type) const const char *get(uint64_t type) const
{ {
int idx = (rand() / 31) & 7; int idx = (rand() / 31) & 7;
switch (type) { switch (type) {
@ -537,7 +537,7 @@ public:
printf("vaddpd(%s%s%s, %s, %s%s); dump();\n", r1, pk, pz, r2, r3, saeTblXbyak[sae]); printf("vaddpd(%s%s%s, %s, %s%s); dump();\n", r1, pk, pz, r2, r3, saeTblXbyak[sae]);
} else { } else {
if (kIdx) CYBOZU_SNPRINTF(pk, sizeof(pk), "{k%d}", kIdx); if (kIdx) CYBOZU_SNPRINTF(pk, sizeof(pk), "{k%d}", kIdx);
if (z) pz = "{z}"; if (z && kIdx) pz = "{z}";
printf("vaddpd %s%s%s, %s, %s%s\n", r1, pk, pz, r2, r3, saeTblNASM[sae]); printf("vaddpd %s%s%s, %s, %s%s\n", r1, pk, pz, r2, r3, saeTblNASM[sae]);
} }
} }
@ -574,9 +574,9 @@ public:
for (size_t k = 0; k < N; k++) { for (size_t k = 0; k < N; k++) {
#ifdef XBYAK64 #ifdef XBYAK64
for (int kIdx = 0; kIdx < 8; kIdx++) { for (int kIdx = 0; kIdx < 8; kIdx++) {
put_vaddpd(xTbl[i], xTbl[j], xTbl[k], kIdx);
put_vaddpd(yTbl[i], yTbl[j], yTbl[k], kIdx);
for (int z = 0; z < 2; z++) { for (int z = 0; z < 2; z++) {
put_vaddpd(xTbl[i], xTbl[j], xTbl[k], kIdx, z == 1);
put_vaddpd(yTbl[i], yTbl[j], yTbl[k], kIdx, z == 1);
for (int sae = 0; sae < 5; sae++) { for (int sae = 0; sae < 5; sae++) {
put_vaddpd(zTbl[i], zTbl[j], zTbl[k], kIdx, z == 1, sae); put_vaddpd(zTbl[i], zTbl[j], zTbl[k], kIdx, z == 1, sae);
} }
@ -615,6 +615,13 @@ public:
put(p->name, K, _YMM, _YMM | MEM, IMM8); put(p->name, K, _YMM, _YMM | MEM, IMM8);
put(p->name, K, _ZMM, _ZMM | MEM, IMM8); put(p->name, K, _ZMM, _ZMM | MEM, IMM8);
} }
put("vcmppd", K, XMM, M_1to2, IMM8);
put("vcmppd", K, YMM, M_1to4, IMM8);
put("vcmppd", K, ZMM, M_1to8, IMM8);
put("vcmpps", K, XMM, M_1to4, IMM8);
put("vcmpps", K, YMM, M_1to8, IMM8);
put("vcmpps", K, ZMM, M_1to16, IMM8);
} }
put("vcmppd", K2, ZMM, ZMM_SAE, IMM); put("vcmppd", K2, ZMM, ZMM_SAE, IMM);
#ifdef XBYAK64 #ifdef XBYAK64

View file

@ -1,5 +1,4 @@
#include <stdio.h> #include <stdio.h>
#define XBYAK_NO_OP_NAMES
#include "xbyak/xbyak.h" #include "xbyak/xbyak.h"
#include "xbyak/xbyak_bin2hex.h" #include "xbyak/xbyak_bin2hex.h"
#include <stdlib.h> #include <stdlib.h>
@ -11,111 +10,111 @@ using namespace Xbyak;
const int bitEnd = 64; const int bitEnd = 64;
const uint64 MMX = 1ULL << 0; const uint64_t MMX = 1ULL << 0;
const uint64 _XMM = 1ULL << 1; const uint64_t _XMM = 1ULL << 1;
const uint64 _MEM = 1ULL << 2; const uint64_t _MEM = 1ULL << 2;
const uint64 _REG32 = 1ULL << 3; const uint64_t _REG32 = 1ULL << 3;
const uint64 EAX = 1ULL << 4; const uint64_t EAX = 1ULL << 4;
const uint64 IMM32 = 1ULL << 5; const uint64_t IMM32 = 1ULL << 5;
const uint64 IMM8 = 1ULL << 6; const uint64_t IMM8 = 1ULL << 6;
const uint64 _REG8 = 1ULL << 7; const uint64_t _REG8 = 1ULL << 7;
const uint64 _REG16 = 1ULL << 8; const uint64_t _REG16 = 1ULL << 8;
const uint64 NEG8 = 1ULL << 9; const uint64_t NEG8 = 1ULL << 9;
const uint64 IMM16 = 1ULL << 10; const uint64_t IMM16 = 1ULL << 10;
const uint64 NEG16 = 1ULL << 11; const uint64_t NEG16 = 1ULL << 11;
const uint64 AX = 1ULL << 12; const uint64_t AX = 1ULL << 12;
const uint64 AL = 1ULL << 13; const uint64_t AL = 1ULL << 13;
const uint64 IMM_1 = 1ULL << 14; const uint64_t IMM_1 = 1ULL << 14;
const uint64 MEM8 = 1ULL << 15; const uint64_t MEM8 = 1ULL << 15;
const uint64 MEM16 = 1ULL << 16; const uint64_t MEM16 = 1ULL << 16;
const uint64 MEM32 = 1ULL << 17; const uint64_t MEM32 = 1ULL << 17;
const uint64 ONE = 1ULL << 19; const uint64_t ONE = 1ULL << 19;
const uint64 CL = 1ULL << 20; const uint64_t CL = 1ULL << 20;
const uint64 MEM_ONLY_DISP = 1ULL << 21; const uint64_t MEM_ONLY_DISP = 1ULL << 21;
const uint64 NEG32 = 1ULL << 23; const uint64_t NEG32 = 1ULL << 23;
const uint64 _YMM = 1ULL << 24; const uint64_t _YMM = 1ULL << 24;
const uint64 VM32X_32 = 1ULL << 39; const uint64_t VM32X_32 = 1ULL << 39;
const uint64 VM32X_64 = 1ULL << 40; const uint64_t VM32X_64 = 1ULL << 40;
const uint64 VM32Y_32 = 1ULL << 41; const uint64_t VM32Y_32 = 1ULL << 41;
const uint64 VM32Y_64 = 1ULL << 42; const uint64_t VM32Y_64 = 1ULL << 42;
#ifdef XBYAK64 #ifdef XBYAK64
const uint64 _MEMe = 1ULL << 25; const uint64_t _MEMe = 1ULL << 25;
const uint64 REG32_2 = 1ULL << 26; // r8d, ... const uint64_t REG32_2 = 1ULL << 26; // r8d, ...
const uint64 REG16_2 = 1ULL << 27; // r8w, ... const uint64_t REG16_2 = 1ULL << 27; // r8w, ...
const uint64 REG8_2 = 1ULL << 28; // r8b, ... const uint64_t REG8_2 = 1ULL << 28; // r8b, ...
const uint64 REG8_3 = 1ULL << 29; // spl, ... const uint64_t REG8_3 = 1ULL << 29; // spl, ...
const uint64 _REG64 = 1ULL << 30; // rax, ... const uint64_t _REG64 = 1ULL << 30; // rax, ...
const uint64 _REG64_2 = 1ULL << 31; // r8, ... const uint64_t _REG64_2 = 1ULL << 31; // r8, ...
const uint64 RAX = 1ULL << 32; const uint64_t RAX = 1ULL << 32;
const uint64 _XMM2 = 1ULL << 33; const uint64_t _XMM2 = 1ULL << 33;
const uint64 _YMM2 = 1ULL << 34; const uint64_t _YMM2 = 1ULL << 34;
const uint64 VM32X = VM32X_32 | VM32X_64; const uint64_t VM32X = VM32X_32 | VM32X_64;
const uint64 VM32Y = VM32Y_32 | VM32Y_64; const uint64_t VM32Y = VM32Y_32 | VM32Y_64;
#else #else
const uint64 _MEMe = 0; const uint64_t _MEMe = 0;
const uint64 REG32_2 = 0; const uint64_t REG32_2 = 0;
const uint64 REG16_2 = 0; const uint64_t REG16_2 = 0;
const uint64 REG8_2 = 0; const uint64_t REG8_2 = 0;
const uint64 REG8_3 = 0; const uint64_t REG8_3 = 0;
const uint64 _REG64 = 0; const uint64_t _REG64 = 0;
const uint64 _REG64_2 = 0; const uint64_t _REG64_2 = 0;
const uint64 RAX = 0; const uint64_t RAX = 0;
const uint64 _XMM2 = 0; const uint64_t _XMM2 = 0;
const uint64 _YMM2 = 0; const uint64_t _YMM2 = 0;
const uint64 VM32X = VM32X_32; const uint64_t VM32X = VM32X_32;
const uint64 VM32Y = VM32Y_32; const uint64_t VM32Y = VM32Y_32;
#endif #endif
const uint64 REG64 = _REG64 | _REG64_2 | RAX; const uint64_t REG64 = _REG64 | _REG64_2 | RAX;
const uint64 REG32 = _REG32 | REG32_2 | EAX; const uint64_t REG32 = _REG32 | REG32_2 | EAX;
const uint64 REG16 = _REG16 | REG16_2 | AX; const uint64_t REG16 = _REG16 | REG16_2 | AX;
const uint64 REG32e = REG32 | REG64; const uint64_t REG32e = REG32 | REG64;
const uint64 REG8 = _REG8 | REG8_2|AL; const uint64_t REG8 = _REG8 | REG8_2|AL;
const uint64 MEM = _MEM | _MEMe; const uint64_t MEM = _MEM | _MEMe;
const uint64 MEM64 = 1ULL << 35; const uint64_t MEM64 = 1ULL << 35;
const uint64 ST0 = 1ULL << 36; const uint64_t ST0 = 1ULL << 36;
const uint64 STi = 1ULL << 37; const uint64_t STi = 1ULL << 37;
const uint64 IMM_2 = 1ULL << 38; const uint64_t IMM_2 = 1ULL << 38;
const uint64 IMM = IMM_1 | IMM_2; const uint64_t IMM = IMM_1 | IMM_2;
const uint64 XMM = _XMM | _XMM2; const uint64_t XMM = _XMM | _XMM2;
const uint64 YMM = _YMM | _YMM2; const uint64_t YMM = _YMM | _YMM2;
const uint64 K = 1ULL << 43; const uint64_t K = 1ULL << 43;
const uint64 _ZMM = 1ULL << 44; const uint64_t _ZMM = 1ULL << 44;
const uint64 _ZMM2 = 1ULL << 45; const uint64_t _ZMM2 = 1ULL << 45;
#ifdef XBYAK64 #ifdef XBYAK64
const uint64 ZMM = _ZMM | _ZMM2; const uint64_t ZMM = _ZMM | _ZMM2;
const uint64 _YMM3 = 1ULL << 46; const uint64_t _YMM3 = 1ULL << 46;
#else #else
const uint64 ZMM = _ZMM; const uint64_t ZMM = _ZMM;
const uint64 _YMM3 = 0; const uint64_t _YMM3 = 0;
#endif #endif
const uint64 K2 = 1ULL << 47; const uint64_t K2 = 1ULL << 47;
const uint64 ZMM_SAE = 1ULL << 48; const uint64_t ZMM_SAE = 1ULL << 48;
const uint64 ZMM_ER = 1ULL << 49; const uint64_t ZMM_ER = 1ULL << 49;
#ifdef XBYAK64 #ifdef XBYAK64
const uint64 _XMM3 = 1ULL << 50; const uint64_t _XMM3 = 1ULL << 50;
#endif #endif
const uint64 XMM_SAE = 1ULL << 51; const uint64_t XMM_SAE = 1ULL << 51;
#ifdef XBYAK64 #ifdef XBYAK64
const uint64 XMM_KZ = 1ULL << 52; const uint64_t XMM_KZ = 1ULL << 52;
const uint64 YMM_KZ = 1ULL << 53; const uint64_t YMM_KZ = 1ULL << 53;
const uint64 ZMM_KZ = 1ULL << 54; const uint64_t ZMM_KZ = 1ULL << 54;
#else #else
const uint64 XMM_KZ = 0; const uint64_t XMM_KZ = 0;
const uint64 YMM_KZ = 0; const uint64_t YMM_KZ = 0;
const uint64 ZMM_KZ = 0; const uint64_t ZMM_KZ = 0;
#endif #endif
const uint64 MEM_K = 1ULL << 55; const uint64_t MEM_K = 1ULL << 55;
const uint64 M_1to2 = 1ULL << 56; const uint64_t M_1to2 = 1ULL << 56;
const uint64 M_1to4 = 1ULL << 57; const uint64_t M_1to4 = 1ULL << 57;
const uint64 M_1to8 = 1ULL << 58; const uint64_t M_1to8 = 1ULL << 58;
const uint64 M_1to16 = 1ULL << 59; const uint64_t M_1to16 = 1ULL << 59;
const uint64 XMM_ER = 1ULL << 60; const uint64_t XMM_ER = 1ULL << 60;
const uint64 M_xword = 1ULL << 61; const uint64_t M_xword = 1ULL << 61;
const uint64 M_yword = 1ULL << 62; const uint64_t M_yword = 1ULL << 62;
const uint64 MY_1to4 = 1ULL << 18; const uint64_t MY_1to4 = 1ULL << 18;
const uint64 BNDREG = 1ULL << 22; const uint64_t BNDREG = 1ULL << 22;
const uint64 NOPARA = 1ULL << (bitEnd - 1); const uint64_t NOPARA = 1ULL << (bitEnd - 1);
class Test { class Test {
Test(const Test&); Test(const Test&);
@ -132,7 +131,7 @@ class Test {
} }
// check all op1, op2, op3 // check all op1, op2, op3
void put(const std::string& nm, uint64 op1 = NOPARA, uint64 op2 = NOPARA, uint64 op3 = NOPARA, uint64 op4 = NOPARA) const void put(const std::string& nm, uint64_t op1 = NOPARA, uint64_t op2 = NOPARA, uint64_t op3 = NOPARA, uint64_t op4 = NOPARA) const
{ {
for (int i = 0; i < bitEnd; i++) { for (int i = 0; i < bitEnd; i++) {
if ((op1 & (1ULL << i)) == 0) continue; if ((op1 & (1ULL << i)) == 0) continue;
@ -155,7 +154,7 @@ class Test {
} }
} }
} }
void put(const char *nm, uint64 op, const char *xbyak, const char *nasm) const void put(const char *nm, uint64_t op, const char *xbyak, const char *nasm) const
{ {
for (int i = 0; i < bitEnd; i++) { for (int i = 0; i < bitEnd; i++) {
if ((op & (1ULL << i)) == 0) continue; if ((op & (1ULL << i)) == 0) continue;
@ -167,7 +166,7 @@ class Test {
printf("\n"); printf("\n");
} }
} }
void put(const char *nm, const char *xbyak, const char *nasm = 0, uint64 op = NOPARA) const void put(const char *nm, const char *xbyak, const char *nasm = 0, uint64_t op = NOPARA) const
{ {
if (nasm == 0) nasm = xbyak; if (nasm == 0) nasm = xbyak;
for (int i = 0; i < bitEnd; i++) { for (int i = 0; i < bitEnd; i++) {
@ -180,7 +179,7 @@ class Test {
printf("\n"); printf("\n");
} }
} }
const char *get(uint64 type) const const char *get(uint64_t type) const
{ {
int idx = (rand() / 31) & 7; int idx = (rand() / 31) & 7;
if (type == ST0) { if (type == ST0) {
@ -460,8 +459,14 @@ class Test {
"cqo", "cqo",
"cmpsq", "cmpsq",
"movsq", "movsq",
"popfq",
"pushfq",
"lodsq",
"movsq",
"scasq", "scasq",
"stosq", "stosq",
"syscall",
"sysret",
#else #else
"aaa", "aaa",
"aad", "aad",
@ -469,6 +474,7 @@ class Test {
"aas", "aas",
"daa", "daa",
"das", "das",
"into",
"popad", "popad",
"popfd", "popfd",
"pusha", "pusha",
@ -493,9 +499,17 @@ class Test {
"cmpsb", "cmpsb",
"cmpsw", "cmpsw",
"cmpsd", "cmpsd",
"int3",
"leave",
"lodsb",
"lodsw",
"lodsd",
"movsb", "movsb",
"movsw", "movsw",
"movsd", "movsd",
"outsb",
"outsw",
"outsd",
"scasb", "scasb",
"scasw", "scasw",
"scasd", "scasd",
@ -508,6 +522,8 @@ class Test {
"stc", "stc",
"std", "std",
"sti", "sti",
"sysenter",
"sysexit",
"emms", "emms",
"pause", "pause",
@ -540,6 +556,8 @@ class Test {
"fabs", "fabs",
"faddp", "faddp",
"fchs", "fchs",
"fclex",
"fnclex",
"fcom", "fcom",
"fcomp", "fcomp",
"fcompp", "fcompp",
@ -579,15 +597,52 @@ class Test {
"fxtract", "fxtract",
"fyl2x", "fyl2x",
"fyl2xp1", "fyl2xp1",
"monitorx",
"mwaitx",
"clzero",
}; };
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
put(tbl[i]); put(tbl[i]);
} }
{
const char memTbl[][16] = {
"clflush",
"clflushopt",
"fbld",
"fbstp",
"fldcw",
"fldenv",
"frstor",
"fsave",
"fnsave",
"fstcw",
"fnstcw",
"fstenv",
"fnstenv",
"fstsw",
"fnstsw",
"fxrstor",
};
for (size_t i = 0; i < NUM_OF_ARRAY(memTbl); i++) {
put(memTbl[i], MEM);
}
put("fstsw", AX);
put("fnstsw", AX);
}
put("bswap", REG32e); put("bswap", REG32e);
put("lea", REG32e|REG16, MEM); put("lea", REG32e|REG16, MEM);
put("fldcw", MEM); put("enter", IMM, IMM);
put("fstcw", MEM); put(isXbyak_ ? "int_" : "int", IMM8);
put(isXbyak_ ? "in_" : "in", AL|AX|EAX, IMM8);
puts(isXbyak_ ? "in_(al, dx); dump();" : "in al, dx");
puts(isXbyak_ ? "in_(ax, dx); dump();" : "in ax, dx");
puts(isXbyak_ ? "in_(eax, dx); dump();" : "in eax, dx");
put(isXbyak_ ? "out_" : "out", IMM8, AL|AX|EAX);
puts(isXbyak_ ? "out_(dx, al); dump();" : "out dx, al");
puts(isXbyak_ ? "out_(dx, ax); dump();" : "out dx, ax");
puts(isXbyak_ ? "out_(dx, eax); dump();" : "out dx, eax");
} }
void putJmp() const void putJmp() const
{ {
@ -803,7 +858,7 @@ class Test {
SD = 1 << 3 SD = 1 << 3
}; };
const struct { const struct {
uint8 code; uint8_t code;
const char *name; const char *name;
} sufTbl[] = { } sufTbl[] = {
{ 0, "ps" }, { 0, "ps" },
@ -812,7 +867,7 @@ class Test {
{ 0xF2, "sd" }, { 0xF2, "sd" },
}; };
static const struct XmmTbl1 { static const struct XmmTbl1 {
uint8 code; uint8_t code;
int mode; int mode;
const char *name; const char *name;
bool hasImm; bool hasImm;
@ -841,7 +896,7 @@ class Test {
for (size_t j = 0; j < NUM_OF_ARRAY(sufTbl); j++) { for (size_t j = 0; j < NUM_OF_ARRAY(sufTbl); j++) {
if (!(p->mode & (1 << j))) continue; if (!(p->mode & (1 << j))) continue;
char buf[16]; char buf[16];
sprintf(buf, "%s%s", p->name, sufTbl[j].name); snprintf(buf, sizeof(buf), "%s%s", p->name, sufTbl[j].name);
if (p->hasImm) { if (p->hasImm) {
put(buf, XMM, XMM|MEM, IMM); put(buf, XMM, XMM|MEM, IMM);
} else { } else {
@ -891,8 +946,8 @@ class Test {
{ {
static const struct Tbl { static const struct Tbl {
const char *name; const char *name;
uint64 op1; uint64_t op1;
uint64 op2; uint64_t op2;
} tbl[] = { } tbl[] = {
{ "cvtpi2ps", XMM, MMX|MEM }, { "cvtpi2ps", XMM, MMX|MEM },
{ "cvtps2pi", MMX, XMM|MEM }, { "cvtps2pi", MMX, XMM|MEM },
@ -928,7 +983,9 @@ class Test {
} }
void putCmov() const void putCmov() const
{ {
const char tbl[][4] = { const struct {
const char *s;
} tbl[] = {
"o", "o",
"no", "no",
"b", "b",
@ -961,12 +1018,12 @@ class Test {
"g", "g",
}; };
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) { for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
char buf[16]; char buf[32];
sprintf(buf, "cmov%s", tbl[i]); snprintf(buf, sizeof(buf), "cmov%s", tbl[i].s);
put(buf, REG16, REG16|MEM); put(buf, REG16, REG16|MEM);
put(buf, REG32, REG32|MEM); put(buf, REG32, REG32|MEM);
put(buf, REG64, REG64|MEM); put(buf, REG64, REG64|MEM);
sprintf(buf, "set%s", tbl[i]); snprintf(buf, sizeof(buf), "set%s", tbl[i].s);
put(buf, REG8|REG8_3|MEM); put(buf, REG8|REG8_3|MEM);
} }
} }
@ -1088,6 +1145,33 @@ class Test {
put("pop", REG32|MEM32); put("pop", REG32|MEM32);
#endif #endif
} }
void putPushPop8_16() const
{
const struct {
int b;
uint32_t v;
} tbl[] = {
{ 8, 0x7f },
{ 8, 0x80 },
{ 8, 0xff },
{ 8, 0x100 },
{ 8, 0x12345 },
{ 16, 0x7fff },
{ 16, 0x8000 },
{ 16, 0xffff },
{ 16, 0x10000 },
{ 16, 0x12345 },
};
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const char *b = tbl[i].b == 8 ? "byte" : "word";
uint32_t v = tbl[i].v;
if (isXbyak_) {
printf("push(%s, 0x%x);dump();\n", b, v);
} else {
printf("push %s 0x%x\n", b, v);
}
}
}
void putTest() const void putTest() const
{ {
const char *p = "test"; const char *p = "test";
@ -1121,6 +1205,30 @@ class Test {
put("mov", REG64, tbl[i].a, tbl[i].b); put("mov", REG64, tbl[i].a, tbl[i].b);
} }
} }
void putLoadSeg() const
{
const struct Tbl {
const char *name;
bool support64Bit;
} tbl[] = {
#ifdef XBYAK32
{ "lds", false },
{ "les", false },
#endif
{ "lss", true },
{ "lfs", true },
{ "lgs", true },
};
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
const Tbl *p = &tbl[i];
put(p->name, REG16|REG32, MEM);
#ifdef XBYAK64
if (p->support64Bit) {
put(p->name, REG64, MEM);
}
#endif
}
}
// only nasm // only nasm
void putMovImm64() const void putMovImm64() const
{ {
@ -1176,6 +1284,7 @@ class Test {
put("cmpxchg8b", MEM); put("cmpxchg8b", MEM);
#ifdef XBYAK64 #ifdef XBYAK64
put("cmpxchg16b", MEM); put("cmpxchg16b", MEM);
put("fxrstor64", MEM);
#endif #endif
{ {
const char tbl[][8] = { const char tbl[][8] = {
@ -1384,9 +1493,9 @@ class Test {
void putMPX() const void putMPX() const
{ {
#ifdef XBYAK64 #ifdef XBYAK64
const uint64 reg = REG64; const uint64_t reg = REG64;
#else #else
const uint64 reg = REG32; const uint64_t reg = REG32;
#endif #endif
put("bndcl", BNDREG, reg|MEM); put("bndcl", BNDREG, reg|MEM);
put("bndcu", BNDREG, reg|MEM); put("bndcu", BNDREG, reg|MEM);
@ -2414,6 +2523,7 @@ public:
separateFunc(); separateFunc();
putSSE4_2(); putSSE4_2();
putSeg(); // same behavior as yasm for mov rax, cx putSeg(); // same behavior as yasm for mov rax, cx
putPushPop8_16();
#else #else
putSIMPLE(); putSIMPLE();
putReg1(); putReg1();
@ -2423,6 +2533,7 @@ public:
putPushPop(); putPushPop();
putTest(); putTest();
separateFunc(); separateFunc();
putLoadSeg();
putEtc(); putEtc();
putShift(); putShift();
putShxd(); putShxd();
@ -2447,7 +2558,6 @@ public:
putFpuMem32_64(); putFpuMem32_64();
separateFunc(); separateFunc();
putFpuMem16_32_64(); putFpuMem16_32_64();
put("clflush", MEM); // current nasm is ok
putFpu(); putFpu();
putFpuFpu(); putFpuFpu();
putCmp(); putCmp();
@ -2546,7 +2656,7 @@ public:
printf("vaddpd(%s%s%s, %s, %s%s); dump();\n", r1, pk, pz, r2, r3, saeTblXbyak[sae]); printf("vaddpd(%s%s%s, %s, %s%s); dump();\n", r1, pk, pz, r2, r3, saeTblXbyak[sae]);
} else { } else {
if (kIdx) CYBOZU_SNPRINTF(pk, sizeof(pk), "{k%d}", kIdx); if (kIdx) CYBOZU_SNPRINTF(pk, sizeof(pk), "{k%d}", kIdx);
if (z) pz = "{z}"; if (z && kIdx) pz = "{z}";
printf("vaddpd %s%s%s, %s, %s%s\n", r1, pk, pz, r2, r3, saeTblNASM[sae]); printf("vaddpd %s%s%s, %s, %s%s\n", r1, pk, pz, r2, r3, saeTblNASM[sae]);
} }
} }
@ -2583,9 +2693,9 @@ public:
for (size_t k = 0; k < N; k++) { for (size_t k = 0; k < N; k++) {
#ifdef XBYAK64 #ifdef XBYAK64
for (int kIdx = 0; kIdx < 8; kIdx++) { for (int kIdx = 0; kIdx < 8; kIdx++) {
put_vaddpd(xTbl[i], xTbl[j], xTbl[k], kIdx);
put_vaddpd(yTbl[i], yTbl[j], yTbl[k], kIdx);
for (int z = 0; z < 2; z++) { for (int z = 0; z < 2; z++) {
put_vaddpd(xTbl[i], xTbl[j], xTbl[k], kIdx, z == 1);
put_vaddpd(yTbl[i], yTbl[j], yTbl[k], kIdx, z == 1);
for (int sae = 0; sae < 5; sae++) { for (int sae = 0; sae < 5; sae++) {
put_vaddpd(zTbl[i], zTbl[j], zTbl[k], kIdx, z == 1, sae); put_vaddpd(zTbl[i], zTbl[j], zTbl[k], kIdx, z == 1, sae);
} }

View file

@ -97,13 +97,43 @@ CYBOZU_TEST_AUTO(align)
CYBOZU_TEST_EQUAL(size_t(getCurr()) % alignSize, 0u); CYBOZU_TEST_EQUAL(size_t(getCurr()) % alignSize, 0u);
} }
align(alignSize); align(alignSize);
const uint8 *p = getCurr(); const uint8_t *p = getCurr();
// do nothing if aligned // do nothing if aligned
align(alignSize); align(alignSize);
CYBOZU_TEST_EQUAL(p, getCurr()); CYBOZU_TEST_EQUAL(p, getCurr());
} }
} c; } c;
} }
CYBOZU_TEST_AUTO(kmask)
{
struct Code : Xbyak::CodeGenerator {
Code()
{
CYBOZU_TEST_EXCEPTION(kmovb(k1, ax), std::exception);
CYBOZU_TEST_EXCEPTION(kmovw(k1, ax), std::exception);
CYBOZU_TEST_EXCEPTION(kmovd(k1, ax), std::exception);
CYBOZU_TEST_EXCEPTION(kmovq(k1, eax), std::exception);
#ifdef XBYAK64
CYBOZU_TEST_EXCEPTION(kmovb(k1, rax), std::exception);
CYBOZU_TEST_EXCEPTION(kmovw(k1, rax), std::exception);
CYBOZU_TEST_EXCEPTION(kmovd(k1, rax), std::exception);
CYBOZU_TEST_NO_EXCEPTION(kmovq(k1, rax));
#endif
CYBOZU_TEST_NO_EXCEPTION(vmovaps(xm0|k0, ptr[eax]));
checkT_z();
}
void checkT_z()
{
const uint8_t *p1 = getCurr();
vmovaps(zm0, ptr[eax]);
const uint8_t *p2 = getCurr();
vmovaps(zm0|T_z, ptr[eax]);
const uint8_t *end = getCurr();
CYBOZU_TEST_EQUAL(p2 - p1, end - p2);
CYBOZU_TEST_EQUAL_ARRAY(p1, p2, end - p2);
}
} c;
}
#ifdef XBYAK64 #ifdef XBYAK64
CYBOZU_TEST_AUTO(vfmaddps) CYBOZU_TEST_AUTO(vfmaddps)
@ -683,4 +713,106 @@ CYBOZU_TEST_AUTO(gf2)
CYBOZU_TEST_EQUAL(c.getSize(), n); CYBOZU_TEST_EQUAL(c.getSize(), n);
CYBOZU_TEST_EQUAL_ARRAY(c.getCode(), tbl, n); CYBOZU_TEST_EQUAL_ARRAY(c.getCode(), tbl, n);
} }
CYBOZU_TEST_AUTO(bf16)
{
struct Code : Xbyak::CodeGenerator {
Code()
{
vcvtne2ps2bf16(xmm0 | k1, xmm1, ptr [rax + 64]);
vcvtne2ps2bf16(ymm0 | k1 | T_z, ymm0, ptr [rax + 64]);
vcvtne2ps2bf16(zmm0 | k1, zmm1, ptr [rax + 64]);
vcvtneps2bf16(xmm0, xword [rax + 64]);
vcvtneps2bf16(xmm0 | k1, yword [rax + 64]);
vcvtneps2bf16(ymm0 | k1, zword [rax + 64]);
vcvtneps2bf16(ymm0 | k1, ptr [rax + 64]);
vdpbf16ps(xmm0 | k1, xmm1, ptr [rax + 64]);
vdpbf16ps(ymm0 | k1, ymm1, ptr [rax + 64]);
vdpbf16ps(zmm0 | k1, zmm1, ptr [rax + 64]);
}
} c;
const uint8_t tbl[] = {
0x62, 0xf2, 0x77, 0x09, 0x72, 0x40, 0x04,
0x62, 0xf2, 0x7f, 0xa9, 0x72, 0x40, 0x02,
0x62, 0xf2, 0x77, 0x49, 0x72, 0x40, 0x01,
0x62, 0xf2, 0x7e, 0x08, 0x72, 0x40, 0x04,
0x62, 0xf2, 0x7e, 0x29, 0x72, 0x40, 0x02,
0x62, 0xf2, 0x7e, 0x49, 0x72, 0x40, 0x01,
0x62, 0xf2, 0x7e, 0x49, 0x72, 0x40, 0x01,
0x62, 0xf2, 0x76, 0x09, 0x52, 0x40, 0x04,
0x62, 0xf2, 0x76, 0x29, 0x52, 0x40, 0x02,
0x62, 0xf2, 0x76, 0x49, 0x52, 0x40, 0x01,
};
const size_t n = sizeof(tbl) / sizeof(tbl[0]);
CYBOZU_TEST_EQUAL(c.getSize(), n);
CYBOZU_TEST_EQUAL_ARRAY(c.getCode(), tbl, n);
}
CYBOZU_TEST_AUTO(AMX)
{
struct Code : Xbyak::CodeGenerator {
Code()
{
ldtilecfg(ptr[rax + rcx * 4 + 64]);
sttilecfg(ptr[rsp + rax * 8 + 128]);
tileloadd(tmm3, ptr[rdi + rdx * 2 + 8]);
tileloaddt1(tmm4, ptr[r8 + r9 + 32]);
tilerelease();
tilestored(ptr[r10 + r11 * 2 + 32], tmm2);
tilezero(tmm7);
tdpbssd(tmm1, tmm2, tmm3);
tdpbsud(tmm2, tmm3, tmm4);
tdpbusd(tmm3, tmm4, tmm5);
tdpbuud(tmm4, tmm5, tmm6);
tdpbf16ps(tmm5, tmm6, tmm7);
}
} c;
// generated code by patch
const uint8_t tbl[] = {
0xc4, 0xe2, 0x78, 0x49, 0x44, 0x88, 0x40, 0xc4, 0xe2, 0x79, 0x49, 0x84, 0xc4, 0x80, 0x00, 0x00,
0x00, 0xc4, 0xe2, 0x7b, 0x4b, 0x5c, 0x57, 0x08, 0xc4, 0x82, 0x79, 0x4b, 0x64, 0x08, 0x20, 0xc4,
0xe2, 0x78, 0x49, 0xc0, 0xc4, 0x82, 0x7a, 0x4b, 0x54, 0x5a, 0x20, 0xc4, 0xe2, 0x7b, 0x49, 0xf8,
0xc4, 0xe2, 0x63, 0x5e, 0xca, 0xc4, 0xe2, 0x5a, 0x5e, 0xd3, 0xc4, 0xe2, 0x51, 0x5e, 0xdc, 0xc4,
0xe2, 0x48, 0x5e, 0xe5, 0xc4, 0xe2, 0x42, 0x5c, 0xee,
};
const size_t n = sizeof(tbl) / sizeof(tbl[0]);
CYBOZU_TEST_EQUAL(c.getSize(), n);
CYBOZU_TEST_EQUAL_ARRAY(c.getCode(), tbl, n);
}
CYBOZU_TEST_AUTO(tileloadd)
{
struct Code : Xbyak::CodeGenerator {
Code()
{
tileloadd(tmm1, ptr[r8+r8]);
tileloadd(tmm1, ptr[rax+rcx*4]);
tileloadd(tmm1, ptr[r8+r9*1+0x40]);
}
void notSupported()
{
tileloadd(tmm1, ptr[r8]);
}
void notSupported2()
{
tileloadd(tmm1, ptr[r8*2]);
}
} c;
const uint8_t tbl[] = {
0xC4, 0x82, 0x7B, 0x4B, 0x0C, 0x00,
0xC4, 0xE2, 0x7B, 0x4B, 0x0C, 0x88,
0xC4, 0x82, 0x7B, 0x4B, 0x4C, 0x08, 0x40,
};
const size_t n = sizeof(tbl) / sizeof(tbl[0]);
CYBOZU_TEST_EQUAL(c.getSize(), n);
CYBOZU_TEST_EQUAL_ARRAY(c.getCode(), tbl, n);
// current version does not support this sibmem format
CYBOZU_TEST_EXCEPTION(c.notSupported(), std::exception);
CYBOZU_TEST_EXCEPTION(c.notSupported2(), std::exception);
}
#endif #endif

View file

@ -1,7 +1,8 @@
#include <stdio.h> #include <stdio.h>
#define XBYAK_NO_OP_NAMES
#define XBYAK_ENABLE_OMITTED_OPERAND #define XBYAK_ENABLE_OMITTED_OPERAND
#include "xbyak/xbyak.h" #include "xbyak/xbyak.h"
#define CYBOZU_TEST_DISABLE_AUTO_RUN
#include "cybozu/test.hpp"
using namespace Xbyak; using namespace Xbyak;
@ -15,39 +16,27 @@ public:
#include "nm.cpp" #include "nm.cpp"
}; };
#define _STR(x) #x
#define TEST(syntax) err = true; try { syntax; err = false; } catch (Xbyak::Error) { } catch (...) { } if (!err) printf("should be err:%s;\n", _STR(syntax))
class ErrorSample : public CodeGenerator { class ErrorSample : public CodeGenerator {
void operator=(const ErrorSample&); void operator=(const ErrorSample&);
public: public:
void gen() void gen()
{ {
bool err; #ifndef XBYAK_NO_EXCEPTION
TEST(mov(ptr[eax],1)); CYBOZU_TEST_EXCEPTION(mov(ptr[eax],1), std::exception);
TEST(test(ptr[eax],1)); CYBOZU_TEST_EXCEPTION(test(ptr[eax],1), std::exception);
TEST(adc(ptr[eax],1)); CYBOZU_TEST_EXCEPTION(adc(ptr[eax],1), std::exception);
TEST(setz(eax)); CYBOZU_TEST_EXCEPTION(setz(eax), std::exception);
#endif
} }
}; };
int main() int main()
try
{ {
size_t size = sizeof(Xbyak::Operand); // the size of Operand exceeds 32 bit.
if (size != 4) { CYBOZU_TEST_EQUAL(sizeof(Xbyak::Operand), 8u);
printf("sizeof Operand %d\n", (int)size); Sample s;
} s.gen();
try {
Sample s;
s.gen();
} catch (std::exception& e) {
printf("ERR:%s\n", e.what());
} catch (...) {
printf("unknown error\n");
}
ErrorSample es; ErrorSample es;
es.gen(); es.gen();
} catch (std::exception& e) {
printf("err %s\n", e.what());
return 1;
} }

View file

@ -0,0 +1,111 @@
#define XBYAK_NO_EXCEPTION
#include <xbyak/xbyak.h>
using namespace Xbyak;
int g_err = 0;
int g_test = 0;
void assertEq(int x, int y)
{
if (x != y) {
printf("ERR x=%d y=%d\n", x, y);
g_err++;
}
g_test++;
}
void assertBool(bool b)
{
if (!b) {
printf("ERR assertBool\n");
g_err++;
}
g_test++;
}
void test1()
{
const int v = 123;
struct Code : CodeGenerator {
Code()
{
mov(eax, v);
ret();
}
} c;
int (*f)() = c.getCode<int (*)()>();
assertEq(f(), v);
assertEq(Xbyak::GetError(), ERR_NONE);
}
void test2()
{
struct Code : CodeGenerator {
Code()
{
Label lp;
L(lp);
L(lp);
}
} c;
assertEq(Xbyak::GetError(), ERR_LABEL_IS_REDEFINED);
Xbyak::ClearError();
}
void test3()
{
static struct EmptyAllocator : Xbyak::Allocator {
uint8_t *alloc() { return 0; }
} emptyAllocator;
struct Code : CodeGenerator {
Code() : CodeGenerator(8, 0, &emptyAllocator)
{
mov(eax, 3);
assertBool(Xbyak::GetError() == 0);
mov(eax, 3);
mov(eax, 3);
assertBool(Xbyak::GetError() != 0);
Xbyak::ClearError();
assertBool(Xbyak::GetError() == 0);
}
} c;
}
void test4()
{
struct Code : CodeGenerator {
Code()
{
mov(ptr[eax], 1);
assertBool(Xbyak::GetError() != 0);
Xbyak::ClearError();
test(ptr[eax], 1);
assertBool(Xbyak::GetError() != 0);
Xbyak::ClearError();
adc(ptr[eax], 1);
assertBool(Xbyak::GetError() != 0);
Xbyak::ClearError();
setz(eax);
assertBool(Xbyak::GetError() != 0);
Xbyak::ClearError();
}
};
}
int main()
{
test1();
test2();
test3();
test4();
if (g_err) {
printf("err %d/%d\n", g_err, g_test);
} else {
printf("all ok %d\n", g_test);
}
return g_err != 0;
}

View file

@ -6,7 +6,7 @@
#include <iostream> #include <iostream>
#include <memory.h> #include <memory.h>
typedef unsigned char uint8; typedef unsigned char uint8_t;
std::string normalize(const std::string& line) std::string normalize(const std::string& line)
{ {

View file

@ -0,0 +1,2 @@
@echo off
set OPT=/EHsc -I../xbyak -I./ /W4 -D_CRT_SECURE_NO_WARNINGS /nologo

View file

@ -218,7 +218,7 @@ void check(int x, int y)
} }
} }
void verify(const Xbyak::uint8 *f, int pNum) void verify(const Xbyak::uint8_t *f, int pNum)
{ {
switch (pNum) { switch (pNum) {
case 0: case 0:
@ -264,7 +264,7 @@ void testAll()
} }
for (int tNum = 0; tNum < maxNum; tNum++) { for (int tNum = 0; tNum < maxNum; tNum++) {
// printf("pNum=%d, tNum=%d, stackSize=%d\n", pNum, tNum | opt, stackSize); // printf("pNum=%d, tNum=%d, stackSize=%d\n", pNum, tNum | opt, stackSize);
const Xbyak::uint8 *f = code.getCurr(); const Xbyak::uint8_t *f = code.getCurr();
code.gen(pNum, tNum | opt, stackSize); code.gen(pNum, tNum | opt, stackSize);
verify(f, pNum); verify(f, pNum);
/* /*

View file

@ -20,7 +20,6 @@ echo "compile nm_frame.cpp"
g++ $CFLAGS -DXBYAK_TEST nm_frame.cpp -o nm_frame g++ $CFLAGS -DXBYAK_TEST nm_frame.cpp -o nm_frame
./nm_frame > x.lst ./nm_frame > x.lst
diff ok.lst x.lst && echo "ok" diff ok.lst x.lst && echo "ok"
wc x.lst
} }

View file

@ -33,12 +33,11 @@ g++ $CFLAGS make_nm.cpp -o make_nm
./make_nm > a.asm ./make_nm > a.asm
echo "asm" echo "asm"
$EXE -f$OPT3 a.asm -l a.lst $EXE -f$OPT3 a.asm -l a.lst
awk '{if (index($3, "-")) { conti=substr($3, 0, length($3) - 1) } else { conti = conti $3; print conti; conti = "" }} ' < a.lst | $FILTER > ok.lst awk '{if (index($3, "-")) { conti=substr($3, 0, length($3) - 1) } else { conti = conti $3; print conti; conti = "" }} ' < a.lst | $FILTER | grep -v "1+1" > ok.lst
echo "xbyak" echo "xbyak"
./make_nm jit > nm.cpp ./make_nm jit > nm.cpp
echo "compile nm_frame.cpp" echo "compile nm_frame.cpp"
g++ $CFLAGS -DXBYAK_TEST nm_frame.cpp -o nm_frame g++ $CFLAGS -DXBYAK_TEST nm_frame.cpp -o nm_frame
./nm_frame | $FILTER > x.lst ./nm_frame | $FILTER > x.lst
diff ok.lst x.lst && echo "ok" diff -B ok.lst x.lst && echo "ok"
exit 0

View file

@ -29,5 +29,4 @@ echo "xbyak"
echo "compile nm_frame.cpp" echo "compile nm_frame.cpp"
g++ $CFLAGS -DXBYAK_TEST nm_frame.cpp -o nm_frame -DXBYAK_AVX512 g++ $CFLAGS -DXBYAK_TEST nm_frame.cpp -o nm_frame -DXBYAK_AVX512
./nm_frame | $FILTER > x.lst ./nm_frame | $FILTER > x.lst
diff ok.lst x.lst && echo "ok" diff -B ok.lst x.lst && echo "ok"
exit 0

View file

@ -7,3 +7,7 @@ echo ** yasm-avx(32bit) ***
call test_avx Y call test_avx Y
echo ** yasm-avx(64bit) *** echo ** yasm-avx(64bit) ***
call test_avx Y64 call test_avx Y64
echo ** nasm-avx512(32bit) ***
call test_avx512
echo ** nasm-avx512(64bit) ***
call test_avx512 64

View file

@ -17,6 +17,10 @@ if /i "%1"=="Y" (
set OPT2=-DUSE_YASM -DXBYAK64 set OPT2=-DUSE_YASM -DXBYAK64
set OPT3=win64 set OPT3=win64
set FILTER=normalize_prefix set FILTER=normalize_prefix
) else if /i "%1"=="noexcept" (
set EXE=nasm.exe
set OPT2=-DXBYAK32 -DXBYAK_NO_EXCEPTION
set OPT3=win32
) else ( ) else (
set EXE=nasm.exe set EXE=nasm.exe
set OPT2=-DXBYAK32 set OPT2=-DXBYAK32
@ -27,7 +31,7 @@ bmake -f Makefile.win all
echo cl -I../ make_nm.cpp %OPT% %OPT2% /EHs echo cl -I../ make_nm.cpp %OPT% %OPT2% /EHs
cl -I../ make_nm.cpp %OPT% %OPT2% /EHs cl -I../ make_nm.cpp %OPT% %OPT2% /EHs
make_nm > a.asm make_nm > a.asm
rm a.lst rm -rf a.lst
echo %EXE% -f %OPT3% -l a.lst a.asm echo %EXE% -f %OPT3% -l a.lst a.asm
%EXE% -f %OPT3% -l a.lst a.asm %EXE% -f %OPT3% -l a.lst a.asm
rem connect "?????-" and "??" rem connect "?????-" and "??"
@ -39,5 +43,4 @@ if /i "%Y%"=="1" (
make_nm jit > nm.cpp make_nm jit > nm.cpp
cl -I../ -DXBYAK_TEST nm_frame.cpp %OPT% %OPT2% cl -I../ -DXBYAK_TEST nm_frame.cpp %OPT% %OPT2%
nm_frame |%FILTER% > x.lst nm_frame |%FILTER% > x.lst
diff -w x.lst ok.lst diff -wb x.lst ok.lst && echo "ok"
wc x.lst

Some files were not shown because too many files have changed in this diff Show more