Merge branch 'upstream-diy' into diy-mpi-nompi

* upstream-diy:
  diy 2020-06-05 (b62915aa)
This commit is contained in:
Sujin Philip 2020-06-08 15:55:58 -05:00
commit f0a37ac6aa
89 changed files with 15780 additions and 6591 deletions

@ -0,0 +1,257 @@
#=============================================================================
# Adds the following DIY library targets:
# 1. diy: The main diy interface library and the only target for
# header-only mode.
# 2. diympi: Generated when `build_diy_mpi_lib` and `mpi` are turned on.
# Isolates the MPI dependent part of diy into a library.
# 3. diympi_nompi: Generated when `build_diy_mpi_lib` is on and either `mpi`
# is off or `build_diy_nompi_lib` is on.
#
# Both mpi and non-mpi libraries can be generated by turning on `build_diy_mpi_lib`
# and `build_diy_nompi_lib`. In this case, one of these targets must be explicitly
# specified when linking againts diy.
#=============================================================================
project (DIY)
cmake_minimum_required (VERSION 3.9)
list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
include(CMakeDependentOption)
# Provides an option if it is not already defined.
# This can be replaced when CMake 3.13 is our cmake_minimum_required
macro (diy_option variable)
if (NOT DEFINED "${variable}")
option("${variable}" ${ARGN})
endif ()
endmacro ()
macro (diy_dependent_option variable)
if (NOT DEFINED "${variable}")
cmake_dependent_option("${variable}" ${ARGN})
endif ()
endmacro ()
diy_option (threads "Build DIY with threading" ON)
diy_option (log "Build DIY with logging" OFF)
diy_option (profile "Build DIY with profiling" OFF)
diy_option (caliper "Build DIY with caliper" OFF)
diy_option (mpi "Build DIY with mpi" ON)
diy_option (wrapped_mpi "MPI compiler wrapper requires no further MPI libraries" OFF)
diy_option (build_diy_mpi_lib "Build diy::mpi as a library" OFF)
diy_dependent_option (BUILD_SHARED_LIBS "Create shared libraries if on" ON "build_diy_mpi_lib" OFF)
diy_dependent_option (build_diy_nompi_lib "Also build the nompi version of diy::mpi" OFF "mpi;build_diy_mpi_lib" OFF)
diy_option (build_examples "Build DIY examples" ON)
diy_option (build_tests "Build DIY tests" ON)
# Default to Release
if (NOT CMAKE_BUILD_TYPE)
set (CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
set_property (CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif (NOT CMAKE_BUILD_TYPE)
set (diy_definitions "")
set (diy_include_directories "")
set (diy_include_thirdparty_directories "")
set (diy_libraries "")
# Debugging
if (${CMAKE_BUILD_TYPE} STREQUAL "Debug" OR
${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
list (APPEND diy_definitions "-DDEBUG")
endif ()
# Logging
if (log)
list (APPEND diy_definitions "-DVTKMDIY_USE_SPDLOG")
find_path (SPDLOG_INCLUDE_DIR spdlog/spdlog.h)
list (APPEND diy_include_thirdparty_directories $<BUILD_INTERFACE:${SPDLOG_INCLUDE_DIR}>)
endif()
# Profiling
if (profile)
list (APPEND diy_definitions "-DVTKMDIY_PROFILE")
endif()
if (caliper)
list (APPEND diy_definitions "-DVTKMDIY_USE_CALIPER")
find_package (caliper)
list (APPEND diy_include_thirdparty_directories $<BUILD_INTERFACE:${caliper_INCLUDE_DIR}>)
list (APPEND diy_libraries caliper caliper-mpi)
endif()
# Threads
if (NOT threads)
list (APPEND diy_definitions "-DVTKMDIY_NO_THREADS")
else (NOT threads)
find_package (Threads)
list (APPEND diy_libraries ${CMAKE_THREAD_LIBS_INIT})
endif (NOT threads)
# MPI
if (mpi AND NOT wrapped_mpi)
find_package(MPI REQUIRED)
endif()
# configuration variables for diy build and install
# if diy is a sub-project, the following variables allow the parent project to
# easily customize the library
if (NOT DEFINED diy_prefix)
set(diy_prefix "diy")
endif()
if (NOT DEFINED diy_install_include_dir)
set(diy_install_include_dir "include")
endif()
if (NOT DEFINED diy_install_lib_dir)
set(diy_install_lib_dir "lib")
endif()
if (NOT DEFINED diy_export_name)
set(diy_export_name "diy_targets")
endif()
set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
# for diy_developer_flags
include(DIYCompilerFlags)
function(add_diy_mpi_library use_mpi)
set (sources
"include/${diy_prefix}/mpi/collectives.cpp"
"include/${diy_prefix}/mpi/communicator.cpp"
"include/${diy_prefix}/mpi/datatypes.cpp"
"include/${diy_prefix}/mpi/environment.cpp"
"include/${diy_prefix}/mpi/io.cpp"
"include/${diy_prefix}/mpi/operations.cpp"
"include/${diy_prefix}/mpi/point-to-point.cpp"
"include/${diy_prefix}/mpi/request.cpp"
"include/${diy_prefix}/mpi/status.cpp"
"include/${diy_prefix}/mpi/window.cpp")
if (use_mpi)
set (lib_name ${diy_prefix}mpi)
set (has_mpi_val 1)
else()
set (lib_name ${diy_prefix}mpi_nompi)
set (has_mpi_val 0)
endif()
add_library(${lib_name} ${sources})
target_compile_features(${lib_name} PRIVATE cxx_std_11)
target_compile_definitions(${lib_name}
PRIVATE -DVTKMDIY_HAS_MPI=${has_mpi_val}
PRIVATE -Ddiy=${diy_prefix} # mangle diy namespace
PRIVATE ${diy_definitions})
target_include_directories(${lib_name} SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/include) # for types.hpp
target_include_directories(${lib_name} SYSTEM PRIVATE ${diy_include_directories}) # for mpitypes.hpp
target_include_directories(${lib_name} SYSTEM PRIVATE ${diy_include_thirdparty_directories})
target_link_libraries(${lib_name} PRIVATE diy_developer_flags)
if (use_mpi AND TARGET MPI::MPI_CXX)
target_link_libraries(${lib_name} PRIVATE MPI::MPI_CXX)
endif()
endfunction()
# create the targets
set (diy_targets)
if (build_diy_mpi_lib)
include(DIYConfigureMPI)
# To be interchangeable, these libraries should only have PRIVATE properties.
# Properties that should be public should also be part of the core diy target.
list(APPEND diy_definitions -DVTKMDIY_MPI_AS_LIB)
list(APPEND diy_include_directories
"$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/${diy_prefix}/mpi>"
"$<INSTALL_INTERFACE:${diy_install_include_dir}/${diy_prefix}/mpi>")
# macro required for proper export macros for static vs shared builds
if (NOT BUILD_SHARED_LIBS)
list(APPEND diy_definitions -DVTKMDIY_MPI_STATIC_BUILD)
endif()
if (mpi)
add_diy_mpi_library(ON)
list(APPEND diy_targets ${diy_prefix}mpi)
endif()
if ((NOT mpi) OR build_diy_nompi_lib)
add_diy_mpi_library(OFF)
list(APPEND diy_targets ${diy_prefix}mpi_nompi)
endif()
endif() # build_diy_mpi_lib
add_library(${diy_prefix} INTERFACE)
target_compile_features(${diy_prefix} INTERFACE cxx_std_11)
target_compile_definitions(${diy_prefix} INTERFACE ${diy_definitions})
target_include_directories(${diy_prefix} SYSTEM INTERFACE
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${diy_install_include_dir}>")
target_include_directories(${diy_prefix} SYSTEM INTERFACE ${diy_include_thirdparty_directories})
if (diy_include_directories)
target_include_directories(${diy_prefix} SYSTEM INTERFACE ${diy_include_directories})
endif()
target_link_libraries(${diy_prefix} INTERFACE ${diy_libraries})
if (NOT build_diy_mpi_lib)
if (mpi)
target_compile_definitions(${diy_prefix} INTERFACE -DVTKMDIY_HAS_MPI=1)
if (TARGET MPI::MPI_CXX)
target_link_libraries(${diy_prefix} INTERFACE MPI::MPI_CXX)
endif()
else()
target_compile_definitions(${diy_prefix} INTERFACE -DVTKMDIY_HAS_MPI=0)
endif()
elseif (NOT build_diy_nompi_lib)
if (mpi)
target_link_libraries(${diy_prefix} INTERFACE ${diy_prefix}mpi)
else()
target_link_libraries(${diy_prefix} INTERFACE ${diy_prefix}mpi_nompi)
endif()
endif()
list(APPEND diy_targets ${diy_prefix} diy_developer_flags)
# libraries used by examples and tests
set(libraries ${diy_prefix})
if (${diy_prefix}mpi IN_LIST diy_targets)
list(APPEND libraries ${diy_prefix}mpi)
elseif (${diy_prefix}mpi_nompi IN_LIST diy_targets)
list(APPEND libraries ${diy_prefix}mpi_nompi)
endif()
list(APPEND libraries diy_developer_flags)
# enable testing and CDash dashboard submission
enable_testing ()
include (CTest)
if (build_examples)
add_subdirectory (examples)
endif (build_examples)
if (build_tests)
add_subdirectory (tests)
endif (build_tests)
# configure find_package script
include(CMakePackageConfigHelpers)
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/diy-config.cmake.in"
"${PROJECT_BINARY_DIR}/diy-config.cmake"
INSTALL_DESTINATION ".")
# install targets
if (NOT DEFINED diy_install_only_libraries) # defined by parent project if building for binary distribution
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/${diy_prefix} DESTINATION ${diy_install_include_dir})
if (build_diy_mpi_lib)
install(FILES ${PROJECT_BINARY_DIR}/include/${diy_prefix}/mpi/mpitypes.hpp DESTINATION ${diy_install_include_dir}/${diy_prefix}/mpi)
endif()
endif()
install(TARGETS ${diy_targets} EXPORT ${diy_export_name} DESTINATION ${diy_install_lib_dir})
export(EXPORT ${diy_export_name} NAMESPACE DIY:: FILE "${PROJECT_BINARY_DIR}/diy-targets.cmake")
if (CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) # Only generate these files when diy is the main project
install(EXPORT ${diy_export_name} NAMESPACE DIY:: DESTINATION "." FILE diy-targets.cmake)
install(FILES "${PROJECT_BINARY_DIR}/diy-config.cmake" DESTINATION ".")
endif()

@ -0,0 +1,76 @@
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR
CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
set(DIY_COMPILER_IS_MSVC 1)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "PGI")
set(DIY_COMPILER_IS_PGI 1)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
set(DIY_COMPILER_IS_ICC 1)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set(DIY_COMPILER_IS_CLANG 1)
set(DIY_COMPILER_IS_APPLECLANG 1)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(DIY_COMPILER_IS_CLANG 1)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(DIY_COMPILER_IS_GNU 1)
endif()
#-----------------------------------------------------------------------------
add_library(diy_developer_flags INTERFACE)
if(DIY_COMPILER_IS_MSVC)
target_compile_definitions(diy_developer_flags INTERFACE
"_SCL_SECURE_NO_WARNINGS" "_CRT_SECURE_NO_WARNINGS")
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.15)
set(cxx_flags "-W3")
endif()
#list(APPEND cxx_flags -wd4702 -wd4505)
if(MSVC_VERSION LESS 1900)
# In VS2013 the C4127 warning has a bug in the implementation and
# generates false positive warnings for lots of template code
#list(APPEND cxx_flags -wd4127)
endif()
target_compile_options(diy_developer_flags INTERFACE $<$<COMPILE_LANGUAGE:CXX>:${cxx_flags}>)
elseif(DIY_COMPILER_IS_ICC)
# dissable some false positive warnings
set(cxx_flags -wd186 -wd3280)
list(APPEND cxx_flags -diag-disable=11074 -diag-disable=11076)
#list(APPEND cxx_flags -wd1478 -wd 13379)
target_compile_options(diy_developer_flags INTERFACE $<$<COMPILE_LANGUAGE:CXX>:${cxx_flags}>)
elseif(DIY_COMPILER_IS_GNU OR DIY_COMPILER_IS_CLANG)
set(cxx_flags -Wall -Wcast-align -Wchar-subscripts -Wextra -Wpointer-arith -Wformat -Wformat-security -Wshadow -Wunused -fno-common)
#Only add float-conversion warnings for gcc as the integer warnigns in GCC
#include the implicit casting of all types smaller than int to ints.
if (DIY_COMPILER_IS_GNU AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.99)
list(APPEND cxx_flags -Wfloat-conversion)
elseif (DIY_COMPILER_IS_CLANG)
list(APPEND cxx_flags -Wconversion)
endif()
# TODO: remove after resolving these warnings
# temporarily disable the following warnings as we will need a well thought out plan for fixing these
list(APPEND cxx_flags -Wno-sign-conversion -Wno-sign-compare -Wno-cast-align)
#Add in the -Wodr warning for GCC versions 5.2+
if (DIY_COMPILER_IS_CLANG OR (DIY_COMPILER_IS_GNU AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.1))
list(APPEND cxx_flags -Wodr)
endif()
#GCC 5, 6 don't properly handle strict-overflow suppression through pragma's.
#Instead of suppressing around the location of the strict-overflow you
#have to suppress around the entry point, or in vtk-m case the worklet
#invocation site. This is incredibly tedious and has been fixed in gcc 7
#
if(DIY_COMPILER_IS_GNU AND
(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.99) AND
(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.99) )
list(APPEND cxx_flags -Wno-strict-overflow)
endif()
target_compile_options(diy_developer_flags INTERFACE $<$<COMPILE_LANGUAGE:CXX>:${cxx_flags}>)
endif()

@ -0,0 +1,51 @@
cmake_policy(PUSH)
if (POLICY CMP0075)
cmake_policy(SET CMP0075 NEW)
endif()
include (CheckTypeSize)
if (mpi)
find_package(MPI REQUIRED)
list(APPEND CMAKE_REQUIRED_INCLUDES ${MPI_CXX_INCLUDE_PATH})
list(APPEND CMAKE_EXTRA_INCLUDE_FILES "mpi.h")
list(APPEND CMAKE_REQUIRED_LIBRARIES ${MPI_CXX_LIBRARIES})
else()
list(APPEND CMAKE_REQUIRED_INCLUDES ${PROJECT_SOURCE_DIR}/include)
list(APPEND CMAKE_EXTRA_INCLUDE_FILES "${diy_prefix}/mpi/no-mpi.hpp")
endif()
list(APPEND CMAKE_REQUIRED_INCLUDES ${PROJECT_SOURCE_DIR}/cmake)
list(APPEND CMAKE_EXTRA_INCLUDE_FILES "mpi_types.h")
if (NOT (DEFINED CACHE{previous_mpi} AND ((previous_mpi AND mpi) OR (NOT(previous_mpi OR mpi)))))
unset(TYPESIZE_MPI_Comm CACHE)
unset(HAVE_TYPESIZE_MPI_Comm CACHE)
unset(TYPESIZE_MPI_Datatype CACHE)
unset(HAVE_TYPESIZE_MPI_Datatype CACHE)
unset(TYPESIZE_MPI_Status CACHE)
unset(HAVE_TYPESIZE_MPI_Status CACHE)
unset(TYPESIZE_MPI_Request CACHE)
unset(HAVE_TYPESIZE_MPI_Request CACHE)
unset(TYPESIZE_MPI_Op CACHE)
unset(HAVE_TYPESIZE_MPI_Op CACHE)
unset(TYPESIZE_MPI_File CACHE)
unset(HAVE_TYPESIZE_MPI_File CACHE)
unset(TYPESIZE_MPI_Win CACHE)
unset(HAVE_TYPESIZE_MPI_Win CACHE)
set (previous_mpi ${mpi} CACHE INTERNAL "" FORCE)
endif()
set(CMAKE_CXX_STANDARD 11)
check_type_size("Wrapped_MPI_Comm" TYPESIZE_MPI_Comm LANGUAGE CXX)
check_type_size("Wrapped_MPI_Datatype" TYPESIZE_MPI_Datatype LANGUAGE CXX)
check_type_size("Wrapped_MPI_Status" TYPESIZE_MPI_Status LANGUAGE CXX)
check_type_size("Wrapped_MPI_Request" TYPESIZE_MPI_Request LANGUAGE CXX)
check_type_size("Wrapped_MPI_Op" TYPESIZE_MPI_Op LANGUAGE CXX)
check_type_size("Wrapped_MPI_File" TYPESIZE_MPI_File LANGUAGE CXX)
check_type_size("Wrapped_MPI_Win" TYPESIZE_MPI_Win LANGUAGE CXX)
configure_file("include/${diy_prefix}/mpi/mpitypes.hpp.in" "include/${diy_prefix}/mpi/mpitypes.hpp" @ONLY)
cmake_policy(POP)

@ -0,0 +1,57 @@
if (CMAKE_VERSION VERSION_LESS "3.9")
message(FATAL_ERROR "Diy requires CMake 3.9+")
endif()
@PACKAGE_INIT@
set(threads "@threads@")
set(log "@log@")
set(caliper "@caliper@")
set(mpi "@mpi@")
include("${CMAKE_CURRENT_LIST_DIR}/diy-targets.cmake")
set(_diy_find_quietly)
if (${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
set(_diy_find_quietly QUIET)
endif()
if (threads)
find_package(Threads ${_diy_find_quietly})
if (NOT Threads_FOUND)
list(APPEND "${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE" "Threads not found")
set("${CMAKE_FIND_PACKAGE_NAME}_FOUND" 0)
endif()
endif()
if (log)
find_path(SPDLOG_INCLUDE_DIR "spdlog/spdlog.h")
if (SPDLOG_INCLUDE_DIR STREQUAL "SPDLOG_INCLUDE_DIR-NOTFOUND")
list(APPEND "${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE" "SPDLOG not found")
set("${CMAKE_FIND_PACKAGE_NAME}_FOUND" 0)
else()
target_include_directories(DIY::@diy_prefix@ INTERFACE $<INSTALL_INTERFACE:${SPDLOG_INCLUDE_DIR}>)
endif()
endif()
if (caliper)
find_package(caliper ${_diy_find_quietly})
if (NOT caliper_FOUND)
list(APPEND "${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE" "Caliper not found")
set("${CMAKE_FIND_PACKAGE_NAME}_FOUND" 0)
else()
target_include_directories(DIY::@diy_prefix@ INTERFACE $<INSTALL_INTERFACE:${caliper_INCLUDE_DIR}>)
endif()
endif()
if (mpi)
find_package(MPI ${_diy_find_quietly})
if (NOT MPI_FOUND)
list(APPEND "${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE" "MPI not found")
set("${CMAKE_FIND_PACKAGE_NAME}_FOUND" 0)
endif()
endif()
if (NOT DEFINED "${CMAKE_FIND_PACKAGE_NAME}_FOUND")
set("${CMAKE_FIND_PACKAGE_NAME}_FOUND" 1)
endif ()

@ -0,0 +1,9 @@
// Wrap the mpi types in structs as they can be macros in some implementations,
// causing `check_type_size` to fail.
struct Wrapped_MPI_Comm { MPI_Comm obj; };
struct Wrapped_MPI_Datatype { MPI_Datatype obj; };
struct Wrapped_MPI_Status { MPI_Status obj; };
struct Wrapped_MPI_Request { MPI_Request obj; };
struct Wrapped_MPI_Op { MPI_Op obj; };
struct Wrapped_MPI_File { MPI_File obj; };
struct Wrapped_MPI_Win { MPI_Win obj; };

@ -86,7 +86,7 @@ namespace diy
typedef diy::RegularContinuousLink RCLink;
for (size_t i = 0; i < master.size(); ++i)
for (int i = 0; i < static_cast<int>(master.size()); ++i)
{
RCLink* link = static_cast<RCLink*>(master.link(i));
*link = RCLink(dim, domain, domain);
@ -96,7 +96,7 @@ namespace diy
diy::BlockID self = { master.gid(i), master.communicator().rank() };
for (int j = 0; j < dim; ++j)
{
diy::Direction dir, wrap_dir;
diy::Direction dir(dim,0), wrap_dir(dim,0);
// left
dir[j] = -1; wrap_dir[j] = -1;
@ -122,7 +122,7 @@ namespace diy
// update master.expected to match the links
int expected = 0;
for (size_t i = 0; i < master.size(); ++i)
for (int i = 0; i < static_cast<int>(master.size()); ++i)
expected += master.link(i)->size_unique();
master.set_expected(expected);
}
@ -146,7 +146,7 @@ namespace diy
typedef diy::RegularContinuousLink RCLink;
for (size_t i = 0; i < master.size(); ++i)
for (int i = 0; i < static_cast<int>(master.size()); ++i)
{
RCLink* link = static_cast<RCLink*>(master.link(i));
*link = RCLink(dim, domain, domain);
@ -156,7 +156,7 @@ namespace diy
diy::BlockID self = { master.gid(i), master.communicator().rank() };
for (int j = 0; j < dim; ++j)
{
diy::Direction dir, wrap_dir;
diy::Direction dir(dim,0), wrap_dir(dim,0);
// left
dir[j] = -1; wrap_dir[j] = -1;
@ -182,7 +182,7 @@ namespace diy
// update master.expected to match the links
int expected = 0;
for (size_t i = 0; i < master.size(); ++i)
for (int i = 0; i < static_cast<int>(master.size()); ++i)
expected += master.link(i)->size_unique();
master.set_expected(expected);
}

@ -22,6 +22,7 @@ namespace diy
int nblocks__ //!< total (global) number of blocks
):
size_(size__), nblocks_(nblocks__) {}
virtual ~Assigner() {}
//! returns the total number of process ranks
int size() const { return size_; }
@ -36,6 +37,7 @@ namespace diy
virtual std::vector<int>
ranks(const std::vector<int>& gids) const;
private:
int size_; // total number of ranks
int nblocks_; // total number of blocks
@ -107,7 +109,7 @@ namespace diy
Assigner(size__, nblocks__),
comm_(comm),
div_(nblocks__ / size__ + ((nblocks__ % size__) == 0 ? 0 : 1)), // NB: same size window everywhere means the last rank may allocate extra space
rank_map_(comm_, div_) { rank_map_.lock_all(MPI_MODE_NOCHECK); }
rank_map_(comm_, div_) { rank_map_.lock_all(mpi::nocheck); }
~DynamicAssigner() { rank_map_.unlock_all(); }
inline
@ -187,7 +189,7 @@ set_nblocks(int nblocks__)
rank_map_.unlock_all();
rank_map_ = mpi::window<int>(comm_, div_);
rank_map_.lock_all(MPI_MODE_NOCHECK);
rank_map_.lock_all(mpi::nocheck);
}
std::tuple<bool,int>

@ -1,26 +1,39 @@
#ifndef VTKMDIY_CONSTANTS_H
#define VTKMDIY_CONSTANTS_H
// Default DIY_MAX_DIM to 4, unless provided by the user
// Default VTKMDIY_MAX_DIM to 4, unless provided by the user
// (used for static min/max size in various Bounds)
#ifndef DIY_MAX_DIM
#define DIY_MAX_DIM 4
#ifndef VTKMDIY_MAX_DIM
#define VTKMDIY_MAX_DIM 4
#endif
enum
{
DIY_X0 = 0x01, /* minimum-side x (left) neighbor */
DIY_X1 = 0x02, /* maximum-side x (right) neighbor */
DIY_Y0 = 0x04, /* minimum-side y (bottom) neighbor */
DIY_Y1 = 0x08, /* maximum-side y (top) neighbor */
DIY_Z0 = 0x10, /* minimum-side z (back) neighbor */
DIY_Z1 = 0x20, /* maximum-side z (front)neighbor */
DIY_T0 = 0x40, /* minimum-side t (earlier) neighbor */
DIY_T1 = 0x80 /* maximum-side t (later) neighbor */
VTKMDIY_X0 = 0x01, /* minimum-side x (left) neighbor */
VTKMDIY_X1 = 0x02, /* maximum-side x (right) neighbor */
VTKMDIY_Y0 = 0x04, /* minimum-side y (bottom) neighbor */
VTKMDIY_Y1 = 0x08, /* maximum-side y (top) neighbor */
VTKMDIY_Z0 = 0x10, /* minimum-side z (back) neighbor */
VTKMDIY_Z1 = 0x20, /* maximum-side z (front)neighbor */
VTKMDIY_T0 = 0x40, /* minimum-side t (earlier) neighbor */
VTKMDIY_T1 = 0x80 /* maximum-side t (later) neighbor */
};
#ifndef DIY_UNUSED
#define DIY_UNUSED(expr) do { (void)(expr); } while (0)
#define VTKMDIY_UNUSED(expr) do { (void)(expr); } while (0)
// From https://stackoverflow.com/a/21265197/44738
#if defined(__cplusplus) && (__cplusplus >= 201402L)
# define DEPRECATED(msg) [[deprecated(#msg)]]
#else
# if defined(__GNUC__) || defined(__clang__)
# define DEPRECATED(msg) __attribute__((deprecated(#msg)))
# elif defined(_MSC_VER)
# define DEPRECATED(msg) __declspec(deprecated(#msg))
# else
# pragma message("WARNING: You need to implement DEPRECATED for this compiler")
# define DEPRECATED(msg)
# endif
#endif
#endif

@ -17,6 +17,9 @@ namespace diy
const T& operator*() const { return x_; }
const T* operator->() const { return &x_; }
void lock() { lock_.lock(); }
void unlock() { lock_.unlock(); }
private:
T& x_;
lock_guard<Mutex> lock_;
@ -33,6 +36,8 @@ namespace diy
critical_resource() {}
critical_resource(const T& x):
x_(x) {}
critical_resource(T&& x):
x_(std::move(x)) {}
accessor access() { return accessor(x_, m_); }
const_accessor const_access() const { return const_accessor(x_, m_); }

@ -63,8 +63,8 @@ namespace detail
static Coordinate from(int i, int n, Coordinate min, Coordinate max, bool) { return min + (max - min)/n * i; }
static Coordinate to (int i, int n, Coordinate min, Coordinate max, bool) { return min + (max - min)/n * (i+1); }
static int lower(Coordinate x, int n, Coordinate min, Coordinate max, bool) { Coordinate width = (max - min)/n; Coordinate res = std::floor((x - min)/width); if (min + res*width == x) return (res - 1); else return res; }
static int upper(Coordinate x, int n, Coordinate min, Coordinate max, bool) { Coordinate width = (max - min)/n; Coordinate res = std::ceil ((x - min)/width); if (min + res*width == x) return (res + 1); else return res; }
static int lower(Coordinate x, int n, Coordinate min, Coordinate max, bool) { Coordinate width = (max - min)/n; auto res = static_cast<int>(std::floor((x - min)/width)); if (min + res*width == x) return (res - 1); else return res; }
static int upper(Coordinate x, int n, Coordinate min, Coordinate max, bool) { Coordinate width = (max - min)/n; auto res = static_cast<int>(std::ceil ((x - min)/width)); if (min + res*width == x) return (res + 1); else return res; }
};
}
@ -74,9 +74,9 @@ namespace detail
template<class Bounds_>
struct RegularDecomposer
{
typedef Bounds_ Bounds;
typedef typename BoundsValue<Bounds>::type Coordinate;
typedef typename RegularLinkSelector<Bounds>::type Link;
using Bounds = Bounds_;
using Coordinate = typename BoundsValue<Bounds>::type;
using Link = RegularLink<Bounds>;
using Creator = std::function<void(int, Bounds, Bounds, Bounds, Link)>;
using Updater = std::function<void(int, int, Bounds, Bounds, Bounds, Link)>;
@ -123,6 +123,7 @@ namespace detail
template<class Point>
int lowest_gid(const Point& p) const;
DivisionsVector gid_to_coords(int gid) const { DivisionsVector coords; gid_to_coords(gid, coords); return coords; }
void gid_to_coords(int gid, DivisionsVector& coords) const { gid_to_coords(gid, coords, divisions); }
int coords_to_gid(const DivisionsVector& coords) const { return coords_to_gid(coords, divisions); }
void fill_divisions(std::vector<int>& divisions) const;
@ -131,8 +132,8 @@ namespace detail
void fill_bounds(Bounds& bounds, int gid, bool add_ghosts = false) const;
static bool all(const std::vector<int>& v, int x);
static void gid_to_coords(int gid, DivisionsVector& coords, const DivisionsVector& divisions);
static int coords_to_gid(const DivisionsVector& coords, const DivisionsVector& divisions);
static void gid_to_coords(int gid, DivisionsVector& coords, const DivisionsVector& divs);
static int coords_to_gid(const DivisionsVector& coords, const DivisionsVector& divs);
static void factor(std::vector<unsigned>& factors, int n);
@ -303,7 +304,7 @@ decompose(int rank, const StaticAssigner& assigner, const Creator& create)
DivisionsVector coords;
gid_to_coords(gid, coords);
Bounds core, bounds;
Bounds core(dim), bounds(dim);
fill_bounds(core, coords);
fill_bounds(bounds, coords, true);
@ -325,7 +326,7 @@ decompose(int rank, const StaticAssigner& assigner, const Creator& create)
if (all(offsets, 0)) continue; // skip ourselves
DivisionsVector nhbr_coords(dim);
Direction dir, wrap_dir;
Direction dir(dim,0), wrap_dir(dim,0);
bool inbounds = true;
for (int k = 0; k < dim; ++k)
{
@ -364,8 +365,12 @@ decompose(int rank, const StaticAssigner& assigner, const Creator& create)
BlockID bid; bid.gid = nhbr_gid; bid.proc = assigner.rank(nhbr_gid);
link.add_neighbor(bid);
Bounds nhbr_bounds;
fill_bounds(nhbr_bounds, nhbr_coords);
Bounds nhbr_core(dim);
fill_bounds(nhbr_core, nhbr_coords);
link.add_core(nhbr_core);
Bounds nhbr_bounds(dim);
fill_bounds(nhbr_bounds, nhbr_coords, true);
link.add_bounds(nhbr_bounds);
link.add_direction(dir);
@ -405,25 +410,25 @@ all(const std::vector<int>& v, int x)
template<class Bounds>
void
diy::RegularDecomposer<Bounds>::
gid_to_coords(int gid, DivisionsVector& coords, const DivisionsVector& divisions)
gid_to_coords(int gid, DivisionsVector& coords, const DivisionsVector& divs)
{
int dim = static_cast<int>(divisions.size());
for (int i = 0; i < dim; ++i)
coords.clear();
for (int i = 0; i < static_cast<int>(divs.size()); ++i)
{
coords.push_back(gid % divisions[i]);
gid /= divisions[i];
coords.push_back(gid % divs[i]);
gid /= divs[i];
}
}
template<class Bounds>
int
diy::RegularDecomposer<Bounds>::
coords_to_gid(const DivisionsVector& coords, const DivisionsVector& divisions)
coords_to_gid(const DivisionsVector& coords, const DivisionsVector& divs)
{
int gid = 0;
for (int i = static_cast<int>(coords.size()) - 1; i >= 0; --i)
{
gid *= divisions[i];
gid *= divs[i];
gid += coords[i];
}
return gid;
@ -445,12 +450,6 @@ fill_bounds(Bounds& bounds, //!< (output) bounds
bounds.max[i] = detail::BoundsHelper<Bounds>::to (coords[i], divisions[i], domain.min[i], domain.max[i], share_face[i]);
}
for (int i = dim; i < DIY_MAX_DIM; ++i) // set the unused dimension to 0
{
bounds.min[i] = 0;
bounds.max[i] = 0;
}
if (!add_ghosts)
return;
@ -554,8 +553,7 @@ fill_divisions(std::vector<int>& divisions_) const
}
// iterate over factorization of number of blocks (factors are sorted smallest to largest)
// NB: using int instead of size_t because must be negative in order to break out of loop
for (int i = factors.size() - 1; i >= 0; --i)
for (auto f = factors.rbegin(); f != factors.rend(); ++f)
{
// fill in missing divs by dividing dimension w/ largest block size
// except when this would be illegal (resulting in bounds.max < bounds.min;
@ -567,19 +565,19 @@ fill_divisions(std::vector<int>& divisions_) const
// split the dimension with the largest block size (first element in vector)
Coordinate min =
detail::BoundsHelper<Bounds>::from(0,
missing_divs[0].nb * factors[i],
missing_divs[0].nb * (*f),
domain.min[missing_divs[0].dim],
domain.max[missing_divs[0].dim],
share_face[missing_divs[0].dim]);
Coordinate max =
detail::BoundsHelper<Bounds>::to(0,
missing_divs[0].nb * factors[i],
missing_divs[0].nb * (*f),
domain.min[missing_divs[0].dim],
domain.max[missing_divs[0].dim],
share_face[missing_divs[0].dim]);
if (max >= min)
{
missing_divs[0].nb *= factors[i];
missing_divs[0].nb *= (*f);
missing_divs[0].b_size = max - min;
}
else

@ -74,7 +74,7 @@ operator()(Block* b, const diy::ReduceProxy& srp, const KDTreePartners& partners
dim = partners.dim(srp.round() - 1);
if (srp.round() == partners.rounds())
update_links(b, srp, dim, partners.sub_round(srp.round() - 2), partners.swap_rounds(), partners.wrap, partners.domain); // -1 would be the "uninformative" link round
update_links(b, srp, dim, partners.sub_round((int)srp.round() - 2), (int)partners.swap_rounds(), partners.wrap, partners.domain); // -1 would be the "uninformative" link round
else if (partners.swap_round(srp.round()) && partners.sub_round(srp.round()) < 0) // link round
{
dequeue_exchange(b, srp, dim); // from the swap round
@ -92,7 +92,7 @@ operator()(Block* b, const diy::ReduceProxy& srp, const KDTreePartners& partners
int prev_dim = dim - 1;
if (prev_dim < 0)
prev_dim += dim_;
update_links(b, srp, prev_dim, partners.sub_round(srp.round() - 2), partners.swap_rounds(), partners.wrap, partners.domain); // -1 would be the "uninformative" link round
update_links(b, srp, prev_dim, partners.sub_round((int)srp.round() - 2), (int)partners.swap_rounds(), partners.wrap, partners.domain); // -1 would be the "uninformative" link round
}
compute_local_samples(b, srp, dim);
@ -134,7 +134,7 @@ divide_gid(int gid, bool lower, int round, int rounds) const
template<class Block, class Point>
void
diy::detail::KDTreeSamplingPartition<Block,Point>::
update_links(Block* b, const diy::ReduceProxy& srp, int dim, int round, int rounds, bool wrap, const Bounds& domain) const
update_links(Block*, const diy::ReduceProxy& srp, int dim, int round, int rounds, bool wrap, const Bounds& domain) const
{
auto log = get_logger();
int gid = srp.gid();
@ -150,7 +150,7 @@ update_links(Block* b, const diy::ReduceProxy& srp, int dim, int round, int roun
std::vector<float> splits(link->size());
for (int i = 0; i < link->size(); ++i)
{
float split; diy::Direction dir;
float split; diy::Direction dir(dim_,0);
int in_gid = link->target(i).gid;
while(srp.incoming(in_gid))
@ -194,7 +194,7 @@ update_links(Block* b, const diy::ReduceProxy& srp, int dim, int round, int roun
if (wrap)
new_link.add_wrap(find_wrap(new_link.bounds(), bounds, domain));
else
new_link.add_wrap(diy::Direction());
new_link.add_wrap(diy::Direction(dim_,0));
}
} else // non-aligned side
{
@ -215,7 +215,7 @@ update_links(Block* b, const diy::ReduceProxy& srp, int dim, int round, int roun
if (wrap)
new_link.add_wrap(find_wrap(new_link.bounds(), bounds, domain));
else
new_link.add_wrap(diy::Direction());
new_link.add_wrap(diy::Direction(dim_,0));
}
}
}
@ -230,16 +230,16 @@ update_links(Block* b, const diy::ReduceProxy& srp, int dim, int round, int roun
update_neighbor_bounds(nbr_bounds, find_split(new_link.bounds(), nbr_bounds), dim, !lower);
new_link.add_bounds(nbr_bounds);
new_link.add_wrap(diy::Direction()); // dual block cannot be wrapped
new_link.add_wrap(diy::Direction(dim_,0)); // dual block cannot be wrapped
if (lower)
{
diy::Direction right;
diy::Direction right(dim_,0);
right[dim] = 1;
new_link.add_direction(right);
} else
{
diy::Direction left;
diy::Direction left(dim_,0);
left[dim] = -1;
new_link.add_direction(left);
}
@ -253,7 +253,7 @@ update_links(Block* b, const diy::ReduceProxy& srp, int dim, int round, int roun
template<class Block, class Point>
void
diy::detail::KDTreeSamplingPartition<Block,Point>::
split_to_neighbors(Block* b, const diy::ReduceProxy& srp, int dim) const
split_to_neighbors(Block*, const diy::ReduceProxy& srp, int) const
{
int lid = srp.master()->lid(srp.gid());
RCLink* link = static_cast<RCLink*>(srp.master()->link(lid));
@ -290,7 +290,7 @@ compute_local_samples(Block* b, const diy::ReduceProxy& srp, int dim) const
template<class Block, class Point>
void
diy::detail::KDTreeSamplingPartition<Block,Point>::
add_samples(Block* b, const diy::ReduceProxy& srp, Samples& samples) const
add_samples(Block*, const diy::ReduceProxy& srp, Samples& samples) const
{
// dequeue and combine the samples
for (int i = 0; i < srp.in_link().size(); ++i)
@ -307,7 +307,7 @@ add_samples(Block* b, const diy::ReduceProxy& srp, Samples& samples) const
template<class Block, class Point>
void
diy::detail::KDTreeSamplingPartition<Block,Point>::
receive_samples(Block* b, const diy::ReduceProxy& srp, Samples& samples) const
receive_samples(Block*, const diy::ReduceProxy& srp, Samples& samples) const
{
srp.dequeue(srp.in_link().target(0).gid, samples);
}
@ -315,7 +315,7 @@ receive_samples(Block* b, const diy::ReduceProxy& srp, Samples& samples) const
template<class Block, class Point>
void
diy::detail::KDTreeSamplingPartition<Block,Point>::
forward_samples(Block* b, const diy::ReduceProxy& srp, const Samples& samples) const
forward_samples(Block*, const diy::ReduceProxy& srp, const Samples& samples) const
{
for (int i = 0; i < srp.out_link().size(); ++i)
srp.enqueue(srp.out_link().target(i), samples);
@ -435,7 +435,7 @@ diy::Direction
diy::detail::KDTreeSamplingPartition<Block,Point>::
find_wrap(const Bounds& bounds, const Bounds& nbr_bounds, const Bounds& domain) const
{
diy::Direction wrap;
diy::Direction wrap(dim_,0);
for (int i = 0; i < dim_; ++i)
{
if (bounds.min[i] == domain.min[i] && nbr_bounds.max[i] == domain.max[i])

@ -68,10 +68,10 @@ struct diy::detail::KDTreePartners
wrap(wrap_),
domain(domain_)
{
for (unsigned i = 0; i < swap.rounds(); ++i)
for (int i = 0; i < swap.rounds(); ++i)
{
// fill histogram rounds
for (unsigned j = 0; j < histogram.rounds(); ++j)
for (int j = 0; j < histogram.rounds(); ++j)
{
rounds_.push_back(std::make_pair(false, j));
dim_.push_back(i % dim);
@ -115,7 +115,7 @@ struct diy::detail::KDTreePartners
else if (swap_round(round) && sub_round(round) < 0) // link round
swap.incoming(sub_round(round - 1) + 1, gid, partners, m);
else if (swap_round(round))
histogram.incoming(histogram.rounds(), gid, partners, m);
histogram.incoming(static_cast<int>(histogram.rounds()), gid, partners, m);
else
{
if (round > 0 && sub_round(round) == 0)
@ -177,7 +177,7 @@ operator()(Block* b, const diy::ReduceProxy& srp, const KDTreePartners& partners
dim = partners.dim(srp.round() - 1);
if (srp.round() == partners.rounds())
update_links(b, srp, dim, partners.sub_round(srp.round() - 2), partners.swap_rounds(), partners.wrap, partners.domain); // -1 would be the "uninformative" link round
update_links(b, srp, dim, partners.sub_round((int)srp.round() - 2), (int)partners.swap_rounds(), partners.wrap, partners.domain); // -1 would be the "uninformative" link round
else if (partners.swap_round(srp.round()) && partners.sub_round(srp.round()) < 0) // link round
{
dequeue_exchange(b, srp, dim); // from the swap round
@ -195,7 +195,7 @@ operator()(Block* b, const diy::ReduceProxy& srp, const KDTreePartners& partners
int prev_dim = dim - 1;
if (prev_dim < 0)
prev_dim += dim_;
update_links(b, srp, prev_dim, partners.sub_round(srp.round() - 2), partners.swap_rounds(), partners.wrap, partners.domain); // -1 would be the "uninformative" link round
update_links(b, srp, prev_dim, partners.sub_round((int)srp.round() - 2), (int)partners.swap_rounds(), partners.wrap, partners.domain); // -1 would be the "uninformative" link round
}
compute_local_histogram(b, srp, dim);
@ -229,7 +229,7 @@ divide_gid(int gid, bool lower, int round, int rounds) const
template<class Block, class Point>
void
diy::detail::KDTreePartition<Block,Point>::
update_links(Block* b, const diy::ReduceProxy& srp, int dim, int round, int rounds, bool wrap, const Bounds& domain) const
update_links(Block*, const diy::ReduceProxy& srp, int dim, int round, int rounds, bool wrap, const Bounds& domain) const
{
int gid = srp.gid();
int lid = srp.master()->lid(gid);
@ -244,7 +244,7 @@ update_links(Block* b, const diy::ReduceProxy& srp, int dim, int round, int roun
std::vector<float> splits(link->size());
for (int i = 0; i < link->size(); ++i)
{
float split; diy::Direction dir;
float split; diy::Direction dir(dim_,0);
int in_gid = link->target(i).gid;
while(srp.incoming(in_gid))
@ -287,7 +287,7 @@ update_links(Block* b, const diy::ReduceProxy& srp, int dim, int round, int roun
if (wrap)
new_link.add_wrap(find_wrap(new_link.bounds(), bounds, domain));
else
new_link.add_wrap(diy::Direction());
new_link.add_wrap(diy::Direction(dim_,0));
}
} else // non-aligned side
{
@ -308,7 +308,7 @@ update_links(Block* b, const diy::ReduceProxy& srp, int dim, int round, int roun
if (wrap)
new_link.add_wrap(find_wrap(new_link.bounds(), bounds, domain));
else
new_link.add_wrap(diy::Direction());
new_link.add_wrap(diy::Direction(dim_, 0));
}
}
}
@ -323,16 +323,16 @@ update_links(Block* b, const diy::ReduceProxy& srp, int dim, int round, int roun
update_neighbor_bounds(nbr_bounds, find_split(new_link.bounds(), nbr_bounds), dim, !lower);
new_link.add_bounds(nbr_bounds);
new_link.add_wrap(diy::Direction()); // dual block cannot be wrapped
new_link.add_wrap(diy::Direction(dim_,0)); // dual block cannot be wrapped
if (lower)
{
diy::Direction right;
diy::Direction right(dim_,0);
right[dim] = 1;
new_link.add_direction(right);
} else
{
diy::Direction left;
diy::Direction left(dim_,0);
left[dim] = -1;
new_link.add_direction(left);
}
@ -346,7 +346,7 @@ update_links(Block* b, const diy::ReduceProxy& srp, int dim, int round, int roun
template<class Block, class Point>
void
diy::detail::KDTreePartition<Block,Point>::
split_to_neighbors(Block* b, const diy::ReduceProxy& srp, int dim) const
split_to_neighbors(Block*, const diy::ReduceProxy& srp, int) const
{
int lid = srp.master()->lid(srp.gid());
RCLink* link = static_cast<RCLink*>(srp.master()->link(lid));
@ -366,20 +366,23 @@ void
diy::detail::KDTreePartition<Block,Point>::
compute_local_histogram(Block* b, const diy::ReduceProxy& srp, int dim) const
{
auto udim = static_cast<unsigned>(dim);
int lid = srp.master()->lid(srp.gid());
RCLink* link = static_cast<RCLink*>(srp.master()->link(lid));
// compute and enqueue local histogram
Histogram histogram(bins_);
float width = (link->core().max[dim] - link->core().min[dim])/bins_;
float width = (link->core().max[udim] - link->core().min[udim])/bins_;
for (size_t i = 0; i < (b->*points_).size(); ++i)
{
float x = (b->*points_)[i][dim];
int loc = (x - link->core().min[dim]) / width;
if (loc < 0)
throw std::runtime_error(fmt::format("{} {} {}", loc, x, link->core().min[dim]));
if (loc >= (int) bins_)
float x = (b->*points_)[i][udim];
float floc = (x - link->core().min[udim]) / width;
if (floc < 0)
throw std::runtime_error(fmt::format("{} {} {}", floc, x, link->core().min[udim]));
auto loc = static_cast<size_t>(floc);
if (loc >= bins_)
loc = bins_ - 1;
++(histogram[loc]);
}
@ -390,7 +393,7 @@ compute_local_histogram(Block* b, const diy::ReduceProxy& srp, int dim) const
template<class Block, class Point>
void
diy::detail::KDTreePartition<Block,Point>::
add_histogram(Block* b, const diy::ReduceProxy& srp, Histogram& histogram) const
add_histogram(Block*, const diy::ReduceProxy& srp, Histogram& histogram) const
{
// dequeue and add up the histograms
for (int i = 0; i < srp.in_link().size(); ++i)
@ -407,7 +410,7 @@ add_histogram(Block* b, const diy::ReduceProxy& srp, Histogram& histogram) const
template<class Block, class Point>
void
diy::detail::KDTreePartition<Block,Point>::
receive_histogram(Block* b, const diy::ReduceProxy& srp, Histogram& histogram) const
receive_histogram(Block*, const diy::ReduceProxy& srp, Histogram& histogram) const
{
srp.dequeue(srp.in_link().target(0).gid, histogram);
}
@ -415,7 +418,7 @@ receive_histogram(Block* b, const diy::ReduceProxy& srp, Histogram& histogram) c
template<class Block, class Point>
void
diy::detail::KDTreePartition<Block,Point>::
forward_histogram(Block* b, const diy::ReduceProxy& srp, const Histogram& histogram) const
forward_histogram(Block*, const diy::ReduceProxy& srp, const Histogram& histogram) const
{
for (int i = 0; i < srp.out_link().size(); ++i)
srp.enqueue(srp.out_link().target(i), histogram);
@ -445,19 +448,26 @@ enqueue_exchange(Block* b, const diy::ReduceProxy& srp, int dim, const Histogram
size_t cur = 0;
float width = (link->core().max[dim] - link->core().min[dim])/bins_;
float split = 0;
for (size_t i = 0; i < histogram.size(); ++i)
// scope-block for variable `i`
{
if (cur + histogram[i] > total/2)
size_t i = 0;
for (; i < histogram.size(); ++i)
{
split = link->core().min[dim] + width*i;
break;
if (cur + histogram[i] > total/2)
break;
cur += histogram[i];
}
cur += histogram[i];
if (i == 0)
++i;
else if (i >= histogram.size() - 1)
i = histogram.size() - 2;
split = link->core().min[dim] + width*i;
log->trace("Found split: {} (dim={}) in {} - {}", split, dim, link->core().min[dim], link->core().max[dim]);
}
log->trace("Found split: {} (dim={}) in {} - {}", split, dim, link->core().min[dim], link->core().max[dim]);
// subset and enqueue
std::vector< std::vector<Point> > out_points(srp.out_link().size());
std::vector< std::vector<Point> > out_points(static_cast<size_t>(srp.out_link().size()));
for (size_t i = 0; i < (b->*points_).size(); ++i)
{
float x = (b->*points_)[i][dim];
@ -554,7 +564,7 @@ diy::Direction
diy::detail::KDTreePartition<Block,Point>::
find_wrap(const Bounds& bounds, const Bounds& nbr_bounds, const Bounds& domain) const
{
diy::Direction wrap;
diy::Direction wrap(dim_,0);
for (int i = 0; i < dim_; ++i)
{
if (bounds.min[i] == domain.min[i] && nbr_bounds.max[i] == domain.max[i])

@ -85,29 +85,29 @@ struct SampleSort<Block,T,Cmp>::Sampler
Sampler(ValuesVector values_, ValuesVector dividers_, const Cmp& cmp_, size_t num_samples_):
values(values_), dividers(dividers_), cmp(cmp_), num_samples(num_samples_) {}
void operator()(Block* b, const ReduceProxy& srp, const RegularSwapPartners& partners) const
void operator()(Block* b, const ReduceProxy& srp, const RegularSwapPartners&) const
{
int k_in = srp.in_link().size();
int k_out = srp.out_link().size();
std::vector<T> samples;
std::vector<T> samps;
if (k_in == 0)
{
// draw random samples
for (size_t i = 0; i < num_samples; ++i)
samples.push_back((b->*values)[std::rand() % (b->*values).size()]);
samps.push_back((b->*values)[std::rand() % (b->*values).size()]);
} else
dequeue_values(samples, srp, false);
dequeue_values(samps, srp, false);
if (k_out == 0)
{
// pick subsamples that separate quantiles
std::sort(samples.begin(), samples.end(), cmp);
std::sort(samps.begin(), samps.end(), cmp);
std::vector<T> subsamples(srp.nblocks() - 1);
int step = samples.size() / srp.nblocks(); // NB: subsamples.size() + 1
size_t step = samps.size() / srp.nblocks(); // NB: subsamples.size() + 1
for (size_t i = 0; i < subsamples.size(); ++i)
subsamples[i] = samples[(i+1)*step];
subsamples[i] = samps[(i+1)*step];
(b->*dividers).swap(subsamples);
}
else
@ -115,7 +115,7 @@ struct SampleSort<Block,T,Cmp>::Sampler
for (int i = 0; i < k_out; ++i)
{
MemoryBuffer& out = srp.outgoing(srp.out_link().target(i));
save(out, &samples[0], samples.size());
save(out, &samps[0], samps.size());
}
}
}
@ -139,7 +139,7 @@ struct SampleSort<Block,T,Cmp>::Exchanger
// enqueue values to the correct locations
for (size_t i = 0; i < (b->*values).size(); ++i)
{
int to = std::lower_bound((b->*samples).begin(), (b->*samples).end(), (b->*values)[i], cmp) - (b->*samples).begin();
int to = static_cast<int>(std::lower_bound((b->*samples).begin(), (b->*samples).end(), (b->*values)[i], cmp) - (b->*samples).begin());
rp.enqueue(rp.out_link().target(to), (b->*values)[i]);
}
(b->*values).clear();

@ -20,7 +20,7 @@ namespace diy
void init() { out_ = in_; }
void update(const CollectiveOp& other) { out_ = op_(out_, static_cast<const AllReduceOp&>(other).in_); }
void global(const mpi::communicator& comm) { T res; mpi::all_reduce(comm, out_, res, op_); out_ = res; }
void global(const mpi::communicator& comm) { T res{}; mpi::all_reduce(comm, out_, res, op_); out_ = res; }
void copy_from(const CollectiveOp& other) { out_ = static_cast<const AllReduceOp&>(other).out_; }
void result_out(void* dest) const { *reinterpret_cast<T*>(dest) = out_; }
@ -95,7 +95,7 @@ diy::Master::
process_collectives()
{
auto scoped = prof.scoped("collectives");
DIY_UNUSED(scoped);
VTKMDIY_UNUSED(scoped);
if (collectives().empty())
return;

@ -1,10 +1,9 @@
namespace diy
{
struct Master::tags { enum { queue, piece }; };
struct Master::MessageInfo
{
int from, to;
int nparts;
int round;
};
@ -19,10 +18,10 @@ namespace diy
struct Master::InFlightRecv
{
MemoryBuffer message;
MessageInfo info { -1, -1, -1 };
MessageInfo info { -1, -1, -1, -1 };
bool done = false;
inline void recv(mpi::communicator& comm, const mpi::status& status);
inline bool recv(mpi::communicator& comm, const mpi::status& status);
inline void place(IncomingRound* in, bool unload, ExternalStorage* storage, IExchangeInfo* iexchange);
void reset() { *this = InFlightRecv(); }
};
@ -43,32 +42,6 @@ namespace diy
size_t limit = 0;
};
struct Master::IExchangeInfo
{
IExchangeInfo():
n(0) {}
IExchangeInfo(size_t n_, mpi::communicator comm_):
n(n_),
comm(comm_),
global_work_(new mpi::window<int>(comm, 1)) { global_work_->lock_all(MPI_MODE_NOCHECK); }
~IExchangeInfo() { global_work_->unlock_all(); }
inline void not_done(int gid);
inline int global_work(); // get global work status (for debugging)
inline bool all_done(); // get global all done status
inline void reset_work(); // reset global work counter
inline int add_work(int work); // add work to global work counter
int inc_work() { return add_work(1); } // increment global work counter
int dec_work() { return add_work(-1); } // decremnent global work counter
size_t n;
mpi::communicator comm;
std::unordered_map<int, bool> done; // gid -> done
std::unique_ptr<mpi::window<int>> global_work_; // global work to do
std::shared_ptr<spd::logger> log = get_logger();
};
// VectorWindow is used to send and receive subsets of a contiguous array in-place
namespace detail
{
@ -90,7 +63,7 @@ namespace diy
struct mpi_datatype< diy::detail::VectorWindow<T> >
{
using VecWin = diy::detail::VectorWindow<T>;
static MPI_Datatype datatype() { return get_mpi_datatype<T>(); }
static diy::mpi::datatype datatype() { return get_mpi_datatype<T>(); }
static const void* address(const VecWin& x) { return x.begin; }
static void* address(VecWin& x) { return x.begin; }
static int count(const VecWin& x) { return static_cast<int>(x.count); }
@ -99,18 +72,8 @@ namespace diy
} // namespace mpi::detail
} // namespace diy
void
diy::Master::IExchangeInfo::
not_done(int gid)
{
if (done[gid])
{
done[gid] = false;
int work = inc_work();
log->debug("[{}] Incrementing work when switching done (on receipt): work = {}\n", gid, work);
} else
log->debug("[{}] Not done, no need to increment work\n", gid);
}
/** InFlightRecv **/
diy::Master::InFlightRecv&
diy::Master::
@ -126,28 +89,27 @@ diy::Master::inflight_sends()
}
// receive message described by status
void
bool
diy::Master::InFlightRecv::
recv(mpi::communicator& comm, const mpi::status& status)
{
bool result = false; // indicates whether this is the first (and possibly only) message of a given queue
if (info.from == -1) // uninitialized
{
MemoryBuffer bb;
comm.recv(status.source(), status.tag(), bb.buffer);
if (status.tag() == tags::piece) // first piece is the header
diy::load_back(bb, info);
info.nparts--;
if (info.nparts > 0) // multi-part message
{
size_t msg_size;
diy::load(bb, msg_size);
diy::load(bb, info);
message.buffer.reserve(msg_size);
}
else // tags::queue
{
diy::load_back(bb, info);
} else
message.swap(bb);
}
result = true;
}
else
{
@ -160,43 +122,34 @@ recv(mpi::communicator& comm, const mpi::status& status)
window.count = count;
comm.recv(status.source(), status.tag(), window);
info.nparts--;
}
if (status.tag() == tags::queue)
if (info.nparts == 0)
done = true;
return result;
}
// once the InFlightRecv is done, place it either out of core or in the appropriate incoming queue
void
diy::Master::InFlightRecv::
place(IncomingRound* in, bool unload, ExternalStorage* storage, IExchangeInfo* iexchange)
place(IncomingRound* in, bool unload, ExternalStorage* storage, IExchangeInfo*)
{
size_t size = message.size();
int from = info.from;
int to = info.to;
int external = -1;
message.reset();
auto access = in->map[to][from].access();
access->emplace_back(std::move(message));
if (unload)
{
get_logger()->debug("Directly unloading queue {} <- {}", to, from);
external = storage->put(message); // unload directly
access->back().unload(storage);
}
else if (!iexchange)
{
in->map[to].queues[from].swap(message);
in->map[to].queues[from].reset(); // buffer position = 0
}
else // iexchange
{
auto log = get_logger();
iexchange->not_done(to);
in->map[to].queues[from].append_binary(&message.buffer[0], message.size()); // append insted of overwrite
int work = iexchange->dec_work();
log->debug("[{}] Decrementing work after receiving: work = {}\n", to, work);
}
in->map[to].records[from] = QueueRecord(size, external);
++(in->received);
}

@ -25,7 +25,10 @@ struct diy::Master::ProcessBlock
if ((size_t)cur >= blocks.size())
return;
int i = blocks[cur];
int i = blocks[cur];
int gid = master.gid(i);
stats::Annotation::Guard g( stats::Annotation("diy.block").set(gid) );
if (master.block(i))
{
if (local.size() == (size_t)local_limit)
@ -33,7 +36,7 @@ struct diy::Master::ProcessBlock
local.push_back(i);
}
master.log->debug("Processing block: {}", master.gid(i));
master.log->debug("Processing block: {}", gid);
bool skip = all_skip(i);
@ -58,8 +61,7 @@ struct diy::Master::ProcessBlock
cmd->execute(skip ? 0 : master.block(i), master.proxy(i));
// no longer need them, so get rid of them
current_incoming[master.gid(i)].queues.clear();
current_incoming[master.gid(i)].records.clear();
current_incoming[gid].clear();
}
if (skip && master.block(i) == 0)
@ -93,7 +95,7 @@ execute()
{
log->debug("Entered execute()");
auto scoped = prof.scoped("execute");
DIY_UNUSED(scoped);
VTKMDIY_UNUSED(scoped);
//show_incoming_records();
// touch the outgoing and incoming queues as well as collectives to make sure they exist

@ -0,0 +1,88 @@
#include <atomic>
namespace diy
{
struct Master::IExchangeInfoCollective: public IExchangeInfo
{
IExchangeInfoCollective(mpi::communicator c, stats::Profiler& p):
IExchangeInfo(c, p)
{
local_work_ = 0;
dirty = 0;
state = 0;
}
inline bool all_done() override; // get global all done status
inline void add_work(int work) override; // add work to global work counter
inline void control() override;
std::atomic<int> local_work_;
std::atomic<int> dirty;
int local_dirty, all_dirty;
std::atomic<int> state;
mpi::request r;
// debug
bool first_ibarrier = true;
using IExchangeInfo::prof;
};
}
bool
diy::Master::IExchangeInfoCollective::
all_done()
{
return state == 3;
}
void
diy::Master::IExchangeInfoCollective::
add_work(int work)
{
local_work_ += work;
if (local_work_ > 0)
dirty = 1;
}
void
diy::Master::IExchangeInfoCollective::
control()
{
if (state == 0 && local_work_ == 0)
{
// debug
if (first_ibarrier)
{
prof >> "iexchange-control"; // consensus-time cannot nest in iexchange-control
prof << "consensus-time";
prof << "iexchange-control";
first_ibarrier = false;
}
r = ibarrier(comm);
dirty = 0;
state = 1;
} else if (state == 1)
{
mpi::optional<mpi::status> ostatus = r.test();
if (ostatus)
{
local_dirty = dirty;
r = mpi::iall_reduce(comm, local_dirty, all_dirty, std::logical_or<int>());
state = 2;
}
} else if (state == 2)
{
mpi::optional<mpi::status> ostatus = r.test();
if (ostatus)
{
if (all_dirty == 0) // done
state = 3;
else
state = 0; // reset
}
}
}

@ -0,0 +1,28 @@
namespace diy
{
struct Master::IExchangeInfo
{
using Clock = std::chrono::high_resolution_clock;
using Time = Clock::time_point;
IExchangeInfo(mpi::communicator c, stats::Profiler& p):
comm(c),
prof(p) {}
virtual ~IExchangeInfo() {}
virtual bool all_done() =0; // get global all done status
virtual void add_work(int work) =0; // add work to global work counter
virtual void control() =0;
void inc_work() { add_work(1); } // increment work counter
void dec_work() { add_work(-1); } // decremnent work counter
mpi::communicator comm;
std::shared_ptr<spd::logger> log = get_logger();
stats::Profiler& prof;
};
}
#include "iexchange-collective.hpp"

@ -23,31 +23,38 @@ namespace detail
}
}
void operator()(Block* b, const ReduceProxy& srp, const RegularSwapPartners& partners) const
void operator()(Block* b, const ReduceProxy& srp, const RegularSwapPartners&) const
{
int k_in = srp.in_link().size();
int k_out = srp.out_link().size();
if (k_in == 0 && k_out == 0) // special case of a single block
{
ReduceProxy all_srp_out(srp, srp.block(), 0, srp.assigner(), empty_link, all_neighbors_link);
ReduceProxy all_srp_in (srp, srp.block(), 1, srp.assigner(), all_neighbors_link, empty_link);
ReduceProxy all_srp(std::move(const_cast<ReduceProxy&>(srp)), srp.block(), 0, srp.assigner(), empty_link, all_neighbors_link);
op(b, all_srp_out);
MemoryBuffer& in_queue = all_srp_in.incoming(all_srp_in.in_link().target(0).gid);
in_queue.swap(all_srp_out.outgoing(all_srp_out.out_link().target(0)));
op(b, all_srp);
MemoryBuffer& in_queue = all_srp.incoming(all_srp.in_link().target(0).gid);
in_queue.swap(all_srp.outgoing(all_srp.out_link().target(0)));
in_queue.reset();
all_srp.outgoing()->clear();
op(b, all_srp_in);
// change to incoming proxy
all_srp.set_round(1);
auto& in_link = const_cast<Link&>(all_srp.in_link());
auto& out_link = const_cast<Link&>(all_srp.out_link());
in_link.swap(out_link);
op(b, all_srp);
return;
}
if (k_in == 0) // initial round
{
ReduceProxy all_srp(srp, srp.block(), 0, srp.assigner(), empty_link, all_neighbors_link);
ReduceProxy all_srp(std::move(const_cast<ReduceProxy&>(srp)), srp.block(), 0, srp.assigner(), empty_link, all_neighbors_link);
op(b, all_srp);
Master::OutgoingQueues all_queues;
Master::Proxy::OutgoingQueues all_queues;
all_queues.swap(*all_srp.outgoing()); // clears out the queues and stores them locally
// enqueue outgoing
@ -67,10 +74,10 @@ namespace detail
} else if (k_out == 0) // final round
{
// dequeue incoming + reorder into the correct order
ReduceProxy all_srp(srp, srp.block(), 1, srp.assigner(), all_neighbors_link, empty_link);
ReduceProxy all_srp(std::move(const_cast<ReduceProxy&>(srp)), srp.block(), 1, srp.assigner(), all_neighbors_link, empty_link);
Master::IncomingQueues all_incoming;
all_incoming.swap(*srp.incoming());
Master::Proxy::IncomingQueues all_incoming;
all_incoming.swap(*all_srp.incoming());
std::pair<int, int> range; // all the ranges should be the same
for (int i = 0; i < k_in; ++i)

@ -0,0 +1,158 @@
#ifndef VTKMDIY_DYNAMIC_POINT_HPP
#define VTKMDIY_DYNAMIC_POINT_HPP
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
#include "constants.h"
#include "thirdparty/chobo/small_vector.hpp"
namespace diy
{
template<class Coordinate_, size_t static_size = VTKMDIY_MAX_DIM>
class DynamicPoint: public chobo::small_vector<Coordinate_, static_size>
{
public:
using Coordinate = Coordinate_;
using Parent = chobo::small_vector<Coordinate_, static_size>;
template<class U>
struct rebind { typedef DynamicPoint<U> type; };
public:
DynamicPoint(size_t dim, Coordinate x = 0):
Parent(dim, x) {}
template<class T> DynamicPoint(const DynamicPoint<T>& p) { for (size_t i = 0; i < dimension(); ++i) (*this)[i] = p[i]; }
template<class T> DynamicPoint(const T* a, size_t dim) { for (size_t i = 0; i < dim; ++i) (*this)[i] = a[i]; }
template<class T> DynamicPoint(const std::vector<T>& a):
Parent(a.begin(), a.end()) {}
DynamicPoint(std::initializer_list<Coordinate> lst):
Parent(lst.size()) { size_t i = 0; for (Coordinate x : lst) (*this)[i++] = x; }
DynamicPoint(DynamicPoint&&) =default;
DynamicPoint(const DynamicPoint&) =default;
DynamicPoint& operator=(const DynamicPoint&) =default;
unsigned dimension() const { return static_cast<unsigned>(Parent::size()); }
static DynamicPoint zero(size_t dim) { return DynamicPoint(dim, 0); }
static DynamicPoint one(size_t dim) { return DynamicPoint(dim, 1); }
DynamicPoint drop(size_t dim) const { DynamicPoint p(dimension() - 1); size_t c = 0; for (size_t i = 0; i < dimension(); ++i) { if (i == dim) continue; p[c++] = (*this)[i]; } return p; }
DynamicPoint lift(size_t dim, Coordinate x) const { DynamicPoint p(dimension() + 1); for (size_t i = 0; i < dimension()+1; ++i) { if (i < dim) p[i] = (*this)[i]; else if (i == dim) p[i] = x; else if (i > dim) p[i] = (*this)[i-1]; } return p; }
using Parent::operator[];
DynamicPoint& operator+=(const DynamicPoint& y) { for (size_t i = 0; i < dimension(); ++i) (*this)[i] += y[i]; return *this; }
DynamicPoint& operator-=(const DynamicPoint& y) { for (size_t i = 0; i < dimension(); ++i) (*this)[i] -= y[i]; return *this; }
DynamicPoint& operator*=(Coordinate a) { for (size_t i = 0; i < dimension(); ++i) (*this)[i] *= a; return *this; }
DynamicPoint& operator/=(Coordinate a) { for (size_t i = 0; i < dimension(); ++i) (*this)[i] /= a; return *this; }
DEPRECATED("Use norm2 instead")
Coordinate norm() const { return norm2(); }
Coordinate norm2() const { return (*this)*(*this); }
std::ostream& operator<<(std::ostream& out) const { out << (*this)[0]; for (size_t i = 1; i < dimension(); ++i) out << " " << (*this)[i]; return out; }
std::istream& operator>>(std::istream& in);
friend
DynamicPoint operator+(DynamicPoint x, const DynamicPoint& y) { x += y; return x; }
friend
DynamicPoint operator-(DynamicPoint x, const DynamicPoint& y) { x -= y; return x; }
friend
DynamicPoint operator/(DynamicPoint x, Coordinate y) { x /= y; return x; }
friend
DynamicPoint operator*(DynamicPoint x, Coordinate y) { x *= y; return x; }
friend
DynamicPoint operator*(Coordinate y, DynamicPoint x) { x *= y; return x; }
friend
Coordinate operator*(const DynamicPoint& x, const DynamicPoint& y) { Coordinate n = 0; for (size_t i = 0; i < x.dimension(); ++i) n += x[i] * y[i]; return n; }
friend
bool operator<(const DynamicPoint& x, const DynamicPoint& y) { return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); }
friend
bool operator>(const DynamicPoint& x, const DynamicPoint& y) { return y < x; }
template<class T, size_t s_>
friend
Coordinate operator*(const DynamicPoint<T,s_>& x, const DynamicPoint& y) { Coordinate n = 0; for (size_t i = 0; i < x.dimension(); ++i) n += x[i] * y[i]; return n; }
};
template<class C, size_t s_>
std::istream&
DynamicPoint<C,s_>::
operator>>(std::istream& in)
{
std::string point_str;
in >> point_str; // read until ' '
std::stringstream ps(point_str);
char x;
for (unsigned i = 0; i < dimension(); ++i)
{
ps >> (*this)[i];
ps >> x;
}
return in;
}
template<class Coordinate, size_t s_>
Coordinate norm2(const DynamicPoint<Coordinate,s_>& p)
{ Coordinate res = 0; for (unsigned i = 0; i < p.dimension(); ++i) res += p[i]*p[i]; return res; }
template<class C, size_t s_>
std::ostream&
operator<<(std::ostream& out, const DynamicPoint<C,s_>& p)
{ return p.operator<<(out); }
template<class C, size_t s_>
std::istream&
operator>>(std::istream& in, DynamicPoint<C,s_>& p)
{ return p.operator>>(in); }
// Serialization
template<class T>
struct Serialization;
struct BinaryBuffer;
template<class T> void save(BinaryBuffer&, const T&);
template<class T> void load(BinaryBuffer&, T&);
template<class T> void save(BinaryBuffer&, const T*, size_t);
template<class T> void load(BinaryBuffer&, T*, size_t);
template<class C, size_t s_>
struct Serialization<DynamicPoint<C, s_>>
{
using Point = DynamicPoint<C,s_>;
static void save(BinaryBuffer& bb, const Point& p)
{
size_t s = p.size();
diy::save(bb, s);
if (s > 0)
diy::save(bb, &p[0], p.size());
}
static void load(BinaryBuffer& bb, Point& p)
{
size_t s;
diy::load(bb, s);
p.resize(s);
if (s > 0)
diy::load(bb, &p[0], s);
}
};
}
#endif // VTKMDIY_POINT_HPP

@ -0,0 +1,84 @@
#ifndef VTKMDIY_FACTORY_HPP
#define VTKMDIY_FACTORY_HPP
// From http://www.nirfriedman.com/2018/04/29/unforgettable-factory/
// with minor changes.
#include <memory>
#include <string>
#include <unordered_map>
namespace diy
{
template <class Base, class... Args>
class Factory
{
public:
template <class... T>
static Base* make(const std::string &s, T&&... args)
{
return data().at(s)(std::forward<T>(args)...);
}
virtual std::string id() const { return typeid(Base).name(); }
template <class T>
struct Registrar: Base
{
static bool registerT()
{
const auto name = typeid(T).name();
Factory::data()[name] = [](Args... args) -> Base*
{
return new T(std::forward<Args>(args)...);
};
return true;
}
static volatile bool registered;
std::string id() const override { return typeid(T).name(); }
#if defined(__NVCC__)
protected:
#else
private:
friend T;
#endif
#if defined(__INTEL_COMPILER)
__attribute__ ((used))
#endif
Registrar(): Base(Key{}) { (void)registered; }
};
#if defined(__NVCC__)
protected:
#else
private:
friend Base;
#endif
class Key
{
Key(){};
template <class T> friend struct Registrar;
};
using FuncType = Base* (*)(Args...);
Factory() = default;
static std::unordered_map<std::string, FuncType>& data()
{
static std::unordered_map<std::string, FuncType> s;
return s;
}
};
template <class Base, class... Args>
template <class T>
volatile bool Factory<Base, Args...>::Registrar<T>::registered = Factory<Base, Args...>::Registrar<T>::registerT();
}
#endif

@ -1,535 +0,0 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "format.h"
#include <string.h>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#if defined(_WIN32) && defined(__MINGW32__)
# include <cstring>
#endif
#if FMT_USE_WINDOWS_H
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
# define WIN32_LEAN_AND_MEAN
# endif
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
# include <windows.h>
# else
# define NOMINMAX
# include <windows.h>
# undef NOMINMAX
# endif
#endif
#if FMT_EXCEPTIONS
# define FMT_TRY try
# define FMT_CATCH(x) catch (x)
#else
# define FMT_TRY if (true)
# define FMT_CATCH(x) if (false)
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
# pragma warning(disable: 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
# pragma warning(disable: 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
return fmt::internal::Null<>();
}
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
return fmt::internal::Null<>();
}
namespace fmt {
FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {}
FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {}
namespace {
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args);
return result;
}
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
# define FMT_SWPRINTF snwprintf
#else
# define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
const char RESET_COLOR[] = "\x1b[0m";
typedef void (*FormatFunc)(Writer &, int, StringRef);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
// Returns one of the following values:
// 0 - success
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
class StrError {
private:
int error_code_;
char *&buffer_;
std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const StrError &) {}
// Handle the result of XSI-compliant version of strerror_r.
int handle(int result) {
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}
// Handle the result of GNU-specific version of strerror_r.
int handle(char *message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}
// Handle the case when strerror_r is not available.
int handle(internal::Null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
ERANGE : result;
}
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::Null<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
public:
StrError(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() {
// Suppress a warning about unused strerror_r.
strerror_r(0, FMT_NULL, "");
return handle(strerror_r(error_code_, buffer_, buffer_size_));
}
};
return StrError(error_code, buffer, buffer_size).run();
}
void format_error_code(Writer &out, int error_code,
StringRef message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
// bad_alloc.
out.clear();
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
// 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;
typedef internal::IntTraits<int>::MainType MainType;
MainType abs_value = static_cast<MainType>(error_code);
if (internal::is_negative(error_code)) {
abs_value = 0 - abs_value;
++error_code_size;
}
error_code_size += internal::count_digits(abs_value);
if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
out << message << SEP;
out << ERROR_STR << error_code;
assert(out.size() <= internal::INLINE_BUFFER_SIZE);
}
void report_error(FormatFunc func, int error_code,
StringRef message) FMT_NOEXCEPT {
MemoryWriter full_message;
func(full_message, error_code, message);
// Use Writer::data instead of Writer::c_str to avoid potential memory
// allocation.
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
} // namespace
FMT_FUNC void SystemError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
format_system_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
template <typename T>
int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, width, value) :
FMT_SNPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, value) :
FMT_SWPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, width, value) :
FMT_SWPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
const char internal::BasicData<T>::DIGITS[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
#define FMT_POWERS_OF_10(factor) \
factor * 10, \
factor * 100, \
factor * 1000, \
factor * 10000, \
factor * 100000, \
factor * 1000000, \
factor * 10000000, \
factor * 100000000, \
factor * 1000000000
template <typename T>
const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1)
};
template <typename T>
const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = {
0,
FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long.
ULongLong(1000000000) * ULongLong(1000000000) * 10
};
FMT_FUNC void internal::report_unknown_type(char code, const char *type) {
(void)type;
if (std::isprint(static_cast<unsigned char>(code))) {
FMT_THROW(FormatError(
format("unknown format code '{}' for {}", code, type)));
}
FMT_THROW(FormatError(
format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)));
}
#if FMT_USE_WINDOWS_H
FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
if (s.size() > INT_MAX)
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
int s_size = static_cast<int>(s.size());
int length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_.resize(length + 1);
length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_[length] = 0;
}
FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) {
if (int error_code = convert(s)) {
FMT_THROW(WindowsError(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
if (s.size() > INT_MAX)
return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
int length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_[length] = 0;
return 0;
}
FMT_FUNC void WindowsError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_windows_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
FMT_FUNC void internal::format_windows_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_TRY {
MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE);
for (;;) {
wchar_t *system_message = &buffer[0];
int result = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
if (result != 0) {
UTF16ToUTF8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
out << message << ": " << utf8_message;
return;
}
break;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
#endif // FMT_USE_WINDOWS_H
FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_TRY {
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
buffer.resize(internal::INLINE_BUFFER_SIZE);
for (;;) {
char *system_message = &buffer[0];
int result = safe_strerror(error_code, system_message, buffer.size());
if (result == 0) {
out << message << ": " << system_message;
return;
}
if (result != ERANGE)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
template <typename Char>
void internal::ArgMap<Char>::init(const ArgList &args) {
if (!map_.empty())
return;
typedef internal::NamedArg<Char> NamedArg;
const NamedArg *named_arg = FMT_NULL;
bool use_values =
args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
if (use_values) {
for (unsigned i = 0;/*nothing*/; ++i) {
internal::Arg::Type arg_type = args.type(i);
switch (arg_type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
return;
}
for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
internal::Arg::Type arg_type = args.type(i);
if (arg_type == internal::Arg::NAMED_ARG) {
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
}
}
for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
switch (args.args_[i].type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
}
template <typename Char>
void internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
internal::Arg arg = args_[arg_index];
switch (arg.type) {
case internal::Arg::NONE:
error = "argument index out of range";
break;
case internal::Arg::NAMED_ARG:
arg = *static_cast<const internal::Arg*>(arg.pointer);
break;
default:
/*nothing*/;
}
return arg;
}
FMT_FUNC void report_system_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
report_error(format_system_error, error_code, message);
}
#if FMT_USE_WINDOWS_H
FMT_FUNC void report_windows_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
report_error(internal::format_windows_error, error_code, message);
}
#endif
FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f);
}
FMT_FUNC void print(CStringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
print(format, args);
std::fputs(RESET_COLOR, stdout);
}
#ifndef FMT_HEADER_ONLY
template struct internal::BasicData<void>;
// Explicit instantiations for char.
template void internal::FixedBuffer<char>::grow(std::size_t);
template void internal::ArgMap<char>::init(const ArgList &args);
template FMT_API int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value);
template FMT_API int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, long double value);
// Explicit instantiations for wchar_t.
template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void internal::ArgMap<wchar_t>::init(const ArgList &args);
template FMT_API int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);
template FMT_API int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value);
#endif // FMT_HEADER_ONLY
} // namespace fmt
#ifdef _MSC_VER
# pragma warning(pop)
#endif

File diff suppressed because it is too large Load Diff

@ -1,35 +0,0 @@
/*
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#include "ostream.h"
namespace fmt {
namespace internal {
FMT_FUNC void write(std::ostream &os, Writer &w) {
const char *data = w.data();
typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
UnsignedStreamSize size = w.size();
UnsignedStreamSize max_size =
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
do {
UnsignedStreamSize n = size <= max_size ? size : max_size;
os.write(data, static_cast<std::streamsize>(n));
data += n;
size -= n;
} while (size != 0);
}
}
FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
internal::write(os, w);
}
} // namespace fmt

@ -1,105 +0,0 @@
/*
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include "format.h"
#include <ostream>
namespace fmt {
namespace internal {
template <class Char>
class FormatBuf : public std::basic_streambuf<Char> {
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
protected:
// The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call
// to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
};
Yes &convert(std::ostream &);
struct DummyStream : std::ostream {
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
};
No &operator<<(std::ostream &, int);
template<typename T>
struct ConvertToIntImpl<T, true> {
// Convert to int only if T doesn't have an overloaded operator<<.
enum {
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
};
// Write the content of w to os.
FMT_API void write(std::ostream &os, Writer &w);
} // namespace internal
// Formats a value.
template <typename Char, typename ArgFormatter_, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
const Char *&format_str, const T &value) {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf);
output << value;
BasicStringRef<Char> str(&buffer[0], buffer.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str));
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "ostream.cc"
#endif
#endif // FMT_OSTREAM_H_

@ -55,7 +55,7 @@ struct GridRef
inline
Vertex vertex(Index idx) const;
Index index(const Vertex& v) const { Index idx = 0; for (unsigned i = 0; i < D; ++i) { idx += ((Index) v[i]) * ((Index) stride_[i]); } return idx; }
Index index(const Vertex& v) const { Index idx = 0; for (unsigned i = 0; i < D; ++i) { idx += ((Index) v[i]) * stride_[i]; } return idx; }
Index size() const { return size(shape()); }
void swap(GridRef& other) { std::swap(data_, other.data_); std::swap(shape_, other.shape_); std::swap(stride_, other.stride_); std::swap(c_order_, other.c_order_); }
@ -73,10 +73,9 @@ struct GridRef
{
Index cur = 1;
if (c_order_)
for (unsigned i = D; i > 0; --i) { stride_[i-1] = cur; cur *= shape_[i-1]; }
for (unsigned i = D; i > 0; --i) { stride_[i-1] = cur; cur *= static_cast<Index>(shape_[i-1]); }
else
for (unsigned i = 0; i < D; ++i) { stride_[i] = cur; cur *= shape_[i]; }
for (unsigned i = 0; i < D; ++i) { stride_[i] = cur; cur *= static_cast<Index>(shape_[i]); }
}
void set_shape(const Vertex& v) { shape_ = v; set_stride(); }
void set_data(C* data) { data_ = data; }
@ -85,7 +84,7 @@ struct GridRef
private:
C* data_;
Vertex shape_;
Vertex stride_;
diy::Point<Index, D> stride_;
bool c_order_;
};
@ -107,8 +106,8 @@ struct Grid: public GridRef<C,D>
Grid():
Parent(new C[0], Vertex::zero()) {}
template<class Int>
Grid(const Point<Int, D>& shape, bool c_order = true):
Parent(new C[size(shape)], shape, c_order)
Grid(const Point<Int, D>& s, bool c_order = true):
Parent(new C[size(s)], s, c_order)
{}
Grid(Grid&& g): Grid() { Parent::swap(g); }
@ -147,11 +146,11 @@ struct Grid: public GridRef<C,D>
private:
template<class OC>
void copy_data(const OC* data)
void copy_data(const OC* data_)
{
Index s = size(shape());
for (Index i = 0; i < s; ++i)
Parent::data()[i] = data[i];
Parent::data()[i] = data_[i];
}
};
@ -181,13 +180,13 @@ vertex(typename GridRef<C, D>::Index idx) const
if (c_order())
for (unsigned i = 0; i < D; ++i)
{
v[i] = idx / stride_[i];
v[i] = static_cast<int>(idx / stride_[i]);
idx %= stride_[i];
}
else
for (int i = D-1; i >= 0; --i)
{
v[i] = idx / stride_[i];
v[i] = static_cast<int>(idx / stride_[i]);
idx %= stride_[i];
}
return v;

@ -205,12 +205,11 @@ namespace io
extra.reset();
// Get local gids from assigner
size_t size = all_offset_counts.size();
assigner.set_nblocks(size);
assigner.set_nblocks(static_cast<int>(all_offset_counts.size()));
std::vector<int> gids;
assigner.local_gids(comm.rank(), gids);
for (unsigned i = 0; i < gids.size(); ++i)
for (size_t i = 0; i < gids.size(); ++i)
{
if (gids[i] != all_offset_counts[gids[i]].gid)
get_logger()->warn("gids don't match in diy::io::read_blocks(), {} vs {}",
@ -342,7 +341,7 @@ namespace split
}
// Get local gids from assigner
assigner.set_nblocks(size);
assigner.set_nblocks(static_cast<int>(size));
std::vector<int> gids;
assigner.local_gids(comm.rank(), gids);

@ -2,11 +2,8 @@
#define VTKMDIY_IO_BOV_HPP
#include <vector>
#include <algorithm>
#include <numeric>
#include "../types.hpp"
#include "../mpi.hpp"
#include "../mpi/io.hpp"
namespace diy
{
@ -39,8 +36,9 @@ namespace io
shape_.push_back(shape[i]);
stride_.push_back(1);
}
for (int i = shape_.size() - 2; i >= 0; --i)
for (auto i = shape_.size() - 2; i == 0; --i)
stride_[i] = stride_[i+1] * shape_[i+1];
stride_[0] = stride_[1] * shape_[1];
}
const Shape& shape() const { return shape_; }
@ -71,50 +69,7 @@ void
diy::io::BOV::
read(const DiscreteBounds& bounds, T* buffer, bool collective, int chunk) const
{
#ifndef VTKM_DIY_NO_MPI
int dim = shape_.size();
int total = 1;
std::vector<int> subsizes;
for (int i = 0; i < dim; ++i)
{
subsizes.push_back(bounds.max[i] - bounds.min[i] + 1);
total *= subsizes.back();
}
MPI_Datatype T_type;
if (chunk == 1)
T_type = mpi::detail::get_mpi_datatype<T>();
else
{
// create an MPI struct of size chunk to read the data in those chunks
// (this allows to work around MPI-IO weirdness where crucial quantities
// are ints, which are too narrow of a type)
int array_of_blocklengths[] = { chunk };
MPI_Aint array_of_displacements[] = { 0 };
MPI_Datatype array_of_types[] = { mpi::detail::get_mpi_datatype<T>() };
MPI_Type_create_struct(1, array_of_blocklengths, array_of_displacements, array_of_types, &T_type);
MPI_Type_commit(&T_type);
}
MPI_Datatype fileblk;
MPI_Type_create_subarray(dim, (int*) &shape_[0], &subsizes[0], (int*) &bounds.min[0], MPI_ORDER_C, T_type, &fileblk);
MPI_Type_commit(&fileblk);
MPI_File_set_view(f_.handle(), offset_, T_type, fileblk, (char*)"native", MPI_INFO_NULL);
mpi::status s;
if (!collective)
MPI_File_read(f_.handle(), buffer, total, T_type, &s.s);
else
MPI_File_read_all(f_.handle(), buffer, total, T_type, &s.s);
if (chunk != 1)
MPI_Type_free(&T_type);
MPI_Type_free(&fileblk);
#else
(void) bounds; (void) buffer; (void) collective; (void)chunk;
DIY_UNSUPPORTED_MPI_CALL(diy::io::BOV::read);
#endif
f_.read_bov(bounds, static_cast<int>(shape_.size()), shape_.data(), reinterpret_cast<char*>(buffer), offset_, mpi::detail::get_mpi_datatype<T>(), collective, chunk);
}
template<class T>
@ -130,52 +85,7 @@ void
diy::io::BOV::
write(const DiscreteBounds& bounds, const T* buffer, const DiscreteBounds& core, bool collective, int chunk)
{
#ifndef VTKM_DIY_NO_MPI
int dim = shape_.size();
std::vector<int> subsizes;
std::vector<int> buffer_shape, buffer_start;
for (int i = 0; i < dim; ++i)
{
buffer_shape.push_back(bounds.max[i] - bounds.min[i] + 1);
buffer_start.push_back(core.min[i] - bounds.min[i]);
subsizes.push_back(core.max[i] - core.min[i] + 1);
}
MPI_Datatype T_type;
if (chunk == 1)
T_type = mpi::detail::get_mpi_datatype<T>();
else
{
// assume T is a binary block and create an MPI struct of appropriate size
int array_of_blocklengths[] = { chunk };
MPI_Aint array_of_displacements[] = { 0 };
MPI_Datatype array_of_types[] = { mpi::detail::get_mpi_datatype<T>() };
MPI_Type_create_struct(1, array_of_blocklengths, array_of_displacements, array_of_types, &T_type);
MPI_Type_commit(&T_type);
}
MPI_Datatype fileblk, subbuffer;
MPI_Type_create_subarray(dim, (int*) &shape_[0], &subsizes[0], (int*) &core.min[0], MPI_ORDER_C, T_type, &fileblk);
MPI_Type_create_subarray(dim, (int*) &buffer_shape[0], &subsizes[0], (int*) &buffer_start[0], MPI_ORDER_C, T_type, &subbuffer);
MPI_Type_commit(&fileblk);
MPI_Type_commit(&subbuffer);
MPI_File_set_view(f_.handle(), offset_, T_type, fileblk, (char*)"native", MPI_INFO_NULL);
mpi::status s;
if (!collective)
MPI_File_write(f_.handle(), (void*)buffer, 1, subbuffer, &s.s);
else
MPI_File_write_all(f_.handle(), (void*)buffer, 1, subbuffer, &s.s);
if (chunk != 1)
MPI_Type_free(&T_type);
MPI_Type_free(&fileblk);
MPI_Type_free(&subbuffer);
#else
(void) bounds; (void) buffer;(void) core; (void) collective; (void) chunk;
DIY_UNSUPPORTED_MPI_CALL(diy::io::bov::write);
#endif
f_.write_bov(bounds, core, static_cast<int>(shape_.size()), shape_.data(), reinterpret_cast<const char*>(buffer), offset_, mpi::detail::get_mpi_datatype<T>(), collective, chunk);
}
#endif

@ -79,21 +79,21 @@ parse_npy_header(BOV::Shape& shape, bool& fortran_order)
header = header.substr(11, nl - 11 + 1);
size_t header_size = nl + 1;
int loc1, loc2;
size_t loc1, loc2;
//fortran order
loc1 = header.find("fortran_order")+16;
fortran_order = (header.substr(loc1,4) == "True" ? true : false);
//shape
unsigned ndims;
size_t ndims;
loc1 = header.find("(");
loc2 = header.find(")");
std::string str_shape = header.substr(loc1+1,loc2-loc1-1);
if(str_shape[str_shape.size()-1] == ',') ndims = 1;
else ndims = std::count(str_shape.begin(),str_shape.end(),',')+1;
shape.resize(ndims);
for(unsigned int i = 0;i < ndims;i++) {
for(size_t i = 0;i < ndims;i++) {
loc1 = str_shape.find(",");
shape[i] = atoi(str_shape.substr(0,loc1).c_str());
str_shape = str_shape.substr(loc1+1);

@ -31,8 +31,8 @@ class SharedOutFile: public std::ostringstream
// write the file serially
std::ofstream out(filename_);
for (auto& contents : all_contents)
out.write(contents.data(), contents.size());
for (auto& cntnts : all_contents)
out.write(cntnts.data(), cntnts.size());
} else
diy::mpi::gather(world_, contents, root_);
}

@ -14,7 +14,7 @@
#include <cstdlib> // mkstemp() on Linux
#include <sys/stat.h>
#include "../constants.h" // for DIY_UNUSED
#include "../constants.h" // for VTKMDIY_UNUSED
namespace diy
{
@ -82,8 +82,8 @@ namespace utils
_close(fd);
}
#else
auto r = ::truncate(filename.c_str(), static_cast<off_t>(length));
(void) r;
int error = ::truncate(filename.c_str(), static_cast<off_t>(length));
VTKMDIY_UNUSED(error);
#endif
}
@ -141,7 +141,7 @@ namespace utils
inline void sync(int fd)
{
#if defined(_WIN32)
DIY_UNUSED(fd);
VTKMDIY_UNUSED(fd);
#else
fsync(fd);
#endif

@ -1,5 +1,5 @@
#ifndef VTKMDIY_COVER_HPP
#define VTKMDIY_COVER_HPP
#ifndef VTKMDIY_LINK_HPP
#define VTKMDIY_LINK_HPP
#include <vector>
#include <map>
@ -9,14 +9,22 @@
#include "serialization.hpp"
#include "assigner.hpp"
#include "factory.hpp"
namespace diy
{
// Local view of a distributed representation of a cover, a completely unstructured link
class Link
class Link: public Factory<Link>
{
public:
using Neighbors = std::vector<BlockID>;
Link(Key) {} // for Factory
Link() = default;
Link(const Link&) = default;
Link(Link&&) = default;
Link& operator=(const Link&) = default;
Link& operator=(Link&&) = default;
virtual ~Link() {} // need to be able to delete derived classes
int size() const { return static_cast<int>(neighbors_.size()); }
@ -38,11 +46,11 @@ namespace diy
Neighbors&
neighbors() { return neighbors_; }
virtual Link* clone() const { return new Link(*this); }
virtual void save(BinaryBuffer& bb) const { diy::save(bb, neighbors_); }
virtual void load(BinaryBuffer& bb) { diy::load(bb, neighbors_); }
virtual size_t id() const { return 0; }
private:
Neighbors neighbors_;
};
@ -50,32 +58,13 @@ namespace diy
template<class Bounds_>
class RegularLink;
typedef RegularLink<DiscreteBounds> RegularGridLink;
typedef RegularLink<ContinuousBounds> RegularContinuousLink;
// Selector between regular discrete and contious links given bounds type
template<class Bounds_>
struct RegularLinkSelector;
template<>
struct RegularLinkSelector<DiscreteBounds>
{
typedef RegularGridLink type;
static const size_t id = 1;
};
template<>
struct RegularLinkSelector<ContinuousBounds>
{
typedef RegularContinuousLink type;
static const size_t id = 2;
};
using RegularGridLink = RegularLink<DiscreteBounds>;
using RegularContinuousLink = RegularLink<ContinuousBounds>;
// for a regular decomposition, it makes sense to address the neighbors by direction
// and store local and neighbor bounds
template<class Bounds_>
class RegularLink: public Link
class RegularLink: public Link::Registrar<RegularLink<Bounds_>>
{
public:
typedef Bounds_ Bounds;
@ -84,6 +73,8 @@ namespace diy
typedef std::vector<Direction> DirVec;
public:
RegularLink():
dim_(0), core_(0), bounds_(0) {} // for Factory
RegularLink(int dim, const Bounds& core__, const Bounds& bounds__):
dim_(dim), core_(core__), bounds_(bounds__) {}
@ -93,7 +84,7 @@ namespace diy
// direction
int direction(Direction dir) const; // convert direction to a neighbor (-1 if no neighbor)
Direction direction(int i) const { return dir_vec_[i]; }
void add_direction(Direction dir) { int c = dir_map_.size(); dir_map_[dir] = c; dir_vec_.push_back(dir); }
void add_direction(Direction dir) { auto c = static_cast<int>(dir_map_.size()); dir_map_[dir] = c; dir_vec_.push_back(dir); }
// wrap
void add_wrap(Direction dir) { wrap_.push_back(dir); }
@ -105,12 +96,16 @@ namespace diy
Bounds& core() { return core_; }
const Bounds& bounds() const { return bounds_; }
Bounds& bounds() { return bounds_; }
const Bounds& core(int i) const { return nbr_cores_[i]; }
const Bounds& bounds(int i) const { return nbr_bounds_[i]; }
void add_core(const Bounds& core__) { nbr_cores_.push_back(core__); }
void add_bounds(const Bounds& bounds__) { nbr_bounds_.push_back(bounds__); }
void swap(RegularLink& other) { Link::swap(other); dir_map_.swap(other.dir_map_); dir_vec_.swap(other.dir_vec_); nbr_bounds_.swap(other.nbr_bounds_); std::swap(dim_, other.dim_); wrap_.swap(other.wrap_); std::swap(core_, other.core_); std::swap(bounds_, other.bounds_); }
void save(BinaryBuffer& bb) const
Link* clone() const override { return new RegularLink(*this); }
void save(BinaryBuffer& bb) const override
{
Link::save(bb);
diy::save(bb, dim_);
@ -118,11 +113,12 @@ namespace diy
diy::save(bb, dir_vec_);
diy::save(bb, core_);
diy::save(bb, bounds_);
diy::save(bb, nbr_cores_);
diy::save(bb, nbr_bounds_);
diy::save(bb, wrap_);
}
void load(BinaryBuffer& bb)
void load(BinaryBuffer& bb) override
{
Link::load(bb);
diy::load(bb, dim_);
@ -130,12 +126,11 @@ namespace diy
diy::load(bb, dir_vec_);
diy::load(bb, core_);
diy::load(bb, bounds_);
diy::load(bb, nbr_cores_);
diy::load(bb, nbr_bounds_);
diy::load(bb, wrap_);
}
virtual size_t id() const { return RegularLinkSelector<Bounds>::id; }
private:
int dim_;
@ -144,31 +139,139 @@ namespace diy
Bounds core_;
Bounds bounds_;
std::vector<Bounds> nbr_cores_;
std::vector<Bounds> nbr_bounds_;
std::vector<Direction> wrap_;
};
// Other cover candidates: KDTreeLink, AMRGridLink
struct AMRLink: public Link::Registrar<AMRLink>
{
public:
using Bounds = DiscreteBounds;
using Directions = std::vector<Direction>;
using Point = Bounds::Point;
struct Description
{
int level { -1 };
Point refinement { 0 }; // refinement of this level w.r.t. level 0
Bounds core { 0 };
Bounds bounds { 0 }; // with ghosts
Description() = default;
Description(int level_, Point refinement_, Bounds core_, Bounds bounds_):
level(level_), refinement(refinement_), core(core_), bounds(bounds_) {}
};
using Descriptions = std::vector<Description>;
public:
AMRLink(int dim, int level, Point refinement, const Bounds& core, const Bounds& bounds):
dim_(dim), local_ { level, refinement, core, bounds } {}
AMRLink(int dim, int level, int refinement, const Bounds& core, const Bounds& bounds):
AMRLink(dim, level, refinement * Point::one(dim), core, bounds) {}
AMRLink(): AMRLink(0, -1, 0, Bounds(0), Bounds(0)) {} // for Factory
// dimension
int dimension() const { return dim_; }
// local information
int level() const { return local_.level; }
int level(int i) const { return nbr_descriptions_[i].level; }
Point refinement() const { return local_.refinement; }
Point refinement(int i) const { return nbr_descriptions_[i].refinement; }
// wrap
void add_wrap(Direction dir) { wrap_.push_back(dir); }
const Directions&
wrap() const { return wrap_; }
// bounds
const Bounds& core() const { return local_.core; }
Bounds& core() { return local_.core; }
const Bounds& bounds() const { return local_.bounds; }
Bounds& bounds() { return local_.bounds; }
const Bounds& core(int i) const { return nbr_descriptions_[i].core; }
const Bounds& bounds(int i) const { return nbr_descriptions_[i].bounds; }
void add_bounds(int level_,
Point refinement_,
const Bounds& core_,
const Bounds& bounds_) { nbr_descriptions_.emplace_back(Description {level_, refinement_, core_, bounds_}); }
void add_bounds(int level_,
int refinement_,
const Bounds& core_,
const Bounds& bounds_) { add_bounds(level_, refinement_ * Point::one(dim_), core_, bounds_); }
Link* clone() const override { return new AMRLink(*this); }
void save(BinaryBuffer& bb) const override
{
Link::save(bb);
diy::save(bb, dim_);
diy::save(bb, local_);
diy::save(bb, nbr_descriptions_);
diy::save(bb, wrap_);
}
void load(BinaryBuffer& bb) override
{
Link::load(bb);
diy::load(bb, dim_);
diy::load(bb, local_);
diy::load(bb, nbr_descriptions_);
diy::load(bb, wrap_);
}
private:
int dim_;
Description local_;
Descriptions nbr_descriptions_;
Directions wrap_;
};
struct LinkFactory
{
public:
static Link* create(size_t id)
static Link* create(std::string name)
{
// not pretty, but will do for now
if (id == 0)
return new Link;
else if (id == 1)
return new RegularGridLink(0, DiscreteBounds(), DiscreteBounds());
else if (id == 2)
return new RegularContinuousLink(0, ContinuousBounds(), ContinuousBounds());
else
return 0;
return Link::make(name);
}
inline static void save(BinaryBuffer& bb, const Link* l);
inline static Link* load(BinaryBuffer& bb);
};
namespace detail
{
inline void instantiate_common_regular_links()
{
// Instantiate the common types to register them
RegularLink<Bounds<int>> rl_int;
RegularLink<Bounds<float>> rl_float;
RegularLink<Bounds<double>> rl_double;
RegularLink<Bounds<long>> rl_long;
}
}
template<>
struct Serialization<diy::AMRLink::Description>
{
static void save(diy::BinaryBuffer& bb, const diy::AMRLink::Description& x)
{
diy::save(bb, x.level);
diy::save(bb, x.refinement);
diy::save(bb, x.core);
diy::save(bb, x.bounds);
}
static void load(diy::BinaryBuffer& bb, diy::AMRLink::Description& x)
{
diy::load(bb, x.level);
diy::load(bb, x.refinement);
diy::load(bb, x.core);
diy::load(bb, x.bounds);
}
};
}
@ -184,7 +287,7 @@ diy::Link*
diy::LinkFactory::
load(BinaryBuffer& bb)
{
size_t id;
std::string id;
diy::load(bb, id);
Link* l = create(id);
l->load(bb);
@ -223,4 +326,4 @@ direction(Direction dir) const
return it->second;
}
#endif
#endif // VTKMDIY_LINK_HPP

@ -4,8 +4,8 @@
#ifndef VTKMDIY_USE_SPDLOG
#include <memory>
#include "fmt/format.h"
#include "fmt/ostream.h"
#include "thirdparty/fmt/format.h"
#include "thirdparty/fmt/ostream.h"
namespace diy
{
@ -47,12 +47,13 @@ set_logger(Args...)
} // diy
#else // DIY_USE_SPDLOG
#else // VTKMDIY_USE_SPDLOG
#include <string>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/null_sink.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/fmt/bundled/format.h>
#include <spdlog/fmt/bundled/ostream.h>
@ -80,10 +81,7 @@ std::shared_ptr<spd::logger>
create_logger(std::string log_level)
{
auto log = spd::stderr_logger_mt("diy");
int lvl;
for (lvl = spd::level::trace; lvl < spd::level::off; ++lvl)
if (spd::level::level_names[lvl] == log_level)
break;
int lvl = spd::level::from_str(log_level);
log->set_level(static_cast<spd::level::level_enum>(lvl));
return log;
}
@ -100,4 +98,4 @@ set_logger(Args... args)
#endif
#endif // DIY_LOG_HPP
#endif // VTKMDIY_LOG_HPP

File diff suppressed because it is too large Load Diff

@ -1,14 +1,9 @@
#ifndef VTKMDIY_MPI_HPP
#define VTKMDIY_MPI_HPP
#ifndef VTKM_DIY_NO_MPI
#include <mpi.h>
#else
#include "mpi/no-mpi.hpp"
#endif
#include "mpi/constants.hpp"
#include "mpi/config.hpp"
#include "mpi/datatypes.hpp"
#include "mpi/environment.hpp"
#include "mpi/optional.hpp"
#include "mpi/status.hpp"
#include "mpi/request.hpp"
@ -18,54 +13,4 @@
#include "mpi/io.hpp"
#include "mpi/window.hpp"
namespace diy
{
namespace mpi
{
//! \ingroup MPI
struct environment
{
inline environment(int threading = MPI_THREAD_FUNNELED);
inline environment(int argc, char* argv[], int threading = MPI_THREAD_FUNNELED);
inline ~environment();
int threading() const { return provided_threading; }
int provided_threading;
};
}
}
diy::mpi::environment::
environment(int threading)
{
#ifndef VTKM_DIY_NO_MPI
int argc = 0; char** argv;
MPI_Init_thread(&argc, &argv, threading, &provided_threading);
#else
provided_threading = threading;
#endif
}
diy::mpi::environment::
environment(int argc, char* argv[], int threading)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Init_thread(&argc, &argv, threading, &provided_threading);
#else
(void) argc; (void) argv;
provided_threading = threading;
#endif
}
diy::mpi::environment::
~environment()
{
#ifndef VTKM_DIY_NO_MPI
MPI_Finalize();
#endif
}
#endif
#endif // VTKMDIY_MPI_HPP

@ -0,0 +1,161 @@
#ifdef VTKMDIY_MPI_AS_LIB
#include "collectives.hpp"
#endif
namespace diy
{
namespace mpi
{
namespace detail
{
inline void copy_buffer(const void* src, void* dst, size_t size, int count)
{
if (src != dst)
{
std::copy_n(static_cast<const int8_t*>(src),
size * static_cast<size_t>(count),
static_cast<int8_t*>(dst));
}
}
void broadcast(const communicator& comm, void* data, int count, const datatype& type, int root)
{
#if VTKMDIY_HAS_MPI
MPI_Bcast(data, count, mpi_cast(type.handle), root, mpi_cast(comm.handle()));
#else
(void) comm; (void) data; (void) count; (void) type; (void) root;
#endif
}
request ibroadcast(const communicator& comm, void* data, int count, const datatype& type, int root)
{
request r;
#if VTKMDIY_HAS_MPI
MPI_Ibcast(data, count, mpi_cast(type.handle), root, mpi_cast(comm.handle()), &mpi_cast(r.handle));
#else
(void) comm; (void) data; (void) count; (void) type; (void) root;
#endif
return r;
}
void gather(const communicator& comm,
const void* dataIn, int count, const datatype& type, void* dataOut,
int root)
{
#if VTKMDIY_HAS_MPI
MPI_Gather(dataIn, count, mpi_cast(type.handle),
dataOut, count, mpi_cast(type.handle),
root, mpi_cast(comm.handle()));
#else
copy_buffer(dataIn, dataOut, mpi_cast(type.handle), count);
(void)comm; (void)root;
#endif
}
void gather_v(const communicator& comm,
const void* dataIn, int countIn, const datatype& type,
void* dataOut, const int counts[], const int offsets[],
int root)
{
#if VTKMDIY_HAS_MPI
MPI_Gatherv(dataIn, countIn, mpi_cast(type.handle),
dataOut, counts, offsets, mpi_cast(type.handle),
root, mpi_cast(comm.handle()));
#else
copy_buffer(dataIn, dataOut, mpi_cast(type.handle), countIn);
(void)comm; (void)counts, (void)offsets, (void)root;
#endif
}
void all_gather(const communicator& comm,
const void* dataIn, int count, const datatype& type, void* dataOut)
{
#if VTKMDIY_HAS_MPI
MPI_Allgather(dataIn, count, mpi_cast(type.handle),
dataOut, count, mpi_cast(type.handle),
mpi_cast(comm.handle()));
#else
copy_buffer(dataIn, dataOut, mpi_cast(type.handle), count);
(void)comm;
#endif
}
void all_gather_v(const communicator& comm,
const void* dataIn, int countIn, const datatype& type,
void* dataOut, const int counts[], const int offsets[])
{
#if VTKMDIY_HAS_MPI
MPI_Allgatherv(dataIn, countIn, mpi_cast(type.handle),
dataOut, counts, offsets, mpi_cast(type.handle),
mpi_cast(comm.handle()));
#else
copy_buffer(dataIn, dataOut, mpi_cast(type.handle), countIn);
(void)comm; (void)counts; (void)offsets;
#endif
}
void reduce(const communicator& comm,
const void* dataIn, int count, const datatype& type, void* dataOut,
const operation& op, int root)
{
#if VTKMDIY_HAS_MPI
MPI_Reduce(dataIn, dataOut, count, mpi_cast(type.handle), mpi_cast(op.handle), root, mpi_cast(comm.handle()));
#else
copy_buffer(dataIn, dataOut, mpi_cast(type.handle), count);
(void)comm; (void)op; (void)root;
#endif
}
void all_reduce(const communicator& comm,
const void* dataIn, void* dataOut, int count, const datatype& type,
const operation& op)
{
#if VTKMDIY_HAS_MPI
MPI_Allreduce(dataIn, dataOut, count, mpi_cast(type.handle), mpi_cast(op.handle), mpi_cast(comm.handle()));
#else
copy_buffer(dataIn, dataOut, mpi_cast(type.handle), count);
(void)comm; (void)op;
#endif
}
request iall_reduce(const communicator& comm,
const void* dataIn, void* dataOut, int count, const datatype& type,
const operation& op)
{
request r;
#if VTKMDIY_HAS_MPI
MPI_Iallreduce(dataIn, dataOut, count, mpi_cast(type.handle), mpi_cast(op.handle), mpi_cast(comm.handle()), &mpi_cast(r.handle));
#else
copy_buffer(dataIn, dataOut, mpi_cast(type.handle), count);
(void)comm; (void)op;
#endif
return r;
}
void scan(const communicator& comm,
const void* dataIn, void* dataOut, int count, const datatype& type,
const operation& op)
{
#if VTKMDIY_HAS_MPI
MPI_Scan(dataIn, dataOut, count, mpi_cast(type.handle), mpi_cast(op.handle), mpi_cast(comm.handle()));
#else
copy_buffer(dataIn, dataOut, mpi_cast(type.handle), count);
(void)comm; (void)op;
#endif
}
void all_to_all(const communicator& comm,
const void* dataIn, int count, const datatype& type, void* dataOut)
{
#if VTKMDIY_HAS_MPI
MPI_Alltoall(dataIn, count, mpi_cast(type.handle), dataOut, count, mpi_cast(type.handle), mpi_cast(comm.handle()));
#else
copy_buffer(dataIn, dataOut, mpi_cast(type.handle), count);
(void)comm;
#endif
}
}
}
} // diy::mpi::detail

@ -1,12 +1,80 @@
#include <vector>
#ifndef VTKMDIY_MPI_COLLECTIVES_HPP
#define VTKMDIY_MPI_COLLECTIVES_HPP
#include "../constants.h" // for DIY_UNUSED.
#include "config.hpp"
#include "communicator.hpp"
#include "datatypes.hpp"
#include "operations.hpp"
#include "request.hpp"
#include <algorithm>
#include <vector>
#include <numeric>
namespace diy
{
namespace mpi
{
namespace detail
{
VTKMDIY_MPI_EXPORT_FUNCTION
void broadcast(const communicator& comm,
void* data, int count, const datatype& type,
int root);
VTKMDIY_MPI_EXPORT_FUNCTION
request ibroadcast(const communicator& comm,
void* data, int count, const datatype& type,
int root);
VTKMDIY_MPI_EXPORT_FUNCTION
void gather(const communicator& comm,
const void* dataIn, int count, const datatype& type, void* dataOut,
int root);
VTKMDIY_MPI_EXPORT_FUNCTION
void gather_v(const communicator& comm,
const void* dataIn, int countIn, const datatype& type,
void* dataOut, const int counts[], const int offsets[],
int root);
VTKMDIY_MPI_EXPORT_FUNCTION
void all_gather(const communicator& comm,
const void* dataIn, int count, const datatype& type, void* dataOut);
VTKMDIY_MPI_EXPORT_FUNCTION
void all_gather_v(const communicator& comm,
const void* dataIn, int countIn, const datatype& type,
void* dataOut, const int counts[], const int offsets[]);
VTKMDIY_MPI_EXPORT_FUNCTION
void reduce(const communicator& comm,
const void* dataIn, int count, const datatype& type, void* dataOut,
const operation& op, int root);
VTKMDIY_MPI_EXPORT_FUNCTION
void all_reduce(const communicator& comm,
const void* dataIn, void* dataOut, int count, const datatype& type,
const operation& op);
VTKMDIY_MPI_EXPORT_FUNCTION
request iall_reduce(const communicator& comm,
const void* dataIn, void* dataOut, int count, const datatype& type,
const operation& op);
VTKMDIY_MPI_EXPORT_FUNCTION
void scan(const communicator& comm,
const void* dataIn, void* dataOut, int count, const datatype& type,
const operation& op);
VTKMDIY_MPI_EXPORT_FUNCTION
void all_to_all(const communicator& comm,
const void* dataIn, int count, const datatype& type, void* dataOut);
} // detail
//!\addtogroup MPI
//!@{
@ -15,259 +83,181 @@ namespace mpi
{
static void broadcast(const communicator& comm, T& x, int root)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Bcast(address(x), count(x), datatype(x), root, comm);
#else
DIY_UNUSED(comm);
DIY_UNUSED(x);
DIY_UNUSED(root);
#endif
detail::broadcast(comm, address(x), count(x), datatype_of(x), root);
}
static void broadcast(const communicator& comm, std::vector<T>& x, int root)
{
#ifndef VTKM_DIY_NO_MPI
size_t sz = x.size();
Collectives<size_t, void*>::broadcast(comm, sz, root);
detail::broadcast(comm, &sz, 1, datatype_of(sz), root);
if (comm.rank() != root)
x.resize(sz);
MPI_Bcast(address(x), count(x), datatype(x), root, comm);
#else
DIY_UNUSED(comm);
DIY_UNUSED(x);
DIY_UNUSED(root);
#endif
detail::broadcast(comm, address(x), count(x), datatype_of(x), root);
}
static request ibroadcast(const communicator& comm, T& x, int root)
{
#ifndef VTKM_DIY_NO_MPI
request r;
MPI_Ibcast(address(x), count(x), datatype(x), root, comm, &r.r);
return r;
#else
DIY_UNUSED(comm);
DIY_UNUSED(x);
DIY_UNUSED(root);
DIY_UNSUPPORTED_MPI_CALL(MPI_Ibcast);
#endif
return detail::ibroadcast(comm, address(x), count(x), datatype_of(x), root);
}
static void gather(const communicator& comm, const T& in, std::vector<T>& out, int root)
{
out.resize(comm.size());
#ifndef VTKM_DIY_NO_MPI
MPI_Gather(address(in), count(in), datatype(in), address(out), count(in), datatype(out), root, comm);
#else
DIY_UNUSED(comm);
DIY_UNUSED(root);
out[0] = in;
#endif
detail::gather(comm, address(in), count(in), datatype_of(in), address(out), root);
}
static void gather(const communicator& comm, const std::vector<T>& in, std::vector< std::vector<T> >& out, int root)
{
#ifndef VTKM_DIY_NO_MPI
std::vector<int> counts(comm.size());
std::vector<int> counts;
if (comm.rank() == root)
{
counts.resize(static_cast<size_t>(comm.size()));
}
Collectives<int,void*>::gather(comm, count(in), counts, root);
std::vector<int> offsets(comm.size(), 0);
for (unsigned i = 1; i < offsets.size(); ++i)
offsets[i] = offsets[i-1] + counts[i-1];
std::vector<int> offsets;
if (comm.rank() == root)
{
offsets.resize(counts.size());
offsets[0] = 0;
std::partial_sum(counts.begin(), counts.end() - 1, offsets.begin() + 1);
}
int elem_size = count(in[0]); // size of 1 vector element in units of mpi datatype
std::vector<T> buffer((offsets.back() + counts.back()) / elem_size);
MPI_Gatherv(address(in), count(in), datatype(in),
address(buffer),
&counts[0],
&offsets[0],
datatype(buffer),
root, comm);
out.resize(comm.size());
size_t cur = 0;
for (unsigned i = 0; i < (unsigned)comm.size(); ++i)
std::vector<T> buffer;
if (comm.rank() == root)
{
out[i].reserve(counts[i] / elem_size);
for (unsigned j = 0; j < (unsigned)(counts[i] / elem_size); ++j)
out[i].push_back(buffer[cur++]);
buffer.resize((offsets.back() + counts.back()) / elem_size);
}
detail::gather_v(comm, address(in), count(in), datatype_of(in),
address(buffer), counts.data(), offsets.data(),
root);
if (comm.rank() == root)
{
out.resize(static_cast<size_t>(comm.size()));
size_t offset = 0;
for (size_t i = 0; i < out.size(); ++i)
{
auto count = static_cast<size_t>(counts[i] / elem_size);
out[i].insert(out[i].end(), buffer.data() + offset, buffer.data() + offset + count);
offset += count;
}
}
#else
DIY_UNUSED(comm);
DIY_UNUSED(root);
out.resize(1);
out[0] = in;
#endif
}
static void gather(const communicator& comm, const T& in, int root)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Gather(address(in), count(in), datatype(in), address(in), count(in), datatype(in), root, comm);
#else
DIY_UNUSED(comm);
DIY_UNUSED(in);
DIY_UNUSED(root);
DIY_UNSUPPORTED_MPI_CALL("MPI_Gather");
#endif
detail::gather(comm, address(in), count(in), datatype_of(in), address(in), root);
}
static void gather(const communicator& comm, const std::vector<T>& in, int root)
{
#ifndef VTKM_DIY_NO_MPI
Collectives<int,void*>::gather(comm, count(in), root);
MPI_Gatherv(address(in), count(in), datatype(in),
0, 0, 0,
datatype(in),
root, comm);
#else
DIY_UNUSED(comm);
DIY_UNUSED(in);
DIY_UNUSED(root);
DIY_UNSUPPORTED_MPI_CALL("MPI_Gatherv");
#endif
detail::gather_v(comm, address(in), count(in), datatype_of(in), 0, 0, 0, root);
}
static void all_gather(const communicator& comm, const T& in, std::vector<T>& out)
{
out.resize(comm.size());
#ifndef VTKM_DIY_NO_MPI
MPI_Allgather(address(in), count(in), datatype(in),
address(out), count(in), datatype(in),
comm);
#else
DIY_UNUSED(comm);
out[0] = in;
#endif
detail::all_gather(comm, address(in), count(in), datatype_of(in), address(out));
}
static void all_gather(const communicator& comm, const std::vector<T>& in, std::vector< std::vector<T> >& out)
{
#ifndef VTKM_DIY_NO_MPI
std::vector<int> counts(comm.size());
std::vector<int> counts(static_cast<size_t>(comm.size()));
Collectives<int,void*>::all_gather(comm, count(in), counts);
std::vector<int> offsets(comm.size(), 0);
for (unsigned i = 1; i < offsets.size(); ++i)
offsets[i] = offsets[i-1] + counts[i-1];
std::vector<int> offsets(counts.size());
offsets[0] = 0;
std::partial_sum(counts.begin(), counts.end() - 1, offsets.begin() + 1);
int elem_size = count(in[0]); // size of 1 vector element in units of mpi datatype
std::vector<T> buffer((offsets.back() + counts.back()) / elem_size);
MPI_Allgatherv(address(in), count(in), datatype(in),
address(buffer),
&counts[0],
&offsets[0],
datatype(buffer),
comm);
detail::all_gather_v(comm,
address(in), count(in), datatype_of(in),
address(buffer),
&counts[0],
&offsets[0]);
out.resize(comm.size());
size_t cur = 0;
for (int i = 0; i < comm.size(); ++i)
out.resize(static_cast<size_t>(comm.size()));
size_t offset = 0;
for (size_t i = 0; i < out.size(); ++i)
{
out[i].reserve(counts[i] / elem_size);
for (int j = 0; j < (int)(counts[i] / elem_size); ++j)
out[i].push_back(buffer[cur++]);
auto count = static_cast<size_t>(counts[i] / elem_size);
out[i].insert(out[i].end(), buffer.data() + offset, buffer.data() + offset + count);
offset += count;
}
#else
DIY_UNUSED(comm);
out.resize(1);
out[0] = in;
#endif
}
static void reduce(const communicator& comm, const T& in, T& out, int root, const Op&)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Reduce(address(in), address(out), count(in), datatype(in),
detail::mpi_op<Op>::get(),
root, comm);
#else
DIY_UNUSED(comm);
DIY_UNUSED(root);
out = in;
#endif
auto op = detail::mpi_op<Op>::get();
detail::reduce(comm, address(in), count(in), datatype_of(in), address(out), op, root);
}
static void reduce(const communicator& comm, const T& in, int root, const Op&)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Reduce(address(in), address(in), count(in), datatype(in),
detail::mpi_op<Op>::get(),
root, comm);
#else
DIY_UNUSED(comm);
DIY_UNUSED(in);
DIY_UNUSED(root);
DIY_UNSUPPORTED_MPI_CALL("MPI_Reduce");
#endif
auto op = detail::mpi_op<Op>::get();
detail::reduce(comm, address(in), count(in), datatype_of(in), address(in), op, root);
}
static void all_reduce(const communicator& comm, const T& in, T& out, const Op&)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Allreduce(address(in), address(out), count(in), datatype(in),
detail::mpi_op<Op>::get(),
comm);
#else
DIY_UNUSED(comm);
out = in;
#endif
auto op = detail::mpi_op<Op>::get();
detail::all_reduce(comm, address(in), address(out), count(in), datatype_of(in), op);
}
static void all_reduce(const communicator& comm, const std::vector<T>& in, std::vector<T>& out, const Op&)
{
#ifndef VTKM_DIY_NO_MPI
auto op = detail::mpi_op<Op>::get();
out.resize(in.size());
MPI_Allreduce(address(in), address(out), count(in),
datatype(in),
detail::mpi_op<Op>::get(),
comm);
#else
DIY_UNUSED(comm);
out = in;
#endif
detail::all_reduce(comm, address(in), address(out), count(in), datatype_of(in), op);
}
static request iall_reduce(const communicator& comm, const T& in, T& out, const Op&)
{
auto op = detail::mpi_op<Op>::get();
return detail::iall_reduce(comm, address(in), address(out), count(in), datatype_of(in), op);
}
static request iall_reduce(const communicator& comm, const std::vector<T>& in, std::vector<T>& out, const Op&)
{
auto op = detail::mpi_op<Op>::get();
out.resize(in.size());
return detail::iall_reduce(comm, address(in), address(out), count(in), datatype_of(in), op);
}
static void scan(const communicator& comm, const T& in, T& out, const Op&)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Scan(address(in), address(out), count(in), datatype(in),
detail::mpi_op<Op>::get(),
comm);
#else
DIY_UNUSED(comm);
out = in;
#endif
auto op = detail::mpi_op<Op>::get();
detail::scan(comm, address(in), address(out), count(in), datatype_of(in), op);
}
static void all_to_all(const communicator& comm, const std::vector<T>& in, std::vector<T>& out, int n = 1)
{
#ifndef VTKM_DIY_NO_MPI
// n specifies how many elements go to/from every process from every process;
// the sizes of in and out are expected to be n * comm.size()
int elem_size = count(in[0]); // size of 1 vector element in units of mpi datatype
// NB: this will fail if T is a vector
MPI_Alltoall(address(in),
elem_size * n,
datatype(in),
address(out),
elem_size * n,
datatype(out),
comm);
#else
DIY_UNUSED(comm);
DIY_UNUSED(n);
out = in;
#endif
detail::all_to_all(comm, address(in), elem_size * n, datatype_of(in), address(out));
}
};
//! iBarrier; standalone function version for completeness
inline request ibarrier(const communicator& comm)
{
return comm.ibarrier();
}
//! Broadcast to all processes in `comm`.
template<class T>
inline
void broadcast(const communicator& comm, T& x, int root)
{
Collectives<T,void*>::broadcast(comm, x, root);
@ -275,6 +265,7 @@ namespace mpi
//! Broadcast for vectors
template<class T>
inline
void broadcast(const communicator& comm, std::vector<T>& x, int root)
{
Collectives<T,void*>::broadcast(comm, x, root);
@ -291,6 +282,7 @@ namespace mpi
//! On `root` process, `out` is resized to `comm.size()` and filled with
//! elements from the respective ranks.
template<class T>
inline
void gather(const communicator& comm, const T& in, std::vector<T>& out, int root)
{
Collectives<T,void*>::gather(comm, in, out, root);
@ -298,6 +290,7 @@ namespace mpi
//! Same as above, but for vectors.
template<class T>
inline
void gather(const communicator& comm, const std::vector<T>& in, std::vector< std::vector<T> >& out, int root)
{
Collectives<T,void*>::gather(comm, in, out, root);
@ -305,6 +298,7 @@ namespace mpi
//! Simplified version (without `out`) for use on non-root processes.
template<class T>
inline
void gather(const communicator& comm, const T& in, int root)
{
Collectives<T,void*>::gather(comm, in, root);
@ -312,6 +306,7 @@ namespace mpi
//! Simplified version (without `out`) for use on non-root processes.
template<class T>
inline
void gather(const communicator& comm, const std::vector<T>& in, int root)
{
Collectives<T,void*>::gather(comm, in, root);
@ -321,6 +316,7 @@ namespace mpi
//! `out` is resized to `comm.size()` and filled with
//! elements from the respective ranks.
template<class T>
inline
void all_gather(const communicator& comm, const T& in, std::vector<T>& out)
{
Collectives<T,void*>::all_gather(comm, in, out);
@ -328,6 +324,7 @@ namespace mpi
//! Same as above, but for vectors.
template<class T>
inline
void all_gather(const communicator& comm, const std::vector<T>& in, std::vector< std::vector<T> >& out)
{
Collectives<T,void*>::all_gather(comm, in, out);
@ -335,6 +332,7 @@ namespace mpi
//! reduce
template<class T, class Op>
inline
void reduce(const communicator& comm, const T& in, T& out, int root, const Op& op)
{
Collectives<T, Op>::reduce(comm, in, out, root, op);
@ -342,6 +340,7 @@ namespace mpi
//! Simplified version (without `out`) for use on non-root processes.
template<class T, class Op>
inline
void reduce(const communicator& comm, const T& in, int root, const Op& op)
{
Collectives<T, Op>::reduce(comm, in, root, op);
@ -349,6 +348,7 @@ namespace mpi
//! all_reduce
template<class T, class Op>
inline
void all_reduce(const communicator& comm, const T& in, T& out, const Op& op)
{
Collectives<T, Op>::all_reduce(comm, in, out, op);
@ -356,13 +356,32 @@ namespace mpi
//! Same as above, but for vectors.
template<class T, class Op>
inline
void all_reduce(const communicator& comm, const std::vector<T>& in, std::vector<T>& out, const Op& op)
{
Collectives<T, Op>::all_reduce(comm, in, out, op);
}
//! iall_reduce
template<class T, class Op>
inline
request iall_reduce(const communicator& comm, const T& in, T& out, const Op& op)
{
return Collectives<T, Op>::iall_reduce(comm, in, out, op);
}
//! Same as above, but for vectors.
template<class T, class Op>
inline
request iall_reduce(const communicator& comm, const std::vector<T>& in, std::vector<T>& out, const Op& op)
{
return Collectives<T, Op>::iall_reduce(comm, in, out, op);
}
//! scan
template<class T, class Op>
inline
void scan(const communicator& comm, const T& in, T& out, const Op& op)
{
Collectives<T, Op>::scan(comm, in, out, op);
@ -370,6 +389,7 @@ namespace mpi
//! all_to_all
template<class T>
inline
void all_to_all(const communicator& comm, const std::vector<T>& in, std::vector<T>& out, int n = 1)
{
Collectives<T, void*>::all_to_all(comm, in, out, n);
@ -378,3 +398,9 @@ namespace mpi
//!@}
}
}
#ifndef VTKMDIY_MPI_AS_LIB
#include "collectives.cpp"
#endif
#endif // VTKMDIY_MPI_COLLECTIVES_HPP

@ -0,0 +1,130 @@
#ifdef VTKMDIY_MPI_AS_LIB
#include "communicator.hpp"
#endif
diy::mpi::communicator::communicator()
: comm_(make_DIY_MPI_Comm(MPI_COMM_WORLD)), rank_(0), size_(1), owner_(false)
{
#if VTKMDIY_HAS_MPI
MPI_Comm_rank(mpi_cast(comm_), &rank_);
MPI_Comm_size(mpi_cast(comm_), &size_);
#endif
}
diy::mpi::communicator::
communicator(DIY_MPI_Comm comm, bool owner):
comm_(comm), rank_(0), size_(1), owner_(owner)
{
#if VTKMDIY_HAS_MPI
if (mpi_cast(comm_) != MPI_COMM_NULL)
{
MPI_Comm_rank(mpi_cast(comm_), &rank_);
MPI_Comm_size(mpi_cast(comm_), &size_);
}
#endif
}
#ifndef VTKMDIY_MPI_AS_LIB // only available in header-only mode
diy::mpi::communicator::
communicator(MPI_Comm comm, bool owner):
comm_(comm), rank_(0), size_(1), owner_(owner)
{
#if VTKMDIY_HAS_MPI
if (comm_ != MPI_COMM_NULL)
{
MPI_Comm_rank(comm_, &rank_);
MPI_Comm_size(comm_, &size_);
}
#endif
}
#endif
void
diy::mpi::communicator::
destroy()
{
#if VTKMDIY_HAS_MPI
if (owner_)
MPI_Comm_free(&mpi_cast(comm_));
#endif
}
diy::mpi::status
diy::mpi::communicator::
probe(int source, int tag) const
{
#if VTKMDIY_HAS_MPI
status s;
MPI_Probe(source, tag, mpi_cast(comm_), &mpi_cast(s.handle));
return s;
#else
(void) source; (void) tag;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_Probe);
#endif
}
diy::mpi::optional<diy::mpi::status>
diy::mpi::communicator::
iprobe(int source, int tag) const
{
(void) source; (void) tag;
#if VTKMDIY_HAS_MPI
status s;
int flag;
MPI_Iprobe(source, tag, mpi_cast(comm_), &flag, &mpi_cast(s.handle));
if (flag)
return s;
#endif
return optional<status>();
}
void
diy::mpi::communicator::
barrier() const
{
#if VTKMDIY_HAS_MPI
MPI_Barrier(mpi_cast(comm_));
#endif
}
diy::mpi::communicator
diy::mpi::communicator::
split(int color, int key) const
{
#if VTKMDIY_HAS_MPI
DIY_MPI_Comm newcomm;
MPI_Comm_split(mpi_cast(comm_), color, key, &mpi_cast(newcomm));
return communicator(newcomm, true);
#else
(void) color; (void) key;
return communicator();
#endif
}
diy::mpi::request
diy::mpi::communicator::
ibarrier() const
{
#if VTKMDIY_HAS_MPI
request r;
MPI_Ibarrier(mpi_cast(comm_), &mpi_cast(r.handle));
return r;
#else
// this is not the ideal fix; in principle we should just return a status
// that tests true, but this requires redesigning some parts of our no-mpi
// handling
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_Ibarrier);
#endif
}
void
diy::mpi::communicator::
duplicate(const communicator& other)
{
#if VTKMDIY_HAS_MPI
DIY_MPI_Comm newcomm;
MPI_Comm_dup(mpi_cast(other.comm_), &mpi_cast(newcomm));
(*this) = communicator(newcomm,true);
#endif
(void) other;
}

@ -1,3 +1,12 @@
#ifndef VTKMDIY_MPI_COMMUNICATOR_HPP
#define VTKMDIY_MPI_COMMUNICATOR_HPP
#include "config.hpp"
#include "optional.hpp"
#include "point-to-point.hpp"
#include "request.hpp"
#include "status.hpp"
namespace diy
{
namespace mpi
@ -8,8 +17,14 @@ namespace mpi
class communicator
{
public:
inline
communicator(MPI_Comm comm = MPI_COMM_WORLD, bool owner = false);
VTKMDIY_MPI_EXPORT_FUNCTION
communicator();
communicator(DIY_MPI_Comm comm):
communicator(comm, false) {}
VTKMDIY_MPI_EXPORT_FUNCTION
communicator(DIY_MPI_Comm comm, bool owner);
~communicator() { destroy(); }
@ -25,9 +40,19 @@ namespace mpi
size_(other.size_),
owner_(other.owner_) { other.owner_ = false; }
communicator&
#ifndef VTKMDIY_MPI_AS_LIB // only available in header-only mode
communicator(MPI_Comm comm):
communicator(comm, false) {}
VTKMDIY_MPI_EXPORT_FUNCTION
communicator(MPI_Comm comm, bool owner);
operator MPI_Comm() { return comm_; }
#endif
communicator&
operator=(const communicator& other) { destroy(); comm_ = other.comm_; rank_ = other.rank_; size_ = other.size_; owner_ = false; return *this; }
communicator&
communicator&
operator=(communicator&& other) { destroy(); comm_ = other.comm_; rank_ = other.rank_; size_ = other.size_; owner_ = other.owner_; other.owner_ = false; return *this; }
int rank() const { return rank_; }
@ -35,193 +60,71 @@ namespace mpi
//! Send `x` to processor `dest` using `tag` (blocking).
template<class T>
void send(int dest, int tag, const T& x) const { detail::send<T>()(comm_, dest, tag, x); }
void send(int dest, int tag, const T& x) const { detail::send(comm_, dest, tag, x); }
//! Receive `x` from `dest` using `tag` (blocking).
//! If `T` is an `std::vector<...>`, `recv` will resize it to fit exactly the sent number of values.
template <class T>
status recv(int source, int tag, T &x) const
{
#if defined(VTKM_DIY_NO_MPI) && defined(__CUDACC_VER_MAJOR__) && __CUDACC_VER_MAJOR__ < 8 // CUDA 7.5 workaround
(void) source; (void)tag; (void)x;
DIY_UNSUPPORTED_MPI_CALL(MPI_Recv);
#else
return detail::recv<T>{}(comm_, source, tag, x);
#endif
}
template<class T>
status recv(int source, int tag, T& x) const { return detail::recv(comm_, source, tag, x); }
//! Non-blocking version of `send()`.
template <class T>
request isend(int dest, int tag, const T &x) const
{
#if defined(VTKM_DIY_NO_MPI) && defined(__CUDACC_VER_MAJOR__) && __CUDACC_VER_MAJOR__ < 8 // CUDA 7.5 workaround
(void) dest; (void)tag; (void)x;
DIY_UNSUPPORTED_MPI_CALL(MPI_Send);
#else
return detail::isend<T>{}(comm_, dest, tag, x);
#endif
}
template<class T>
request isend(int dest, int tag, const T& x) const { return detail::isend(comm_, dest, tag, x); }
//! Non-blocking version of `ssend()`.
template<class T>
request issend(int dest, int tag, const T& x) const { return detail::issend<T>()(comm_, dest, tag, x); }
request issend(int dest, int tag, const T& x) const { return detail::issend(comm_, dest, tag, x); }
//! Non-blocking version of `recv()`.
//! If `T` is an `std::vector<...>`, its size must be big enough to accommodate the sent values.
template <class T>
request irecv(int source, int tag, T &x) const
{
#if defined(VTKM_DIY_NO_MPI) && defined(__CUDACC_VER_MAJOR__) && __CUDACC_VER_MAJOR__ < 8 // CUDA 7.5 workaround
(void)source; (void)tag; (void)x;
DIY_UNSUPPORTED_MPI_CALL(MPI_Irecv);
#else
return detail::irecv<T>()(comm_, source, tag, x);
#endif
}
template<class T>
request irecv(int source, int tag, T& x) const { return detail::irecv(comm_, source, tag, x); }
//! probe
inline
VTKMDIY_MPI_EXPORT_FUNCTION
status probe(int source, int tag) const;
//! iprobe
inline
VTKMDIY_MPI_EXPORT_FUNCTION
optional<status>
iprobe(int source, int tag) const;
//! barrier
inline
VTKMDIY_MPI_EXPORT_FUNCTION
void barrier() const;
//! Nonblocking version of barrier
inline
VTKMDIY_MPI_EXPORT_FUNCTION
request ibarrier() const;
operator MPI_Comm() const { return comm_; }
//! split
//! When keys are the same, the ties are broken by the rank in the original comm.
inline
VTKMDIY_MPI_EXPORT_FUNCTION
communicator
split(int color, int key = 0) const;
//! duplicate
inline
VTKMDIY_MPI_EXPORT_FUNCTION
void duplicate(const communicator& other);
DIY_MPI_Comm handle() const { return comm_; }
private:
inline
VTKMDIY_MPI_EXPORT_FUNCTION
void destroy();
private:
MPI_Comm comm_;
int rank_;
int size_;
bool owner_;
DIY_MPI_Comm comm_;
int rank_;
int size_;
bool owner_;
};
}
}
diy::mpi::communicator::
communicator(MPI_Comm comm, bool owner):
comm_(comm), rank_(0), size_(1), owner_(owner)
{
#ifndef VTKM_DIY_NO_MPI
if (comm != MPI_COMM_NULL)
{
MPI_Comm_rank(comm_, &rank_);
MPI_Comm_size(comm_, &size_);
}
}
} // diy::mpi
#ifndef VTKMDIY_MPI_AS_LIB
#include "communicator.cpp"
#endif
}
void
diy::mpi::communicator::
destroy()
{
#ifndef VTKM_DIY_NO_MPI
if (owner_)
MPI_Comm_free(&comm_);
#endif
}
diy::mpi::status
diy::mpi::communicator::
probe(int source, int tag) const
{
(void) source;
(void) tag;
#ifndef VTKM_DIY_NO_MPI
status s;
MPI_Probe(source, tag, comm_, &s.s);
return s;
#else
DIY_UNSUPPORTED_MPI_CALL(MPI_Probe);
#endif
}
diy::mpi::optional<diy::mpi::status>
diy::mpi::communicator::
iprobe(int source, int tag) const
{
(void) source;
(void) tag;
#ifndef VTKM_DIY_NO_MPI
status s;
int flag;
MPI_Iprobe(source, tag, comm_, &flag, &s.s);
if (flag)
return s;
#endif
return optional<status>();
}
void
diy::mpi::communicator::
barrier() const
{
#ifndef VTKM_DIY_NO_MPI
MPI_Barrier(comm_);
#endif
}
diy::mpi::communicator
diy::mpi::communicator::
split(int color, int key) const
{
#ifndef VTKM_DIY_NO_MPI
MPI_Comm newcomm;
MPI_Comm_split(comm_, color, key, &newcomm);
return communicator(newcomm, true);
#else
return communicator();
#endif
}
diy::mpi::request
diy::mpi::communicator::
ibarrier() const
{
#ifndef VTKM_DIY_NO_MPI
request r_;
MPI_Ibarrier(comm_, &r_.r);
return r_;
#else
// this is not the ideal fix; in principle we should just return a status
// that tests true, but this requires redesigning some parts of our no-mpi
// handling
DIY_UNSUPPORTED_MPI_CALL(MPI_Ibarrier);
#endif
}
void
diy::mpi::communicator::
duplicate(const communicator& other)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Comm newcomm;
MPI_Comm_dup(other.comm_, &newcomm);
(*this) = communicator(newcomm,true);
#endif
}
#endif // VTKMDIY_MPI_COMMUNICATOR_HPP

@ -0,0 +1,70 @@
#ifndef VTKMDIY_MPI_CONFIG_HPP
#define VTKMDIY_MPI_CONFIG_HPP
/// We want to allow the use of `diy::mpi` in either header-only or library mode.
/// VTKMDIY_MPI_AS_LIB is defined when using library mode.
/// This file contains some configuration macros. To maintain backwards compatibility
/// suitable default values should be defined when using header-only mode.
/// VTKMDIY_HAS_MPI should always be defined when VTKMDIY_MPI_AS_LIB is defined, but only for
/// the compilation units that are part of the library.
/// VTKMDIY_HAS_MPI=1 means MPI library is availalbe.
/// For header-only, the default is to assume MPI is available
#if !defined(VTKMDIY_MPI_AS_LIB) && !defined(VTKMDIY_HAS_MPI)
# define VTKMDIY_HAS_MPI 1
#endif
/// Include appropriate mpi header. Since VTKMDIY_HAS_MPI is only defined for
/// the compilation units of the library, when in library mode, the header is
/// only included for the library's compilation units.
#ifdef VTKMDIY_HAS_MPI
# if VTKMDIY_HAS_MPI
# include <mpi.h>
# else
# include "no-mpi.hpp"
# endif
#endif
/// Classes and objects that need to be visible to clients of the library should be
/// marked as VTKMDIY_MPI_EXPORT. Similarly API functions should be marked as
/// VTKMDIY_MPI_EXPORT_FUNCTION.
#include "diy-mpi-export.h" // defines VTKMDIY_MPI_EXPORT and VTKMDIY_MPI_EXPORT_FUNCTION
/// Define alisases for MPI types
#ifdef VTKMDIY_MPI_AS_LIB
# include "mpitypes.hpp" // only configured in library mode
#else // ifdef VTKMDIY_MPI_AS_LIB
namespace diy
{
namespace mpi
{
#define DEFINE_DIY_MPI_TYPE(mpitype) \
struct DIY_##mpitype { \
DIY_##mpitype() = default; \
DIY_##mpitype(const mpitype& obj) : data(obj) {} \
DIY_##mpitype& operator=(const mpitype& obj) { data = obj; return *this; } \
operator mpitype() { return data; } \
mpitype data; \
};
DEFINE_DIY_MPI_TYPE(MPI_Comm)
DEFINE_DIY_MPI_TYPE(MPI_Datatype)
DEFINE_DIY_MPI_TYPE(MPI_Status)
DEFINE_DIY_MPI_TYPE(MPI_Request)
DEFINE_DIY_MPI_TYPE(MPI_Op)
DEFINE_DIY_MPI_TYPE(MPI_File)
DEFINE_DIY_MPI_TYPE(MPI_Win)
#undef DEFINE_DIY_MPI_TYPE
}
} // diy::mpi
#endif // ifdef VTKMDIY_MPI_AS_LIB
#ifdef VTKMDIY_HAS_MPI
# include "mpi_cast.hpp"
#endif
#endif // VTKMDIY_MPI_CONFIG_HPP

@ -1,13 +0,0 @@
#ifndef VTKMDIY_MPI_CONSTANTS_HPP
#define VTKMDIY_MPI_CONSTANTS_HPP
namespace diy
{
namespace mpi
{
const int any_source = MPI_ANY_SOURCE;
const int any_tag = MPI_ANY_TAG;
}
}
#endif

@ -0,0 +1,34 @@
#ifdef VTKMDIY_MPI_AS_LIB
#include "datatypes.hpp"
#endif
namespace diy
{
namespace mpi
{
namespace detail
{
#define DIY_MPI_DATATYPE_MAP(cpp_type, mpi_type) \
template<> datatype get_mpi_datatype<cpp_type>() { \
return datatype(make_DIY_MPI_Datatype(mpi_type)); \
}
DIY_MPI_DATATYPE_MAP(char, MPI_BYTE)
DIY_MPI_DATATYPE_MAP(unsigned char, MPI_BYTE)
DIY_MPI_DATATYPE_MAP(bool, MPI_BYTE)
DIY_MPI_DATATYPE_MAP(int, MPI_INT)
DIY_MPI_DATATYPE_MAP(unsigned, MPI_UNSIGNED)
DIY_MPI_DATATYPE_MAP(long, MPI_LONG)
DIY_MPI_DATATYPE_MAP(unsigned long, MPI_UNSIGNED_LONG)
DIY_MPI_DATATYPE_MAP(long long, MPI_LONG_LONG_INT)
DIY_MPI_DATATYPE_MAP(unsigned long long, MPI_UNSIGNED_LONG_LONG)
DIY_MPI_DATATYPE_MAP(float, MPI_FLOAT)
DIY_MPI_DATATYPE_MAP(double, MPI_DOUBLE)
#undef DIY_MPI_DATATYPE_MAP
}
}
} // diy::mpi::detail

@ -1,16 +1,31 @@
#ifndef VTKMDIY_MPI_DATATYPES_HPP
#define VTKMDIY_MPI_DATATYPES_HPP
#include "config.hpp"
#include <vector>
#include <array>
namespace diy
{
namespace mpi
{
struct datatype
{
datatype() = default;
datatype(const DIY_MPI_Datatype& dt) : handle(dt) {}
#ifndef VTKMDIY_MPI_AS_LIB // only available in header-only mode
datatype(const MPI_Datatype& dt) : handle(dt) {}
operator MPI_Datatype() { return handle; }
#endif
DIY_MPI_Datatype handle;
};
namespace detail
{
template<class T> MPI_Datatype get_mpi_datatype();
struct true_type {};
struct false_type {};
@ -18,28 +33,34 @@ namespace detail
template<class T>
struct is_mpi_datatype { typedef false_type type; };
#define VTKMDIY_MPI_DATATYPE_MAP(cpp_type, mpi_type) \
template<> inline MPI_Datatype get_mpi_datatype<cpp_type>() { return mpi_type; } \
template<> struct is_mpi_datatype<cpp_type> { typedef true_type type; }; \
template<> struct is_mpi_datatype< std::vector<cpp_type> > { typedef true_type type; };
template<class T> datatype get_mpi_datatype();
VTKMDIY_MPI_DATATYPE_MAP(char, MPI_BYTE);
VTKMDIY_MPI_DATATYPE_MAP(unsigned char, MPI_BYTE);
VTKMDIY_MPI_DATATYPE_MAP(bool, MPI_BYTE);
VTKMDIY_MPI_DATATYPE_MAP(int, MPI_INT);
VTKMDIY_MPI_DATATYPE_MAP(unsigned, MPI_UNSIGNED);
VTKMDIY_MPI_DATATYPE_MAP(long, MPI_LONG);
VTKMDIY_MPI_DATATYPE_MAP(unsigned long, MPI_UNSIGNED_LONG);
VTKMDIY_MPI_DATATYPE_MAP(long long, MPI_LONG_LONG_INT);
VTKMDIY_MPI_DATATYPE_MAP(unsigned long long, MPI_UNSIGNED_LONG_LONG);
VTKMDIY_MPI_DATATYPE_MAP(float, MPI_FLOAT);
VTKMDIY_MPI_DATATYPE_MAP(double, MPI_DOUBLE);
#define VTKMDIY_MPI_DATATYPE_DEFAULT(cpp_type) \
template<> VTKMDIY_MPI_EXPORT_FUNCTION datatype get_mpi_datatype<cpp_type>(); \
template<> struct is_mpi_datatype< cpp_type > { typedef true_type type; }; \
template<> struct is_mpi_datatype< std::vector<cpp_type> > { typedef true_type type; }; \
template<size_t N> \
struct is_mpi_datatype< std::array<cpp_type, N> > { typedef true_type type; };
/* mpi_datatype: helper routines, specialized for std::vector<...> */
VTKMDIY_MPI_DATATYPE_DEFAULT(char)
VTKMDIY_MPI_DATATYPE_DEFAULT(unsigned char)
VTKMDIY_MPI_DATATYPE_DEFAULT(bool)
VTKMDIY_MPI_DATATYPE_DEFAULT(int)
VTKMDIY_MPI_DATATYPE_DEFAULT(unsigned)
VTKMDIY_MPI_DATATYPE_DEFAULT(long)
VTKMDIY_MPI_DATATYPE_DEFAULT(unsigned long)
VTKMDIY_MPI_DATATYPE_DEFAULT(long long)
VTKMDIY_MPI_DATATYPE_DEFAULT(unsigned long long)
VTKMDIY_MPI_DATATYPE_DEFAULT(float)
VTKMDIY_MPI_DATATYPE_DEFAULT(double)
#undef VTKMDIY_MPI_DATATYPE_DEFAULT
/* mpi_datatype: helper routines, specialized for std::vector<...>, std::array<...> */
template<class T>
struct mpi_datatype
{
static MPI_Datatype datatype() { return get_mpi_datatype<T>(); }
static diy::mpi::datatype datatype() { return get_mpi_datatype<T>(); }
static const void* address(const T& x) { return &x; }
static void* address(T& x) { return &x; }
static int count(const T&) { return 1; }
@ -50,44 +71,53 @@ namespace detail
{
typedef std::vector<U> VecU;
static MPI_Datatype datatype() { return mpi_datatype<U>::datatype(); }
static diy::mpi::datatype datatype() { return mpi_datatype<U>::datatype(); }
static const void* address(const VecU& x) { return x.data(); }
static void* address(VecU& x) { return x.data(); }
static int count(const VecU& x) { return x.empty() ? 0 : (static_cast<int>(x.size()) * mpi_datatype<U>::count(x[0])); }
};
template<class U, size_t D>
struct mpi_datatype< std::array<U,D> >
{
typedef std::array<U,D> ArrayU;
static diy::mpi::datatype datatype() { return mpi_datatype<U>::datatype(); }
static const void* address(const ArrayU& x) { return x.data(); }
static void* address(ArrayU& x) { return x.data(); }
static int count(const ArrayU& x) { return x.empty() ? 0 : (static_cast<int>(x.size()) * mpi_datatype<U>::count(x[0])); }
};
} // detail
template<class U>
static MPI_Datatype datatype(const U&)
static datatype datatype_of(const U&)
{
using Datatype = detail::mpi_datatype<U>;
return Datatype::datatype();
return detail::mpi_datatype<U>::datatype();
}
template<class U>
static void* address(const U& x)
{
using Datatype = detail::mpi_datatype<U>;
return const_cast<void*>(Datatype::address(x));
return const_cast<void*>(detail::mpi_datatype<U>::address(x));
}
template<class U>
static void* address(U& x)
{
using Datatype = detail::mpi_datatype<U>;
return Datatype::address(x);
return detail::mpi_datatype<U>::address(x);
}
template<class U>
static int count(const U& x)
{
using Datatype = detail::mpi_datatype<U>;
return Datatype::count(x);
return detail::mpi_datatype<U>::count(x);
}
} // mpi
} // diy
#ifndef VTKMDIY_MPI_AS_LIB
#include "datatypes.cpp"
#endif
#endif // VTKMDIY_MPI_DATATYPES_HPP

@ -0,0 +1,49 @@
#ifndef VTKMDIY_MPI_EXPORT_H
#define VTKMDIY_MPI_EXPORT_H
#if defined(_MSC_VER)
# ifdef VTKMDIY_MPI_STATIC_BUILD
/* This is a static component and has no need for exports
elf based static libraries are able to have hidden/default visibility
controls on symbols so we should propagate this information in that
use case
*/
# define VTKMDIY_MPI_EXPORT_DEFINE
# define VTKMDIY_MPI_IMPORT_DEFINE
# define VTKMDIY_MPI_NO_EXPORT_DEFINE
# else
# define VTKMDIY_MPI_EXPORT_DEFINE __declspec(dllexport)
# define VTKMDIY_MPI_IMPORT_DEFINE __declspec(dllimport)
# define VTKMDIY_MPI_NO_EXPORT_DEFINE
# endif
#else
# define VTKMDIY_MPI_EXPORT_DEFINE __attribute__((visibility("default")))
# define VTKMDIY_MPI_IMPORT_DEFINE __attribute__((visibility("default")))
# define VTKMDIY_MPI_NO_EXPORT_DEFINE __attribute__((visibility("hidden")))
#endif
#ifndef VTKMDIY_MPI_EXPORT
# if !defined(VTKMDIY_MPI_AS_LIB)
# define VTKMDIY_MPI_EXPORT
# define VTKMDIY_MPI_EXPORT_FUNCTION inline
# else
# if defined(VTKMDIY_HAS_MPI)
/* We are building this library */
# define VTKMDIY_MPI_EXPORT VTKMDIY_MPI_EXPORT_DEFINE
# else
/* We are using this library */
# define VTKMDIY_MPI_EXPORT VTKMDIY_MPI_IMPORT_DEFINE
# endif
# define VTKMDIY_MPI_EXPORT_FUNCTION VTKMDIY_MPI_EXPORT
# endif
#endif
#ifndef VTKMDIY_MPI_EXPORT_FUNCTION
#error "VTKMDIY_MPI_EXPORT_FUNCTION not defined"
#endif
#ifndef VTKMDIY_MPI_NO_EXPORT
# define VTKMDIY_MPI_NO_EXPORT VTKMDIY_MPI_NO_EXPORT_DEFINE
#endif
#endif // VTKMDIY_MPI_EXPORT_H

@ -0,0 +1,62 @@
#ifdef VTKMDIY_MPI_AS_LIB
#include "environment.hpp"
#endif
bool diy::mpi::environment::initialized()
{
#if VTKMDIY_HAS_MPI
int flag;
MPI_Initialized(&flag);
return flag != 0;
#else
return true;
#endif
}
diy::mpi::environment::environment()
{
#if VTKMDIY_HAS_MPI
int argc = 0; char** argv = nullptr;
MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided_threading);
#else
provided_threading = MPI_THREAD_FUNNELED;
#endif
}
diy::mpi::environment::environment(int requested_threading)
{
#if VTKMDIY_HAS_MPI
int argc = 0; char** argv = nullptr;
MPI_Init_thread(&argc, &argv, requested_threading, &provided_threading);
#else
provided_threading = requested_threading;
#endif
}
diy::mpi::environment::environment(int argc, char* argv[])
{
#if VTKMDIY_HAS_MPI
MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided_threading);
#else
(void) argc; (void) argv;
provided_threading = MPI_THREAD_FUNNELED;
#endif
}
diy::mpi::environment::environment(int argc, char* argv[], int requested_threading)
{
#if VTKMDIY_HAS_MPI
MPI_Init_thread(&argc, &argv, requested_threading, &provided_threading);
#else
(void) argc; (void) argv;
provided_threading = requested_threading;
#endif
}
diy::mpi::environment::
~environment()
{
#if VTKMDIY_HAS_MPI
MPI_Finalize();
#endif
}

@ -0,0 +1,35 @@
#ifndef VTKMDIY_MPI_ENVIRONMENT_HPP
#define VTKMDIY_MPI_ENVIRONMENT_HPP
#include "config.hpp"
namespace diy
{
namespace mpi
{
//! \ingroup MPI
struct environment
{
VTKMDIY_MPI_EXPORT_FUNCTION static bool initialized();
VTKMDIY_MPI_EXPORT_FUNCTION environment();
VTKMDIY_MPI_EXPORT_FUNCTION environment(int requested_threading);
VTKMDIY_MPI_EXPORT_FUNCTION environment(int argc, char* argv[]);
VTKMDIY_MPI_EXPORT_FUNCTION environment(int argc, char* argv[], int requested_threading);
VTKMDIY_MPI_EXPORT_FUNCTION ~environment();
int threading() const { return provided_threading; }
int provided_threading;
};
}
} // diy::mpi
#ifndef VTKMDIY_MPI_AS_LIB
#include "environment.cpp"
#endif
#endif // VTKMDIY_MPI_ENVIRONMENT_HPP

@ -0,0 +1,222 @@
#ifdef VTKMDIY_MPI_AS_LIB
#include "io.hpp"
#endif
#include "status.hpp"
#ifdef VTKMDIY_MPI_AS_LIB
const int diy::mpi::io::file::rdonly = MPI_MODE_RDONLY;
const int diy::mpi::io::file::rdwr = MPI_MODE_RDWR;
const int diy::mpi::io::file::wronly = MPI_MODE_WRONLY;
const int diy::mpi::io::file::create = MPI_MODE_CREATE;
const int diy::mpi::io::file::exclusive = MPI_MODE_EXCL;
const int diy::mpi::io::file::delete_on_close = MPI_MODE_DELETE_ON_CLOSE;
const int diy::mpi::io::file::unique_open = MPI_MODE_UNIQUE_OPEN;
const int diy::mpi::io::file::sequential = MPI_MODE_SEQUENTIAL;
const int diy::mpi::io::file::append = MPI_MODE_APPEND;
#endif
diy::mpi::io::file::
file(const communicator& comm__, const std::string& filename, int mode)
: comm_(comm__)
{
#if VTKMDIY_HAS_MPI
int ret = MPI_File_open(diy::mpi::mpi_cast(comm__.handle()), const_cast<char*>(filename.c_str()), mode, MPI_INFO_NULL, &diy::mpi::mpi_cast(fh));
if (ret)
throw std::runtime_error("DIY cannot open file: " + filename);
#else
(void)comm__; (void)filename; (void)mode;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_File_open);
#endif
}
void
diy::mpi::io::file::
close()
{
#if VTKMDIY_HAS_MPI
if (diy::mpi::mpi_cast(fh) != MPI_FILE_NULL)
MPI_File_close(&diy::mpi::mpi_cast(fh));
#endif
}
diy::mpi::io::offset
diy::mpi::io::file::
size() const
{
#if VTKMDIY_HAS_MPI
MPI_Offset sz;
MPI_File_get_size(diy::mpi::mpi_cast(fh), &sz);
return static_cast<offset>(sz);
#else
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_File_get_size);
#endif
}
void
diy::mpi::io::file::
resize(diy::mpi::io::offset size_)
{
#if VTKMDIY_HAS_MPI
MPI_File_set_size(diy::mpi::mpi_cast(fh), static_cast<MPI_Offset>(size_));
#else
(void)size_;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_File_set_size);
#endif
}
void
diy::mpi::io::file::
read_at(offset o, char* buffer, size_t size_)
{
#if VTKMDIY_HAS_MPI
status s;
MPI_File_read_at(diy::mpi::mpi_cast(fh), static_cast<MPI_Offset>(o), buffer, static_cast<int>(size_), MPI_BYTE, &diy::mpi::mpi_cast(s.handle));
#else
(void)o; (void)buffer; (void)size_;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_File_read_at);
#endif
}
void
diy::mpi::io::file::
read_at_all(offset o, char* buffer, size_t size_)
{
#if VTKMDIY_HAS_MPI
status s;
MPI_File_read_at_all(diy::mpi::mpi_cast(fh), static_cast<MPI_Offset>(o), buffer, static_cast<int>(size_), MPI_BYTE, &diy::mpi::mpi_cast(s.handle));
#else
(void)o; (void)buffer; (void)size_;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_File_read_at_all);
#endif
}
void
diy::mpi::io::file::
write_at(offset o, const char* buffer, size_t size_)
{
#if VTKMDIY_HAS_MPI
status s;
MPI_File_write_at(diy::mpi::mpi_cast(fh), static_cast<MPI_Offset>(o), (void *)buffer, static_cast<int>(size_), MPI_BYTE, &diy::mpi::mpi_cast(s.handle));
#else
(void)o; (void)buffer; (void)size_;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_File_write_at);
#endif
}
void
diy::mpi::io::file::
write_at_all(offset o, const char* buffer, size_t size_)
{
#if VTKMDIY_HAS_MPI
status s;
MPI_File_write_at_all(diy::mpi::mpi_cast(fh), static_cast<MPI_Offset>(o), (void *)buffer, static_cast<int>(size_), MPI_BYTE, &diy::mpi::mpi_cast(s.handle));
#else
(void)o; (void)buffer; (void)size_;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_File_write_at_all);
#endif
}
void
diy::mpi::io::file::
read_bov(const DiscreteBounds& bounds, int ndims, const int dims[], char* buffer, size_t offset, const datatype& dt, bool collective, int chunk)
{
#if VTKMDIY_HAS_MPI
int total = 1;
std::vector<int> subsizes;
for (unsigned i = 0; i < static_cast<unsigned>(ndims); ++i)
{
subsizes.push_back(bounds.max[i] - bounds.min[i] + 1);
total *= subsizes.back();
}
MPI_Datatype T_type;
if (chunk == 1)
{
T_type = diy::mpi::mpi_cast(dt.handle);
}
else
{
// create an MPI struct of size chunk to read the data in those chunks
// (this allows to work around MPI-IO weirdness where crucial quantities
// are ints, which are too narrow of a type)
int array_of_blocklengths[] = { chunk };
MPI_Aint array_of_displacements[] = { 0 };
MPI_Datatype array_of_types[] = { diy::mpi::mpi_cast(dt.handle) };
MPI_Type_create_struct(1, array_of_blocklengths, array_of_displacements, array_of_types, &T_type);
MPI_Type_commit(&T_type);
}
MPI_Datatype fileblk;
MPI_Type_create_subarray(ndims, dims, subsizes.data(), (int*) &bounds.min[0], MPI_ORDER_C, T_type, &fileblk);
MPI_Type_commit(&fileblk);
MPI_File_set_view(diy::mpi::mpi_cast(fh), static_cast<MPI_Offset>(offset), T_type, fileblk, (char*)"native", MPI_INFO_NULL);
mpi::status s;
if (!collective)
MPI_File_read(diy::mpi::mpi_cast(fh), buffer, total, T_type, &mpi_cast(s.handle));
else
MPI_File_read_all(diy::mpi::mpi_cast(fh), buffer, total, T_type, &mpi_cast(s.handle));
if (chunk != 1)
MPI_Type_free(&T_type);
MPI_Type_free(&fileblk);
#else
(void) bounds; (void) ndims; (void) dims, (void) buffer; (void) offset, (void) dt, (void) collective; (void) chunk;
VTKMDIY_UNSUPPORTED_MPI_CALL(diy::mpi::io::file::read_bov);
#endif
}
void
diy::mpi::io::file::
write_bov(const DiscreteBounds& bounds, const DiscreteBounds& core, int ndims, const int dims[], const char* buffer, size_t offset, const datatype& dt, bool collective, int chunk)
{
#if VTKMDIY_HAS_MPI
std::vector<int> subsizes;
std::vector<int> buffer_shape, buffer_start;
for (unsigned i = 0; i < static_cast<unsigned>(ndims); ++i)
{
buffer_shape.push_back(bounds.max[i] - bounds.min[i] + 1);
buffer_start.push_back(core.min[i] - bounds.min[i]);
subsizes.push_back(core.max[i] - core.min[i] + 1);
}
MPI_Datatype T_type;
if (chunk == 1)
{
T_type = diy::mpi::mpi_cast(dt.handle);
}
else
{
// assume T is a binary block and create an MPI struct of appropriate size
int array_of_blocklengths[] = { chunk };
MPI_Aint array_of_displacements[] = { 0 };
MPI_Datatype array_of_types[] = { diy::mpi::mpi_cast(dt.handle) };
MPI_Type_create_struct(1, array_of_blocklengths, array_of_displacements, array_of_types, &T_type);
MPI_Type_commit(&T_type);
}
MPI_Datatype fileblk, subbuffer;
MPI_Type_create_subarray(ndims, dims, subsizes.data(), (int*) &core.min[0], MPI_ORDER_C, T_type, &fileblk);
MPI_Type_create_subarray(ndims, buffer_shape.data(), subsizes.data(), buffer_start.data(), MPI_ORDER_C, T_type, &subbuffer);
MPI_Type_commit(&fileblk);
MPI_Type_commit(&subbuffer);
MPI_File_set_view(diy::mpi::mpi_cast(fh), static_cast<MPI_Offset>(offset), T_type, fileblk, (char*)"native", MPI_INFO_NULL);
mpi::status s;
if (!collective)
MPI_File_write(diy::mpi::mpi_cast(fh), (void*)buffer, 1, subbuffer, &mpi_cast(s.handle));
else
MPI_File_write_all(diy::mpi::mpi_cast(fh), (void*)buffer, 1, subbuffer, &mpi_cast(s.handle));
if (chunk != 1)
MPI_Type_free(&T_type);
MPI_Type_free(&fileblk);
MPI_Type_free(&subbuffer);
#else
(void) bounds; (void) core, (void) ndims; (void) dims, (void) buffer; (void) offset, (void) dt, (void) collective; (void) chunk;
VTKMDIY_UNSUPPORTED_MPI_CALL(diy::mpi::io::file::write_bov);
#endif
}

@ -1,139 +1,82 @@
#ifndef VTKMDIY_MPI_IO_HPP
#define VTKMDIY_MPI_IO_HPP
#include "../constants.h"
#include "config.hpp"
#include "communicator.hpp"
#include <vtkmdiy/types.hpp>
#include <vector>
#include <string>
#include <stdexcept>
namespace diy
{
namespace mpi
{
namespace io
{
typedef MPI_Offset offset;
#if !defined(VTKMDIY_MPI_AS_LIB) && VTKMDIY_HAS_MPI
using offset = MPI_Offset;
#else
using offset = long long;
#endif
//! Wraps MPI file IO. \ingroup MPI
class file
{
public:
enum
{
rdonly = MPI_MODE_RDONLY,
rdwr = MPI_MODE_RDWR,
wronly = MPI_MODE_WRONLY,
create = MPI_MODE_CREATE,
exclusive = MPI_MODE_EXCL,
delete_on_close = MPI_MODE_DELETE_ON_CLOSE,
unique_open = MPI_MODE_UNIQUE_OPEN,
sequential = MPI_MODE_SEQUENTIAL,
append = MPI_MODE_APPEND
};
#ifndef VTKMDIY_MPI_AS_LIB
static constexpr int rdonly = MPI_MODE_RDONLY;
static constexpr int rdwr = MPI_MODE_RDWR;
static constexpr int wronly = MPI_MODE_WRONLY;
static constexpr int create = MPI_MODE_CREATE;
static constexpr int exclusive = MPI_MODE_EXCL;
static constexpr int delete_on_close = MPI_MODE_DELETE_ON_CLOSE;
static constexpr int unique_open = MPI_MODE_UNIQUE_OPEN;
static constexpr int sequential = MPI_MODE_SEQUENTIAL;
static constexpr int append = MPI_MODE_APPEND;
#else
static const int rdonly, rdwr, wronly, create, exclusive, delete_on_close, unique_open, sequential, append;
#endif
public:
inline file(const communicator& comm, const std::string& filename, int mode);
~file() { close(); }
inline void close();
VTKMDIY_MPI_EXPORT_FUNCTION file(const communicator& comm, const std::string& filename, int mode);
~file() { close(); }
VTKMDIY_MPI_EXPORT_FUNCTION void close();
inline offset size() const;
inline void resize(offset size);
VTKMDIY_MPI_EXPORT_FUNCTION offset size() const;
VTKMDIY_MPI_EXPORT_FUNCTION void resize(offset size);
inline void read_at(offset o, char* buffer, size_t size);
inline void read_at_all(offset o, char* buffer, size_t size);
inline void write_at(offset o, const char* buffer, size_t size);
inline void write_at_all(offset o, const char* buffer, size_t size);
VTKMDIY_MPI_EXPORT_FUNCTION void read_at(offset o, char* buffer, size_t size);
VTKMDIY_MPI_EXPORT_FUNCTION void read_at_all(offset o, char* buffer, size_t size);
VTKMDIY_MPI_EXPORT_FUNCTION void write_at(offset o, const char* buffer, size_t size);
VTKMDIY_MPI_EXPORT_FUNCTION void write_at_all(offset o, const char* buffer, size_t size);
template<class T>
inline void read_at(offset o, std::vector<T>& data);
inline void read_at(offset o, std::vector<T>& data);
template<class T>
inline void read_at_all(offset o, std::vector<T>& data);
inline void read_at_all(offset o, std::vector<T>& data);
template<class T>
inline void write_at(offset o, const std::vector<T>& data);
inline void write_at(offset o, const std::vector<T>& data);
template<class T>
inline void write_at_all(offset o, const std::vector<T>& data);
inline void write_at_all(offset o, const std::vector<T>& data);
const communicator&
comm() const { return comm_; }
VTKMDIY_MPI_EXPORT_FUNCTION void read_bov(const DiscreteBounds& bounds, int ndims, const int dims[], char* buffer, size_t offset, const datatype& dt, bool collective, int chunk);
VTKMDIY_MPI_EXPORT_FUNCTION void write_bov(const DiscreteBounds& bounds, const DiscreteBounds& core, int ndims, const int dims[], const char* buffer, size_t offset, const datatype& dt, bool collective, int chunk);
MPI_File& handle() { return fh; }
const communicator& comm() const { return comm_; }
private:
const communicator& comm_;
MPI_File fh;
communicator comm_;
protected: // mark protected to avoid the "unused private field" warning
DIY_MPI_File fh;
};
}
}
}
diy::mpi::io::file::
file(const communicator& comm__, const std::string& filename, int mode)
: comm_(comm__)
{
#ifndef VTKM_DIY_NO_MPI
int ret = MPI_File_open(comm__, const_cast<char*>(filename.c_str()), mode, MPI_INFO_NULL, &fh);
if (ret)
throw std::runtime_error("DIY cannot open file: " + filename);
#else
DIY_UNUSED(comm__);
DIY_UNUSED(filename);
DIY_UNUSED(mode);
DIY_UNSUPPORTED_MPI_CALL(MPI_File_open);
#endif
}
void
diy::mpi::io::file::
close()
{
#ifndef VTKM_DIY_NO_MPI
if (fh != MPI_FILE_NULL)
MPI_File_close(&fh);
#endif
}
diy::mpi::io::offset
diy::mpi::io::file::
size() const
{
#ifndef VTKM_DIY_NO_MPI
offset sz;
MPI_File_get_size(fh, &sz);
return sz;
#else
DIY_UNSUPPORTED_MPI_CALL(MPI_File_get_size);
#endif
}
void
diy::mpi::io::file::
resize(diy::mpi::io::offset size_)
{
#ifndef VTKM_DIY_NO_MPI
MPI_File_set_size(fh, size_);
#else
DIY_UNUSED(size_);
DIY_UNSUPPORTED_MPI_CALL(MPI_File_set_size);
#endif
}
void
diy::mpi::io::file::
read_at(offset o, char* buffer, size_t size_)
{
#ifndef VTKM_DIY_NO_MPI
status s;
MPI_File_read_at(fh, o, buffer, static_cast<int>(size_), detail::get_mpi_datatype<char>(), &s.s);
#else
DIY_UNUSED(o);
DIY_UNUSED(buffer);
DIY_UNUSED(size_);
DIY_UNSUPPORTED_MPI_CALL(MPI_File_read_at);
#endif
}
template<class T>
void
@ -143,21 +86,6 @@ read_at(offset o, std::vector<T>& data)
read_at(o, &data[0], data.size()*sizeof(T));
}
void
diy::mpi::io::file::
read_at_all(offset o, char* buffer, size_t size_)
{
#ifndef VTKM_DIY_NO_MPI
status s;
MPI_File_read_at_all(fh, o, buffer, static_cast<int>(size_), detail::get_mpi_datatype<char>(), &s.s);
#else
DIY_UNUSED(o);
DIY_UNUSED(buffer);
DIY_UNUSED(size_);
DIY_UNSUPPORTED_MPI_CALL(MPI_File_read_at_all);
#endif
}
template<class T>
void
diy::mpi::io::file::
@ -166,21 +94,6 @@ read_at_all(offset o, std::vector<T>& data)
read_at_all(o, (char*) &data[0], data.size()*sizeof(T));
}
void
diy::mpi::io::file::
write_at(offset o, const char* buffer, size_t size_)
{
#ifndef VTKM_DIY_NO_MPI
status s;
MPI_File_write_at(fh, o, (void *)buffer, static_cast<int>(size_), detail::get_mpi_datatype<char>(), &s.s);
#else
DIY_UNUSED(o);
DIY_UNUSED(buffer);
DIY_UNUSED(size_);
DIY_UNSUPPORTED_MPI_CALL(MPI_File_write_at);
#endif
}
template<class T>
void
diy::mpi::io::file::
@ -189,21 +102,6 @@ write_at(offset o, const std::vector<T>& data)
write_at(o, (const char*) &data[0], data.size()*sizeof(T));
}
void
diy::mpi::io::file::
write_at_all(offset o, const char* buffer, size_t size_)
{
#ifndef VTKM_DIY_NO_MPI
status s;
MPI_File_write_at_all(fh, o, (void *)buffer, static_cast<int>(size_), detail::get_mpi_datatype<char>(), &s.s);
#else
DIY_UNUSED(o);
DIY_UNUSED(buffer);
DIY_UNUSED(size_);
DIY_UNSUPPORTED_MPI_CALL(MPI_File_write_at_all);
#endif
}
template<class T>
void
diy::mpi::io::file::
@ -212,4 +110,11 @@ write_at_all(offset o, const std::vector<T>& data)
write_at_all(o, &data[0], data.size()*sizeof(T));
}
}
} // diy::mpi::io
#ifndef VTKMDIY_MPI_AS_LIB
#include "io.cpp"
#endif
#endif // VTKMDIY_MPI_IO_HPP

@ -0,0 +1,34 @@
#ifndef VTKMDIY_MPI_MPICAST_HPP
#define VTKMDIY_MPI_MPICAST_HPP
/// This header provides convinience functions to cast from diy's type erased MPI objects
/// to thier correct types.
#ifndef VTKMDIY_HAS_MPI
# include <mpi.h>
#endif
namespace diy
{
namespace mpi
{
#define DEFINE_MPI_CAST(mpitype) \
inline mpitype& mpi_cast(DIY_##mpitype& obj) { return *reinterpret_cast<mpitype*>(&obj); } \
inline const mpitype& mpi_cast(const DIY_##mpitype& obj) { return *reinterpret_cast<const mpitype*>(&obj); } \
inline DIY_##mpitype make_DIY_##mpitype(const mpitype& obj) { DIY_##mpitype ret; mpi_cast(ret) = obj; return ret; }
DEFINE_MPI_CAST(MPI_Comm)
DEFINE_MPI_CAST(MPI_Datatype)
DEFINE_MPI_CAST(MPI_Status)
DEFINE_MPI_CAST(MPI_Request)
DEFINE_MPI_CAST(MPI_Op)
DEFINE_MPI_CAST(MPI_File)
DEFINE_MPI_CAST(MPI_Win)
#undef DEFINE_MPI_CAST
}
} // diy::mpi
#endif // VTKMDIY_MPI_MPICAST_HPP

@ -0,0 +1,51 @@
#ifndef VTKMDIY_MPI_MPITYPES_H
#define VTKMDIY_MPI_MPITYPES_H
#cmakedefine TYPESIZE_MPI_Comm @TYPESIZE_MPI_Comm@
#cmakedefine TYPESIZE_MPI_Datatype @TYPESIZE_MPI_Datatype@
#cmakedefine TYPESIZE_MPI_Status @TYPESIZE_MPI_Status@
#cmakedefine TYPESIZE_MPI_Request @TYPESIZE_MPI_Request@
#cmakedefine TYPESIZE_MPI_Op @TYPESIZE_MPI_Op@
#cmakedefine TYPESIZE_MPI_File @TYPESIZE_MPI_File@
#cmakedefine TYPESIZE_MPI_Win @TYPESIZE_MPI_Win@
namespace diy
{
namespace mpi
{
#if defined(VTKMDIY_HAS_MPI)
# define ASSERT_MPI_TYPE_SIZE(mpitype) static_assert(sizeof(mpitype) <= sizeof(DIY_##mpitype), "");
#else
# define ASSERT_MPI_TYPE_SIZE(mpitype)
#endif
#define DEFINE_DIY_MPI_TYPE(mpitype) \
struct DIY_##mpitype { \
void* data[((TYPESIZE_##mpitype) + sizeof(void*) - 1)/sizeof(void*)]; \
}; \
ASSERT_MPI_TYPE_SIZE(mpitype)
DEFINE_DIY_MPI_TYPE(MPI_Comm)
DEFINE_DIY_MPI_TYPE(MPI_Datatype)
DEFINE_DIY_MPI_TYPE(MPI_Status)
DEFINE_DIY_MPI_TYPE(MPI_Request)
DEFINE_DIY_MPI_TYPE(MPI_Op)
DEFINE_DIY_MPI_TYPE(MPI_File)
DEFINE_DIY_MPI_TYPE(MPI_Win)
#undef DEFINE_DIY_MPI_TYPE
#undef ASSERT_MPI_TYPE_SIZE
}
} // diy::mpi
#undef TYPESIZE_MPI_Comm
#undef TYPESIZE_MPI_Datatype
#undef TYPESIZE_MPI_Status
#undef TYPESIZE_MPI_Request
#undef TYPESIZE_MPI_Op
#undef TYPESIZE_MPI_File
#undef TYPESIZE_MPI_Win
#endif // VTKMDIY_MPI_MPITYPES_H

@ -22,18 +22,17 @@ static const int MPI_THREAD_MULTIPLE = 3;
/* define datatypes */
using MPI_Datatype = size_t;
#define VTKM_DIY_NO_MPI_DATATYPE(cpp_type, mpi_type) \
#define VTKMDIY_NO_MPI_DATATYPE(cpp_type, mpi_type) \
static const MPI_Datatype mpi_type = sizeof(cpp_type);
VTKM_DIY_NO_MPI_DATATYPE(char, MPI_BYTE);
VTKM_DIY_NO_MPI_DATATYPE(int, MPI_INT);
VTKM_DIY_NO_MPI_DATATYPE(unsigned, MPI_UNSIGNED);
VTKM_DIY_NO_MPI_DATATYPE(long, MPI_LONG);
VTKM_DIY_NO_MPI_DATATYPE(unsigned long, MPI_UNSIGNED_LONG);
VTKM_DIY_NO_MPI_DATATYPE(long long, MPI_LONG_LONG_INT);
VTKM_DIY_NO_MPI_DATATYPE(unsigned long long, MPI_UNSIGNED_LONG_LONG);
VTKM_DIY_NO_MPI_DATATYPE(float, MPI_FLOAT);
VTKM_DIY_NO_MPI_DATATYPE(double, MPI_DOUBLE);
#endif
VTKMDIY_NO_MPI_DATATYPE(char, MPI_BYTE);
VTKMDIY_NO_MPI_DATATYPE(int, MPI_INT);
VTKMDIY_NO_MPI_DATATYPE(unsigned, MPI_UNSIGNED);
VTKMDIY_NO_MPI_DATATYPE(long, MPI_LONG);
VTKMDIY_NO_MPI_DATATYPE(unsigned long, MPI_UNSIGNED_LONG);
VTKMDIY_NO_MPI_DATATYPE(long long, MPI_LONG_LONG_INT);
VTKMDIY_NO_MPI_DATATYPE(unsigned long long, MPI_UNSIGNED_LONG_LONG);
VTKMDIY_NO_MPI_DATATYPE(float, MPI_FLOAT);
VTKMDIY_NO_MPI_DATATYPE(double, MPI_DOUBLE);
/* status type */
struct MPI_Status
@ -48,10 +47,8 @@ struct MPI_Status
/* define MPI_Request */
using MPI_Request = int;
#ifndef DIY_UNSUPPORTED_MPI_CALL
#define DIY_UNSUPPORTED_MPI_CALL(name) \
throw std::runtime_error("`" #name "` not supported when VTKM_DIY_NO_MPI is defined.");
#endif
#define VTKMDIY_UNSUPPORTED_MPI_CALL(name) \
throw std::runtime_error("`" #name "` not supported when VTKMDIY_HAS_MPI is false.");
/* define operations */
using MPI_Op = int;
@ -63,7 +60,7 @@ static const MPI_Op MPI_LAND = 0;
static const MPI_Op MPI_LOR = 0;
/* mpi i/o stuff */
using MPI_Offset = size_t;
using MPI_Offset = long long;
using MPI_File = int;
static const MPI_File MPI_FILE_NULL = 0;
@ -78,7 +75,7 @@ static const int MPI_MODE_APPEND = 128;
static const int MPI_MODE_SEQUENTIAL = 256;
/* define window type */
using MPI_Win = int;
using MPI_Win = void*;
/* window fence assertions */
static const int MPI_MODE_NOSTORE = 1;
@ -90,3 +87,5 @@ static const int MPI_MODE_NOCHECK = 16;
/* window lock types */
static const int MPI_LOCK_SHARED = 1;
static const int MPI_LOCK_EXCLUSIVE = 2;
#endif // VTKMDIY_MPI_NO_MPI_HPP

@ -0,0 +1,33 @@
#ifdef VTKMDIY_MPI_AS_LIB
#include "operations.hpp"
#endif
#include <functional>
namespace diy
{
namespace mpi
{
namespace detail
{
operation get_builtin_operation(BuiltinOperation id)
{
operation op{};
switch(id)
{
case OP_MAXIMUM: op.handle = make_DIY_MPI_Op(MPI_MAX); break;
case OP_MINIMUM: op.handle = make_DIY_MPI_Op(MPI_MIN); break;
case OP_PLUS: op.handle = make_DIY_MPI_Op(MPI_SUM); break;
case OP_MULTIPLIES: op.handle = make_DIY_MPI_Op(MPI_PROD); break;
case OP_LOGICAL_AND: op.handle = make_DIY_MPI_Op(MPI_LAND); break;
case OP_LOGICAL_OR: op.handle = make_DIY_MPI_Op(MPI_LOR); break;
default: break;
}
return op;
}
}
}
} // diy::mpi::detail

@ -1,3 +1,8 @@
#ifndef VTKMDIY_MPI_OPERATIONS_HPP
#define VTKMDIY_MPI_OPERATIONS_HPP
#include "config.hpp"
#include <algorithm> // for std::min/max
#include <functional>
@ -7,6 +12,19 @@ namespace mpi
{
//! \addtogroup MPI
//!@{
struct operation
{
operation() = default;
operation(const DIY_MPI_Op& op) : handle(op) {}
#ifndef VTKMDIY_MPI_AS_LIB // only available in header-only mode
operation(const MPI_Op& op) : handle(op) {}
operator MPI_Op() { return handle; }
#endif
DIY_MPI_Op handle;
};
template<class U>
struct maximum { const U& operator()(const U& x, const U& y) const { return std::max(x,y); } };
template<class U>
@ -15,13 +33,32 @@ namespace mpi
namespace detail
{
template<class T> struct mpi_op { static MPI_Op get(); };
template<class U> struct mpi_op< maximum<U> > { static MPI_Op get() { return MPI_MAX; } };
template<class U> struct mpi_op< minimum<U> > { static MPI_Op get() { return MPI_MIN; } };
template<class U> struct mpi_op< std::plus<U> > { static MPI_Op get() { return MPI_SUM; } };
template<class U> struct mpi_op< std::multiplies<U> > { static MPI_Op get() { return MPI_PROD; } };
template<class U> struct mpi_op< std::logical_and<U> > { static MPI_Op get() { return MPI_LAND; } };
template<class U> struct mpi_op< std::logical_or<U> > { static MPI_Op get() { return MPI_LOR; } };
}
enum BuiltinOperation {
OP_MAXIMUM = 0,
OP_MINIMUM,
OP_PLUS,
OP_MULTIPLIES,
OP_LOGICAL_AND,
OP_LOGICAL_OR
};
VTKMDIY_MPI_EXPORT_FUNCTION operation get_builtin_operation(BuiltinOperation id);
template<class T> struct mpi_op;
template<class U> struct mpi_op< maximum<U> > { static operation get() { return get_builtin_operation(OP_MAXIMUM); } };
template<class U> struct mpi_op< minimum<U> > { static operation get() { return get_builtin_operation(OP_MINIMUM); } };
template<class U> struct mpi_op< std::plus<U> > { static operation get() { return get_builtin_operation(OP_PLUS); } };
template<class U> struct mpi_op< std::multiplies<U> > { static operation get() { return get_builtin_operation(OP_MULTIPLIES); } };
template<class U> struct mpi_op< std::logical_and<U> > { static operation get() { return get_builtin_operation(OP_LOGICAL_AND); } };
template<class U> struct mpi_op< std::logical_or<U> > { static operation get() { return get_builtin_operation(OP_LOGICAL_OR); } };
}
}
} // diy::mpi
#ifndef VTKMDIY_MPI_AS_LIB
#include "operations.cpp"
#endif
#endif // VTKMDIY_MPI_OPERATIONS_HPP

@ -1,3 +1,6 @@
#ifndef VTKMDIY_MPI_OPTIONAL_HPP
#define VTKMDIY_MPI_OPTIONAL_HPP
namespace diy
{
namespace mpi
@ -53,3 +56,5 @@ operator=(const optional& o)
return *this;
}
#endif // VTKMDIY_MPI_OPTIONAL_HPP

@ -0,0 +1,96 @@
#ifdef VTKMDIY_MPI_AS_LIB
#include "point-to-point.hpp"
#endif
namespace diy
{
namespace mpi
{
#ifdef VTKMDIY_MPI_AS_LIB
# ifdef _MSC_VER
# define EXPORT_MACRO VTKMDIY_MPI_EXPORT
# else
# define EXPORT_MACRO
# endif
EXPORT_MACRO const int any_source = MPI_ANY_SOURCE;
EXPORT_MACRO const int any_tag = MPI_ANY_TAG;
# undef EXPORT_MACRO
#endif
namespace detail
{
void send(DIY_MPI_Comm comm, int dest, int tag, const void* data, int count, const datatype& type)
{
#if VTKMDIY_HAS_MPI
MPI_Send(data, count, mpi_cast(type.handle), dest, tag, mpi_cast(comm));
#else
(void) comm; (void) dest; (void) tag; (void) data; (void) count; (void) type;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_Send);
#endif
}
status probe(DIY_MPI_Comm comm, int source, int tag)
{
#if VTKMDIY_HAS_MPI
status s;
MPI_Probe(source, tag, mpi_cast(comm), &mpi_cast(s.handle));
return s;
#else
(void) comm; (void) source; (void) tag;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_Probe);
#endif
}
status recv(DIY_MPI_Comm comm, int source, int tag, void* data, int count, const datatype& type)
{
#if VTKMDIY_HAS_MPI
status s;
MPI_Recv(data, count, mpi_cast(type.handle), source, tag, mpi_cast(comm), &mpi_cast(s.handle));
return s;
#else
(void) comm; (void) source; (void) tag; (void) data; (void) count; (void) type;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_Recv);
#endif
}
request isend(DIY_MPI_Comm comm, int dest, int tag, const void* data, int count, const datatype& type)
{
#if VTKMDIY_HAS_MPI
request r;
MPI_Isend(data, count, mpi_cast(type.handle), dest, tag, mpi_cast(comm), &mpi_cast(r.handle));
return r;
#else
(void) comm; (void) dest; (void) tag; (void) data; (void) count; (void) type;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_Isend);
#endif
}
request issend(DIY_MPI_Comm comm, int dest, int tag, const void* data, int count, const datatype& type)
{
#if VTKMDIY_HAS_MPI
request r;
MPI_Issend(data, count, mpi_cast(type.handle), dest, tag, mpi_cast(comm), &mpi_cast(r.handle));
return r;
#else
(void) comm; (void) dest; (void) tag; (void) data; (void) count; (void) type;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_Issend);
#endif
}
request irecv(DIY_MPI_Comm comm, int source, int tag, void* data, int count, const datatype& type)
{
#if VTKMDIY_HAS_MPI
request r;
MPI_Irecv(data, count, mpi_cast(type.handle), source, tag, mpi_cast(comm), &mpi_cast(r.handle));
return r;
#else
(void) comm; (void) source; (void) tag; (void) data; (void) count; (void) type;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_Irecv);
#endif
}
}
}
} // diy::mpi::detail

@ -1,147 +1,84 @@
#ifndef VTKMDIY_MPI_POINT_TO_POINT_HPP
#define VTKMDIY_MPI_POINT_TO_POINT_HPP
#include "config.hpp"
#include "datatypes.hpp"
#include "request.hpp"
#include "status.hpp"
#include <vector>
namespace diy
{
namespace mpi
{
#ifndef VTKMDIY_MPI_AS_LIB
constexpr int any_source = MPI_ANY_SOURCE;
constexpr int any_tag = MPI_ANY_TAG;
#else
VTKMDIY_MPI_EXPORT extern const int any_source;
VTKMDIY_MPI_EXPORT extern const int any_tag;
#endif
namespace detail
{
// send
template< class T, class is_mpi_datatype_ = typename is_mpi_datatype<T>::type >
struct send;
VTKMDIY_MPI_EXPORT_FUNCTION void send(DIY_MPI_Comm comm, int dest, int tag, const void* data, int count, const datatype& type);
VTKMDIY_MPI_EXPORT_FUNCTION request isend(DIY_MPI_Comm comm, int dest, int tag, const void* data, int count, const datatype& type);
VTKMDIY_MPI_EXPORT_FUNCTION request issend(DIY_MPI_Comm comm, int dest, int tag, const void* data, int count, const datatype& type);
VTKMDIY_MPI_EXPORT_FUNCTION status probe(DIY_MPI_Comm comm, int source, int tag);
VTKMDIY_MPI_EXPORT_FUNCTION status recv(DIY_MPI_Comm comm, int source, int tag, void* data, int count, const datatype& type);
VTKMDIY_MPI_EXPORT_FUNCTION request irecv(DIY_MPI_Comm comm, int source, int tag, void* data, int count, const datatype& type);
template<class T>
struct send<T, true_type>
template <class T>
inline void send(DIY_MPI_Comm comm, int dest, int tag, const T& x)
{
void operator()(MPI_Comm comm, int dest, int tag, const T& x) const
{
#ifndef VTKM_DIY_NO_MPI
typedef mpi_datatype<T> Datatype;
MPI_Send((void*) Datatype::address(x),
Datatype::count(x),
Datatype::datatype(),
dest, tag, comm);
#else
(void) comm; (void) dest; (void) tag; (void) x;
DIY_UNSUPPORTED_MPI_CALL(MPI_Send);
#endif
}
};
static_assert(std::is_same<typename is_mpi_datatype<T>::type, true_type>::value, "is_mpi_datatype<T>::type must be true_type");
send(comm, dest, tag, address(x), count(x), datatype_of(x));
}
// recv
template< class T, class is_mpi_datatype_ = typename is_mpi_datatype<T>::type >
struct recv;
template<class T>
struct recv<T, true_type>
template <class T>
status recv(DIY_MPI_Comm comm, int source, int tag, T& x)
{
status operator()(MPI_Comm comm, int source, int tag, T& x) const
{
#ifndef VTKM_DIY_NO_MPI
typedef mpi_datatype<T> Datatype;
status s;
MPI_Recv((void*) Datatype::address(x),
Datatype::count(x),
Datatype::datatype(),
source, tag, comm, &s.s);
return s;
#else
(void) comm; (void) source; (void) tag; (void) x;
DIY_UNSUPPORTED_MPI_CALL(MPI_Recv);
#endif
}
};
static_assert(std::is_same<typename is_mpi_datatype<T>::type, true_type>::value, "is_mpi_datatype<T>::type must be true_type");
return recv(comm, source, tag, address(x), count(x), datatype_of(x));
}
template<class U>
struct recv<std::vector<U>, true_type>
template <class T>
status recv(DIY_MPI_Comm comm, int source, int tag, std::vector<T>& x)
{
status operator()(MPI_Comm comm, int source, int tag, std::vector<U>& x) const
{
#ifndef VTKM_DIY_NO_MPI
status s;
auto s = probe(comm, source, tag);
x.resize(static_cast<size_t>(s.count<T>()));
return recv(comm, source, tag, address(x), count(x), datatype_of(x));
}
MPI_Probe(source, tag, comm, &s.s);
x.resize(s.count<U>());
MPI_Recv(&x[0], static_cast<int>(x.size()), get_mpi_datatype<U>(), source, tag, comm, &s.s);
return s;
#else
(void) comm; (void) source; (void) tag; (void) x;
DIY_UNSUPPORTED_MPI_CALL(MPI_Recv);
#endif
}
};
// isend
template< class T, class is_mpi_datatype_ = typename is_mpi_datatype<T>::type >
struct isend;
template<class T>
struct isend<T, true_type>
template <class T>
request isend(DIY_MPI_Comm comm, int dest, int tag, const T& x)
{
request operator()(MPI_Comm comm, int dest, int tag, const T& x) const
{
#ifndef VTKM_DIY_NO_MPI
request r;
typedef mpi_datatype<T> Datatype;
MPI_Isend((void*) Datatype::address(x),
Datatype::count(x),
Datatype::datatype(),
dest, tag, comm, &r.r);
return r;
#else
(void) comm; (void) dest; (void) tag; (void) x;
DIY_UNSUPPORTED_MPI_CALL(MPI_Isend);
#endif
}
};
static_assert(std::is_same<typename is_mpi_datatype<T>::type, true_type>::value, "is_mpi_datatype<T>::type must be true_type");
return isend(comm, dest, tag, address(x), count(x), datatype_of(x));
}
// issend
template< class T, class is_mpi_datatype_ = typename is_mpi_datatype<T>::type >
struct issend;
template<class T>
struct issend<T, true_type>
template <class T>
request issend(DIY_MPI_Comm comm, int dest, int tag, const T& x)
{
request operator()(MPI_Comm comm, int dest, int tag, const T& x) const
{
#ifndef VTKM_DIY_NO_MPI
request r;
typedef mpi_datatype<T> Datatype;
MPI_Issend((void*) Datatype::address(x),
Datatype::count(x),
Datatype::datatype(),
dest, tag, comm, &r.r);
return r;
#else
(void) comm; (void) dest; (void) tag; (void) x;
DIY_UNSUPPORTED_MPI_CALL(MPI_Issend);
#endif
}
};
static_assert(std::is_same<typename is_mpi_datatype<T>::type, true_type>::value, "is_mpi_datatype<T>::type must be true_type");
return issend(comm, dest, tag, address(x), count(x), datatype_of(x));
}
// irecv
template< class T, class is_mpi_datatype_ = typename is_mpi_datatype<T>::type >
struct irecv;
template<class T>
struct irecv<T, true_type>
template <class T>
request irecv(DIY_MPI_Comm comm, int source, int tag, T& x)
{
request operator()(MPI_Comm comm, int source, int tag, T& x) const
{
#ifndef VTKM_DIY_NO_MPI
request r;
typedef mpi_datatype<T> Datatype;
MPI_Irecv(Datatype::address(x),
Datatype::count(x),
Datatype::datatype(),
source, tag, comm, &r.r);
return r;
#else
(void) comm; (void) source; (void) tag; (void) x;
DIY_UNSUPPORTED_MPI_CALL(MPI_Irecv);
#endif
}
};
}
static_assert(std::is_same<typename is_mpi_datatype<T>::type, true_type>::value, "is_mpi_datatype<T>::type must be true_type");
return irecv(comm, source, tag, address(x), count(x), datatype_of(x));
}
}
}
} // diy::mpi::detail
#ifndef VTKMDIY_MPI_AS_LIB
#include "point-to-point.cpp"
#endif
#endif // VTKMDIY_MPI_POINT_TO_POINT_HPP

@ -0,0 +1,45 @@
#ifdef VTKMDIY_MPI_AS_LIB
#include "request.hpp"
#endif
#include <algorithm>
#include <iterator>
#if defined(VTKMDIY_MPI_AS_LIB) && !VTKMDIY_HAS_MPI
diy::mpi::request::request()
{
std::fill(std::begin(this->handle.data), std::end(this->handle.data), nullptr);
}
#else
diy::mpi::request::request() = default;
#endif
diy::mpi::status diy::mpi::request::wait()
{
#if VTKMDIY_HAS_MPI
status s;
MPI_Wait(&mpi_cast(handle), &mpi_cast(s.handle));
return s;
#else
VTKMDIY_UNSUPPORTED_MPI_CALL(diy::mpi::request::wait);
#endif
}
diy::mpi::optional<diy::mpi::status> diy::mpi::request::test()
{
#if VTKMDIY_HAS_MPI
status s;
int flag;
MPI_Test(&mpi_cast(handle), &flag, &mpi_cast(s.handle));
if (flag)
return s;
#endif
return optional<status>();
}
void diy::mpi::request::cancel()
{
#if VTKMDIY_HAS_MPI
MPI_Cancel(&mpi_cast(handle));
#endif
}

@ -1,50 +1,29 @@
#ifndef VTKMDIY_MPI_REQUEST_HPP
#define VTKMDIY_MPI_REQUEST_HPP
#include "config.hpp"
#include "status.hpp"
#include "optional.hpp"
namespace diy
{
namespace mpi
{
struct request
{
inline
status wait();
inline
optional<status> test();
inline
void cancel();
VTKMDIY_MPI_EXPORT_FUNCTION request();
VTKMDIY_MPI_EXPORT_FUNCTION status wait();
VTKMDIY_MPI_EXPORT_FUNCTION optional<status> test();
VTKMDIY_MPI_EXPORT_FUNCTION void cancel();
MPI_Request r;
DIY_MPI_Request handle;
};
}
}
diy::mpi::status
diy::mpi::request::wait()
{
#ifndef VTKM_DIY_NO_MPI
status s;
MPI_Wait(&r, &s.s);
return s;
#else
DIY_UNSUPPORTED_MPI_CALL(diy::mpi::request::wait);
#endif
}
} // diy::mpi
diy::mpi::optional<diy::mpi::status>
diy::mpi::request::test()
{
#ifndef VTKM_DIY_NO_MPI
status s;
int flag;
MPI_Test(&r, &flag, &s.s);
if (flag)
return s;
#ifndef VTKMDIY_MPI_AS_LIB
#include "request.cpp"
#endif
return optional<status>();
}
void
diy::mpi::request::cancel()
{
#ifndef VTKM_DIY_NO_MPI
MPI_Cancel(&r);
#endif
}
#endif // VTKMDIY_MPI_REQUEST_HPP

@ -0,0 +1,30 @@
#ifdef VTKMDIY_MPI_AS_LIB
#include "status.hpp"
#endif
int diy::mpi::status::source() const { return mpi_cast(handle).MPI_SOURCE; }
int diy::mpi::status::tag() const { return mpi_cast(handle).MPI_TAG; }
int diy::mpi::status::error() const { return mpi_cast(handle).MPI_ERROR; }
bool diy::mpi::status::cancelled() const
{
#if VTKMDIY_HAS_MPI
int flag;
MPI_Test_cancelled(&mpi_cast(handle), &flag);
return flag;
#else
VTKMDIY_UNSUPPORTED_MPI_CALL(diy::mpi::status::cancelled);
#endif
}
int diy::mpi::status::count(const diy::mpi::datatype& type) const
{
#if VTKMDIY_HAS_MPI
int c;
MPI_Get_count(&mpi_cast(handle), mpi_cast(type.handle), &c);
return c;
#else
(void) type;
VTKMDIY_UNSUPPORTED_MPI_CALL(diy::mpi::status::count);
#endif
}

@ -1,49 +1,42 @@
#ifndef VTKMDIY_MPI_STATUS_HPP
#define VTKMDIY_MPI_STATUS_HPP
#include "config.hpp"
#include "datatypes.hpp"
namespace diy
{
namespace mpi
{
struct status
{
int source() const { return s.MPI_SOURCE; }
int tag() const { return s.MPI_TAG; }
int error() const { return s.MPI_ERROR; }
status() = default;
status(const DIY_MPI_Status& s) : handle(s) {}
inline
bool cancelled() const;
#ifndef VTKMDIY_MPI_AS_LIB // only available in header-only mode
status(const MPI_Status& s) : handle(s) {}
operator MPI_Status() { return handle; }
#endif
template<class T>
int count() const;
VTKMDIY_MPI_EXPORT_FUNCTION int source() const;
VTKMDIY_MPI_EXPORT_FUNCTION int tag() const;
VTKMDIY_MPI_EXPORT_FUNCTION int error() const;
VTKMDIY_MPI_EXPORT_FUNCTION bool cancelled() const;
VTKMDIY_MPI_EXPORT_FUNCTION int count(const datatype& type) const;
operator MPI_Status&() { return s; }
operator const MPI_Status&() const { return s; }
template<class T> int count() const
{
return this->count(detail::get_mpi_datatype<T>());
}
MPI_Status s;
DIY_MPI_Status handle;
};
}
}
}
} // diy::mpi
bool
diy::mpi::status::cancelled() const
{
#ifndef VTKM_DIY_NO_MPI
int flag;
MPI_Test_cancelled(const_cast<MPI_Status*>(&s), &flag);
return flag;
#else
DIY_UNSUPPORTED_MPI_CALL(diy::mpi::status::cancelled);
#ifndef VTKMDIY_MPI_AS_LIB
#include "status.cpp"
#endif
}
template<class T>
int
diy::mpi::status::count() const
{
#ifndef VTKM_DIY_NO_MPI
int c;
MPI_Get_count(const_cast<MPI_Status*>(&s), detail::get_mpi_datatype<T>(), &c);
return c;
#else
DIY_UNSUPPORTED_MPI_CALL(diy::mpi::status::count);
#endif
}
#endif // VTKMDIY_MPI_STATUS_HPP

@ -0,0 +1,208 @@
#ifdef VTKMDIY_MPI_AS_LIB
#include "window.hpp"
#endif
#include <algorithm>
namespace diy
{
namespace mpi
{
#ifdef VTKMDIY_MPI_AS_LIB
# ifdef _MSC_VER
# define EXPORT_MACRO VTKMDIY_MPI_EXPORT
# else
# define EXPORT_MACRO
# endif
EXPORT_MACRO const int nocheck = MPI_MODE_NOCHECK;
# undef EXPORT_MACRO
#endif
namespace detail
{
DIY_MPI_Win win_create(const communicator& comm, void* base, unsigned size, int disp)
{
#if VTKMDIY_HAS_MPI
DIY_MPI_Win win;
MPI_Win_create(base, size, disp, MPI_INFO_NULL, mpi_cast(comm.handle()), &mpi_cast(win));
return win;
#else
(void)comm; (void)size; (void)disp;
auto win = make_DIY_MPI_Win(base);
return win;
#endif
}
void win_free(DIY_MPI_Win& win)
{
#if VTKMDIY_HAS_MPI
MPI_Win_free(&mpi_cast(win));
#else
(void)win;
#endif
}
void put(const DIY_MPI_Win& win, const void* data, int count, const datatype& type, int rank, unsigned offset)
{
#if VTKMDIY_HAS_MPI
MPI_Put(data, count, mpi_cast(type.handle), rank, offset, count, mpi_cast(type.handle), mpi_cast(win));
#else
void* buffer = mpi_cast(win);
size_t size = mpi_cast(type.handle);
std::copy_n(static_cast<const int8_t*>(data),
size * static_cast<size_t>(count),
static_cast<int8_t*>(buffer) + (offset * size));
(void)rank;
#endif
}
void get(const DIY_MPI_Win& win, void* data, int count, const datatype& type, int rank, unsigned offset)
{
#if VTKMDIY_HAS_MPI
MPI_Get(data, count, mpi_cast(type.handle), rank, offset, count, mpi_cast(type.handle), mpi_cast(win));
#else
const void* buffer = mpi_cast(win);
size_t size = mpi_cast(type.handle);
std::copy_n(static_cast<const int8_t*>(buffer) + (offset * size),
size * static_cast<size_t>(count),
static_cast<int8_t*>(data));
(void)rank;
#endif
}
void fence(const DIY_MPI_Win& win, int assert)
{
#if VTKMDIY_HAS_MPI
MPI_Win_fence(assert, mpi_cast(win));
#else
(void) win; (void) assert;
#endif
}
void lock(const DIY_MPI_Win& win, int lock_type, int rank, int assert)
{
#if VTKMDIY_HAS_MPI
MPI_Win_lock(lock_type, rank, assert, mpi_cast(win));
#else
(void) win; (void) lock_type; (void) rank; (void) assert;
#endif
}
void unlock(const DIY_MPI_Win& win, int rank)
{
#if VTKMDIY_HAS_MPI
MPI_Win_unlock(rank, mpi_cast(win));
#else
(void) win; (void) rank;
#endif
}
void lock_all(const DIY_MPI_Win& win, int assert)
{
#if VTKMDIY_HAS_MPI
MPI_Win_lock_all(assert, mpi_cast(win));
#else
(void) win; (void) assert;
#endif
}
void unlock_all(const DIY_MPI_Win& win)
{
#if VTKMDIY_HAS_MPI
MPI_Win_unlock_all(mpi_cast(win));
#else
(void) win;
#endif
}
void fetch_and_op(const DIY_MPI_Win& win,
const void* origin, void* result, const datatype& type,
int rank, unsigned offset,
const operation& op)
{
#if VTKMDIY_HAS_MPI
MPI_Fetch_and_op(origin, result, mpi_cast(type.handle), rank, offset, mpi_cast(op.handle), mpi_cast(win));
#else
(void) win; (void) origin; (void) result; (void) type; (void) rank; (void) offset; (void) op;
VTKMDIY_UNSUPPORTED_MPI_CALL(MPI_Fetch_and_op);
#endif
}
void fetch(const DIY_MPI_Win& win, void* result, const datatype& type, int rank, unsigned offset)
{
#if VTKMDIY_HAS_MPI
MPI_Fetch_and_op(nullptr, result, mpi_cast(type.handle), rank, offset, MPI_NO_OP, mpi_cast(win));
#else
(void) rank;
const void* buffer = mpi_cast(win);
size_t size = mpi_cast(type.handle);
std::copy_n(static_cast<const int8_t*>(buffer) + (offset * size),
size,
static_cast<int8_t*>(result));
#endif
}
void replace(const DIY_MPI_Win& win, const void* value, const datatype& type, int rank, unsigned offset)
{
#if VTKMDIY_HAS_MPI
MPI_Fetch_and_op(value, nullptr, mpi_cast(type.handle), rank, offset, MPI_REPLACE, mpi_cast(win));
#else
(void) rank;
void* buffer = mpi_cast(win);
size_t size = mpi_cast(type.handle);
std::copy_n(static_cast<const int8_t*>(value),
size,
static_cast<int8_t*>(buffer) + (offset * size));
#endif
}
void sync(const DIY_MPI_Win& win)
{
#if VTKMDIY_HAS_MPI
MPI_Win_sync(mpi_cast(win));
#else
(void) win;
#endif
}
void flush(const DIY_MPI_Win& win, int rank)
{
#if VTKMDIY_HAS_MPI
MPI_Win_flush(rank, mpi_cast(win));
#else
(void) win; (void) rank;
#endif
}
void flush_all(const DIY_MPI_Win& win)
{
#if VTKMDIY_HAS_MPI
MPI_Win_flush_all(mpi_cast(win));
#else
(void) win;
#endif
}
void flush_local(const DIY_MPI_Win& win, int rank)
{
#if VTKMDIY_HAS_MPI
MPI_Win_flush_local(rank, mpi_cast(win));
#else
(void) win; (void) rank;
#endif
}
void flush_local_all(const DIY_MPI_Win& win)
{
#if VTKMDIY_HAS_MPI
MPI_Win_flush_local_all(mpi_cast(win));
#else
(void) win;
#endif
}
}
}
} // diy::mpi::detail

@ -1,10 +1,89 @@
#ifndef VTKMDIY_MPI_WINODW_HPP
#define VTKMDIY_MPI_WINODW_HPP
#include "config.hpp"
#include "communicator.hpp"
#include "operations.hpp"
#include <type_traits>
#include <vector>
namespace diy
{
namespace mpi
{
#ifndef VTKMDIY_MPI_AS_LIB
constexpr int nocheck = MPI_MODE_NOCHECK;
#else
VTKMDIY_MPI_EXPORT extern const int nocheck;
#endif
namespace detail
{
VTKMDIY_MPI_EXPORT_FUNCTION
DIY_MPI_Win win_create(const communicator& comm, void* base, unsigned size, int disp);
VTKMDIY_MPI_EXPORT_FUNCTION
void win_free(DIY_MPI_Win& win);
VTKMDIY_MPI_EXPORT_FUNCTION
void put(const DIY_MPI_Win& win,
const void* data, int count, const datatype& type,
int rank, unsigned offset);
VTKMDIY_MPI_EXPORT_FUNCTION
void get(const DIY_MPI_Win& win,
void* data, int count, const datatype& type,
int rank, unsigned offset);
VTKMDIY_MPI_EXPORT_FUNCTION
void fence(const DIY_MPI_Win& win, int assert);
VTKMDIY_MPI_EXPORT_FUNCTION
void lock(const DIY_MPI_Win& win, int lock_type, int rank, int assert);
VTKMDIY_MPI_EXPORT_FUNCTION
void unlock(const DIY_MPI_Win& win, int rank);
VTKMDIY_MPI_EXPORT_FUNCTION
void lock_all(const DIY_MPI_Win& win, int assert);
VTKMDIY_MPI_EXPORT_FUNCTION
void unlock_all(const DIY_MPI_Win& win);
VTKMDIY_MPI_EXPORT_FUNCTION
void fetch_and_op(const DIY_MPI_Win& win,
const void* origin, void* result, const datatype& type,
int rank, unsigned offset,
const operation& op);
VTKMDIY_MPI_EXPORT_FUNCTION
void fetch(const DIY_MPI_Win& win, void* result, const datatype& type, int rank, unsigned offset);
VTKMDIY_MPI_EXPORT_FUNCTION
void replace(const DIY_MPI_Win& win,
const void* value, const datatype& type,
int rank, unsigned offset);
VTKMDIY_MPI_EXPORT_FUNCTION
void sync(const DIY_MPI_Win& win);
VTKMDIY_MPI_EXPORT_FUNCTION
void flush(const DIY_MPI_Win& win, int rank);
VTKMDIY_MPI_EXPORT_FUNCTION
void flush_all(const DIY_MPI_Win& win);
VTKMDIY_MPI_EXPORT_FUNCTION
void flush_local(const DIY_MPI_Win& win, int rank);
VTKMDIY_MPI_EXPORT_FUNCTION
void flush_local_all(const DIY_MPI_Win& win);
} // detail
//! \ingroup MPI
//! Simple wrapper around MPI window functions.
template<class T>
@ -38,7 +117,7 @@ namespace mpi
inline void lock_all(int assert = 0);
inline void unlock_all();
inline void fetch_and_op(const T* origin, T* result, int rank, unsigned offset, MPI_Op op);
inline void fetch_and_op(const T* origin, T* result, int rank, unsigned offset, const operation& op);
inline void fetch(T& result, int rank, unsigned offset);
inline void replace(const T& value, int rank, unsigned offset);
@ -52,30 +131,25 @@ namespace mpi
private:
std::vector<T> buffer_;
int rank_;
#ifndef VTKM_DIY_NO_MPI
MPI_Win window_;
#endif
DIY_MPI_Win window_;
};
} // mpi
} // diy
template<class T>
diy::mpi::window<T>::
window(const communicator& comm, unsigned size):
window(const diy::mpi::communicator& comm, unsigned size):
buffer_(size), rank_(comm.rank())
{
#ifndef VTKM_DIY_NO_MPI
MPI_Win_create(buffer_.data(), buffer_.size()*sizeof(T), sizeof(T), MPI_INFO_NULL, comm, &window_);
#endif
window_ = detail::win_create(comm, buffer_.data(), static_cast<unsigned>(buffer_.size()*sizeof(T)), static_cast<int>(sizeof(T)));
}
template<class T>
diy::mpi::window<T>::
~window()
{
#ifndef VTKM_DIY_NO_MPI
MPI_Win_free(&window_);
#endif
detail::win_free(window_);
}
template<class T>
@ -83,15 +157,7 @@ void
diy::mpi::window<T>::
put(const T& x, int rank, unsigned offset)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Put(address(x), count(x), datatype(x),
rank,
offset,
count(x), datatype(x),
window_);
#else
buffer_[offset] = x;
#endif
detail::put(window_, address(x), count(x), datatype_of(x), rank, offset);
}
template<class T>
@ -99,16 +165,7 @@ void
diy::mpi::window<T>::
put(const std::vector<T>& x, int rank, unsigned offset)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Put(address(x), count(x), datatype(x),
rank,
offset,
count(x), datatype(x),
window_);
#else
for (size_t i = 0; i < x.size(); ++i)
buffer_[offset + i] = x[i];
#endif
detail::put(window_, address(x), count(x), datatype_of(x), rank, offset);
}
template<class T>
@ -116,15 +173,7 @@ void
diy::mpi::window<T>::
get(T& x, int rank, unsigned offset)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Get(address(x), count(x), datatype(x),
rank,
offset,
count(x), datatype(x),
window_);
#else
x = buffer_[offset];
#endif
detail::get(window_, address(x), count(x), datatype_of(x), rank, offset);
}
template<class T>
@ -132,16 +181,7 @@ void
diy::mpi::window<T>::
get(std::vector<T>& x, int rank, unsigned offset)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Get(address(x), count(x), datatype(x),
rank,
offset,
count(x), datatype(x),
window_);
#else
for (size_t i = 0; i < x.size(); ++i)
x[i] = buffer_[offset + i];
#endif
detail::get(window_, address(x), count(x), datatype_of(x), rank, offset);
}
template<class T>
@ -149,9 +189,7 @@ void
diy::mpi::window<T>::
fence(int assert)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Win_fence(assert, window_);
#endif
detail::fence(window_, assert);
}
template<class T>
@ -159,9 +197,7 @@ void
diy::mpi::window<T>::
lock(int lock_type, int rank, int assert)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Win_lock(lock_type, rank, assert, window_);
#endif
detail::lock(window_, lock_type, rank, assert);
}
template<class T>
@ -169,9 +205,7 @@ void
diy::mpi::window<T>::
unlock(int rank)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Win_unlock(rank, window_);
#endif
detail::unlock(window_, rank);
}
template<class T>
@ -179,9 +213,7 @@ void
diy::mpi::window<T>::
lock_all(int assert)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Win_lock_all(assert, window_);
#endif
detail::lock_all(window_, assert);
}
template<class T>
@ -189,20 +221,15 @@ void
diy::mpi::window<T>::
unlock_all()
{
#ifndef VTKM_DIY_NO_MPI
MPI_Win_unlock_all(window_);
#endif
detail::unlock_all(window_);
}
template<class T>
void
diy::mpi::window<T>::
fetch_and_op(const T* origin, T* result, int rank, unsigned offset, MPI_Op op)
fetch_and_op(const T* origin, T* result, int rank, unsigned offset, const diy::mpi::operation& op)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Fetch_and_op(origin, result, datatype(*origin), rank, offset, op, window_);
#else
DIY_UNSUPPORTED_MPI_CALL(MPI_Fetch_and_op);
#endif
detail::fetch_and_op(window_, origin, result, datatype_of(*origin), rank, offset, op);
}
template<class T>
@ -210,12 +237,7 @@ void
diy::mpi::window<T>::
fetch(T& result, int rank, unsigned offset)
{
#ifndef VTKM_DIY_NO_MPI
T unused;
fetch_and_op(&unused, &result, rank, offset, MPI_NO_OP);
#else
result = buffer_[offset];
#endif
detail::fetch(window_, &result, datatype_of(result), rank, offset);
}
template<class T>
@ -223,12 +245,7 @@ void
diy::mpi::window<T>::
replace(const T& value, int rank, unsigned offset)
{
#ifndef VTKM_DIY_NO_MPI
T unused;
fetch_and_op(&value, &unused, rank, offset, MPI_REPLACE);
#else
buffer_[offset] = value;
#endif
detail::replace(window_, &value, datatype_of(value), rank, offset);
}
template<class T>
@ -236,9 +253,7 @@ void
diy::mpi::window<T>::
sync()
{
#ifndef VTKM_DIY_NO_MPI
MPI_Win_sync(window_);
#endif
detail::sync(window_);
}
template<class T>
@ -246,9 +261,7 @@ void
diy::mpi::window<T>::
flush(int rank)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Win_flush(rank, window_);
#endif
detail::flush(window_, rank);
}
template<class T>
@ -256,9 +269,7 @@ void
diy::mpi::window<T>::
flush_all()
{
#ifndef VTKM_DIY_NO_MPI
MPI_Win_flush_all(window_);
#endif
detail::flush_all(window_);
}
template<class T>
@ -266,9 +277,7 @@ void
diy::mpi::window<T>::
flush_local(int rank)
{
#ifndef VTKM_DIY_NO_MPI
MPI_Win_flush_local(rank, window_);
#endif
detail::flush_local(window_, rank);
}
template<class T>
@ -276,7 +285,11 @@ void
diy::mpi::window<T>::
flush_local_all()
{
#ifndef VTKM_DIY_NO_MPI
MPI_Win_flush_local_all(window_);
#endif
detail::flush_local_all(window_);
}
#ifndef VTKMDIY_MPI_AS_LIB
#include "window.cpp"
#endif
#endif // VTKMDIY_MPI_WINODW_HPP

@ -18,6 +18,8 @@ namespace diy
template<class Function, class... Args>
explicit thread(Function&& f, Args&&... args) { f(args...); } // not ideal, since it doesn't support member functions
thread& operator=(thread&&) = default;
void join() {}
static unsigned hardware_concurrency() { return 1; }
@ -31,8 +33,13 @@ namespace diy
struct lock_guard
{
lock_guard(T&) {}
void lock() {}
void unlock() {}
};
template<class T, class U>
using concurrent_map = std::map<T,U>;
namespace this_thread
{
inline unsigned long int get_id() { return 0; }

@ -2,63 +2,74 @@
#define VTKMDIY_PICK_HPP
#include "link.hpp"
#include "constants.h" // for DEPRECATED
namespace diy
{
template<class Bounds, class Point, class OutIter>
void near(const RegularLink<Bounds>& link, const Point& p, float r, OutIter out,
const Bounds& domain);
template<class Bounds, class Point, class OutIter, class Coordinate>
void near(const RegularLink<Bounds>& link, const Point& p, Coordinate r, OutIter out, const Bounds& domain);
template<class Bounds, class Point, class OutIter>
void in(const RegularLink<Bounds>& link, const Point& p, OutIter out, const Bounds& domain);
void in(const RegularLink<Bounds>& link, const Point& p, OutIter out, const Bounds& domain, bool core = true);
template<class Point, class Bounds>
float distance(int dim, const Bounds& bounds, const Point& p);
template<class Point, class Bounds, class Out = double>
Out distance(const Bounds& bounds, const Point& p);
template<class Point, class Bounds, class Out = double>
DEPRECATED("Use distance(const Bounds& bounds, const Point& p) instead.")
Out distance(int dim, const Bounds& bounds, const Point& p);
template<class Bounds, class Out = double>
Out distance(const Bounds& bounds1, const Bounds& bounds2);
template<class Bounds, class Out = double>
DEPRECATED("Use distance(const Bounds& bounds1, const Bounds& bounds2) instead.")
Out distance(int dim, const Bounds& bounds1, const Bounds& bounds2);
template<class Bounds>
inline
float distance(int dim, const Bounds& bounds1, const Bounds& bounds2);
void wrap_bounds(Bounds& bounds, Direction wrap_dir, const Bounds& domain);
template<class Bounds>
DEPRECATED("Use wrap_bounds(Bounds& bounds, Direction wrap_dir, const Bounds& domain) instead.")
void wrap_bounds(Bounds& bounds, Direction wrap_dir, const Bounds& domain, int dim);
}
//! Finds the neighbors within radius r of a target point.
template<class Bounds, class Point, class OutIter>
template<class Bounds, class Point, class OutIter, class Coordinate>
void
diy::
near(const RegularLink<Bounds>& link, //!< neighbors
const Point& p, //!< target point (must be in current block)
float r, //!< target radius (>= 0.0)
Coordinate r, //!< target radius (>= 0.0)
OutIter out, //!< insert iterator for output set of neighbors
const Bounds& domain) //!< global domain bounds
{
Bounds neigh_bounds; // neighbor block bounds
Bounds neigh_bounds {0}; // neighbor block bounds
// for all neighbors of this block
for (int n = 0; n < link.size(); n++)
{
// wrap neighbor bounds, if necessary, otherwise bounds will be unchanged
neigh_bounds = link.bounds(n);
wrap_bounds(neigh_bounds, link.wrap(n), domain, link.dimension());
wrap_bounds(neigh_bounds, link.wrap(n), domain);
if (distance(link.dimension(), neigh_bounds, p) <= r)
if (distance(neigh_bounds, p) <= r)
*out++ = n;
} // for all neighbors
}
//! Find the distance between point `p` and box `bounds`.
template<class Point, class Bounds>
float
template<class Point, class Bounds, class Out>
Out
diy::
distance(int dim, const Bounds& bounds, const Point& p)
distance(const Bounds& bounds, const Point& p)
{
float res = 0;
for (int i = 0; i < dim; ++i)
Out res = 0;
for (int i = 0; i < p.size(); ++i)
{
// avoids all the annoying case logic by finding
// diff = max(bounds.min[i] - p[i], 0, p[i] - bounds.max[i])
float diff = 0, d;
Out diff = 0, d;
d = bounds.min[i] - p[i];
if (d > diff) diff = d;
@ -70,18 +81,68 @@ distance(int dim, const Bounds& bounds, const Point& p)
return sqrt(res);
}
template<class Bounds>
float
// DEPRECATED
//! Find the distance between point `p` and box `bounds`.
template<class Point, class Bounds, class Out>
Out
diy::
distance(int dim, const Bounds& bounds, const Point& p)
{
Out res = 0;
for (int i = 0; i < dim; ++i)
{
// avoids all the annoying case logic by finding
// diff = max(bounds.min[i] - p[i], 0, p[i] - bounds.max[i])
Out diff = 0, d;
d = bounds.min[i] - p[i];
if (d > diff) diff = d;
d = p[i] - bounds.max[i];
if (d > diff) diff = d;
res += diff*diff;
}
return sqrt(res);
}
template<class Bounds, class Out>
Out
diy::
distance(const Bounds& bounds1, const Bounds& bounds2)
{
Out res = 0;
for (int i = 0; i < bounds1.min.size(); ++i) // assume min, max of both bounds have same size
{
Out diff = 0, d;
Out d1 = bounds1.max[i] - bounds2.min[i];
Out d2 = bounds2.max[i] - bounds1.min[i];
if (d1 > 0 && d2 > 0)
diff = 0;
else if (d1 <= 0)
diff = -d1;
else if (d2 <= 0)
diff = -d2;
res += diff*diff;
}
return sqrt(res);
}
// DEPRECATED
template<class Bounds, class Out>
Out
diy::
distance(int dim, const Bounds& bounds1, const Bounds& bounds2)
{
float res = 0;
Out res = 0;
for (int i = 0; i < dim; ++i)
{
float diff = 0, d;
Out diff = 0, d;
float d1 = bounds1.max[i] - bounds2.min[i];
float d2 = bounds2.max[i] - bounds1.min[i];
Out d1 = bounds1.max[i] - bounds2.min[i];
Out d2 = bounds2.max[i] - bounds1.min[i];
if (d1 > 0 && d2 > 0)
diff = 0;
@ -102,22 +163,43 @@ diy::
in(const RegularLink<Bounds>& link, //!< neighbors
const Point& p, //!< target point
OutIter out, //!< insert iterator for output set of neighbors
const Bounds& domain) //!< global domain bounds
const Bounds& domain, //!< global domain bounds
bool core) //!< check against core (or bounds, if false)
{
Bounds neigh_bounds; // neighbor block bounds
Bounds neigh_bounds {0}; // neighbor block bounds
// for all neighbors of this block
for (int n = 0; n < link.size(); n++)
{
// wrap neighbor bounds, if necessary, otherwise bounds will be unchanged
neigh_bounds = link.bounds(n);
wrap_bounds(neigh_bounds, link.wrap(n), domain, link.dimension());
if (core)
neigh_bounds = link.core(n);
else
neigh_bounds = link.bounds(n);
if (distance(link.dimension(), neigh_bounds, p) == 0)
// wrap neighbor bounds, if necessary, otherwise bounds will be unchanged
wrap_bounds(neigh_bounds, link.wrap(n), domain);
if (distance(neigh_bounds, p) == 0)
*out++ = n;
} // for all neighbors
}
// wraps block bounds
// wrap dir is the wrapping direction from original block to wrapped neighbor block
// overall domain bounds and dimensionality are also needed
template<class Bounds>
void
diy::
wrap_bounds(Bounds& bounds, Direction wrap_dir, const Bounds& domain)
{
for (int i = 0; i < bounds.min.size(); ++i) // assume min, max of bounds, domain have same size
{
bounds.min[i] += wrap_dir[i] * (domain.max[i] - domain.min[i]);
bounds.max[i] += wrap_dir[i] * (domain.max[i] - domain.min[i]);
}
}
// DEPRECATED
// wraps block bounds
// wrap dir is the wrapping direction from original block to wrapped neighbor block
// overall domain bounds and dimensionality are also needed
@ -133,5 +215,4 @@ wrap_bounds(Bounds& bounds, Direction wrap_dir, const Bounds& domain, int dim)
}
}
#endif

@ -8,6 +8,8 @@
#include <array>
#include "constants.h" // for DEPRECATED
namespace diy
{
@ -53,7 +55,9 @@ class Point: public std::array<Coordinate_, D>
Point& operator*=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] *= a; return *this; }
Point& operator/=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] /= a; return *this; }
Coordinate norm() const { return (*this)*(*this); }
DEPRECATED("Use norm2 instead")
Coordinate norm() const { return norm2(); }
Coordinate norm2() const { return (*this)*(*this); }
std::ostream& operator<<(std::ostream& out) const { out << (*this)[0]; for (unsigned i = 1; i < D; ++i) out << " " << (*this)[i]; return out; }
std::istream& operator>>(std::istream& in);
@ -117,4 +121,4 @@ operator>>(std::istream& in, Point<C,D>& p)
}
#endif // DIY_POINT_HPP
#endif // VTKMDIY_POINT_HPP

@ -10,29 +10,99 @@ namespace diy
template <class T>
struct EnqueueIterator;
Proxy(Master* master__, int gid__):
using IncomingQueues = std::map<int, MemoryBuffer>;
using OutgoingQueues = std::map<BlockID, MemoryBuffer>;
Proxy(Master* master__, int gid__,
IExchangeInfo* iexchange__ = 0):
gid_(gid__),
master_(master__),
incoming_(&master__->incoming(gid__)),
outgoing_(&master__->outgoing(gid__)),
collectives_(&master__->collectives(gid__)) {}
iexchange_(iexchange__),
collectives_(&master__->collectives(gid__))
{
fill_incoming();
// move outgoing_ back into proxy, in case it's a multi-foreach round
if (!iexchange_)
for (auto& x : master_->outgoing(gid_))
{
auto access = x.second.access();
if (!access->empty())
{
outgoing_.emplace(x.first, access->back().move());
access->pop_back();
}
}
}
// delete copy constructor to avoid coping incoming_ and outgoing_ (plus it
// won't work otherwise because MemoryBuffer has a deleted copy
// constructor)
Proxy(const Proxy&) =delete;
Proxy(Proxy&&) =default;
Proxy& operator=(const Proxy&) =delete;
Proxy& operator=(Proxy&&) =default;
~Proxy()
{
auto& outgoing = master_->outgoing(gid_);
auto& incoming = master_->incoming(gid_);
// copy out outgoing_
for (auto& x : outgoing_)
{
outgoing[x.first].access()->emplace_back(std::move(x.second));
if (iexchange_)
iexchange_->inc_work();
}
// move incoming_ back into master, in case it's a multi-foreach round
if (!iexchange_)
for (auto& x : incoming_)
incoming[x.first].access()->emplace_front(std::move(x.second));
}
int gid() const { return gid_; }
bool fill_incoming() const
{
bool exists = false;
incoming_.clear();
// fill incoming_
for (auto& x : master_->incoming(gid_))
{
auto access = x.second.access();
if (!access->empty())
{
exists = true;
incoming_.emplace(x.first, access->front().move());
access->pop_front();
if (iexchange_)
iexchange_->dec_work();
}
}
return exists;
}
//! Enqueue data whose size can be determined automatically, e.g., an STL vector.
template<class T>
void enqueue(const BlockID& to, //!< target block (gid,proc)
const T& x, //!< data (eg. STL vector)
void (*save)(BinaryBuffer&, const T&) = &::diy::save<T> //!< optional serialization function
void (*save)(BinaryBuffer&, const T&) = &::diy::save //!< optional serialization function
) const
{ OutgoingQueues& out = *outgoing_; save(out[to], x); }
{
save(outgoing_[to], x);
}
//! Enqueue data whose size is given explicitly by the user, e.g., an array.
template<class T>
void enqueue(const BlockID& to, //!< target block (gid,proc)
const T* x, //!< pointer to the data (eg. address of start of vector)
size_t n, //!< size in data elements (eg. ints)
void (*save)(BinaryBuffer&, const T&) = &::diy::save<T> //!< optional serialization function
void (*save)(BinaryBuffer&, const T&) = &::diy::save //!< optional serialization function
) const;
//! Dequeue data whose size can be determined automatically (e.g., STL vector) and that was
@ -41,9 +111,9 @@ namespace diy
template<class T>
void dequeue(int from, //!< target block gid
T& x, //!< data (eg. STL vector)
void (*load)(BinaryBuffer&, T&) = &::diy::load<T> //!< optional serialization function
void (*load)(BinaryBuffer&, T&) = &::diy::load //!< optional serialization function
) const
{ IncomingQueues& in = *incoming_; load(in[from], x); }
{ load(incoming_[from], x); }
//! Dequeue an array of data whose size is given explicitly by the user.
//! In this case, the user needs to allocate the receive buffer prior to calling dequeue.
@ -51,7 +121,7 @@ namespace diy
void dequeue(int from, //!< target block gid
T* x, //!< pointer to the data (eg. address of start of vector)
size_t n, //!< size in data elements (eg. ints)
void (*load)(BinaryBuffer&, T&) = &::diy::load<T> //!< optional serialization function
void (*load)(BinaryBuffer&, T&) = &::diy::load //!< optional serialization function
) const;
//! Dequeue data whose size can be determined automatically (e.g., STL vector) and that was
@ -60,7 +130,7 @@ namespace diy
template<class T>
void dequeue(const BlockID& from, //!< target block (gid,proc)
T& x, //!< data (eg. STL vector)
void (*load)(BinaryBuffer&, T&) = &::diy::load<T> //!< optional serialization function
void (*load)(BinaryBuffer&, T&) = &::diy::load //!< optional serialization function
) const { dequeue(from.gid, x, load); }
//! Dequeue an array of data whose size is given explicitly by the user.
@ -69,20 +139,24 @@ namespace diy
void dequeue(const BlockID& from, //!< target block (gid,proc)
T* x, //!< pointer to the data (eg. address of start of vector)
size_t n, //!< size in data elements (eg. ints)
void (*load)(BinaryBuffer&, T&) = &::diy::load<T> //!< optional serialization function
void (*load)(BinaryBuffer&, T&) = &::diy::load //!< optional serialization function
) const { dequeue(from.gid, x, n, load); }
template<class T>
EnqueueIterator<T> enqueuer(const T& x,
void (*save)(BinaryBuffer&, const T&) = &::diy::save<T>) const
void (*save)(BinaryBuffer&, const T&) = &::diy::save ) const
{ return EnqueueIterator<T>(this, x, save); }
IncomingQueues* incoming() const { return incoming_; }
MemoryBuffer& incoming(int from) const { return (*incoming_)[from]; }
IncomingQueues* incoming() const { return &incoming_; }
MemoryBuffer& incoming(int from) const { return incoming_[from]; }
inline void incoming(std::vector<int>& v) const; // fill v with every gid from which we have a message
OutgoingQueues* outgoing() const { return outgoing_; }
MemoryBuffer& outgoing(const BlockID& to) const { return (*outgoing_)[to]; }
OutgoingQueues* outgoing() const { return &outgoing_; }
MemoryBuffer& outgoing(const BlockID& to) const { return outgoing_[to]; }
inline bool empty_incoming_queues() const;
inline bool empty_outgoing_queues() const;
inline bool empty_queues() const;
/**
* \ingroup Communication
@ -118,12 +192,18 @@ namespace diy
CollectivesList* collectives() const { return collectives_; }
Master* master() const { return master_; }
IExchangeInfo* iexchange() const { return iexchange_; }
private:
int gid_;
Master* master_;
IncomingQueues* incoming_;
OutgoingQueues* outgoing_;
IExchangeInfo* iexchange_;
// TODO: these are marked mutable to not have to undo consts on enqueue/dequeue, in case it breaks things;
// eventually, implement this change
mutable IncomingQueues incoming_;
mutable OutgoingQueues outgoing_;
CollectivesList* collectives_;
};
@ -151,14 +231,12 @@ namespace diy
struct Master::ProxyWithLink: public Master::Proxy
{
ProxyWithLink(const Proxy& proxy,
ProxyWithLink(Proxy&& proxy,
void* block__,
Link* link__,
IExchangeInfo* iexchange__ = 0):
Proxy(proxy),
Link* link__):
Proxy(std::move(proxy)),
block_(block__),
link_(link__),
iexchange_(iexchange__) {}
link_(link__) {}
Link* link() const { return link_; }
void* block() const { return block_; }
@ -166,52 +244,6 @@ namespace diy
private:
void* block_;
Link* link_;
IExchangeInfo* iexchange_; // not used for iexchange presently, but later could trigger some special behavior
public:
template<class T>
void enqueue(const BlockID& to,
const T& x,
void (*save)(BinaryBuffer&, const T&) = &::diy::save<T>) const
{
diy::Master::Proxy::enqueue(to, x, save);
if (iexchange_)
master()->icommunicate(iexchange_);
}
template<class T>
void enqueue(const BlockID& to,
const T* x,
size_t n,
void (*save)(BinaryBuffer&, const T&) = &::diy::save<T>) const
{
diy::Master::Proxy::enqueue(to, x, n, save);
if (iexchange_)
master()->icommunicate(iexchange_);
}
template<class T>
void dequeue(int from,
T& x,
void (*load)(BinaryBuffer&, T&) = &::diy::load<T>) const
{
// TODO: uncomment if necessary, try first without icommunicating on dequeue
// if (iexchange_)
// master()->icommunicate(iexchange_);
diy::Master::Proxy::dequeue(from, x, load);
}
template<class T>
void dequeue(int from,
T* x,
size_t n,
void (*load)(BinaryBuffer&, T&) = &::diy::load<T>) const
{
// TODO: uncomment if necessary, try first without icommunicating on dequeue
// if (iexchange_)
// master()->icommunicate(iexchange_);
diy::Master::Proxy::dequeue(from, x, n, load);
}
};
} // diy namespace
@ -219,10 +251,38 @@ void
diy::Master::Proxy::
incoming(std::vector<int>& v) const
{
for (IncomingQueues::const_iterator it = incoming_->begin(); it != incoming_->end(); ++it)
v.push_back(it->first);
for (auto& x : incoming_)
v.push_back(x.first);
}
bool
diy::Master::Proxy::
empty_incoming_queues() const
{
for (auto& x : *incoming())
if (x.second)
return false;
return true;
}
bool
diy::Master::Proxy::
empty_outgoing_queues() const
{
for (auto& x : *outgoing())
if (x.second.size())
return false;
return true;
}
bool
diy::Master::Proxy::
empty_queues() const
{
return empty_incoming_queues() && empty_outgoing_queues();
}
template<class T, class Op>
void
diy::Master::Proxy::
@ -265,8 +325,7 @@ diy::Master::Proxy::
enqueue(const BlockID& to, const T* x, size_t n,
void (*save)(BinaryBuffer&, const T&)) const
{
OutgoingQueues& out = *outgoing_;
BinaryBuffer& bb = out[to];
BinaryBuffer& bb = outgoing_[to];
if (save == (void (*)(BinaryBuffer&, const T&)) &::diy::save<T>)
diy::save(bb, x, n); // optimized for unspecialized types
else
@ -280,8 +339,7 @@ diy::Master::Proxy::
dequeue(int from, T* x, size_t n,
void (*load)(BinaryBuffer&, T&)) const
{
IncomingQueues& in = *incoming_;
BinaryBuffer& bb = in[from];
BinaryBuffer& bb = incoming_[from];
if (load == (void (*)(BinaryBuffer&, T&)) &::diy::load<T>)
diy::load(bb, x, n); // optimized for unspecialized types
else

@ -16,13 +16,13 @@ struct ReduceProxy: public Master::Proxy
{
typedef std::vector<int> GIDVector;
ReduceProxy(const Master::Proxy& proxy, //!< parent proxy
ReduceProxy(Master::Proxy&& proxy, //!< parent proxy
void* block, //!< diy block
unsigned round, //!< current round
const Assigner& assigner, //!< assigner
const GIDVector& incoming_gids, //!< incoming gids in this group
const GIDVector& outgoing_gids): //!< outgoing gids in this group
Master::Proxy(proxy),
Master::Proxy(std::move(proxy)),
block_(block),
round_(round),
assigner_(assigner)
@ -46,13 +46,13 @@ struct ReduceProxy: public Master::Proxy
}
}
ReduceProxy(const Master::Proxy& proxy, //!< parent proxy
ReduceProxy(Master::Proxy&& proxy, //!< parent proxy
void* block, //!< diy block
unsigned round, //!< current round
const Assigner& assigner,
const Link& in_link,
const Link& out_link):
Master::Proxy(proxy),
Master::Proxy(std::move(proxy)),
block_(block),
round_(round),
assigner_(assigner),
@ -170,7 +170,7 @@ namespace detail
{
using Callback = std::function<void(Block*, const ReduceProxy&, const Partners&)>;
ReductionFunctor(unsigned round_, const Callback& reduce_, const Partners& partners_, const Assigner& assigner_):
ReductionFunctor(int round_, const Callback& reduce_, const Partners& partners_, const Assigner& assigner_):
round(round_), reduce(reduce_), partners(partners_), assigner(assigner_) {}
void operator()(Block* b, const Master::ProxyWithLink& cp) const
@ -180,20 +180,20 @@ namespace detail
std::vector<int> incoming_gids, outgoing_gids;
if (round > 0)
partners.incoming(round, cp.gid(), incoming_gids, *cp.master()); // receive from the previous round
if (round < partners.rounds())
if (round < static_cast<int>(partners.rounds()))
partners.outgoing(round, cp.gid(), outgoing_gids, *cp.master()); // send to the next round
ReduceProxy rp(cp, b, round, assigner, incoming_gids, outgoing_gids);
ReduceProxy rp(std::move(const_cast<Master::ProxyWithLink&>(cp)), b, round, assigner, incoming_gids, outgoing_gids);
reduce(b, rp, partners);
// touch the outgoing queues to make sure they exist
Master::OutgoingQueues& outgoing = *cp.outgoing();
if (outgoing.size() < (size_t) rp.out_link().size())
for (int j = 0; j < rp.out_link().size(); ++j)
outgoing[rp.out_link().target(j)]; // touch the outgoing queue, creating it if necessary
Master::Proxy::OutgoingQueues& outgoing = *rp.outgoing();
if (outgoing.size() < static_cast<size_t>(rp.out_link().size()))
for (BlockID target : rp.out_link().neighbors())
outgoing[target]; // touch the outgoing queue, creating it if necessary
}
unsigned round;
int round;
Callback reduce;
Partners partners;
const Assigner& assigner;
@ -213,4 +213,4 @@ namespace detail
} // diy
#endif // DIY_REDUCE_HPP
#endif // VTKMDIY_REDUCE_HPP

@ -13,6 +13,8 @@
#include <unordered_set>
#include <type_traits> // this is used for a safety check for default serialization
#include <cassert>
namespace diy
{
//! A serialization buffer. \ingroup Serialization
@ -30,6 +32,11 @@ namespace diy
MemoryBuffer(size_t position_ = 0):
position(position_) {}
MemoryBuffer(MemoryBuffer&&) =default;
MemoryBuffer(const MemoryBuffer&) =delete;
MemoryBuffer& operator=(MemoryBuffer&&) =default;
MemoryBuffer& operator=(const MemoryBuffer&) =delete;
virtual inline void save_binary(const char* x, size_t count) override; //!< copy `count` bytes from `x` into the buffer
virtual inline void append_binary(const char* x, size_t count) override; //!< append `count` bytes from `x` to end of buffer
virtual inline void load_binary(char* x, size_t count) override; //!< copy `count` bytes into `x` from the buffer
@ -52,7 +59,7 @@ namespace diy
static float growth_multiplier() { return 1.5; }
// simple file IO
void write(const std::string& fn) const { std::ofstream out(fn.c_str()); out.write(&buffer[0], size()); }
void write(const std::string& fn) const { std::ofstream out(fn.c_str()); out.write(&buffer[0], static_cast<std::streamsize>(size())); }
void read(const std::string& fn)
{
std::ifstream in(fn.c_str(), std::ios::binary | std::ios::ate);
@ -99,22 +106,16 @@ namespace diy
// 20150422 == 5.1
// 20141030 == 4.9.2
// See https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html#abi.versioning.__GLIBCXX__
#if defined(__GLIBCXX__) && \
#if !(defined(__GLIBCXX__) && \
(__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20160726 || __GLIBCXX__ == 20150626 || \
__GLIBCXX__ == 20150623)
#define VTKMDIY_USING_GLIBCXX_4
#endif
#if !defined(VTKMDIY_USING_GLIBCXX_4)
__GLIBCXX__ == 20150623))
//exempt glibcxx-4 variants as they don't have is_trivially_copyable implemented
static_assert(std::is_trivially_copyable<T>::value, "Default serialization works only for trivially copyable types");
#else
# undef VTKMDIY_USING_GLIBCXX_4
#endif
static void save(BinaryBuffer& bb, const T& x) { bb.save_binary((const char*) &x, sizeof(T)); }
static void load(BinaryBuffer& bb, T& x) { bb.load_binary((char*) &x, sizeof(T)); }
static size_t size(const T& x) { return sizeof(T); }
static size_t size(const T&) { return sizeof(T); }
};
//! Saves `x` to `bb` by calling `diy::Serialization<T>::save(bb,x)`.
@ -185,14 +186,16 @@ namespace diy
static void save(BinaryBuffer& bb, const MemoryBuffer& x)
{
diy::save(bb, x.position);
diy::save(bb, &x.buffer[0], x.position);
if (x.position > 0)
diy::save(bb, &x.buffer[0], x.position);
}
static void load(BinaryBuffer& bb, MemoryBuffer& x)
{
diy::load(bb, x.position);
x.buffer.resize(x.position);
diy::load(bb, &x.buffer[0], x.position);
if (x.position > 0)
diy::load(bb, &x.buffer[0], x.position);
}
static size_t size(const MemoryBuffer& x)
@ -219,7 +222,7 @@ namespace diy
{
size_t s;
diy::load(bb, s);
v.resize(s);
v.resize(s, U());
if (s > 0)
diy::load(bb, &v[0], s);
}
@ -242,7 +245,7 @@ namespace diy
{
size_t s;
diy::load(bb, s);
v.resize(s);
v.resize(s, U());
if (s > 0)
diy::load(bb, &v[0], s);
}

@ -4,15 +4,83 @@
#include <chrono>
#include <string>
#include <vector>
#include <unordered_map>
#include "log.hpp" // need this for format
#include "log.hpp"
#if defined(VTKMDIY_USE_CALIPER)
#include <caliper/cali.h>
#include <caliper/common/Variant.h>
#endif
namespace diy
{
namespace stats
{
#if defined(DIY_PROFILE)
inline
std::ostream&
operator<<(std::ostream& out, const std::chrono::high_resolution_clock::duration& d)
{
auto time = std::chrono::duration_cast<std::chrono::microseconds>(d).count();
fmt::print(out, "{:02d}:{:02d}:{:02d}.{:06d}",
time/1000000/60/60,
time/1000000/60 % 60,
time/1000000 % 60,
time % 1000000);
return out;
}
struct DurationAccumulator
{
using Clock = std::chrono::high_resolution_clock;
using Time = Clock::time_point;
using Duration = Clock::duration;
void operator<<(std::string name) { last[name] = Clock::now(); }
void operator>>(std::string name) { duration[name] += Clock::now() - last[name]; }
void clear() { last.clear(); duration.clear(); }
std::unordered_map<std::string, Time> last;
std::unordered_map<std::string, Duration> duration;
void output(std::ostream& out, std::string prefix = "") const
{
if (!prefix.empty())
prefix += " ";
for (auto& x : duration)
out << prefix << x.second << ' ' << x.first << '\n';
}
};
template<class Profiler>
struct ScopedProfile
{
ScopedProfile(Profiler& prof_, std::string name_):
prof(prof_), name(name_), active(true) { prof << name; }
~ScopedProfile() { if (active) prof >> name; }
ScopedProfile(ScopedProfile&& other):
prof(other.prof),
name(other.name),
active(other.active) { other.active = false; }
ScopedProfile&
operator=(ScopedProfile&& other) = delete;
ScopedProfile(const ScopedProfile&) = delete;
ScopedProfile&
operator=(const ScopedProfile&) = delete;
Profiler& prof;
std::string name;
bool active;
};
#if !defined(VTKMDIY_USE_CALIPER)
#if defined(VTKMDIY_PROFILE)
struct Profiler
{
using Clock = std::chrono::high_resolution_clock;
@ -32,28 +100,7 @@ struct Profiler
};
using EventsVector = std::vector<Event>;
struct Scoped
{
Scoped(Profiler& prof_, std::string name_):
prof(prof_), name(name_), active(true) { prof << name; }
~Scoped() { if (active) prof >> name; }
Scoped(Scoped&& other):
prof(other.prof),
name(other.name),
active(other.active) { other.active = false; }
Scoped&
operator=(Scoped&& other) = delete;
Scoped(const Scoped&) = delete;
Scoped&
operator=(const Scoped&) = delete;
Profiler& prof;
std::string name;
bool active;
};
using Scoped = ScopedProfile<Profiler>;
Profiler() { reset_time(); }
@ -62,10 +109,10 @@ struct Profiler
void operator<<(std::string name) { enter(name); }
void operator>>(std::string name) { exit(name); }
void enter(std::string name) { events.push_back(Event(name, true)); }
void exit(std::string name) { events.push_back(Event(name, false)); }
void enter(std::string name) { events.push_back(Event(name, true)); total << name; }
void exit(std::string name) { events.push_back(Event(name, false)); total >> name; }
void output(std::ostream& out, std::string prefix = "")
void output(std::ostream& out, std::string prefix = "") const
{
if (!prefix.empty())
prefix += " ";
@ -73,44 +120,103 @@ struct Profiler
for (size_t i = 0; i < events.size(); ++i)
{
const Event& e = events[i];
auto time = std::chrono::duration_cast<std::chrono::microseconds>(e.stamp - start).count();
fmt::print(out, "{}{:02d}:{:02d}:{:02d}.{:06d} {}{}\n",
prefix,
time/1000000/60/60,
time/1000000/60 % 60,
time/1000000 % 60,
time % 1000000,
(e.begin ? '<' : '>'),
e.name);
out << prefix << (e.stamp - start) << ' ' << (e.begin ? '<' : '>') << e.name << '\n';
}
out << "# Total times:\n";
total.output(out, "# ");
}
Scoped scoped(std::string name) { return Scoped(*this, name); }
void clear() { events.clear(); }
void clear() { events.clear(); total.clear(); }
const DurationAccumulator& totals() const { return total; }
private:
Time start;
EventsVector events;
Time start;
EventsVector events;
DurationAccumulator total;
};
#else
#else // VTKMDIY_PROFILE
struct Profiler
{
struct Scoped {};
using Scoped = ScopedProfile<Profiler>;
void reset_time() {}
void reset_time() {}
void operator<<(std::string) {}
void operator>>(std::string) {}
void operator<<(std::string name) { enter(name); }
void operator>>(std::string name) { exit(name); }
void enter(const std::string&) {}
void exit(const std::string&) {}
void enter(std::string) {}
void exit(std::string) {}
void output(std::ostream&, std::string = "") {}
void clear() {}
void output(std::ostream& out, std::string = "") const
{
out << "# Total times:\n";
total.output(out, "# ");
}
void clear() { total.clear(); }
Scoped scoped(std::string) { return Scoped(); }
Scoped scoped(std::string name) { return Scoped(*this, name); }
const DurationAccumulator&
totals() const { return total; }
private:
DurationAccumulator total;
};
#endif // VTKMDIY_PROFILE
// Annotations don't do anything without Caliper
struct Annotation
{
struct Guard
{
Guard(Annotation&) {}
};
Annotation(const char*) {}
template<class T>
Annotation& set(T) { return *this; }
};
struct Variant
{
template<class T>
Variant(T) {}
};
#else // VTKMDIY_USE_CALIPER
using Annotation = cali::Annotation;
using Variant = cali::Variant;
struct Profiler
{
using Scoped = ScopedProfile<Profiler>;
void reset_time() {}
void operator<<(std::string name) { enter(name); }
void operator>>(std::string name) { exit(name); }
void enter(std::string name) { CALI_MARK_BEGIN(name.c_str()); }
void exit(std::string name) { CALI_MARK_END(name.c_str()); }
void output(std::ostream& out, std::string = "") const {}
void clear() {}
Scoped scoped(std::string name) { return Scoped(*this, name); }
// unused
const DurationAccumulator&
totals() const { return total; }
private:
DurationAccumulator total;
};
#endif
}

@ -26,14 +26,14 @@ namespace diy
virtual inline void save_binary(const char* x, size_t count) override { fwrite(x, 1, count, file); head += count; }
virtual inline void append_binary(const char* x, size_t count) override
{
size_t temp_pos = ftell(file);
auto temp_pos = ftell(file);
fseek(file, static_cast<long>(tail), SEEK_END);
fwrite(x, 1, count, file);
tail += count;
fseek(file, temp_pos, SEEK_SET);
}
virtual inline void load_binary(char* x, size_t count) override { auto n = fread(x, 1, count, file); (void) n;}
virtual inline void load_binary_back(char* x, size_t count) override { fseek(file, static_cast<long>(tail), SEEK_END); auto n = fread(x, 1, count, file); tail += count; fseek(file, static_cast<long>(head), SEEK_SET); (void) n;}
virtual inline void load_binary(char* x, size_t count) override { auto n = fread(x, 1, count, file); VTKMDIY_UNUSED(n);}
virtual inline void load_binary_back(char* x, size_t count) override { fseek(file, static_cast<long>(tail), SEEK_END); auto n = fread(x, 1, count, file); tail += count; fseek(file, static_cast<long>(head), SEEK_SET); VTKMDIY_UNUSED(n);}
size_t size() const { return head; }
@ -135,7 +135,8 @@ namespace diy
_read(fh, &bb.buffer[0], static_cast<unsigned int>(fr.size));
#else
int fh = open(fr.name.c_str(), O_RDONLY | O_SYNC, 0600);
auto n = read(fh, &bb.buffer[0], fr.size); (void) n;
auto n = read(fh, &bb.buffer[0], fr.size);
VTKMDIY_UNUSED(n);
#endif
io::utils::close(fh);
remove_file(fr);

File diff suppressed because it is too large Load Diff

@ -0,0 +1,829 @@
// Formatting library for C++ - chrono support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_CHRONO_H_
#define FMT_CHRONO_H_
#include "format.h"
#include "locale.h"
#include <chrono>
#include <ctime>
#include <locale>
#include <sstream>
// enable safe chrono durations, unless explicitly disabled
#ifndef FMT_SAFE_DURATION_CAST
# define FMT_SAFE_DURATION_CAST 1
#endif
#if FMT_SAFE_DURATION_CAST
# include "safe-duration-cast.h"
#endif
FMT_BEGIN_NAMESPACE
// Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO()
#define FMT_NOMACRO
namespace internal {
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }
} // namespace internal
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
using namespace fmt::internal;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
}
#endif
};
dispatcher lt(time);
// Too big time values may be unsupported.
if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
return lt.tm_;
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
}
#endif
};
dispatcher gt(time);
// Too big time values may be unsupported.
if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
return gt.tm_;
}
namespace internal {
inline std::size_t strftime(char* str, std::size_t count, const char* format,
const std::tm* time) {
return std::strftime(str, count, format, time);
}
inline std::size_t strftime(wchar_t* str, std::size_t count,
const wchar_t* format, const std::tm* time) {
return std::wcsftime(str, count, format, time);
}
} // namespace internal
template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
if (it != ctx.end() && *it == ':') ++it;
auto end = it;
while (end != ctx.end() && *end != '}') ++end;
tm_format.reserve(internal::to_unsigned(end - it + 1));
tm_format.append(it, end);
tm_format.push_back('\0');
return end;
}
template <typename FormatContext>
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buf;
std::size_t start = buf.size();
for (;;) {
std::size_t size = buf.capacity() - start;
std::size_t count =
internal::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) {
buf.resize(start + count);
break;
}
if (size >= tm_format.size() * 256) {
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
return std::copy(buf.begin(), buf.end(), ctx.out());
}
basic_memory_buffer<Char> tm_format;
};
namespace internal {
template <typename Period> FMT_CONSTEXPR const char* get_units() {
return nullptr;
}
template <> FMT_CONSTEXPR const char* get_units<std::atto>() { return "as"; }
template <> FMT_CONSTEXPR const char* get_units<std::femto>() { return "fs"; }
template <> FMT_CONSTEXPR const char* get_units<std::pico>() { return "ps"; }
template <> FMT_CONSTEXPR const char* get_units<std::nano>() { return "ns"; }
template <> FMT_CONSTEXPR const char* get_units<std::micro>() { return "µs"; }
template <> FMT_CONSTEXPR const char* get_units<std::milli>() { return "ms"; }
template <> FMT_CONSTEXPR const char* get_units<std::centi>() { return "cs"; }
template <> FMT_CONSTEXPR const char* get_units<std::deci>() { return "ds"; }
template <> FMT_CONSTEXPR const char* get_units<std::ratio<1>>() { return "s"; }
template <> FMT_CONSTEXPR const char* get_units<std::deca>() { return "das"; }
template <> FMT_CONSTEXPR const char* get_units<std::hecto>() { return "hs"; }
template <> FMT_CONSTEXPR const char* get_units<std::kilo>() { return "ks"; }
template <> FMT_CONSTEXPR const char* get_units<std::mega>() { return "Ms"; }
template <> FMT_CONSTEXPR const char* get_units<std::giga>() { return "Gs"; }
template <> FMT_CONSTEXPR const char* get_units<std::tera>() { return "Ts"; }
template <> FMT_CONSTEXPR const char* get_units<std::peta>() { return "Ps"; }
template <> FMT_CONSTEXPR const char* get_units<std::exa>() { return "Es"; }
template <> FMT_CONSTEXPR const char* get_units<std::ratio<60>>() {
return "m";
}
template <> FMT_CONSTEXPR const char* get_units<std::ratio<3600>>() {
return "h";
}
enum class numeric_system {
standard,
// Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
alternative
};
// Parses a put_time-like format string and invokes handler actions.
template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
const Char* end,
Handler&& handler) {
auto ptr = begin;
while (ptr != end) {
auto c = *ptr;
if (c == '}') break;
if (c != '%') {
++ptr;
continue;
}
if (begin != ptr) handler.on_text(begin, ptr);
++ptr; // consume '%'
if (ptr == end) FMT_THROW(format_error("invalid format"));
c = *ptr++;
switch (c) {
case '%':
handler.on_text(ptr - 1, ptr);
break;
case 'n': {
const char newline[] = "\n";
handler.on_text(newline, newline + 1);
break;
}
case 't': {
const char tab[] = "\t";
handler.on_text(tab, tab + 1);
break;
}
// Day of the week:
case 'a':
handler.on_abbr_weekday();
break;
case 'A':
handler.on_full_weekday();
break;
case 'w':
handler.on_dec0_weekday(numeric_system::standard);
break;
case 'u':
handler.on_dec1_weekday(numeric_system::standard);
break;
// Month:
case 'b':
handler.on_abbr_month();
break;
case 'B':
handler.on_full_month();
break;
// Hour, minute, second:
case 'H':
handler.on_24_hour(numeric_system::standard);
break;
case 'I':
handler.on_12_hour(numeric_system::standard);
break;
case 'M':
handler.on_minute(numeric_system::standard);
break;
case 'S':
handler.on_second(numeric_system::standard);
break;
// Other:
case 'c':
handler.on_datetime(numeric_system::standard);
break;
case 'x':
handler.on_loc_date(numeric_system::standard);
break;
case 'X':
handler.on_loc_time(numeric_system::standard);
break;
case 'D':
handler.on_us_date();
break;
case 'F':
handler.on_iso_date();
break;
case 'r':
handler.on_12_hour_time();
break;
case 'R':
handler.on_24_hour_time();
break;
case 'T':
handler.on_iso_time();
break;
case 'p':
handler.on_am_pm();
break;
case 'Q':
handler.on_duration_value();
break;
case 'q':
handler.on_duration_unit();
break;
case 'z':
handler.on_utc_offset();
break;
case 'Z':
handler.on_tz_name();
break;
// Alternative representation:
case 'E': {
if (ptr == end) FMT_THROW(format_error("invalid format"));
c = *ptr++;
switch (c) {
case 'c':
handler.on_datetime(numeric_system::alternative);
break;
case 'x':
handler.on_loc_date(numeric_system::alternative);
break;
case 'X':
handler.on_loc_time(numeric_system::alternative);
break;
default:
FMT_THROW(format_error("invalid format"));
}
break;
}
case 'O':
if (ptr == end) FMT_THROW(format_error("invalid format"));
c = *ptr++;
switch (c) {
case 'w':
handler.on_dec0_weekday(numeric_system::alternative);
break;
case 'u':
handler.on_dec1_weekday(numeric_system::alternative);
break;
case 'H':
handler.on_24_hour(numeric_system::alternative);
break;
case 'I':
handler.on_12_hour(numeric_system::alternative);
break;
case 'M':
handler.on_minute(numeric_system::alternative);
break;
case 'S':
handler.on_second(numeric_system::alternative);
break;
default:
FMT_THROW(format_error("invalid format"));
}
break;
default:
FMT_THROW(format_error("invalid format"));
}
begin = ptr;
}
if (begin != ptr) handler.on_text(begin, ptr);
return ptr;
}
struct chrono_format_checker {
FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); }
template <typename Char> void on_text(const Char*, const Char*) {}
FMT_NORETURN void on_abbr_weekday() { report_no_date(); }
FMT_NORETURN void on_full_weekday() { report_no_date(); }
FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); }
FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); }
FMT_NORETURN void on_abbr_month() { report_no_date(); }
FMT_NORETURN void on_full_month() { report_no_date(); }
void on_24_hour(numeric_system) {}
void on_12_hour(numeric_system) {}
void on_minute(numeric_system) {}
void on_second(numeric_system) {}
FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); }
FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); }
FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); }
FMT_NORETURN void on_us_date() { report_no_date(); }
FMT_NORETURN void on_iso_date() { report_no_date(); }
void on_12_hour_time() {}
void on_24_hour_time() {}
void on_iso_time() {}
void on_am_pm() {}
void on_duration_value() {}
void on_duration_unit() {}
FMT_NORETURN void on_utc_offset() { report_no_date(); }
FMT_NORETURN void on_tz_name() { report_no_date(); }
};
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline bool isnan(T) {
return false;
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline bool isnan(T value) {
return std::isnan(value);
}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline bool isfinite(T) {
return true;
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline bool isfinite(T value) {
return std::isfinite(value);
}
// Converts value to int and checks that it's in the range [0, upper).
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline int to_nonnegative_int(T value, int upper) {
FMT_ASSERT(value >= 0 && value <= upper, "invalid value");
(void)upper;
return static_cast<int>(value);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
inline int to_nonnegative_int(T value, int upper) {
FMT_ASSERT(
std::isnan(value) || (value >= 0 && value <= static_cast<T>(upper)),
"invalid value");
(void)upper;
return static_cast<int>(value);
}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline T mod(T x, int y) {
return x % y;
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline T mod(T x, int y) {
return std::fmod(x, static_cast<T>(y));
}
// If T is an integral type, maps T to its unsigned counterpart, otherwise
// leaves it unchanged (unlike std::make_unsigned).
template <typename T, bool INTEGRAL = std::is_integral<T>::value>
struct make_unsigned_or_unchanged {
using type = T;
};
template <typename T> struct make_unsigned_or_unchanged<T, true> {
using type = typename std::make_unsigned<T>::type;
};
#if FMT_SAFE_DURATION_CAST
// throwing version of safe_duration_cast
template <typename To, typename FromRep, typename FromPeriod>
To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
int ec;
To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
if (ec) FMT_THROW(format_error("cannot format duration"));
return to;
}
#endif
template <typename Rep, typename Period,
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
std::chrono::duration<Rep, Period> d) {
// this may overflow and/or the result may not fit in the
// target type.
#if FMT_SAFE_DURATION_CAST
using CommonSecondsType =
typename std::common_type<decltype(d), std::chrono::seconds>::type;
const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
const auto d_as_whole_seconds =
fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
// this conversion should be nonproblematic
const auto diff = d_as_common - d_as_whole_seconds;
const auto ms =
fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
return ms;
#else
auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
#endif
}
template <typename Rep, typename Period,
FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
std::chrono::duration<Rep, Period> d) {
using common_type = typename std::common_type<Rep, std::intmax_t>::type;
auto ms = mod(d.count() * static_cast<common_type>(Period::num) /
static_cast<common_type>(Period::den) * 1000,
1000);
return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
}
template <typename Rep, typename OutputIt>
OutputIt format_chrono_duration_value(OutputIt out, Rep val, int precision) {
if (precision >= 0) return format_to(out, "{:.{}f}", val, precision);
return format_to(out, std::is_floating_point<Rep>::value ? "{:g}" : "{}",
val);
}
template <typename Period, typename OutputIt>
static OutputIt format_chrono_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) return format_to(out, "{}", unit);
if (Period::den == 1) return format_to(out, "[{}]s", Period::num);
return format_to(out, "[{}/{}]s", Period::num, Period::den);
}
template <typename FormatContext, typename OutputIt, typename Rep,
typename Period>
struct chrono_formatter {
FormatContext& context;
OutputIt out;
int precision;
// rep is unsigned to avoid overflow.
using rep =
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
rep val;
using seconds = std::chrono::duration<rep>;
seconds s;
using milliseconds = std::chrono::duration<rep, std::milli>;
bool negative;
using char_type = typename FormatContext::char_type;
explicit chrono_formatter(FormatContext& ctx, OutputIt o,
std::chrono::duration<Rep, Period> d)
: context(ctx), out(o), val(d.count()), negative(false) {
if (d.count() < 0) {
val = 0 - val;
negative = true;
}
// this may overflow and/or the result may not fit in the
// target type.
#if FMT_SAFE_DURATION_CAST
// might need checked conversion (rep!=Rep)
auto tmpval = std::chrono::duration<rep, Period>(val);
s = fmt_safe_duration_cast<seconds>(tmpval);
#else
s = std::chrono::duration_cast<seconds>(
std::chrono::duration<rep, Period>(val));
#endif
}
// returns true if nan or inf, writes to out.
bool handle_nan_inf() {
if (isfinite(val)) {
return false;
}
if (isnan(val)) {
write_nan();
return true;
}
// must be +-inf
if (val > 0) {
write_pinf();
} else {
write_ninf();
}
return true;
}
Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
Rep hour12() const {
Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
return hour <= 0 ? 12 : hour;
}
Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
std::tm time() const {
auto time = std::tm();
time.tm_hour = to_nonnegative_int(hour(), 24);
time.tm_min = to_nonnegative_int(minute(), 60);
time.tm_sec = to_nonnegative_int(second(), 60);
return time;
}
void write_sign() {
if (negative) {
*out++ = '-';
negative = false;
}
}
void write(Rep value, int width) {
write_sign();
if (isnan(value)) return write_nan();
uint32_or_64_t<int> n = to_unsigned(
to_nonnegative_int(value, (std::numeric_limits<int>::max)()));
int num_digits = internal::count_digits(n);
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits);
}
void write_nan() { std::copy_n("nan", 3, out); }
void write_pinf() { std::copy_n("inf", 3, out); }
void write_ninf() { std::copy_n("-inf", 4, out); }
void format_localized(const tm& time, const char* format) {
if (isnan(val)) return write_nan();
auto locale = context.locale().template get<std::locale>();
auto& facet = std::use_facet<std::time_put<char_type>>(locale);
std::basic_ostringstream<char_type> os;
os.imbue(locale);
facet.put(os, os, ' ', &time, format, format + std::strlen(format));
auto str = os.str();
std::copy(str.begin(), str.end(), out);
}
void on_text(const char_type* begin, const char_type* end) {
std::copy(begin, end, out);
}
// These are not implemented because durations don't have date information.
void on_abbr_weekday() {}
void on_full_weekday() {}
void on_dec0_weekday(numeric_system) {}
void on_dec1_weekday(numeric_system) {}
void on_abbr_month() {}
void on_full_month() {}
void on_datetime(numeric_system) {}
void on_loc_date(numeric_system) {}
void on_loc_time(numeric_system) {}
void on_us_date() {}
void on_iso_date() {}
void on_utc_offset() {}
void on_tz_name() {}
void on_24_hour(numeric_system ns) {
if (handle_nan_inf()) return;
if (ns == numeric_system::standard) return write(hour(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour(), 24);
format_localized(time, "%OH");
}
void on_12_hour(numeric_system ns) {
if (handle_nan_inf()) return;
if (ns == numeric_system::standard) return write(hour12(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour12(), 12);
format_localized(time, "%OI");
}
void on_minute(numeric_system ns) {
if (handle_nan_inf()) return;
if (ns == numeric_system::standard) return write(minute(), 2);
auto time = tm();
time.tm_min = to_nonnegative_int(minute(), 60);
format_localized(time, "%OM");
}
void on_second(numeric_system ns) {
if (handle_nan_inf()) return;
if (ns == numeric_system::standard) {
write(second(), 2);
#if FMT_SAFE_DURATION_CAST
// convert rep->Rep
using duration_rep = std::chrono::duration<rep, Period>;
using duration_Rep = std::chrono::duration<Rep, Period>;
auto tmpval = fmt_safe_duration_cast<duration_Rep>(duration_rep{val});
#else
auto tmpval = std::chrono::duration<Rep, Period>(val);
#endif
auto ms = get_milliseconds(tmpval);
if (ms != std::chrono::milliseconds(0)) {
*out++ = '.';
write(ms.count(), 3);
}
return;
}
auto time = tm();
time.tm_sec = to_nonnegative_int(second(), 60);
format_localized(time, "%OS");
}
void on_12_hour_time() {
if (handle_nan_inf()) return;
format_localized(time(), "%r");
}
void on_24_hour_time() {
if (handle_nan_inf()) {
*out++ = ':';
handle_nan_inf();
return;
}
write(hour(), 2);
*out++ = ':';
write(minute(), 2);
}
void on_iso_time() {
on_24_hour_time();
*out++ = ':';
if (handle_nan_inf()) return;
write(second(), 2);
}
void on_am_pm() {
if (handle_nan_inf()) return;
format_localized(time(), "%p");
}
void on_duration_value() {
if (handle_nan_inf()) return;
write_sign();
out = format_chrono_duration_value(out, val, precision);
}
void on_duration_unit() { out = format_chrono_duration_unit<Period>(out); }
};
} // namespace internal
template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
private:
basic_format_specs<Char> specs;
int precision;
using arg_ref_type = internal::arg_ref<Char>;
arg_ref_type width_ref;
arg_ref_type precision_ref;
mutable basic_string_view<Char> format_str;
using duration = std::chrono::duration<Rep, Period>;
struct spec_handler {
formatter& f;
basic_parse_context<Char>& context;
basic_string_view<Char> format_str;
template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
context.check_arg_id(arg_id);
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
context.check_arg_id(arg_id);
const auto str_val = internal::string_view_metadata(format_str, arg_id);
return arg_ref_type(str_val);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
return arg_ref_type(context.next_arg_id());
}
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
void on_fill(Char fill) { f.specs.fill[0] = fill; }
void on_align(align_t align) { f.specs.align = align; }
void on_width(unsigned width) { f.specs.width = width; }
void on_precision(unsigned precision) { f.precision = precision; }
void end_precision() {}
template <typename Id> void on_dynamic_width(Id arg_id) {
f.width_ref = make_arg_ref(arg_id);
}
template <typename Id> void on_dynamic_precision(Id arg_id) {
f.precision_ref = make_arg_ref(arg_id);
}
};
using iterator = typename basic_parse_context<Char>::iterator;
struct parse_range {
iterator begin;
iterator end;
};
FMT_CONSTEXPR parse_range do_parse(basic_parse_context<Char>& ctx) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str};
begin = internal::parse_align(begin, end, handler);
if (begin == end) return {begin, begin};
begin = internal::parse_width(begin, end, handler);
if (begin == end) return {begin, begin};
if (*begin == '.') {
if (std::is_floating_point<Rep>::value)
begin = internal::parse_precision(begin, end, handler);
else
handler.on_error("precision not allowed for this argument type");
}
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
return {begin, end};
}
public:
formatter() : precision(-1) {}
FMT_CONSTEXPR auto parse(basic_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);
format_str = basic_string_view<Char>(
&*range.begin, internal::to_unsigned(range.end - range.begin));
return range.end;
}
template <typename FormatContext>
auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) {
auto begin = format_str.begin(), end = format_str.end();
// As a possible future optimization, we could avoid extra copying if width
// is not specified.
basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>;
internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(
specs.width, width_ref, ctx, format_str.begin());
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx, format_str.begin());
if (begin == end || *begin == '}') {
out = internal::format_chrono_duration_value(out, d.count(), precision);
internal::format_chrono_duration_unit<Period>(out);
} else {
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d);
f.precision = precision;
parse_chrono_format(begin, end, f);
}
w.write(buf.data(), buf.size(), specs);
return w.out();
}
};
FMT_END_NAMESPACE
#endif // FMT_CHRONO_H_

@ -0,0 +1,585 @@
// Formatting library for C++ - color support
//
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COLOR_H_
#define FMT_COLOR_H_
#include "format.h"
FMT_BEGIN_NAMESPACE
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color
enum class terminal_color : uint8_t {
black = 30,
red,
green,
yellow,
blue,
magenta,
cyan,
white,
bright_black = 90,
bright_red,
bright_green,
bright_yellow,
bright_blue,
bright_magenta,
bright_cyan,
bright_white
};
enum class emphasis : uint8_t {
bold = 1,
italic = 1 << 1,
underline = 1 << 2,
strikethrough = 1 << 3
};
// rgb is a struct for red, green and blue colors.
// Using the name "rgb" makes some editors show the color in a tooltip.
struct rgb {
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
FMT_CONSTEXPR rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
FMT_CONSTEXPR rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF),
g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {}
uint8_t r;
uint8_t g;
uint8_t b;
};
namespace internal {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color);
}
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
}
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
value{} {
value.term_color = static_cast<uint8_t>(term_color);
}
bool is_rgb;
union color_union {
uint8_t term_color;
uint32_t rgb_color;
} value;
};
} // namespace internal
// Experimental text formatting support.
class text_style {
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
const text_style& rhs) {
return lhs |= rhs;
}
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
const text_style& rhs) {
return lhs &= rhs;
}
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
return set_foreground_color;
}
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
return set_background_color;
}
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
assert(has_foreground() && "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
assert(has_background() && "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
assert(has_emphasis() && "no emphasis specified for this style");
return ems;
}
private:
FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems() {
if (is_foreground) {
foreground_color = text_color;
set_foreground_color = true;
} else {
background_color = text_color;
set_background_color = true;
}
}
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
FMT_NOEXCEPT;
internal::color_type foreground_color;
internal::color_type background_color;
bool set_foreground_color;
bool set_background_color;
emphasis ems;
};
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground);
}
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background);
}
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs;
}
namespace internal {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
const char* esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code
// sequence.
if (!text_color.is_rgb) {
bool is_background = esc == internal::data::background_color;
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background) value += 10u;
std::size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
if (value >= 100u) {
buffer[index++] = static_cast<Char>('1');
value %= 100u;
}
buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
return;
}
for (int i = 0; i < 7; i++) {
buffer[i] = static_cast<Char>(esc[i]);
}
rgb color(text_color.value.rgb_color);
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
}
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
uint8_t em_codes[4] = {};
uint8_t em_bits = static_cast<uint8_t>(em);
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9;
std::size_t index = 0;
for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
}
buffer[index++] = static_cast<Char>(0);
}
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
return buffer + std::strlen(buffer);
}
private:
Char buffer[7u + 3u * 4u + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT {
out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10);
out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter);
}
};
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
internal::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
internal::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::background_color);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
return ansi_color_escape<Char>(em);
}
template <typename Char>
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
std::fputs(chars, stream);
}
template <>
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
std::fputws(chars, stream);
}
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::reset_color, stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::wreset_color, stream);
}
template <typename Char>
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
const char* begin = data::reset_color;
const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end);
}
template <typename Char>
std::basic_string<Char> vformat(const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char> > args) {
basic_memory_buffer<Char> buffer;
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
ansi_color_escape<Char> escape = make_emphasis<Char>(ts.get_emphasis());
buffer.append(escape.begin(), escape.end());
}
if (ts.has_foreground()) {
has_style = true;
ansi_color_escape<Char> escape =
make_foreground_color<Char>(ts.get_foreground());
buffer.append(escape.begin(), escape.end());
}
if (ts.has_background()) {
has_style = true;
ansi_color_escape<Char> escape =
make_background_color<Char>(ts.get_background());
buffer.append(escape.begin(), escape.end());
}
internal::vformat_to(buffer, format_str, args);
if (has_style) {
reset_color<Char>(buffer);
}
return fmt::to_string(buffer);
}
} // namespace internal
template <typename S, typename Char = char_t<S> >
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char> > args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
internal::fputs<Char>(internal::make_emphasis<Char>(ts.get_emphasis()), f);
}
if (ts.has_foreground()) {
has_style = true;
internal::fputs<Char>(
internal::make_foreground_color<Char>(ts.get_foreground()), f);
}
if (ts.has_background()) {
has_style = true;
internal::fputs<Char>(
internal::make_background_color<Char>(ts.get_background()), f);
}
vprint(f, format, args);
if (has_style) {
internal::reset_color<Char>(f);
}
}
/**
Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting.
Example:
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S> >;
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
}
/**
Formats a string and prints it to stdout using ANSI escape sequences to
specify text formatting.
Example:
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
template <typename S, typename Char = char_t<S> >
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char> > args) {
return internal::vformat(ts, to_string_view(format_str), args);
}
/**
\rst
Formats arguments and returns the result as a string using ANSI
escape sequences to specify text formatting.
**Example**::
#include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S> >
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return internal::vformat(ts, to_string_view(format_str),
{internal::make_args_checked(format_str, args...)});
}
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_

@ -0,0 +1,466 @@
// Formatting library for C++ - experimental format string compilation
//
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#include <vector>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Char> struct format_part {
public:
struct named_argument_id {
FMT_CONSTEXPR named_argument_id(internal::string_view_metadata id)
: id(id) {}
internal::string_view_metadata id;
};
struct argument_id {
FMT_CONSTEXPR argument_id() : argument_id(0u) {}
FMT_CONSTEXPR argument_id(unsigned id)
: which(which_arg_id::index), val(id) {}
FMT_CONSTEXPR argument_id(internal::string_view_metadata id)
: which(which_arg_id::named_index), val(id) {}
enum class which_arg_id { index, named_index };
which_arg_id which;
union value {
FMT_CONSTEXPR value() : index(0u) {}
FMT_CONSTEXPR value(unsigned id) : index(id) {}
FMT_CONSTEXPR value(internal::string_view_metadata id)
: named_index(id) {}
unsigned index;
internal::string_view_metadata named_index;
} val;
};
struct specification {
FMT_CONSTEXPR specification() : arg_id(0u) {}
FMT_CONSTEXPR specification(unsigned id) : arg_id(id) {}
FMT_CONSTEXPR specification(internal::string_view_metadata id)
: arg_id(id) {}
argument_id arg_id;
internal::dynamic_format_specs<Char> parsed_specs;
};
FMT_CONSTEXPR format_part()
: which(kind::argument_id), end_of_argument_id(0u), val(0u) {}
FMT_CONSTEXPR format_part(internal::string_view_metadata text)
: which(kind::text), end_of_argument_id(0u), val(text) {}
FMT_CONSTEXPR format_part(unsigned id)
: which(kind::argument_id), end_of_argument_id(0u), val(id) {}
FMT_CONSTEXPR format_part(named_argument_id arg_id)
: which(kind::named_argument_id), end_of_argument_id(0u), val(arg_id) {}
FMT_CONSTEXPR format_part(specification spec)
: which(kind::specification), end_of_argument_id(0u), val(spec) {}
enum class kind { argument_id, named_argument_id, text, specification };
kind which;
std::size_t end_of_argument_id;
union value {
FMT_CONSTEXPR value() : arg_id(0u) {}
FMT_CONSTEXPR value(unsigned id) : arg_id(id) {}
FMT_CONSTEXPR value(named_argument_id named_id)
: named_arg_id(named_id.id) {}
FMT_CONSTEXPR value(internal::string_view_metadata t) : text(t) {}
FMT_CONSTEXPR value(specification s) : spec(s) {}
unsigned arg_id;
internal::string_view_metadata named_arg_id;
internal::string_view_metadata text;
specification spec;
} val;
};
template <typename Char, typename PartsContainer>
class format_preparation_handler : public internal::error_handler {
private:
using part = format_part<Char>;
public:
using iterator = typename basic_string_view<Char>::iterator;
FMT_CONSTEXPR format_preparation_handler(basic_string_view<Char> format,
PartsContainer& parts)
: parts_(parts), format_(format), parse_context_(format) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin == end) return;
const auto offset = begin - format_.data();
const auto size = end - begin;
parts_.push_back(part(string_view_metadata(offset, size)));
}
FMT_CONSTEXPR void on_arg_id() {
parts_.push_back(part(parse_context_.next_arg_id()));
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
parse_context_.check_arg_id(id);
parts_.push_back(part(id));
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
const auto view = string_view_metadata(format_, id);
const auto arg_id = typename part::named_argument_id(view);
parts_.push_back(part(arg_id));
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
parts_.back().end_of_argument_id = ptr - format_.begin();
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
const auto specs_offset = to_unsigned(begin - format_.begin());
using parse_context = basic_parse_context<Char>;
internal::dynamic_format_specs<Char> parsed_specs;
dynamic_specs_handler<parse_context> handler(parsed_specs, parse_context_);
begin = parse_format_specs(begin, end, handler);
if (*begin != '}') on_error("missing '}' in format string");
auto& last_part = parts_.back();
auto specs = last_part.which == part::kind::argument_id
? typename part::specification(last_part.val.arg_id)
: typename part::specification(last_part.val.named_arg_id);
specs.parsed_specs = parsed_specs;
last_part = part(specs);
last_part.end_of_argument_id = specs_offset;
return begin;
}
private:
PartsContainer& parts_;
basic_string_view<Char> format_;
basic_parse_context<Char> parse_context_;
};
template <typename Format, typename PreparedPartsProvider, typename... Args>
class prepared_format {
public:
using char_type = char_t<Format>;
using format_part_t = format_part<char_type>;
constexpr prepared_format(Format f)
: format_(std::move(f)), parts_provider_(to_string_view(format_)) {}
prepared_format() = delete;
using context = buffer_context<char_type>;
template <typename Range, typename Context>
auto vformat_to(Range out, basic_format_args<Context> args) const ->
typename Context::iterator {
const auto format_view = internal::to_string_view(format_);
basic_parse_context<char_type> parse_ctx(format_view);
Context ctx(out.begin(), args);
const auto& parts = parts_provider_.parts();
for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
switch (part.which) {
case format_part_t::kind::text: {
const auto text = value.text.to_view(format_view.data());
auto output = ctx.out();
auto&& it = internal::reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
} break;
case format_part_t::kind::argument_id: {
advance_parse_context_to_specification(parse_ctx, part);
format_arg<Range>(parse_ctx, ctx, value.arg_id);
} break;
case format_part_t::kind::named_argument_id: {
advance_parse_context_to_specification(parse_ctx, part);
const auto named_arg_id =
value.named_arg_id.to_view(format_view.data());
format_arg<Range>(parse_ctx, ctx, named_arg_id);
} break;
case format_part_t::kind::specification: {
const auto& arg_id_value = value.spec.arg_id.val;
const auto arg = value.spec.arg_id.which ==
format_part_t::argument_id::which_arg_id::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.named_index.to_view(
to_string_view(format_).data()));
auto specs = value.spec.parsed_specs;
handle_dynamic_spec<internal::width_checker>(
specs.width, specs.width_ref, ctx, format_view.begin());
handle_dynamic_spec<internal::precision_checker>(
specs.precision, specs.precision_ref, ctx, format_view.begin());
check_prepared_specs(specs, arg.type());
advance_parse_context_to_specification(parse_ctx, part);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
} break;
}
}
return ctx.out();
}
private:
void advance_parse_context_to_specification(
basic_parse_context<char_type>& parse_ctx,
const format_part_t& part) const {
const auto view = to_string_view(format_);
const auto specification_begin = view.data() + part.end_of_argument_id;
advance_to(parse_ctx, specification_begin);
}
template <typename Range, typename Context, typename Id>
void format_arg(basic_parse_context<char_type>& parse_ctx, Context& ctx,
Id arg_id) const {
parse_ctx.check_arg_id(arg_id);
const auto stopped_at =
visit_format_arg(arg_formatter<Range>(ctx), ctx.arg(arg_id));
ctx.advance_to(stopped_at);
}
template <typename Char>
void check_prepared_specs(const basic_format_specs<Char>& specs,
internal::type arg_type) const {
internal::error_handler h;
numeric_specs_checker<internal::error_handler> checker(h, arg_type);
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
}
private:
Format format_;
PreparedPartsProvider parts_provider_;
};
template <typename Char> struct part_counter {
unsigned num_parts = 0;
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end) ++num_parts;
}
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(unsigned) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned braces_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++braces_counter;
} else if (*begin == '}') {
if (braces_counter == 0u) break;
--braces_counter;
}
}
return begin;
}
FMT_CONSTEXPR void on_error(const char*) {}
};
template <typename Format> class compiletime_prepared_parts_type_provider {
private:
using char_type = char_t<Format>;
static FMT_CONSTEXPR unsigned count_parts() {
FMT_CONSTEXPR_DECL const auto text = to_string_view(Format{});
part_counter<char_type> counter;
internal::parse_format_string</*IS_CONSTEXPR=*/true>(text, counter);
return counter.num_parts;
}
// Workaround for old compilers. Compiletime parts preparation will not be
// performed with them anyway.
#if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned number_of_format_parts =
compiletime_prepared_parts_type_provider::count_parts();
#else
static const unsigned number_of_format_parts = 0u;
#endif
public:
template <unsigned N> struct format_parts_array {
using value_type = format_part<char_type>;
FMT_CONSTEXPR format_parts_array() : arr{} {}
FMT_CONSTEXPR value_type& operator[](unsigned ind) { return arr[ind]; }
FMT_CONSTEXPR const value_type* begin() const { return arr; }
FMT_CONSTEXPR const value_type* end() const { return begin() + N; }
private:
value_type arr[N];
};
struct empty {
// Parts preparator will search for it
using value_type = format_part<char_type>;
};
using type = conditional_t<number_of_format_parts != 0,
format_parts_array<number_of_format_parts>, empty>;
};
template <typename Parts> class compiletime_prepared_parts_collector {
private:
using format_part = typename Parts::value_type;
public:
FMT_CONSTEXPR explicit compiletime_prepared_parts_collector(Parts& parts)
: parts_{parts}, counter_{0u} {}
FMT_CONSTEXPR void push_back(format_part part) { parts_[counter_++] = part; }
FMT_CONSTEXPR format_part& back() { return parts_[counter_ - 1]; }
private:
Parts& parts_;
unsigned counter_;
};
template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer prepare_parts(basic_string_view<Char> format) {
PartsContainer parts;
internal::parse_format_string</*IS_CONSTEXPR=*/false>(
format, format_preparation_handler<Char, PartsContainer>(format, parts));
return parts;
}
template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer
prepare_compiletime_parts(basic_string_view<Char> format) {
using collector = compiletime_prepared_parts_collector<PartsContainer>;
PartsContainer parts;
collector c(parts);
internal::parse_format_string</*IS_CONSTEXPR=*/true>(
format, format_preparation_handler<Char, collector>(format, c));
return parts;
}
template <typename PartsContainer> class runtime_parts_provider {
public:
runtime_parts_provider() = delete;
template <typename Char>
runtime_parts_provider(basic_string_view<Char> format)
: parts_(prepare_parts<PartsContainer>(format)) {}
const PartsContainer& parts() const { return parts_; }
private:
PartsContainer parts_;
};
template <typename Format, typename PartsContainer>
struct compiletime_parts_provider {
compiletime_parts_provider() = delete;
template <typename Char>
FMT_CONSTEXPR compiletime_parts_provider(basic_string_view<Char>) {}
const PartsContainer& parts() const {
static FMT_CONSTEXPR_DECL const PartsContainer prepared_parts =
prepare_compiletime_parts<PartsContainer>(
internal::to_string_view(Format{}));
return prepared_parts;
}
};
} // namespace internal
#if FMT_USE_CONSTEXPR
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
FMT_CONSTEXPR auto compile(S format_str) -> internal::prepared_format<
S,
internal::compiletime_parts_provider<
S,
typename internal::compiletime_prepared_parts_type_provider<S>::type>,
Args...> {
return format_str;
}
#endif
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N]) -> internal::prepared_format<
std::basic_string<Char>,
internal::runtime_parts_provider<std::vector<internal::format_part<Char>>>,
Args...> {
return std::basic_string<Char>(format_str, N - 1);
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = internal::buffer_range<Char>;
using context = buffer_context<Char>;
cf.template vformat_to<range, context>(range(buffer),
{make_format_args<context>(args...)});
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>;
return cf.template vformat_to<range, context>(
range(out), {make_format_args<context>(args...)});
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const CompiledFormat& cf,
const Args&... args) {
auto it =
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()};
}
template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return fmt::format_to(
internal::counting_iterator<typename CompiledFormat::char_type>(),
cf, args...)
.count();
}
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,57 @@
// Formatting library for C++
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "format-inl.h"
FMT_BEGIN_NAMESPACE
template struct FMT_API internal::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of grisu_format.
bool (*instantiate_grisu_format)(double, internal::buffer<char>&, int, unsigned,
int&) = internal::grisu_format;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char.
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char*, const char*);
template FMT_API void internal::arg_map<format_context>::init(
const basic_format_args<format_context>& args);
template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer<char>&, string_view, basic_format_args<format_context>);
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);
// Explicit instantiations for wchar_t.
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template FMT_API void internal::arg_map<wformat_context>::init(
const basic_format_args<wformat_context>&);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE

File diff suppressed because it is too large Load Diff

@ -0,0 +1,77 @@
// Formatting library for C++ - std::locale support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_
#include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Char>
typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
internal::locale_ref(loc));
}
template <typename Char>
std::basic_string<Char> vformat(const std::locale& loc,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer);
}
} // namespace internal
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<Char>> args) {
return internal::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return internal::vformat(
loc, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
}
template <typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t<
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
inline OutputIt vformat_to(OutputIt out, const std::locale& loc,
const S& format_str,
format_args_t<OutputIt, Char> args) {
using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) {
internal::check_format_string<Args...>(format_str);
using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
}
FMT_END_NAMESPACE
#endif // FMT_LOCALE_H_

@ -0,0 +1,136 @@
// Formatting library for C++ - std::ostream support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private:
using int_type = typename std::basic_streambuf<Char>::int_type;
using traits_type = typename std::basic_streambuf<Char>::traits_type;
buffer<Char>& buffer_;
public:
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
protected:
// The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call
// to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
};
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
struct null;
// Hide all operator<< from std::basic_ostream<Char>.
void operator<<(null);
};
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
private:
template <typename U>
static decltype((void)(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
std::true_type())
test(int);
template <typename> static std::false_type test(...);
using result = decltype(test<T>(0));
public:
static const bool value = result::value;
};
// Write the content of buf to os.
template <typename Char>
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size =
to_unsigned((std::numeric_limits<std::streamsize>::max)());
do {
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
buf_data += n;
size -= n;
} while (size != 0);
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
buf.resize(buf.size());
}
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: formatter<basic_string_view<Char>, Char> {
template <typename Context>
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buffer;
format_value(buffer, value);
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
};
} // namespace internal
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer);
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
}
FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_

@ -0,0 +1,233 @@
// A C++ interface to POSIX functions.
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
// Disable bogus MSVC warnings.
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "posix.h"
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifndef _WIN32
# include <unistd.h>
#else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
# include <windows.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
#endif // _WIN32
#ifdef fileno
# undef fileno
#endif
namespace {
#ifdef _WIN32
// Return type of read and write functions.
typedef int RWResult;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned convert_rwcount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#else
// Return type of read and write functions.
typedef ssize_t RWResult;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
} // namespace
FMT_BEGIN_NAMESPACE
buffered_file::~buffered_file() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
report_system_error(errno, "cannot close file");
}
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
nullptr);
if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
}
void buffered_file::close() {
if (!file_) return;
int result = FMT_SYSTEM(fclose(file_));
file_ = nullptr;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
#define FMT_ARGS
int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd;
}
file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
#else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
#endif
if (fd_ == -1)
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
}
file::~file() FMT_NOEXCEPT {
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
report_system_error(errno, "cannot close file");
}
void file::close() {
if (fd_ == -1) return;
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
}
long long file::size() const {
#ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds.
// Both functions support large file sizes.
DWORD size_upper = 0;
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
}
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
#else
typedef struct stat Stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes"));
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of file::size is not large enough");
return file_stat.st_size;
#endif
}
std::size_t file::read(void* buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
return internal::to_unsigned(result);
}
std::size_t file::write(const void* buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
return internal::to_unsigned(result);
}
file file::dup(int fd) {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
return file(new_fd);
}
void file::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
fd_, fd));
}
}
void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) ec = error_code(errno);
}
void file::pipe(file& read_end, file& write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
write_end.close();
int fds[2] = {};
#ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
#else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
#endif
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = file(fds[0]);
write_end = file(fds[1]);
}
buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR.
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f)
FMT_THROW(
system_error(errno, "cannot associate stream with file descriptor"));
buffered_file bf(f);
fd_ = -1;
return bf;
}
long getpagesize() {
#ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
#else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
return size;
#endif
}
FMT_END_NAMESPACE

@ -0,0 +1,311 @@
// A C++ interface to POSIX functions.
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_POSIX_H_
#define FMT_POSIX_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
result = (expression); \
} while (result == error_result && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
public:
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
private:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...));
}
};
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
private:
file(const file&) = delete;
void operator=(const file&) = delete;
public:
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void* buffer, std::size_t count);
// 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);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char* mode);
};
// Returns the memory page size.
long getpagesize();
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char* locale, locale_t) {
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) { _free_locale(locale); }
static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return _strtod_l(nptr, endptr, locale);
}
# endif
locale_t locale_;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
public:
using type = locale_t;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~Locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_POSIX_H_

@ -0,0 +1,715 @@
// Formatting library for C++
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits
#include "ostream.h"
FMT_BEGIN_NAMESPACE
namespace internal {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template <typename T> inline T const_check(T value) { return value; }
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) {
unsigned max = std::numeric_limits<int>::max();
return value <= max;
}
static bool fits_in_int(bool) { return true; }
};
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() &&
value <= std::numeric_limits<int>::max();
}
static bool fits_in_int(int) { return true; }
};
class printf_precision_handler {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big"));
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
int operator()(T) {
FMT_THROW(format_error("precision is not integer"));
return 0;
}
};
// An argument visitor that returns true iff arg is a zero integer.
class is_zero_int {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
bool operator()(T value) {
return value == 0;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
bool operator()(T) {
return false;
}
};
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
template <typename T, typename Context> class arg_converter {
private:
using char_type = typename Context::char_type;
basic_format_arg<Context>& arg_;
char_type type_;
public:
arg_converter(basic_format_arg<Context>& arg, char_type type)
: arg_(arg), type_(type) {}
void operator()(bool value) {
if (type_ != 's') operator()<bool>(value);
}
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
void operator()(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_ = internal::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value)));
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = internal::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value)));
}
} else {
if (is_signed) {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
} else {
arg_ = internal::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value));
}
}
}
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
void operator()(U) {} // No conversion needed for non-integral types.
};
// Converts an integer argument to T for printf, if T is an integral type.
// If T is void, the argument is converted to corresponding signed or unsigned
// type depending on the type specifier: 'd' and 'i' - signed, other -
// unsigned).
template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) {
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
}
// Converts an integer argument to char for printf.
template <typename Context> class char_converter {
private:
basic_format_arg<Context>& arg_;
public:
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
arg_ = internal::make_arg<Context>(
static_cast<typename Context::char_type>(value));
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
void operator()(T) {} // No conversion needed for non-integral types.
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
private:
using format_specs = basic_format_specs<Char>;
format_specs& specs_;
public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_t<T>>(value);
if (internal::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
unsigned int_max = std::numeric_limits<int>::max();
if (width > int_max) FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) {
FMT_THROW(format_error("width is not integer"));
return 0;
}
};
template <typename Char, typename Context>
void printf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format();
}
template <typename OutputIt, typename Char, typename Context>
internal::truncating_iterator<OutputIt> printf(
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
basic_format_args<Context> args) {
return Context(it, format, args).format();
}
} // namespace internal
using internal::printf; // For printing into memory_buffer.
template <typename Range> class printf_arg_formatter;
template <typename OutputIt, typename Char> class basic_printf_context;
/**
\rst
The ``printf`` argument formatter.
\endrst
*/
template <typename Range>
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
public:
using iterator = typename Range::iterator;
private:
using char_type = typename Range::value_type;
using base = internal::arg_formatter_base<Range>;
using context_type = basic_printf_context<iterator, char_type>;
context_type& context_;
void write_null_pointer(char) {
this->specs()->type = 0;
this->write("(nil)");
}
void write_null_pointer(wchar_t) {
this->specs()->type = 0;
this->write(L"(nil)");
}
public:
using format_specs = typename base::format_specs;
/**
\rst
Constructs an argument formatter object.
*buffer* is a reference to the output buffer and *specs* contains format
specifier information for standard argument types.
\endrst
*/
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
if (std::is_same<T, bool>::value) {
format_specs& fmt_specs = *this->specs();
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
fmt_specs.type = 0;
this->write(value != 0);
} else if (std::is_same<T, char_type>::value) {
format_specs& fmt_specs = *this->specs();
if (fmt_specs.type && fmt_specs.type != 'c')
return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.align = align::right;
return base::operator()(value);
} else {
return base::operator()(value);
}
return this->out();
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) {
return base::operator()(value);
}
/** Formats a null-terminated C string. */
iterator operator()(const char* value) {
if (value)
base::operator()(value);
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write("(null)");
return this->out();
}
/** Formats a null-terminated wide C string. */
iterator operator()(const wchar_t* value) {
if (value)
base::operator()(value);
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write(L"(null)");
return this->out();
}
iterator operator()(basic_string_view<char_type> value) {
return base::operator()(value);
}
iterator operator()(monostate value) { return base::operator()(value); }
/** Formats a pointer. */
iterator operator()(const void* value) {
if (value) return base::operator()(value);
this->specs()->type = 0;
write_null_pointer(char_type());
return this->out();
}
/** Formats an argument of a custom (user-defined) type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
handle.format(context_.parse_context(), context_);
return this->out();
}
};
template <typename T> struct printf_formatter {
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
internal::format_value(internal::get_container(ctx.out()), value);
return ctx.out();
}
};
/** This template formats data and writes the output to a writer. */
template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
using char_type = Char;
using format_arg = basic_format_arg<basic_printf_context>;
template <typename T> using formatter_type = printf_formatter<T>;
private:
using format_specs = basic_format_specs<char_type>;
OutputIt out_;
basic_format_args<basic_printf_context> args_;
basic_parse_context<Char> parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
format_arg get_arg(unsigned arg_index = std::numeric_limits<unsigned>::max());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char*& it, const Char* end, format_specs& specs);
public:
/**
\rst
Constructs a ``printf_context`` object. References to the arguments and
the writer are stored in the context object so make sure they have
appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args), parse_ctx_(format_str) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
format_arg arg(unsigned id) const { return args_.get(id); }
basic_parse_context<Char>& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter =
printf_arg_formatter<internal::buffer_range<Char>>>
OutputIt format();
};
template <typename OutputIt, typename Char>
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
const Char*& it,
const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-':
specs.align = align::left;
break;
case '+':
specs.sign = sign::plus;
break;
case '0':
specs.fill[0] = '0';
break;
case ' ':
specs.sign = sign::space;
break;
case '#':
specs.alt = true;
break;
default:
return;
}
}
}
template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
if (arg_index == std::numeric_limits<unsigned>::max())
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
return internal::get_arg(*this, arg_index);
}
template <typename OutputIt, typename Char>
unsigned basic_printf_context<OutputIt, Char>::parse_header(
const Char*& it, const Char* end, format_specs& specs) {
unsigned arg_index = std::numeric_limits<unsigned>::max();
char_type c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
internal::error_handler eh;
unsigned value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value;
} else {
if (c == '0') specs.fill[0] = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
specs.width = value;
return arg_index;
}
}
}
parse_flags(specs, it, end);
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
internal::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') {
++it;
specs.width = visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg());
}
}
return arg_index;
}
template <typename OutputIt, typename Char>
template <typename ArgFormatter>
OutputIt basic_printf_context<OutputIt, Char>::format() {
auto out = this->out();
const Char* start = parse_ctx_.begin();
const Char* end = parse_ctx_.end();
auto it = start;
while (it != end) {
char_type c = *it++;
if (c != '%') continue;
if (it != end && *it == c) {
out = std::copy(start, it, out);
start = ++it;
continue;
}
out = std::copy(start, it - 1, out);
format_specs specs;
specs.align = align::right;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(it, end, specs);
// Parse precision.
if (it != end && *it == '.') {
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
internal::error_handler eh;
specs.precision = static_cast<int>(parse_nonnegative_int(it, end, eh));
} else if (c == '*') {
++it;
specs.precision =
visit_format_arg(internal::printf_precision_handler(), get_arg());
} else {
specs.precision = 0;
}
}
format_arg arg = get_arg(arg_index);
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
specs.alt = false;
if (specs.fill[0] == '0') {
if (arg.is_arithmetic())
specs.align = align::numeric;
else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0;
using internal::convert_arg;
switch (c) {
case 'h':
if (t == 'h') {
++it;
t = it != end ? *it : 0;
convert_arg<signed char>(arg, t);
} else {
convert_arg<short>(arg, t);
}
break;
case 'l':
if (t == 'l') {
++it;
t = it != end ? *it : 0;
convert_arg<long long>(arg, t);
} else {
convert_arg<long>(arg, t);
}
break;
case 'j':
convert_arg<intmax_t>(arg, t);
break;
case 'z':
convert_arg<std::size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, t);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--it;
convert_arg<void>(arg, c);
}
// Parse type.
if (it == end) FMT_THROW(format_error("invalid format string"));
specs.type = static_cast<char>(*it++);
if (arg.is_integral()) {
// Normalize type.
switch (specs.type) {
case 'i':
case 'u':
specs.type = 'd';
break;
case 'c':
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
arg);
break;
}
}
start = it;
// Format argument.
visit_format_arg(ArgFormatter(out, specs, *this), arg);
}
return std::copy(start, it, out);
}
template <typename Char>
using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>;
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst
*/
template <typename... Args>
inline format_arg_store<printf_context, Args...> make_printf_args(
const Args&... args) {
return {args...};
}
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst
*/
template <typename... Args>
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
const Args&... args) {
return {args...};
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf(
const S& format, basic_format_args<basic_printf_context_t<Char>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
return to_string(buffer);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), {make_format_args<context>(args...)});
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
}
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format),
{make_format_args<context>(args...)});
}
template <typename S, typename Char = char_t<S>>
inline int vprintf(const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
return vfprintf(stdout, to_string_view(format), args);
}
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
inline int printf(const S& format_str, const Args&... args) {
using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str),
{make_format_args<context>(args...)});
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
internal::write(os, buffer);
return static_cast<int>(buffer.size());
}
/** Formats arguments and writes the output to the range. */
template <typename ArgFormatter, typename Char,
typename Context =
basic_printf_context<typename ArgFormatter::iterator, Char>>
typename ArgFormatter::iterator vprintf(internal::buffer<Char>& out,
basic_string_view<Char> format_str,
basic_format_args<Context> args) {
typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>();
return iter;
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str),
{make_format_args<context>(args...)});
}
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_

@ -0,0 +1,288 @@
// Formatting library for C++ - experimental range support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
//
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
// All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface.
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include <type_traits>
#include "format.h"
// output only up to N items from the range.
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif
FMT_BEGIN_NAMESPACE
template <typename Char> struct formatting_base {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix;
Char delimiter;
Char postfix;
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
template <typename Char, typename Enable = void>
struct formatting_tuple : formatting_base<Char> {
Char prefix;
Char delimiter;
Char postfix;
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
namespace internal {
template <typename RangeT, typename OutputIterator>
OutputIterator copy(const RangeT& range, OutputIterator out) {
for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it;
return out;
}
template <typename OutputIterator>
OutputIterator copy(const char* str, OutputIterator out) {
while (*str) *out++ = *str++;
return out;
}
template <typename OutputIterator>
OutputIterator copy(char ch, OutputIterator out) {
*out++ = ch;
return out;
}
/// Return true value if T has std::string interface, like std::string_view.
template <typename T> class is_like_std_string {
template <typename U>
static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Char>
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
template <typename T>
struct is_range_<
T, conditional_t<false,
conditional_helper<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>,
void>> : std::true_type {};
#endif
/// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
template <typename U>
static auto check(U* p)
-> decltype(std::tuple_size<U>::value,
(void)std::declval<typename std::tuple_element<0, U>::type>(),
int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
};
template <std::size_t... N>
using index_sequence = integer_sequence<std::size_t, N...>;
template <typename T, std::size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
#endif
template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
using std::get;
// using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
(void)_; // blocks warnings
}
template <class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
T const&) {
return {};
}
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
return add_space ? " {}" : "{}";
}
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
return add_space ? " \"{}\"" : "\"{}\"";
}
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
return add_space ? " \"{}\"" : "\"{}\"";
}
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
return add_space ? L" \"{}\"" : L"\"{}\"";
}
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
return add_space ? " '{}'" : "'{}'";
}
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace internal
template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
};
template <typename TupleT, typename Char>
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
private:
// C++11 generic lambda for format()
template <typename FormatContext> struct format_each {
template <typename T> void operator()(const T& v) {
if (i > 0) {
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
out = internal::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i;
}
formatting_tuple<Char>& formatting;
std::size_t& i;
typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
};
public:
formatting_tuple<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
return ctx.out();
}
};
template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value &&
!internal::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value;
};
template <typename RangeT, typename Char>
struct formatter<RangeT, Char,
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
formatting_range<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext>
typename FormatContext::iterator format(const RangeT& values,
FormatContext& ctx) {
auto out = internal::copy(formatting.prefix, ctx.out());
std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
if (i > 0) {
if (formatting.add_prepostfix_space) *out++ = ' ';
out = internal::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
out = format_to(out, " ... <other elements>");
break;
}
}
if (formatting.add_prepostfix_space) *out++ = ' ';
return internal::copy(formatting.postfix, out);
}
};
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_

@ -0,0 +1,293 @@
/*
* For conversion between std::chrono::durations without undefined
* behaviour or erroneous results.
* This is a stripped down version of duration_cast, for inclusion in fmt.
* See https://github.com/pauldreik/safe_duration_cast
*
* Copyright Paul Dreik 2019
*
* This file is licensed under the fmt license, see format.h
*/
#include <chrono>
#include <cmath>
#include <limits>
#include <type_traits>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace safe_duration_cast {
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
// outside range.
ec = 1;
return {};
}
}
return static_cast<To>(from);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed !=
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (from < 0) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
ec = 1;
return {};
}
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
// outside range.
ec = 1;
return {};
}
}
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
return from;
} // function
// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
* input | output
* ---------------------------------|---------------
* NaN | NaN
* Inf | Inf
* normal, fits in output | converted (possibly lossy)
* normal, does not fit in output | ec is set
* subnormal | best effort
* -Inf | -Inf
*/
// clang-format on
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
using T = std::numeric_limits<To>;
static_assert(std::is_floating_point<From>::value, "From must be floating");
static_assert(std::is_floating_point<To>::value, "To must be floating");
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
return static_cast<To>(from);
}
// not within range.
ec = 1;
return {};
}
// nan and inf will be preserved
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
static_assert(std::is_floating_point<From>::value, "From must be floating");
return from;
}
/**
* safe duration cast between integral durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_integral<FromRep>::value),
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
using Factor = std::ratio_divide<typename From::period, typename To::period>;
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 =
std::numeric_limits<IntermediateRep>::max() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 =
std::numeric_limits<IntermediateRep>::min() / Factor::num;
if (count < min1) {
ec = 1;
return {};
}
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
/**
* safe duration_cast between floating point durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
using Factor = std::ratio_divide<typename From::period, typename To::period>;
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// force conversion of From::rep -> IntermediateRep to be safe,
// even if it will never happen be narrowing in this context.
IntermediateRep count =
safe_float_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = std::numeric_limits<IntermediateRep>::max() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
static_cast<IntermediateRep>(Factor::num);
if (count < min1) {
ec = 1;
return {};
}
count *= static_cast<IntermediateRep>(Factor::num);
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
} // namespace safe_duration_cast
FMT_END_NAMESPACE

@ -1,11 +1,15 @@
#ifndef VTKMDIY_THREAD_H
#define VTKMDIY_THREAD_H
#ifdef VTKM_DIY_NO_THREADS
#include <map>
#ifdef VTKMDIY_NO_THREADS
#include "no-thread.hpp"
#else
#include "thread/fast_mutex.h"
#if !defined(_MSC_VER)
#include "thirdparty/thread/fast_mutex.h"
#endif
#include <thread>
#include <mutex>
@ -17,15 +21,71 @@ namespace diy
using std::recursive_mutex;
namespace this_thread = std::this_thread;
#if defined(_MSC_VER)
// fast_mutex implementation has issues on MSVC. Just use std::mutex
using fast_mutex = std::mutex;
#else
// TODO: replace with our own implementation using std::atomic_flag
using fast_mutex = tthread::fast_mutex;
#endif
template<class Mutex>
using lock_guard = std::unique_lock<Mutex>;
template<class T, class U>
struct concurrent_map;
}
#endif
#endif // VTKMDIY_NO_THREADS
#include "critical-resource.hpp"
#if !defined(VTKMDIY_NO_THREADS)
template<class T, class U>
struct diy::concurrent_map
{
using Map = std::map<T,U>;
using SharedPtr = std::shared_ptr<lock_guard<fast_mutex>>;
template<class MapIterator>
struct iterator_
{
MapIterator it;
SharedPtr lock_ptr;
iterator_(const MapIterator& it_, const SharedPtr& lock_ptr_ = SharedPtr()):
it(it_), lock_ptr(lock_ptr_) {}
iterator_& operator++() { ++it; return *this; }
iterator_ operator++(int) { iterator_ retval = *this; ++(*this); return retval; }
bool operator==(const iterator_& other) const { return it == other.it;}
bool operator!=(const iterator_& other) const { return !(*this == other); }
decltype(*it) operator*() const { return *it; }
decltype(it.operator->()) operator->() const { return it.operator->(); }
};
using iterator = iterator_<typename Map::iterator>;
using const_iterator = iterator_<typename Map::const_iterator>;
U& operator[](const T& x) { lock_guard<fast_mutex> l(mutex_); return map_[x]; }
iterator begin() { auto p = std::make_shared<lock_guard<fast_mutex>>(mutex_); return iterator(map_.begin(), p); }
iterator end() { return iterator(map_.end()); }
const_iterator begin() const { auto p = std::make_shared<lock_guard<fast_mutex>>(mutex_); return const_iterator(map_.begin(), p); }
const_iterator end() const { return const_iterator(map_.end()); }
iterator find(const T& x) { auto p = std::make_shared<lock_guard<fast_mutex>>(mutex_); return iterator(map_.find(x), p); }
const_iterator find(const T& x) const { auto p = std::make_shared<lock_guard<fast_mutex>>(mutex_); return const_iterator(map_.find(x), p); }
void clear() { lock_guard<fast_mutex> l(mutex_); map_.clear(); }
bool empty() { lock_guard<fast_mutex> l(mutex_); return map_.empty(); }
Map map_;
mutable fast_mutex mutex_;
};
#endif // !defined(VTKMDIY_NO_THREADS)
#endif

@ -3,7 +3,7 @@
#include <iostream>
#include "constants.h"
#include "point.hpp"
#include "dynamic-point.hpp"
namespace diy
{
@ -19,12 +19,21 @@ namespace diy
struct Bounds
{
using Coordinate = Coordinate_;
using Point = diy::Point<Coordinate, DIY_MAX_DIM>;
using Point = diy::DynamicPoint<Coordinate>;
Point min, max;
Bounds() = default;
Bounds(int dim): min(dim), max(dim) {}
Bounds(const Point& _min, const Point& _max) : min(_min), max(_max) {}
private:
// make default constructor private to explicitly break old deprecated behavior;
// any call to the default constructor should be replaced by a call to Bounds(0)
Bounds():
Bounds(0) {}
template<class T> friend struct diy::Serialization;
};
using DiscreteBounds = Bounds<int>;
using ContinuousBounds = Bounds<float>;
@ -32,30 +41,43 @@ namespace diy
//! Helper to create a 1-dimensional discrete domain with the specified extents
inline
diy::DiscreteBounds
interval(int from, int to) { DiscreteBounds domain; domain.min[0] = from; domain.max[0] = to; return domain; }
interval(int from, int to) { DiscreteBounds domain(1); domain.min[0] = from; domain.max[0] = to; return domain; }
struct Direction: public Point<int,DIY_MAX_DIM>
struct Direction: public DynamicPoint<int>
{
Direction() { for (size_t i = 0; i < DIY_MAX_DIM; ++i) (*this)[i] = 0; }
Direction(std::initializer_list<int> lst):
Direction() { size_t i = 0; for(int x : lst) (*this)[i++] = x; }
Direction(int dir)
using Parent = DynamicPoint<int>;
using Parent::dimension;
using Parent::operator[];
// enable inherited ctor
using Parent::Parent;
// DM: This breaks the old behavior. Ideally, we'd explicitly deprecate
// this, but we need the default constructor in Serialization. I
// believe I've fixed all uses of this In DIY proper. Hopefully, no
// existing codes break.
Direction(): Parent(0) {}
Direction(int dim, int dir):
Parent(dim)
{
for (size_t i = 0; i < DIY_MAX_DIM; ++i) (*this)[i] = 0;
if (dir & DIY_X0) (*this)[0] -= 1;
if (dir & DIY_X1) (*this)[0] += 1;
if (dir & DIY_Y0) (*this)[1] -= 1;
if (dir & DIY_Y1) (*this)[1] += 1;
if (dir & DIY_Z0) (*this)[2] -= 1;
if (dir & DIY_Z1) (*this)[2] += 1;
if (dir & DIY_T0) (*this)[3] -= 1;
if (dir & DIY_T1) (*this)[3] += 1;
if (dim > 0 && dir & VTKMDIY_X0) (*this)[0] -= 1;
if (dim > 0 && dir & VTKMDIY_X1) (*this)[0] += 1;
if (dim > 1 && dir & VTKMDIY_Y0) (*this)[1] -= 1;
if (dim > 1 && dir & VTKMDIY_Y1) (*this)[1] += 1;
if (dim > 2 && dir & VTKMDIY_Z0) (*this)[2] -= 1;
if (dim > 2 && dir & VTKMDIY_Z1) (*this)[2] += 1;
if (dim > 3 && dir & VTKMDIY_T0) (*this)[3] -= 1;
if (dim > 3 && dir & VTKMDIY_T1) (*this)[3] += 1;
}
static Direction from_bits(int dir, int dim = VTKMDIY_MAX_DIM) { return Direction(dim, dir); }
bool
operator==(const diy::Direction& y) const
{
for (size_t i = 0; i < DIY_MAX_DIM; ++i)
for (size_t i = 0; i < dimension(); ++i)
if ((*this)[i] != y[i]) return false;
return true;
}
@ -64,7 +86,7 @@ namespace diy
bool
operator<(const diy::Direction& y) const
{
for (size_t i = 0; i < DIY_MAX_DIM; ++i)
for (size_t i = 0; i < dimension(); ++i)
{
if ((*this)[i] < y[i]) return true;
if ((*this)[i] > y[i]) return false;
@ -89,6 +111,36 @@ namespace diy
bool
operator==(const diy::BlockID& x, const diy::BlockID& y)
{ return x.gid == y.gid; }
// Serialization
template<class C>
struct Serialization<Bounds<C>>
{
static void save(BinaryBuffer& bb, const Bounds<C>& b)
{
diy::save(bb, b.min);
diy::save(bb, b.max);
}
static void load(BinaryBuffer& bb, Bounds<C>& b)
{
diy::load(bb, b.min);
diy::load(bb, b.max);
}
};
template<>
struct Serialization<Direction>
{
static void save(BinaryBuffer& bb, const Direction& d)
{
diy::save(bb, static_cast<const Direction::Parent&>(d));
}
static void load(BinaryBuffer& bb, Direction& d)
{
diy::load(bb, static_cast<Direction::Parent&>(d));
}
};
}
#endif

@ -3,6 +3,6 @@
#define VTKMDIY_VERSION_MAJOR 3
#define VTKMDIY_VERSION_MINOR 5
#define VTKMDIY_VERSION_PATCH dev1
#define DIY_VERSION_PATCH dev1
#endif