diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 40cefc55..b9c18596 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -1 +1 @@ -ADD_SUBDIRECTORY(o2) +ADD_SUBDIRECTORY(cpprestsdk) diff --git a/3rdparty/cpprestsdk/CMakeLists.txt b/3rdparty/cpprestsdk/CMakeLists.txt new file mode 100644 index 00000000..d6637104 --- /dev/null +++ b/3rdparty/cpprestsdk/CMakeLists.txt @@ -0,0 +1,204 @@ +set(CMAKE_LEGACY_CYGWIN_WIN32 0) +cmake_minimum_required(VERSION 2.6) + +enable_testing() + +set(WARNINGS) +set(ANDROID_STL_FLAGS) + +# Platform (not compiler) specific settings +if(IOS) + set(IOS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Build_iOS") + set(Boost_FRAMEWORK "-F ${IOS_SOURCE_DIR} -framework boost") + set(Boost_INCLUDE_DIR "${IOS_SOURCE_DIR}/boost.framework/Headers") + + set(OPENSSL_FOUND 1) + set(OPENSSL_INCLUDE_DIR "${IOS_SOURCE_DIR}/openssl/include") + set(OPENSSL_LIBRARIES + "${IOS_SOURCE_DIR}/openssl/lib/libcrypto.a" + "${IOS_SOURCE_DIR}/openssl/lib/libssl.a" + ) + + # The cxx_flags must be reset here, because the ios-cmake toolchain file unfortunately sets "-headerpad_max_install_names" which is not a valid clang flag. + set(CMAKE_CXX_FLAGS "-fvisibility=hidden -fvisibility-inlines-hidden") + + set(BUILD_SHARED_LIBS OFF) + set(BUILD_SAMPLES OFF) + option(BUILD_TESTS "Build tests." ON) +elseif(ANDROID) + set(Boost_COMPILER "-clang") + set(Boost_USE_STATIC_LIBS ON) + if(ARM) + set(BOOST_ROOT "${CMAKE_BINARY_DIR}/../Boost-for-Android/build") + set(BOOST_LIBRARYDIR "${CMAKE_BINARY_DIR}/../Boost-for-Android/build/lib") + else() + set(BOOST_ROOT "${CMAKE_BINARY_DIR}/../Boost-for-Android-x86/build") + set(BOOST_LIBRARYDIR "${CMAKE_BINARY_DIR}/../Boost-for-Android-x86/build/lib") + endif() + find_host_package(Boost 1.55 EXACT REQUIRED COMPONENTS random system thread filesystem chrono atomic) + + set(OPENSSL_FOUND 1) + if(ARM) + set(OPENSSL_INCLUDE_DIR "${CMAKE_BINARY_DIR}/../openssl/armeabi-v7a/include") + set(OPENSSL_LIBRARIES + "${CMAKE_BINARY_DIR}/../openssl/armeabi-v7a/lib/libssl.a" + "${CMAKE_BINARY_DIR}/../openssl/armeabi-v7a/lib/libcrypto.a" + ) + else() + set(OPENSSL_INCLUDE_DIR "${CMAKE_BINARY_DIR}/../openssl/x86/include") + set(OPENSSL_LIBRARIES + "${CMAKE_BINARY_DIR}/../openssl/x86/lib/libssl.a" + "${CMAKE_BINARY_DIR}/../openssl/x86/lib/libcrypto.a" + ) + endif() + + if(ARM) + set(LIBCXX_STL "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/thumb/libgnustl_static.a") + else() + set(LIBCXX_STL "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.8/libs/x86/libgnustl_static.a") + endif() + # These are used in the shared library case + set(ANDROID_STL_FLAGS + ${LIBCXX_STL} + atomic + dl + gcc + c + m + -nodefaultlibs + ) + + option(BUILD_SHARED_LIBS "Build shared Libraries." OFF) + set(BUILD_SAMPLES OFF) + option(BUILD_TESTS "Build tests." ON) +elseif(UNIX) # This includes OSX + find_package(Boost REQUIRED COMPONENTS random chrono system thread regex filesystem) + find_package(Threads REQUIRED) + if(APPLE AND NOT OPENSSL_ROOT_DIR) + # Prefer a homebrew version of OpenSSL over the one in /usr/lib + file(GLOB OPENSSL_ROOT_DIR /usr/local/Cellar/openssl/*) + # Prefer the latest (make the latest one first) + list(REVERSE OPENSSL_ROOT_DIR) + endif() + # This should prevent linking against the system provided 0.9.8y + set(_OPENSSL_VERSION "") + find_package(OpenSSL 1.0.0 REQUIRED) + + option(BUILD_SHARED_LIBS "Build shared Libraries." ON) + option(BUILD_TESTS "Build tests." OFF) + option(BUILD_SAMPLES "Build samples." OFF) + option(CASA_INSTALL_HEADERS "Install header files." ON) + if(CASA_INSTALL_HEADERS) + file(GLOB CASA_HEADERS_CPPREST include/cpprest/*.hpp include/cpprest/*.h include/cpprest/*.dat) + install(FILES ${CASA_HEADERS_CPPREST} DESTINATION include/cpprest) + file(GLOB CASA_HEADERS_PPLX include/pplx/*.hpp include/pplx/*.h) + install(FILES ${CASA_HEADERS_PPLX} DESTINATION include/pplx) + file(GLOB CASA_HEADERS_DETAILS include/cpprest/details/*.hpp include/cpprest/details/*.h include/cpprest/details/*.dat) + install(FILES ${CASA_HEADERS_DETAILS} DESTINATION include/cpprest/details) + endif() +elseif(WIN32) + option(BUILD_SHARED_LIBS "Build shared Libraries." ON) + option(BUILD_TESTS "Build tests." ON) + option(BUILD_SAMPLES "Build samples." ON) + option(Boost_USE_STATIC_LIBS ON) + + add_definitions(-DUNICODE -D_UNICODE) + + if(NOT BUILD_SHARED_LIBS) + # This causes cmake to not link the test libraries separately, but instead hold onto their object files. + set(TEST_LIBRARY_TARGET_TYPE OBJECT) + set(Casablanca_DEFINITIONS -D_NO_ASYNCRTIMP -D_NO_PPLXIMP CACHE INTERNAL "Definitions for consume casablanca library") + else() + set(Casablanca_DEFINITIONS "" CACHE INTERNAL "Definitions for consume casablanca library") + endif() + add_definitions(${Casablanca_DEFINITIONS} -D_WINSOCK_DEPRECATED_NO_WARNINGS -DWIN32) + + find_package(Boost 1.55 REQUIRED COMPONENTS random system thread filesystem chrono atomic) + find_package(OpenSSL 1.0 REQUIRED) +else() + message(FATAL_ERROR "-- Unsupported Build Platform.") +endif() + +# Compiler (not platform) specific settings +if(ANDROID) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-strict-aliasing") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pedantic") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes -Wno-pointer-arith") + include_directories( + "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.8/include" + "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/include" + "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.8/include/backward" + ) +elseif((CMAKE_CXX_COMPILER_ID MATCHES "Clang") OR IOS) + message("-- Setting clang options") + + set(WARNINGS "-Wall -Wextra -Wcast-qual -Wconversion -Wformat=2 -Winit-self -Winvalid-pch -Wmissing-format-attribute -Wmissing-include-dirs -Wpacked -Wredundant-decls") + set(OSX_SUPPRESSIONS "-Wno-overloaded-virtual -Wno-sign-conversion -Wno-deprecated -Wno-unknown-pragmas -Wno-reorder -Wno-char-subscripts -Wno-switch -Wno-unused-parameter -Wno-unused-variable -Wno-deprecated -Wno-unused-value -Wno-unknown-warning-option -Wno-return-type-c-linkage -Wno-unused-function -Wno-sign-compare -Wno-shorten-64-to-32 -Wno-reorder") + set(WARNINGS "${WARNINGS} ${OSX_SUPPRESSIONS}") + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -Wno-return-type-c-linkage -Wno-unneeded-internal-declaration") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11") + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-strict-aliasing") +elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + message("-- Setting gcc options") + + set(WARNINGS "-Wall -Wextra -Wunused-parameter -Wcast-align -Wcast-qual -Wconversion -Wformat=2 -Winit-self -Winvalid-pch -Wmissing-format-attribute -Wmissing-include-dirs -Wpacked -Wredundant-decls -Wunreachable-code") + + set(LD_FLAGS "${LD_FLAGS} -Wl,-z,defs") + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-strict-aliasing") +elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + message("-- Setting msvc options") + set(WARNINGS) +else() + message("-- Unknown compiler, success is doubtful.") + message("CMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID}") +endif() + +# Reconfigure final output directory +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) + +# These settings can be used by the test targets +set(Casablanca_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(Casablanca_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include ${Boost_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/libs/websocketpp) + +set(Casablanca_LIBRARY ${LIB}cpprest) +set(Casablanca_LIBRARIES ${Casablanca_LIBRARY} ${Boost_LIBRARIES} ${Boost_FRAMEWORK}) + +# Everything in the project needs access to the casablanca include directories +include_directories(${Casablanca_INCLUDE_DIRS}) + +# Finally, the tests all use the same style declaration to build themselves, so we use a function +function(add_casablanca_test NAME SOURCES_VAR) + add_library(${NAME} ${TEST_LIBRARY_TARGET_TYPE} ${${SOURCES_VAR}}) + message("-- Added test library ${NAME}") + if (NOT TEST_LIBRARY_TARGET_TYPE STREQUAL "OBJECT") + target_link_libraries(${NAME} + ${LIB}httptest_utilities + ${LIB}common_utilities + ${LIB}unittestpp + ${Casablanca_LIBRARIES} + ${ANDROID_STL_FLAGS} + ) + if (BUILD_SHARED_LIBS) + add_test(NAME ${NAME} + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + COMMAND test_runner lib${NAME}${CMAKE_SHARED_LIBRARY_SUFFIX} + ) + endif() + endif() +endfunction() + +add_subdirectory(src) + +if(BUILD_TESTS) + add_subdirectory(tests) +endif() + +if(BUILD_SAMPLES) + add_subdirectory(samples) +endif() diff --git a/3rdparty/cpprestsdk/dirs.proj b/3rdparty/cpprestsdk/dirs.proj new file mode 100644 index 00000000..bdbd321a --- /dev/null +++ b/3rdparty/cpprestsdk/dirs.proj @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/3rdparty/cpprestsdk/include/cpprest/astreambuf.h b/3rdparty/cpprestsdk/include/cpprest/astreambuf.h new file mode 100644 index 00000000..a12605d9 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/astreambuf.h @@ -0,0 +1,1199 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Asynchronous I/O: stream buffer. This is an extension to the PPL concurrency features and therefore +* lives in the Concurrency namespace. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#include +#include +#include +#include + +#include "pplx/pplxtasks.h" +#include "cpprest/details/basic_types.h" +#include "cpprest/asyncrt_utils.h" + +namespace Concurrency +{ +/// Library for asynchronous streams. +namespace streams +{ + /// + /// Extending the standard char_traits type with one that adds values and types + /// that are unique to "C++ REST SDK" streams. + /// + /// + /// The data type of the basic element of the stream. + /// + template + struct char_traits : std::char_traits<_CharType> + { + /// + /// Some synchronous functions will return this value if the operation + /// requires an asynchronous call in a given situation. + /// + /// An int_type value which implies that an asynchronous call is required. + static typename std::char_traits<_CharType>::int_type requires_async() { return std::char_traits<_CharType>::eof()-1; } + }; +#if !defined(_WIN32) + template<> + struct char_traits : private std::char_traits + { + public: + typedef unsigned char char_type; + + using std::char_traits::eof; + using std::char_traits::int_type; + using std::char_traits::off_type; + using std::char_traits::pos_type; + + static size_t length(const unsigned char* str) + { + return std::char_traits::length(reinterpret_cast(str)); + } + + static void assign(unsigned char& left, const unsigned char& right) { left = right; } + static unsigned char* assign(unsigned char* left, size_t n, unsigned char value) + { + return reinterpret_cast(std::char_traits::assign(reinterpret_cast(left), n, static_cast(value))); + } + + static unsigned char* copy(unsigned char* left, const unsigned char* right, size_t n) + { + return reinterpret_cast(std::char_traits::copy(reinterpret_cast(left), reinterpret_cast(right), n)); + } + + static unsigned char* move(unsigned char* left, const unsigned char* right, size_t n) + { + return reinterpret_cast(std::char_traits::move(reinterpret_cast(left), reinterpret_cast(right), n)); + } + + static int_type requires_async() { return eof() - 1; } + }; +#endif + + namespace details { + + /// + /// Stream buffer base class. + /// + template + class basic_streambuf + { + public: + typedef _CharType char_type; + typedef ::concurrency::streams::char_traits<_CharType> traits; + + typedef typename traits::int_type int_type; + typedef typename traits::pos_type pos_type; + typedef typename traits::off_type off_type; + + + /// + /// Virtual constructor for stream buffers. + /// + virtual ~basic_streambuf() { } + + /// + /// can_read is used to determine whether a stream buffer will support read operations (get). + /// + virtual bool can_read() const = 0; + + /// + /// can_write is used to determine whether a stream buffer will support write operations (put). + /// + virtual bool can_write() const = 0; + + /// + /// can_seek is used to determine whether a stream buffer supports seeking. + /// + virtual bool can_seek() const = 0; + + /// + /// has_size is used to determine whether a stream buffer supports size(). + /// + virtual bool has_size() const = 0; + + /// + /// is_eof is used to determine whether a read head has reached the end of the buffer. + /// + virtual bool is_eof() const = 0; + + /// + /// Gets the stream buffer size, if one has been set. + /// + /// The direction of buffering (in or out) + /// The size of the internal buffer (for the given direction). + /// An implementation that does not support buffering will always return 0. + virtual size_t buffer_size(std::ios_base::openmode direction = std::ios_base::in) const = 0; + + /// + /// Sets the stream buffer implementation to buffer or not buffer. + /// + /// The size to use for internal buffering, 0 if no buffering should be done. + /// The direction of buffering (in or out) + /// An implementation that does not support buffering will silently ignore calls to this function and it will not have any effect on what is returned by subsequent calls to . + virtual void set_buffer_size(size_t size, std::ios_base::openmode direction = std::ios_base::in) = 0; + + /// + /// For any input stream, in_avail returns the number of characters that are immediately available + /// to be consumed without blocking. May be used in conjunction with to read data without + /// incurring the overhead of using tasks. + /// + virtual size_t in_avail() const = 0; + + /// + /// Checks if the stream buffer is open. + /// + /// No separation is made between open for reading and open for writing. + virtual bool is_open() const = 0; + + /// + /// Closes the stream buffer, preventing further read or write operations. + /// + /// The I/O mode (in or out) to close for. + virtual pplx::task close(std::ios_base::openmode mode = (std::ios_base::in | std::ios_base::out)) = 0; + + /// + /// Closes the stream buffer with an exception. + /// + /// The I/O mode (in or out) to close for. + /// Pointer to the exception. + virtual pplx::task close(std::ios_base::openmode mode, std::exception_ptr eptr) = 0; + + /// + /// Writes a single character to the stream. + /// + /// The character to write + /// A task that holds the value of the character. This value is EOF if the write operation fails. + virtual pplx::task putc(_CharType ch) = 0; + + /// + /// Writes a number of characters to the stream. + /// + /// A pointer to the block of data to be written. + /// The number of characters to write. + /// A task that holds the number of characters actually written, either 'count' or 0. + virtual pplx::task putn(const _CharType *ptr, size_t count) = 0; + + /// + /// Writes a number of characters to the stream. Note: callers must make sure the data to be written is valid until + /// the returned task completes. + /// + /// A pointer to the block of data to be written. + /// The number of characters to write. + /// A task that holds the number of characters actually written, either 'count' or 0. + virtual pplx::task putn_nocopy(const _CharType *ptr, size_t count) = 0; + + /// + /// Reads a single character from the stream and advances the read position. + /// + /// A task that holds the value of the character. This value is EOF if the read fails. + virtual pplx::task bumpc() = 0; + + /// + /// Reads a single character from the stream and advances the read position. + /// + /// The value of the character. -1 if the read fails. -2 if an asynchronous read is required + /// This is a synchronous operation, but is guaranteed to never block. + virtual int_type sbumpc() = 0; + + /// + /// Reads a single character from the stream without advancing the read position. + /// + /// A task that holds the value of the byte. This value is EOF if the read fails. + virtual pplx::task getc() = 0; + + /// + /// Reads a single character from the stream without advancing the read position. + /// + /// The value of the character. EOF if the read fails. if an asynchronous read is required + /// This is a synchronous operation, but is guaranteed to never block. + virtual int_type sgetc() = 0; + + /// + /// Advances the read position, then returns the next character without advancing again. + /// + /// A task that holds the value of the character. This value is EOF if the read fails. + virtual pplx::task nextc() = 0; + + /// + /// Retreats the read position, then returns the current character without advancing. + /// + /// A task that holds the value of the character. This value is EOF if the read fails, requires_async if an asynchronous read is required + virtual pplx::task ungetc() = 0; + + /// + /// Reads up to a given number of characters from the stream. + /// + /// The address of the target memory area. + /// The maximum number of characters to read. + /// A task that holds the number of characters read. This value is O if the end of the stream is reached. + virtual pplx::task getn(_Out_writes_(count) _CharType *ptr, _In_ size_t count) = 0; + + /// + /// Copies up to a given number of characters from the stream, synchronously. + /// + /// The address of the target memory area. + /// The maximum number of characters to read. + /// The number of characters copied. O if the end of the stream is reached or an asynchronous read is required. + /// This is a synchronous operation, but is guaranteed to never block. + virtual size_t scopy(_Out_writes_(count) _CharType *ptr, _In_ size_t count) = 0; + + /// + /// Gets the current read or write position in the stream. + /// + /// The I/O direction to seek (see remarks) + /// The current position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. + /// For such streams, the direction parameter defines whether to move the read or the write cursor. + virtual pos_type getpos(std::ios_base::openmode direction) const = 0; + + /// + /// Gets the size of the stream, if known. Calls to has_size will determine whether + /// the result of size can be relied on. + /// + virtual utility::size64_t size() const = 0; + + /// + /// Seeks to the given position. + /// + /// The offset from the beginning of the stream. + /// The I/O direction to seek (see remarks). + /// The position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. For such streams, the direction parameter defines whether to move the read or the write cursor. + virtual pos_type seekpos(pos_type pos, std::ios_base::openmode direction) = 0; + + /// + /// Seeks to a position given by a relative offset. + /// + /// The relative position to seek to + /// The starting point (beginning, end, current) for the seek. + /// The I/O direction to seek (see remarks) + /// The position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. + /// For such streams, the mode parameter defines whether to move the read or the write cursor. + virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode) = 0; + + /// + /// For output streams, flush any internally buffered data to the underlying medium. + /// + /// A task that returns true if the sync succeeds, false if not. + virtual pplx::task sync() = 0; + + // + // Efficient read and write. + // + // The following routines are intended to be used for more efficient, copy-free, reading and + // writing of data from/to the stream. Rather than having the caller provide a buffer into which + // data is written or from which it is read, the stream buffer provides a pointer directly to the + // internal data blocks that it is using. Since not all stream buffers use internal data structures + // to copy data, the functions may not be supported by all. An application that wishes to use this + // functionality should therefore first try them and check for failure to support. If there is + // such failure, the application should fall back on the copying interfaces (putn / getn) + // + + /// + /// Allocates a contiguous memory block and returns it. + /// + /// The number of characters to allocate. + /// A pointer to a block to write to, null if the stream buffer implementation does not support alloc/commit. + virtual _CharType* alloc(_In_ size_t count) = 0; + + /// + /// Submits a block already allocated by the stream buffer. + /// + /// The number of characters to be committed. + virtual void commit(_In_ size_t count) = 0; + + /// + /// Gets a pointer to the next already allocated contiguous block of data. + /// + /// A reference to a pointer variable that will hold the address of the block on success. + /// The number of contiguous characters available at the address in 'ptr.' + /// true if the operation succeeded, false otherwise. + /// + /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that + /// there is no block to return immediately or that the stream buffer does not support the operation. + /// The stream buffer may not de-allocate the block until is called. + /// If the end of the stream is reached, the function will return true, a null pointer, and a count of zero; + /// a subsequent read will not succeed. + /// + virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count) = 0; + + /// + /// Releases a block of data acquired using . This frees the stream buffer to de-allocate the + /// memory, if it so desires. Move the read position ahead by the count. + /// + /// A pointer to the block of data to be released. + /// The number of characters that were read. + virtual void release(_Out_writes_(count) _CharType *ptr, _In_ size_t count) = 0; + + /// + /// Retrieves the stream buffer exception_ptr if it has been set. + /// + /// Pointer to the exception, if it has been set; otherwise, nullptr will be returned + virtual std::exception_ptr exception() const = 0; + }; + + + template + class streambuf_state_manager : public basic_streambuf<_CharType>, public std::enable_shared_from_this> + { + public: + typedef typename details::basic_streambuf<_CharType>::traits traits; + typedef typename details::basic_streambuf<_CharType>::int_type int_type; + typedef typename details::basic_streambuf<_CharType>::pos_type pos_type; + typedef typename details::basic_streambuf<_CharType>::off_type off_type; + + /// + /// can_read is used to determine whether a stream buffer will support read operations (get). + /// + virtual bool can_read() const + { + return m_stream_can_read; + } + + /// + /// can_write is used to determine whether a stream buffer will support write operations (put). + /// + virtual bool can_write() const + { + return m_stream_can_write; + } + + /// + /// Checks if the stream buffer is open. + /// + /// No separation is made between open for reading and open for writing. + virtual bool is_open() const + { + return can_read() || can_write(); + } + + /// + /// Closes the stream buffer, preventing further read or write operations. + /// + /// The I/O mode (in or out) to close for. + virtual pplx::task close(std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + { + pplx::task closeOp = pplx::task_from_result(); + + if (mode & std::ios_base::in && can_read()) { + closeOp = _close_read(); + } + + // After the flush_internal task completed, "this" object may have been destroyed, + // accessing the members is invalid, use shared_from_this to avoid access violation exception. + auto this_ptr = std::static_pointer_cast(this->shared_from_this()); + + if (mode & std::ios_base::out && can_write()) { + if (closeOp.is_done()) + closeOp = closeOp && _close_write().then([this_ptr]{}); // passing down exceptions from closeOp + else + closeOp = closeOp.then([this_ptr] { return this_ptr->_close_write().then([this_ptr]{}); }); + } + + return closeOp; + } + + /// + /// Closes the stream buffer with an exception. + /// + /// The I/O mode (in or out) to close for. + /// Pointer to the exception. + virtual pplx::task close(std::ios_base::openmode mode, std::exception_ptr eptr) + { + if (m_currentException == nullptr) + m_currentException = eptr; + return close(mode); + } + + /// + /// is_eof is used to determine whether a read head has reached the end of the buffer. + /// + virtual bool is_eof() const + { + return m_stream_read_eof; + } + + /// + /// Writes a single character to the stream. + /// + /// The character to write + /// The value of the character. EOF if the write operation fails + virtual pplx::task putc(_CharType ch) + { + if (!can_write()) + return create_exception_checked_value_task(traits::eof()); + + return create_exception_checked_task(_putc(ch), [](int_type) { + return false; // no EOF for write + }); + } + + /// + /// Writes a number of characters to the stream. + /// + /// A pointer to the block of data to be written. + /// The number of characters to write. + /// The number of characters actually written, either 'count' or 0. +// CASABLANCA_DEPRECATED("This API in some cases performs a copy. It is deprecated and will be removed in a future release. Use putn_nocopy instead.") + virtual pplx::task putn(const _CharType *ptr, size_t count) + { + if (!can_write()) + return create_exception_checked_value_task(0); + if (count == 0) + return pplx::task_from_result(0); + + return create_exception_checked_task(_putn(ptr, count, true), [](size_t) { + return false; // no EOF for write + }); + } + + /// + /// Writes a number of characters to the stream. Note: callers must make sure the data to be written is valid until + /// the returned task completes. + /// + /// A pointer to the block of data to be written. + /// The number of characters to write. + /// A task that holds the number of characters actually written, either 'count' or 0. + virtual pplx::task putn_nocopy(const _CharType *ptr, size_t count) + { + if (!can_write()) + return create_exception_checked_value_task(0); + if (count == 0) + return pplx::task_from_result(0); + + return create_exception_checked_task(_putn(ptr, count), [](size_t) { + return false; // no EOF for write + }); + } + + /// + /// Reads a single character from the stream and advances the read position. + /// + /// The value of the character. EOF if the read fails. + virtual pplx::task bumpc() + { + if (!can_read()) + return create_exception_checked_value_task(streambuf_state_manager<_CharType>::traits::eof()); + + return create_exception_checked_task(_bumpc(), [](int_type val) { + return val == streambuf_state_manager<_CharType>::traits::eof(); + }); + } + + /// + /// Reads a single character from the stream and advances the read position. + /// + /// The value of the character. -1 if the read fails. -2 if an asynchronous read is required + /// This is a synchronous operation, but is guaranteed to never block. + virtual int_type sbumpc() + { + if ( !(m_currentException == nullptr) ) + std::rethrow_exception(m_currentException); + if (!can_read()) + return traits::eof(); + return check_sync_read_eof(_sbumpc()); + } + + /// + /// Reads a single character from the stream without advancing the read position. + /// + /// The value of the byte. EOF if the read fails. + virtual pplx::task getc() + { + if (!can_read()) + return create_exception_checked_value_task(traits::eof()); + + return create_exception_checked_task(_getc(), [](int_type val) { + return val == streambuf_state_manager<_CharType>::traits::eof(); + }); + } + + /// + /// Reads a single character from the stream without advancing the read position. + /// + /// The value of the character. EOF if the read fails. if an asynchronous read is required + /// This is a synchronous operation, but is guaranteed to never block. + virtual int_type sgetc() + { + if ( !(m_currentException == nullptr) ) + std::rethrow_exception(m_currentException); + if (!can_read()) + return traits::eof(); + return check_sync_read_eof(_sgetc()); + } + + /// + /// Advances the read position, then returns the next character without advancing again. + /// + /// The value of the character. EOF if the read fails. + virtual pplx::task nextc() + { + if (!can_read()) + return create_exception_checked_value_task(traits::eof()); + + return create_exception_checked_task(_nextc(), [](int_type val) { + return val == streambuf_state_manager<_CharType>::traits::eof(); + }); + } + + /// + /// Retreats the read position, then returns the current character without advancing. + /// + /// The value of the character. EOF if the read fails. if an asynchronous read is required + virtual pplx::task ungetc() + { + if (!can_read()) + return create_exception_checked_value_task(traits::eof()); + + return create_exception_checked_task(_ungetc(), [](int_type) { + return false; + }); + } + + /// + /// Reads up to a given number of characters from the stream. + /// + /// The address of the target memory area. + /// The maximum number of characters to read. + /// The number of characters read. O if the end of the stream is reached. + virtual pplx::task getn(_Out_writes_(count) _CharType *ptr, _In_ size_t count) + { + if (!can_read()) + return create_exception_checked_value_task(0); + if (count == 0) + return pplx::task_from_result(0); + + return create_exception_checked_task(_getn(ptr, count), [](size_t val) { + return val == 0; + }); + } + + /// + /// Copies up to a given number of characters from the stream, synchronously. + /// + /// The address of the target memory area. + /// The maximum number of characters to read. + /// The number of characters copied. O if the end of the stream is reached or an asynchronous read is required. + /// This is a synchronous operation, but is guaranteed to never block. + virtual size_t scopy(_Out_writes_(count) _CharType *ptr, _In_ size_t count) + { + if ( !(m_currentException == nullptr) ) + std::rethrow_exception(m_currentException); + if (!can_read()) + return 0; + + return _scopy(ptr, count); + } + + /// + /// For output streams, flush any internally buffered data to the underlying medium. + /// + /// true if the flush succeeds, false if not + virtual pplx::task sync() + { + if (!can_write()) + { + if (m_currentException == nullptr) + return pplx::task_from_result(); + else + return pplx::task_from_exception(m_currentException); + } + return create_exception_checked_task(_sync(), [](bool) { + return false; + }).then([](bool){}); + } + + /// + /// Retrieves the stream buffer exception_ptr if it has been set. + /// + /// Pointer to the exception, if it has been set; otherwise, nullptr will be returned. + virtual std::exception_ptr exception() const + { + return m_currentException; + } + + /// + /// Allocates a contiguous memory block and returns it. + /// + /// The number of characters to allocate. + /// A pointer to a block to write to, null if the stream buffer implementation does not support alloc/commit. + /// This is intended as an advanced API to be used only when it is important to avoid extra copies. + _CharType* alloc(size_t count) + { + if (m_alloced) + throw std::logic_error("The buffer is already allocated, this maybe caused by overlap of stream read or write"); + + _CharType* alloc_result = _alloc(count); + + if (alloc_result) + m_alloced = true; + + return alloc_result; + } + + /// + /// Submits a block already allocated by the stream buffer. + /// + /// The number of characters to be committed. + /// This is intended as an advanced API to be used only when it is important to avoid extra copies. + void commit(size_t count) + { + if (!m_alloced) + throw std::logic_error("The buffer needs to allocate first"); + + _commit(count); + m_alloced = false; + } + + public: + virtual bool can_seek() const = 0; + virtual bool has_size() const = 0; + virtual utility::size64_t size() const { return 0; } + virtual size_t buffer_size(std::ios_base::openmode direction = std::ios_base::in) const = 0; + virtual void set_buffer_size(size_t size, std::ios_base::openmode direction = std::ios_base::in) = 0; + virtual size_t in_avail() const = 0; + virtual pos_type getpos(std::ios_base::openmode direction) const = 0; + virtual pos_type seekpos(pos_type pos, std::ios_base::openmode direction) = 0; + virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode) = 0; + virtual bool acquire(_Out_writes_(count) _CharType*& ptr, _In_ size_t& count) = 0; + virtual void release(_Out_writes_(count) _CharType *ptr, _In_ size_t count) = 0; + protected: + virtual pplx::task _putc(_CharType ch) = 0; + + // This API is only needed for file streams and until we remove the deprecated stream buffer putn overload. + virtual pplx::task _putn(const _CharType *ptr, size_t count, bool) + { + // Default to no copy, only the file streams API overloads and performs a copy. + return _putn(ptr, count); + } + virtual pplx::task _putn(const _CharType *ptr, size_t count) = 0; + + virtual pplx::task _bumpc() = 0; + virtual int_type _sbumpc() = 0; + virtual pplx::task _getc() = 0; + virtual int_type _sgetc() = 0; + virtual pplx::task _nextc() = 0; + virtual pplx::task _ungetc() = 0; + virtual pplx::task _getn(_Out_writes_(count) _CharType *ptr, _In_ size_t count) = 0; + virtual size_t _scopy(_Out_writes_(count) _CharType *ptr, _In_ size_t count) = 0; + virtual pplx::task _sync() = 0; + virtual _CharType* _alloc(size_t count) = 0; + virtual void _commit(size_t count) = 0; + + /// + /// The real read head close operation, implementation should override it if there is any resource to be released. + /// + virtual pplx::task _close_read() + { + m_stream_can_read = false; + return pplx::task_from_result(); + } + + /// + /// The real write head close operation, implementation should override it if there is any resource to be released. + /// + virtual pplx::task _close_write() + { + m_stream_can_write = false; + return pplx::task_from_result(); + } + + protected: + streambuf_state_manager(std::ios_base::openmode mode) + { + m_stream_can_read = (mode & std::ios_base::in) != 0; + m_stream_can_write = (mode & std::ios_base::out) != 0; + m_stream_read_eof = false; + m_alloced = false; + } + + std::exception_ptr m_currentException; + // The in/out mode for the buffer + bool m_stream_can_read, m_stream_can_write, m_stream_read_eof, m_alloced; + + + private: + template + pplx::task<_CharType1> create_exception_checked_value_task(const _CharType1 &val) const + { + if (this->exception() == nullptr) + return pplx::task_from_result<_CharType1>(static_cast<_CharType1>(val)); + else + return pplx::task_from_exception<_CharType1>(this->exception()); + } + + // Set exception and eof states for async read + template + pplx::task<_CharType1> create_exception_checked_task(pplx::task<_CharType1> result, std::function eof_test, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + { + auto thisPointer = this->shared_from_this(); + + auto func1 = [=](pplx::task<_CharType1> t1) -> pplx::task<_CharType1> { + try { + thisPointer->m_stream_read_eof = eof_test(t1.get()); + } catch (...) { + thisPointer->close(mode, std::current_exception()).get(); + return pplx::task_from_exception<_CharType1>(thisPointer->exception(), pplx::task_options()); + } + if (thisPointer->m_stream_read_eof && !(thisPointer->exception() == nullptr)) + return pplx::task_from_exception<_CharType1>(thisPointer->exception(), pplx::task_options()); + return t1; + }; + + if ( result.is_done() ) + { + // If the data is already available, we should avoid scheduling a continuation, so we do it inline. + return func1(result); + } + else + { + return result.then(func1); + } + } + + // Set eof states for sync read + int_type check_sync_read_eof(int_type ch) + { + m_stream_read_eof = ch == traits::eof(); + return ch; + } + + }; + + } // namespace details + + // Forward declarations + template class basic_istream; + template class basic_ostream; + + /// + /// Reference-counted stream buffer. + /// + /// + /// The data type of the basic element of the streambuf. + /// + /// + /// The data type of the basic element of the streambuf. + /// + template + class streambuf : public details::basic_streambuf<_CharType> + { + public: + typedef typename details::basic_streambuf<_CharType>::traits traits; + typedef typename details::basic_streambuf<_CharType>::int_type int_type; + typedef typename details::basic_streambuf<_CharType>::pos_type pos_type; + typedef typename details::basic_streambuf<_CharType>::off_type off_type; + typedef typename details::basic_streambuf<_CharType>::char_type char_type; + + template friend class streambuf; + + /// + /// Constructor. + /// + /// A pointer to the concrete stream buffer implementation. + streambuf(_In_ const std::shared_ptr> &ptr) : m_buffer(ptr) {} + + /// + /// Default constructor. + /// + streambuf() { } + + /// + /// Converter Constructor. + /// + /// + /// The data type of the basic element of the source streambuf. + /// + /// The source buffer to be converted. + template + streambuf(const streambuf &other) : + m_buffer(std::static_pointer_cast>(std::static_pointer_cast(other.m_buffer))) + { + static_assert(std::is_same::pos_type>::value + && std::is_same::off_type>::value + && std::is_integral<_CharType>::value && std::is_integral::value + && std::is_integral::value && std::is_integral::int_type>::value + && sizeof(_CharType) == sizeof(AlterCharType) + && sizeof(int_type) == sizeof(typename details::basic_streambuf::int_type), + "incompatible stream character types"); + } + + /// + /// Constructs an input stream head for this stream buffer. + /// + /// basic_istream. + concurrency::streams::basic_istream<_CharType> create_istream() const + { + if (!can_read()) throw std::runtime_error("stream buffer not set up for input of data"); + return concurrency::streams::basic_istream<_CharType>(*this); + } + + /// + /// Constructs an output stream for this stream buffer. + /// + /// basic_ostream + concurrency::streams::basic_ostream<_CharType> create_ostream() const + { + if (!can_write()) throw std::runtime_error("stream buffer not set up for output of data"); + return concurrency::streams::basic_ostream<_CharType>(*this); + } + + /// + /// Checks if the stream buffer has been initialized or not. + /// + operator bool() const { return (bool)m_buffer; } + + /// + /// Destructor + /// + virtual ~streambuf() { } + + const std::shared_ptr> & get_base() const + { + if (!m_buffer) + { + throw std::invalid_argument("Invalid streambuf object"); + } + + return m_buffer; + } + + /// + /// can_read is used to determine whether a stream buffer will support read operations (get). + /// + virtual bool can_read() const { return get_base()->can_read(); } + + /// + /// can_write is used to determine whether a stream buffer will support write operations (put). + /// + virtual bool can_write() const { return get_base()->can_write(); } + + /// + /// can_seek is used to determine whether a stream buffer supports seeking. + /// + /// True if seeking is supported, false otherwise. + virtual bool can_seek() const { return get_base()->can_seek(); } + + /// + /// has_size is used to determine whether a stream buffer supports size(). + /// + /// True if the size API is supported, false otherwise. + virtual bool has_size() const { return get_base()->has_size(); } + + /// + /// Gets the total number of characters in the stream buffer, if known. Calls to has_size will determine whether + /// the result of size can be relied on. + /// + /// The total number of characters in the stream buffer. + virtual utility::size64_t size() const { return get_base()->size(); } + + /// + /// Gets the stream buffer size, if one has been set. + /// + /// The direction of buffering (in or out) + /// The size of the internal buffer (for the given direction). + /// An implementation that does not support buffering will always return 0. + virtual size_t buffer_size(std::ios_base::openmode direction = std::ios_base::in) const { return get_base()->buffer_size(direction); } + + /// + /// Sets the stream buffer implementation to buffer or not buffer. + /// + /// The size to use for internal buffering, 0 if no buffering should be done. + /// The direction of buffering (in or out) + /// An implementation that does not support buffering will silently ignore calls to this function and it will not have any effect on what is returned by subsequent calls to . + virtual void set_buffer_size(size_t size, std::ios_base::openmode direction = std::ios_base::in) { get_base()->set_buffer_size(size,direction); } + + /// + /// For any input stream, in_avail returns the number of characters that are immediately available + /// to be consumed without blocking. May be used in conjunction with to read data without + /// incurring the overhead of using tasks. + /// + /// Number of characters that are ready to read. + virtual size_t in_avail() const { return get_base()->in_avail(); } + + /// + /// Checks if the stream buffer is open. + /// + /// No separation is made between open for reading and open for writing. + /// True if the stream buffer is open for reading or writing, false otherwise. + virtual bool is_open() const { return get_base()->is_open(); } + + /// + /// is_eof is used to determine whether a read head has reached the end of the buffer. + /// + /// True if at the end of the buffer, false otherwise. + virtual bool is_eof() const { return get_base()->is_eof(); } + + /// + /// Closes the stream buffer, preventing further read or write operations. + /// + /// The I/O mode (in or out) to close for. + virtual pplx::task close(std::ios_base::openmode mode = (std::ios_base::in | std::ios_base::out)) + { + // We preserve the check here to workaround a Dev10 compiler crash + auto buffer = get_base(); + return buffer ? buffer->close(mode) : pplx::task_from_result(); + } + + /// + /// Closes the stream buffer with an exception. + /// + /// The I/O mode (in or out) to close for. + /// Pointer to the exception. + virtual pplx::task close(std::ios_base::openmode mode, std::exception_ptr eptr) + { + // We preserve the check here to workaround a Dev10 compiler crash + auto buffer = get_base(); + return buffer ? buffer->close(mode, eptr) : pplx::task_from_result(); + } + + /// + /// Writes a single character to the stream. + /// + /// The character to write + /// The value of the character. EOF if the write operation fails + virtual pplx::task putc(_CharType ch) + { + return get_base()->putc(ch); + } + + /// + /// Allocates a contiguous memory block and returns it. + /// + /// The number of characters to allocate. + /// A pointer to a block to write to, null if the stream buffer implementation does not support alloc/commit. + virtual _CharType* alloc(size_t count) + { + return get_base()->alloc(count); + } + + /// + /// Submits a block already allocated by the stream buffer. + /// + /// The number of characters to be committed. + virtual void commit(size_t count) + { + get_base()->commit(count); + } + + /// + /// Gets a pointer to the next already allocated contiguous block of data. + /// + /// A reference to a pointer variable that will hold the address of the block on success. + /// The number of contiguous characters available at the address in 'ptr.' + /// true if the operation succeeded, false otherwise. + /// + /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that + /// there is no block to return immediately or that the stream buffer does not support the operation. + /// The stream buffer may not de-allocate the block until is called. + /// If the end of the stream is reached, the function will return true, a null pointer, and a count of zero; + /// a subsequent read will not succeed. + /// + virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count) + { + ptr = nullptr; + count = 0; + return get_base()->acquire(ptr, count); + } + + /// + /// Releases a block of data acquired using . This frees the stream buffer to de-allocate the + /// memory, if it so desires. Move the read position ahead by the count. + /// + /// A pointer to the block of data to be released. + /// The number of characters that were read. + virtual void release(_Out_writes_(count) _CharType *ptr, _In_ size_t count) + { + get_base()->release(ptr, count); + } + + /// + /// Writes a number of characters to the stream. + /// + /// A pointer to the block of data to be written. + /// The number of characters to write. + /// The number of characters actually written, either 'count' or 0. +// CASABLANCA_DEPRECATED("This API in some cases performs a copy. It is deprecated and will be removed in a future release. Use putn_nocopy instead.") + virtual pplx::task putn(const _CharType *ptr, size_t count) + { + return get_base()->putn(ptr, count); + } + + /// + /// Writes a number of characters to the stream. Note: callers must make sure the data to be written is valid until + /// the returned task completes. + /// + /// A pointer to the block of data to be written. + /// The number of characters to write. + /// The number of characters actually written, either 'count' or 0. + virtual pplx::task putn_nocopy(const _CharType *ptr, size_t count) + { + return get_base()->putn_nocopy(ptr, count); + } + + /// + /// Reads a single character from the stream and advances the read position. + /// + /// The value of the character. EOF if the read fails. + virtual pplx::task bumpc() + { + return get_base()->bumpc(); + } + + /// + /// Reads a single character from the stream and advances the read position. + /// + /// The value of the character. -1 if the read fails. -2 if an asynchronous read is required + /// This is a synchronous operation, but is guaranteed to never block. + virtual typename details::basic_streambuf<_CharType>::int_type sbumpc() + { + return get_base()->sbumpc(); + } + + /// + /// Reads a single character from the stream without advancing the read position. + /// + /// The value of the byte. EOF if the read fails. + virtual pplx::task getc() + { + return get_base()->getc(); + } + + /// + /// Reads a single character from the stream without advancing the read position. + /// + /// The value of the character. EOF if the read fails. if an asynchronous read is required + /// This is a synchronous operation, but is guaranteed to never block. + virtual typename details::basic_streambuf<_CharType>::int_type sgetc() + { + return get_base()->sgetc(); + } + + /// + /// Advances the read position, then returns the next character without advancing again. + /// + /// The value of the character. EOF if the read fails. + pplx::task nextc() + { + return get_base()->nextc(); + } + + /// + /// Retreats the read position, then returns the current character without advancing. + /// + /// The value of the character. EOF if the read fails. if an asynchronous read is required + pplx::task ungetc() + { + return get_base()->ungetc(); + } + + /// + /// Reads up to a given number of characters from the stream. + /// + /// The address of the target memory area. + /// The maximum number of characters to read. + /// The number of characters read. O if the end of the stream is reached. + virtual pplx::task getn(_Out_writes_(count) _CharType *ptr, _In_ size_t count) + { + return get_base()->getn(ptr, count); + } + + /// + /// Copies up to a given number of characters from the stream, synchronously. + /// + /// The address of the target memory area. + /// The maximum number of characters to read. + /// The number of characters copied. O if the end of the stream is reached or an asynchronous read is required. + /// This is a synchronous operation, but is guaranteed to never block. + virtual size_t scopy(_Out_writes_(count) _CharType *ptr, _In_ size_t count) + { + return get_base()->scopy(ptr, count); + } + + /// + /// Gets the current read or write position in the stream. + /// + /// The I/O direction to seek (see remarks) + /// The current position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. + /// For such streams, the direction parameter defines whether to move the read or the write cursor. + virtual typename details::basic_streambuf<_CharType>::pos_type getpos(std::ios_base::openmode direction) const + { + return get_base()->getpos(direction); + } + + /// + /// Seeks to the given position. + /// + /// The offset from the beginning of the stream. + /// The I/O direction to seek (see remarks). + /// The position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. For such streams, the direction parameter defines whether to move the read or the write cursor. + virtual typename details::basic_streambuf<_CharType>::pos_type seekpos(typename details::basic_streambuf<_CharType>::pos_type pos, std::ios_base::openmode direction) + { + return get_base()->seekpos(pos, direction); + } + + /// + /// Seeks to a position given by a relative offset. + /// + /// The relative position to seek to + /// The starting point (beginning, end, current) for the seek. + /// The I/O direction to seek (see remarks) + /// The position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. + /// For such streams, the mode parameter defines whether to move the read or the write cursor. + virtual typename details::basic_streambuf<_CharType>::pos_type seekoff(typename details::basic_streambuf<_CharType>::off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode) + { + return get_base()->seekoff(offset, way, mode); + } + + /// + /// For output streams, flush any internally buffered data to the underlying medium. + /// + /// true if the flush succeeds, false if not + virtual pplx::task sync() + { + return get_base()->sync(); + } + + /// + /// Retrieves the stream buffer exception_ptr if it has been set. + /// + /// Pointer to the exception, if it has been set; otherwise, nullptr will be returned + virtual std::exception_ptr exception() const + { + return get_base()->exception(); + } + + private: + std::shared_ptr> m_buffer; + + }; + +}} diff --git a/3rdparty/cpprestsdk/include/cpprest/asyncrt_utils.h b/3rdparty/cpprestsdk/include/cpprest/asyncrt_utils.h new file mode 100644 index 00000000..eb162451 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/asyncrt_utils.h @@ -0,0 +1,593 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Various common utilities. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "pplx/pplxtasks.h" +#include "cpprest/details/basic_types.h" + +#if !defined(_WIN32) || (_MSC_VER >= 1700) +#include +#endif + +#ifndef _WIN32 +#include +#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269 +#include +#endif +#endif + +/// Various utilities for string conversions and date and time manipulation. +namespace utility +{ + +// Left over from VS2010 support, remains to avoid breaking. +typedef std::chrono::seconds seconds; + +/// Functions for converting to/from std::chrono::seconds to xml string. +namespace timespan +{ + /// + /// Converts a timespan/interval in seconds to xml duration string as specified by + /// http://www.w3.org/TR/xmlschema-2/#duration + /// + _ASYNCRTIMP utility::string_t __cdecl seconds_to_xml_duration(utility::seconds numSecs); + + /// + /// Converts an xml duration to timespan/interval in seconds + /// http://www.w3.org/TR/xmlschema-2/#duration + /// + _ASYNCRTIMP utility::seconds __cdecl xml_duration_to_seconds(const utility::string_t ×panString); +} + +/// Functions for Unicode string conversions. +namespace conversions +{ + /// + /// Converts a UTF-16 string to a UTF-8 string. + /// + /// A two byte character UTF-16 string. + /// A single byte character UTF-8 string. + _ASYNCRTIMP std::string __cdecl utf16_to_utf8(const utf16string &w); + + /// + /// Converts a UTF-8 string to a UTF-16 + /// + /// A single byte character UTF-8 string. + /// A two byte character UTF-16 string. + _ASYNCRTIMP utf16string __cdecl utf8_to_utf16(const std::string &s); + + /// + /// Converts a ASCII (us-ascii) string to a UTF-16 string. + /// + /// A single byte character us-ascii string. + /// A two byte character UTF-16 string. + _ASYNCRTIMP utf16string __cdecl usascii_to_utf16(const std::string &s); + + /// + /// Converts a Latin1 (iso-8859-1) string to a UTF-16 string. + /// + /// A single byte character UTF-8 string. + /// A two byte character UTF-16 string. + _ASYNCRTIMP utf16string __cdecl latin1_to_utf16(const std::string &s); + + /// + /// Converts a Latin1 (iso-8859-1) string to a UTF-8 string. + /// + /// A single byte character UTF-8 string. + /// A single byte character UTF-8 string. + _ASYNCRTIMP utf8string __cdecl latin1_to_utf8(const std::string &s); + + /// + /// Converts to a platform dependent Unicode string type. + /// + /// A single byte character UTF-8 string. + /// A platform dependent string type. + _ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string &&s); + + /// + /// Converts to a platform dependent Unicode string type. + /// + /// A two byte character UTF-16 string. + /// A platform dependent string type. + _ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string &&s); + + /// + /// Converts to a platform dependent Unicode string type. + /// + /// A single byte character UTF-8 string. + /// A platform dependent string type. + _ASYNCRTIMP utility::string_t __cdecl to_string_t(const std::string &s); + + /// + /// Converts to a platform dependent Unicode string type. + /// + /// A two byte character UTF-16 string. + /// A platform dependent string type. + _ASYNCRTIMP utility::string_t __cdecl to_string_t(const utf16string &s); + + /// + /// Converts to a UTF-16 from string. + /// + /// A single byte character UTF-8 string. + /// A two byte character UTF-16 string. + _ASYNCRTIMP utf16string __cdecl to_utf16string(const std::string &value); + + /// + /// Converts to a UTF-16 from string. + /// + /// A two byte character UTF-16 string. + /// A two byte character UTF-16 string. + _ASYNCRTIMP utf16string __cdecl to_utf16string(utf16string value); + + /// + /// Converts to a UTF-8 string. + /// + /// A single byte character UTF-8 string. + /// A single byte character UTF-8 string. + _ASYNCRTIMP std::string __cdecl to_utf8string(std::string value); + + /// + /// Converts to a UTF-8 string. + /// + /// A two byte character UTF-16 string. + /// A single byte character UTF-8 string. + _ASYNCRTIMP std::string __cdecl to_utf8string(const utf16string &value); + + /// + /// Encode the given byte array into a base64 string + /// + _ASYNCRTIMP utility::string_t __cdecl to_base64(const std::vector& data); + + /// + /// Encode the given 8-byte integer into a base64 string + /// + _ASYNCRTIMP utility::string_t __cdecl to_base64(uint64_t data); + + /// + /// Decode the given base64 string to a byte array + /// + _ASYNCRTIMP std::vector __cdecl from_base64(const utility::string_t& str); + + template + utility::string_t print_string(const Source &val, const std::locale &loc) + { + utility::ostringstream_t oss; + oss.imbue(loc); + oss << val; + if (oss.bad()) + { + throw std::bad_cast(); + } + return oss.str(); + } + + template + utility::string_t print_string(const Source &val) + { + return print_string(val, std::locale()); + } + + template + Target scan_string(const utility::string_t &str, const std::locale &loc) + { + Target t; + utility::istringstream_t iss(str); + iss.imbue(loc); + iss >> t; + if (iss.bad()) + { + throw std::bad_cast(); + } + return t; + } + + template + Target scan_string(const utility::string_t &str) + { + return scan_string(str, std::locale()); + } +} + +namespace details +{ + /// + /// Cross platform RAII container for setting thread local locale. + /// + class scoped_c_thread_locale + { + public: + _ASYNCRTIMP scoped_c_thread_locale(); + _ASYNCRTIMP ~scoped_c_thread_locale(); + +#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269 +#ifdef _WIN32 + typedef _locale_t xplat_locale; +#else + typedef locale_t xplat_locale; +#endif + + static _ASYNCRTIMP xplat_locale __cdecl c_locale(); +#endif + private: +#ifdef _WIN32 + std::string m_prevLocale; + int m_prevThreadSetting; +#elif !(defined(ANDROID) || defined(__ANDROID__)) + locale_t m_prevLocale; +#endif + scoped_c_thread_locale(const scoped_c_thread_locale &); + scoped_c_thread_locale & operator=(const scoped_c_thread_locale &); + }; + + /// + /// Our own implementation of alpha numeric instead of std::isalnum to avoid + /// taking global lock for performance reasons. + /// + inline bool __cdecl is_alnum(char ch) + { + return (ch >= '0' && ch <= '9') + || (ch >= 'A' && ch <= 'Z') + || (ch >= 'a' && ch <= 'z'); + } + + /// + /// Simplistic implementation of make_unique. A better implementation would be based on variadic templates + /// and therefore not be compatible with Dev10. + /// + template + std::unique_ptr<_Type> make_unique() { + return std::unique_ptr<_Type>(new _Type()); + } + + template + std::unique_ptr<_Type> make_unique(_Arg1&& arg1) { + return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1))); + } + + template + std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2) { + return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2))); + } + + template + std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3) { + return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3))); + } + + /// + /// Cross platform utility function for performing case insensitive string comparision. + /// + /// First string to compare. + /// Second strong to compare. + /// true if the strings are equivalent, false otherwise + inline bool str_icmp(const utility::string_t &left, const utility::string_t &right) + { +#ifdef _WIN32 + return _wcsicmp(left.c_str(), right.c_str()) == 0; +#else + return boost::iequals(left, right); +#endif + } + +#ifdef _WIN32 + +/// +/// Category error type for Windows OS errors. +/// +class windows_category_impl : public std::error_category +{ +public: + virtual const char *name() const CPPREST_NOEXCEPT { return "windows"; } + + _ASYNCRTIMP virtual std::string message(int errorCode) const CPPREST_NOEXCEPT; + + _ASYNCRTIMP virtual std::error_condition default_error_condition(int errorCode) const CPPREST_NOEXCEPT; +}; + +/// +/// Gets the one global instance of the windows error category. +/// +/// An error category instance. +_ASYNCRTIMP const std::error_category & __cdecl windows_category(); + +#else + +/// +/// Gets the one global instance of the linux error category. +/// +/// An error category instance. +_ASYNCRTIMP const std::error_category & __cdecl linux_category(); + +#endif + +/// +/// Gets the one global instance of the current platform's error category. +/// +_ASYNCRTIMP const std::error_category & __cdecl platform_category(); + +/// +/// Creates an instance of std::system_error from a OS error code. +/// +inline std::system_error __cdecl create_system_error(unsigned long errorCode) +{ + std::error_code code((int)errorCode, platform_category()); + return std::system_error(code, code.message()); +} + +/// +/// Creates a std::error_code from a OS error code. +/// +inline std::error_code __cdecl create_error_code(unsigned long errorCode) +{ + return std::error_code((int)errorCode, platform_category()); +} + +/// +/// Creates the corresponding error message from a OS error code. +/// +inline utility::string_t __cdecl create_error_message(unsigned long errorCode) +{ + return utility::conversions::to_string_t(create_error_code(errorCode).message()); +} + +} + +class datetime +{ +public: + typedef uint64_t interval_type; + + /// + /// Defines the supported date and time string formats. + /// + enum date_format { RFC_1123, ISO_8601 }; + + /// + /// Returns the current UTC time. + /// + static _ASYNCRTIMP datetime __cdecl utc_now(); + + /// + /// An invalid UTC timestamp value. + /// + enum:interval_type { utc_timestamp_invalid = static_cast(-1) }; + + /// + /// Returns seconds since Unix/POSIX time epoch at 01-01-1970 00:00:00. + /// If time is before epoch, utc_timestamp_invalid is returned. + /// + static interval_type utc_timestamp() + { + const auto seconds = utc_now().to_interval() / _secondTicks; + if (seconds >= 11644473600LL) + { + return seconds - 11644473600LL; + } + else + { + return utc_timestamp_invalid; + } + } + + datetime() : m_interval(0) + { + } + + /// + /// Creates datetime from a string representing time in UTC in RFC 1123 format. + /// + /// Returns a datetime of zero if not successful. + static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123); + + /// + /// Returns a string representation of the datetime. + /// + _ASYNCRTIMP utility::string_t to_string(date_format format = RFC_1123) const; + + /// + /// Returns the integral time value. + /// + interval_type to_interval() const + { + return m_interval; + } + + datetime operator- (interval_type value) const + { + return datetime(m_interval - value); + } + + datetime operator+ (interval_type value) const + { + return datetime(m_interval + value); + } + + bool operator== (datetime dt) const + { + return m_interval == dt.m_interval; + } + + bool operator!= (const datetime& dt) const + { + return !(*this == dt); + } + + static interval_type from_milliseconds(unsigned int milliseconds) + { + return milliseconds*_msTicks; + } + + static interval_type from_seconds(unsigned int seconds) + { + return seconds*_secondTicks; + } + + static interval_type from_minutes(unsigned int minutes) + { + return minutes*_minuteTicks; + } + + static interval_type from_hours(unsigned int hours) + { + return hours*_hourTicks; + } + + static interval_type from_days(unsigned int days) + { + return days*_dayTicks; + } + + bool is_initialized() const + { + return m_interval != 0; + } + +private: + + friend int operator- (datetime t1, datetime t2); + + static const interval_type _msTicks = static_cast(10000); + static const interval_type _secondTicks = 1000*_msTicks; + static const interval_type _minuteTicks = 60*_secondTicks; + static const interval_type _hourTicks = 60*60*_secondTicks; + static const interval_type _dayTicks = 24*60*60*_secondTicks; + + +#ifdef _WIN32 + // void* to avoid pulling in windows.h + static _ASYNCRTIMP bool __cdecl datetime::system_type_to_datetime(/*SYSTEMTIME*/ void* psysTime, uint64_t seconds, datetime * pdt); +#else + static datetime timeval_to_datetime(const timeval &time); +#endif + + // Private constructor. Use static methods to create an instance. + datetime(interval_type interval) : m_interval(interval) + { + } + + // Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns. + interval_type m_interval; +}; + +#ifndef _WIN32 + +// temporary workaround for the fact that +// utf16char is not fully supported in GCC +class cmp +{ +public: + + static int icmp(std::string left, std::string right) + { + size_t i; + for (i = 0; i < left.size(); ++i) + { + if (i == right.size()) return 1; + + auto l = cmp::tolower(left[i]); + auto r = cmp::tolower(right[i]); + if (l > r) return 1; + if (l < r) return -1; + } + if (i < right.size()) return -1; + return 0; + } + +private: + static char tolower(char c) + { + if (c >= 'A' && c <= 'Z') + return static_cast(c - 'A' + 'a'); + return c; + } +}; + +#endif + +inline int operator- (datetime t1, datetime t2) +{ + auto diff = (t1.m_interval - t2.m_interval); + + // Round it down to seconds + diff /= 10 * 1000 * 1000; + + return static_cast(diff); +} + +/// +/// Nonce string generator class. +/// +class nonce_generator +{ +public: + + /// + /// Define default nonce length. + /// + enum { default_length = 32 }; + + /// + /// Nonce generator constructor. + /// + /// Length of the generated nonce string. + nonce_generator(int length=default_length) : + m_random(static_cast(utility::datetime::utc_timestamp())), + m_length(length) + {} + + /// + /// Generate a nonce string containing random alphanumeric characters (A-Za-z0-9). + /// Length of the generated string is set by length(). + /// + /// The generated nonce string. + _ASYNCRTIMP utility::string_t generate(); + + /// + /// Get length of generated nonce string. + /// + /// Nonce string length. + int length() const { return m_length; } + + /// + /// Set length of the generated nonce string. + /// + /// Lenght of nonce string. + void set_length(int length) { m_length = length; } + +private: + static const utility::string_t c_allowed_chars; + std::mt19937 m_random; + int m_length; +}; + +} // namespace utility; diff --git a/3rdparty/cpprestsdk/include/cpprest/base_uri.h b/3rdparty/cpprestsdk/include/cpprest/base_uri.h new file mode 100644 index 00000000..f3327796 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/base_uri.h @@ -0,0 +1,450 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Protocol independent support for URIs. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "cpprest/asyncrt_utils.h" +#include "cpprest/details/basic_types.h" + +namespace web { + namespace details + { + struct uri_components + { + uri_components() : m_path(_XPLATSTR("/")), m_port(-1) + {} + + uri_components(const uri_components &other) : + m_scheme(other.m_scheme), + m_host(other.m_host), + m_user_info(other.m_user_info), + m_path(other.m_path), + m_query(other.m_query), + m_fragment(other.m_fragment), + m_port(other.m_port) + {} + + uri_components & operator=(const uri_components &other) + { + if (this != &other) + { + m_scheme = other.m_scheme; + m_host = other.m_host; + m_user_info = other.m_user_info; + m_path = other.m_path; + m_query = other.m_query; + m_fragment = other.m_fragment; + m_port = other.m_port; + } + return *this; + } + + uri_components(uri_components &&other) CPPREST_NOEXCEPT : + m_scheme(std::move(other.m_scheme)), + m_host(std::move(other.m_host)), + m_user_info(std::move(other.m_user_info)), + m_path(std::move(other.m_path)), + m_query(std::move(other.m_query)), + m_fragment(std::move(other.m_fragment)), + m_port(other.m_port) + {} + + uri_components & operator=(uri_components &&other) CPPREST_NOEXCEPT + { + if (this != &other) + { + m_scheme = std::move(other.m_scheme); + m_host = std::move(other.m_host); + m_user_info = std::move(other.m_user_info); + m_path = std::move(other.m_path); + m_query = std::move(other.m_query); + m_fragment = std::move(other.m_fragment); + m_port = other.m_port; + } + return *this; + } + + _ASYNCRTIMP utility::string_t join(); + + utility::string_t m_scheme; + utility::string_t m_host; + utility::string_t m_user_info; + utility::string_t m_path; + utility::string_t m_query; + utility::string_t m_fragment; + int m_port; + }; + } + + /// + /// A single exception type to represent errors in parsing, encoding, and decoding URIs. + /// + class uri_exception : public std::exception + { + public: + + uri_exception(std::string msg) : m_msg(std::move(msg)) {} + + ~uri_exception() CPPREST_NOEXCEPT {} + + const char* what() const CPPREST_NOEXCEPT + { + return m_msg.c_str(); + } + + private: + std::string m_msg; + }; + + /// + /// A flexible, protocol independent URI implementation. + /// + /// URI instances are immutable. Querying the various fields on an emtpy URI will return empty strings. Querying + /// various diagnostic members on an empty URI will return false. + /// + /// + /// This implementation accepts both URIs ('http://msn.com/path') and URI relative-references + /// ('/path?query#frag'). + /// + /// This implementation does not provide any scheme-specific handling -- an example of this + /// would be the following: 'http://path1/path'. This is a valid URI, but it's not a valid + /// http-uri -- that is, it's syntactically correct but does not conform to the requirements + /// of the http scheme (http requires a host). + /// We could provide this by allowing a pluggable 'scheme' policy-class, which would provide + /// extra capability for validating and canonicalizing a URI according to scheme, and would + /// introduce a layer of type-safety for URIs of differing schemes, and thus differing semantics. + /// + /// One issue with implementing a scheme-independent URI facility is that of comparing for equality. + /// For instance, these URIs are considered equal 'http://msn.com', 'http://msn.com:80'. That is -- + /// the 'default' port can be either omitted or explicit. Since we don't have a way to map a scheme + /// to it's default port, we don't have a way to know these are equal. This is just one of a class of + /// issues with regard to scheme-specific behavior. + /// + class uri + { + public: + + /// + /// The various components of a URI. This enum is used to indicate which + /// URI component is being encoded to the encode_uri_component. This allows + /// specific encoding to be performed. + /// + /// Scheme and port don't allow '%' so they don't need to be encoded. + /// + class components + { + public: + enum component + { + user_info, + host, + path, + query, + fragment, + full_uri + }; + }; + + /// + /// Encodes a URI component according to RFC 3986. + /// Note if a full URI is specified instead of an individual URI component all + /// characters not in the unreserved set are escaped. + /// + /// The URI as a string. + /// The encoded string. + _ASYNCRTIMP static utility::string_t __cdecl encode_uri(const utility::string_t &raw, uri::components::component = components::full_uri); + + /// + /// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their + /// hexadecimal representation. + /// + /// The UTF-8 string data. + /// The encoded string. + _ASYNCRTIMP static utility::string_t __cdecl encode_data_string(const utility::string_t &utf8data); + + /// + /// Decodes an encoded string. + /// + /// The URI as a string. + /// The decoded string. + _ASYNCRTIMP static utility::string_t __cdecl decode(const utility::string_t &encoded); + + /// + /// Splits a path into its hierarchical components. + /// + /// The path as a string + /// A std::vector<utility::string_t> containing the segments in the path. + _ASYNCRTIMP static std::vector __cdecl split_path(const utility::string_t &path); + + /// + /// Splits a query into its key-value components. + /// + /// The query string + /// A std::map<utility::string_t, utility::string_t> containing the key-value components of the query. + _ASYNCRTIMP static std::map __cdecl split_query(const utility::string_t &query); + + /// + /// Validates a string as a URI. + /// + /// The URI string to be validated. + /// true if the given string represents a valid URI, false otherwise. + _ASYNCRTIMP static bool __cdecl validate(const utility::string_t &uri_string); + + /// + /// Creates an empty uri + /// + uri() { m_uri = _XPLATSTR("/");}; + + /// + /// Creates a URI from the given encoded string. This will throw an exception if the string + /// does not contain a valid URI. Use uri::validate if processing user-input. + /// + /// A pointer to an encoded string to create the URI instance. + _ASYNCRTIMP uri(const utility::char_t *uri_string); + + /// + /// Creates a URI from the given encoded string. This will throw an exception if the string + /// does not contain a valid URI. Use uri::validate if processing user-input. + /// + /// An encoded URI string to create the URI instance. + _ASYNCRTIMP uri(const utility::string_t &uri_string); + + /// + /// Copy constructor. + /// + uri(const uri &other) : + m_uri(other.m_uri), + m_components(other.m_components) + {} + + /// + /// Copy assignment operator. + /// + uri & operator=(const uri &other) + { + if (this != &other) + { + m_uri = other.m_uri; + m_components = other.m_components; + } + return *this; + } + + /// + /// Move constructor. + /// + uri(uri &&other) CPPREST_NOEXCEPT : + m_uri(std::move(other.m_uri)), + m_components(std::move(other.m_components)) + {} + + /// + /// Move assignment operator + /// + uri & operator=(uri &&other) CPPREST_NOEXCEPT + { + if (this != &other) + { + m_uri = std::move(other.m_uri); + m_components = std::move(other.m_components); + } + return *this; + } + + /// + /// Get the scheme component of the URI as an encoded string. + /// + /// The URI scheme as a string. + const utility::string_t &scheme() const { return m_components.m_scheme; } + + /// + /// Get the user information component of the URI as an encoded string. + /// + /// The URI user information as a string. + const utility::string_t &user_info() const { return m_components.m_user_info; } + + /// + /// Get the host component of the URI as an encoded string. + /// + /// The URI host as a string. + const utility::string_t &host() const { return m_components.m_host; } + + /// + /// Get the port component of the URI. Returns -1 if no port is specified. + /// + /// The URI port as an integer. + int port() const { return m_components.m_port; } + + /// + /// Get the path component of the URI as an encoded string. + /// + /// The URI path as a string. + const utility::string_t &path() const { return m_components.m_path; } + + /// + /// Get the query component of the URI as an encoded string. + /// + /// The URI query as a string. + const utility::string_t &query() const { return m_components.m_query; } + + /// + /// Get the fragment component of the URI as an encoded string. + /// + /// The URI fragment as a string. + const utility::string_t &fragment() const { return m_components.m_fragment; } + + /// + /// Creates a new uri object with the same authority portion as this one, omitting the resource and query portions. + /// + /// The new uri object with the same authority. + _ASYNCRTIMP uri authority() const; + + /// + /// Gets the path, query, and fragment portion of this uri, which may be empty. + /// + /// The new URI object with the path, query and fragment portion of this URI. + _ASYNCRTIMP uri resource() const; + + /// + /// An empty URI specifies no components, and serves as a default value + /// + bool is_empty() const + { + return this->m_uri.empty() || this->m_uri == _XPLATSTR("/"); + } + + /// + /// A loopback URI is one which refers to a hostname or ip address with meaning only on the local machine. + /// + /// + /// Examples include "locahost", or ip addresses in the loopback range (127.0.0.0/24). + /// + /// true if this URI references the local host, false otherwise. + bool is_host_loopback() const + { + return !is_empty() && ((host() == _XPLATSTR("localhost")) || (host().size() > 4 && host().substr(0,4) == _XPLATSTR("127."))); + } + + /// + /// A wildcard URI is one which refers to all hostnames that resolve to the local machine (using the * or +) + /// + /// + /// http://*:80 + /// + bool is_host_wildcard() const + { + return !is_empty() && (this->host() == _XPLATSTR("*") || this->host() == _XPLATSTR("+")); + } + + /// + /// A portable URI is one with a hostname that can be resolved globally (used from another machine). + /// + /// true if this URI can be resolved globally (used from another machine), false otherwise. + /// + /// The hostname "localhost" is a reserved name that is guaranteed to resolve to the local machine, + /// and cannot be used for inter-machine communication. Likewise the hostnames "*" and "+" on Windows + /// represent wildcards, and do not map to a resolvable address. + /// + bool is_host_portable() const + { + return !(is_empty() || is_host_loopback() || is_host_wildcard()); + } + + // + /// A default port is one where the port is unspecified, and will be determined by the operating system. + /// The choice of default port may be dictated by the scheme (http -> 80) or not. + /// + /// true if this URI instance has a default port, false otherwise. + bool is_port_default() const + { + return !is_empty() && this->port() == 0; + } + + /// + /// An "authority" URI is one with only a scheme, optional userinfo, hostname, and (optional) port. + /// + /// true if this is an "authority" URI, false otherwise. + bool is_authority() const + { + return !is_empty() && is_path_empty() && query().empty() && fragment().empty(); + } + + /// + /// Returns whether the other URI has the same authority as this one + /// + /// The URI to compare the authority with. + /// true if both the URI's have the same authority, false otherwise. + bool has_same_authority(const uri &other) const + { + return !is_empty() && this->authority() == other.authority(); + } + + /// + /// Returns whether the path portion of this URI is empty + /// + /// true if the path portion of this URI is empty, false otherwise. + bool is_path_empty() const + { + return path().empty() || path() == _XPLATSTR("/"); + } + + /// + /// Returns the full (encoded) URI as a string. + /// + /// The full encoded URI string. + utility::string_t to_string() const + { + return m_uri; + } + + _ASYNCRTIMP bool operator == (const uri &other) const; + + bool operator < (const uri &other) const + { + return m_uri < other.m_uri; + } + + bool operator != (const uri &other) const + { + return !(this->operator == (other)); + } + + private: + friend class uri_builder; + + // Encodes all characters not in given set determined by given function. + static utility::string_t encode_impl(const utility::string_t &raw, const std::function& should_encode); + + utility::string_t m_uri; + details::uri_components m_components; + }; + +} // namespace web diff --git a/3rdparty/cpprestsdk/include/cpprest/containerstream.h b/3rdparty/cpprestsdk/include/cpprest/containerstream.h new file mode 100644 index 00000000..5a9f4c1e --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/containerstream.h @@ -0,0 +1,632 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* This file defines a basic STL-container-based stream buffer. Reading from the buffer will not remove any data +* from it and seeking is thus supported. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#include +#include +#include +#include + +#include "pplx/pplxtasks.h" +#include "cpprest/astreambuf.h" +#include "cpprest/streams.h" + +namespace Concurrency { namespace streams { + + // Forward declarations + + template class container_buffer; + + namespace details { + + /// + /// The basic_container_buffer class serves as a memory-based steam buffer that supports writing or reading + /// sequences of characters. + /// The class itself should not be used in application code, it is used by the stream definitions farther down in the header file. + /// + /// When closed, neither writing nor reading is supported any longer. basic_container_buffer does not support simultaneous use of the buffer + /// for reading and writing. + template + class basic_container_buffer : public streams::details::streambuf_state_manager + { + public: + typedef typename _CollectionType::value_type _CharType; + typedef typename basic_streambuf<_CharType>::traits traits; + typedef typename basic_streambuf<_CharType>::int_type int_type; + typedef typename basic_streambuf<_CharType>::pos_type pos_type; + typedef typename basic_streambuf<_CharType>::off_type off_type; + + /// + /// Returns the underlying data container + /// + _CollectionType& collection() + { + return m_data; + } + + /// + /// Destructor + /// + virtual ~basic_container_buffer() + { + // Invoke the synchronous versions since we need to + // purge the request queue before deleting the buffer + this->_close_read(); + this->_close_write(); + } + + + protected: + /// + /// can_seek is used to determine whether a stream buffer supports seeking. + /// + virtual bool can_seek() const { return this->is_open(); } + + /// + /// has_size is used to determine whether a stream buffer supports size(). + /// + virtual bool has_size() const { return this->is_open(); } + + /// + /// Gets the size of the stream, if known. Calls to has_size will determine whether + /// the result of size can be relied on. + /// + virtual utility::size64_t size() const + { + return utility::size64_t(m_data.size()); + } + + /// + /// Get the stream buffer size, if one has been set. + /// + /// The direction of buffering (in or out) + /// An implementation that does not support buffering will always return '0'. + virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const + { + return 0; + } + + /// + /// Sets the stream buffer implementation to buffer or not buffer. + /// + /// The size to use for internal buffering, 0 if no buffering should be done. + /// The direction of buffering (in or out) + /// An implementation that does not support buffering will silently ignore calls to this function and it will not have any effect on what is returned by subsequent calls to . + virtual void set_buffer_size(size_t , std::ios_base::openmode = std::ios_base::in) + { + return; + } + + /// + /// For any input stream, in_avail returns the number of characters that are immediately available + /// to be consumed without blocking. May be used in conjunction with to read data without + /// incurring the overhead of using tasks. + /// + virtual size_t in_avail() const + { + // See the comment in seek around the restriction that we do not allow read head to + // seek beyond the current write_end. + _ASSERTE(m_current_position <= m_data.size()); + + msl::safeint3::SafeInt readhead(m_current_position); + msl::safeint3::SafeInt writeend(m_data.size()); + return (size_t)(writeend - readhead); + } + + virtual pplx::task _sync() + { + return pplx::task_from_result(true); + } + + virtual pplx::task _putc(_CharType ch) + { + int_type retVal = (this->write(&ch, 1) == 1) ? static_cast(ch) : traits::eof(); + return pplx::task_from_result(retVal); + } + + virtual pplx::task _putn(const _CharType *ptr, size_t count) + { + return pplx::task_from_result(this->write(ptr, count)); + } + + /// + /// Allocates a contiguous memory block and returns it. + /// + /// The number of characters to allocate. + /// A pointer to a block to write to, null if the stream buffer implementation does not support alloc/commit. + _CharType* _alloc(size_t count) + { + if (!this->can_write()) return nullptr; + + // Allocate space + resize_for_write(m_current_position+count); + + // Let the caller copy the data + return (_CharType*)&m_data[m_current_position]; + } + + /// + /// Submits a block already allocated by the stream buffer. + /// + /// The number of characters to be committed. + void _commit(size_t actual ) + { + // Update the write position and satisfy any pending reads + update_current_position(m_current_position+actual); + } + + /// + /// Gets a pointer to the next already allocated contiguous block of data. + /// + /// A reference to a pointer variable that will hold the address of the block on success. + /// The number of contiguous characters available at the address in 'ptr.' + /// true if the operation succeeded, false otherwise. + /// + /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that + /// there is no block to return immediately or that the stream buffer does not support the operation. + /// The stream buffer may not de-allocate the block until is called. + /// If the end of the stream is reached, the function will return true, a null pointer, and a count of zero; + /// a subsequent read will not succeed. + /// + virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count) + { + ptr = nullptr; + count = 0; + + if (!this->can_read()) return false; + + count = in_avail(); + + if (count > 0) + { + ptr = (_CharType*)&m_data[m_current_position]; + return true; + } + else + { + // Can only be open for read OR write, not both. If there is no data then + // we have reached the end of the stream so indicate such with true. + return true; + } + } + + /// + /// Releases a block of data acquired using . This frees the stream buffer to de-allocate the + /// memory, if it so desires. Move the read position ahead by the count. + /// + /// A pointer to the block of data to be released. + /// The number of characters that were read. + virtual void release(_Out_writes_opt_ (count) _CharType *ptr, _In_ size_t count) + { + if (ptr != nullptr) + update_current_position(m_current_position + count); + } + + virtual pplx::task _getn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) + { + return pplx::task_from_result(this->read(ptr, count)); + } + + size_t _sgetn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) + { + return this->read(ptr, count); + } + + virtual size_t _scopy(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) + { + return this->read(ptr, count, false); + } + + virtual pplx::task _bumpc() + { + return pplx::task_from_result(this->read_byte(true)); + } + + virtual int_type _sbumpc() + { + return this->read_byte(true); + } + + virtual pplx::task _getc() + { + return pplx::task_from_result(this->read_byte(false)); + } + + int_type _sgetc() + { + return this->read_byte(false); + } + + virtual pplx::task _nextc() + { + this->read_byte(true); + return pplx::task_from_result(this->read_byte(false)); + } + + virtual pplx::task _ungetc() + { + auto pos = seekoff(-1, std::ios_base::cur, std::ios_base::in); + if ( pos == (pos_type)traits::eof()) + return pplx::task_from_result(traits::eof()); + return this->getc(); + } + + /// + /// Gets the current read or write position in the stream. + /// + /// The I/O direction to seek (see remarks) + /// The current position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. + /// For such streams, the direction parameter defines whether to move the read or the write cursor. + virtual pos_type getpos(std::ios_base::openmode mode) const + { + if ( ((mode & std::ios_base::in) && !this->can_read()) || + ((mode & std::ios_base::out) && !this->can_write())) + return static_cast(traits::eof()); + + return static_cast(m_current_position); + } + + /// + /// Seeks to the given position. + /// + /// The offset from the beginning of the stream. + /// The I/O direction to seek (see remarks). + /// The position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. For such streams, the direction parameter defines whether to move the read or the write cursor. + virtual pos_type seekpos(pos_type position, std::ios_base::openmode mode) + { + pos_type beg(0); + + // In order to support relative seeking from the end position we need to fix an end position. + // Technically, there is no end for the stream buffer as new writes would just expand the buffer. + // For now, we assume that the current write_end is the end of the buffer. We use this artificial + // end to restrict the read head from seeking beyond what is available. + + pos_type end(m_data.size()); + + if (position >= beg) + { + auto pos = static_cast(position); + + // Read head + if ((mode & std::ios_base::in) && this->can_read()) + { + if (position <= end) + { + // We do not allow reads to seek beyond the end or before the start position. + update_current_position(pos); + return static_cast(m_current_position); + } + } + + // Write head + if ((mode & std::ios_base::out) && this->can_write()) + { + // Allocate space + resize_for_write(pos); + + // Nothing to really copy + + // Update write head and satisfy read requests if any + update_current_position(pos); + + return static_cast(m_current_position); + } + } + + return static_cast(traits::eof()); + } + + /// + /// Seeks to a position given by a relative offset. + /// + /// The relative position to seek to + /// The starting point (beginning, end, current) for the seek. + /// The I/O direction to seek (see remarks) + /// The position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. + /// For such streams, the mode parameter defines whether to move the read or the write cursor. + virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode) + { + pos_type beg = 0; + pos_type cur = static_cast(m_current_position); + pos_type end = static_cast(m_data.size()); + + switch ( way ) + { + case std::ios_base::beg: + return seekpos(beg + offset, mode); + + case std::ios_base::cur: + return seekpos(cur + offset, mode); + + case std::ios_base::end: + return seekpos(end + offset, mode); + + default: + return static_cast(traits::eof()); + } + } + + private: + template friend class streams::container_buffer; + + /// + /// Constructor + /// + basic_container_buffer(std::ios_base::openmode mode) + : streambuf_state_manager(mode), + m_current_position(0) + { + validate_mode(mode); + } + + /// + /// Constructor + /// + basic_container_buffer(_CollectionType data, std::ios_base::openmode mode) + : streambuf_state_manager(mode), + m_data(std::move(data)), + m_current_position((mode & std::ios_base::in) ? 0 : m_data.size()) + { + validate_mode(mode); + } + + static void validate_mode(std::ios_base::openmode mode) + { + // Disallow simultaneous use of the stream buffer for writing and reading. + if ((mode & std::ios_base::in) && (mode & std::ios_base::out)) + throw std::invalid_argument("this combination of modes on container stream not supported"); + } + + /// + /// Determine if the request can be satisfied. + /// + bool can_satisfy(size_t) + { + // We can always satisfy a read, at least partially, unless the + // read position is at the very end of the buffer. + return (in_avail() > 0); + } + + /// + /// Reads a byte from the stream and returns it as int_type. + /// Note: This routine shall only be called if can_satisfy() returned true. + /// + int_type read_byte(bool advance = true) + { + _CharType value; + auto read_size = this->read(&value, 1, advance); + return read_size == 1 ? static_cast(value) : traits::eof(); + } + + /// + /// Reads up to count characters into ptr and returns the count of characters copied. + /// The return value (actual characters copied) could be <= count. + /// Note: This routine shall only be called if can_satisfy() returned true. + /// + size_t read(_Out_writes_ (count) _CharType *ptr, _In_ size_t count, bool advance = true) + { + if (!can_satisfy(count)) + return 0; + + msl::safeint3::SafeInt request_size(count); + msl::safeint3::SafeInt read_size = request_size.Min(in_avail()); + + size_t newPos = m_current_position + read_size; + + auto readBegin = begin(m_data) + m_current_position; + auto readEnd = begin(m_data) + newPos; + +#ifdef _WIN32 + // Avoid warning C4996: Use checked iterators under SECURE_SCL + std::copy(readBegin, readEnd, stdext::checked_array_iterator<_CharType *>(ptr, count)); +#else + std::copy(readBegin, readEnd, ptr); +#endif // _WIN32 + + if (advance) + { + update_current_position(newPos); + } + + return (size_t) read_size; + } + + /// + /// Write count characters from the ptr into the stream buffer + /// + size_t write(const _CharType *ptr, size_t count) + { + if (!this->can_write() || (count == 0)) return 0; + + auto newSize = m_current_position + count; + + // Allocate space + resize_for_write(newSize); + + // Copy the data + std::copy(ptr, ptr + count, begin(m_data) + m_current_position); + + // Update write head and satisfy pending reads if any + update_current_position(newSize); + + return count; + } + + /// + /// Resize the underlying container to match the new write head + /// + void resize_for_write(size_t newPos) + { + // Resize the container if required + if (newPos > m_data.size()) + { + m_data.resize(newPos); + } + } + + /// + /// Updates the write head to the new position + /// + void update_current_position(size_t newPos) + { + // The new write head + m_current_position = newPos; + _ASSERTE(m_current_position <= m_data.size()); + } + + // The actual data store + _CollectionType m_data; + + // Read/write head + size_t m_current_position; + }; + + } // namespace details + + /// + /// The basic_container_buffer class serves as a memory-based steam buffer that supports writing or reading + /// sequences of characters. Note that it cannot be used as a consumer producer buffer. + /// + /// + /// The type of the container. + /// + /// + /// This is a reference-counted version of basic_container_buffer. + /// + template + class container_buffer : public streambuf + { + public: + typedef typename _CollectionType::value_type char_type; + + /// + /// Creates a container_buffer given a collection, copying its data into the buffer. + /// + /// The collection that is the starting point for the buffer + /// The I/O mode that the buffer should use (in / out) + container_buffer(_CollectionType data, std::ios_base::openmode mode = std::ios_base::in) + : streambuf( + std::shared_ptr>(new streams::details::basic_container_buffer<_CollectionType>(std::move(data), mode))) + { + } + + /// + /// Creates a container_buffer starting from an empty collection. + /// + /// The I/O mode that the buffer should use (in / out) + container_buffer(std::ios_base::openmode mode = std::ios_base::out) + : streambuf( + std::shared_ptr>(new details::basic_container_buffer<_CollectionType>(mode))) + { + } + + _CollectionType& collection() const + { + auto listBuf = static_cast *>(this->get_base().get()); + return listBuf->collection(); + } + }; + + /// + /// A static class to allow users to create input and out streams based off STL + /// collections. The sole purpose of this class to avoid users from having to know + /// anything about stream buffers. + /// + /// The type of the STL collection. + template + class container_stream + { + public: + + typedef typename _CollectionType::value_type char_type; + typedef container_buffer<_CollectionType> buffer_type; + + /// + /// Creates an input stream given an STL container. + /// + /// STL container to back the input stream. + /// An input stream. + static concurrency::streams::basic_istream open_istream(_CollectionType data) + { + return concurrency::streams::basic_istream(buffer_type(std::move(data), std::ios_base::in)); + } + + /// + /// Creates an output stream using an STL container as the storage. + /// + /// An output stream. + static concurrency::streams::basic_ostream open_ostream() + { + return concurrency::streams::basic_ostream(buffer_type(std::ios_base::out)); + } + }; + + /// + /// The stringstream allows an input stream to be constructed from std::string or std::wstring + /// For output streams the underlying string container could be retrieved using buf->collection(). + /// + typedef container_stream> stringstream; + typedef stringstream::buffer_type stringstreambuf; + + typedef container_stream wstringstream; + typedef wstringstream::buffer_type wstringstreambuf; + + /// + /// The bytestream is a static class that allows an input stream to be constructed from any STL container. + /// + class bytestream + { + public: + + /// + /// Creates a single byte character input stream given an STL container. + /// + /// The type of the STL collection. + /// STL container to back the input stream. + /// An single byte character input stream. + template + static concurrency::streams::istream open_istream(_CollectionType data) + { + return concurrency::streams::istream(streams::container_buffer<_CollectionType>(std::move(data), std::ios_base::in)); + } + + /// + /// Creates a single byte character output stream using an STL container as storage. + /// + /// The type of the STL collection. + /// A single byte character output stream. + template + static concurrency::streams::ostream open_ostream() + { + return concurrency::streams::ostream(streams::container_buffer<_CollectionType>()); + } +}; + + +}} // namespaces diff --git a/3rdparty/cpprestsdk/include/cpprest/details/SafeInt3.hpp b/3rdparty/cpprestsdk/include/cpprest/details/SafeInt3.hpp new file mode 100644 index 00000000..5f40af5b --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/SafeInt3.hpp @@ -0,0 +1,7047 @@ +/*----------------------------------------------------------------------------------------------------------- +SafeInt.hpp +Version 3.0.18p + +This software is licensed under the Microsoft Public License (Ms-PL). +For more information about Microsoft open source licenses, refer to +http://www.microsoft.com/opensource/licenses.mspx + +This license governs use of the accompanying software. If you use the software, you accept this license. +If you do not accept the license, do not use the software. + +Definitions +The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here +as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to +the software. A "contributor" is any person that distributes its contribution under this license. +"Licensed patents" are a contributor's patent claims that read directly on its contribution. + +Grant of Rights +(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations +in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to +reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution +or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in +section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed +patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution +in the software or derivative works of the contribution in the software. + +Conditions and Limitations +(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, + or trademarks. +(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the + software, your patent license from such contributor to the software ends automatically. +(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and + attribution notices that are present in the software. +(D) If you distribute any portion of the software in source code form, you may do so only under this license + by including a complete copy of this license with your distribution. If you distribute any portion of the + software in compiled or object code form, you may only do so under a license that complies with this license. +(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, + guarantees, or conditions. You may have additional consumer rights under your local laws which this license + cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties + of merchantability, fitness for a particular purpose and non-infringement. + + +Copyright (c) Microsoft Corporation. All rights reserved. + +This header implements an integer handling class designed to catch +unsafe integer operations + +This header compiles properly at Wall on Visual Studio, -Wall on gcc, and -Weverything on clang. + +Please read the leading comments before using the class. +---------------------------------------------------------------*/ +#pragma once + +// It is a bit tricky to sort out what compiler we are actually using, +// do this once here, and avoid cluttering the code +#define VISUAL_STUDIO_COMPILER 0 +#define CLANG_COMPILER 1 +#define GCC_COMPILER 2 +#define UNKNOWN_COMPILER -1 + +// Clang will sometimes pretend to be Visual Studio +// and does pretend to be gcc. Check it first, as nothing else pretends to be clang +#if defined __clang__ +#define SAFEINT_COMPILER CLANG_COMPILER +#elif defined __GNUC__ +#define SAFEINT_COMPILER GCC_COMPILER +#elif defined _MSC_VER +#define SAFEINT_COMPILER VISUAL_STUDIO_COMPILER +#else +#define SAFEINT_COMPILER UNKNOWN_COMPILER +#endif + +// Enable compiling with /Wall under VC +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning( push ) +// Disable warnings coming from headers +#pragma warning( disable:4987 4820 4987 4820 ) + +#endif + +// Need this for ptrdiff_t on some compilers +#include +#include + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER && defined _M_AMD64 + #include + #define SAFEINT_USE_INTRINSICS 1 +#else + #define SAFEINT_USE_INTRINSICS 0 +#endif + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning( pop ) +#endif + +// Various things needed for GCC +#if SAFEINT_COMPILER == GCC_COMPILER || SAFEINT_COMPILER == CLANG_COMPILER + +#define NEEDS_INT_DEFINED + +#if !defined NULL +#define NULL 0 +#endif + +// GCC warning suppression +#if SAFEINT_COMPILER == GCC_COMPILER +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#endif + +#include + +// clang only +#if SAFEINT_COMPILER == CLANG_COMPILER + +#if __has_feature(cxx_nullptr) + #define NEEDS_NULLPTR_DEFINED 0 +#endif + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++11-long-long" +#pragma clang diagnostic ignored "-Wold-style-cast" +#endif + +#endif + +// If the user made a choice, respect it #if !defined +#if !defined NEEDS_NULLPTR_DEFINED + // Visual Studio 2010 and higher support this + #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER + #if (_MSC_VER < 1600) + #define NEEDS_NULLPTR_DEFINED 1 + #else + #define NEEDS_NULLPTR_DEFINED 0 + #endif + #else + // Let everything else trigger based on whether we use c++11 or above + #if __cplusplus >= 201103L + #define NEEDS_NULLPTR_DEFINED 0 + #else + #define NEEDS_NULLPTR_DEFINED 1 + #endif + #endif +#endif + +#if NEEDS_NULLPTR_DEFINED +#define nullptr NULL +#endif + +#ifndef C_ASSERT +#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] +#endif + +// Let's test some assumptions +// We're assuming two's complement negative numbers +C_ASSERT( -1 == static_cast(0xffffffff) ); + +/************* Compiler Options ***************************************************************************************************** + +SafeInt supports several compile-time options that can change the behavior of the class. + +Compiler options: +SAFEINT_WARN_64BIT_PORTABILITY - this re-enables various warnings that happen when /Wp64 is used. Enabling this option is not + recommended. +NEEDS_INT_DEFINED - if your compiler does not support __int8, __int16, __int32 and __int64, you can enable this. +SAFEINT_ASSERT_ON_EXCEPTION - it is often easier to stop on an assert and figure out a problem than to try and figure out + how you landed in the catch block. +SafeIntDefaultExceptionHandler - if you'd like to replace the exception handlers SafeInt provides, define your replacement and + define this. Note - two built in (Windows-specific) options exist: + - SAFEINT_FAILFAST - bypasses all exception handlers, exits the app with an exception + - SAFEINT_RAISE_EXCEPTION - throws Win32 exceptions, which can be caught +SAFEINT_DISALLOW_UNSIGNED_NEGATION - Invoking the unary negation operator creates warnings, but if you'd like it to completely fail + to compile, define this. +ANSI_CONVERSIONS - This changes the class to use default comparison behavior, which may be unsafe. Enabling this + option is not recommended. +SAFEINT_DISABLE_BINARY_ASSERT - binary AND, OR or XOR operations on mixed size types can produce unexpected results. If you do + this, the default is to assert. Set this if you prefer not to assert under these conditions. +SIZE_T_CAST_NEEDED - some compilers complain if there is not a cast to size_t, others complain if there is one. + This lets you not have your compiler complain. +SAFEINT_DISABLE_SHIFT_ASSERT - Set this option if you don't want to assert when shifting more bits than the type has. Enabling + this option is not recommended. + +************************************************************************************************************************************/ + +/* +* The SafeInt class is designed to have as low an overhead as possible +* while still ensuring that all integer operations are conducted safely. +* Nearly every operator has been overloaded, with a very few exceptions. +* +* A usability-safety trade-off has been made to help ensure safety. This +* requires that every operation return either a SafeInt or a bool. If we +* allowed an operator to return a base integer type T, then the following +* can happen: +* +* char i = SafeInt(32) * 2 + SafeInt(16) * 4; +* +* The * operators take precedence, get overloaded, return a char, and then +* you have: +* +* char i = (char)64 + (char)64; //overflow! +* +* This situation would mean that safety would depend on usage, which isn't +* acceptable. +* +* One key operator that is missing is an implicit cast to type T. The reason for +* this is that if there is an implicit cast operator, then we end up with +* an ambiguous compile-time precedence. Because of this amiguity, there +* are two methods that are provided: +* +* Casting operators for every native integer type +* Version 3 note - it now compiles correctly for size_t without warnings +* +* SafeInt::Ptr() - returns the address of the internal integer +* Note - the '&' (address of) operator has been overloaded and returns +* the address of the internal integer. +* +* The SafeInt class should be used in any circumstances where ensuring +* integrity of the calculations is more important than performance. See Performance +* Notes below for additional information. +* +* Many of the conditionals will optimize out or be inlined for a release +* build (especially with /Ox), but it does have significantly more overhead, +* especially for signed numbers. If you do not _require_ negative numbers, use +* unsigned integer types - certain types of problems cannot occur, and this class +* performs most efficiently. +* +* Here's an example of when the class should ideally be used - +* +* void* AllocateMemForStructs(int StructSize, int HowMany) +* { +* SafeInt s(StructSize); +* +* s *= HowMany; +* +* return malloc(s); +* +* } +* +* Here's when it should NOT be used: +* +* void foo() +* { +* int i; +* +* for(i = 0; i < 0xffff; i++) +* .... +* } +* +* Error handling - a SafeInt class will throw exceptions if something +* objectionable happens. The exceptions are SafeIntException classes, +* which contain an enum as a code. +* +* Typical usage might be: +* +* bool foo() +* { +* SafeInt s; //note that s == 0 unless set +* +* try{ +* s *= 23; +* .... +* } +* catch(SafeIntException err) +* { +* //handle errors here +* } +* } +* +* Update for 3.0 - the exception class is now a template parameter. +* You can replace the exception class with any exception class you like. This is accomplished by: +* 1) Create a class that has the following interface: +* + template <> class YourSafeIntExceptionHandler < YourException > + { + public: + static __declspec(noreturn) void __stdcall SafeIntOnOverflow() + { + throw YourException( YourSafeIntArithmeticOverflowError ); + } + + static __declspec(noreturn) void __stdcall SafeIntOnDivZero() + { + throw YourException( YourSafeIntDivideByZeroError ); + } + }; +* +* Note that you don't have to throw C++ exceptions, you can throw Win32 exceptions, or do +* anything you like, just don't return from the call back into the code. +* +* 2) Either explicitly declare SafeInts like so: +* SafeInt< int, YourSafeIntExceptionHandler > si; +* or +* #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler +* +* Performance: +* +* Due to the highly nested nature of this class, you can expect relatively poor +* performance in unoptimized code. In tests of optimized code vs. correct inline checks +* in native code, this class has been found to take approximately 8% more CPU time (this varies), +* most of which is due to exception handling. Solutions: +* +* 1) Compile optimized code - /Ox is best, /O2 also performs well. Interestingly, /O1 +* (optimize for size) does not work as well. +* 2) If that 8% hit is really a serious problem, walk through the code and inline the +* exact same checks as the class uses. +* 3) Some operations are more difficult than others - avoid using signed integers, and if +* possible keep them all the same size. 64-bit integers are also expensive. Mixing +* different integer sizes and types may prove expensive. Be aware that literals are +* actually ints. For best performance, cast literals to the type desired. +* +* +* Performance update +* The current version of SafeInt uses template specialization to force the compiler to invoke only the +* operator implementation needed for any given pair of types. This will dramatically improve the perf +* of debug builds. +* +* 3.0 update - not only have we maintained the specialization, there were some cases that were overly complex, +* and using some additional cases (e.g. signed __int64 and unsigned __int64) resulted in some simplification. +* Additionally, there was a lot of work done to better optimize the 64-bit multiplication. +* +* Binary Operators +* +* All of the binary operators have certain assumptions built into the class design. +* This is to ensure correctness. Notes on each class of operator follow: +* +* Arithmetic Operators (*,/,+,-,%) +* There are three possible variants: +* SafeInt< T, E > op SafeInt< T, E > +* SafeInt< T, E > op U +* U op SafeInt< T, E > +* +* The SafeInt< T, E > op SafeInt< U, E > variant is explicitly not supported, and if you try to do +* this the compiler with throw the following error: +* +* error C2593: 'operator *' is ambiguous +* +* This is because the arithmetic operators are required to return a SafeInt of some type. +* The compiler cannot know whether you'd prefer to get a type T or a type U returned. If +* you need to do this, you need to extract the value contained within one of the two using +* the casting operator. For example: +* +* SafeInt< T, E > t, result; +* SafeInt< U, E > u; +* +* result = t * (U)u; +* +* Comparison Operators +* Because each of these operators return type bool, mixing SafeInts of differing types is +* allowed. +* +* Shift Operators +* Shift operators always return the type on the left hand side of the operator. Mixed type +* operations are allowed because the return type is always known. +* +* Boolean Operators +* Like comparison operators, these overloads always return type bool, and mixed-type SafeInts +* are allowed. Additionally, specific overloads exist for type bool on both sides of the +* operator. +* +* Binary Operators +* Mixed-type operations are discouraged, however some provision has been made in order to +* enable things like: +* +* SafeInt c = 2; +* +* if(c & 0x02) +* ... +* +* The "0x02" is actually an int, and it needs to work. +* In the case of binary operations on integers smaller than 32-bit, or of mixed type, corner +* cases do exist where you could get unexpected results. In any case where SafeInt returns a different +* result than the underlying operator, it will call assert(). You should examine your code and cast things +* properly so that you are not programming with side effects. +* +* Documented issues: +* +* This header compiles correctly at /W4 using VC++ 8 (Version 14.00.50727.42) and later. +* As of this writing, I believe it will also work for VC 7.1, but not for VC 7.0 or below. +* If you need a version that will work with lower level compilers, try version 1.0.7. None +* of them work with Visual C++ 6, and gcc didn't work very well, either, though this hasn't +* been tried recently. +* +* It is strongly recommended that any code doing integer manipulation be compiled at /W4 +* - there are a number of warnings which pertain to integer manipulation enabled that are +* not enabled at /W3 (default for VC++) +* +* Perf note - postfix operators are slightly more costly than prefix operators. +* Unless you're actually assigning it to something, ++SafeInt is less expensive than SafeInt++ +* +* The comparison operator behavior in this class varies from the ANSI definition, which is +* arguably broken. As an example, consider the following: +* +* unsigned int l = 0xffffffff; +* char c = -1; +* +* if(c == l) +* printf("Why is -1 equal to 4 billion???\n"); +* +* The problem here is that c gets cast to an int, now has a value of 0xffffffff, and then gets +* cast again to an unsigned int, losing the true value. This behavior is despite the fact that +* an __int64 exists, and the following code will yield a different (and intuitively correct) +* answer: +* +* if((__int64)c == (__int64)l)) +* printf("Why is -1 equal to 4 billion???\n"); +* else +* printf("Why doesn't the compiler upcast to 64-bits when needed?\n"); +* +* Note that combinations with smaller integers won't display the problem - if you +* changed "unsigned int" above to "unsigned short", you'd get the right answer. +* +* If you prefer to retain the ANSI standard behavior insert +* #define ANSI_CONVERSIONS +* into your source. Behavior differences occur in the following cases: +* 8, 16, and 32-bit signed int, unsigned 32-bit int +* any signed int, unsigned 64-bit int +* Note - the signed int must be negative to show the problem +* +* +* Revision history: +* +* Oct 12, 2003 - Created +* Author - David LeBlanc - dleblanc@microsoft.com +* +* Oct 27, 2003 - fixed numerous items pointed out by michmarc and bdawson +* Dec 28, 2003 - 1.0 +* added support for mixed-type operations +* thanks to vikramh +* also fixed broken __int64 multiplication section +* added extended support for mixed-type operations where possible +* Jan 28, 2004 - 1.0.1 +* changed WCHAR to wchar_t +* fixed a construct in two mixed-type assignment overloads that was +* not compiling on some compilers +* Also changed name of private method to comply with standards on +* reserved names +* Thanks to Niels Dekker for the input +* Feb 12, 2004 - 1.0.2 +* Minor changes to remove dependency on Windows headers +* Consistently used __int16, __int32 and __int64 to ensure +* portability +* May 10, 2004 - 1.0.3 +* Corrected bug in one case of GreaterThan +* July 22, 2004 - 1.0.4 +* Tightened logic in addition check (saving 2 instructions) +* Pulled error handler out into function to enable user-defined replacement +* Made internal type of SafeIntException an enum (as per Niels' suggestion) +* Added casts for base integer types (as per Scott Meyers' suggestion) +* Updated usage information - see important new perf notes. +* Cleaned up several const issues (more thanks to Niels) +* +* Oct 1, 2004 - 1.0.5 +* Added support for SEH exceptions instead of C++ exceptions - Win32 only +* Made handlers for DIV0 and overflows individually overridable +* Commented out the destructor - major perf gains here +* Added cast operator for type long, since long != __int32 +* Corrected a couple of missing const modifiers +* Fixed broken >= and <= operators for type U op SafeInt< T, E > +* Nov 5, 2004 - 1.0.6 +* Implemented new logic in binary operators to resolve issues with +* implicit casts +* Fixed casting operator because char != signed char +* Defined __int32 as int instead of long +* Removed unsafe SafeInt::Value method +* Re-implemented casting operator as a result of removing Value method +* Dec 1, 2004 - 1.0.7 +* Implemented specialized operators for pointer arithmetic +* Created overloads for cases of U op= SafeInt. What you do with U +* after that may be dangerous. +* Fixed bug in corner case of MixedSizeModulus +* Fixed bug in MixedSizeMultiply and MixedSizeDivision with input of 0 +* Added throw() decorations +* +* Apr 12, 2005 - 2.0 +* Extensive revisions to leverage template specialization. +* April, 2007 Extensive revisions for version 3.0 +* Nov 22, 2009 Forked from MS internal code +* Changes needed to support gcc compiler - many thanks to Niels Dekker +* for determining not just the issues, but also suggesting fixes. +* Also updating some of the header internals to be the same as the upcoming Visual Studio version. +* +* Jan 16, 2010 64-bit gcc has long == __int64, which means that many of the existing 64-bit +* templates are over-specialized. This forces a redefinition of all the 64-bit +* multiplication routines to use pointers instead of references for return +* values. Also, let's use some intrinsics for x64 Microsoft compiler to +* reduce code size, and hopefully improve efficiency. +* +* June 21, 2014 Better support for clang, higher warning levels supported for all 3 primary supported + compilers (Visual Studio, clang, gcc). + Also started to converge the code base such that the public CodePlex version will + be a drop-in replacement for the Visual Studio version. + +* Note about code style - throughout this class, casts will be written using C-style (T), +* not C++ style static_cast< T >. This is because the class is nearly always dealing with integer +* types, and in this case static_cast and a C cast are equivalent. Given the large number of casts, +* the code is a little more readable this way. In the event a cast is needed where static_cast couldn't +* be substituted, we'll use the new templatized cast to make it explicit what the operation is doing. +* +************************************************************************************************************ +* Version 3.0 changes: +* +* 1) The exception type thrown is now replacable, and you can throw your own exception types. This should help +* those using well-developed exception classes. +* 2) The 64-bit multiplication code has had a lot of perf work done, and should be faster than 2.0. +* 3) There is now limited floating point support. You can initialize a SafeInt with a floating point type, +* and you can cast it out (or assign) to a float as well. +* 4) There is now an Align method. I noticed people use this a lot, and rarely check errors, so now you have one. +* +* Another major improvement is the addition of external functions - if you just want to check an operation, this can now happen: +* All of the following can be invoked without dealing with creating a class, or managing exceptions. This is especially handy +* for 64-bit porting, since SafeCast compiles away for a 32-bit cast from size_t to unsigned long, but checks it for 64-bit. +* +* inline bool SafeCast( const T From, U& To ) throw() +* inline bool SafeEquals( const T t, const U u ) throw() +* inline bool SafeNotEquals( const T t, const U u ) throw() +* inline bool SafeGreaterThan( const T t, const U u ) throw() +* inline bool SafeGreaterThanEquals( const T t, const U u ) throw() +* inline bool SafeLessThan( const T t, const U u ) throw() +* inline bool SafeLessThanEquals( const T t, const U u ) throw() +* inline bool SafeModulus( const T& t, const U& u, T& result ) throw() +* inline bool SafeMultiply( T t, U u, T& result ) throw() +* inline bool SafeDivide( T t, U u, T& result ) throw() +* inline bool SafeAdd( T t, U u, T& result ) throw() +* inline bool SafeSubtract( T t, U u, T& result ) throw() +* +*/ + +//use these if the compiler does not support _intXX +#ifdef NEEDS_INT_DEFINED +#define __int8 char +#define __int16 short +#define __int32 int +#define __int64 long long +#endif + +namespace msl +{ + +namespace safeint3 +{ + +// catch these to handle errors +// Currently implemented code values: +// ERROR_ARITHMETIC_OVERFLOW +// EXCEPTION_INT_DIVIDE_BY_ZERO +enum SafeIntError +{ + SafeIntNoError = 0, + SafeIntArithmeticOverflow, + SafeIntDivideByZero +}; + +} // safeint3 +} // msl + + +/* +* Error handler classes +* Using classes to deal with exceptions is going to allow the most +* flexibility, and we can mix different error handlers in the same project +* or even the same file. It isn't advisable to do this in the same function +* because a SafeInt< int, MyExceptionHandler > isn't the same thing as +* SafeInt< int, YourExceptionHander >. +* If for some reason you have to translate between the two, cast one of them back to its +* native type. +* +* To use your own exception class with SafeInt, first create your exception class, +* which may look something like the SafeIntException class below. The second step is to +* create a template specialization that implements SafeIntOnOverflow and SafeIntOnDivZero. +* For example: +* +* template <> class SafeIntExceptionHandler < YourExceptionClass > +* { +* static __declspec(noreturn) void __stdcall SafeIntOnOverflow() +* { +* throw YourExceptionClass( EXCEPTION_INT_OVERFLOW ); +* } +* +* static __declspec(noreturn) void __stdcall SafeIntOnDivZero() +* { +* throw YourExceptionClass( EXCEPTION_INT_DIVIDE_BY_ZERO ); +* } +* }; +* +* typedef SafeIntExceptionHandler < YourExceptionClass > YourSafeIntExceptionHandler +* You'd then declare your SafeInt objects like this: +* SafeInt< int, YourSafeIntExceptionHandler > +* +* Unfortunately, there is no such thing as partial template specialization in typedef +* statements, so you have three options if you find this cumbersome: +* +* 1) Create a holder class: +* +* template < typename T > +* class MySafeInt +* { +* public: +* SafeInt< T, MyExceptionClass> si; +* }; +* +* You'd then declare an instance like so: +* MySafeInt< int > i; +* +* You'd lose handy things like initialization - it would have to be initialized as: +* +* i.si = 0; +* +* 2) You could create a typedef for every int type you deal with: +* +* typedef SafeInt< int, MyExceptionClass > MySafeInt; +* typedef SafeInt< char, MyExceptionClass > MySafeChar; +* +* and so on. The second approach is probably more usable, and will just drop into code +* better, which is the original intent of the SafeInt class. +* +* 3) If you're going to consistently use a different class to handle your exceptions, +* you can override the default typedef like so: +* +* #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler +* +* Overall, this is probably the best approach. +* */ + +// On the Microsoft compiler, violating a throw() annotation is a silent error. +// Other compilers might turn these into exceptions, and some users may want to not have throw() enabled. +// In addition, some error handlers may not throw C++ exceptions, which makes everything no throw. +#if defined SAFEINT_REMOVE_NOTHROW +#define SAFEINT_NOTHROW +#else +#define SAFEINT_NOTHROW throw() +#endif + +namespace msl +{ + +namespace safeint3 +{ + +// If you would like to use your own custom assert +// Define SAFEINT_ASSERT +#if !defined SAFEINT_ASSERT +#include +#define SAFEINT_ASSERT(x) assert(x) +#endif + +#if defined SAFEINT_ASSERT_ON_EXCEPTION + inline void SafeIntExceptionAssert() SAFEINT_NOTHROW { SAFEINT_ASSERT(false); } +#else + inline void SafeIntExceptionAssert() SAFEINT_NOTHROW {} +#endif + +#if SAFEINT_COMPILER == GCC_COMPILER || SAFEINT_COMPILER == CLANG_COMPILER + #define SAFEINT_NORETURN __attribute__((noreturn)) + #define SAFEINT_STDCALL + #define SAFEINT_VISIBLE __attribute__ ((__visibility__("default"))) + #define SAFEINT_WEAK __attribute__ ((weak)) +#else + #define SAFEINT_NORETURN __declspec(noreturn) + #define SAFEINT_STDCALL __stdcall + #define SAFEINT_VISIBLE + #define SAFEINT_WEAK +#endif + +class SAFEINT_VISIBLE SafeIntException +{ +public: + SafeIntException() SAFEINT_NOTHROW { m_code = SafeIntNoError; } + SafeIntException( SafeIntError code ) SAFEINT_NOTHROW + { + m_code = code; + } + SafeIntError m_code; +}; + +namespace SafeIntInternal +{ + // Visual Studio version of SafeInt provides for two possible error + // handlers: + // SafeIntErrorPolicy_SafeIntException - C++ exception, default if not otherwise defined + // SafeIntErrorPolicy_InvalidParameter - Calls fail fast (Windows-specific), bypasses any exception handlers, + // exits the app with a crash + template < typename E > class SafeIntExceptionHandler; + + template <> class SafeIntExceptionHandler < SafeIntException > + { + public: + + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() + { + SafeIntExceptionAssert(); + throw SafeIntException( SafeIntArithmeticOverflow ); + } + + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() + { + SafeIntExceptionAssert(); + throw SafeIntException( SafeIntDivideByZero ); + } + }; + +#if !defined _CRT_SECURE_INVALID_PARAMETER + // Calling fail fast is somewhat more robust than calling abort, + // but abort is the closest we can manage without Visual Studio support + // Need the header for abort() + #include + #define _CRT_SECURE_INVALID_PARAMETER(msg) abort() +#endif + + class SafeInt_InvalidParameter + { + public: + static SAFEINT_NORETURN void SafeIntOnOverflow() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + _CRT_SECURE_INVALID_PARAMETER("SafeInt Arithmetic Overflow"); + } + + static SAFEINT_NORETURN void SafeIntOnDivZero() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + _CRT_SECURE_INVALID_PARAMETER("SafeInt Divide By Zero"); + } + }; + +#if defined _WINDOWS_ + + class SafeIntWin32ExceptionHandler + { + public: + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + RaiseException( static_cast(EXCEPTION_INT_OVERFLOW), EXCEPTION_NONCONTINUABLE, 0, 0); + } + + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + RaiseException( static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), EXCEPTION_NONCONTINUABLE, 0, 0); + } + }; + +#endif + +} // namespace SafeIntInternal + +// both of these have cross-platform support +typedef SafeIntInternal::SafeIntExceptionHandler < SafeIntException > CPlusPlusExceptionHandler; +typedef SafeIntInternal::SafeInt_InvalidParameter InvalidParameterExceptionHandler; + +// This exception handler is no longer recommended, but is left here in order not to break existing users +#if defined _WINDOWS_ +typedef SafeIntInternal::SafeIntWin32ExceptionHandler Win32ExceptionHandler; +#endif + +// For Visual Studio compatibility +#if defined VISUAL_STUDIO_SAFEINT_COMPAT + typedef CPlusPlusExceptionHandler SafeIntErrorPolicy_SafeIntException; + typedef InvalidParameterExceptionHandler SafeIntErrorPolicy_InvalidParameter; +#endif + +// If the user hasn't defined a default exception handler, +// define one now, depending on whether they would like Win32 or C++ exceptions + +// This library will use conditional noexcept soon, but not in this release +// Some users might mix exception handlers, which is not advised, but is supported +#if !defined SafeIntDefaultExceptionHandler + #if defined SAFEINT_RAISE_EXCEPTION + #if !defined _WINDOWS_ + #error Include windows.h in order to use Win32 exceptions + #endif + + #define SafeIntDefaultExceptionHandler Win32ExceptionHandler + #elif defined SAFEINT_FAILFAST + #define SafeIntDefaultExceptionHandler InvalidParameterExceptionHandler + #else + #define SafeIntDefaultExceptionHandler CPlusPlusExceptionHandler + #if !defined SAFEINT_EXCEPTION_HANDLER_CPP + #define SAFEINT_EXCEPTION_HANDLER_CPP 1 + #endif + #endif +#endif + +#if !defined SAFEINT_EXCEPTION_HANDLER_CPP +#define SAFEINT_EXCEPTION_HANDLER_CPP 0 +#endif + +// If an error handler is chosen other than C++ exceptions, such as Win32 exceptions, fail fast, +// or abort, then all methods become no throw. Some teams track throw() annotations closely, +// and the following option provides for this. +#if SAFEINT_EXCEPTION_HANDLER_CPP +#define SAFEINT_CPP_THROW +#else +#define SAFEINT_CPP_THROW SAFEINT_NOTHROW +#endif + +// Turns out we can fool the compiler into not seeing compile-time constants with +// a simple template specialization +template < int method > class CompileConst; +template <> class CompileConst { public: static bool Value() SAFEINT_NOTHROW { return true; } }; +template <> class CompileConst { public: static bool Value() SAFEINT_NOTHROW { return false; } }; + +// The following template magic is because we're now not allowed +// to cast a float to an enum. This means that if we happen to assign +// an enum to a SafeInt of some type, it won't compile, unless we prevent +// isFloat = ( (T)( (float)1.1 ) > (T)1 ) +// from compiling in the case of an enum, which is the point of the specialization +// that follows. + +// If we have support for std, then we can do this easily, and detect enums as well +template < typename T > class NumericType; + +#if defined _LIBCPP_TYPE_TRAITS || defined _TYPE_TRAITS_ +// Continue to special case bool +template <> class NumericType { public: enum{ isBool = true, isFloat = false, isInt = false }; }; +template < typename T > class NumericType +{ + public: + enum + { + isBool = false, // We specialized out a bool + isFloat = std::is_floating_point::value, + // If it is an enum, then consider it an int type + // This does allow someone to make a SafeInt from an enum type, which is not recommended, + // but it also allows someone to add an enum value to a SafeInt, which is handy. + isInt = std::is_integral::value || std::is_enum::value + }; +}; + +#else + +template <> class NumericType { public: enum{ isBool = true, isFloat = false, isInt = false }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +#if defined SAFEINT_USE_WCHAR_T || defined _NATIVE_WCHAR_T_DEFINED +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +#endif +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType<__int64> { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = false, isInt = true }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = true, isInt = false }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = true, isInt = false }; }; +template <> class NumericType { public: enum{ isBool = false, isFloat = true, isInt = false }; }; +// Catch-all for anything not supported +template < typename T > class NumericType +{ +public: + // We have some unknown type, which could be an enum. For parity with the code that uses , + // We can try a static_cast - it if compiles, then it might be an enum, and should work. + // If it is something else that just happens to have a constructor that takes an int, and a casting operator, + // then it is possible something will go wrong, and for best results, cast it directly to an int before letting it + // interact with a SafeInt + + enum + { + isBool = false, + isFloat = false, + isInt = static_cast( static_cast(0) ) == 0 + }; +}; +#endif // type traits + +// Use this to avoid compile-time const truncation warnings +template < int fSigned, int bits > class SafeIntMinMax; + +template <> class SafeIntMinMax< true, 8 > { public: const static signed __int8 min = (-0x7f - 1); + const static signed __int8 max = 0x7f; }; +template <> class SafeIntMinMax< true, 16 > { public: const static __int16 min = ( -0x7fff - 1 ); + const static __int16 max = 0x7fff; }; +template <> class SafeIntMinMax< true, 32 > { public: const static __int32 min = ( -0x7fffffff -1 ); + const static __int32 max = 0x7fffffff; }; +template <> class SafeIntMinMax< true, 64 > { public: const static __int64 min = static_cast<__int64>(0x8000000000000000LL); + const static __int64 max = 0x7fffffffffffffffLL; }; + +template <> class SafeIntMinMax< false, 8 > { public: const static unsigned __int8 min = 0; + const static unsigned __int8 max = 0xff; }; +template <> class SafeIntMinMax< false, 16 > { public: const static unsigned __int16 min = 0; + const static unsigned __int16 max = 0xffff; }; +template <> class SafeIntMinMax< false, 32 > { public: const static unsigned __int32 min = 0; + const static unsigned __int32 max = 0xffffffff; }; +template <> class SafeIntMinMax< false, 64 > { public: const static unsigned __int64 min = 0; + const static unsigned __int64 max = 0xffffffffffffffffULL; }; + +template < typename T > class IntTraits +{ +public: + C_ASSERT( NumericType::isInt ); + enum + { + isSigned = ( (T)(-1) < 0 ), + is64Bit = ( sizeof(T) == 8 ), + is32Bit = ( sizeof(T) == 4 ), + is16Bit = ( sizeof(T) == 2 ), + is8Bit = ( sizeof(T) == 1 ), + isLT32Bit = ( sizeof(T) < 4 ), + isLT64Bit = ( sizeof(T) < 8 ), + isInt8 = ( sizeof(T) == 1 && isSigned ), + isUint8 = ( sizeof(T) == 1 && !isSigned ), + isInt16 = ( sizeof(T) == 2 && isSigned ), + isUint16 = ( sizeof(T) == 2 && !isSigned ), + isInt32 = ( sizeof(T) == 4 && isSigned ), + isUint32 = ( sizeof(T) == 4 && !isSigned ), + isInt64 = ( sizeof(T) == 8 && isSigned ), + isUint64 = ( sizeof(T) == 8 && !isSigned ), + bitCount = ( sizeof(T)*8 ), + isBool = ( (T)2 == (T)1 ) + }; + + // On version 13.10 enums cannot define __int64 values + // so we'll use const statics instead! + // These must be cast to deal with the possibility of a SafeInt being given an enum as an argument + const static T maxInt = static_cast(SafeIntMinMax< isSigned, bitCount >::max); + const static T minInt = static_cast(SafeIntMinMax< isSigned, bitCount >::min); +}; + +template < typename T > +const T IntTraits< T >::maxInt; +template < typename T > +const T IntTraits< T >::minInt; + +template < typename T, typename U > class SafeIntCompare +{ +public: + enum + { + isBothSigned = (IntTraits< T >::isSigned && IntTraits< U >::isSigned), + isBothUnsigned = (!IntTraits< T >::isSigned && !IntTraits< U >::isSigned), + isLikeSigned = ((bool)(IntTraits< T >::isSigned) == (bool)(IntTraits< U >::isSigned)), + isCastOK = ((isLikeSigned && sizeof(T) >= sizeof(U)) || + (IntTraits< T >::isSigned && sizeof(T) > sizeof(U))), + isBothLT32Bit = (IntTraits< T >::isLT32Bit && IntTraits< U >::isLT32Bit), + isBothLT64Bit = (IntTraits< T >::isLT64Bit && IntTraits< U >::isLT64Bit) + }; +}; + +//all of the arithmetic operators can be solved by the same code within +//each of these regions without resorting to compile-time constant conditionals +//most operators collapse the problem into less than the 22 zones, but this is used +//as the first cut +//using this also helps ensure that we handle all of the possible cases correctly + +template < typename T, typename U > class IntRegion +{ +public: + enum + { + //unsigned-unsigned zone + IntZone_UintLT32_UintLT32 = SafeIntCompare< T,U >::isBothUnsigned && SafeIntCompare< T,U >::isBothLT32Bit, + IntZone_Uint32_UintLT64 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::is32Bit && IntTraits< U >::isLT64Bit, + IntZone_UintLT32_Uint32 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::isLT32Bit && IntTraits< U >::is32Bit, + IntZone_Uint64_Uint = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::is64Bit, + IntZone_UintLT64_Uint64 = SafeIntCompare< T,U >::isBothUnsigned && IntTraits< T >::isLT64Bit && IntTraits< U >::is64Bit, + //unsigned-signed + IntZone_UintLT32_IntLT32 = !IntTraits< T >::isSigned && IntTraits< U >::isSigned && SafeIntCompare< T,U >::isBothLT32Bit, + IntZone_Uint32_IntLT64 = IntTraits< T >::isUint32 && IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit, + IntZone_UintLT32_Int32 = !IntTraits< T >::isSigned && IntTraits< T >::isLT32Bit && IntTraits< U >::isInt32, + IntZone_Uint64_Int = IntTraits< T >::isUint64 && IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit, + IntZone_UintLT64_Int64 = !IntTraits< T >::isSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::isInt64, + IntZone_Uint64_Int64 = IntTraits< T >::isUint64 && IntTraits< U >::isInt64, + //signed-signed + IntZone_IntLT32_IntLT32 = SafeIntCompare< T,U >::isBothSigned && SafeIntCompare< T, U >::isBothLT32Bit, + IntZone_Int32_IntLT64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::is32Bit && IntTraits< U >::isLT64Bit, + IntZone_IntLT32_Int32 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isLT32Bit && IntTraits< U >::is32Bit, + IntZone_Int64_Int64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isInt64 && IntTraits< U >::isInt64, + IntZone_Int64_Int = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::is64Bit && IntTraits< U >::isLT64Bit, + IntZone_IntLT64_Int64 = SafeIntCompare< T,U >::isBothSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::is64Bit, + //signed-unsigned + IntZone_IntLT32_UintLT32 = IntTraits< T >::isSigned && !IntTraits< U >::isSigned && SafeIntCompare< T,U >::isBothLT32Bit, + IntZone_Int32_UintLT32 = IntTraits< T >::isInt32 && !IntTraits< U >::isSigned && IntTraits< U >::isLT32Bit, + IntZone_IntLT64_Uint32 = IntTraits< T >::isSigned && IntTraits< T >::isLT64Bit && IntTraits< U >::isUint32, + IntZone_Int64_UintLT64 = IntTraits< T >::isInt64 && !IntTraits< U >::isSigned && IntTraits< U >::isLT64Bit, + IntZone_Int_Uint64 = IntTraits< T >::isSigned && IntTraits< U >::isUint64 && IntTraits< T >::isLT64Bit, + IntZone_Int64_Uint64 = IntTraits< T >::isInt64 && IntTraits< U >::isUint64 + }; +}; + + +// In all of the following functions, we have two versions +// One for SafeInt, which throws C++ (or possibly SEH) exceptions +// The non-throwing versions are for use by the helper functions that return success and failure. +// Some of the non-throwing functions are not used, but are maintained for completeness. + +// There's no real alternative to duplicating logic, but keeping the two versions +// immediately next to one another will help reduce problems + + +// useful function to help with getting the magnitude of a negative number +enum AbsMethod +{ + AbsMethodInt, + AbsMethodInt64, + AbsMethodNoop +}; + +template < typename T > +class GetAbsMethod +{ +public: + enum + { + method = IntTraits< T >::isLT64Bit && IntTraits< T >::isSigned ? AbsMethodInt : + IntTraits< T >::isInt64 ? AbsMethodInt64 : AbsMethodNoop + }; +}; + +// let's go ahead and hard-code a dependency on the +// representation of negative numbers to keep compilers from getting overly +// happy with optimizing away things like -MIN_INT. +template < typename T, int > class AbsValueHelper; + +template < typename T > class AbsValueHelper < T, AbsMethodInt> +{ +public: + static unsigned __int32 Abs( T t ) SAFEINT_NOTHROW + { + SAFEINT_ASSERT( t < 0 ); + return ~(unsigned __int32)t + 1; + } +}; + +template < typename T > class AbsValueHelper < T, AbsMethodInt64 > +{ +public: + static unsigned __int64 Abs( T t ) SAFEINT_NOTHROW + { + SAFEINT_ASSERT( t < 0 ); + return ~(unsigned __int64)t + 1; + } +}; + +template < typename T > class AbsValueHelper < T, AbsMethodNoop > +{ +public: + static T Abs( T t ) SAFEINT_NOTHROW + { + // Why are you calling Abs on an unsigned number ??? + SAFEINT_ASSERT( false ); + return t; + } +}; + +template < typename T, bool > class NegationHelper; +// Previous versions had an assert that the type being negated was 32-bit or higher +// In retrospect, this seems like something to just document +// Negation will normally upcast to int +// For example -(unsigned short)0xffff == (int)0xffff0001 +// This class will retain the type, and will truncate, which may not be what +// you wanted +// If you want normal operator casting behavior, do this: +// SafeInt ss = 0xffff; +// then: +// -(SafeInt(ss)) +// will then emit a signed int with the correct value and bitfield + +template < typename T > class NegationHelper // Signed +{ +public: + template + static T NegativeThrow( T t ) SAFEINT_CPP_THROW + { + // corner case + if( t != IntTraits< T >::minInt ) + { + // cast prevents unneeded checks in the case of small ints + return -t; + } + E::SafeIntOnOverflow(); + } + + static bool Negative( T t, T& ret ) SAFEINT_NOTHROW + { + // corner case + if( t != IntTraits< T >::minInt ) + { + // cast prevents unneeded checks in the case of small ints + ret = -t; + return true; + } + return false; + } +}; + +// Helper classes to work keep compilers from +// optimizing away negation +template < typename T > class SignedNegation; + +template <> +class SignedNegation +{ +public: + static signed __int32 Value(unsigned __int64 in) SAFEINT_NOTHROW + { + return (signed __int32)(~(unsigned __int32)in + 1); + } + + static signed __int32 Value(unsigned __int32 in) SAFEINT_NOTHROW + { + return (signed __int32)(~in + 1); + } +}; + +template <> +class SignedNegation +{ +public: + static signed __int64 Value(unsigned __int64 in) SAFEINT_NOTHROW + { + return (signed __int64)(~in + 1); + } +}; + +template < typename T > class NegationHelper // unsigned +{ +public: + template + static T NegativeThrow( T t ) SAFEINT_CPP_THROW + { +#if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION + C_ASSERT( sizeof(T) == 0 ); +#endif + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning(push) +//this avoids warnings from the unary '-' operator being applied to unsigned numbers +#pragma warning(disable:4146) +#endif + // Note - this could be quenched on gcc + // by doing something like: + // return (T)-((__int64)t); + // but it seems like you would want a warning when doing this. + return (T)-t; + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning(pop) +#endif + } + + static bool Negative( T t, T& ret ) SAFEINT_NOTHROW + { + if( IntTraits::isLT32Bit ) + { + // See above + SAFEINT_ASSERT( false ); + } +#if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION + C_ASSERT( sizeof(T) == 0 ); +#endif + // Do it this way to avoid warning + ret = -t; + return true; + } +}; + +//core logic to determine casting behavior +enum CastMethod +{ + CastOK = 0, + CastCheckLTZero, + CastCheckGTMax, + CastCheckSafeIntMinMaxUnsigned, + CastCheckSafeIntMinMaxSigned, + CastToFloat, + CastFromFloat, + CastToBool, + CastFromBool +}; + + +template < typename ToType, typename FromType > +class GetCastMethod +{ +public: + enum + { + method = ( IntTraits< FromType >::isBool && + !IntTraits< ToType >::isBool ) ? CastFromBool : + + ( !IntTraits< FromType >::isBool && + IntTraits< ToType >::isBool ) ? CastToBool : + + ( SafeIntCompare< ToType, FromType >::isCastOK ) ? CastOK : + + ( ( IntTraits< ToType >::isSigned && + !IntTraits< FromType >::isSigned && + sizeof( FromType ) >= sizeof( ToType ) ) || + ( SafeIntCompare< ToType, FromType >::isBothUnsigned && + sizeof( FromType ) > sizeof( ToType ) ) ) ? CastCheckGTMax : + + ( !IntTraits< ToType >::isSigned && + IntTraits< FromType >::isSigned && + sizeof( ToType ) >= sizeof( FromType ) ) ? CastCheckLTZero : + + ( !IntTraits< ToType >::isSigned ) ? CastCheckSafeIntMinMaxUnsigned + : CastCheckSafeIntMinMaxSigned + }; +}; + +template < typename FromType > class GetCastMethod < float, FromType > +{ +public: + enum{ method = CastOK }; +}; + +template < typename FromType > class GetCastMethod < double, FromType > +{ +public: + enum{ method = CastOK }; +}; + +template < typename FromType > class GetCastMethod < long double, FromType > +{ +public: + enum{ method = CastOK }; +}; + +template < typename ToType > class GetCastMethod < ToType, float > +{ +public: + enum{ method = CastFromFloat }; +}; + +template < typename ToType > class GetCastMethod < ToType, double > +{ +public: + enum{ method = CastFromFloat }; +}; + +template < typename ToType > class GetCastMethod < ToType, long double > +{ +public: + enum{ method = CastFromFloat }; +}; + +template < typename T, typename U, int > class SafeCastHelper; + +template < typename T, typename U > class SafeCastHelper < T, U, CastOK > +{ +public: + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + t = (T)u; + return true; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + t = (T)u; + } +}; + +// special case floats and doubles +// tolerate loss of precision +template < typename T, typename U > class SafeCastHelper < T, U, CastFromFloat > +{ +public: + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + if( u <= (U)IntTraits< T >::maxInt && + u >= (U)IntTraits< T >::minInt ) + { + t = (T)u; + return true; + } + return false; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + if( u <= (U)IntTraits< T >::maxInt && + u >= (U)IntTraits< T >::minInt ) + { + t = (T)u; + return; + } + E::SafeIntOnOverflow(); + } +}; + +// Match on any method where a bool is cast to type T +template < typename T > class SafeCastHelper < T, bool, CastFromBool > +{ +public: + static bool Cast( bool b, T& t ) SAFEINT_NOTHROW + { + t = (T)( b ? 1 : 0 ); + return true; + } + + template < typename E > + static void CastThrow( bool b, T& t ) SAFEINT_CPP_THROW + { + t = (T)( b ? 1 : 0 ); + } +}; + +template < typename T > class SafeCastHelper < bool, T, CastToBool > +{ +public: + static bool Cast( T t, bool& b ) SAFEINT_NOTHROW + { + b = !!t; + return true; + } + + template < typename E > + static void CastThrow( bool b, T& t ) SAFEINT_CPP_THROW + { + b = !!t; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckLTZero > +{ +public: + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + if( u < 0 ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + if( u < 0 ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckGTMax > +{ +public: + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + if( u > (U)IntTraits< T >::maxInt ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + if( u > (U)IntTraits< T >::maxInt ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckSafeIntMinMaxUnsigned > +{ +public: + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + // U is signed - T could be either signed or unsigned + if( u > IntTraits< T >::maxInt || u < 0 ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + // U is signed - T could be either signed or unsigned + if( u > IntTraits< T >::maxInt || u < 0 ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckSafeIntMinMaxSigned > +{ +public: + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + // T, U are signed + if( u > IntTraits< T >::maxInt || u < IntTraits< T >::minInt ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + //T, U are signed + if( u > IntTraits< T >::maxInt || u < IntTraits< T >::minInt ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +//core logic to determine whether a comparison is valid, or needs special treatment +enum ComparisonMethod +{ + ComparisonMethod_Ok = 0, + ComparisonMethod_CastInt, + ComparisonMethod_CastInt64, + ComparisonMethod_UnsignedT, + ComparisonMethod_UnsignedU +}; + + // Note - the standard is arguably broken in the case of some integer + // conversion operations + // For example, signed char a = -1 = 0xff + // unsigned int b = 0xffffffff + // If you then test if a < b, a value-preserving cast + // is made, and you're essentially testing + // (unsigned int)a < b == false + // + // I do not think this makes sense - if you perform + // a cast to an __int64, which can clearly preserve both value and signedness + // then you get a different and intuitively correct answer + // IMHO, -1 should be less than 4 billion + // If you prefer to retain the ANSI standard behavior + // insert #define ANSI_CONVERSIONS into your source + // Behavior differences occur in the following cases: + // 8, 16, and 32-bit signed int, unsigned 32-bit int + // any signed int, unsigned 64-bit int + // Note - the signed int must be negative to show the problem + +template < typename T, typename U > +class ValidComparison +{ +public: + enum + { +#ifdef ANSI_CONVERSIONS + method = ComparisonMethod_Ok +#else + method = ( ( SafeIntCompare< T, U >::isLikeSigned ) ? ComparisonMethod_Ok : + ( ( IntTraits< T >::isSigned && sizeof(T) < 8 && sizeof(U) < 4 ) || + ( IntTraits< U >::isSigned && sizeof(T) < 4 && sizeof(U) < 8 ) ) ? ComparisonMethod_CastInt : + ( ( IntTraits< T >::isSigned && sizeof(U) < 8 ) || + ( IntTraits< U >::isSigned && sizeof(T) < 8 ) ) ? ComparisonMethod_CastInt64 : + ( !IntTraits< T >::isSigned ) ? ComparisonMethod_UnsignedT : + ComparisonMethod_UnsignedU ) +#endif + }; +}; + +template class EqualityTest; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_Ok > +{ +public: + static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( t == u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt > +{ +public: + static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( (int)t == (int)u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt64 > +{ +public: + static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( (__int64)t == (__int64)u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedT > +{ +public: + static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW + { + //one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( u < 0 ) + return false; + + //else safe to cast to type T + return ( t == (T)u ); + } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedU> +{ +public: + static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW + { + //one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( t < 0 ) + return false; + + //else safe to cast to type U + return ( (U)t == u ); + } +}; + +template class GreaterThanTest; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_Ok > +{ +public: + static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( t > u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt > +{ +public: + static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( (int)t > (int)u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt64 > +{ +public: + static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( (__int64)t > (__int64)u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedT > +{ +public: + static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW + { + // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( u < 0 ) + return true; + + // else safe to cast to type T + return ( t > (T)u ); + } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedU > +{ +public: + static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW + { + // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( t < 0 ) + return false; + + // else safe to cast to type U + return ( (U)t > u ); + } +}; + +// Modulus is simpler than comparison, but follows much the same logic +// using this set of functions, it can't fail except in a div 0 situation +template class ModulusHelper; + +template class ModulusHelper +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //trap corner case + if( CompileConst< IntTraits< U >::isSigned >::Value() ) + { + // Some compilers don't notice that this only compiles when u is signed + // Add cast to make them happy + if( u == (U)-1 ) + { + result = 0; + return SafeIntNoError; + } + } + + result = (T)(t % u); + return SafeIntNoError; + } + + template < typename E > + static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + //trap corner case + if( CompileConst< IntTraits< U >::isSigned >::Value() ) + { + if( u == (U)-1 ) + { + result = 0; + return; + } + } + + result = (T)(t % u); + } +}; + +template class ModulusHelper +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //trap corner case + if( CompileConst< IntTraits< U >::isSigned >::Value() ) + { + if( u == (U)-1 ) + { + result = 0; + return SafeIntNoError; + } + } + + result = (T)(t % u); + return SafeIntNoError; + } + + template < typename E > + static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + //trap corner case + if( CompileConst< IntTraits< U >::isSigned >::Value() ) + { + if( u == (U)-1 ) + { + result = 0; + return; + } + } + + result = (T)(t % u); + } +}; + +template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_CastInt64> +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //trap corner case + if( CompileConst< IntTraits< U >::isSigned >::Value() ) + { + if( u == (U)-1 ) + { + result = 0; + return SafeIntNoError; + } + } + + result = (T)((__int64)t % (__int64)u); + return SafeIntNoError; + } + + template < typename E > + static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + if( CompileConst< IntTraits< U >::isSigned >::Value() ) + { + if( u == (U)-1 ) + { + result = 0; + return; + } + } + + result = (T)((__int64)t % (__int64)u); + } +}; + +// T is unsigned __int64, U is any signed int +template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_UnsignedT> +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + // u could be negative - if so, need to convert to positive + // casts below are always safe due to the way modulus works + if(u < 0) + result = (T)(t % AbsValueHelper< U, GetAbsMethod< U >::method >::Abs(u)); + else + result = (T)(t % u); + + return SafeIntNoError; + } + + template < typename E > + static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + // u could be negative - if so, need to convert to positive + if(u < 0) + result = (T)(t % AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u )); + else + result = (T)(t % u); + } +}; + +// U is unsigned __int64, T any signed int +template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_UnsignedU> +{ +public: + static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //t could be negative - if so, need to convert to positive + if(t < 0) + result = (T)( ~( AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( t ) % u ) + 1 ); + else + result = (T)((T)t % u); + + return SafeIntNoError; + } + + template < typename E > + static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + //t could be negative - if so, need to convert to positive + if(t < 0) + result = (T)( ~( AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( t ) % u ) + 1); + else + result = (T)( (T)t % u ); + } +}; + +//core logic to determine method to check multiplication +enum MultiplicationState +{ + MultiplicationState_CastInt = 0, // One or both signed, smaller than 32-bit + MultiplicationState_CastInt64, // One or both signed, smaller than 64-bit + MultiplicationState_CastUint, // Both are unsigned, smaller than 32-bit + MultiplicationState_CastUint64, // Both are unsigned, both 32-bit or smaller + MultiplicationState_Uint64Uint, // Both are unsigned, lhs 64-bit, rhs 32-bit or smaller + MultiplicationState_Uint64Uint64, // Both are unsigned int64 + MultiplicationState_Uint64Int, // lhs is unsigned int64, rhs int32 + MultiplicationState_Uint64Int64, // lhs is unsigned int64, rhs signed int64 + MultiplicationState_UintUint64, // Both are unsigned, lhs 32-bit or smaller, rhs 64-bit + MultiplicationState_UintInt64, // lhs unsigned 32-bit or less, rhs int64 + MultiplicationState_Int64Uint, // lhs int64, rhs unsigned int32 + MultiplicationState_Int64Int64, // lhs int64, rhs int64 + MultiplicationState_Int64Int, // lhs int64, rhs int32 + MultiplicationState_IntUint64, // lhs int, rhs unsigned int64 + MultiplicationState_IntInt64, // lhs int, rhs int64 + MultiplicationState_Int64Uint64, // lhs int64, rhs uint64 + MultiplicationState_Error +}; + +template < typename T, typename U > +class MultiplicationMethod +{ +public: + enum + { + // unsigned-unsigned + method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? MultiplicationState_CastUint : + (IntRegion< T,U >::IntZone_Uint32_UintLT64 || + IntRegion< T,U >::IntZone_UintLT32_Uint32) ? MultiplicationState_CastUint64 : + SafeIntCompare< T,U >::isBothUnsigned && + IntTraits< T >::isUint64 && IntTraits< U >::isUint64 ? MultiplicationState_Uint64Uint64 : + (IntRegion< T,U >::IntZone_Uint64_Uint) ? MultiplicationState_Uint64Uint : + (IntRegion< T,U >::IntZone_UintLT64_Uint64) ? MultiplicationState_UintUint64 : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Uint64_Int) ? MultiplicationState_Uint64Int : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? MultiplicationState_UintInt64 : + (IntRegion< T,U >::IntZone_Uint64_Int64) ? MultiplicationState_Uint64Int64 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Int64_Int64) ? MultiplicationState_Int64Int64 : + (IntRegion< T,U >::IntZone_Int64_Int) ? MultiplicationState_Int64Int : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? MultiplicationState_IntInt64 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? MultiplicationState_Int64Uint : + (IntRegion< T,U >::IntZone_Int_Uint64) ? MultiplicationState_IntUint64 : + (IntRegion< T,U >::IntZone_Int64_Uint64 ? MultiplicationState_Int64Uint64 : + MultiplicationState_Error ) ) + }; +}; + +template class MultiplicationHelper; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastInt> +{ +public: + //accepts signed, both less than 32-bit + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + int tmp = t * u; + + if( tmp > IntTraits< T >::maxInt || tmp < IntTraits< T >::minInt ) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + int tmp = t * u; + + if( tmp > IntTraits< T >::maxInt || tmp < IntTraits< T >::minInt ) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastUint > +{ +public: + //accepts unsigned, both less than 32-bit + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + unsigned int tmp = (unsigned int)(t * u); + + if( tmp > IntTraits< T >::maxInt ) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + unsigned int tmp = (unsigned int)( t * u ); + + if( tmp > IntTraits< T >::maxInt ) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastInt64> +{ +public: + //mixed signed or both signed where at least one argument is 32-bit, and both a 32-bit or less + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + __int64 tmp = (__int64)t * (__int64)u; + + if(tmp > (__int64)IntTraits< T >::maxInt || tmp < (__int64)IntTraits< T >::minInt) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + __int64 tmp = (__int64)t * (__int64)u; + + if(tmp > (__int64)IntTraits< T >::maxInt || tmp < (__int64)IntTraits< T >::minInt) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastUint64> +{ +public: + //both unsigned where at least one argument is 32-bit, and both are 32-bit or less + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + unsigned __int64 tmp = (unsigned __int64)t * (unsigned __int64)u; + + if(tmp > (unsigned __int64)IntTraits< T >::maxInt) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + unsigned __int64 tmp = (unsigned __int64)t * (unsigned __int64)u; + + if(tmp > (unsigned __int64)IntTraits< T >::maxInt) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +// T = left arg and return type +// U = right arg +template < typename T, typename U > class LargeIntRegMultiply; + +#if SAFEINT_USE_INTRINSICS +// As usual, unsigned is easy +inline bool IntrinsicMultiplyUint64( const unsigned __int64& a, const unsigned __int64& b, unsigned __int64* pRet ) SAFEINT_NOTHROW +{ + unsigned __int64 ulHigh = 0; + *pRet = _umul128(a , b, &ulHigh); + return ulHigh == 0; +} + +// Signed, is not so easy +inline bool IntrinsicMultiplyInt64( const signed __int64& a, const signed __int64& b, signed __int64* pRet ) SAFEINT_NOTHROW +{ + __int64 llHigh = 0; + *pRet = _mul128(a , b, &llHigh); + + // Now we need to figure out what we expect + // If llHigh is 0, then treat *pRet as unsigned + // If llHigh is < 0, then treat *pRet as signed + + if( (a ^ b) < 0 ) + { + // Negative result expected + if( llHigh == -1 && *pRet < 0 || + llHigh == 0 && *pRet == 0 ) + { + // Everything is within range + return true; + } + } + else + { + // Result should be positive + // Check for overflow + if( llHigh == 0 && (unsigned __int64)*pRet <= IntTraits< signed __int64 >::maxInt ) + return true; + } + return false; +} + +#endif + +template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int64 > +{ +public: + static bool RegMultiply( const unsigned __int64& a, const unsigned __int64& b, unsigned __int64* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, b, pRet ); +#else + unsigned __int32 aHigh, aLow, bHigh, bLow; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + // Note - same approach applies for 128 bit math on a 64-bit system + + aHigh = (unsigned __int32)(a >> 32); + aLow = (unsigned __int32)a; + bHigh = (unsigned __int32)(b >> 32); + bLow = (unsigned __int32)b; + + *pRet = 0; + + if(aHigh == 0) + { + if(bHigh != 0) + { + *pRet = (unsigned __int64)aLow * (unsigned __int64)bHigh; + } + } + else if(bHigh == 0) + { + if(aHigh != 0) + { + *pRet = (unsigned __int64)aHigh * (unsigned __int64)bLow; + } + } + else + { + return false; + } + + if(*pRet != 0) + { + unsigned __int64 tmp; + + if((unsigned __int32)(*pRet >> 32) != 0) + return false; + + *pRet <<= 32; + tmp = (unsigned __int64)aLow * (unsigned __int64)bLow; + *pRet += tmp; + + if(*pRet < tmp) + return false; + + return true; + } + + *pRet = (unsigned __int64)aLow * (unsigned __int64)bLow; + return true; +#endif + } + + template < typename E > + static void RegMultiplyThrow( const unsigned __int64& a, const unsigned __int64& b, unsigned __int64* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, b, pRet ) ) + E::SafeIntOnOverflow(); +#else + unsigned __int32 aHigh, aLow, bHigh, bLow; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + // Note - same approach applies for 128 bit math on a 64-bit system + + aHigh = (unsigned __int32)(a >> 32); + aLow = (unsigned __int32)a; + bHigh = (unsigned __int32)(b >> 32); + bLow = (unsigned __int32)b; + + *pRet = 0; + + if(aHigh == 0) + { + if(bHigh != 0) + { + *pRet = (unsigned __int64)aLow * (unsigned __int64)bHigh; + } + } + else if(bHigh == 0) + { + if(aHigh != 0) + { + *pRet = (unsigned __int64)aHigh * (unsigned __int64)bLow; + } + } + else + { + E::SafeIntOnOverflow(); + } + + if(*pRet != 0) + { + unsigned __int64 tmp; + + if((unsigned __int32)(*pRet >> 32) != 0) + E::SafeIntOnOverflow(); + + *pRet <<= 32; + tmp = (unsigned __int64)aLow * (unsigned __int64)bLow; + *pRet += tmp; + + if(*pRet < tmp) + E::SafeIntOnOverflow(); + + return; + } + + *pRet = (unsigned __int64)aLow * (unsigned __int64)bLow; +#endif + } +}; + +template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int32 > +{ +public: + static bool RegMultiply( const unsigned __int64& a, unsigned __int32 b, unsigned __int64* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ); +#else + unsigned __int32 aHigh, aLow; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * b + // => (aHigh * b * 2^32) + (aLow * b) + + aHigh = (unsigned __int32)(a >> 32); + aLow = (unsigned __int32)a; + + *pRet = 0; + + if(aHigh != 0) + { + *pRet = (unsigned __int64)aHigh * (unsigned __int64)b; + + unsigned __int64 tmp; + + if((unsigned __int32)(*pRet >> 32) != 0) + return false; + + *pRet <<= 32; + tmp = (unsigned __int64)aLow * (unsigned __int64)b; + *pRet += tmp; + + if(*pRet < tmp) + return false; + + return true; + } + + *pRet = (unsigned __int64)aLow * (unsigned __int64)b; + return true; +#endif + } + + template < typename E > + static void RegMultiplyThrow( const unsigned __int64& a, unsigned __int32 b, unsigned __int64* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + unsigned __int32 aHigh, aLow; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * b + // => (aHigh * b * 2^32) + (aLow * b) + + aHigh = (unsigned __int32)(a >> 32); + aLow = (unsigned __int32)a; + + *pRet = 0; + + if(aHigh != 0) + { + *pRet = (unsigned __int64)aHigh * (unsigned __int64)b; + + unsigned __int64 tmp; + + if((unsigned __int32)(*pRet >> 32) != 0) + E::SafeIntOnOverflow(); + + *pRet <<= 32; + tmp = (unsigned __int64)aLow * (unsigned __int64)b; + *pRet += tmp; + + if(*pRet < tmp) + E::SafeIntOnOverflow(); + + return; + } + + *pRet = (unsigned __int64)aLow * (unsigned __int64)b; + return; +#endif + } +}; + +template<> class LargeIntRegMultiply< unsigned __int64, signed __int32 > +{ +public: + // Intrinsic not needed + static bool RegMultiply( const unsigned __int64& a, signed __int32 b, unsigned __int64* pRet ) SAFEINT_NOTHROW + { + if( b < 0 && a != 0 ) + return false; + +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ); +#else + return LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply(a, (unsigned __int32)b, pRet); +#endif + } + + template < typename E > + static void RegMultiplyThrow( const unsigned __int64& a, signed __int32 b, unsigned __int64* pRet ) SAFEINT_CPP_THROW + { + if( b < 0 && a != 0 ) + E::SafeIntOnOverflow(); + +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( a, (unsigned __int32)b, pRet ); +#endif + } +}; + +template<> class LargeIntRegMultiply< unsigned __int64, signed __int64 > +{ +public: + static bool RegMultiply( const unsigned __int64& a, signed __int64 b, unsigned __int64* pRet ) SAFEINT_NOTHROW + { + if( b < 0 && a != 0 ) + return false; + +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ); +#else + return LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply(a, (unsigned __int64)b, pRet); +#endif + } + + template < typename E > + static void RegMultiplyThrow( const unsigned __int64& a, signed __int64 b, unsigned __int64* pRet ) SAFEINT_CPP_THROW + { + if( b < 0 && a != 0 ) + E::SafeIntOnOverflow(); + +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::template RegMultiplyThrow< E >( a, (unsigned __int64)b, pRet ); +#endif + } +}; + +template<> class LargeIntRegMultiply< signed __int32, unsigned __int64 > +{ +public: + // Devolves into ordinary 64-bit calculation + static bool RegMultiply( signed __int32 a, const unsigned __int64& b, signed __int32* pRet ) SAFEINT_NOTHROW + { + unsigned __int32 bHigh, bLow; + bool fIsNegative = false; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + // aHigh == 0 implies: + // ( aLow * bHigh * 2^32 ) + ( aLow + bLow ) + // If the first part is != 0, fail + + bHigh = (unsigned __int32)(b >> 32); + bLow = (unsigned __int32)b; + + *pRet = 0; + + if(bHigh != 0 && a != 0) + return false; + + if( a < 0 ) + { + + a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); + fIsNegative = true; + } + + unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow; + + if( !fIsNegative ) + { + if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt ) + { + *pRet = (signed __int32)tmp; + return true; + } + } + else + { + if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt+1 ) + { + *pRet = SignedNegation< signed __int32 >::Value( tmp ); + return true; + } + } + + return false; + } + + template < typename E > + static void RegMultiplyThrow( signed __int32 a, const unsigned __int64& b, signed __int32* pRet ) SAFEINT_CPP_THROW + { + unsigned __int32 bHigh, bLow; + bool fIsNegative = false; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + + bHigh = (unsigned __int32)(b >> 32); + bLow = (unsigned __int32)b; + + *pRet = 0; + + if(bHigh != 0 && a != 0) + E::SafeIntOnOverflow(); + + if( a < 0 ) + { + a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); + fIsNegative = true; + } + + unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow; + + if( !fIsNegative ) + { + if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt ) + { + *pRet = (signed __int32)tmp; + return; + } + } + else + { + if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt+1 ) + { + *pRet = SignedNegation< signed __int32 >::Value( tmp ); + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template<> class LargeIntRegMultiply< unsigned __int32, unsigned __int64 > +{ +public: + // Becomes ordinary 64-bit multiplication, intrinsic not needed + static bool RegMultiply( unsigned __int32 a, const unsigned __int64& b, unsigned __int32* pRet ) SAFEINT_NOTHROW + { + // Consider that a*b can be broken up into: + // (bHigh * 2^32 + bLow) * a + // => (bHigh * a * 2^32) + (bLow * a) + // In this case, the result must fit into 32-bits + // If bHigh != 0 && a != 0, immediate error. + + if( (unsigned __int32)(b >> 32) != 0 && a != 0 ) + return false; + + unsigned __int64 tmp = b * (unsigned __int64)a; + + if( (unsigned __int32)(tmp >> 32) != 0 ) // overflow + return false; + + *pRet = (unsigned __int32)tmp; + return true; + } + + template < typename E > + static void RegMultiplyThrow( unsigned __int32 a, const unsigned __int64& b, unsigned __int32* pRet ) SAFEINT_CPP_THROW + { + if( (unsigned __int32)(b >> 32) != 0 && a != 0 ) + E::SafeIntOnOverflow(); + + unsigned __int64 tmp = b * (unsigned __int64)a; + + if( (unsigned __int32)(tmp >> 32) != 0 ) // overflow + E::SafeIntOnOverflow(); + + *pRet = (unsigned __int32)tmp; + } +}; + +template<> class LargeIntRegMultiply< unsigned __int32, signed __int64 > +{ +public: + static bool RegMultiply( unsigned __int32 a, const signed __int64& b, unsigned __int32* pRet ) SAFEINT_NOTHROW + { + if( b < 0 && a != 0 ) + return false; + return LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::RegMultiply( a, (unsigned __int64)b, pRet ); + } + + template < typename E > + static void RegMultiplyThrow( unsigned __int32 a, const signed __int64& b, unsigned __int32* pRet ) SAFEINT_CPP_THROW + { + if( b < 0 && a != 0 ) + E::SafeIntOnOverflow(); + + LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::template RegMultiplyThrow< E >( a, (unsigned __int64)b, pRet ); + } +}; + +template<> class LargeIntRegMultiply< signed __int64, signed __int64 > +{ +public: + static bool RegMultiply( const signed __int64& a, const signed __int64& b, signed __int64* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyInt64( a, b, pRet ); +#else + bool aNegative = false; + bool bNegative = false; + + unsigned __int64 tmp; + __int64 a1 = a; + __int64 b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( (unsigned __int64)a1, (unsigned __int64)b1, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + static void RegMultiplyThrow( const signed __int64& a, const signed __int64& b, signed __int64* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyInt64( a, b, pRet ) ) + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + bool bNegative = false; + + unsigned __int64 tmp; + __int64 a1 = a; + __int64 b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); + } + + LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::template RegMultiplyThrow< E >( (unsigned __int64)a1, (unsigned __int64)b1, &tmp ); + + // The unsigned multiplication didn't overflow or we'd be in the exception handler + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< signed __int64, unsigned __int32 > +{ +public: + static bool RegMultiply( const signed __int64& a, unsigned __int32 b, signed __int64* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyInt64( a, (signed __int64)b, pRet ); +#else + bool aNegative = false; + unsigned __int64 tmp; + __int64 a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply( (unsigned __int64)a1, b, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + static void RegMultiplyThrow( const signed __int64& a, unsigned __int32 b, signed __int64* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyInt64( a, (signed __int64)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + unsigned __int64 tmp; + __int64 a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( (unsigned __int64)a1, b, &tmp ); + + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< signed __int64, signed __int32 > +{ +public: + static bool RegMultiply( const signed __int64& a, signed __int32 b, signed __int64* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyInt64( a, (signed __int64)b, pRet ); +#else + bool aNegative = false; + bool bNegative = false; + + unsigned __int64 tmp; + __int64 a1 = a; + __int64 b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply( (unsigned __int64)a1, (unsigned __int32)b1, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + static void RegMultiplyThrow( signed __int64 a, signed __int32 b, signed __int64* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyInt64( a, (signed __int64)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + bool bNegative = false; + + unsigned __int64 tmp; + + if( a < 0 ) + { + aNegative = true; + a = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a); + } + + if( b < 0 ) + { + bNegative = true; + b = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(b); + } + + LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( (unsigned __int64)a, (unsigned __int32)b, &tmp ); + + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< signed __int32, signed __int64 > +{ +public: + static bool RegMultiply( signed __int32 a, const signed __int64& b, signed __int32* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + __int64 tmp; + + if( IntrinsicMultiplyInt64( a, b, &tmp ) ) + { + if( tmp > IntTraits< signed __int32 >::maxInt || + tmp < IntTraits< signed __int32 >::minInt ) + { + return false; + } + + *pRet = (__int32)tmp; + return true; + } + return false; +#else + bool aNegative = false; + bool bNegative = false; + + unsigned __int32 tmp; + __int64 b1 = b; + + if( a < 0 ) + { + aNegative = true; + a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); + } + + if( LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::RegMultiply( (unsigned __int32)a, (unsigned __int64)b1, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::minInt ) + { + *pRet = SignedNegation< signed __int32 >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::maxInt ) + { + *pRet = (signed __int32)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + static void RegMultiplyThrow( signed __int32 a, const signed __int64& b, signed __int32* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + __int64 tmp; + + if( IntrinsicMultiplyInt64( a, b, &tmp ) ) + { + if( tmp > IntTraits< signed __int32 >::maxInt || + tmp < IntTraits< signed __int32 >::minInt ) + { + E::SafeIntOnOverflow(); + } + + *pRet = (__int32)tmp; + return; + } + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + bool bNegative = false; + + unsigned __int32 tmp; + signed __int64 b2 = b; + + if( a < 0 ) + { + aNegative = true; + a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); + } + + if( b < 0 ) + { + bNegative = true; + b2 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b2); + } + + LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::template RegMultiplyThrow< E >( (unsigned __int32)a, (unsigned __int64)b2, &tmp ); + + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::minInt ) + { + *pRet = SignedNegation< signed __int32 >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::maxInt ) + { + *pRet = (signed __int32)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< signed __int64, unsigned __int64 > +{ +public: + // Leave this one as-is - will call unsigned intrinsic internally + static bool RegMultiply( const signed __int64& a, const unsigned __int64& b, signed __int64* pRet ) SAFEINT_NOTHROW + { + bool aNegative = false; + + unsigned __int64 tmp; + __int64 a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( (unsigned __int64)a1, (unsigned __int64)b, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return true; + } + } + } + + return false; + } + + template < typename E > + static void RegMultiplyThrow( const signed __int64& a, const unsigned __int64& b, signed __int64* pRet ) SAFEINT_CPP_THROW + { + bool aNegative = false; + unsigned __int64 tmp; + __int64 a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + } + + if( LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( (unsigned __int64)a1, (unsigned __int64)b, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + { + *pRet = SignedNegation< signed __int64 >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + { + *pRet = (signed __int64)tmp; + return; + } + } + } + + E::SafeIntOnOverflow(); + } +}; + +// In all of the following functions where LargeIntRegMultiply methods are called, +// we need to properly transition types. The methods need __int64, __int32, etc. +// but the variables being passed to us could be long long, long int, or long, depending on +// the compiler. Microsoft compiler knows that long long is the same type as __int64, but gcc doesn't + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Uint64 > +{ +public: + // T, U are unsigned __int64 + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isUint64 && IntTraits::isUint64 ); + unsigned __int64 t1 = t; + unsigned __int64 u1 = u; + return LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( t1, u1, reinterpret_cast(&ret) ); + } + + template < typename E > + static void MultiplyThrow(const unsigned __int64& t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isUint64 && IntTraits::isUint64 ); + unsigned __int64 t1 = t; + unsigned __int64 u1 = u; + LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::template RegMultiplyThrow< E >( t1, u1, reinterpret_cast(&ret) ); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Uint > +{ +public: + // T is unsigned __int64 + // U is any unsigned int 32-bit or less + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 t1 = t; + return LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply( t1, (unsigned __int32)u, reinterpret_cast(&ret) ); + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 t1 = t; + LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( t1, (unsigned __int32)u, reinterpret_cast(&ret) ); + } +}; + +// converse of the previous function +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_UintUint64 > +{ +public: + // T is any unsigned int up to 32-bit + // U is unsigned __int64 + static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 u1 = u; + unsigned __int32 tmp; + + if( LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::RegMultiply( t, u1, &tmp ) && + SafeCastHelper< T, unsigned __int32, GetCastMethod< T, unsigned __int32 >::method >::Cast(tmp, ret) ) + { + return true; + } + + return false; + } + + template < typename E > + static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 u1 = u; + unsigned __int32 tmp; + + LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::template RegMultiplyThrow< E >( t, u1, &tmp ); + SafeCastHelper< T, unsigned __int32, GetCastMethod< T, unsigned __int32 >::method >::template CastThrow< E >(tmp, ret); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Int > +{ +public: + // T is unsigned __int64 + // U is any signed int, up to 64-bit + static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 t1 = t; + return LargeIntRegMultiply< unsigned __int64, signed __int32 >::RegMultiply(t1, (signed __int32)u, reinterpret_cast< unsigned __int64* >(&ret)); + } + + template < typename E > + static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 t1 = t; + LargeIntRegMultiply< unsigned __int64, signed __int32 >::template RegMultiplyThrow< E >(t1, (signed __int32)u, reinterpret_cast< unsigned __int64* >(&ret)); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Int64 > +{ +public: + // T is unsigned __int64 + // U is __int64 + static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isUint64 && IntTraits::isInt64 ); + unsigned __int64 t1 = t; + __int64 u1 = u; + return LargeIntRegMultiply< unsigned __int64, __int64 >::RegMultiply(t1, u1, reinterpret_cast< unsigned __int64* >(&ret)); + } + + template < typename E > + static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isUint64 && IntTraits::isInt64 ); + unsigned __int64 t1 = t; + __int64 u1 = u; + LargeIntRegMultiply< unsigned __int64, __int64 >::template RegMultiplyThrow< E >(t1, u1, reinterpret_cast< unsigned __int64* >(&ret)); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_UintInt64 > +{ +public: + // T is unsigned up to 32-bit + // U is __int64 + static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 u1 = u; + unsigned __int32 tmp; + + if( LargeIntRegMultiply< unsigned __int32, __int64 >::RegMultiply( (unsigned __int32)t, u1, &tmp ) && + SafeCastHelper< T, unsigned __int32, GetCastMethod< T, unsigned __int32 >::method >::Cast(tmp, ret) ) + { + return true; + } + + return false; + } + + template < typename E > + static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 u1 = u; + unsigned __int32 tmp; + + LargeIntRegMultiply< unsigned __int32, __int64 >::template RegMultiplyThrow< E >( (unsigned __int32)t, u1, &tmp ); + SafeCastHelper< T, unsigned __int32, GetCastMethod< T, unsigned __int32 >::method >::template CastThrow< E >(tmp, ret); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Uint > +{ +public: + // T is __int64 + // U is unsigned up to 32-bit + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 t1 = t; + return LargeIntRegMultiply< __int64, unsigned __int32 >::RegMultiply( t1, (unsigned __int32)u, reinterpret_cast< __int64* >(&ret) ); + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 t1 = t; + LargeIntRegMultiply< __int64, unsigned __int32 >::template RegMultiplyThrow< E >( t1, (unsigned __int32)u, reinterpret_cast< __int64* >(&ret) ); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Int64 > +{ +public: + // T, U are __int64 + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isInt64 && IntTraits::isInt64 ); + __int64 t1 = t; + __int64 u1 = u; + return LargeIntRegMultiply< __int64, __int64 >::RegMultiply( t1, u1, reinterpret_cast< __int64* >(&ret) ); + } + + template < typename E > + static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isInt64 && IntTraits::isInt64 ); + __int64 t1 = t; + __int64 u1 = u; + LargeIntRegMultiply< __int64, __int64 >::template RegMultiplyThrow< E >( t1, u1, reinterpret_cast< __int64* >(&ret)); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Int > +{ +public: + // T is __int64 + // U is signed up to 32-bit + static bool Multiply( const T& t, U u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 t1 = t; + return LargeIntRegMultiply< __int64, __int32 >::RegMultiply( t1, (__int32)u, reinterpret_cast< __int64* >(&ret)); + } + + template < typename E > + static void MultiplyThrow( const __int64& t, U u, T& ret ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 t1 = t; + LargeIntRegMultiply< __int64, __int32 >::template RegMultiplyThrow< E >(t1, (__int32)u, reinterpret_cast< __int64* >(&ret)); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_IntUint64 > +{ +public: + // T is signed up to 32-bit + // U is unsigned __int64 + static bool Multiply(T t, const U& u, T& ret) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 u1 = u; + __int32 tmp; + + if( LargeIntRegMultiply< __int32, unsigned __int64 >::RegMultiply( (__int32)t, u1, &tmp ) && + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( tmp, ret ) ) + { + return true; + } + + return false; + } + + template < typename E > + static void MultiplyThrow(T t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isUint64 ); + unsigned __int64 u1 = u; + __int32 tmp; + + LargeIntRegMultiply< __int32, unsigned __int64 >::template RegMultiplyThrow< E >( (__int32)t, u1, &tmp ); + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( tmp, ret ); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Uint64> +{ +public: + // T is __int64 + // U is unsigned __int64 + static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isInt64 && IntTraits::isUint64 ); + __int64 t1 = t; + unsigned __int64 u1 = u; + return LargeIntRegMultiply< __int64, unsigned __int64 >::RegMultiply( t1, u1, reinterpret_cast< __int64* >(&ret) ); + } + + template < typename E > + static void MultiplyThrow( const __int64& t, const unsigned __int64& u, T& ret ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isInt64 && IntTraits::isUint64 ); + __int64 t1 = t; + unsigned __int64 u1 = u; + LargeIntRegMultiply< __int64, unsigned __int64 >::template RegMultiplyThrow< E >( t1, u1, reinterpret_cast< __int64* >(&ret) ); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_IntInt64> +{ +public: + // T is signed, up to 32-bit + // U is __int64 + static bool Multiply( T t, const U& u, T& ret ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 u1 = u; + __int32 tmp; + + if( LargeIntRegMultiply< __int32, __int64 >::RegMultiply( (__int32)t, u1, &tmp ) && + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( tmp, ret ) ) + { + return true; + } + + return false; + } + + template < typename E > + static void MultiplyThrow(T t, const U& u, T& ret) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits::isInt64 ); + __int64 u1 = u; + __int32 tmp; + + LargeIntRegMultiply< __int32, __int64 >::template RegMultiplyThrow< E >( (__int32)t, u1, &tmp ); + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( tmp, ret ); + } +}; + +enum DivisionState +{ + DivisionState_OK, + DivisionState_UnsignedSigned, + DivisionState_SignedUnsigned32, + DivisionState_SignedUnsigned64, + DivisionState_SignedUnsigned, + DivisionState_SignedSigned +}; + +template < typename T, typename U > class DivisionMethod +{ +public: + enum + { + method = (SafeIntCompare< T, U >::isBothUnsigned ? DivisionState_OK : + (!IntTraits< T >::isSigned && IntTraits< U >::isSigned) ? DivisionState_UnsignedSigned : + (IntTraits< T >::isSigned && + IntTraits< U >::isUint32 && + IntTraits< T >::isLT64Bit) ? DivisionState_SignedUnsigned32 : + (IntTraits< T >::isSigned && IntTraits< U >::isUint64) ? DivisionState_SignedUnsigned64 : + (IntTraits< T >::isSigned && !IntTraits< U >::isSigned) ? DivisionState_SignedUnsigned : + DivisionState_SignedSigned) + }; +}; + +template < typename T, typename U, int state > class DivisionHelper; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_OK > +{ +public: + static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + return SafeIntDivideByZero; + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + result = (T)( t/u ); + return SafeIntNoError; + } + + template < typename E > + static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if( u == 0 ) + E::SafeIntOnDivZero(); + + if( t == 0 ) + { + result = 0; + return; + } + + result = (T)( t/u ); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_UnsignedSigned> +{ +public: + static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + + if( u == 0 ) + return SafeIntDivideByZero; + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + if( u > 0 ) + { + result = (T)( t/u ); + return SafeIntNoError; + } + + // it is always an error to try and divide an unsigned number by a negative signed number + // unless u is bigger than t + if( AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u ) > t ) + { + result = 0; + return SafeIntNoError; + } + + return SafeIntArithmeticOverflow; + } + + template < typename E > + static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + + if( u == 0 ) + E::SafeIntOnDivZero(); + + if( t == 0 ) + { + result = 0; + return; + } + + if( u > 0 ) + { + result = (T)( t/u ); + return; + } + + // it is always an error to try and divide an unsigned number by a negative signed number + // unless u is bigger than t + if( AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u ) > t ) + { + result = 0; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned32 > +{ +public: + static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + return SafeIntDivideByZero; + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + // Test for t > 0 + // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors + // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional + + if( t > 0 ) + result = (T)( t/u ); + else + result = (T)( (__int64)t/(__int64)u ); + + return SafeIntNoError; + } + + template < typename E > + static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if( u == 0 ) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + // Test for t > 0 + // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors + // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional + + if( t > 0 ) + result = (T)( t/u ); + else + result = (T)( (__int64)t/(__int64)u ); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned64 > +{ +public: + static SafeIntError Divide( const T& t, const unsigned __int64& u, T& result ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits< U >::isUint64 ); + + if( u == 0 ) + { + return SafeIntDivideByZero; + } + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + if( u <= (unsigned __int64)IntTraits< T >::maxInt ) + { + // Else u can safely be cast to T + if( CompileConst< sizeof( T ) < sizeof( __int64 )>::Value() ) + result = (T)( (int)t/(int)u ); + else + result = (T)((__int64)t/(__int64)u); + } + else // Corner case + if( t == IntTraits< T >::minInt && u == (unsigned __int64)IntTraits< T >::minInt ) + { + // Min int divided by it's own magnitude is -1 + result = -1; + } + else + { + result = 0; + } + return SafeIntNoError; + } + + template < typename E > + static void DivideThrow( const T& t, const unsigned __int64& u, T& result ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits< U >::isUint64 ); + + if( u == 0 ) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + if( u <= (unsigned __int64)IntTraits< T >::maxInt ) + { + // Else u can safely be cast to T + if( CompileConst< sizeof( T ) < sizeof( __int64 ) >::Value() ) + result = (T)( (int)t/(int)u ); + else + result = (T)((__int64)t/(__int64)u); + } + else // Corner case + if( t == IntTraits< T >::minInt && u == (unsigned __int64)IntTraits< T >::minInt ) + { + // Min int divided by it's own magnitude is -1 + result = -1; + } + else + { + result = 0; + } + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned> +{ +public: + // T is any signed, U is unsigned and smaller than 32-bit + // In this case, standard operator casting is correct + static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + { + return SafeIntDivideByZero; + } + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + result = (T)( t/u ); + return SafeIntNoError; + } + + template < typename E > + static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if( u == 0 ) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + result = (T)( t/u ); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedSigned> +{ +public: + static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + { + return SafeIntDivideByZero; + } + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + // Must test for corner case + if( t == IntTraits< T >::minInt && u == (U)-1 ) + return SafeIntArithmeticOverflow; + + result = (T)( t/u ); + return SafeIntNoError; + } + + template < typename E > + static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + // Must test for corner case + if( t == IntTraits< T >::minInt && u == (U)-1 ) + E::SafeIntOnOverflow(); + + result = (T)( t/u ); + } +}; + +enum AdditionState +{ + AdditionState_CastIntCheckMax, + AdditionState_CastUintCheckOverflow, + AdditionState_CastUintCheckOverflowMax, + AdditionState_CastUint64CheckOverflow, + AdditionState_CastUint64CheckOverflowMax, + AdditionState_CastIntCheckSafeIntMinMax, + AdditionState_CastInt64CheckSafeIntMinMax, + AdditionState_CastInt64CheckMax, + AdditionState_CastUint64CheckSafeIntMinMax, + AdditionState_CastUint64CheckSafeIntMinMax2, + AdditionState_CastInt64CheckOverflow, + AdditionState_CastInt64CheckOverflowSafeIntMinMax, + AdditionState_CastInt64CheckOverflowMax, + AdditionState_ManualCheckInt64Uint64, + AdditionState_ManualCheck, + AdditionState_Error +}; + +template< typename T, typename U > +class AdditionMethod +{ +public: + enum + { + //unsigned-unsigned + method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? AdditionState_CastIntCheckMax : + (IntRegion< T,U >::IntZone_Uint32_UintLT64) ? AdditionState_CastUintCheckOverflow : + (IntRegion< T,U >::IntZone_UintLT32_Uint32) ? AdditionState_CastUintCheckOverflowMax : + (IntRegion< T,U >::IntZone_Uint64_Uint) ? AdditionState_CastUint64CheckOverflow : + (IntRegion< T,U >::IntZone_UintLT64_Uint64) ? AdditionState_CastUint64CheckOverflowMax : + //unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? AdditionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? AdditionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? AdditionState_CastUint64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? AdditionState_CastUint64CheckSafeIntMinMax2 : + //signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? AdditionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? AdditionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? AdditionState_CastInt64CheckOverflow : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? AdditionState_CastInt64CheckOverflowSafeIntMinMax : + //signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? AdditionState_CastIntCheckMax : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? AdditionState_CastInt64CheckMax : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? AdditionState_CastInt64CheckOverflowMax : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? AdditionState_ManualCheckInt64Uint64 : + (IntRegion< T,U >::IntZone_Int_Uint64) ? AdditionState_ManualCheck : + AdditionState_Error) + }; +}; + +template < typename T, typename U, int method > class AdditionHelper; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastIntCheckMax > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //16-bit or less unsigned addition + __int32 tmp = lhs + rhs; + + if( tmp <= (__int32)IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //16-bit or less unsigned addition + __int32 tmp = lhs + rhs; + + if( tmp <= (__int32)IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUintCheckOverflow > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - both are unsigned + unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; + + //we added didn't get smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return true; + } + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 32-bit or less - both are unsigned + unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; + + //we added didn't get smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return; + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUintCheckOverflowMax> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - both are unsigned + unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; + + // We added and it didn't get smaller or exceed maxInt + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //32-bit or less - both are unsigned + unsigned __int32 tmp = (unsigned __int32)lhs + (unsigned __int32)rhs; + + // We added and it didn't get smaller or exceed maxInt + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckOverflow> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs unsigned __int64, rhs unsigned + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it didn't get smaller + if(tmp >= lhs) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs unsigned __int64, rhs unsigned + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it didn't get smaller + if(tmp >= lhs) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckOverflowMax > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //lhs unsigned __int64, rhs unsigned + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it didn't get smaller + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //lhs unsigned __int64, rhs unsigned + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it didn't get smaller + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastIntCheckSafeIntMinMax > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 16-bit or less - one or both are signed + __int32 tmp = lhs + rhs; + + if( tmp <= (__int32)IntTraits< T >::maxInt && tmp >= (__int32)IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 16-bit or less - one or both are signed + __int32 tmp = lhs + rhs; + + if( tmp <= (__int32)IntTraits< T >::maxInt && tmp >= (__int32)IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckSafeIntMinMax > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - one or both are signed + __int64 tmp = (__int64)lhs + (__int64)rhs; + + if( tmp <= (__int64)IntTraits< T >::maxInt && tmp >= (__int64)IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 32-bit or less - one or both are signed + __int64 tmp = (__int64)lhs + (__int64)rhs; + + if( tmp <= (__int64)IntTraits< T >::maxInt && tmp >= (__int64)IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckMax > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - lhs signed, rhs unsigned + __int64 tmp = (__int64)lhs + (__int64)rhs; + + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 32-bit or less - lhs signed, rhs unsigned + __int64 tmp = (__int64)lhs + (__int64)rhs; + + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckSafeIntMinMax > +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is unsigned __int64, rhs signed + unsigned __int64 tmp; + + if( rhs < 0 ) + { + // So we're effectively subtracting + tmp = AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if( tmp <= lhs ) + { + result = lhs - tmp; + return true; + } + } + else + { + // now we know that rhs can be safely cast into an unsigned __int64 + tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it did not become smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is unsigned __int64, rhs signed + unsigned __int64 tmp; + + if( rhs < 0 ) + { + // So we're effectively subtracting + tmp = AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if( tmp <= lhs ) + { + result = lhs - tmp; + return; + } + } + else + { + // now we know that rhs can be safely cast into an unsigned __int64 + tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // We added and it did not become smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckSafeIntMinMax2> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is unsigned and < 64-bit, rhs signed __int64 + if( rhs < 0 ) + { + if( lhs >= ~(unsigned __int64)( rhs ) + 1 )//negation is safe, since rhs is 64-bit + { + result = (T)( lhs + rhs ); + return true; + } + } + else + { + // now we know that rhs can be safely cast into an unsigned __int64 + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff + // it is not possible for the operation above to overflow, so just check max + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + } + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is unsigned and < 64-bit, rhs signed __int64 + if( rhs < 0 ) + { + if( lhs >= ~(unsigned __int64)( rhs ) + 1) //negation is safe, since rhs is 64-bit + { + result = (T)( lhs + rhs ); + return; + } + } + else + { + // now we know that rhs can be safely cast into an unsigned __int64 + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff + // it is not possible for the operation above to overflow, so just check max + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflow> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is signed __int64, rhs signed + __int64 tmp = (__int64)((unsigned __int64)lhs + (unsigned __int64)rhs); + + if( lhs >= 0 ) + { + // mixed sign cannot overflow + if( rhs >= 0 && tmp < lhs ) + return false; + } + else + { + // lhs negative + if( rhs < 0 && tmp > lhs ) + return false; + } + + result = (T)tmp; + return true; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is signed __int64, rhs signed + __int64 tmp = (__int64)((unsigned __int64)lhs + (unsigned __int64)rhs); + + if( lhs >= 0 ) + { + // mixed sign cannot overflow + if( rhs >= 0 && tmp < lhs ) + E::SafeIntOnOverflow(); + } + else + { + // lhs negative + if( rhs < 0 && tmp > lhs ) + E::SafeIntOnOverflow(); + } + + result = (T)tmp; + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflowSafeIntMinMax> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //rhs is signed __int64, lhs signed + __int64 tmp; + + if( AdditionHelper< __int64, __int64, AdditionState_CastInt64CheckOverflow >::Addition( (__int64)lhs, (__int64)rhs, tmp ) && + tmp <= IntTraits< T >::maxInt && + tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //rhs is signed __int64, lhs signed + __int64 tmp; + + AdditionHelper< __int64, __int64, AdditionState_CastInt64CheckOverflow >::AdditionThrow< E >( (__int64)lhs, (__int64)rhs, tmp ); + + if( tmp <= IntTraits< T >::maxInt && + tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflowMax> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //lhs is signed __int64, rhs unsigned < 64-bit + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + if( (__int64)tmp >= lhs ) + { + result = (T)(__int64)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is signed __int64, rhs unsigned < 64-bit + // Some compilers get optimization-happy, let's thwart them + + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; + + if( (__int64)tmp >= lhs ) + { + result = (T)(__int64)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_ManualCheckInt64Uint64 > +{ +public: + static bool Addition( const __int64& lhs, const unsigned __int64& rhs, __int64& result ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); + // rhs is unsigned __int64, lhs __int64 + // cast everything to unsigned, perform addition, then + // cast back for check - this is done to stop optimizers from removing the code + unsigned __int64 tmp = (unsigned __int64)lhs + rhs; + + if( (__int64)tmp >= lhs ) + { + result = (__int64)tmp; + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); + // rhs is unsigned __int64, lhs __int64 + unsigned __int64 tmp = (unsigned __int64)lhs + rhs; + + if( (__int64)tmp >= lhs ) + { + result = (__int64)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_ManualCheck> +{ +public: + static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // rhs is unsigned __int64, lhs signed, 32-bit or less + if( (unsigned __int32)( rhs >> 32 ) == 0 ) + { + // Now it just happens to work out that the standard behavior does what we want + // Adding explicit casts to show exactly what's happening here + // Note - this is tweaked to keep optimizers from tossing out the code. + unsigned __int32 tmp = (unsigned __int32)rhs + (unsigned __int32)lhs; + + if( (__int32)tmp >= lhs && SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( (__int32)tmp, result ) ) + return true; + } + + return false; + } + + template < typename E > + static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // rhs is unsigned __int64, lhs signed, 32-bit or less + + if( (unsigned __int32)( rhs >> 32 ) == 0 ) + { + // Now it just happens to work out that the standard behavior does what we want + // Adding explicit casts to show exactly what's happening here + unsigned __int32 tmp = (unsigned __int32)rhs + (unsigned __int32)lhs; + + if( (__int32)tmp >= lhs ) + { + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( (__int32)tmp, result ); + return; + } + } + E::SafeIntOnOverflow(); + } +}; + +enum SubtractionState +{ + SubtractionState_BothUnsigned, + SubtractionState_CastIntCheckSafeIntMinMax, + SubtractionState_CastIntCheckMin, + SubtractionState_CastInt64CheckSafeIntMinMax, + SubtractionState_CastInt64CheckMin, + SubtractionState_Uint64Int, + SubtractionState_UintInt64, + SubtractionState_Int64Int, + SubtractionState_IntInt64, + SubtractionState_Int64Uint, + SubtractionState_IntUint64, + SubtractionState_Int64Uint64, + // states for SubtractionMethod2 + SubtractionState_BothUnsigned2, + SubtractionState_CastIntCheckSafeIntMinMax2, + SubtractionState_CastInt64CheckSafeIntMinMax2, + SubtractionState_Uint64Int2, + SubtractionState_UintInt642, + SubtractionState_Int64Int2, + SubtractionState_IntInt642, + SubtractionState_Int64Uint2, + SubtractionState_IntUint642, + SubtractionState_Int64Uint642, + SubtractionState_Error +}; + +template < typename T, typename U > class SubtractionMethod +{ +public: + enum + { + // unsigned-unsigned + method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 || + (IntRegion< T,U >::IntZone_Uint32_UintLT64) || + (IntRegion< T,U >::IntZone_UintLT32_Uint32) || + (IntRegion< T,U >::IntZone_Uint64_Uint) || + (IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt64 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt64 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckMin : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckMin : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint : + (IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint64 : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint64 : + SubtractionState_Error) + }; +}; + +// this is for the case of U - SafeInt< T, E > +template < typename T, typename U > class SubtractionMethod2 +{ +public: + enum + { + // unsigned-unsigned + method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 || + (IntRegion< T,U >::IntZone_Uint32_UintLT64) || + (IntRegion< T,U >::IntZone_UintLT32_Uint32) || + (IntRegion< T,U >::IntZone_Uint64_Uint) || + (IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned2 : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int2 : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt642 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int2 : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt642 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint2 : + (IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint642 : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint642 : + SubtractionState_Error) + }; +}; + +template < typename T, typename U, int method > class SubtractionHelper; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_BothUnsigned > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both are unsigned - easy case + if( rhs <= lhs ) + { + result = (T)( lhs - rhs ); + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both are unsigned - easy case + if( rhs <= lhs ) + { + result = (T)( lhs - rhs ); + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_BothUnsigned2 > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, U& result ) SAFEINT_NOTHROW + { + // both are unsigned - easy case + // Except we do have to check for overflow - lhs could be larger than result can hold + if( rhs <= lhs ) + { + T tmp = (T)(lhs - rhs); + return SafeCastHelper< U, T, GetCastMethod::method>::Cast( tmp, result); + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, U& result ) SAFEINT_CPP_THROW + { + // both are unsigned - easy case + if( rhs <= lhs ) + { + T tmp = (T)(lhs - rhs); + SafeCastHelper< U, T, GetCastMethod::method >::template CastThrow( tmp, result); + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastIntCheckSafeIntMinMax > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + __int32 tmp = lhs - rhs; + + if( SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( tmp, result ) ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + __int32 tmp = lhs - rhs; + + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template class SubtractionHelper< U, T, SubtractionState_CastIntCheckSafeIntMinMax2 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + __int32 tmp = lhs - rhs; + + return SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::Cast( tmp, result ); + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + __int32 tmp = lhs - rhs; + + SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastIntCheckMin > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 16-bit or less + // rhs is unsigned - check only minimum + __int32 tmp = lhs - rhs; + + if( tmp >= (__int32)IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 16-bit or less + // rhs is unsigned - check only minimum + __int32 tmp = lhs - rhs; + + if( tmp >= (__int32)IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastInt64CheckSafeIntMinMax > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + __int64 tmp = (__int64)lhs - (__int64)rhs; + + return SafeCastHelper< T, __int64, GetCastMethod< T, __int64 >::method >::Cast( tmp, result ); + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + __int64 tmp = (__int64)lhs - (__int64)rhs; + + SafeCastHelper< T, __int64, GetCastMethod< T, __int64 >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template class SubtractionHelper< U, T, SubtractionState_CastInt64CheckSafeIntMinMax2 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + __int64 tmp = (__int64)lhs - (__int64)rhs; + + return SafeCastHelper< T, __int64, GetCastMethod< T, __int64 >::method >::Cast( tmp, result ); + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + __int64 tmp = (__int64)lhs - (__int64)rhs; + + SafeCastHelper< T, __int64, GetCastMethod< T, __int64 >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastInt64CheckMin > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 32-bit or less + // rhs is unsigned - check only minimum + __int64 tmp = (__int64)lhs - (__int64)rhs; + + if( tmp >= (__int64)IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 32-bit or less + // rhs is unsigned - check only minimum + __int64 tmp = (__int64)lhs - (__int64)rhs; + + if( tmp >= (__int64)IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Uint64Int > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is an unsigned __int64, rhs signed + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (unsigned __int64)rhs <= lhs ) + { + result = (T)( lhs - (unsigned __int64)rhs ); + return true; + } + } + else + { + T tmp = lhs; + // we're now effectively adding + result = lhs + AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if(result >= tmp) + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is an unsigned __int64, rhs signed + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (unsigned __int64)rhs <= lhs ) + { + result = (T)( lhs - (unsigned __int64)rhs ); + return; + } + } + else + { + T tmp = lhs; + // we're now effectively adding + result = lhs + AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if(result >= tmp) + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Uint64Int2 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // U is unsigned __int64, T is signed + if( rhs < 0 ) + { + // treat this as addition + unsigned __int64 tmp; + + tmp = lhs + (unsigned __int64)AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( rhs ); + + // must check for addition overflow and max + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + } + else if( (unsigned __int64)rhs > lhs ) // now both are positive, so comparison always works + { + // result is negative + // implies that lhs must fit into T, and result cannot overflow + // Also allows us to drop to 32-bit math, which is faster on a 32-bit system + result = (T)lhs - (T)rhs; + return true; + } + else + { + // result is positive + unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; + + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // U is unsigned __int64, T is signed + if( rhs < 0 ) + { + // treat this as addition + unsigned __int64 tmp; + + tmp = lhs + (unsigned __int64)AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( rhs ); + + // must check for addition overflow and max + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + } + else if( (unsigned __int64)rhs > lhs ) // now both are positive, so comparison always works + { + // result is negative + // implies that lhs must fit into T, and result cannot overflow + // Also allows us to drop to 32-bit math, which is faster on a 32-bit system + result = (T)lhs - (T)rhs; + return; + } + else + { + // result is positive + unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; + + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_UintInt64 > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is an unsigned int32 or smaller, rhs signed __int64 + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (unsigned __int64)rhs <= lhs ) + { + result = (T)( lhs - (T)rhs ); + return true; + } + } + else + { + // we're now effectively adding + // since lhs is 32-bit, and rhs cannot exceed 2^63 + // this addition cannot overflow + unsigned __int64 tmp = lhs + ~(unsigned __int64)( rhs ) + 1; // negation safe + + // but we could exceed MaxInt + if(tmp <= IntTraits< T >::maxInt) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is an unsigned int32 or smaller, rhs signed __int64 + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (unsigned __int64)rhs <= lhs ) + { + result = (T)( lhs - (T)rhs ); + return; + } + } + else + { + // we're now effectively adding + // since lhs is 32-bit, and rhs cannot exceed 2^63 + // this addition cannot overflow + unsigned __int64 tmp = lhs + ~(unsigned __int64)( rhs ) + 1; // negation safe + + // but we could exceed MaxInt + if(tmp <= IntTraits< T >::maxInt) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template class SubtractionHelper< U, T, SubtractionState_UintInt642 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // U unsigned 32-bit or less, T __int64 + if( rhs >= 0 ) + { + // overflow not possible + result = (T)( (__int64)lhs - rhs ); + return true; + } + else + { + // we effectively have an addition + // which cannot overflow internally + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)( -rhs ); + + if( tmp <= (unsigned __int64)IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // U unsigned 32-bit or less, T __int64 + if( rhs >= 0 ) + { + // overflow not possible + result = (T)( (__int64)lhs - rhs ); + return; + } + else + { + // we effectively have an addition + // which cannot overflow internally + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)( -rhs ); + + if( tmp <= (unsigned __int64)IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Int > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is an __int64, rhs signed (up to 64-bit) + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible + + __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); + + // Note - ideally, we can order these so that true conditionals + // lead to success, which enables better pipelining + // It isn't practical here + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || // condition 2 + ( rhs >= 0 && tmp > lhs ) ) // condition 3 + { + return false; + } + + result = (T)tmp; + return true; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is an __int64, rhs signed (up to 64-bit) + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible + + __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); + + // Note - ideally, we can order these so that true conditionals + // lead to success, which enables better pipelining + // It isn't practical here + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || // condition 2 + ( rhs >= 0 && tmp > lhs ) ) // condition 3 + { + E::SafeIntOnOverflow(); + } + + result = (T)tmp; + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Int2 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs __int64, rhs any signed int (including __int64) + __int64 tmp = lhs - rhs; + + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible in tmp + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible in tmp + + if( lhs >= 0 ) + { + // if both positive, overflow to negative not possible + // which is why we'll explicitly check maxInt, and not call SafeCast + if( ( IntTraits< T >::isLT64Bit && tmp > IntTraits< T >::maxInt ) || + ( rhs < 0 && tmp < lhs ) ) + { + return false; + } + } + else + { + // lhs negative + if( ( IntTraits< T >::isLT64Bit && tmp < IntTraits< T >::minInt) || + ( rhs >=0 && tmp > lhs ) ) + { + return false; + } + } + + result = (T)tmp; + return true; + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs __int64, rhs any signed int (including __int64) + __int64 tmp = lhs - rhs; + + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible in tmp + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible in tmp + + if( lhs >= 0 ) + { + // if both positive, overflow to negative not possible + // which is why we'll explicitly check maxInt, and not call SafeCast + if( ( CompileConst< IntTraits< T >::isLT64Bit >::Value() && tmp > IntTraits< T >::maxInt ) || + ( rhs < 0 && tmp < lhs ) ) + { + E::SafeIntOnOverflow(); + } + } + else + { + // lhs negative + if( ( CompileConst< IntTraits< T >::isLT64Bit >::Value() && tmp < IntTraits< T >::minInt) || + ( rhs >=0 && tmp > lhs ) ) + { + E::SafeIntOnOverflow(); + } + } + + result = (T)tmp; + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_IntInt64 > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is a 32-bit int or less, rhs __int64 + // we have essentially 4 cases: + // + // lhs positive, rhs positive - rhs could be larger than lhs can represent + // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int + // lhs negative, rhs positive - check tmp <= lhs and tmp < min int + // lhs negative, rhs negative - addition cannot internally overflow, check against max + + __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); + + if( lhs >= 0 ) + { + // first case + if( rhs >= 0 ) + { + if( tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + } + else + { + // second case + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + } + } + else + { + // lhs < 0 + // third case + if( rhs >= 0 ) + { + if( tmp <= lhs && tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return true; + } + } + else + { + // fourth case + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return true; + } + } + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is a 32-bit int or less, rhs __int64 + // we have essentially 4 cases: + // + // lhs positive, rhs positive - rhs could be larger than lhs can represent + // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int + // lhs negative, rhs positive - check tmp <= lhs and tmp < min int + // lhs negative, rhs negative - addition cannot internally overflow, check against max + + __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); + + if( lhs >= 0 ) + { + // first case + if( rhs >= 0 ) + { + if( tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + } + else + { + // second case + if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + } + } + else + { + // lhs < 0 + // third case + if( rhs >= 0 ) + { + if( tmp <= lhs && tmp >= IntTraits< T >::minInt ) + { + result = (T)tmp; + return; + } + } + else + { + // fourth case + if( tmp <= IntTraits< T >::maxInt ) + { + result = (T)tmp; + return; + } + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_IntInt642 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is any signed int32 or smaller, rhs is int64 + __int64 tmp = (__int64)lhs - rhs; + + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || + ( rhs > 0 && tmp > lhs ) ) + { + return false; + //else OK + } + + result = (T)tmp; + return true; + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is any signed int32 or smaller, rhs is int64 + __int64 tmp = (__int64)lhs - rhs; + + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || + ( rhs > 0 && tmp > lhs ) ) + { + E::SafeIntOnOverflow(); + //else OK + } + + result = (T)tmp; + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Uint > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is a 64-bit int, rhs unsigned int32 or smaller + // perform test as unsigned to prevent unwanted optimizations + unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; + + if( (__int64)tmp <= lhs ) + { + result = (T)(__int64)tmp; + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is a 64-bit int, rhs unsigned int32 or smaller + // perform test as unsigned to prevent unwanted optimizations + unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; + + if( (__int64)tmp <= lhs ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Uint2 > +{ +public: + // lhs is __int64, rhs is unsigned 32-bit or smaller + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // Do this as unsigned to prevent unwanted optimizations + unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; + + if( (__int64)tmp <= IntTraits< T >::maxInt && (__int64)tmp >= IntTraits< T >::minInt ) + { + result = (T)(__int64)tmp; + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // Do this as unsigned to prevent unwanted optimizations + unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; + + if( (__int64)tmp <= IntTraits< T >::maxInt && (__int64)tmp >= IntTraits< T >::minInt ) + { + result = (T)(__int64)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_IntUint64 > +{ +public: + static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is any signed int, rhs unsigned int64 + // check against available range + + // We need the absolute value of IntTraits< T >::minInt + // This will give it to us without extraneous compiler warnings + const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits< T >::maxInt + 1; + + if( lhs < 0 ) + { + if( rhs <= AbsMinIntT - AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( lhs ) ) + { + result = (T)( lhs - rhs ); + return true; + } + } + else + { + if( rhs <= AbsMinIntT + (unsigned __int64)lhs ) + { + result = (T)( lhs - rhs ); + return true; + } + } + + return false; + } + + template < typename E > + static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is any signed int, rhs unsigned int64 + // check against available range + + // We need the absolute value of IntTraits< T >::minInt + // This will give it to us without extraneous compiler warnings + const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits< T >::maxInt + 1; + + if( lhs < 0 ) + { + if( rhs <= AbsMinIntT - AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( lhs ) ) + { + result = (T)( lhs - rhs ); + return; + } + } + else + { + if( rhs <= AbsMinIntT + (unsigned __int64)lhs ) + { + result = (T)( lhs - rhs ); + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_IntUint642 > +{ +public: + static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // We run into upcasting problems on comparison - needs 2 checks + if( lhs >= 0 && (T)lhs >= rhs ) + { + result = (T)((U)lhs - (U)rhs); + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // We run into upcasting problems on comparison - needs 2 checks + if( lhs >= 0 && (T)lhs >= rhs ) + { + result = (T)((U)lhs - (U)rhs); + return; + } + + E::SafeIntOnOverflow(); + } + +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Uint64 > +{ +public: + static bool Subtract( const __int64& lhs, const unsigned __int64& rhs, __int64& result ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); + // if we subtract, and it gets larger, there's a problem + // Perform test as unsigned to prevent unwanted optimizations + unsigned __int64 tmp = (unsigned __int64)lhs - rhs; + + if( (__int64)tmp <= lhs ) + { + result = (__int64)tmp; + return true; + } + return false; + } + + template < typename E > + static void SubtractThrow( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); + // if we subtract, and it gets larger, there's a problem + // Perform test as unsigned to prevent unwanted optimizations + unsigned __int64 tmp = (unsigned __int64)lhs - rhs; + + if( (__int64)tmp <= lhs ) + { + result = (__int64)tmp; + return; + } + + E::SafeIntOnOverflow(); + } + +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Uint642 > +{ +public: + // If lhs is negative, immediate problem - return must be positive, and subtracting only makes it + // get smaller. If rhs > lhs, then it would also go negative, which is the other case + static bool Subtract( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_NOTHROW + { + C_ASSERT( IntTraits< T >::isUint64 && IntTraits< U >::isInt64 ); + if( lhs >= 0 && (unsigned __int64)lhs >= rhs ) + { + result = (unsigned __int64)lhs - rhs; + return true; + } + + return false; + } + + template < typename E > + static void SubtractThrow( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_CPP_THROW + { + C_ASSERT( IntTraits< T >::isUint64 && IntTraits< U >::isInt64 ); + if( lhs >= 0 && (unsigned __int64)lhs >= rhs ) + { + result = (unsigned __int64)lhs - rhs; + return; + } + + E::SafeIntOnOverflow(); + } + +}; + +enum BinaryState +{ + BinaryState_OK, + BinaryState_Int8, + BinaryState_Int16, + BinaryState_Int32 +}; + +template < typename T, typename U > class BinaryMethod +{ +public: + enum + { + // If both operands are unsigned OR + // return type is smaller than rhs OR + // return type is larger and rhs is unsigned + // Then binary operations won't produce unexpected results + method = ( sizeof( T ) <= sizeof( U ) || + SafeIntCompare< T, U >::isBothUnsigned || + !IntTraits< U >::isSigned ) ? BinaryState_OK : + IntTraits< U >::isInt8 ? BinaryState_Int8 : + IntTraits< U >::isInt16 ? BinaryState_Int16 + : BinaryState_Int32 + }; +}; + +#ifdef SAFEINT_DISABLE_BINARY_ASSERT +#define BinaryAssert(x) +#else +#define BinaryAssert(x) SAFEINT_ASSERT(x) +#endif + +template < typename T, typename U, int method > class BinaryAndHelper; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_OK > +{ +public: + static T And( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs & rhs ); } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int8 > +{ +public: + static T And( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs & rhs ) == ( lhs & (unsigned __int8)rhs ) ); + return (T)( lhs & (unsigned __int8)rhs ); + } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int16 > +{ +public: + static T And( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs & rhs ) == ( lhs & (unsigned __int16)rhs ) ); + return (T)( lhs & (unsigned __int16)rhs ); + } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int32 > +{ +public: + static T And( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs & rhs ) == ( lhs & (unsigned __int32)rhs ) ); + return (T)( lhs & (unsigned __int32)rhs ); + } +}; + +template < typename T, typename U, int method > class BinaryOrHelper; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_OK > +{ +public: + static T Or( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs | rhs ); } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int8 > +{ +public: + static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs | rhs ) == ( lhs | (unsigned __int8)rhs ) ); + return (T)( lhs | (unsigned __int8)rhs ); + } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int16 > +{ +public: + static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs | rhs ) == ( lhs | (unsigned __int16)rhs ) ); + return (T)( lhs | (unsigned __int16)rhs ); + } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int32 > +{ +public: + static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs | rhs ) == ( lhs | (unsigned __int32)rhs ) ); + return (T)( lhs | (unsigned __int32)rhs ); + } +}; + +template class BinaryXorHelper; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_OK > +{ +public: + static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs ^ rhs ); } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int8 > +{ +public: + static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int8)rhs ) ); + return (T)( lhs ^ (unsigned __int8)rhs ); + } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int16 > +{ +public: + static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int16)rhs ) ); + return (T)( lhs ^ (unsigned __int16)rhs ); + } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int32 > +{ +public: + static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (unsigned __int32)rhs ) ); + return (T)( lhs ^ (unsigned __int32)rhs ); + } +}; + +/***************** External functions ****************************************/ + +// External functions that can be used where you only need to check one operation +// non-class helper function so that you can check for a cast's validity +// and handle errors how you like +template < typename T, typename U > +inline bool SafeCast( const T From, U& To ) SAFEINT_NOTHROW +{ + return SafeCastHelper< U, T, GetCastMethod< U, T >::method >::Cast( From, To ); +} + +template < typename T, typename U > +inline bool SafeEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( t, u ); +} + +template < typename T, typename U > +inline bool SafeNotEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( t, u ); +} + +template < typename T, typename U > +inline bool SafeGreaterThan( const T t, const U u ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( t, u ); +} + +template < typename T, typename U > +inline bool SafeGreaterThanEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( u, t ); +} + +template < typename T, typename U > +inline bool SafeLessThan( const T t, const U u ) SAFEINT_NOTHROW +{ + return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( u, t ); +} + +template < typename T, typename U > +inline bool SafeLessThanEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( t, u ); +} + +template < typename T, typename U > +inline bool SafeModulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW +{ + return ( ModulusHelper< T, U, ValidComparison< T, U >::method >::Modulus( t, u, result ) == SafeIntNoError ); +} + +template < typename T, typename U > +inline bool SafeMultiply( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::Multiply( t, u, result ); +} + +template < typename T, typename U > +inline bool SafeDivide( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return ( DivisionHelper< T, U, DivisionMethod< T, U >::method >::Divide( t, u, result ) == SafeIntNoError ); +} + +template < typename T, typename U > +inline bool SafeAdd( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return AdditionHelper< T, U, AdditionMethod< T, U >::method >::Addition( t, u, result ); +} + +template < typename T, typename U > +inline bool SafeSubtract( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::Subtract( t, u, result ); +} + +/***************** end external functions ************************************/ + +// Main SafeInt class +// Assumes exceptions can be thrown +template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeInt +{ +public: + SafeInt() SAFEINT_NOTHROW + { + C_ASSERT( NumericType< T >::isInt ); + m_int = 0; + } + + // Having a constructor for every type of int + // avoids having the compiler evade our checks when doing implicit casts - + // e.g., SafeInt s = 0x7fffffff; + SafeInt( const T& i ) SAFEINT_NOTHROW + { + C_ASSERT( NumericType< T >::isInt ); + //always safe + m_int = i; + } + + // provide explicit boolean converter + SafeInt( bool b ) SAFEINT_NOTHROW + { + C_ASSERT( NumericType< T >::isInt ); + m_int = (T)( b ? 1 : 0 ); + } + + template < typename U > + SafeInt(const SafeInt< U, E >& u) SAFEINT_CPP_THROW + { + C_ASSERT( NumericType< T >::isInt ); + *this = SafeInt< T, E >( (U)u ); + } + + template < typename U > + SafeInt( const U& i ) SAFEINT_CPP_THROW + { + C_ASSERT( NumericType< T >::isInt ); + // SafeCast will throw exceptions if i won't fit in type T + SafeCastHelper< T, U, GetCastMethod< T, U >::method >::template CastThrow< E >( i, m_int ); + } + + // The destructor is intentionally commented out - no destructor + // vs. a do-nothing destructor makes a huge difference in + // inlining characteristics. It wasn't doing anything anyway. + // ~SafeInt(){}; + + + // now start overloading operators + // assignment operator + // constructors exist for all int types and will ensure safety + + template < typename U > + SafeInt< T, E >& operator =( const U& rhs ) SAFEINT_CPP_THROW + { + // use constructor to test size + // constructor is optimized to do minimal checking based + // on whether T can contain U + // note - do not change this + *this = SafeInt< T, E >( rhs ); + return *this; + } + + SafeInt< T, E >& operator =( const T& rhs ) SAFEINT_NOTHROW + { + m_int = rhs; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator =( const SafeInt< U, E >& rhs ) SAFEINT_CPP_THROW + { + SafeCastHelper< T, U, GetCastMethod< T, U >::method >::template CastThrow< E >( rhs.Ref(), m_int ); + return *this; + } + + SafeInt< T, E >& operator =( const SafeInt< T, E >& rhs ) SAFEINT_NOTHROW + { + m_int = rhs.m_int; + return *this; + } + + // Casting operators + + operator bool() const SAFEINT_NOTHROW + { + return !!m_int; + } + + operator char() const SAFEINT_CPP_THROW + { + char val; + SafeCastHelper< char, T, GetCastMethod< char, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator signed char() const SAFEINT_CPP_THROW + { + signed char val; + SafeCastHelper< signed char, T, GetCastMethod< signed char, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator unsigned char() const SAFEINT_CPP_THROW + { + unsigned char val; + SafeCastHelper< unsigned char, T, GetCastMethod< unsigned char, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator __int16() const SAFEINT_CPP_THROW + { + __int16 val; + SafeCastHelper< __int16, T, GetCastMethod< __int16, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator unsigned __int16() const SAFEINT_CPP_THROW + { + unsigned __int16 val; + SafeCastHelper< unsigned __int16, T, GetCastMethod< unsigned __int16, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator __int32() const SAFEINT_CPP_THROW + { + __int32 val; + SafeCastHelper< __int32, T, GetCastMethod< __int32, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator unsigned __int32() const SAFEINT_CPP_THROW + { + unsigned __int32 val; + SafeCastHelper< unsigned __int32, T, GetCastMethod< unsigned __int32, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + // The compiler knows that int == __int32 + // but not that long == __int32 + operator long() const SAFEINT_CPP_THROW + { + long val; + SafeCastHelper< long, T, GetCastMethod< long, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator unsigned long() const SAFEINT_CPP_THROW + { + unsigned long val; + SafeCastHelper< unsigned long, T, GetCastMethod< unsigned long, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator __int64() const SAFEINT_CPP_THROW + { + __int64 val; + SafeCastHelper< __int64, T, GetCastMethod< __int64, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator unsigned __int64() const SAFEINT_CPP_THROW + { + unsigned __int64 val; + SafeCastHelper< unsigned __int64, T, GetCastMethod< unsigned __int64, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + +#if defined SAFEINT_USE_WCHAR_T || defined _NATIVE_WCHAR_T_DEFINED + operator wchar_t() const SAFEINT_CPP_THROW + { + wchar_t val; + SafeCastHelper< wchar_t, T, GetCastMethod< wchar_t, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } +#endif + +#ifdef SIZE_T_CAST_NEEDED + // We also need an explicit cast to size_t, or the compiler will complain + // Apparently, only SOME compilers complain, and cl 14.00.50727.42 isn't one of them + // Leave here in case we decide to backport this to an earlier compiler + operator size_t() const SAFEINT_CPP_THROW + { + size_t val; + SafeCastHelper< size_t, T, GetCastMethod< size_t, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } +#endif + + // Also provide a cast operator for floating point types + operator float() const SAFEINT_CPP_THROW + { + float val; + SafeCastHelper< float, T, GetCastMethod< float, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + operator double() const SAFEINT_CPP_THROW + { + double val; + SafeCastHelper< double, T, GetCastMethod< double, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + operator long double() const SAFEINT_CPP_THROW + { + long double val; + SafeCastHelper< long double, T, GetCastMethod< long double, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + // If you need a pointer to the data + // this could be dangerous, but allows you to correctly pass + // instances of this class to APIs that take a pointer to an integer + // also see overloaded address-of operator below + T* Ptr() SAFEINT_NOTHROW { return &m_int; } + const T* Ptr() const SAFEINT_NOTHROW { return &m_int; } + const T& Ref() const SAFEINT_NOTHROW { return m_int; } + + // Or if SafeInt< T, E >::Ptr() is inconvenient, use the overload + // operator & + // This allows you to do unsafe things! + // It is meant to allow you to more easily + // pass a SafeInt into things like ReadFile + T* operator &() SAFEINT_NOTHROW { return &m_int; } + const T* operator &() const SAFEINT_NOTHROW { return &m_int; } + + // Unary operators + bool operator !() const SAFEINT_NOTHROW { return (!m_int) ? true : false; } + + // operator + (unary) + // note - normally, the '+' and '-' operators will upcast to a signed int + // for T < 32 bits. This class changes behavior to preserve type + const SafeInt< T, E >& operator +() const SAFEINT_NOTHROW { return *this; } + + //unary - + + SafeInt< T, E > operator -() const SAFEINT_CPP_THROW + { + // Note - unsigned still performs the bitwise manipulation + // will warn at level 2 or higher if the value is 32-bit or larger + return SafeInt(NegationHelper::isSigned>::template NegativeThrow(m_int)); + } + + // prefix increment operator + SafeInt< T, E >& operator ++() SAFEINT_CPP_THROW + { + if( m_int != IntTraits< T >::maxInt ) + { + ++m_int; + return *this; + } + E::SafeIntOnOverflow(); + } + + // prefix decrement operator + SafeInt< T, E >& operator --() SAFEINT_CPP_THROW + { + if( m_int != IntTraits< T >::minInt ) + { + --m_int; + return *this; + } + E::SafeIntOnOverflow(); + } + + // note that postfix operators have inherently worse perf + // characteristics + + // postfix increment operator + SafeInt< T, E > operator ++( int ) SAFEINT_CPP_THROW // dummy arg to comply with spec + { + if( m_int != IntTraits< T >::maxInt ) + { + SafeInt< T, E > tmp( m_int ); + + m_int++; + return tmp; + } + E::SafeIntOnOverflow(); + } + + // postfix decrement operator + SafeInt< T, E > operator --( int ) SAFEINT_CPP_THROW // dummy arg to comply with spec + { + if( m_int != IntTraits< T >::minInt ) + { + SafeInt< T, E > tmp( m_int ); + m_int--; + return tmp; + } + E::SafeIntOnOverflow(); + } + + // One's complement + // Note - this operator will normally change size to an int + // cast in return improves perf and maintains type + SafeInt< T, E > operator ~() const SAFEINT_NOTHROW { return SafeInt< T, E >( (T)~m_int ); } + + // Binary operators + // + // arithmetic binary operators + // % modulus + // * multiplication + // / division + // + addition + // - subtraction + // + // For each of the arithmetic operators, you will need to + // use them as follows: + // + // SafeInt c = 2; + // SafeInt i = 3; + // + // SafeInt i2 = i op (char)c; + // OR + // SafeInt i2 = (int)i op c; + // + // The base problem is that if the lhs and rhs inputs are different SafeInt types + // it is not possible in this implementation to determine what type of SafeInt + // should be returned. You have to let the class know which of the two inputs + // need to be the return type by forcing the other value to the base integer type. + // + // Note - as per feedback from Scott Meyers, I'm exploring how to get around this. + // 3.0 update - I'm still thinking about this. It can be done with template metaprogramming, + // but it is tricky, and there's a perf vs. correctness tradeoff where the right answer + // is situational. + // + // The case of: + // + // SafeInt< T, E > i, j, k; + // i = j op k; + // + // works just fine and no unboxing is needed because the return type is not ambiguous. + + // Modulus + // Modulus has some convenient properties - + // first, the magnitude of the return can never be + // larger than the lhs operand, and it must be the same sign + // as well. It does, however, suffer from the same promotion + // problems as comparisons, division and other operations + template < typename U > + SafeInt< T, E > operator %( U rhs ) const SAFEINT_CPP_THROW + { + T result; + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, rhs, result ); + return SafeInt< T, E >( result ); + } + + SafeInt< T, E > operator %( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T result; + ModulusHelper< T, T, ValidComparison< T, T >::method >::template ModulusThrow< E >( m_int, rhs, result ); + return SafeInt< T, E >( result ); + } + + // Modulus assignment + template < typename U > + SafeInt< T, E >& operator %=( U rhs ) SAFEINT_CPP_THROW + { + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator %=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, (U)rhs, m_int ); + return *this; + } + + // Multiplication + template < typename U > + SafeInt< T, E > operator *( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + SafeInt< T, E > operator *( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + MultiplicationHelper< T, T, MultiplicationMethod< T, T >::method >::template MultiplyThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Multiplication assignment + SafeInt< T, E >& operator *=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + { + MultiplicationHelper< T, T, MultiplicationMethod< T, T >::method >::template MultiplyThrow< E >( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator *=( U rhs ) SAFEINT_CPP_THROW + { + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator *=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs.Ref(), m_int ); + return *this; + } + + // Division + template < typename U > + SafeInt< T, E > operator /( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + SafeInt< T, E > operator /( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + DivisionHelper< T, T, DivisionMethod< T, T >::method >::template DivideThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Division assignment + SafeInt< T, E >& operator /=( SafeInt< T, E > i ) SAFEINT_CPP_THROW + { + DivisionHelper< T, T, DivisionMethod< T, T >::method >::template DivideThrow< E >( m_int, (T)i, m_int ); + return *this; + } + + template < typename U > SafeInt< T, E >& operator /=( U i ) SAFEINT_CPP_THROW + { + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, i, m_int ); + return *this; + } + + template < typename U > SafeInt< T, E >& operator /=( SafeInt< U, E > i ) + { + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, (U)i, m_int ); + return *this; + } + + // For addition and subtraction + + // Addition + SafeInt< T, E > operator +( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + AdditionHelper< T, T, AdditionMethod< T, T >::method >::template AdditionThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + template < typename U > + SafeInt< T, E > operator +( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + //addition assignment + SafeInt< T, E >& operator +=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + { + AdditionHelper< T, T, AdditionMethod< T, T >::method >::template AdditionThrow< E >( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator +=( U rhs ) SAFEINT_CPP_THROW + { + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator +=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, (U)rhs, m_int ); + return *this; + } + + // Subtraction + template < typename U > + SafeInt< T, E > operator -( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + SafeInt< T, E > operator -(SafeInt< T, E > rhs) const SAFEINT_CPP_THROW + { + T ret( 0 ); + SubtractionHelper< T, T, SubtractionMethod< T, T >::method >::template SubtractThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Subtraction assignment + SafeInt< T, E >& operator -=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + { + SubtractionHelper< T, T, SubtractionMethod< T, T >::method >::template SubtractThrow< E >( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator -=( U rhs ) SAFEINT_CPP_THROW + { + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator -=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, (U)rhs, m_int ); + return *this; + } + + // Shift operators + // Note - shift operators ALWAYS return the same type as the lhs + // specific version for SafeInt< T, E > not needed - + // code path is exactly the same as for SafeInt< U, E > as rhs + + // Left shift + // Also, shifting > bitcount is undefined - trap in debug +#ifdef SAFEINT_DISABLE_SHIFT_ASSERT + #define ShiftAssert(x) +#else + #define ShiftAssert(x) SAFEINT_ASSERT(x) +#endif + + template < typename U > + SafeInt< T, E > operator <<( U bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); + ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int << bits ) ); + } + + template < typename U > + SafeInt< T, E > operator <<( SafeInt< U, E > bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)IntTraits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int << (U)bits ) ); + } + + // Left shift assignment + + template < typename U > + SafeInt< T, E >& operator <<=( U bits ) SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); + ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + + m_int <<= bits; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator <<=( SafeInt< U, E > bits ) SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)IntTraits< T >::bitCount ); + + m_int <<= (U)bits; + return *this; + } + + // Right shift + template < typename U > + SafeInt< T, E > operator >>( U bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); + ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int >> bits ) ); + } + + template < typename U > + SafeInt< T, E > operator >>( SafeInt< U, E > bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); + ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + + return SafeInt< T, E >( (T)(m_int >> (U)bits) ); + } + + // Right shift assignment + template < typename U > + SafeInt< T, E >& operator >>=( U bits ) SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); + ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + + m_int >>= bits; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator >>=( SafeInt< U, E > bits ) SAFEINT_NOTHROW + { + ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)IntTraits< T >::bitCount ); + + m_int >>= (U)bits; + return *this; + } + + // Bitwise operators + // This only makes sense if we're dealing with the same type and size + // demand a type T, or something that fits into a type T + + // Bitwise & + SafeInt< T, E > operator &( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( m_int & (T)rhs ); + } + + template < typename U > + SafeInt< T, E > operator &( U rhs ) const SAFEINT_NOTHROW + { + // we want to avoid setting bits by surprise + // consider the case of lhs = int, value = 0xffffffff + // rhs = char, value = 0xff + // + // programmer intent is to get only the lower 8 bits + // normal behavior is to upcast both sides to an int + // which then sign extends rhs, setting all the bits + + // If you land in the assert, this is because the bitwise operator + // was causing unexpected behavior. Fix is to properly cast your inputs + // so that it works like you meant, not unexpectedly + + return SafeInt< T, E >( BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, rhs ) ); + } + + // Bitwise & assignment + SafeInt< T, E >& operator &=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + { + m_int &= (T)rhs; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator &=( U rhs ) SAFEINT_NOTHROW + { + m_int = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, rhs ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator &=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + { + m_int = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, (U)rhs ); + return *this; + } + + // XOR + SafeInt< T, E > operator ^( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( (T)( m_int ^ (T)rhs ) ); + } + + template < typename U > + SafeInt< T, E > operator ^( U rhs ) const SAFEINT_NOTHROW + { + // If you land in the assert, this is because the bitwise operator + // was causing unexpected behavior. Fix is to properly cast your inputs + // so that it works like you meant, not unexpectedly + + return SafeInt< T, E >( BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, rhs ) ); + } + + // XOR assignment + SafeInt< T, E >& operator ^=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + { + m_int ^= (T)rhs; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator ^=( U rhs ) SAFEINT_NOTHROW + { + m_int = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, rhs ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator ^=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + { + m_int = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, (U)rhs ); + return *this; + } + + // bitwise OR + SafeInt< T, E > operator |( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( (T)( m_int | (T)rhs ) ); + } + + template < typename U > + SafeInt< T, E > operator |( U rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, rhs ) ); + } + + // bitwise OR assignment + SafeInt< T, E >& operator |=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + { + m_int |= (T)rhs; + return *this; + } + + template < typename U > + SafeInt< T, E >& operator |=( U rhs ) SAFEINT_NOTHROW + { + m_int = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, rhs ); + return *this; + } + + template < typename U > + SafeInt< T, E >& operator |=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + { + m_int = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, (U)rhs ); + return *this; + } + + // Miscellaneous helper functions + SafeInt< T, E > Min( SafeInt< T, E > test, const T floor = IntTraits< T >::minInt ) const SAFEINT_NOTHROW + { + T tmp = test < m_int ? (T)test : m_int; + return tmp < floor ? floor : tmp; + } + + SafeInt< T, E > Max( SafeInt< T, E > test, const T upper = IntTraits< T >::maxInt ) const SAFEINT_NOTHROW + { + T tmp = test > m_int ? (T)test : m_int; + return tmp > upper ? upper : tmp; + } + + void Swap( SafeInt< T, E >& with ) SAFEINT_NOTHROW + { + T temp( m_int ); + m_int = with.m_int; + with.m_int = temp; + } + + static SafeInt< T, E > SafeAtoI( const char* input ) SAFEINT_CPP_THROW + { + return SafeTtoI( input ); + } + + static SafeInt< T, E > SafeWtoI( const wchar_t* input ) + { + return SafeTtoI( input ); + } + + enum alignBits + { + align2 = 1, + align4 = 2, + align8 = 3, + align16 = 4, + align32 = 5, + align64 = 6, + align128 = 7, + align256 = 8 + }; + + template < alignBits bits > + const SafeInt< T, E >& Align() SAFEINT_CPP_THROW + { + // Zero is always aligned + if( m_int == 0 ) + return *this; + + // We don't support aligning negative numbers at this time + // Can't align unsigned numbers on bitCount (e.g., 8 bits = 256, unsigned char max = 255) + // or signed numbers on bitCount-1 (e.g., 7 bits = 128, signed char max = 127). + // Also makes no sense to try to align on negative or no bits. + + ShiftAssert( ( ( IntTraits::isSigned && bits < (int)IntTraits< T >::bitCount - 1 ) + || ( !IntTraits::isSigned && bits < (int)IntTraits< T >::bitCount ) ) && + bits >= 0 && ( !IntTraits::isSigned || m_int > 0 ) ); + + const T AlignValue = ( (T)1 << bits ) - 1; + + m_int = (T)( ( m_int + AlignValue ) & ~AlignValue ); + + if( m_int <= 0 ) + E::SafeIntOnOverflow(); + + return *this; + } + + // Commonly needed alignments: + const SafeInt< T, E >& Align2() { return Align< align2 >(); } + const SafeInt< T, E >& Align4() { return Align< align4 >(); } + const SafeInt< T, E >& Align8() { return Align< align8 >(); } + const SafeInt< T, E >& Align16() { return Align< align16 >(); } + const SafeInt< T, E >& Align32() { return Align< align32 >(); } + const SafeInt< T, E >& Align64() { return Align< align64 >(); } +private: + + // This is almost certainly not the best optimized version of atoi, + // but it does not display a typical bug where it isn't possible to set MinInt + // and it won't allow you to overflow your integer. + // This is here because it is useful, and it is an example of what + // can be done easily with SafeInt. + template < typename U > + static SafeInt< T, E > SafeTtoI( U* input ) SAFEINT_CPP_THROW + { + U* tmp = input; + SafeInt< T, E > s; + bool negative = false; + + // Bad input, or empty string + if( input == nullptr || input[0] == 0 ) + E::SafeIntOnOverflow(); + + switch( *tmp ) + { + case '-': + tmp++; + negative = true; + break; + case '+': + tmp++; + break; + } + + while( *tmp != 0 ) + { + if( *tmp < '0' || *tmp > '9' ) + break; + + if( (T)s != 0 ) + s *= (T)10; + + if( !negative ) + s += (T)( *tmp - '0' ); + else + s -= (T)( *tmp - '0' ); + + tmp++; + } + + return s; + } + + T m_int; +}; + +// Helper function used to subtract pointers. +// Used to squelch warnings +template +SafeInt SafePtrDiff(const P* p1, const P* p2) SAFEINT_CPP_THROW +{ + return SafeInt( p1 - p2 ); +} + +// Comparison operators + +//Less than +template < typename T, typename U, typename E > +bool operator <( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +bool operator <( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( rhs, (T)lhs ); +} + +template < typename T, typename U, typename E > +bool operator <( SafeInt< U, E > lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, (U)lhs ); +} + +// Greater than +template < typename T, typename U, typename E > +bool operator >( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( lhs, (T)rhs ); +} + +template < typename T, typename U, typename E > +bool operator >( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +bool operator >( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, (U)rhs ); +} + +// Greater than or equal +template < typename T, typename U, typename E > +bool operator >=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +bool operator >=( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( rhs, (T)lhs ); +} + +template < typename T, typename U, typename E > +bool operator >=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( (U)rhs, (T)lhs ); +} + +// Less than or equal +template < typename T, typename U, typename E > +bool operator <=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( lhs, (T)rhs ); +} + +template < typename T, typename U, typename E > +bool operator <=( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +bool operator <=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, (U)rhs ); +} + +// equality +// explicit overload for bool +template < typename T, typename E > +bool operator ==( bool lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return lhs == ( (T)rhs == 0 ? false : true ); +} + +template < typename T, typename E > +bool operator ==( SafeInt< T, E > lhs, bool rhs ) SAFEINT_NOTHROW +{ + return rhs == ( (T)lhs == 0 ? false : true ); +} + +template < typename T, typename U, typename E > +bool operator ==( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals((T)rhs, lhs); +} + +template < typename T, typename U, typename E > +bool operator ==( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +bool operator ==( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, (U)rhs ); +} + +//not equals +template < typename T, typename U, typename E > +bool operator !=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +bool operator !=( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +bool operator !=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( lhs, rhs ); +} + + +template < typename T, typename E > +bool operator !=( bool lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return ( (T)rhs == 0 ? false : true ) != lhs; +} + +template < typename T, typename E > +bool operator !=( SafeInt< T, E > lhs, bool rhs ) SAFEINT_NOTHROW +{ + return ( (T)lhs == 0 ? false : true ) != rhs; +} + + +template < typename T, typename U, typename E, int method > class ModulusSimpleCaseHelper; + +template < typename T, typename E, int method > class ModulusSignedCaseHelper; + +template < typename T, typename E > class ModulusSignedCaseHelper < T, E, true > +{ +public: + static bool SignedCase( SafeInt< T, E > rhs, SafeInt< T, E >& result ) SAFEINT_NOTHROW + { + if( (T)rhs == (T)-1 ) + { + result = 0; + return true; + } + return false; + } +}; + +template < typename T, typename E > class ModulusSignedCaseHelper < T, E, false > +{ +public: + static bool SignedCase( SafeInt< T, E > /*rhs*/, SafeInt< T, E >& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +template < typename T, typename U, typename E > +class ModulusSimpleCaseHelper < T, U, E, true > +{ +public: + static bool ModulusSimpleCase( U lhs, SafeInt< T, E > rhs, SafeInt< T, E >& result ) SAFEINT_CPP_THROW + { + if( rhs != 0 ) + { + if( ModulusSignedCaseHelper< T, E, IntTraits< T >::isSigned >::SignedCase( rhs, result ) ) + return true; + + result = SafeInt< T, E >( (T)( lhs % (T)rhs ) ); + return true; + } + + E::SafeIntOnDivZero(); + } +}; + +template< typename T, typename U, typename E > +class ModulusSimpleCaseHelper < T, U, E, false > +{ +public: + static bool ModulusSimpleCase( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt< T, E >& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +// Modulus +template < typename T, typename U, typename E > +SafeInt< T, E > operator %( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + // Value of return depends on sign of lhs + // This one may not be safe - bounds check in constructor + // if lhs is negative and rhs is unsigned, this will throw an exception. + + // Fast-track the simple case + // same size and same sign + SafeInt< T, E > result; + + if( ModulusSimpleCaseHelper< T, U, E, + sizeof(T) == sizeof(U) && (bool)IntTraits< T >::isSigned == (bool)IntTraits< U >::isSigned >::ModulusSimpleCase( lhs, rhs, result ) ) + return result; + + return SafeInt< T, E >( ( SafeInt< U, E >( lhs ) % (T)rhs ) ); +} + +// Multiplication +template < typename T, typename U, typename E > +SafeInt< T, E > operator *( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( (T)rhs, lhs, ret ); + return SafeInt< T, E >(ret); +} + +template < typename T, typename U, typename E, int method > class DivisionNegativeCornerCaseHelper; + +template < typename T, typename U, typename E > class DivisionNegativeCornerCaseHelper< T, U, E, true > +{ +public: + static bool NegativeCornerCase( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + { + // Problem case - normal casting behavior changes meaning + // flip rhs to positive + // any operator casts now do the right thing + U tmp; + + if( CompileConst< sizeof(T) == 4 >::Value() ) + tmp = lhs/(U)( ~(unsigned __int32)(T)rhs + 1 ); + else + tmp = lhs/(U)( ~(unsigned __int64)(T)rhs + 1 ); + + if( tmp <= (U)IntTraits< T >::maxInt ) + { + result = SafeInt< T, E >( (T)(~(unsigned __int64)tmp + 1) ); + return true; + } + + // Corner case + T maxT = IntTraits< T >::maxInt; + if( tmp == (U)maxT + 1 ) + { + T minT = IntTraits< T >::minInt; + result = SafeInt< T, E >( minT ); + return true; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U, typename E > class DivisionNegativeCornerCaseHelper< T, U, E, false > +{ +public: + static bool NegativeCornerCase( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +template < typename T, typename U, typename E, int method > class DivisionCornerCaseHelper; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper < T, U, E, true > +{ +public: + static bool DivisionCornerCase1( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + { + if( (T)rhs > 0 ) + { + result = SafeInt< T, E >( lhs/(T)rhs ); + return true; + } + + // Now rhs is either negative, or zero + if( (T)rhs != 0 ) + { + if( DivisionNegativeCornerCaseHelper< T, U, E, sizeof( U ) >= 4 && sizeof( T ) <= sizeof( U ) >::NegativeCornerCase( lhs, rhs, result ) ) + return true; + + result = SafeInt< T, E >(lhs/(T)rhs); + return true; + } + + E::SafeIntOnDivZero(); + } +}; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper < T, U, E, false > +{ +public: + static bool DivisionCornerCase1( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +template < typename T, typename U, typename E, int method > class DivisionCornerCaseHelper2; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 < T, U, E, true > +{ +public: + static bool DivisionCornerCase2( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + { + if( lhs == IntTraits< U >::minInt && (T)rhs == -1 ) + { + // corner case of a corner case - lhs = min int, rhs = -1, + // but rhs is the return type, so in essence, we can return -lhs + // if rhs is a larger type than lhs + // If types are wrong, throws + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning(push) +//cast truncates constant value +#pragma warning(disable:4310) +#endif + + if( CompileConst::Value() ) + result = SafeInt< T, E >( (T)( -(T)IntTraits< U >::minInt ) ); + else + E::SafeIntOnOverflow(); + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning(pop) +#endif + + return true; + } + + return false; + } +}; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 < T, U, E, false > +{ +public: + static bool DivisionCornerCase2( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +// Division +template < typename T, typename U, typename E > SafeInt< T, E > operator /( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + // Corner case - has to be handled seperately + SafeInt< T, E > result; + if( DivisionCornerCaseHelper< T, U, E, (int)DivisionMethod< U, T >::method == (int)DivisionState_UnsignedSigned >::DivisionCornerCase1( lhs, rhs, result ) ) + return result; + + if( DivisionCornerCaseHelper2< T, U, E, SafeIntCompare< T, U >::isBothSigned >::DivisionCornerCase2( lhs, rhs, result ) ) + return result; + + // Otherwise normal logic works with addition of bounds check when casting from U->T + U ret; + DivisionHelper< U, T, DivisionMethod< U, T >::method >::template DivideThrow< E >( lhs, (T)rhs, ret ); + return SafeInt< T, E >( ret ); +} + +// Addition +template < typename T, typename U, typename E > +SafeInt< T, E > operator +( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( (T)rhs, lhs, ret ); + return SafeInt< T, E >( ret ); +} + +// Subtraction +template < typename T, typename U, typename E > +SafeInt< T, E > operator -( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + SubtractionHelper< U, T, SubtractionMethod2< U, T >::method >::template SubtractThrow< E >( lhs, rhs.Ref(), ret ); + + return SafeInt< T, E >( ret ); +} + +// Overrides designed to deal with cases where a SafeInt is assigned out +// to a normal int - this at least makes the last operation safe +// += +template < typename T, typename U, typename E > +T& operator +=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator -=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator *=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator /=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator %=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +T& operator &=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +T& operator ^=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +T& operator |=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +T& operator <<=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = (T)( SafeInt< T, E >( lhs ) << (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +T& operator >>=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = (T)( SafeInt< T, E >( lhs ) >> (U)rhs ); + return lhs; +} + +// Specific pointer overrides +// Note - this function makes no attempt to ensure +// that the resulting pointer is still in the buffer, only +// that no int overflows happened on the way to getting the new pointer +template < typename T, typename U, typename E > +T*& operator +=( T*& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + // Cast the pointer to a number so we can do arithmetic + SafeInt< size_t, E > ptr_val = reinterpret_cast< size_t >( lhs ); + // Check first that rhs is valid for the type of ptrdiff_t + // and that multiplying by sizeof( T ) doesn't overflow a ptrdiff_t + // Next, we need to add 2 SafeInts of different types, so unbox the ptr_diff + // Finally, cast the number back to a pointer of the correct type + lhs = reinterpret_cast< T* >( (size_t)( ptr_val + (ptrdiff_t)( SafeInt< ptrdiff_t, E >( rhs ) * sizeof( T ) ) ) ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator -=( T*& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + // Cast the pointer to a number so we can do arithmetic + SafeInt< size_t, E > ptr_val = reinterpret_cast< size_t >( lhs ); + // See above for comments + lhs = reinterpret_cast< T* >( (size_t)( ptr_val - (ptrdiff_t)( SafeInt< ptrdiff_t, E >( rhs ) * sizeof( T ) ) ) ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator *=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator /=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator %=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator &=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator ^=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator |=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator <<=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +template < typename T, typename U, typename E > +T*& operator >>=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + C_ASSERT( sizeof(T) == 0 ); + return (lhs = NULL); +} + +// Shift operators +// NOTE - shift operators always return the type of the lhs argument + +// Left shift +template < typename T, typename U, typename E > +SafeInt< U, E > operator <<( U lhs, SafeInt< T, E > bits ) SAFEINT_NOTHROW +{ + ShiftAssert( !IntTraits< T >::isSigned || (T)bits >= 0 ); + ShiftAssert( (T)bits < (int)IntTraits< U >::bitCount ); + + return SafeInt< U, E >( (U)( lhs << (T)bits ) ); +} + +// Right shift +template < typename T, typename U, typename E > +SafeInt< U, E > operator >>( U lhs, SafeInt< T, E > bits ) SAFEINT_NOTHROW +{ + ShiftAssert( !IntTraits< T >::isSigned || (T)bits >= 0 ); + ShiftAssert( (T)bits < (int)IntTraits< U >::bitCount ); + + return SafeInt< U, E >( (U)( lhs >> (T)bits ) ); +} + +// Bitwise operators +// This only makes sense if we're dealing with the same type and size +// demand a type T, or something that fits into a type T. + +// Bitwise & +template < typename T, typename U, typename E > +SafeInt< T, E > operator &( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return SafeInt< T, E >( BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( (T)rhs, lhs ) ); +} + +// Bitwise XOR +template < typename T, typename U, typename E > +SafeInt< T, E > operator ^( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return SafeInt< T, E >(BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( (T)rhs, lhs ) ); +} + +// Bitwise OR +template < typename T, typename U, typename E > +SafeInt< T, E > operator |( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return SafeInt< T, E >( BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( (T)rhs, lhs ) ); +} + +#if SAFEINT_COMPILER == GCC_COMPILER +#pragma GCC diagnostic pop +#endif + +#if SAFEINT_COMPILER == CLANG_COMPILER +#pragma clang diagnostic pop +#endif + +} // utilities +} // safeint3 + diff --git a/3rdparty/cpprestsdk/include/cpprest/details/basic_types.h b/3rdparty/cpprestsdk/include/cpprest/details/basic_types.h new file mode 100644 index 00000000..b7821b70 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/basic_types.h @@ -0,0 +1,141 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Platform-dependent type definitions +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include +#include +#include +#include +#include "cpprest/details/cpprest_compat.h" +#include "cpprest/details/basic_types.h" + +#ifndef _WIN32 +# define __STDC_LIMIT_MACROS +# include +#else +#include +#endif + +#include "cpprest/details/SafeInt3.hpp" + +namespace utility +{ + +#ifdef _WIN32 +#define _UTF16_STRINGS +#endif + +// We should be using a 64-bit size type for most situations that do +// not involve specifying the size of a memory allocation or buffer. +typedef uint64_t size64_t; + +#ifndef _WIN32 +typedef uint32_t HRESULT; // Needed for PPLX +#endif + +#ifdef _UTF16_STRINGS +// +// On Windows, all strings are wide +// +typedef wchar_t char_t ; +typedef std::wstring string_t; +#define _XPLATSTR(x) L ## x +typedef std::wostringstream ostringstream_t; +typedef std::wofstream ofstream_t; +typedef std::wostream ostream_t; +typedef std::wistream istream_t; +typedef std::wifstream ifstream_t; +typedef std::wistringstream istringstream_t; +typedef std::wstringstream stringstream_t; +#define ucout std::wcout +#define ucin std::wcin +#define ucerr std::wcerr +#else +// +// On POSIX platforms, all strings are narrow +// +typedef char char_t; +typedef std::string string_t; +#define _XPLATSTR(x) x +typedef std::ostringstream ostringstream_t; +typedef std::ofstream ofstream_t; +typedef std::ostream ostream_t; +typedef std::istream istream_t; +typedef std::ifstream ifstream_t; +typedef std::istringstream istringstream_t; +typedef std::stringstream stringstream_t; +#define ucout std::cout +#define ucin std::cin +#define ucerr std::cerr +#endif // endif _UTF16_STRINGS + +#ifndef _TURN_OFF_PLATFORM_STRING +#define U(x) _XPLATSTR(x) +#endif // !_TURN_OFF_PLATFORM_STRING + +}// namespace utility + +typedef char utf8char; +typedef std::string utf8string; +typedef std::stringstream utf8stringstream; +typedef std::ostringstream utf8ostringstream; +typedef std::ostream utf8ostream; +typedef std::istream utf8istream; +typedef std::istringstream utf8istringstream; + +#ifdef _UTF16_STRINGS +typedef wchar_t utf16char; +typedef std::wstring utf16string; +typedef std::wstringstream utf16stringstream; +typedef std::wostringstream utf16ostringstream; +typedef std::wostream utf16ostream; +typedef std::wistream utf16istream; +typedef std::wistringstream utf16istringstream; +#else +typedef char16_t utf16char; +typedef std::u16string utf16string; +typedef std::basic_stringstream utf16stringstream; +typedef std::basic_ostringstream utf16ostringstream; +typedef std::basic_ostream utf16ostream; +typedef std::basic_istream utf16istream; +typedef std::basic_istringstream utf16istringstream; +#endif + + +#if defined(_WIN32) +// Include on everything except Windows Desktop ARM, unless explicitly excluded. +#if !defined(CPPREST_EXCLUDE_WEBSOCKETS) +#if defined(WINAPI_FAMILY) +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && defined(_M_ARM) +#define CPPREST_EXCLUDE_WEBSOCKETS +#endif +#else +#if defined(_M_ARM) +#define CPPREST_EXCLUDE_WEBSOCKETS +#endif +#endif +#endif +#endif diff --git a/3rdparty/cpprestsdk/include/cpprest/details/cpprest_compat.h b/3rdparty/cpprestsdk/include/cpprest/details/cpprest_compat.h new file mode 100644 index 00000000..67d41bb3 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/cpprest_compat.h @@ -0,0 +1,103 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Standard macros and definitions. +* This header has minimal dependency on windows headers and is safe for use in the public API +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#if defined(_WIN32) // Settings specific to Windows + +#if _MSC_VER >= 1900 +#define CPPREST_NOEXCEPT noexcept +#else +#define CPPREST_NOEXCEPT +#endif + +#define CASABLANCA_UNREFERENCED_PARAMETER(x) (x) + +#include + +#else // End settings specific to Windows + +// Settings common to all but Windows + +#define __declspec(x) __attribute__ ((x)) +#define dllimport +#define novtable /* no novtable equivalent */ +#define __assume(x) do { if (!(x)) __builtin_unreachable(); } while (false) +#define CASABLANCA_UNREFERENCED_PARAMETER(x) (void)x +#define CPPREST_NOEXCEPT noexcept + +#include +#define _ASSERTE(x) assert(x) + +// No SAL on non Windows platforms +#include "cpprest/details/nosal.h" + +#if defined(__APPLE__) // Settings specific to Apple +#define __cdecl +#else + +// Settings specific to Linux and Android + +// Ignore cdecl on ANDROID ARM and 64bit +#if defined(__ANDROID__) && defined(__arm__) || defined(__LP64__) +#define __cdecl +#else +#define __cdecl __attribute__ ((cdecl)) +#endif + +#if defined(__ANDROID__) +// This is needed to disable the use of __thread inside the boost library. +// Android does not support thread local storage -- if boost is included +// without this macro defined, it will create references to __tls_get_addr +// which (while able to link) will not be available at runtime and prevent +// the .so from loading. +#define BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION +#endif + +#ifdef __clang__ +#include +#endif + +#endif // defined(__APPLE__) + +#endif + + +#ifdef _NO_ASYNCRTIMP +#define _ASYNCRTIMP +#else +#ifdef _ASYNCRT_EXPORT +#define _ASYNCRTIMP __declspec(dllexport) +#else +#define _ASYNCRTIMP __declspec(dllimport) +#endif +#endif + +#ifdef CASABLANCA_DEPRECATION_NO_WARNINGS +#define CASABLANCA_DEPRECATED(x) +#else +#define CASABLANCA_DEPRECATED(x) __declspec(deprecated(x)) +#endif diff --git a/3rdparty/cpprestsdk/include/cpprest/details/fileio.h b/3rdparty/cpprestsdk/include/cpprest/details/fileio.h new file mode 100644 index 00000000..db13b765 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/fileio.h @@ -0,0 +1,206 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* fileio.h +* +* Asynchronous I/O: stream buffer implementation details +* +* We're going to some lengths to avoid exporting C++ class member functions and implementation details across +* module boundaries, and the factoring requires that we keep the implementation details away from the main header +* files. The supporting functions, which are in this file, use C-like signatures to avoid as many issues as +* possible. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#ifdef _WIN32 +#include +#endif + +#include "pplx/pplxtasks.h" +#include "cpprest/details/basic_types.h" + +namespace Concurrency { namespace streams +{ +namespace details +{ + /// + /// A record containing the essential private data members of a file stream, + /// in particular the parts that need to be shared between the public header + /// file and the implementation in the implementation file. + /// + struct _file_info + { + _ASYNCRTIMP _file_info(std::ios_base::openmode mode, size_t buffer_size) : + m_rdpos(0), + m_wrpos(0), + m_atend(false), + m_buffer_size(buffer_size), + m_buffer(nullptr), + m_bufoff(0), + m_bufsize(0), + m_buffill(0), + m_mode(mode) + { + } + + // Positional data + + size_t m_rdpos; + size_t m_wrpos; + bool m_atend; + + // Input buffer + + size_t m_buffer_size; // The intended size of the buffer to read into. + char *m_buffer; + + size_t m_bufoff; // File position that the start of the buffer represents. + msl::safeint3::SafeInt m_bufsize; // Buffer allocated size, as actually allocated. + size_t m_buffill; // Amount of file data actually in the buffer + + std::ios_base::openmode m_mode; + + pplx::extensibility::recursive_lock_t m_lock; + }; + + + /// + /// This interface provides the necessary callbacks for completion events. + /// + class _filestream_callback + { + public: + virtual void on_opened(_In_ details::_file_info *) { } + virtual void on_closed() { } + virtual void on_error(const std::exception_ptr &) { } + virtual void on_completed(size_t) { } + protected: + virtual ~_filestream_callback() {} + }; + +} +}} + +extern "C" +{ +/// +/// Open a file and create a streambuf instance to represent it. +/// +/// A pointer to the callback interface to invoke when the file has been opened. +/// The name of the file to open +/// A creation mode for the stream buffer +/// A file protection mode to use for the file stream (not supported on Linux) +/// true if the opening operation could be initiated, false otherwise. +/// +/// True does not signal that the file will eventually be successfully opened, just that the process was started. +/// +#if !defined(__cplusplus_winrt) +_ASYNCRTIMP bool __cdecl _open_fsb_str(_In_ concurrency::streams::details::_filestream_callback *callback, const utility::char_t *filename, std::ios_base::openmode mode, int prot); +#endif + +/// +/// Create a streambuf instance to represent a WinRT file. +/// +/// A pointer to the callback interface to invoke when the file has been opened. +/// The file object +/// A creation mode for the stream buffer +/// true if the opening operation could be initiated, false otherwise. +/// +/// True does not signal that the file will eventually be successfully opened, just that the process was started. +/// This is only available for WinRT. +/// +#if defined(__cplusplus_winrt) +_ASYNCRTIMP bool __cdecl _open_fsb_stf_str(_In_ concurrency::streams::details::_filestream_callback *callback, ::Windows::Storage::StorageFile^ file, std::ios_base::openmode mode, int prot); +#endif + +/// +/// Close a file stream buffer. +/// +/// The file info record of the file +/// A pointer to the callback interface to invoke when the file has been opened. +/// true if the closing operation could be initiated, false otherwise. +/// +/// True does not signal that the file will eventually be successfully closed, just that the process was started. +/// +_ASYNCRTIMP bool __cdecl _close_fsb_nolock(_In_ concurrency::streams::details::_file_info **info, _In_ concurrency::streams::details::_filestream_callback *callback); +_ASYNCRTIMP bool __cdecl _close_fsb(_In_ concurrency::streams::details::_file_info **info, _In_ concurrency::streams::details::_filestream_callback *callback); + + +/// +/// Write data from a buffer into the file stream. +/// +/// The file info record of the file +/// A pointer to the callback interface to invoke when the write request is completed. +/// A pointer to a buffer where the data should be placed +/// The size (in characters) of the buffer +/// 0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read into the buffer +_ASYNCRTIMP size_t __cdecl _putn_fsb(_In_ concurrency::streams::details::_file_info *info, _In_ concurrency::streams::details::_filestream_callback *callback, const void *ptr, size_t count, size_t char_size); + +/// +/// Read data from a file stream into a buffer +/// +/// The file info record of the file +/// A pointer to the callback interface to invoke when the write request is completed. +/// A pointer to a buffer where the data should be placed +/// The size (in characters) of the buffer +/// 0 if the read request is still outstanding, -1 if the request failed, otherwise the size of the data read into the buffer +_ASYNCRTIMP size_t __cdecl _getn_fsb(_In_ concurrency::streams::details::_file_info *info, _In_ concurrency::streams::details::_filestream_callback *callback, _Out_writes_ (count) void *ptr, _In_ size_t count, size_t char_size); + +/// +/// Flush all buffered data to the underlying file. +/// +/// The file info record of the file +/// A pointer to the callback interface to invoke when the write request is completed. +/// true if the request was initiated +_ASYNCRTIMP bool __cdecl _sync_fsb(_In_ concurrency::streams::details::_file_info *info, _In_ concurrency::streams::details::_filestream_callback *callback); + +/// +/// Get the size of the underlying file. +/// +/// The file info record of the file +/// The file size +_ASYNCRTIMP utility::size64_t __cdecl _get_size(_In_ concurrency::streams::details::_file_info *info, size_t char_size); + +/// +/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream. +/// +/// The file info record of the file +/// The new position (offset from the start) in the file stream +/// true if the request was initiated +_ASYNCRTIMP size_t __cdecl _seekrdpos_fsb(_In_ concurrency::streams::details::_file_info *info, size_t pos, size_t char_size); + +/// +/// Adjust the internal buffers and pointers when the application seeks to a new read location in the stream. +/// +/// The file info record of the file +/// The new position (offset from the start) in the file stream +/// true if the request was initiated +_ASYNCRTIMP size_t __cdecl _seekrdtoend_fsb(_In_ concurrency::streams::details::_file_info *info, int64_t offset, size_t char_size); + +/// +/// Adjust the internal buffers and pointers when the application seeks to a new write location in the stream. +/// +/// The file info record of the file +/// The new position (offset from the start) in the file stream +/// true if the request was initiated +_ASYNCRTIMP size_t __cdecl _seekwrpos_fsb(_In_ concurrency::streams::details::_file_info *info, size_t pos, size_t char_size); +} diff --git a/3rdparty/cpprestsdk/include/cpprest/details/http_client_impl.h b/3rdparty/cpprestsdk/include/cpprest/details/http_client_impl.h new file mode 100644 index 00000000..377e8acd --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/http_client_impl.h @@ -0,0 +1,466 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: Client-side APIs, non-public declarations used in the implementation. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#include "cpprest/details/basic_types.h" +#include "cpprest/details/http_helpers.h" + +#ifdef _WIN32 +# define CRLF _XPLATSTR("\r\n") +#else +# define CRLF std::string("\r\n") +#endif + +namespace web { namespace http { namespace client { namespace details +{ + +#ifdef _WIN32 +static const utility::char_t * get_with_body = _XPLATSTR("A GET or HEAD request should not have an entity body."); + +// Helper function to trim leading and trailing null characters from a string. +static void trim_nulls(utility::string_t &str) +{ + size_t index; + for(index = 0; index < str.size() && str[index] == 0; ++index); + str.erase(0, index); + for(index = str.size(); index > 0 && str[index - 1] == 0; --index); + str.erase(index); +} + +#endif + +// Flatten the http_headers into a name:value pairs separated by a carriage return and line feed. +static utility::string_t flatten_http_headers(const http_headers &headers) +{ + utility::string_t flattened_headers; + for(auto iter = headers.begin(); iter != headers.end(); ++iter) + { + flattened_headers.append(iter->first); + flattened_headers.push_back(':'); + flattened_headers.append(iter->second); + flattened_headers.append(CRLF); + } + return flattened_headers; +} + +#ifdef _WIN32 +/// +/// Parses a string containing Http headers. +/// +static void parse_headers_string(_Inout_z_ utf16char *headersStr, http_headers &headers) +{ + utf16char *context = nullptr; + utf16char *line = wcstok_s(headersStr, CRLF, &context); + while(line != nullptr) + { + const utility::string_t header_line(line); + const size_t colonIndex = header_line.find_first_of(_XPLATSTR(":")); + if(colonIndex != utility::string_t::npos) + { + utility::string_t key = header_line.substr(0, colonIndex); + utility::string_t value = header_line.substr(colonIndex + 1, header_line.length() - colonIndex - 1); + http::details::trim_whitespace(key); + http::details::trim_whitespace(value); + headers.add(key, value); + } + line = wcstok_s(nullptr, CRLF, &context); + } +} +#endif + +class _http_client_communicator; + +// Request context encapsulating everything necessary for creating and responding to a request. +class request_context +{ +public: + + // Destructor to clean up any held resources. + virtual ~request_context() + { + } + + void complete_headers() + { + // We have already read (and transmitted) the request body. Should we explicitly close the stream? + // Well, there are test cases that assumes that the istream is valid when t receives the response! + // For now, we will drop our reference which will close the stream if the user doesn't have one. + m_request.set_body(Concurrency::streams::istream()); + m_request_completion.set(m_response); + } + + /// + /// Completes this request, setting the underlying task completion event, and cleaning up the handles + /// + void complete_request(utility::size64_t body_size) + { + m_response._get_impl()->_complete(body_size); + + finish(); + } + + void report_error(unsigned long error_code, const std::string &errorMessage) + { + report_exception(http_exception(static_cast(error_code), errorMessage)); + } + +#ifdef _WIN32 + void report_error(unsigned long error_code, const std::wstring &errorMessage) + { + report_exception(http_exception(static_cast(error_code), errorMessage)); + } +#endif + + template + void report_exception(const _ExceptionType &e) + { + report_exception(std::make_exception_ptr(e)); + } + + virtual void report_exception(std::exception_ptr exceptionPtr) + { + auto response_impl = m_response._get_impl(); + + // If cancellation has been triggered then ignore any errors. + if(m_request._cancellation_token().is_canceled()) + { + exceptionPtr = std::make_exception_ptr(http_exception((int)std::errc::operation_canceled, std::generic_category())); + } + + // First try to complete the headers with an exception. + if(m_request_completion.set_exception(exceptionPtr)) + { + // Complete the request with no msg body. The exception + // should only be propagated to one of the tce. + response_impl->_complete(0); + } + else + { + // Complete the request with an exception + response_impl->_complete(0, exceptionPtr); + } + + finish(); + } + + virtual concurrency::streams::streambuf _get_readbuffer() + { + auto instream = m_request.body(); + + _ASSERTE((bool)instream); + return instream.streambuf(); + } + + concurrency::streams::streambuf _get_writebuffer() + { + auto outstream = m_response._get_impl()->outstream(); + + _ASSERTE((bool)outstream); + return outstream.streambuf(); + } + + // Reference to the http_client implementation. + std::shared_ptr<_http_client_communicator> m_http_client; + + // request/response pair. + http_request m_request; + http_response m_response; + + utility::size64_t m_uploaded; + utility::size64_t m_downloaded; + + // task completion event to signal request is completed. + pplx::task_completion_event m_request_completion; + + // Registration for cancellation notification if enabled. + pplx::cancellation_token_registration m_cancellationRegistration; + +protected: + + request_context(const std::shared_ptr<_http_client_communicator> &client, const http_request &request) + : m_http_client(client), + m_request(request), + m_uploaded(0), + m_downloaded(0) + { + auto responseImpl = m_response._get_impl(); + + // Copy the user specified output stream over to the response + responseImpl->set_outstream(request._get_impl()->_response_stream(), false); + + // Prepare for receiving data from the network. Ideally, this should be done after + // we receive the headers and determine that there is a response body. We will do it here + // since it is not immediately apparent where that would be in the callback handler + responseImpl->_prepare_to_receive_data(); + } + + virtual void finish(); +}; + +// +// Interface used by client implementations. Concrete implementations are responsible for +// sending HTTP requests and receiving the responses. +// +class _http_client_communicator +{ +public: + + // Destructor to clean up any held resources. + virtual ~_http_client_communicator() {} + + // Asynchronously send a HTTP request and process the response. + void async_send_request(const std::shared_ptr &request) + { + if(m_client_config.guarantee_order()) + { + // Send to call block to be processed. + push_request(request); + } + else + { + // Schedule a task to start sending. + pplx::create_task([this, request] + { + open_and_send_request(request); + }); + } + } + + void finish_request() + { + // If guarantee order is specified we don't need to do anything. + if(m_client_config.guarantee_order()) + { + pplx::extensibility::scoped_critical_section_t l(m_open_lock); + + --m_scheduled; + + if( !m_requests_queue.empty()) + { + auto request = m_requests_queue.front(); + m_requests_queue.pop(); + + // Schedule a task to start sending. + pplx::create_task([this, request] + { + open_and_send_request(request); + }); + } + } + } + + const http_client_config& client_config() const + { + return m_client_config; + } + + const uri & base_uri() const + { + return m_uri; + } + +protected: + _http_client_communicator(http::uri address, http_client_config client_config) + : m_uri(std::move(address)), m_client_config(std::move(client_config)), m_opened(false), m_scheduled(0) + { + } + + // Method to open client. + virtual unsigned long open() = 0; + + // HTTP client implementations must implement send_request. + virtual void send_request(_In_ const std::shared_ptr &request) = 0; + + // URI to connect to. + const http::uri m_uri; + +private: + + http_client_config m_client_config; + + bool m_opened; + + pplx::extensibility::critical_section_t m_open_lock; + + // Wraps opening the client around sending a request. + void open_and_send_request(const std::shared_ptr &request) + { + // First see if client needs to be opened. + auto error = open_if_required(); + + if (error != 0) + { + // Failed to open + request->report_error(error, _XPLATSTR("Open failed")); + + // DO NOT TOUCH the this pointer after completing the request + // This object could be freed along with the request as it could + // be the last reference to this object + return; + } + + send_request(request); + } + + unsigned long open_if_required() + { + unsigned long error = 0; + + if(!m_opened) + { + pplx::extensibility::scoped_critical_section_t l(m_open_lock); + + // Check again with the lock held + if (!m_opened) + { + error = open(); + + if (error == 0) + { + m_opened = true; + } + } + } + + return error; + } + + void push_request(const std::shared_ptr &request) + { + pplx::extensibility::scoped_critical_section_t l(m_open_lock); + + if(++m_scheduled == 1) + { + // Schedule a task to start sending. + pplx::create_task([this, request]() + { + open_and_send_request(request); + }); + } + else + { + m_requests_queue.push(request); + } + } + + // Queue used to guarantee ordering of requests, when applicable. + std::queue> m_requests_queue; + int m_scheduled; +}; + +inline void request_context::finish() +{ + // If cancellation is enabled and registration was performed, unregister. + if(m_cancellationRegistration != pplx::cancellation_token_registration()) + { + _ASSERTE(m_request._cancellation_token() != pplx::cancellation_token::none()); + m_request._cancellation_token().deregister_callback(m_cancellationRegistration); + } + + m_http_client->finish_request(); +} + +class http_network_handler : public http_pipeline_stage +{ +public: + http_network_handler(const uri &base_uri, const http_client_config &client_config); + + virtual pplx::task propagate(http_request request); + + const std::shared_ptr& http_client_impl() const + { + return m_http_client_impl; + } + +private: + std::shared_ptr<_http_client_communicator> m_http_client_impl; +}; + +// Helper function to check to make sure the uri is valid. +void verify_uri(const uri &uri) +{ + // Some things like proper URI schema are verified by the URI class. + // We only need to check certain things specific to HTTP. + if (uri.scheme() != _XPLATSTR("http") && uri.scheme() != _XPLATSTR("https")) + { + throw std::invalid_argument("URI scheme must be 'http' or 'https'"); + } + + if(uri.host().empty()) + { + throw std::invalid_argument("URI must contain a hostname."); + } +} + +} // namespace details + +http_client::http_client(const uri &base_uri) +{ + build_pipeline(base_uri, http_client_config()); +} + +http_client::http_client(const uri &base_uri, const http_client_config &client_config) +{ + build_pipeline(base_uri, client_config); +} + +void http_client::build_pipeline(const uri &base_uri, const http_client_config &client_config) +{ + if (base_uri.scheme().empty()) + { + auto uribuilder = uri_builder(base_uri); + uribuilder.set_scheme(_XPLATSTR("http")); + uri uriWithScheme = uribuilder.to_uri(); + details::verify_uri(uriWithScheme); + m_pipeline = ::web::http::http_pipeline::create_pipeline(std::make_shared(uriWithScheme, client_config)); + } + else + { + details::verify_uri(base_uri); + m_pipeline = ::web::http::http_pipeline::create_pipeline(std::make_shared(base_uri, client_config)); + } + +#if !defined(CPPREST_TARGET_XP) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP || _MSC_VER > 1700) + add_handler(std::static_pointer_cast( + std::make_shared(client_config.oauth1()))); +#endif + + add_handler(std::static_pointer_cast( + std::make_shared(client_config.oauth2()))); +} + +const http_client_config & http_client::client_config() const +{ + auto ph = std::static_pointer_cast(m_pipeline->last_stage()); + return ph->http_client_impl()->client_config(); +} + +const uri & http_client::base_uri() const +{ + auto ph = std::static_pointer_cast(m_pipeline->last_stage()); + return ph->http_client_impl()->base_uri(); +} + +}}} // namespaces diff --git a/3rdparty/cpprestsdk/include/cpprest/details/http_constants.dat b/3rdparty/cpprestsdk/include/cpprest/details/http_constants.dat new file mode 100644 index 00000000..a36e931a --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/http_constants.dat @@ -0,0 +1,179 @@ +#ifdef _METHODS +DAT(GET, _XPLATSTR("GET")) +DAT(POST, _XPLATSTR("POST")) +DAT(PUT, _XPLATSTR("PUT")) +DAT(DEL, _XPLATSTR("DELETE")) +DAT(HEAD, _XPLATSTR("HEAD")) +DAT(OPTIONS, _XPLATSTR("OPTIONS")) +DAT(TRCE, _XPLATSTR("TRACE")) +DAT(CONNECT, _XPLATSTR("CONNECT")) +DAT(MERGE, _XPLATSTR("MERGE")) +DAT(PATCH, _XPLATSTR("PATCH")) +#endif + +#ifdef _PHRASES +DAT(Continue, 100, _XPLATSTR("Continue")) +DAT(SwitchingProtocols, 101, _XPLATSTR("Switching Protocols")) +DAT(OK, 200, _XPLATSTR("OK")) +DAT(Created, 201, _XPLATSTR("Created")) +DAT(Accepted, 202, _XPLATSTR("Accepted")) +DAT(NonAuthInfo, 203, _XPLATSTR("Non-Authoritative Information")) +DAT(NoContent, 204, _XPLATSTR("No Content")) +DAT(ResetContent, 205, _XPLATSTR("Reset Content")) +DAT(PartialContent, 206, _XPLATSTR("Partial Content")) +DAT(MultipleChoices, 300, _XPLATSTR("Multiple Choices")) +DAT(MovedPermanently, 301, _XPLATSTR("Moved Permanently")) +DAT(Found, 302, _XPLATSTR("Found")) +DAT(SeeOther, 303, _XPLATSTR("See Other")) +DAT(NotModified, 304, _XPLATSTR("Not Modified")) +DAT(UseProxy, 305, _XPLATSTR("Use Proxy")) +DAT(TemporaryRedirect, 307, _XPLATSTR("Temporary Redirect")) +DAT(BadRequest, 400, _XPLATSTR("Bad Request")) +DAT(Unauthorized, 401, _XPLATSTR("Unauthorized")) +DAT(PaymentRequired, 402, _XPLATSTR("Payment Required")) +DAT(Forbidden, 403, _XPLATSTR("Forbidden")) +DAT(NotFound, 404, _XPLATSTR("Not Found")) +DAT(MethodNotAllowed, 405, _XPLATSTR("Method Not Allowed")) +DAT(NotAcceptable, 406, _XPLATSTR("Not Acceptable")) +DAT(ProxyAuthRequired, 407, _XPLATSTR("Proxy Authentication Required")) +DAT(RequestTimeout, 408, _XPLATSTR("Request Time-out")) +DAT(Conflict, 409, _XPLATSTR("Conflict")) +DAT(Gone, 410, _XPLATSTR("Gone")) +DAT(LengthRequired, 411, _XPLATSTR("Length Required")) +DAT(PreconditionFailed, 412, _XPLATSTR("Precondition Failed")) +DAT(RequestEntityTooLarge, 413, _XPLATSTR("Request Entity Too Large")) +DAT(RequestUriTooLarge, 414, _XPLATSTR("Request Uri Too Large")) +DAT(UnsupportedMediaType, 415, _XPLATSTR("Unsupported Media Type")) +DAT(RangeNotSatisfiable, 416, _XPLATSTR("Requested range not satisfiable")) +DAT(ExpectationFailed, 417, _XPLATSTR("Expectation Failed")) +DAT(InternalError, 500, _XPLATSTR("Internal Error")) +DAT(NotImplemented, 501, _XPLATSTR("Not Implemented")) +DAT(BadGateway, 502, _XPLATSTR("Bad Gateway")) +DAT(ServiceUnavailable, 503, _XPLATSTR("Service Unavailable")) +DAT(GatewayTimeout, 504, _XPLATSTR("Gateway Time-out")) +DAT(HttpVersionNotSupported, 505, _XPLATSTR("HTTP Version not supported")) +#endif // _PHRASES + +#ifdef _HEADER_NAMES +DAT(accept, "Accept") +DAT(accept_charset, "Accept-Charset") +DAT(accept_encoding, "Accept-Encoding") +DAT(accept_language, "Accept-Language") +DAT(accept_ranges, "Accept-Ranges") +DAT(age, "Age") +DAT(allow, "Allow") +DAT(authorization, "Authorization") +DAT(cache_control, "Cache-Control") +DAT(connection, "Connection") +DAT(content_encoding, "Content-Encoding") +DAT(content_language, "Content-Language") +DAT(content_length, "Content-Length") +DAT(content_location, "Content-Location") +DAT(content_md5, "Content-MD5") +DAT(content_range, "Content-Range") +DAT(content_type, "Content-Type") +DAT(content_disposition, "Content-Disposition") +DAT(date, "Date") +DAT(etag, "ETag") +DAT(expect, "Expect") +DAT(expires, "Expires") +DAT(from, "From") +DAT(host, "Host") +DAT(if_match, "If-Match") +DAT(if_modified_since, "If-Modified-Since") +DAT(if_none_match, "If-None-Match") +DAT(if_range, "If-Range") +DAT(if_unmodified_since, "If-Unmodified-Since") +DAT(last_modified, "Last-Modified") +DAT(location, "Location") +DAT(max_forwards, "Max-Forwards") +DAT(pragma, "Pragma") +DAT(proxy_authenticate, "Proxy-Authenticate") +DAT(proxy_authorization, "Proxy-Authorization") +DAT(range, "Range") +DAT(referer, "Referer") +DAT(retry_after, "Retry-After") +DAT(server, "Server") +DAT(te, "TE") +DAT(trailer, "Trailer") +DAT(transfer_encoding, "Transfer-Encoding") +DAT(upgrade, "Upgrade") +DAT(user_agent, "User-Agent") +DAT(vary, "Vary") +DAT(via, "Via") +DAT(warning, "Warning") +DAT(www_authenticate, "WWW-Authenticate") +#endif // _HEADER_NAMES + +#ifdef _MIME_TYPES +DAT(application_atom_xml, "application/atom+xml") +DAT(application_http, "application/http") +DAT(application_javascript, "application/javascript") +DAT(application_json, "application/json") +DAT(application_xjson, "application/x-json") +DAT(application_octetstream, "application/octet-stream") +DAT(application_x_www_form_urlencoded, "application/x-www-form-urlencoded") +DAT(multipart_form_data, "multipart/form-data") +DAT(boundary, "boundary") +DAT(form_data, "form-data") +DAT(application_xjavascript, "application/x-javascript") +DAT(application_xml, "application/xml") +DAT(message_http, "message/http") +DAT(text, "text") +DAT(text_javascript, "text/javascript") +DAT(text_json, "text/json") +DAT(text_plain, "text/plain") +DAT(text_plain_utf16, "text/plain; charset=utf-16") +DAT(text_plain_utf16le, "text/plain; charset=utf-16le") +DAT(text_plain_utf8, "text/plain; charset=utf-8") +DAT(text_xjavascript, "text/x-javascript") +DAT(text_xjson, "text/x-json") +#endif // _MIME_TYPES + +#ifdef _CHARSET_TYPES +DAT(ascii, "ascii") +DAT(usascii, "us-ascii") +DAT(latin1, "iso-8859-1") +DAT(utf8, "utf-8") +DAT(utf16, "utf-16") +DAT(utf16le, "utf-16le") +DAT(utf16be, "utf-16be") +#endif // _CHARSET_TYPES + +#ifdef _OAUTH1_METHODS +DAT(hmac_sha1, _XPLATSTR("HMAC-SHA1")) +DAT(plaintext, _XPLATSTR("PLAINTEXT")) +#endif // _OAUTH1_METHODS + +#ifdef _OAUTH1_STRINGS +DAT(callback, "oauth_callback") +DAT(callback_confirmed, "oauth_callback_confirmed") +DAT(consumer_key, "oauth_consumer_key") +DAT(nonce, "oauth_nonce") +DAT(realm, "realm") // NOTE: No "oauth_" prefix. +DAT(signature, "oauth_signature") +DAT(signature_method, "oauth_signature_method") +DAT(timestamp, "oauth_timestamp") +DAT(token, "oauth_token") +DAT(token_secret, "oauth_token_secret") +DAT(verifier, "oauth_verifier") +DAT(version, "oauth_version") +#endif // _OAUTH1_STRINGS + +#ifdef _OAUTH2_STRINGS +DAT(access_token, "access_token") +DAT(authorization_code, "authorization_code") +DAT(bearer, "bearer") +DAT(client_id, "client_id") +DAT(client_secret, "client_secret") +DAT(code, "code") +DAT(expires_in, "expires_in") +DAT(grant_type, "grant_type") +DAT(redirect_uri, "redirect_uri") +DAT(refresh_token, "refresh_token") +DAT(response_type, "response_type") +DAT(scope, "scope") +DAT(state, "state") +DAT(token, "token") +DAT(token_type, "token_type") +#endif // _OAUTH2_STRINGS diff --git a/3rdparty/cpprestsdk/include/cpprest/details/http_helpers.h b/3rdparty/cpprestsdk/include/cpprest/details/http_helpers.h new file mode 100644 index 00000000..a1a32de6 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/http_helpers.h @@ -0,0 +1,122 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Implementation Details of the http.h layer of messaging +* +* Functions and types for interoperating with http.h from modern C++ +* This file includes windows definitions and should not be included in a public header +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#include "cpprest/http_msg.h" + +namespace web { namespace http +{ +namespace details +{ + + /// + /// Constants for MIME types. + /// + class mime_types + { + public: + #define _MIME_TYPES + #define DAT(a,b) _ASYNCRTIMP const static utility::string_t a; + #include "cpprest/details/http_constants.dat" + #undef _MIME_TYPES + #undef DAT + }; + + /// + /// Constants for charset types. + /// + class charset_types + { + public: + #define _CHARSET_TYPES + #define DAT(a,b) _ASYNCRTIMP const static utility::string_t a; + #include "cpprest/details/http_constants.dat" + #undef _CHARSET_TYPES + #undef DAT + }; + + /// + /// Determines whether or not the given content type is 'textual' according the feature specifications. + /// + bool is_content_type_textual(const utility::string_t &content_type); + + /// + /// Determines whether or not the given content type is JSON according the feature specifications. + /// + bool is_content_type_json(const utility::string_t &content_type); + + /// + /// Parses the given Content-Type header value to get out actual content type and charset. + /// If the charset isn't specified the default charset for the content type will be set. + /// + void parse_content_type_and_charset(const utility::string_t &content_type, utility::string_t &content, utility::string_t &charset); + + /// + /// Gets the default charset for given content type. If the MIME type is not textual or recognized Latin1 will be returned. + /// + utility::string_t get_default_charset(const utility::string_t &content_type); + + /// + /// Helper functions to convert a series of bytes from a charset to utf-8 or utf-16. + /// These APIs deal with checking for and handling byte order marker (BOM). + /// + utility::string_t convert_utf16_to_string_t(utf16string src); + utf16string convert_utf16_to_utf16(utf16string src); + std::string convert_utf16_to_utf8(utf16string src); + utility::string_t convert_utf16le_to_string_t(utf16string src, bool erase_bom); + std::string convert_utf16le_to_utf8(utf16string src, bool erase_bom); + utility::string_t convert_utf16be_to_string_t(utf16string src, bool erase_bom); + std::string convert_utf16be_to_utf8(utf16string src, bool erase_bom); + utf16string convert_utf16be_to_utf16le(utf16string src, bool erase_bom); + + // simple helper functions to trim whitespace. + _ASYNCRTIMP void __cdecl ltrim_whitespace(utility::string_t &str); + _ASYNCRTIMP void __cdecl rtrim_whitespace(utility::string_t &str); + _ASYNCRTIMP void __cdecl trim_whitespace(utility::string_t &str); + + bool validate_method(const utility::string_t& method); + + + namespace chunked_encoding + { + // Transfer-Encoding: chunked support + static const size_t additional_encoding_space = 12; + static const size_t data_offset = additional_encoding_space-2; + + // Add the data necessary for properly sending data with transfer-encoding: chunked. + // + // There are up to 12 additional bytes needed for each chunk: + // + // The last chunk requires 5 bytes, and is fixed. + // All other chunks require up to 8 bytes for the length, and four for the two CRLF + // delimiters. + // + _ASYNCRTIMP size_t __cdecl add_chunked_delimiters(_Out_writes_(buffer_size) uint8_t *data, _In_ size_t buffer_size, size_t bytes_read); + } + +}}} diff --git a/3rdparty/cpprestsdk/include/cpprest/details/http_server.h b/3rdparty/cpprestsdk/include/cpprest/details/http_server.h new file mode 100644 index 00000000..55837425 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/http_server.h @@ -0,0 +1,81 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: interface to implement HTTP server to service http_listeners. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#if _WIN32_WINNT < _WIN32_WINNT_VISTA +#error "Error: http server APIs are not supported in XP" +#endif //_WIN32_WINNT < _WIN32_WINNT_VISTA + +#include "cpprest/http_listener.h" + +namespace web { namespace http +{ +namespace experimental { +namespace details +{ + +/// +/// Interface http listeners interact with for receiving and responding to http requests. +/// +class http_server +{ +public: + + /// + /// Release any held resources. + /// + virtual ~http_server() { }; + + /// + /// Start listening for incoming requests. + /// + virtual pplx::task start() = 0; + + /// + /// Registers an http listener. + /// + virtual pplx::task register_listener(_In_ web::http::experimental::listener::details::http_listener_impl *pListener) = 0; + + /// + /// Unregisters an http listener. + /// + virtual pplx::task unregister_listener(_In_ web::http::experimental::listener::details::http_listener_impl *pListener) = 0; + + /// + /// Stop processing and listening for incoming requests. + /// + virtual pplx::task stop() = 0; + + /// + /// Asynchronously sends the specified http response. + /// + /// The http_response to send. + /// A operation which is completed once the response has been sent. + virtual pplx::task respond(http::http_response response) = 0; +}; + +} // namespace details +} // namespace experimental +}} // namespace web::http diff --git a/3rdparty/cpprestsdk/include/cpprest/details/http_server_api.h b/3rdparty/cpprestsdk/include/cpprest/details/http_server_api.h new file mode 100644 index 00000000..492f1f7b --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/http_server_api.h @@ -0,0 +1,101 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: exposes the entry points to the http server transport apis. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#if _WIN32_WINNT < _WIN32_WINNT_VISTA +#error "Error: http server APIs are not supported in XP" +#endif //_WIN32_WINNT < _WIN32_WINNT_VISTA + +#include + +#include "cpprest/http_listener.h" + +namespace web { namespace http +{ +namespace experimental { +namespace details +{ + +class http_server; + +/// +/// Singleton class used to register for http requests and send responses. +/// +/// The lifetime is tied to http listener registration. When the first listener registers an instance is created +/// and when the last one unregisters the receiver stops and is destroyed. It can be started back up again if +/// listeners are again registered. +/// +class http_server_api +{ +public: + + /// + /// Returns whether or not any listeners are registered. + /// + static bool __cdecl has_listener(); + + /// + /// Registers a HTTP server API. + /// + static void __cdecl register_server_api(std::unique_ptr server_api); + + /// + /// Clears the http server API. + /// + static void __cdecl unregister_server_api(); + + /// + /// Registers a listener for HTTP requests and starts receiving. + /// + static pplx::task __cdecl register_listener(_In_ web::http::experimental::listener::details::http_listener_impl *pListener); + + /// + /// Unregisters the given listener and stops listening for HTTP requests. + /// + static pplx::task __cdecl unregister_listener(_In_ web::http::experimental::listener::details::http_listener_impl *pListener); + + /// + /// Gets static HTTP server API. Could be null if no registered listeners. + /// + static http_server * __cdecl server_api(); + +private: + + /// Used to lock access to the server api registration + static pplx::extensibility::critical_section_t s_lock; + + /// Registers a server API set -- this assumes the lock has already been taken + static void unsafe_register_server_api(std::unique_ptr server_api); + + // Static instance of the HTTP server API. + static std::unique_ptr s_server_api; + + /// Number of registered listeners; + static pplx::details::atomic_long s_registrations; + + // Static only class. No creation. + http_server_api(); +}; + +}}}} // namespaces diff --git a/3rdparty/cpprestsdk/include/cpprest/details/http_server_asio.h b/3rdparty/cpprestsdk/include/cpprest/details/http_server_asio.h new file mode 100644 index 00000000..465adf9c --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/http_server_asio.h @@ -0,0 +1,202 @@ +/* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#pragma once + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#endif +#include +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#include +#include +#include "pplx/threadpool.h" +#include "cpprest/details/http_server.h" + +namespace web +{ +namespace http +{ +namespace experimental +{ +namespace listener +{ + +class http_linux_server; + +namespace details +{ + +struct linux_request_context : web::http::details::_http_server_context +{ + linux_request_context(){} + + pplx::task_completion_event m_response_completed; + +private: + linux_request_context(const linux_request_context&); + linux_request_context& operator=(const linux_request_context&); +}; + +class hostport_listener; + +class connection +{ +private: + std::unique_ptr m_socket; + boost::asio::streambuf m_request_buf; + boost::asio::streambuf m_response_buf; + http_linux_server* m_p_server; + hostport_listener* m_p_parent; + http_request m_request; + size_t m_read, m_write; + size_t m_read_size, m_write_size; + bool m_close; + bool m_chunked; + std::atomic m_refs; // track how many threads are still referring to this + +public: + connection(std::unique_ptr socket, http_linux_server* server, hostport_listener* parent) + : m_socket(std::move(socket)) + , m_request_buf() + , m_response_buf() + , m_p_server(server) + , m_p_parent(parent) + , m_refs(1) + { + start_request_response(); + } + + connection(const connection&) = delete; + connection& operator=(const connection&) = delete; + + void close(); + +private: + void start_request_response(); + void handle_http_line(const boost::system::error_code& ec); + void handle_headers(); + void handle_body(const boost::system::error_code& ec); + void handle_chunked_header(const boost::system::error_code& ec); + void handle_chunked_body(const boost::system::error_code& ec, int toWrite); + void dispatch_request_to_listener(); + void do_response(bool bad_request); + template + void async_read_until_buffersize(size_t size, const ReadHandler &handler); + void async_process_response(http_response response); + void cancel_sending_response_with_error(const http_response &response, const std::exception_ptr &); + void handle_headers_written(const http_response &response, const boost::system::error_code& ec); + void handle_write_large_response(const http_response &response, const boost::system::error_code& ec); + void handle_write_chunked_response(const http_response &response, const boost::system::error_code& ec); + void handle_response_written(const http_response &response, const boost::system::error_code& ec); + void finish_request_response(); +}; + +class hostport_listener +{ +private: + friend class connection; + + std::unique_ptr m_acceptor; + std::map m_listeners; + pplx::extensibility::reader_writer_lock_t m_listeners_lock; + + pplx::extensibility::recursive_lock_t m_connections_lock; + pplx::extensibility::event_t m_all_connections_complete; + std::set m_connections; + + http_linux_server* m_p_server; + + std::string m_host; + std::string m_port; + +public: + hostport_listener(http_linux_server* server, const std::string& hostport) + : m_acceptor() + , m_listeners() + , m_listeners_lock() + , m_connections_lock() + , m_connections() + , m_p_server(server) + { + m_all_connections_complete.set(); + + std::istringstream hostport_in(hostport); + hostport_in.imbue(std::locale::classic()); + + std::getline(hostport_in, m_host, ':'); + std::getline(hostport_in, m_port); + } + + ~hostport_listener() + { + stop(); + } + + void start(); + void stop(); + + void add_listener(const std::string& path, web::http::experimental::listener::details::http_listener_impl* listener); + void remove_listener(const std::string& path, web::http::experimental::listener::details::http_listener_impl* listener); + +private: + void on_accept(boost::asio::ip::tcp::socket* socket, const boost::system::error_code& ec); + +}; + +} + +struct iequal_to +{ + bool operator()(const std::string& left, const std::string& right) const + { + return boost::ilexicographical_compare(left, right); + } +}; + +class http_linux_server : public web::http::experimental::details::http_server +{ +private: + friend class http::experimental::listener::details::connection; + + pplx::extensibility::reader_writer_lock_t m_listeners_lock; + std::map, iequal_to> m_listeners; + std::unordered_map> m_registered_listeners; + bool m_started; + +public: + http_linux_server() + : m_listeners_lock() + , m_listeners() + , m_started(false) + {} + + ~http_linux_server() + { + stop(); + } + + virtual pplx::task start(); + virtual pplx::task stop(); + + virtual pplx::task register_listener(web::http::experimental::listener::details::http_listener_impl* listener); + virtual pplx::task unregister_listener(web::http::experimental::listener::details::http_listener_impl* listener); + + pplx::task respond(http::http_response response); +}; + +}}}} diff --git a/3rdparty/cpprestsdk/include/cpprest/details/http_server_httpsys.h b/3rdparty/cpprestsdk/include/cpprest/details/http_server_httpsys.h new file mode 100644 index 00000000..0de4934c --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/http_server_httpsys.h @@ -0,0 +1,250 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: implementation of HTTP server API built on Windows HTTP Server APIs. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#if _WIN32_WINNT < _WIN32_WINNT_VISTA +#error "Error: http server APIs are not supported in XP" +#endif //_WIN32_WINNT < _WIN32_WINNT_VISTA + +// Windows Sockets are not code analysis clean. +#pragma warning(push) +#pragma warning(disable : 6386) +#include +#pragma warning(pop) + +#include +#include + +#include "cpprest/details/http_server.h" + +namespace web +{ +namespace http +{ +namespace experimental +{ +namespace details +{ + +class http_windows_server; +struct windows_request_context; + +/// +/// Class used to wrap OVERLAPPED I/O with any HTTP I/O. +/// +class http_overlapped : public OVERLAPPED +{ +public: + void set_http_io_completion(std::function http_io_completion) + { + ZeroMemory(this, sizeof(OVERLAPPED)); + m_http_io_completion = http_io_completion; + } + + /// + /// Callback for all I/O completions. + /// + static void CALLBACK io_completion_callback( + PTP_CALLBACK_INSTANCE instance, + PVOID context, + PVOID pOverlapped, + ULONG result, + ULONG_PTR numberOfBytesTransferred, + PTP_IO io) + { + CASABLANCA_UNREFERENCED_PARAMETER(io); + CASABLANCA_UNREFERENCED_PARAMETER(context); + CASABLANCA_UNREFERENCED_PARAMETER(instance); + + http_overlapped *p_http_overlapped = (http_overlapped *)pOverlapped; + p_http_overlapped->m_http_io_completion(result, (DWORD) numberOfBytesTransferred); + } + +private: + std::function m_http_io_completion; +}; + +/// +/// Context for http request through Windows HTTP Server API. +/// +struct windows_request_context : http::details::_http_server_context +{ + windows_request_context(); + virtual ~windows_request_context(); + + // Asynchronously starts processing the current request. + void async_process_request(HTTP_REQUEST_ID request_id, http::http_request msg, const unsigned long headers_size); + + // Dispatch request to the provided http_listener. + void dispatch_request_to_listener(_In_ web::http::experimental::listener::details::http_listener_impl *pListener); + + // Read in a portion of the request body. + void read_request_body_chunk(); + + // Start processing the response. + void async_process_response(); + + void transmit_body(); + + // Read request headers io completion callback function . + void read_headers_io_completion(DWORD error_code, DWORD bytes_read); + + // Read request body io completion callback function. + void read_body_io_completion(DWORD error_code, DWORD bytes_read); + + // Send response io completion callback function . + void send_response_io_completion(DWORD error_code, DWORD bytes_read); + + // Send response body io completion callback function. + void send_response_body_io_completion(DWORD error_code, DWORD bytes_read); + + // Cancel request io completion callback function. + void cancel_request_io_completion(DWORD error_code, DWORD bytes_read); + + // TCE that indicates the completion of response + // Workaround for ppl task_completion_event bug. + std::mutex m_responseCompletedLock; + pplx::task_completion_event m_response_completed; + + // Id of the currently processed request on this connection. + HTTP_REQUEST_ID m_request_id; + + bool m_sending_in_chunks; + bool m_transfer_encoding; + + size_t m_remaining_to_write; + + HTTP_REQUEST *m_request; + std::unique_ptr m_request_buffer; + + std::unique_ptr m_headers; + std::vector m_headers_buffer; + + http_overlapped m_overlapped; + + http_request m_msg; + http_response m_response; + + std::exception_ptr m_except_ptr; +private: + windows_request_context(const windows_request_context &); + windows_request_context& operator=(const windows_request_context &); + + // Sends entity body chunk. + void send_entity_body(_In_reads_(data_length) unsigned char * data, _In_ size_t data_length); + + // Cancels this request. + void cancel_request(std::exception_ptr except_ptr); + + std::vector m_body_data; +}; + +/// +/// Class to implement HTTP server API on Windows. +/// +class http_windows_server : public http_server +{ +public: + + /// + /// Constructs a http_windows_server. + /// + http_windows_server(); + + /// + /// Releases resources held. + /// + ~http_windows_server(); + + /// + /// Start listening for incoming requests. + /// + virtual pplx::task start(); + + /// + /// Registers an http listener. + /// + virtual pplx::task register_listener(_In_ web::http::experimental::listener::details::http_listener_impl *pListener); + + /// + /// Unregisters an http listener. + /// + virtual pplx::task unregister_listener(_In_ web::http::experimental::listener::details::http_listener_impl *pListener); + + /// + /// Stop processing and listening for incoming requests. + /// + virtual pplx::task stop(); + + /// + /// Asynchronously sends the specified http response. + /// + /// The http_response to send. + /// A operation which is completed once the response has been sent. + virtual pplx::task respond(http::http_response response); + +private: + friend struct details::windows_request_context; + + // Structure to hold each registered listener. + class listener_registration + { + public: + listener_registration(HTTP_URL_GROUP_ID urlGroupId) + : m_urlGroupId(urlGroupId) + {} + + // URL group id for this listener. Each listener needs it own URL group + // because configuration like timeouts, authentication, etc... + HTTP_URL_GROUP_ID m_urlGroupId; + + // Request handler lock to guard against removing a listener while in user code. + pplx::extensibility::reader_writer_lock_t m_requestHandlerLock; + }; + + // Registered listeners + pplx::extensibility::reader_writer_lock_t _M_listenersLock; + std::unordered_map> _M_registeredListeners; + + // HTTP Server API server session id. + HTTP_SERVER_SESSION_ID m_serverSessionId; + + // Tracks the number of outstanding requests being processed. + std::atomic m_numOutstandingRequests; + pplx::extensibility::event_t m_zeroOutstandingRequests; + + // Handle to HTTP Server API request queue. + HANDLE m_hRequestQueue; + + // Threadpool I/O structure for overlapped I/O. + TP_IO * m_threadpool_io; + + // Task which actually handles receiving requests from HTTP Server API request queue. + pplx::task m_receivingTask; + void receive_requests(); +}; + +} // namespace details; +} // namespace experimental +}} // namespace web::http diff --git a/3rdparty/cpprestsdk/include/cpprest/details/nosal.h b/3rdparty/cpprestsdk/include/cpprest/details/nosal.h new file mode 100644 index 00000000..d211694d --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/nosal.h @@ -0,0 +1,89 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +***/ + +#pragma once +// selected MS SAL annotations + +#ifdef _In_ +#undef _In_ +#endif +#define _In_ + +#ifdef _Inout_ +#undef _Inout_ +#endif +#define _Inout_ + +#ifdef _Out_ +#undef _Out_ +#endif +#define _Out_ + +#ifdef _In_z_ +#undef _In_z_ +#endif +#define _In_z_ + +#ifdef _Out_z_ +#undef _Out_z_ +#endif +#define _Out_z_ + +#ifdef _Inout_z_ +#undef _Inout_z_ +#endif +#define _Inout_z_ + +#ifdef _In_opt_ +#undef _In_opt_ +#endif +#define _In_opt_ + +#ifdef _Out_opt_ +#undef _Out_opt_ +#endif +#define _Out_opt_ + +#ifdef _Inout_opt_ +#undef _Inout_opt_ +#endif +#define _Inout_opt_ + +#ifdef _Out_writes_ +#undef _Out_writes_ +#endif +#define _Out_writes_(x) + +#ifdef _Out_writes_opt_ +#undef _Out_writes_opt_ +#endif +#define _Out_writes_opt_(x) + +#ifdef _In_reads_ +#undef _In_reads_ +#endif +#define _In_reads_(x) + +#ifdef _Inout_updates_bytes_ +#undef _Inout_updates_bytes_ +#endif +#define _Inout_updates_bytes_(x) \ No newline at end of file diff --git a/3rdparty/cpprestsdk/include/cpprest/details/resource.h b/3rdparty/cpprestsdk/include/cpprest/details/resource.h new file mode 100644 index 00000000..7ca31da7 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Resource.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/3rdparty/cpprestsdk/include/cpprest/details/uri_parser.h b/3rdparty/cpprestsdk/include/cpprest/details/uri_parser.h new file mode 100644 index 00000000..fd7530f1 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/uri_parser.h @@ -0,0 +1,215 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* URI parsing implementation +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include + +namespace web { namespace details +{ + namespace uri_parser + { + + /// + /// Parses the uri, attempting to determine its validity. + /// + /// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query') + /// + bool validate(const utility::string_t &encoded_string); + + /// + /// Parses the uri, setting each provided string to the value of that component. Components + /// that are not part of the provided text are set to the empty string. Component strings + /// DO NOT contain their beginning or ending delimiters. + /// + /// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query') + /// + bool parse(const utility::string_t &encoded_string, uri_components &components); + + /// + /// Unreserved characters are those that are allowed in a URI but do not have a reserved purpose. They include: + /// - A-Z + /// - a-z + /// - 0-9 + /// - '-' (hyphen) + /// - '.' (period) + /// - '_' (underscore) + /// - '~' (tilde) + /// + inline bool is_unreserved(int c) + { + return ::utility::details::is_alnum((char)c) || c == '-' || c == '.' || c == '_' || c == '~'; + } + + /// + /// General delimiters serve as the delimiters between different uri components. + /// General delimiters include: + /// - All of these :/?#[]@ + /// + inline bool is_gen_delim(int c) + { + return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@'; + } + + /// + /// Subdelimiters are those characters that may have a defined meaning within component + /// of a uri for a particular scheme. They do not serve as delimiters in any case between + /// uri segments. sub_delimiters include: + /// - All of these !$&'()*+,;= + /// + inline bool is_sub_delim(int c) + { + switch (c) + { + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + return true; + default: + return false; + } + } + + /// + /// Reserved characters includes the general delimiters and sub delimiters. Some characters + /// are neither reserved nor unreserved, and must be percent-encoded. + /// + inline bool is_reserved(int c) + { + return is_gen_delim(c) || is_sub_delim(c); + } + + /// + /// Legal characters in the scheme portion include: + /// - Any alphanumeric character + /// - '+' (plus) + /// - '-' (hyphen) + /// - '.' (period) + /// + /// Note that the scheme must BEGIN with an alpha character. + /// + inline bool is_scheme_character(int c) + { + return ::utility::details::is_alnum((char)c) || c == '+' || c == '-' || c == '.'; + } + + /// + /// Legal characters in the user information portion include: + /// - Any unreserved character + /// - The percent character ('%'), and thus any percent-endcoded octet + /// - The sub-delimiters + /// - ':' (colon) + /// + inline bool is_user_info_character(int c) + { + return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == ':'; + } + + /// + /// Legal characters in the host portion include: + /// - Any unreserved character + /// - The percent character ('%'), and thus any percent-endcoded octet + /// - The sub-delimiters + /// - ':' (colon) + /// - '[' (open bracket) + /// - ']' (close bracket) + /// + inline bool is_host_character(int c) + { + return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == ':' || c == '[' || c == ']'; + } + + /// + /// Legal characters in the authority portion include: + /// - Any unreserved character + /// - The percent character ('%'), and thus any percent-endcoded octet + /// - The sub-delimiters + /// - ':' (colon) + /// + /// Note that we don't currently support: + /// - IPv6 addresses (requires '[]') + /// + inline bool is_authority_character(int c) + { + return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == '@' || c == ':'; + } + + /// + /// Legal characters in the path portion include: + /// - Any unreserved character + /// - The percent character ('%'), and thus any percent-endcoded octet + /// - The sub-delimiters + /// - ':' (colon) + /// - '@' (ampersand) + /// + inline bool is_path_character(int c) + { + return is_unreserved(c) || is_sub_delim(c) || c == '%' || c == '/' || c == ':' || c == '@'; + } + + /// + /// Legal characters in the query portion include: + /// - Any path character + /// - '?' (question mark) + /// + inline bool is_query_character(int c) + { + return is_path_character(c) || c == '?'; + } + + /// + /// Legal characters in the fragment portion include: + /// - Any path character + /// - '?' (question mark) + /// + inline bool is_fragment_character(int c) + { + // this is intentional, they have the same set of legal characters + return is_query_character(c); + } + + /// + /// Parses the uri, setting the given pointers to locations inside the given buffer. + /// 'encoded' is expected to point to an encoded zero-terminated string containing a uri + /// + bool inner_parse( + const utility::char_t *encoded, + const utility::char_t **scheme_begin, const utility::char_t **scheme_end, + const utility::char_t **uinfo_begin, const utility::char_t **uinfo_end, + const utility::char_t **host_begin, const utility::char_t **host_end, + _Out_ int *port, + const utility::char_t **path_begin, const utility::char_t **path_end, + const utility::char_t **query_begin, const utility::char_t **query_end, + const utility::char_t **fragment_begin, const utility::char_t **fragment_end); + } +}} diff --git a/3rdparty/cpprestsdk/include/cpprest/details/web_utilities.h b/3rdparty/cpprestsdk/include/cpprest/details/web_utilities.h new file mode 100644 index 00000000..d7cebab6 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/web_utilities.h @@ -0,0 +1,248 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* utility classes used by the different web:: clients +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#include "cpprest/asyncrt_utils.h" + +namespace web +{ + +namespace http { namespace client { namespace details { +class winhttp_client; +class winrt_client; +}}} +namespace websockets { namespace client { namespace details { +class winrt_callback_client; +class wspp_callback_client; +}}} + +namespace details +{ + +class zero_memory_deleter +{ +public: + _ASYNCRTIMP void operator()(::utility::string_t *data) const; +}; +typedef std::unique_ptr<::utility::string_t, zero_memory_deleter> plaintext_string; + +#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) +#if defined(__cplusplus_winrt) +#if !(WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP && _MSC_VER < 1800) +class winrt_encryption +{ +public: + winrt_encryption() {} + _ASYNCRTIMP winrt_encryption(const std::wstring &data); + _ASYNCRTIMP plaintext_string decrypt() const; +private: + ::pplx::task m_buffer; +}; +#endif +#else +class win32_encryption +{ +public: + win32_encryption() {} + _ASYNCRTIMP win32_encryption(const std::wstring &data); + _ASYNCRTIMP ~win32_encryption(); + _ASYNCRTIMP plaintext_string decrypt() const; +private: + std::vector m_buffer; + size_t m_numCharacters; +}; +#endif +#endif +} + +/// +/// Represents a set of user credentials (user name and password) to be used +/// for authentication. +/// +class credentials +{ +public: + /// + /// Constructs an empty set of credentials without a user name or password. + /// + credentials() {} + + /// + /// Constructs credentials from given user name and password. + /// + /// User name as a string. + /// Password as a string. + credentials(utility::string_t username, const utility::string_t & password) : + m_username(std::move(username)), + m_password(password) + {} + + /// + /// The user name associated with the credentials. + /// + /// A string containing the user name. + const utility::string_t &username() const { return m_username; } + + /// + /// The password for the user name associated with the credentials. + /// + /// A string containing the password. + CASABLANCA_DEPRECATED("This API is deprecated for security reasons to avoid unnecessary password copies stored in plaintext.") + utility::string_t password() const + { +#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) +#if defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP && _MSC_VER < 1800 + return m_password; +#else + return utility::string_t(*m_password.decrypt()); +#endif +#else + return m_password; +#endif + } + + /// + /// Checks if credentials have been set + /// + /// true if user name and password is set, false otherwise. + bool is_set() const { return !m_username.empty(); } + +private: + friend class http::client::details::winhttp_client; + friend class http::client::details::winrt_client; + friend class websockets::client::details::winrt_callback_client; + friend class websockets::client::details::wspp_callback_client; + + details::plaintext_string decrypt() const + { + // Encryption APIs not supported on Windows Phone 8.0 or XP +#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) +#if defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP && _MSC_VER < 1800 + return details::plaintext_string(new ::utility::string_t(m_password)); +#else + return m_password.decrypt(); +#endif +#else + return details::plaintext_string(new ::utility::string_t(m_password)); +#endif + } + + ::utility::string_t m_username; + +#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) +#if defined(__cplusplus_winrt) +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP && _MSC_VER < 1800 + ::utility::string_t m_password; +#else + details::winrt_encryption m_password; +#endif +#else + details::win32_encryption m_password; +#endif +#else + ::utility::string_t m_password; +#endif +}; + +/// +/// web_proxy represents the concept of the web proxy, which can be auto-discovered, +/// disabled, or specified explicitly by the user. +/// +class web_proxy +{ + enum web_proxy_mode_internal{ use_default_, use_auto_discovery_, disabled_, user_provided_ }; +public: + enum web_proxy_mode{ use_default = use_default_, use_auto_discovery = use_auto_discovery_, disabled = disabled_}; + + /// + /// Constructs a proxy with the default settings. + /// + web_proxy() : m_address(_XPLATSTR("")), m_mode(use_default_) {} + + /// + /// Creates a proxy with specified mode. + /// + /// Mode to use. + web_proxy( web_proxy_mode mode ) : m_address(_XPLATSTR("")), m_mode(static_cast(mode)) {} + + /// + /// Creates a proxy explicitly with provided address. + /// + /// Proxy URI to use. + web_proxy( uri address ) : m_address(address), m_mode(user_provided_) {} + + /// + /// Gets this proxy's URI address. Returns an empty URI if not explicitly set by user. + /// + /// A reference to this proxy's URI. + const uri& address() const { return m_address; } + + /// + /// Gets the credentials used for authentication with this proxy. + /// + /// Credentials to for this proxy. + const web::credentials& credentials() const { return m_credentials; } + + /// + /// Sets the credentials to use for authentication with this proxy. + /// + /// Credentials to use for this proxy. + void set_credentials(web::credentials cred) { + if( m_mode == disabled_ ) + { + throw std::invalid_argument("Cannot attach credentials to a disabled proxy"); + } + m_credentials = std::move(cred); + } + + /// + /// Checks if this proxy was constructed with default settings. + /// + /// True if default, false otherwise. + bool is_default() const { return m_mode == use_default_; } + + /// + /// Checks if using a proxy is disabled. + /// + /// True if disabled, false otherwise. + bool is_disabled() const { return m_mode == disabled_; } + + /// + /// Checks if the auto discovery protocol, WPAD, is to be used. + /// + /// True if auto discovery enabled, false otherwise. + bool is_auto_discovery() const { return m_mode == use_auto_discovery_; } + + /// + /// Checks if a proxy address is explicitly specified by the user. + /// + /// True if a proxy address was explicitly specified, false otherwise. + bool is_specified() const { return m_mode == user_provided_; } + +private: + web::uri m_address; + web_proxy_mode_internal m_mode; + web::credentials m_credentials; +}; + +} diff --git a/3rdparty/cpprestsdk/include/cpprest/details/x509_cert_utilities.h b/3rdparty/cpprestsdk/include/cpprest/details/x509_cert_utilities.h new file mode 100644 index 00000000..297700b6 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/details/x509_cert_utilities.h @@ -0,0 +1,57 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Contains utility functions for helping to verify server certificates in OS X/iOS and Android. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include +#include + +#if defined(__APPLE__) || (defined(ANDROID) || defined(__ANDROID__)) || (defined(_WIN32) && !defined(__cplusplus_winrt) && !defined(_M_ARM) && !defined(CPPREST_EXCLUDE_WEBSOCKETS)) + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4005) +#endif +#include +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +namespace web { namespace http { namespace client { namespace details { + +/// +/// Using platform specific APIs verifies server certificate. +/// Currently implemented to work on iOS, Android, and OS X. +/// +/// Boost.ASIO context get certificate chain from. +/// Host name from the URI. +/// True if verification passed and server can be trusted, false otherwise. +bool verify_cert_chain_platform_specific(boost::asio::ssl::verify_context &verifyCtx, const std::string &hostName); + +bool verify_X509_cert_chain(const std::vector &certChain, const std::string &hostName); + +}}}} + +#endif \ No newline at end of file diff --git a/3rdparty/cpprestsdk/include/cpprest/filestream.h b/3rdparty/cpprestsdk/include/cpprest/filestream.h new file mode 100644 index 00000000..0565a7f3 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/filestream.h @@ -0,0 +1,1132 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Asynchronous File streams +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#ifndef _CASA_FILE_STREAMS_H +#define _CASA_FILE_STREAMS_H + +#include "cpprest/details/fileio.h" +#include "cpprest/astreambuf.h" +#include "cpprest/streams.h" +#include + +#ifndef _CONCRT_H +#ifndef _LWRCASE_CNCRRNCY +#define _LWRCASE_CNCRRNCY +// Note to reader: we're using lower-case namespace names everywhere, but the 'Concurrency' namespace +// is capitalized for historical reasons. The alias let's us pretend that style issue doesn't exist. +namespace Concurrency { } +namespace concurrency = Concurrency; +#endif +#endif + +namespace Concurrency { namespace streams +{ + // Forward declarations + template class file_buffer; + +namespace details { + // This operation queue is NOT thread safe + class async_operation_queue + { + pplx::task m_lastOperation; + public: + async_operation_queue() + { + m_lastOperation = pplx::task_from_result(); + } + + // It only accepts functors that take no argument and return pplx::task + // This function may execute op inline, thus it could throw immediately + template + auto enqueue_operation(Func &&op) -> decltype(op()) + { + decltype(op()) res; // res is task , which always has default constructor + if (m_lastOperation.is_done()) + { + res = op(); // Exceptions are expected to be thrown directly without catching + if (res.is_done()) + return res; + } + else + { + res = m_lastOperation.then([=] { + return op(); // It will cause task unwrapping + }); + } + m_lastOperation = res.then([&] (decltype(op())) { + // This empty task is necessary for keeping the rest of the operations on the list running + // even when the previous operation gets error. + // Don't observe exception here. + }); + return res; + } + + void wait() const + { + m_lastOperation.wait(); + } + }; + + + /// + /// Private stream buffer implementation for file streams. + /// The class itself should not be used in application code, it is used by the stream definitions farther down in the header file. + /// + template + class basic_file_buffer : public details::streambuf_state_manager<_CharType> + { + public: + typedef typename basic_streambuf<_CharType>::traits traits; + typedef typename basic_streambuf<_CharType>::int_type int_type; + typedef typename basic_streambuf<_CharType>::pos_type pos_type; + typedef typename basic_streambuf<_CharType>::off_type off_type; + + virtual ~basic_file_buffer() + { + if( this->can_read() ) + { + this->_close_read().wait(); + } + + if (this->can_write()) + { + this->_close_write().wait(); + } + } + + protected: + + /// + /// can_seek is used to determine whether a stream buffer supports seeking. + /// + virtual bool can_seek() const { return this->is_open(); } + + /// + /// has_size is used to determine whether a stream buffer supports size(). + /// + virtual bool has_size() const { return this->is_open(); } + + virtual utility::size64_t size() const + { + if (!this->is_open()) + return 0; + return _get_size(m_info, sizeof(_CharType)); + } + + + /// + /// Gets the stream buffer size, if one has been set. + /// + /// The direction of buffering (in or out) + /// An implementation that does not support buffering will always return '0'. + virtual size_t buffer_size(std::ios_base::openmode direction = std::ios_base::in) const + { + if ( direction == std::ios_base::in ) + return m_info->m_buffer_size; + else + return 0; + } + + /// + /// Sets the stream buffer implementation to buffer or not buffer. + /// + /// The size to use for internal buffering, 0 if no buffering should be done. + /// The direction of buffering (in or out) + /// An implementation that does not support buffering will silently ignore calls to this function and it will not have + /// any effect on what is returned by subsequent calls to buffer_size(). + virtual void set_buffer_size(size_t size, std::ios_base::openmode direction = std::ios_base::in) + { + if ( direction == std::ios_base::out ) return; + + m_info->m_buffer_size = size; + + if ( size == 0 && m_info->m_buffer != nullptr ) + { + delete m_info->m_buffer; + m_info->m_buffer = nullptr; + } + } + + /// + /// For any input stream, in_avail returns the number of characters that are immediately available + /// to be consumed without blocking. May be used in conjunction with to read data without + /// incurring the overhead of using tasks. + /// + virtual size_t in_avail() const + { + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); + + return _in_avail_unprot(); + } + + size_t _in_avail_unprot() const + { + if ( !this->is_open() ) return 0; + + if ( m_info->m_buffer == nullptr || m_info->m_buffill == 0 ) return 0; + if ( m_info->m_bufoff > m_info->m_rdpos || (m_info->m_bufoff+m_info->m_buffill) < m_info->m_rdpos ) return 0; + + msl::safeint3::SafeInt rdpos(m_info->m_rdpos); + msl::safeint3::SafeInt buffill(m_info->m_buffill); + msl::safeint3::SafeInt bufpos = rdpos - m_info->m_bufoff; + + return buffill - bufpos; + } + + _file_info * _close_stream() + { + // indicate that we are no longer open + auto fileInfo = m_info; + m_info = nullptr; + return fileInfo; + } + + static pplx::task _close_file(_In_ _file_info * fileInfo) + { + pplx::task_completion_event result_tce; + auto callback = new _filestream_callback_close(result_tce); + + if ( !_close_fsb_nolock(&fileInfo, callback) ) + { + delete callback; + return pplx::task_from_result(); + } + return pplx::create_task(result_tce); + } + + // Workaround GCC compiler bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58972 + void _invoke_parent_close_read() + { + streambuf_state_manager<_CharType>::_close_read(); + } + + pplx::task _close_read() + { + return m_readOps.enqueue_operation([this] + { + _invoke_parent_close_read(); + + if (this->can_write()) + { + return pplx::task_from_result(); + } + else + { + // Neither heads are open. Close the underlying device + // to indicate that we are no longer open + auto fileInfo = _close_stream(); + + return _close_file(fileInfo); + } + }); + } + + pplx::task _close_write() + { + streambuf_state_manager<_CharType>::_close_write(); + if (this->can_read()) + { + // Read head is still open. Just flush the write data + return flush_internal(); + } + else + { + // Neither heads are open. Close the underlying device + + // We need to flush all writes if the file was opened for writing. + return flush_internal().then([=](pplx::task flushTask) -> pplx::task + { + // swallow exception from flush + try + { + flushTask.wait(); + } + catch(...) + { + } + + // indicate that we are no longer open + auto fileInfo = this->_close_stream(); + + return this->_close_file(fileInfo); + }); + } + } + + /// + /// Writes a single byte to an output stream. + /// + /// The byte to write + /// A task that holds the value of the byte written. This is EOF if the write operation fails. + virtual pplx::task _putc(_CharType ch) + { + auto result_tce = pplx::task_completion_event(); + auto callback = new _filestream_callback_write(m_info, result_tce); + + // Potentially we should consider deprecating this API, it is TERRIBLY inefficient. + std::shared_ptr<_CharType> sharedCh; + try + { + sharedCh = std::make_shared<_CharType>(ch); + } catch (const std::bad_alloc &) + { + delete callback; + throw; + } + + size_t written = _putn_fsb(m_info, callback, sharedCh.get(), 1, sizeof(_CharType)); + if (written == sizeof(_CharType)) + { + delete callback; + return pplx::task_from_result(ch); + } + + return pplx::create_task(result_tce).then([sharedCh](size_t) + { + return static_cast(*sharedCh); + }); + } + + /// + /// Allocates a contiguous memory block and returns it. + /// + /// The number of characters to allocate. + /// A pointer to a block to write to, null if the stream buffer implementation does not support alloc/commit. + _CharType* _alloc(size_t) + { + return nullptr; + } + + /// + /// Submits a block already allocated by the stream buffer. + /// + /// Count of characters to be commited. + void _commit(size_t) + { + } + + /// + /// Gets a pointer to the next already allocated contiguous block of data. + /// + /// A reference to a pointer variable that will hold the address of the block on success. + /// The number of contiguous characters available at the address in 'ptr.' + /// true if the operation succeeded, false otherwise. + /// + /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that + /// there is no block to return immediately or that the stream buffer does not support the operation. + /// The stream buffer may not de-allocate the block until is called. + /// If the end of the stream is reached, the function will return true, a null pointer, and a count of zero; + /// a subsequent read will not succeed. + /// + virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count) + { + ptr = nullptr; + count = 0; + return false; + } + + /// + /// Releases a block of data acquired using . This frees the stream buffer to de-allocate the + /// memory, if it so desires. Move the read position ahead by the count. + /// + /// A pointer to the block of data to be released. + /// The number of characters that were read. + virtual void release(_Out_writes_ (count) _CharType *, _In_ size_t count) + { + (void)(count); + } + + /// + /// Writes a number of characters to the stream. + /// + /// A pointer to the block of data to be written. + /// The number of characters to write. + /// A task that holds the number of characters actually written, either 'count' or 0. + virtual pplx::task _putn(const _CharType *ptr, size_t count) + { + auto result_tce = pplx::task_completion_event(); + auto callback = new _filestream_callback_write(m_info, result_tce); + + size_t written = _putn_fsb(m_info, callback, ptr, count, sizeof(_CharType)); + + if ( written != 0 && written != -1 ) + { + delete callback; + written = written/sizeof(_CharType); + return pplx::task_from_result(written); + } + return pplx::create_task(result_tce); + } + + // Temporarily needed until the deprecated putn is removed. + virtual pplx::task _putn(const _CharType *ptr, size_t count, bool copy) + { + if (copy) + { + auto sharedData = std::make_shared>(ptr, ptr + count); + return _putn(ptr, count).then([sharedData](size_t size) + { + return size; + }); + } + else + { + return _putn(ptr, count); + } + } + + /// + /// Reads a single byte from the stream and advance the read position. + /// + /// A task that holds the value of the byte read. This is EOF if the read fails. + virtual pplx::task _bumpc() + { + return m_readOps.enqueue_operation([this]()-> pplx::task { + if ( _in_avail_unprot() > 0 ) + { + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); + + // Check again once the lock is held. + + if ( _in_avail_unprot() > 0 ) + { + auto bufoff = m_info->m_rdpos - m_info->m_bufoff; + _CharType ch = m_info->m_buffer[bufoff*sizeof(_CharType)]; + m_info->m_rdpos += 1; + return pplx::task_from_result(ch); + } + } + + auto result_tce = pplx::task_completion_event(); + auto callback = new _filestream_callback_bumpc(m_info, result_tce); + + size_t ch = _getn_fsb(m_info, callback, &callback->m_ch, 1, sizeof(_CharType)); + + if ( ch == sizeof(_CharType) ) + { + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); + m_info->m_rdpos += 1; + _CharType ch1 = (_CharType)callback->m_ch; + delete callback; + return pplx::task_from_result(ch1); + } + return pplx::create_task(result_tce); + }); + } + + /// + /// Reads a single byte from the stream and advance the read position. + /// + /// The value of the byte. EOF if the read fails. if an asynchronous read is required + /// This is a synchronous operation, but is guaranteed to never block. + virtual int_type _sbumpc() + { + m_readOps.wait(); + if ( m_info->m_atend ) return traits::eof(); + + if ( _in_avail_unprot() == 0 ) return traits::requires_async(); + + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); + + if ( _in_avail_unprot() == 0 ) return traits::requires_async(); + + auto bufoff = m_info->m_rdpos - m_info->m_bufoff; + _CharType ch = m_info->m_buffer[bufoff*sizeof(_CharType)]; + m_info->m_rdpos += 1; + return (int_type)ch; + } + + pplx::task _getcImpl() + { + if ( _in_avail_unprot() > 0 ) + { + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); + + // Check again once the lock is held. + + if ( _in_avail_unprot() > 0 ) + { + auto bufoff = m_info->m_rdpos - m_info->m_bufoff; + _CharType ch = m_info->m_buffer[bufoff*sizeof(_CharType)]; + return pplx::task_from_result(ch); + } + } + + auto result_tce = pplx::task_completion_event(); + auto callback = new _filestream_callback_getc(m_info, result_tce); + + size_t ch = _getn_fsb(m_info, callback, &callback->m_ch, 1, sizeof(_CharType)); + + if ( ch == sizeof(_CharType) ) + { + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); + _CharType ch1 = (_CharType)callback->m_ch; + delete callback; + return pplx::task_from_result(ch1); + } + return pplx::create_task(result_tce); + + } + + /// + /// Reads a single byte from the stream without advancing the read position. + /// + /// The value of the byte. EOF if the read fails. + pplx::task _getc() + { + return m_readOps.enqueue_operation([this]()-> pplx::task { + return _getcImpl(); + }); + } + + /// + /// Reads a single byte from the stream without advancing the read position. + /// + /// The value of the byte. EOF if the read fails. if an asynchronous read is required + /// This is a synchronous operation, but is guaranteed to never block. + int_type _sgetc() + { + m_readOps.wait(); + if ( m_info->m_atend ) return traits::eof(); + + if ( _in_avail_unprot() == 0 ) return traits::requires_async(); + + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); + + if ( _in_avail_unprot() == 0 ) return traits::requires_async(); + + auto bufoff = m_info->m_rdpos - m_info->m_bufoff; + _CharType ch = m_info->m_buffer[bufoff*sizeof(_CharType)]; + return (int_type)ch; + } + + /// + /// Advances the read position, then return the next character without advancing again. + /// + /// A task that holds the value of the byte, which is EOF if the read fails. + virtual pplx::task _nextc() + { + return m_readOps.enqueue_operation([this]()-> pplx::task { + _seekrdpos_fsb(m_info, m_info->m_rdpos+1, sizeof(_CharType)); + if ( m_info->m_atend ) + return pplx::task_from_result(basic_file_buffer<_CharType>::traits::eof()); + return this->_getcImpl(); + }); + } + + /// + /// Retreats the read position, then return the current character without advancing. + /// + /// A task that holds the value of the byte. The value is EOF if the read fails, requires_async if an asynchronous read is required + virtual pplx::task _ungetc() + { + return m_readOps.enqueue_operation([this]()-> pplx::task { + if ( m_info->m_rdpos == 0 ) + return pplx::task_from_result(basic_file_buffer<_CharType>::traits::eof()); + _seekrdpos_fsb(m_info, m_info->m_rdpos-1, sizeof(_CharType)); + return this->_getcImpl(); + }); + } + + /// + /// Reads up to a given number of characters from the stream. + /// + /// The address of the target memory area + /// The maximum number of characters to read + /// A task that holds the number of characters read. This number is O if the end of the stream is reached, EOF if there is some error. + virtual pplx::task _getn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) + { + return m_readOps.enqueue_operation([=] ()-> pplx::task{ + if ( m_info->m_atend || count == 0 ) + return pplx::task_from_result(0); + + if ( _in_avail_unprot() >= count ) + { + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); + + // Check again once the lock is held. + + if ( _in_avail_unprot() >= count ) + { + auto bufoff = m_info->m_rdpos - m_info->m_bufoff; + std::memcpy((void *)ptr, this->m_info->m_buffer+bufoff*sizeof(_CharType), count*sizeof(_CharType)); + + m_info->m_rdpos += count; + return pplx::task_from_result(count); + } + } + + auto result_tce = pplx::task_completion_event(); + auto callback = new _filestream_callback_read(m_info, result_tce); + + size_t read = _getn_fsb(m_info, callback, ptr, count, sizeof(_CharType)); + + if ( read != 0 && read != -1) + { + delete callback; + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); + m_info->m_rdpos += read/sizeof(_CharType); + return pplx::task_from_result(read/sizeof(_CharType)); + } + return pplx::create_task(result_tce); + }); + } + + /// + /// Reads up to a given number of characters from the stream. + /// + /// The address of the target memory area + /// The maximum number of characters to read + /// The number of characters read. O if the end of the stream is reached or an asynchronous read is required. + /// This is a synchronous operation, but is guaranteed to never block. + size_t _sgetn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) + { + m_readOps.wait(); + if ( m_info->m_atend ) return 0; + + if ( count == 0 || in_avail() == 0 ) return 0; + + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); + + size_t available = _in_avail_unprot(); + size_t copy = (count < available) ? count : available; + + auto bufoff = m_info->m_rdpos - m_info->m_bufoff; + std::memcpy((void *)ptr, this->m_info->m_buffer+bufoff*sizeof(_CharType), copy*sizeof(_CharType)); + + m_info->m_rdpos += copy; + m_info->m_atend = (copy < count); + return copy; + } + + /// + /// Copies up to a given number of characters from the stream. + /// + /// The address of the target memory area + /// The maximum number of characters to copy + /// The number of characters copied. O if the end of the stream is reached or an asynchronous read is required. + /// This is a synchronous operation, but is guaranteed to never block. + virtual size_t _scopy(_CharType *, size_t) + { + return 0; + } + + /// + /// Gets the current read or write position in the stream. + /// + /// The I/O direction to seek (see remarks) + /// The current position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. + /// For such streams, the direction parameter defines whether to move the read or the write cursor. + virtual pos_type getpos(std::ios_base::openmode mode) const + { + return const_cast(this)->seekoff(0, std::ios_base::cur, mode); + } + + /// + /// Seeks to the given position. + /// + /// The offset from the beginning of the stream + /// The I/O direction to seek (see remarks) + /// The position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. + /// For such streams, the direction parameter defines whether to move the read or the write cursor. + virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode) + { + if ( mode == std::ios_base::in ) + { + m_readOps.wait(); + return (pos_type)_seekrdpos_fsb(m_info, size_t(pos), sizeof(_CharType)); + } + else if ( (m_info->m_mode & std::ios::ios_base::app) == 0 ) + { + return (pos_type)_seekwrpos_fsb(m_info, size_t(pos), sizeof(_CharType)); + } + return (pos_type)Concurrency::streams::char_traits<_CharType>::eof(); + } + + /// + /// Seeks to a position given by a relative offset. + /// + /// The relative position to seek to + /// The starting point (beginning, end, current) for the seek. + /// The I/O direction to seek (see remarks) + /// The position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. + /// For such streams, the mode parameter defines whether to move the read or the write cursor. + virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode) + { + if ( mode == std::ios_base::in ) + { + m_readOps.wait(); + switch ( way ) + { + case std::ios_base::beg: + return (pos_type)_seekrdpos_fsb(m_info, size_t(offset), sizeof(_CharType)); + case std::ios_base::cur: + return (pos_type)_seekrdpos_fsb(m_info, size_t(m_info->m_rdpos+offset), sizeof(_CharType)); + case std::ios_base::end: + return (pos_type)_seekrdtoend_fsb(m_info, int64_t(offset), sizeof(_CharType)); + default: + // Fail on invalid input (_S_ios_seekdir_end) + assert(false); + } + } + else if ( (m_info->m_mode & std::ios::ios_base::app) == 0 ) + { + switch ( way ) + { + case std::ios_base::beg: + return (pos_type)_seekwrpos_fsb(m_info, size_t(offset), sizeof(_CharType)); + case std::ios_base::cur: + return (pos_type)_seekwrpos_fsb(m_info, size_t(m_info->m_wrpos+offset), sizeof(_CharType)); + case std::ios_base::end: + return (pos_type)_seekwrpos_fsb(m_info, size_t(-1), sizeof(_CharType)); + default: + // Fail on invalid input (_S_ios_seekdir_end) + assert(false); + } + } + return (pos_type)traits::eof(); + } + + /// + /// For output streams, flush any internally buffered data to the underlying medium. + /// + virtual pplx::task _sync() + { + return flush_internal().then([](){return true;}); + } + + private: + template friend class ::concurrency::streams::file_buffer; + + pplx::task flush_internal() + { + pplx::task_completion_event result_tce; + auto callback = utility::details::make_unique<_filestream_callback_write_b>(m_info, result_tce); + + if ( !_sync_fsb(m_info, callback.get()) ) + { + return pplx::task_from_exception(std::runtime_error("failure to flush stream")); + } + callback.release(); + return pplx::create_task(result_tce); + } + + basic_file_buffer(_In_ _file_info *info) : streambuf_state_manager<_CharType>(info->m_mode), m_info(info) { } + +#if !defined(__cplusplus_winrt) + static pplx::task>> open( + const utility::string_t &_Filename, + std::ios_base::openmode _Mode = std::ios_base::out, +#ifdef _WIN32 + int _Prot = (int)std::ios_base::_Openprot +#else + int _Prot = 0 // unsupported on Linux, for now +#endif + ) + { + auto result_tce = pplx::task_completion_event>>(); + auto callback = new _filestream_callback_open(result_tce); + _open_fsb_str(callback, _Filename.c_str(), _Mode, _Prot); + return pplx::create_task(result_tce); + } + +#else + static pplx::task>> open( + ::Windows::Storage::StorageFile^ file, + std::ios_base::openmode _Mode = std::ios_base::out) + { + auto result_tce = pplx::task_completion_event>>(); + auto callback = new _filestream_callback_open(result_tce); + _open_fsb_stf_str(callback, file, _Mode, 0); + return pplx::create_task(result_tce); + } +#endif + + class _filestream_callback_open : public details::_filestream_callback + { + public: + _filestream_callback_open(const pplx::task_completion_event>> &op) : m_op(op) { } + + virtual void on_opened(_In_ _file_info *info) + { + m_op.set(std::shared_ptr>(new basic_file_buffer<_CharType>(info))); + delete this; + } + + virtual void on_error(const std::exception_ptr & e) + { + m_op.set_exception(e); + delete this; + } + + private: + pplx::task_completion_event>> m_op; + }; + + class _filestream_callback_close : public details::_filestream_callback + { + public: + _filestream_callback_close(const pplx::task_completion_event &op) : m_op(op) { } + + virtual void on_closed() + { + m_op.set(); + delete this; + } + + virtual void on_error(const std::exception_ptr & e) + { + m_op.set_exception(e); + delete this; + } + + private: + pplx::task_completion_event m_op; + }; + + template + class _filestream_callback_write : public details::_filestream_callback + { + public: + _filestream_callback_write(_In_ _file_info *info, const pplx::task_completion_event &op) : m_info(info), m_op(op) { } + + virtual void on_completed(size_t result) + { + m_op.set((ResultType)result / sizeof(_CharType)); + delete this; + } + + virtual void on_error(const std::exception_ptr & e) + { + m_op.set_exception(e); + delete this; + } + + private: + _file_info *m_info; + pplx::task_completion_event m_op; + }; + + class _filestream_callback_write_b : public details::_filestream_callback + { + public: + _filestream_callback_write_b(_In_ _file_info *info, const pplx::task_completion_event &op) : m_info(info), m_op(op) { } + + virtual void on_completed(size_t) + { + m_op.set(); + delete this; + } + + virtual void on_error(const std::exception_ptr & e) + { + m_op.set_exception(e); + delete this; + } + + private: + _file_info *m_info; + pplx::task_completion_event m_op; + }; + + + class _filestream_callback_read : public details::_filestream_callback + { + public: + _filestream_callback_read(_In_ _file_info *info, const pplx::task_completion_event &op) : m_info(info), m_op(op) { } + + virtual void on_completed(size_t result) + { + result = result / sizeof(_CharType); + m_info->m_rdpos += result; + m_op.set(result); + delete this; + } + + virtual void on_error(const std::exception_ptr & e) + { + m_op.set_exception(e); + delete this; + } + + private: + _file_info *m_info; + pplx::task_completion_event m_op; + }; + + class _filestream_callback_bumpc : public details::_filestream_callback + { + public: + _filestream_callback_bumpc(_In_ _file_info *info, const pplx::task_completion_event &op) : m_ch(0), m_info(info), m_op(op) { } + + virtual void on_completed(size_t result) + { + if ( result == sizeof(_CharType) ) + { + m_info->m_rdpos += 1; + m_op.set(m_ch); + } + else + { + m_op.set(traits::eof()); + } + delete this; + } + + virtual void on_error(const std::exception_ptr & e) + { + m_op.set_exception(e); + delete this; + } + + int_type m_ch; + + private: + _file_info *m_info; + pplx::task_completion_event m_op; + }; + + class _filestream_callback_getc : public details::_filestream_callback + { + public: + _filestream_callback_getc(_In_ _file_info *info, const pplx::task_completion_event &op) : m_ch(0), m_info(info), m_op(op) { } + + virtual void on_completed(size_t result) + { + if ( result == sizeof(_CharType) ) + { + m_op.set(m_ch); + } + else + { + m_op.set(traits::eof()); + } + delete this; + } + + int_type m_ch; + + virtual void on_error(const std::exception_ptr & e) + { + m_op.set_exception(e); + delete this; + } + + private: + _file_info *m_info; + pplx::task_completion_event m_op; + }; + + _file_info *m_info; + async_operation_queue m_readOps; + }; + + } // namespace details + + /// + /// Stream buffer for file streams. + /// + /// + /// The data type of the basic element of the file_buffer. + /// + template + class file_buffer + { + public: +#if !defined(__cplusplus_winrt) + /// + /// Open a new stream buffer representing the given file. + /// + /// The name of the file + /// The opening mode of the file + /// The file protection mode + /// A task that returns an opened stream buffer on completion. + static pplx::task> open( + const utility::string_t &file_name, + std::ios_base::openmode mode = std::ios_base::out, +#ifdef _WIN32 + int prot = _SH_DENYRD +#else + int prot = 0 // unsupported on Linux +#endif + ) + { + auto bfb = details::basic_file_buffer<_CharType>::open(file_name, mode, prot); + return bfb.then([](pplx::task>> op) -> streambuf<_CharType> + { + return streambuf<_CharType>(op.get()); + }); + } + +#else + /// + /// Open a new stream buffer representing the given file. + /// + /// The StorageFile instance + /// The opening mode of the file + /// The file protection mode + /// A task that returns an opened stream buffer on completion. + static pplx::task> open( + ::Windows::Storage::StorageFile^ file, + std::ios_base::openmode mode = std::ios_base::out) + { + auto bfb = details::basic_file_buffer<_CharType>::open(file, mode); + return bfb.then([](pplx::task>> op) -> streambuf<_CharType> + { + return streambuf<_CharType>(op.get()); + }); + } +#endif + }; + + + /// + /// File stream class containing factory functions for file streams. + /// + /// + /// The data type of the basic element of the file_stream. + /// + template + class file_stream + { + public: + +#if !defined(__cplusplus_winrt) + /// + /// Open a new input stream representing the given file. + /// The file should already exist on disk, or an exception will be thrown. + /// + /// The name of the file + /// The opening mode of the file + /// The file protection mode + /// A task that returns an opened input stream on completion. + static pplx::task> open_istream( + const utility::string_t &file_name, + std::ios_base::openmode mode = std::ios_base::in, +#ifdef _WIN32 + int prot = (int) std::ios_base::_Openprot +#else + int prot = 0 +#endif + ) + { + mode |= std::ios_base::in; + return streams::file_buffer<_CharType>::open(file_name, mode, prot) + .then([](streams::streambuf<_CharType> buf) -> basic_istream<_CharType> + { + return basic_istream<_CharType>(buf); + }); + } + + /// + /// Open a new ouput stream representing the given file. + /// If the file does not exist, it will be create unless the folder or directory + /// where it is to be found also does not exist. + /// + /// The name of the file + /// The opening mode of the file + /// The file protection mode + /// A task that returns an opened output stream on completion. + static pplx::task> open_ostream( + const utility::string_t &file_name, + std::ios_base::openmode mode = std::ios_base::out, +#ifdef _WIN32 + int prot = (int) std::ios_base::_Openprot +#else + int prot = 0 +#endif + ) + { + mode |= std::ios_base::out; + return streams::file_buffer<_CharType>::open(file_name, mode, prot) + .then([](streams::streambuf<_CharType> buf) -> basic_ostream<_CharType> + { + return basic_ostream<_CharType>(buf); + }); + } +#else + /// + /// Open a new input stream representing the given file. + /// The file should already exist on disk, or an exception will be thrown. + /// + /// The StorageFile reference representing the file + /// The opening mode of the file + /// A task that returns an opened input stream on completion. + static pplx::task> open_istream( + ::Windows::Storage::StorageFile^ file, + std::ios_base::openmode mode = std::ios_base::in) + { + mode |= std::ios_base::in; + return streams::file_buffer<_CharType>::open(file, mode) + .then([](streams::streambuf<_CharType> buf) -> basic_istream<_CharType> + { + return basic_istream<_CharType>(buf); + }); + } + + /// + /// Open a new ouput stream representing the given file. + /// If the file does not exist, it will be create unless the folder or directory + /// where it is to be found also does not exist. + /// + /// The StorageFile reference representing the file + /// The opening mode of the file + /// A task that returns an opened output stream on completion. + static pplx::task> open_ostream( + ::Windows::Storage::StorageFile^ file, + std::ios_base::openmode mode = std::ios_base::out) + { + mode |= std::ios_base::out; + return streams::file_buffer<_CharType>::open(file, mode) + .then([](streams::streambuf<_CharType> buf) -> basic_ostream<_CharType> + { + return basic_ostream<_CharType>(buf); + }); + } +#endif + }; + + typedef file_stream fstream; +}} // namespace concurrency::streams + +#endif + + + + + + + + + + + + + + diff --git a/3rdparty/cpprestsdk/include/cpprest/http_client.h b/3rdparty/cpprestsdk/include/cpprest/http_client.h new file mode 100644 index 00000000..9ba99c8d --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/http_client.h @@ -0,0 +1,670 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: Client-side APIs. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#ifndef _CASA_HTTP_CLIENT_H +#define _CASA_HTTP_CLIENT_H + +#if defined (__cplusplus_winrt) +#if !defined(__WRL_NO_DEFAULT_LIB__) +#define __WRL_NO_DEFAULT_LIB__ +#endif +#include +#include +namespace web { namespace http{namespace client{ +typedef IXMLHTTPRequest2* native_handle;}}} +#else +namespace web { namespace http{namespace client{ +typedef void* native_handle;}}} +#endif // __cplusplus_winrt + +#include +#include + +#include "pplx/pplxtasks.h" +#include "cpprest/http_msg.h" +#include "cpprest/json.h" +#include "cpprest/uri.h" +#include "cpprest/details/web_utilities.h" +#include "cpprest/details/basic_types.h" +#include "cpprest/asyncrt_utils.h" + +#if !defined(CPPREST_TARGET_XP) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP || _MSC_VER > 1700) +#include "cpprest/oauth1.h" +#endif + +#include "cpprest/oauth2.h" + +/// The web namespace contains functionality common to multiple protocols like HTTP and WebSockets. +namespace web +{ +/// Declarations and functionality for the HTTP protocol. +namespace http +{ +/// HTTP client side library. +namespace client +{ + +// credentials and web_proxy class has been moved from web::http::client namespace to web namespace. +// The below using declarations ensure we don't break existing code. +// Please use the web::credentials and web::web_proxy class going forward. +using web::credentials; +using web::web_proxy; + +/// +/// HTTP client configuration class, used to set the possible configuration options +/// used to create an http_client instance. +/// +class http_client_config +{ +public: + http_client_config() : + m_guarantee_order(false), + m_timeout(utility::seconds(30)), + m_chunksize(0) +#if !defined(__cplusplus_winrt) + , m_validate_certificates(true) +#endif + , m_set_user_nativehandle_options([](native_handle)->void{}) +#if defined(_WIN32) && !defined(__cplusplus_winrt) + , m_buffer_request(false) +#endif + { + } + +#if !defined(CPPREST_TARGET_XP) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP || _MSC_VER > 1700) + /// + /// Get OAuth 1.0 configuration. + /// + /// Shared pointer to OAuth 1.0 configuration. + const std::shared_ptr oauth1() const + { + return m_oauth1; + } + + /// + /// Set OAuth 1.0 configuration. + /// + /// OAuth 1.0 configuration to set. + void set_oauth1(oauth1::experimental::oauth1_config config) + { + m_oauth1 = std::make_shared(std::move(config)); + } +#endif + + /// + /// Get OAuth 2.0 configuration. + /// + /// Shared pointer to OAuth 2.0 configuration. + const std::shared_ptr oauth2() const + { + return m_oauth2; + } + + /// + /// Set OAuth 2.0 configuration. + /// + /// OAuth 2.0 configuration to set. + void set_oauth2(oauth2::experimental::oauth2_config config) + { + m_oauth2 = std::make_shared(std::move(config)); + } + + /// + /// Get the web proxy object + /// + /// A reference to the web proxy object. + const web_proxy& proxy() const + { + return m_proxy; + } + + /// + /// Set the web proxy object + /// + /// A reference to the web proxy object. + void set_proxy(web_proxy proxy) + { + m_proxy = std::move(proxy); + } + + /// + /// Get the client credentials + /// + /// A reference to the client credentials. + const http::client::credentials& credentials() const + { + return m_credentials; + } + + /// + /// Set the client credentials + /// + /// A reference to the client credentials. + void set_credentials(const http::client::credentials& cred) + { + m_credentials = cred; + } + + /// + /// Get the 'guarantee order' property + /// + /// The value of the property. + bool guarantee_order() const + { + return m_guarantee_order; + } + + /// + /// Set the 'guarantee order' property + /// + /// The value of the property. + CASABLANCA_DEPRECATED("Confusing API will be removed in future releases. If you need to order HTTP requests use task continuations.") + void set_guarantee_order(bool guarantee_order) + { + m_guarantee_order = guarantee_order; + } + + /// + /// Get the timeout + /// + /// The timeout (in seconds) used for each send and receive operation on the client. + utility::seconds timeout() const + { + return m_timeout; + } + + /// + /// Set the timeout + /// + /// The timeout (in seconds) used for each send and receive operation on the client. + void set_timeout(const utility::seconds &timeout) + { + m_timeout = timeout; + } + + /// + /// Get the client chunk size. + /// + /// The internal buffer size used by the http client when sending and receiving data from the network. + size_t chunksize() const + { + return m_chunksize == 0 ? 64 * 1024 : m_chunksize; + } + + /// + /// Sets the client chunk size. + /// + /// The internal buffer size used by the http client when sending and receiving data from the network. + /// This is a hint -- an implementation may disregard the setting and use some other chunk size. + void set_chunksize(size_t size) + { + m_chunksize = size; + } + + /// + /// Returns true if the default chunk size is in use. + /// If true, implementations are allowed to choose whatever size is best. + /// + /// True if default, false if set by user. + bool is_default_chunksize() const + { + return m_chunksize == 0; + } + +#if !defined(__cplusplus_winrt) + /// + /// Gets the server certificate validation property. + /// + /// True if certificates are to be verified, false otherwise. + bool validate_certificates() const + { + return m_validate_certificates; + } + + /// + /// Sets the server certificate validation property. + /// + /// False to turn ignore all server certificate validation errors, true otherwise. + /// Note ignoring certificate errors can be dangerous and should be done with caution. + void set_validate_certificates(bool validate_certs) + { + m_validate_certificates = validate_certs; + } +#endif + +#ifdef _WIN32 +#if !defined(__cplusplus_winrt) + /// + /// Checks if request data buffering is turned on, the default is off. + /// + /// True if buffering is enabled, false otherwise + bool buffer_request() const + { + return m_buffer_request; + } + + /// + /// Sets the request buffering property. + /// If true, in cases where the request body/stream doesn't support seeking the request data will be buffered. + /// This can help in situations where an authentication challenge might be expected. + /// + /// True to turn on buffer, false otherwise. + /// Please note there is a performance cost due to copying the request data. + void set_buffer_request(bool buffer_request) + { + m_buffer_request = buffer_request; + } +#endif +#endif + + /// + /// Sets a callback to enable custom setting of platform specific options. + /// + /// + /// The native_handle is the following type depending on the underlying platform: + /// Windows Desktop, WinHTTP - HINTERNET + /// Windows Runtime, WinRT - IXMLHTTPRequest2 * + /// All other platforms, Boost.Asio: + /// https - boost::asio::ssl::stream * + /// http - boost::asio::ip::tcp::socket * + /// + /// A user callback allowing for customization of the request + void set_nativehandle_options(const std::function &callback) + { + m_set_user_nativehandle_options = callback; + } + + /// + /// Invokes a user's callback to allow for customization of the request. + /// + /// A internal implementation handle. + void invoke_nativehandle_options(native_handle handle) const + { + m_set_user_nativehandle_options(handle); + } + +private: +#if !defined(CPPREST_TARGET_XP) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP || _MSC_VER > 1700) + std::shared_ptr m_oauth1; +#endif + + std::shared_ptr m_oauth2; + web_proxy m_proxy; + http::client::credentials m_credentials; + // Whether or not to guarantee ordering, i.e. only using one underlying TCP connection. + bool m_guarantee_order; + + utility::seconds m_timeout; + size_t m_chunksize; + +#if !defined(__cplusplus_winrt) + // IXmlHttpRequest2 doesn't allow configuration of certificate verification. + bool m_validate_certificates; +#endif + + std::function m_set_user_nativehandle_options; + +#if defined(_WIN32) && !defined(__cplusplus_winrt) + bool m_buffer_request; +#endif +}; + +/// +/// HTTP client class, used to maintain a connection to an HTTP service for an extended session. +/// +class http_client +{ +public: + /// + /// Creates a new http_client connected to specified uri. + /// + /// A string representation of the base uri to be used for all requests. Must start with either "http://" or "https://" + _ASYNCRTIMP http_client(const uri &base_uri); + + /// + /// Creates a new http_client connected to specified uri. + /// + /// A string representation of the base uri to be used for all requests. Must start with either "http://" or "https://" + /// The http client configuration object containing the possible configuration options to initialize the http_client. + _ASYNCRTIMP http_client(const uri &base_uri, const http_client_config &client_config); + + /// + /// Note the destructor doesn't necessarily close the connection and release resources. + /// The connection is reference counted with the http_responses. + /// + ~http_client() CPPREST_NOEXCEPT {} + + /// + /// Gets the base URI. + /// + /// + /// A base URI initialized in constructor + /// + _ASYNCRTIMP const uri& base_uri() const; + + /// + /// Get client configuration object + /// + /// A reference to the client configuration object. + _ASYNCRTIMP const http_client_config& client_config() const; + + /// + /// Adds an HTTP pipeline stage to the client. + /// + /// A function object representing the pipeline stage. + void add_handler(const std::function(http_request, std::shared_ptr)> &handler) + { + m_pipeline->append(std::make_shared<::web::http::details::function_pipeline_wrapper>(handler)); + } + + /// + /// Adds an HTTP pipeline stage to the client. + /// + /// A shared pointer to a pipeline stage. + void add_handler(const std::shared_ptr &stage) + { + m_pipeline->append(stage); + } + + /// + /// Asynchronously sends an HTTP request. + /// + /// Request to send. + /// Cancellation token for cancellation of this request operation. + /// An asynchronous operation that is completed once a response from the request is received. + _ASYNCRTIMP pplx::task request(http_request request, const pplx::cancellation_token &token = pplx::cancellation_token::none()); + + /// + /// Asynchronously sends an HTTP request. + /// + /// HTTP request method. + /// Cancellation token for cancellation of this request operation. + /// An asynchronous operation that is completed once a response from the request is received. + pplx::task request(const method &mtd, const pplx::cancellation_token &token = pplx::cancellation_token::none()) + { + http_request msg(mtd); + return request(msg, token); + } + + /// + /// Asynchronously sends an HTTP request. + /// + /// HTTP request method. + /// String containing the path, query, and fragment, relative to the http_client's base URI. + /// Cancellation token for cancellation of this request operation. + /// An asynchronous operation that is completed once a response from the request is received. + pplx::task request( + const method &mtd, + const utility::string_t &path_query_fragment, + const pplx::cancellation_token &token = pplx::cancellation_token::none()) + { + http_request msg(mtd); + msg.set_request_uri(path_query_fragment); + return request(msg, token); + } + + /// + /// Asynchronously sends an HTTP request. + /// + /// HTTP request method. + /// String containing the path, query, and fragment, relative to the http_client's base URI. + /// The data to be used as the message body, represented using the json object library. + /// Cancellation token for cancellation of this request operation. + /// An asynchronous operation that is completed once a response from the request is received. + pplx::task request( + const method &mtd, + const utility::string_t &path_query_fragment, + const json::value &body_data, + const pplx::cancellation_token &token = pplx::cancellation_token::none()) + { + http_request msg(mtd); + msg.set_request_uri(path_query_fragment); + msg.set_body(body_data); + return request(msg, token); + } + + /// + /// Asynchronously sends an HTTP request with a string body. Assumes the + /// character encoding of the string is UTF-8. + /// + /// HTTP request method. + /// String containing the path, query, and fragment, relative to the http_client's base URI. + /// A string holding the MIME type of the message body. + /// String containing the text to use in the message body. + /// Cancellation token for cancellation of this request operation. + /// An asynchronous operation that is completed once a response from the request is received. + pplx::task request( + const method &mtd, + const utf8string &path_query_fragment, + const utf8string &body_data, + const utf8string &content_type = "text/plain; charset=utf-8", + const pplx::cancellation_token &token = pplx::cancellation_token::none()) + { + http_request msg(mtd); + msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment)); + msg.set_body(body_data, content_type); + return request(msg, token); + } + + /// + /// Asynchronously sends an HTTP request with a string body. Assumes the + /// character encoding of the string is UTF-8. + /// + /// HTTP request method. + /// String containing the path, query, and fragment, relative to the http_client's base URI. + /// A string holding the MIME type of the message body. + /// String containing the text to use in the message body. + /// Cancellation token for cancellation of this request operation. + /// An asynchronous operation that is completed once a response from the request is received. + pplx::task request( + const method &mtd, + const utf8string &path_query_fragment, + utf8string &&body_data, + const utf8string &content_type = "text/plain; charset=utf-8", + const pplx::cancellation_token &token = pplx::cancellation_token::none()) + { + http_request msg(mtd); + msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment)); + msg.set_body(std::move(body_data), content_type); + return request(msg, token); + } + + /// + /// Asynchronously sends an HTTP request with a string body. Assumes the + /// character encoding of the string is UTF-16 will perform conversion to UTF-8. + /// + /// HTTP request method. + /// String containing the path, query, and fragment, relative to the http_client's base URI. + /// A string holding the MIME type of the message body. + /// String containing the text to use in the message body. + /// Cancellation token for cancellation of this request operation. + /// An asynchronous operation that is completed once a response from the request is received. + pplx::task request( + const method &mtd, + const utf16string &path_query_fragment, + const utf16string &body_data, + const utf16string &content_type = ::utility::conversions::to_utf16string("text/plain"), + const pplx::cancellation_token &token = pplx::cancellation_token::none()) + { + http_request msg(mtd); + msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment)); + msg.set_body(body_data, content_type); + return request(msg, token); + } + + /// + /// Asynchronously sends an HTTP request with a string body. Assumes the + /// character encoding of the string is UTF-8. + /// + /// HTTP request method. + /// String containing the path, query, and fragment, relative to the http_client's base URI. + /// String containing the text to use in the message body. + /// Cancellation token for cancellation of this request operation. + /// An asynchronous operation that is completed once a response from the request is received. + pplx::task request( + const method &mtd, + const utf8string &path_query_fragment, + const utf8string &body_data, + const pplx::cancellation_token &token) + { + return request(mtd, path_query_fragment, body_data, "text/plain; charset=utf-8", token); + } + + /// + /// Asynchronously sends an HTTP request with a string body. Assumes the + /// character encoding of the string is UTF-8. + /// + /// HTTP request method. + /// String containing the path, query, and fragment, relative to the http_client's base URI. + /// String containing the text to use in the message body. + /// Cancellation token for cancellation of this request operation. + /// An asynchronous operation that is completed once a response from the request is received. + pplx::task request( + const method &mtd, + const utf8string &path_query_fragment, + utf8string &&body_data, + const pplx::cancellation_token &token) + { + http_request msg(mtd); + msg.set_request_uri(::utility::conversions::to_string_t(path_query_fragment)); + msg.set_body(std::move(body_data), "text/plain; charset=utf-8"); + return request(msg, token); + } + + /// + /// Asynchronously sends an HTTP request with a string body. Assumes + /// the character encoding of the string is UTF-16 will perform conversion to UTF-8. + /// + /// HTTP request method. + /// String containing the path, query, and fragment, relative to the http_client's base URI. + /// String containing the text to use in the message body. + /// Cancellation token for cancellation of this request operation. + /// An asynchronous operation that is completed once a response from the request is received. + pplx::task request( + const method &mtd, + const utf16string &path_query_fragment, + const utf16string &body_data, + const pplx::cancellation_token &token) + { + return request(mtd, path_query_fragment, body_data, ::utility::conversions::to_utf16string("text/plain"), token); + } + +#if !defined (__cplusplus_winrt) + /// + /// Asynchronously sends an HTTP request. + /// + /// HTTP request method. + /// String containing the path, query, and fragment, relative to the http_client's base URI. + /// An asynchronous stream representing the body data. + /// A string holding the MIME type of the message body. + /// Cancellation token for cancellation of this request operation. + /// A task that is completed once a response from the request is received. + pplx::task request( + const method &mtd, + const utility::string_t &path_query_fragment, + const concurrency::streams::istream &body, + const utility::string_t &content_type = _XPLATSTR("application/octet-stream"), + const pplx::cancellation_token &token = pplx::cancellation_token::none()) + { + http_request msg(mtd); + msg.set_request_uri(path_query_fragment); + msg.set_body(body, content_type); + return request(msg, token); + } + + /// + /// Asynchronously sends an HTTP request. + /// + /// HTTP request method. + /// String containing the path, query, and fragment, relative to the http_client's base URI. + /// An asynchronous stream representing the body data. + /// Cancellation token for cancellation of this request operation. + /// A task that is completed once a response from the request is received. + pplx::task request( + const method &mtd, + const utility::string_t &path_query_fragment, + const concurrency::streams::istream &body, + const pplx::cancellation_token &token) + { + return request(mtd, path_query_fragment, body, _XPLATSTR("application/octet-stream"), token); + } +#endif // __cplusplus_winrt + + /// + /// Asynchronously sends an HTTP request. + /// + /// HTTP request method. + /// String containing the path, query, and fragment, relative to the http_client's base URI. + /// An asynchronous stream representing the body data. + /// Size of the message body. + /// A string holding the MIME type of the message body. + /// Cancellation token for cancellation of this request operation. + /// A task that is completed once a response from the request is received. + /// Winrt requires to provide content_length. + pplx::task request( + const method &mtd, + const utility::string_t &path_query_fragment, + const concurrency::streams::istream &body, + size_t content_length, + const utility::string_t &content_type = _XPLATSTR("application/octet-stream"), + const pplx::cancellation_token &token = pplx::cancellation_token::none()) + { + http_request msg(mtd); + msg.set_request_uri(path_query_fragment); + msg.set_body(body, content_length, content_type); + return request(msg, token); + } + + /// + /// Asynchronously sends an HTTP request. + /// + /// HTTP request method. + /// String containing the path, query, and fragment, relative to the http_client's base URI. + /// An asynchronous stream representing the body data. + /// Size of the message body. + /// Cancellation token for cancellation of this request operation. + /// A task that is completed once a response from the request is received. + /// Winrt requires to provide content_length. + pplx::task request( + const method &mtd, + const utility::string_t &path_query_fragment, + const concurrency::streams::istream &body, + size_t content_length, + const pplx::cancellation_token &token) + { + return request(mtd, path_query_fragment, body, content_length, _XPLATSTR("application/octet-stream"), token); + } + +private: + + void build_pipeline(const uri &base_uri, const http_client_config &client_config); + + std::shared_ptr<::web::http::http_pipeline> m_pipeline; +}; + +}}} + +#endif diff --git a/3rdparty/cpprestsdk/include/cpprest/http_headers.h b/3rdparty/cpprestsdk/include/cpprest/http_headers.h new file mode 100644 index 00000000..62da60fd --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/http_headers.h @@ -0,0 +1,331 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#include +#include +#include +#include +#include +#include "cpprest/asyncrt_utils.h" + +namespace web { namespace http { + +/// +/// Binds an individual reference to a string value. +/// +/// The type of string value. +/// The type of the value to bind to. +/// The string value. +/// The value to bind to. +/// true if the binding succeeds, false otherwise. +template +CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release, std::istringstream instead.") +bool bind(const key_type &text, _t &ref) // const +{ + utility::istringstream_t iss(text); + iss >> ref; + if (iss.fail() || !iss.eof()) + { + return false; + } + + return true; +} + +/// +/// Binds an individual reference to a string value. +/// This specialization is need because istringstream::>> delimits on whitespace. +/// +/// The type of the string value. +/// The string value. +/// The value to bind to. +/// true if the binding succeeds, false otherwise. +template +CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release.") +bool bind(const key_type &text, utility::string_t &ref) //const +{ + ref = text; + return true; +} + +/// +/// Represents HTTP headers, acts like a map. +/// +class http_headers +{ +public: + /// Function object to perform case insensitive comparison of wstrings. + struct _case_insensitive_cmp + { + bool operator()(const utility::string_t &str1, const utility::string_t &str2) const + { +#ifdef _WIN32 + return _wcsicmp(str1.c_str(), str2.c_str()) < 0; +#else + return utility::cmp::icmp(str1, str2) < 0; +#endif + } + }; + + /// + /// STL-style typedefs + /// + typedef std::map::key_type key_type; + typedef std::map::key_compare key_compare; + typedef std::map::allocator_type allocator_type; + typedef std::map::size_type size_type; + typedef std::map::difference_type difference_type; + typedef std::map::pointer pointer; + typedef std::map::const_pointer const_pointer; + typedef std::map::reference reference; + typedef std::map::const_reference const_reference; + typedef std::map::iterator iterator; + typedef std::map::const_iterator const_iterator; + typedef std::map::reverse_iterator reverse_iterator; + typedef std::map::const_reverse_iterator const_reverse_iterator; + + /// + /// Constructs an empty set of HTTP headers. + /// + http_headers() {} + + /// + /// Copy constructor. + /// + /// An http_headers object to copy from. + http_headers(const http_headers &other) : m_headers(other.m_headers) {} + + /// + /// Assignment operator. + /// + /// An http_headers object to copy from. + http_headers &operator=(const http_headers &other) + { + if(this != &other) + { + m_headers = other.m_headers; + } + return *this; + } + + /// + /// Move constructor. + /// + /// An http_headers object to move. + http_headers(http_headers &&other) : m_headers(std::move(other.m_headers)) {} + + /// + /// Move assignment operator. + /// + /// An http_headers object to move. + http_headers &operator=(http_headers &&other) + { + if(this != &other) + { + m_headers = std::move(other.m_headers); + } + return *this; + } + + /// + /// Adds a header field using the '<<' operator. + /// + /// The name of the header field. + /// The value of the header field. + /// If the header field exists, the value will be combined as comma separated string. + template + void add(const key_type& name, const _t1& value) + { + if (has(name)) + { + m_headers[name] = m_headers[name].append(_XPLATSTR(", ") + utility::conversions::print_string(value)); + } + else + { + m_headers[name] = utility::conversions::print_string(value); + } + } + + /// + /// Removes a header field. + /// + /// The name of the header field. + void remove(const key_type& name) + { + m_headers.erase(name); + } + + /// + /// Removes all elements from the headers. + /// + void clear() { m_headers.clear(); } + + /// + /// Checks if there is a header with the given key. + /// + /// The name of the header field. + /// true if there is a header with the given name, false otherwise. + bool has(const key_type& name) const { return m_headers.find(name) != m_headers.end(); } + + /// + /// Returns the number of header fields. + /// + /// Number of header fields. + size_type size() const { return m_headers.size(); } + + /// + /// Tests to see if there are any header fields. + /// + /// true if there are no headers, false otherwise. + bool empty() const { return m_headers.empty(); } + + /// + /// Returns a reference to header field with given name, if there is no header field one is inserted. + /// + utility::string_t & operator[](const key_type &name) { return m_headers[name]; } + + /// + /// Checks if a header field exists with given name and returns an iterator if found. Otherwise + /// and iterator to end is returned. + /// + /// The name of the header field. + /// An iterator to where the HTTP header is found. + iterator find(const key_type &name) { return m_headers.find(name); } + const_iterator find(const key_type &name) const { return m_headers.find(name); } + + /// + /// Attempts to match a header field with the given name using the '>>' operator. + /// + /// The name of the header field. + /// The value of the header field. + /// true if header field was found and successfully stored in value parameter. + template + bool match(const key_type &name, _t1 &value) const + { + auto iter = m_headers.find(name); + if (iter != m_headers.end()) + { + // Check to see if doesn't have a value. + if(iter->second.empty()) + { + bind_impl(iter->second, value); + return true; + } + return bind_impl(iter->second, value); + } + else + { + return false; + } + } + + /// + /// Returns an iterator referring to the first header field. + /// + /// An iterator to the beginning of the HTTP headers + iterator begin() { return m_headers.begin(); } + const_iterator begin() const { return m_headers.begin(); } + + /// + /// Returns an iterator referring to the past-the-end header field. + /// + /// An iterator to the element past the end of the HTTP headers. + iterator end() { return m_headers.end(); } + const_iterator end() const { return m_headers.end(); } + + /// + /// Gets the content length of the message. + /// + /// The length of the content. + _ASYNCRTIMP utility::size64_t content_length() const; + + /// + /// Sets the content length of the message. + /// + /// The length of the content. + _ASYNCRTIMP void set_content_length(utility::size64_t length); + + /// + /// Gets the content type of the message. + /// + /// The content type of the body. + _ASYNCRTIMP utility::string_t content_type() const; + + /// + /// Sets the content type of the message. + /// + /// The content type of the body. + _ASYNCRTIMP void set_content_type(utility::string_t type); + + /// + /// Gets the cache control header of the message. + /// + /// The cache control header value. + _ASYNCRTIMP utility::string_t cache_control() const; + + /// + /// Sets the cache control header of the message. + /// + /// The cache control header value. + _ASYNCRTIMP void set_cache_control(utility::string_t control); + + /// + /// Gets the date header of the message. + /// + /// The date header value. + _ASYNCRTIMP utility::string_t date() const; + + /// + /// Sets the date header of the message. + /// + /// The date header value. + _ASYNCRTIMP void set_date(const utility::datetime& date); + +private: + + template + bool bind_impl(const key_type &text, _t &ref) const + { + utility::istringstream_t iss(text); + iss.imbue(std::locale::classic()); + iss >> ref; + if (iss.fail() || !iss.eof()) + { + return false; + } + + return true; + } + + bool bind_impl(const key_type &text, ::utility::string_t &ref) const + { + ref = text; + return true; + } + + // Headers are stored in a map with case insensitive key. + std::map m_headers; +}; + +}} \ No newline at end of file diff --git a/3rdparty/cpprestsdk/include/cpprest/http_listener.h b/3rdparty/cpprestsdk/include/cpprest/http_listener.h new file mode 100644 index 00000000..e3d47c83 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/http_listener.h @@ -0,0 +1,314 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: HTTP listener (server-side) APIs +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#ifndef _CASA_HTTP_LISTENER_H +#define _CASA_HTTP_LISTENER_H + +#include +#include + +#include "cpprest/http_msg.h" + +#if !defined(_WIN32) || (_WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt)) + +namespace web +{ +namespace http +{ +/// HTTP listener is currently in beta. +namespace experimental +{ +/// HTTP server side library. +namespace listener +{ + +/// +/// Configuration class used to set various options when constructing and http_listener instance. +/// +class http_listener_config +{ +public: + + /// + /// Create an http_listener configuration with default options. + /// + http_listener_config() + : m_timeout(utility::seconds(120)) + {} + + /// + /// Copy constructor. + /// + /// http_listener_config to copy. + http_listener_config(const http_listener_config &other) + : m_timeout(other.m_timeout) + {} + + /// + /// Move constructor. + /// + /// http_listener_config to move from. + http_listener_config(http_listener_config &&other) + : m_timeout(std::move(other.m_timeout)) + {} + + /// + /// Assignment operator. + /// + /// http_listener_config instance. + http_listener_config & operator=(const http_listener_config &rhs) + { + if(this != &rhs) + { + m_timeout = rhs.m_timeout; + } + return *this; + } + + /// + /// Assignment operator. + /// + /// http_listener_config instance. + http_listener_config & operator=(http_listener_config &&rhs) + { + if(this != &rhs) + { + m_timeout = std::move(rhs.m_timeout); + } + return *this; + } + + /// + /// Get the timeout + /// + /// The timeout (in seconds). + utility::seconds timeout() const + { + return m_timeout; + } + + /// + /// Set the timeout + /// + /// The timeout (in seconds) used for each send and receive operation on the client. + void set_timeout(utility::seconds timeout) + { + m_timeout = std::move(timeout); + } + +private: + + utility::seconds m_timeout; +}; + +namespace details +{ + +/// +/// Internal class for pointer to implementation design pattern. +/// +class http_listener_impl +{ +public: + + http_listener_impl() + : m_closed(true) + , m_close_task(pplx::task_from_result()) + { + } + + _ASYNCRTIMP http_listener_impl(http::uri address); + _ASYNCRTIMP http_listener_impl(http::uri address, http_listener_config config); + + _ASYNCRTIMP pplx::task open(); + _ASYNCRTIMP pplx::task close(); + + /// + /// Handler for all requests. The HTTP host uses this to dispatch a message to the pipeline. + /// + /// Only HTTP server implementations should call this API. + _ASYNCRTIMP void handle_request(http::http_request msg); + + const http::uri & uri() const { return m_uri; } + + const http_listener_config & configuration() const { return m_config; } + + // Handlers + std::function m_all_requests; + std::map> m_supported_methods; + +private: + + // Default implementation for TRACE and OPTIONS. + void handle_trace(http::http_request message); + void handle_options(http::http_request message); + + // Gets a comma separated string containing the methods supported by this listener. + utility::string_t get_supported_methods() const; + + http::uri m_uri; + http_listener_config m_config; + + // Used to record that the listener is closed. + bool m_closed; + pplx::task m_close_task; +}; + +} // namespace details + +/// +/// A class for listening and processing HTTP requests at a specific URI. +/// +class http_listener +{ +public: + + /// + /// Create a listener from a URI. + /// + /// The listener will not have been opened when returned. + /// URI at which the listener should accept requests. + http_listener(http::uri address) + : m_impl(utility::details::make_unique(std::move(address))) + { + } + + /// + /// Create a listener with specified URI and configuration. + /// + /// URI at which the listener should accept requests. + /// Configuration to create listener with. + http_listener(http::uri address, http_listener_config config) + : m_impl(utility::details::make_unique(std::move(address), std::move(config))) + { + } + + /// + /// Default constructor. + /// + /// The resulting listener cannot be used for anything, but is useful to initialize a variable + /// that will later be overwritten with a real listener instance. + http_listener() + : m_impl(utility::details::make_unique()) + { + } + + /// + /// Destructor frees any held resources. + /// + /// Call close() before allowing a listener to be destroyed. + _ASYNCRTIMP ~http_listener(); + + /// + /// Asynchronously open the listener, i.e. start accepting requests. + /// + /// A task that will be completed once this listener is actually opened, accepting requests. + pplx::task open() + { + return m_impl->open(); + } + + /// + /// Asynchronously stop accepting requests and close all connections. + /// + /// A task that will be completed once this listener is actually closed, no longer accepting requests. + /// + /// This function will stop accepting requests and wait for all outstanding handler calls + /// to finish before completing the task. Waiting on the task returned from close() within + /// a handler and blocking waiting for its result will result in a deadlock. + /// + /// Call close() before allowing a listener to be destroyed. + /// + pplx::task close() + { + return m_impl->close(); + } + + /// + /// Add a general handler to support all requests. + /// + /// Function object to be called for all requests. + void support(const std::function &handler) + { + m_impl->m_all_requests = handler; + } + + /// + /// Add support for a specific HTTP method. + /// + /// An HTTP method. + /// Function object to be called for all requests for the given HTTP method. + void support(const http::method &method, const std::function &handler) + { + m_impl->m_supported_methods[method] = handler; + } + + /// + /// Get the URI of the listener. + /// + /// The URI this listener is for. + const http::uri & uri() const { return m_impl->uri(); } + + /// + /// Get the configuration of this listener. + /// + /// Configuration this listener was constructed with. + const http_listener_config & configuration() const { return m_impl->configuration(); } + + /// + /// Move constructor. + /// + /// http_listener instance to construct this one from. + http_listener(http_listener &&other) + : m_impl(std::move(other.m_impl)) + { + } + + /// + /// Move assignment operator. + /// + /// http_listener to replace this one with. + http_listener &operator=(http_listener &&other) + { + if(this != &other) + { + m_impl = std::move(other.m_impl); + } + return *this; + } + +private: + + // No copying of listeners. + http_listener(const http_listener &other); + http_listener &operator=(const http_listener &other); + + std::unique_ptr m_impl; +}; + +}}}} + +#endif +#endif \ No newline at end of file diff --git a/3rdparty/cpprestsdk/include/cpprest/http_msg.h b/3rdparty/cpprestsdk/include/cpprest/http_msg.h new file mode 100644 index 00000000..8c70766b --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/http_msg.h @@ -0,0 +1,1463 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: Request and reply message definitions. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#include +#include +#include +#include +#include + +#include "pplx/pplxtasks.h" +#include "cpprest/json.h" +#include "cpprest/uri.h" +#include "cpprest/http_headers.h" +#include "cpprest/details/cpprest_compat.h" +#include "cpprest/asyncrt_utils.h" +#include "cpprest/streams.h" +#include "cpprest/containerstream.h" + +namespace web +{ +namespace http +{ + +// URI class has been moved from web::http namespace to web namespace. +// The below using declarations ensure we don't break existing code. +// Please use the web::uri class going forward. +using web::uri; +using web::uri_builder; + +namespace client +{ + class http_client; +} + +/// +/// Predefined method strings for the standard HTTP methods mentioned in the +/// HTTP 1.1 specification. +/// +typedef utility::string_t method; + +/// +/// Common HTTP methods. +/// +class methods +{ +public: +#define _METHODS +#define DAT(a,b) _ASYNCRTIMP const static method a; +#include "cpprest/details/http_constants.dat" +#undef _METHODS +#undef DAT +}; + +typedef unsigned short status_code; + +/// +/// Predefined values for all of the standard HTTP 1.1 response status codes. +/// +class status_codes +{ +public: +#define _PHRASES +#define DAT(a,b,c) const static status_code a=b; +#include "cpprest/details/http_constants.dat" +#undef _PHRASES +#undef DAT +}; + +/// Message direction +namespace message_direction +{ + /// + /// Enumeration used to denote the direction of a message: a request with a body is + /// an upload, a response with a body is a download. + /// + enum direction { + upload, + download + }; +} + +typedef utility::string_t reason_phrase; +typedef std::function progress_handler; + +struct http_status_to_phrase +{ + unsigned short id; + reason_phrase phrase; +}; + +/// +/// Constants for the HTTP headers mentioned in RFC 2616. +/// +class header_names +{ +public: +#define _HEADER_NAMES +#define DAT(a,b) _ASYNCRTIMP const static utility::string_t a; +#include "cpprest/details/http_constants.dat" +#undef _HEADER_NAMES +#undef DAT +}; + +/// +/// Represents an HTTP error. This class holds an error message and an optional error code. +/// +class http_exception : public std::exception +{ +public: + + /// + /// Creates an http_exception with just a string message and no error code. + /// + /// Error message string. + http_exception(const utility::string_t &whatArg) + : m_msg(utility::conversions::to_utf8string(whatArg)) {} + +#ifdef _WIN32 + /// + /// Creates an http_exception with just a string message and no error code. + /// + /// Error message string. + http_exception(std::string whatArg) : m_msg(std::move(whatArg)) {} +#endif + + /// + /// Creates an http_exception with from a error code using the current platform error category. + /// The message of the error code will be used as the what() string message. + /// + /// Error code value. + http_exception(int errorCode) + : m_errorCode(utility::details::create_error_code(errorCode)) + { + m_msg = m_errorCode.message(); + } + + /// + /// Creates an http_exception with from a error code using the current platform error category. + /// + /// Error code value. + /// Message to use in what() string. + http_exception(int errorCode, const utility::string_t &whatArg) + : m_errorCode(utility::details::create_error_code(errorCode)), + m_msg(utility::conversions::to_utf8string(whatArg)) + {} + +#ifdef _WIN32 + /// + /// Creates an http_exception with from a error code using the current platform error category. + /// + /// Error code value. + /// Message to use in what() string. + http_exception(int errorCode, std::string whatArg) : + m_errorCode(utility::details::create_error_code(errorCode)), + m_msg(std::move(whatArg)) + {} +#endif + + /// + /// Creates an http_exception with from a error code and category. The message of the error code will be used + /// as the what string message. + /// + /// Error code value. + /// Error category for the code. + http_exception(int errorCode, const std::error_category &cat) : m_errorCode(std::error_code(errorCode, cat)) + { + m_msg = m_errorCode.message(); + } + + /// + /// Gets a string identifying the cause of the exception. + /// + /// A null terminated character string. + const char* what() const CPPREST_NOEXCEPT + { + return m_msg.c_str(); + } + + /// + /// Retrieves the underlying error code causing this exception. + /// + /// A std::error_code. + const std::error_code & error_code() const + { + return m_errorCode; + } + +private: + std::error_code m_errorCode; + std::string m_msg; +}; + +namespace details +{ + +/// +/// Base class for HTTP messages. +/// This class is to store common functionality so it isn't duplicated on +/// both the request and response side. +/// +class http_msg_base +{ +public: + + friend class http::client::http_client; + + _ASYNCRTIMP http_msg_base(); + + virtual ~http_msg_base() {} + + http_headers &headers() { return m_headers; } + + _ASYNCRTIMP void set_body(const concurrency::streams::istream &instream, const utf8string &contentType); + _ASYNCRTIMP void set_body(const concurrency::streams::istream &instream, const utf16string &contentType); + _ASYNCRTIMP void set_body(const concurrency::streams::istream &instream, utility::size64_t contentLength, const utf8string &contentType); + _ASYNCRTIMP void set_body(const concurrency::streams::istream &instream, utility::size64_t contentLength, const utf16string &contentType); + + /// + /// Helper function for extract functions. Parses the Content-Type header and check to make sure it matches, + /// throws an exception if not. + /// + /// If true ignores the Content-Type header value. + /// Function to verify additional information on Content-Type. + /// A string containing the charset, an empty string if no Content-Type header is empty. + utility::string_t parse_and_check_content_type(bool ignore_content_type, const std::function &check_content_type); + + _ASYNCRTIMP utf8string extract_utf8string(bool ignore_content_type = false); + _ASYNCRTIMP utf16string extract_utf16string(bool ignore_content_type = false); + _ASYNCRTIMP utility::string_t extract_string(bool ignore_content_type = false); + + _ASYNCRTIMP json::value _extract_json(bool ignore_content_type = false); + _ASYNCRTIMP std::vector _extract_vector(); + + virtual _ASYNCRTIMP utility::string_t to_string() const; + + /// + /// Completes this message + /// + virtual _ASYNCRTIMP void _complete(utility::size64_t bodySize, const std::exception_ptr &exceptionPtr = std::exception_ptr()); + + /// + /// Set the stream through which the message body could be read + /// + void set_instream(const concurrency::streams::istream &instream) { m_inStream = instream; } + + /// + /// Get the stream through which the message body could be read + /// + const concurrency::streams::istream & instream() const { return m_inStream; } + + /// + /// Set the stream through which the message body could be written + /// + void set_outstream(const concurrency::streams::ostream &outstream, bool is_default) { m_outStream = outstream; m_default_outstream = is_default; } + + /// + /// Get the stream through which the message body could be written + /// + const concurrency::streams::ostream & outstream() const { return m_outStream; } + + const pplx::task_completion_event & _get_data_available() const { return m_data_available; } + + /// + /// Prepare the message with an output stream to receive network data + /// + _ASYNCRTIMP void _prepare_to_receive_data(); + + /// + /// Determine the content length + /// + /// + /// size_t::max if there is content with unknown length (transfer_encoding:chunked) + /// 0 if there is no content + /// length if there is content with known length + /// + /// + /// This routine should only be called after a msg (request/response) has been + /// completely constructed. + /// + _ASYNCRTIMP size_t _get_content_length(); + +protected: + + /// + /// Stream to read the message body. + /// By default this is an invalid stream. The user could set the instream on + /// a request by calling set_request_stream(...). This would also be set when + /// set_body() is called - a stream from the body is constructed and set. + /// Even in the presense of msg body this stream could be invalid. An example + /// would be when the user sets an ostream for the response. With that API the + /// user does not provide the ability to read the msg body. + /// Thus m_instream is valid when there is a msg body and it can actually be read + /// + concurrency::streams::istream m_inStream; + + /// + /// stream to write the msg body + /// By default this is an invalid stream. The user could set this on the response + /// (for http_client). In all the other cases we would construct one to transfer + /// the data from the network into the message body. + /// + concurrency::streams::ostream m_outStream; + + http_headers m_headers; + bool m_default_outstream; + + /// The TCE is used to signal the availability of the message body. + pplx::task_completion_event m_data_available; +}; + +/// +/// Base structure for associating internal server information +/// with an HTTP request/response. +/// +class _http_server_context +{ +public: + _http_server_context() {} + virtual ~_http_server_context() {} +private: +}; + +/// +/// Internal representation of an HTTP response. +/// +class _http_response : public http::details::http_msg_base +{ +public: + _http_response() : m_status_code((std::numeric_limits::max)()) { } + + _http_response(http::status_code code) : m_status_code(code) {} + + http::status_code status_code() const { return m_status_code; } + + void set_status_code(http::status_code code) { m_status_code = code; } + + const http::reason_phrase & reason_phrase() const { return m_reason_phrase; } + + void set_reason_phrase(const http::reason_phrase &reason) { m_reason_phrase = reason; } + + _ASYNCRTIMP utility::string_t to_string() const; + + _http_server_context * _get_server_context() const { return m_server_context.get(); } + + void _set_server_context(std::unique_ptr server_context) { m_server_context = std::move(server_context); } + +private: + std::unique_ptr<_http_server_context> m_server_context; + + http::status_code m_status_code; + http::reason_phrase m_reason_phrase; +}; + +} // namespace details + + +/// +/// Represents an HTTP response. +/// +class http_response +{ +public: + + /// + /// Constructs a response with an empty status code, no headers, and no body. + /// + /// A new HTTP response. + http_response() : _m_impl(std::make_shared()) { } + + /// + /// Constructs a response with given status code, no headers, and no body. + /// + /// HTTP status code to use in response. + /// A new HTTP response. + http_response(http::status_code code) + : _m_impl(std::make_shared(code)) { } + + /// + /// Gets the status code of the response message. + /// + /// status code. + http::status_code status_code() const { return _m_impl->status_code(); } + + /// + /// Sets the status code of the response message. + /// + /// Status code to set. + /// + /// This will overwrite any previously set status code. + /// + void set_status_code(http::status_code code) const { _m_impl->set_status_code(code); } + + /// + /// Gets the reason phrase of the response message. + /// If no reason phrase is set it will default to the standard one corresponding to the status code. + /// + /// Reason phrase. + const http::reason_phrase & reason_phrase() const { return _m_impl->reason_phrase(); } + + /// + /// Sets the reason phrase of the response message. + /// If no reason phrase is set it will default to the standard one corresponding to the status code. + /// + /// The reason phrase to set. + void set_reason_phrase(const http::reason_phrase &reason) const { _m_impl->set_reason_phrase(reason); } + + /// + /// Gets the headers of the response message. + /// + /// HTTP headers for this response. + /// + /// Use the to fill in desired headers. + /// + http_headers &headers() { return _m_impl->headers(); } + + /// + /// Gets a const reference to the headers of the response message. + /// + /// HTTP headers for this response. + const http_headers &headers() const { return _m_impl->headers(); } + + /// + /// Generates a string representation of the message, including the body when possible. + /// Mainly this should be used for debugging purposes as it has to copy the + /// message body and doesn't have excellent performance. + /// + /// A string representation of this HTTP request. + /// Note this function is synchronous and doesn't wait for the + /// entire message body to arrive. If the message body has arrived by the time this + /// function is called and it is has a textual Content-Type it will be included. + /// Otherwise just the headers will be present. + utility::string_t to_string() const { return _m_impl->to_string(); } + + /// + /// Extracts the body of the response message as a string value, checking that the content type is a MIME text type. + /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. + /// + /// If true, ignores the Content-Type header and assumes UTF-8. + /// String containing body of the message. + pplx::task extract_string(bool ignore_content_type = false) const + { + auto impl = _m_impl; + return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->extract_string(ignore_content_type); }); + } + + /// + /// Extracts the body of the response message as a UTF-8 string value, checking that the content type is a MIME text type. + /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. + /// + /// If true, ignores the Content-Type header and assumes UTF-8. + /// String containing body of the message. + pplx::task extract_utf8string(bool ignore_content_type = false) const + { + auto impl = _m_impl; + return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->extract_utf8string(ignore_content_type); }); + } + + /// + /// Extracts the body of the response message as a UTF-16 string value, checking that the content type is a MIME text type. + /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. + /// + /// If true, ignores the Content-Type header and assumes UTF-16. + /// String containing body of the message. + pplx::task extract_utf16string(bool ignore_content_type = false) const + { + auto impl = _m_impl; + return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->extract_utf16string(ignore_content_type); }); + } + + /// + /// Extracts the body of the response message into a json value, checking that the content type is application/json. + /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. + /// + /// If true, ignores the Content-Type header and assumes UTF-8. + /// JSON value from the body of this message. + pplx::task extract_json(bool ignore_content_type = false) const + { + auto impl = _m_impl; + return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->_extract_json(ignore_content_type); }); + } + + /// + /// Extracts the body of the response message into a vector of bytes. + /// + /// The body of the message as a vector of bytes. + pplx::task> extract_vector() const + { + auto impl = _m_impl; + return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) { return impl->_extract_vector(); }); + } + + /// + /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes + /// the character encoding of the string is UTF-8. + /// + /// String containing body text. + /// MIME type to set the "Content-Type" header to. Default to "text/plain; charset=utf-8". + /// + /// This will overwrite any previously set body data and "Content-Type" header. + /// + void set_body(utf8string &&body_text, const utf8string &content_type = utf8string("text/plain; charset=utf-8")) + { + const auto length = body_text.size(); + _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)), length, content_type); + } + + /// + /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes + /// the character encoding of the string is UTF-8. + /// + /// String containing body text. + /// MIME type to set the "Content-Type" header to. Default to "text/plain; charset=utf-8". + /// + /// This will overwrite any previously set body data and "Content-Type" header. + /// + void set_body(const utf8string &body_text, const utf8string &content_type = utf8string("text/plain; charset=utf-8")) + { + _m_impl->set_body(concurrency::streams::bytestream::open_istream(body_text), body_text.size(), content_type); + } + + /// + /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes + /// the character encoding of the string is UTF-16 will perform conversion to UTF-8. + /// + /// String containing body text. + /// MIME type to set the "Content-Type" header to. Default to "text/plain". + /// + /// This will overwrite any previously set body data and "Content-Type" header. + /// + void set_body(const utf16string &body_text, utf16string content_type = ::utility::conversions::to_utf16string("text/plain")) + { + if (content_type.find(::utility::conversions::to_utf16string("charset=")) != content_type.npos) + { + throw std::invalid_argument("content_type can't contain a 'charset'."); + } + + auto utf8body = utility::conversions::utf16_to_utf8(body_text); + auto length = utf8body.size(); + _m_impl->set_body(concurrency::streams::bytestream::open_istream( + std::move(utf8body)), + length, + std::move(content_type.append(::utility::conversions::to_utf16string("; charset=utf-8")))); + } + + /// + /// Sets the body of the message to contain json value. If the 'Content-Type' + /// header hasn't already been set it will be set to 'application/json'. + /// + /// json value. + /// + /// This will overwrite any previously set body data. + /// + void set_body(const json::value &body_data) + { + auto body_text = utility::conversions::to_utf8string(body_data.serialize()); + auto length = body_text.size(); + set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)), length, _XPLATSTR("application/json")); + } + + /// + /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type' + /// header hasn't already been set it will be set to 'application/octet-stream'. + /// + /// Vector containing body data. + /// + /// This will overwrite any previously set body data. + /// + void set_body(std::vector &&body_data) + { + auto length = body_data.size(); + set_body(concurrency::streams::bytestream::open_istream(std::move(body_data)), length); + } + + /// + /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type' + /// header hasn't already been set it will be set to 'application/octet-stream'. + /// + /// Vector containing body data. + /// + /// This will overwrite any previously set body data. + /// + void set_body(const std::vector &body_data) + { + set_body(concurrency::streams::bytestream::open_istream(body_data), body_data.size()); + } + + /// + /// Defines a stream that will be relied on to provide the body of the HTTP message when it is + /// sent. + /// + /// A readable, open asynchronous stream. + /// A string holding the MIME type of the message body. + /// + /// This cannot be used in conjunction with any other means of setting the body of the request. + /// The stream will not be read until the message is sent. + /// + void set_body(const concurrency::streams::istream &stream, const utility::string_t &content_type = _XPLATSTR("application/octet-stream")) + { + _m_impl->set_body(stream, content_type); + } + + /// + /// Defines a stream that will be relied on to provide the body of the HTTP message when it is + /// sent. + /// + /// A readable, open asynchronous stream. + /// The size of the data to be sent in the body. + /// A string holding the MIME type of the message body. + /// + /// This cannot be used in conjunction with any other means of setting the body of the request. + /// The stream will not be read until the message is sent. + /// + void set_body(const concurrency::streams::istream &stream, utility::size64_t content_length, const utility::string_t &content_type = _XPLATSTR("application/octet-stream")) + { + _m_impl->set_body(stream, content_length, content_type); + } + + /// + /// Produces a stream which the caller may use to retrieve data from an incoming request. + /// + /// A readable, open asynchronous stream. + /// + /// This cannot be used in conjunction with any other means of getting the body of the request. + /// It is not necessary to wait until the message has been sent before starting to write to the + /// stream, but it is advisable to do so, since it will allow the network I/O to start earlier + /// and the work of sending data can be overlapped with the production of more data. + /// + concurrency::streams::istream body() const + { + return _m_impl->instream(); + } + + /// + /// Signals the user (client) when all the data for this response message has been received. + /// + /// A task which is completed when all of the response body has been received. + pplx::task content_ready() const + { + http_response resp = *this; + return pplx::create_task(_m_impl->_get_data_available()).then([resp](utility::size64_t) mutable { return resp; }); + } + + std::shared_ptr _get_impl() const { return _m_impl; } + + http::details::_http_server_context * _get_server_context() const { return _m_impl->_get_server_context(); } + void _set_server_context(std::unique_ptr server_context) { _m_impl->_set_server_context(std::move(server_context)); } + +private: + + std::shared_ptr _m_impl; +}; + +namespace details { +/// +/// Internal representation of an HTTP request message. +/// +class _http_request : public http::details::http_msg_base, public std::enable_shared_from_this<_http_request> +{ +public: + + _ASYNCRTIMP _http_request(http::method mtd); + + _ASYNCRTIMP _http_request(std::unique_ptr server_context); + + virtual ~_http_request() {} + + http::method &method() { return m_method; } + + uri &request_uri() { return m_uri; } + + _ASYNCRTIMP uri absolute_uri() const; + + _ASYNCRTIMP uri relative_uri() const; + + _ASYNCRTIMP void set_request_uri(const uri&); + + const pplx::cancellation_token &cancellation_token() const { return m_cancellationToken; } + + void set_cancellation_token(const pplx::cancellation_token &token) + { + m_cancellationToken = token; + } + + _ASYNCRTIMP utility::string_t to_string() const; + + _ASYNCRTIMP pplx::task reply(const http_response &response); + + pplx::task get_response() + { + return pplx::task(m_response); + } + + _ASYNCRTIMP pplx::task _reply_if_not_already(http::status_code status); + + void set_response_stream(const concurrency::streams::ostream &stream) + { + m_response_stream = stream; + } + + void set_progress_handler(const progress_handler &handler) + { + m_progress_handler = std::make_shared(handler); + } + + const concurrency::streams::ostream & _response_stream() const { return m_response_stream; } + + const std::shared_ptr & _progress_handler() const { return m_progress_handler; } + + http::details::_http_server_context * _get_server_context() const { return m_server_context.get(); } + + void _set_server_context(std::unique_ptr server_context) { m_server_context = std::move(server_context); } + + void _set_listener_path(const utility::string_t &path) { m_listener_path = path; } + + void _set_base_uri(const http::uri &base_uri) { m_base_uri = base_uri; } + +private: + + // Actual initiates sending the response, without checking if a response has already been sent. + pplx::task _reply_impl(http_response response); + + http::method m_method; + + // Tracks whether or not a response has already been started for this message. + pplx::details::atomic_long m_initiated_response; + + std::unique_ptr m_server_context; + + pplx::cancellation_token m_cancellationToken; + + http::uri m_base_uri; + http::uri m_uri; + utility::string_t m_listener_path; + + concurrency::streams::ostream m_response_stream; + + std::shared_ptr m_progress_handler; + + pplx::task_completion_event m_response; +}; + + +} // namespace details + +/// +/// Represents an HTTP request. +/// +class http_request +{ +public: + /// + /// Constructs a new HTTP request with the 'GET' method. + /// + http_request() + : _m_impl(std::make_shared(methods::GET)) {} + + /// + /// Constructs a new HTTP request with the given request method. + /// + /// Request method. + http_request(http::method mtd) + : _m_impl(std::make_shared(std::move(mtd))) {} + + /// + /// Destructor frees any held resources. + /// + ~http_request() {} + + /// + /// Get the method (GET/PUT/POST/DELETE) of the request message. + /// + /// Request method of this HTTP request. + const http::method &method() const { return _m_impl->method(); } + + /// + /// Get the method (GET/PUT/POST/DELETE) of the request message. + /// + /// Request method of this HTTP request. + void set_method(const http::method &method) const { _m_impl->method() = method; } + + /// + /// Get the underling URI of the request message. + /// + /// The uri of this message. + const uri & request_uri() const { return _m_impl->request_uri(); } + + /// + /// Set the underling URI of the request message. + /// + /// The uri for this message. + void set_request_uri(const uri& uri) { return _m_impl->set_request_uri(uri); } + + /// + /// Gets a reference the URI path, query, and fragment part of this request message. + /// This will be appended to the base URI specified at construction of the http_client. + /// + /// A string. + /// When the request is the one passed to a listener's handler, the + /// relative URI is the request URI less the listener's path. In all other circumstances, + /// request_uri() and relative_uri() will return the same value. + /// + uri relative_uri() const { return _m_impl->relative_uri(); } + + /// + /// Get an absolute URI with scheme, host, port, path, query, and fragment part of + /// the request message. + /// + /// Absolute URI is only valid after this http_request object has been passed + /// to http_client::request(). + /// + uri absolute_uri() const { return _m_impl->absolute_uri(); } + + /// + /// Gets a reference to the headers of the response message. + /// + /// HTTP headers for this response. + /// + /// Use the http_headers::add to fill in desired headers. + /// + http_headers &headers() { return _m_impl->headers(); } + + /// + /// Gets a const reference to the headers of the response message. + /// + /// HTTP headers for this response. + /// + /// Use the http_headers::add to fill in desired headers. + /// + const http_headers &headers() const { return _m_impl->headers(); } + + /// + /// Extract the body of the request message as a string value, checking that the content type is a MIME text type. + /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. + /// + /// If true, ignores the Content-Type header and assumes UTF-8. + /// String containing body of the message. + pplx::task extract_string(bool ignore_content_type = false) + { + auto impl = _m_impl; + return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->extract_string(ignore_content_type); }); + } + + /// + /// Extract the body of the request message as a UTF-8 string value, checking that the content type is a MIME text type. + /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. + /// + /// If true, ignores the Content-Type header and assumes UTF-8. + /// String containing body of the message. + pplx::task extract_utf8string(bool ignore_content_type = false) + { + auto impl = _m_impl; + return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->extract_utf8string(ignore_content_type); }); + } + + /// + /// Extract the body of the request message as a UTF-16 string value, checking that the content type is a MIME text type. + /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. + /// + /// If true, ignores the Content-Type header and assumes UTF-16. + /// String containing body of the message. + pplx::task extract_utf16string(bool ignore_content_type = false) + { + auto impl = _m_impl; + return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->extract_utf16string(ignore_content_type); }); + } + + /// + /// Extracts the body of the request message into a json value, checking that the content type is application/json. + /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. + /// + /// If true, ignores the Content-Type header and assumes UTF-8. + /// JSON value from the body of this message. + pplx::task extract_json(bool ignore_content_type = false) const + { + auto impl = _m_impl; + return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->_extract_json(ignore_content_type); }); + } + + /// + /// Extract the body of the response message into a vector of bytes. Extracting a vector can be done on + /// + /// The body of the message as a vector of bytes. + pplx::task> extract_vector() const + { + auto impl = _m_impl; + return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) { return impl->_extract_vector(); }); + } + + /// + /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes + /// the character encoding of the string is UTF-8. + /// + /// String containing body text. + /// MIME type to set the "Content-Type" header to. Default to "text/plain; charset=utf-8". + /// + /// This will overwrite any previously set body data and "Content-Type" header. + /// + void set_body(utf8string &&body_text, const utf8string &content_type = utf8string("text/plain; charset=utf-8")) + { + const auto length = body_text.size(); + _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)), length, content_type); + } + + /// + /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes + /// the character encoding of the string is UTF-8. + /// + /// String containing body text. + /// MIME type to set the "Content-Type" header to. Default to "text/plain; charset=utf-8". + /// + /// This will overwrite any previously set body data and "Content-Type" header. + /// + void set_body(const utf8string &body_text, const utf8string &content_type = utf8string("text/plain; charset=utf-8")) + { + _m_impl->set_body(concurrency::streams::bytestream::open_istream(body_text), body_text.size(), content_type); + } + + /// + /// Sets the body of the message to a textual string and set the "Content-Type" header. Assumes + /// the character encoding of the string is UTF-16 will perform conversion to UTF-8. + /// + /// + /// String containing body text. + /// MIME type to set the "Content-Type" header to. Default to "text/plain". + /// + /// This will overwrite any previously set body data and "Content-Type" header. + /// + void set_body(const utf16string &body_text, utf16string content_type = ::utility::conversions::to_utf16string("text/plain")) + { + if(content_type.find(::utility::conversions::to_utf16string("charset=")) != content_type.npos) + { + throw std::invalid_argument("content_type can't contain a 'charset'."); + } + + auto utf8body = utility::conversions::utf16_to_utf8(body_text); + auto length = utf8body.size(); + _m_impl->set_body(concurrency::streams::bytestream::open_istream( + std::move(utf8body)), + length, + std::move(content_type.append(::utility::conversions::to_utf16string("; charset=utf-8")))); + } + + /// + /// Sets the body of the message to contain json value. If the 'Content-Type' + /// header hasn't already been set it will be set to 'application/json'. + /// + /// json value. + /// + /// This will overwrite any previously set body data. + /// + void set_body(const json::value &body_data) + { + auto body_text = utility::conversions::to_utf8string(body_data.serialize()); + auto length = body_text.size(); + _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)), length, _XPLATSTR("application/json")); + } + + /// + /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type' + /// header hasn't already been set it will be set to 'application/octet-stream'. + /// + /// Vector containing body data. + /// + /// This will overwrite any previously set body data. + /// + void set_body(std::vector &&body_data) + { + auto length = body_data.size(); + _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_data)), length, _XPLATSTR("application/octet-stream")); + } + + /// + /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type' + /// header hasn't already been set it will be set to 'application/octet-stream'. + /// + /// Vector containing body data. + /// + /// This will overwrite any previously set body data. + /// + void set_body(const std::vector &body_data) + { + set_body(concurrency::streams::bytestream::open_istream(body_data), body_data.size()); + } + + /// + /// Defines a stream that will be relied on to provide the body of the HTTP message when it is + /// sent. + /// + /// A readable, open asynchronous stream. + /// A string holding the MIME type of the message body. + /// + /// This cannot be used in conjunction with any other means of setting the body of the request. + /// The stream will not be read until the message is sent. + /// + void set_body(const concurrency::streams::istream &stream, const utility::string_t &content_type = _XPLATSTR("application/octet-stream")) + { + _m_impl->set_body(stream, content_type); + } + + /// + /// Defines a stream that will be relied on to provide the body of the HTTP message when it is + /// sent. + /// + /// A readable, open asynchronous stream. + /// The size of the data to be sent in the body. + /// A string holding the MIME type of the message body. + /// + /// This cannot be used in conjunction with any other means of setting the body of the request. + /// The stream will not be read until the message is sent. + /// + void set_body(const concurrency::streams::istream &stream, utility::size64_t content_length, const utility::string_t &content_type = _XPLATSTR("application/octet-stream")) + { + _m_impl->set_body(stream, content_length, content_type); + } + + /// + /// Produces a stream which the caller may use to retrieve data from an incoming request. + /// + /// A readable, open asynchronous stream. + /// + /// This cannot be used in conjunction with any other means of getting the body of the request. + /// It is not necessary to wait until the message has been sent before starting to write to the + /// stream, but it is advisable to do so, since it will allow the network I/O to start earlier + /// and the work of sending data can be overlapped with the production of more data. + /// + concurrency::streams::istream body() const + { + return _m_impl->instream(); + } + + /// + /// Defines a stream that will be relied on to hold the body of the HTTP response message that + /// results from the request. + /// + /// A writable, open asynchronous stream. + /// + /// If this function is called, the body of the response should not be accessed in any other + /// way. + /// + void set_response_stream(const concurrency::streams::ostream &stream) + { + return _m_impl->set_response_stream(stream); + } + + /// + /// Defines a callback function that will be invoked for every chunk of data uploaded or downloaded + /// as part of the request. + /// + /// A function representing the progress handler. It's parameters are: + /// up: a message_direction::direction value indicating the direction of the message + /// that is being reported. + /// progress: the number of bytes that have been processed so far. + /// + /// + /// This function will be called at least once for upload and at least once for + /// the download body, unless there is some exception generated. An HTTP message with an error + /// code is not an exception. This means, that even if there is no body, the progress handler + /// will be called. + /// + /// Setting the chunk size on the http_client does not guarantee that the client will be using + /// exactly that increment for uploading and downloading data. + /// + /// The handler will be called only once for each combination of argument values, in order. Depending + /// on how a service responds, some download values may come before all upload values have been + /// reported. + /// + /// The progress handler will be called on the thread processing the request. This means that + /// the implementation of the handler must take care not to block the thread or do anything + /// that takes significant amounts of time. In particular, do not do any kind of I/O from within + /// the handler, do not update user interfaces, and to not acquire any locks. If such activities + /// are necessary, it is the handler's responsibility to execute that work on a separate thread. + /// + void set_progress_handler(const progress_handler &handler) + { + return _m_impl->set_progress_handler(handler); + } + + /// + /// Asynchronously responses to this HTTP request. + /// + /// Response to send. + /// An asynchronous operation that is completed once response is sent. + pplx::task reply(const http_response &response) const { return _m_impl->reply(response); } + + /// + /// Asynchronously responses to this HTTP request. + /// + /// Response status code. + /// An asynchronous operation that is completed once response is sent. + pplx::task reply(http::status_code status) const + { + return reply(http_response(status)); + } + + /// + /// Responds to this HTTP request. + /// + /// Response status code. + /// Json value to use in the response body. + /// An asynchronous operation that is completed once response is sent. + pplx::task reply(http::status_code status, const json::value &body_data) const + { + http_response response(status); + response.set_body(body_data); + return reply(response); + } + + /// Responds to this HTTP request with a string. + /// Assumes the character encoding of the string is UTF-8. + /// + /// Response status code. + /// UTF-8 string containing the text to use in the response body. + /// Content type of the body. + /// An asynchronous operation that is completed once response is sent. + /// + // Callers of this function do NOT need to block waiting for the response to be + /// sent to before the body data is destroyed or goes out of scope. + /// + pplx::task reply(http::status_code status, utf8string &&body_data, const utf8string &content_type = "text/plain; charset=utf-8") const + { + http_response response(status); + response.set_body(std::move(body_data), content_type); + return reply(response); + } + + /// + /// Responds to this HTTP request with a string. + /// Assumes the character encoding of the string is UTF-8. + /// + /// Response status code. + /// UTF-8 string containing the text to use in the response body. + /// Content type of the body. + /// An asynchronous operation that is completed once response is sent. + /// + // Callers of this function do NOT need to block waiting for the response to be + /// sent to before the body data is destroyed or goes out of scope. + /// + pplx::task reply(http::status_code status, const utf8string &body_data, const utf8string &content_type = "text/plain; charset=utf-8") const + { + http_response response(status); + response.set_body(body_data, content_type); + return reply(response); + } + + /// + /// Responds to this HTTP request with a string. Assumes the character encoding + /// of the string is UTF-16 will perform conversion to UTF-8. + /// + /// Response status code. + /// UTF-16 string containing the text to use in the response body. + /// Content type of the body. + /// An asynchronous operation that is completed once response is sent. + /// + // Callers of this function do NOT need to block waiting for the response to be + /// sent to before the body data is destroyed or goes out of scope. + /// + pplx::task reply(http::status_code status, const utf16string &body_data, const utf16string &content_type = ::utility::conversions::to_utf16string("text/plain")) const + { + http_response response(status); + response.set_body(body_data, content_type); + return reply(response); + } + + /// + /// Responds to this HTTP request. + /// + /// Response status code. + /// A string holding the MIME type of the message body. + /// An asynchronous stream representing the body data. + /// A task that is completed once a response from the request is received. + pplx::task reply(status_code status, const concurrency::streams::istream &body, const utility::string_t &content_type = _XPLATSTR("application/octet-stream")) const + { + http_response response(status); + response.set_body(body, content_type); + return reply(response); + } + + /// + /// Responds to this HTTP request. + /// + /// Response status code. + /// The size of the data to be sent in the body.. + /// A string holding the MIME type of the message body. + /// An asynchronous stream representing the body data. + /// A task that is completed once a response from the request is received. + pplx::task reply(status_code status, const concurrency::streams::istream &body, utility::size64_t content_length, const utility::string_t &content_type = _XPLATSTR("application/octet-stream")) const + { + http_response response(status); + response.set_body(body, content_length, content_type); + return reply(response); + } + + /// + /// Signals the user (listener) when all the data for this request message has been received. + /// + /// A task which is completed when all of the response body has been received + pplx::task content_ready() const + { + http_request req = *this; + return pplx::create_task(_m_impl->_get_data_available()).then([req](utility::size64_t) mutable { return req; }); + } + + /// + /// Gets a task representing the response that will eventually be sent. + /// + /// A task that is completed once response is sent. + pplx::task get_response() const + { + return _m_impl->get_response(); + } + + /// + /// Generates a string representation of the message, including the body when possible. + /// Mainly this should be used for debugging purposes as it has to copy the + /// message body and doesn't have excellent performance. + /// + /// A string representation of this HTTP request. + /// Note this function is synchronous and doesn't wait for the + /// entire message body to arrive. If the message body has arrived by the time this + /// function is called and it is has a textual Content-Type it will be included. + /// Otherwise just the headers will be present. + utility::string_t to_string() const { return _m_impl->to_string(); } + + /// + /// Sends a response if one has not already been sent. + /// + pplx::task _reply_if_not_already(status_code status) { return _m_impl->_reply_if_not_already(status); } + + /// + /// Gets the server context associated with this HTTP message. + /// + http::details::_http_server_context * _get_server_context() const { return _m_impl->_get_server_context(); } + + /// + /// These are used for the initial creation of the HTTP request. + /// + static http_request _create_request(std::unique_ptr server_context) { return http_request(std::move(server_context)); } + void _set_server_context(std::unique_ptr server_context) { _m_impl->_set_server_context(std::move(server_context)); } + + void _set_listener_path(const utility::string_t &path) { _m_impl->_set_listener_path(path); } + + const std::shared_ptr & _get_impl() const { return _m_impl; } + + void _set_cancellation_token(const pplx::cancellation_token &token) + { + _m_impl->set_cancellation_token(token); + } + + const pplx::cancellation_token & _cancellation_token() const + { + return _m_impl->cancellation_token(); + } + + void _set_base_uri(const http::uri &base_uri) + { + _m_impl->_set_base_uri(base_uri); + } + +private: + friend class http::details::_http_request; + friend class http::client::http_client; + + http_request(std::unique_ptr server_context) : _m_impl(std::make_shared(std::move(server_context))) {} + + std::shared_ptr _m_impl; +}; + +/// +/// HTTP client handler class, used to represent an HTTP pipeline stage. +/// +/// +/// When a request goes out, it passes through a series of stages, customizable by +/// the application and/or libraries. The default stage will interact with lower-level +/// communication layers to actually send the message on the network. When creating a client +/// instance, an application may add pipeline stages in front of the already existing +/// stages. Each stage has a reference to the next stage available in the +/// value. +/// +class http_pipeline_stage : public std::enable_shared_from_this +{ +public: + + virtual ~http_pipeline_stage() + { + } + + /// + /// Runs this stage against the given request and passes onto the next stage. + /// + /// The HTTP request. + /// A task of the HTTP response. + virtual pplx::task propagate(http_request request) = 0; + +protected: + + http_pipeline_stage() + { + } + + /// + /// Gets the next stage in the pipeline. + /// + /// A shared pointer to a pipeline stage. + const std::shared_ptr & next_stage() const + { + return m_next_stage; + } + + /// + /// Gets a shared pointer to this pipeline stage. + /// + /// A shared pointer to a pipeline stage. + std::shared_ptr current_stage() + { + return this->shared_from_this(); + } + +private: + friend class http_pipeline; + + void set_next_stage(const std::shared_ptr &next) + { + m_next_stage = next; + } + + std::shared_ptr m_next_stage; + + // No copy or assignment. + http_pipeline_stage & operator=(const http_pipeline_stage &); + http_pipeline_stage(const http_pipeline_stage &); +}; + +namespace details { + +class function_pipeline_wrapper : public http::http_pipeline_stage +{ +public: + function_pipeline_wrapper(std::function(http_request, std::shared_ptr)> handler) : m_handler(handler) + { + } + + virtual pplx::task propagate(http_request request) override + { + return m_handler(request, next_stage()); + } +private: + + std::function(http_request, std::shared_ptr)> m_handler; +}; + +} // namespace details + +/// +/// +/// +class http_pipeline +{ +public: + + /// + /// Create an http pipeline that consists of a linear chain of stages + /// + /// The final stage + static std::shared_ptr create_pipeline(const std::shared_ptr &last) + { + return std::shared_ptr(new http_pipeline(last)); + } + + /// + /// Initiate an http request into the pipeline + /// + /// Http request + pplx::task propagate(http_request request) + { + std::shared_ptr first; + { + pplx::extensibility::scoped_recursive_lock_t l(m_lock); + first = (m_stages.size() > 0) ? m_stages[0] : m_last_stage; + } + return first->propagate(request); + } + + /// + /// Adds an HTTP pipeline stage to the pipeline. + /// + /// A pipeline stage. + void append(const std::shared_ptr &stage) + { + pplx::extensibility::scoped_recursive_lock_t l(m_lock); + + if (m_stages.size() > 0) + { + std::shared_ptr penultimate = m_stages[m_stages.size()-1]; + penultimate->set_next_stage(stage); + } + stage->set_next_stage(m_last_stage); + + m_stages.push_back(stage); + } + + /// + /// Sets the last stage of the pipeline. + /// + /// Shared pointer to pipeline stage to set as the last. + void set_last_stage(const std::shared_ptr &last) + { + m_last_stage = last; + } + + /// + /// Retrieves the last stage in this pipeline. + /// + /// A shared pointer to last stage. + const std::shared_ptr& last_stage() const + { + return m_last_stage; + } + +private: + + http_pipeline(const std::shared_ptr &last) : m_last_stage(last) + { + } + + // The vector of pipeline stages. + std::vector> m_stages; + + // The last stage is always set up by the client or listener and cannot + // be changed. All application-defined stages are executed before the + // last stage, which is typically a send or dispatch. + std::shared_ptr m_last_stage; + + pplx::extensibility::recursive_lock_t m_lock; + + // No copy or assignment. + http_pipeline & operator=(const http_pipeline &); + http_pipeline(const http_pipeline &); +}; + +}} diff --git a/3rdparty/cpprestsdk/include/cpprest/interopstream.h b/3rdparty/cpprestsdk/include/cpprest/interopstream.h new file mode 100644 index 00000000..48c26d05 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/interopstream.h @@ -0,0 +1,536 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Adapter classes for async and STD stream buffers, used to connect std-based and async-based APIs. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#include "pplx/pplxtasks.h" +#include "cpprest/astreambuf.h" +#include "cpprest/streams.h" + +#if defined(_WIN32) +#pragma warning(push) +#pragma warning(disable : 4250) +#endif + +namespace Concurrency { namespace streams { + + template class stdio_ostream; + template class stdio_istream; + + namespace details { + + /// + /// The basic_stdio_buffer class serves to support interoperability with STL stream buffers. + /// Sitting atop a std::streambuf, which does all the I/O, instances of this class may read + /// and write data to standard iostreams. The class itself should not be used in application + /// code, it is used by the stream definitions farther down in the header file. + /// + template + class basic_stdio_buffer : public streambuf_state_manager<_CharType> + { + typedef concurrency::streams::char_traits<_CharType> traits; + typedef typename traits::int_type int_type; + typedef typename traits::pos_type pos_type; + typedef typename traits::off_type off_type; + /// + /// Private constructor + /// + basic_stdio_buffer(_In_ std::basic_streambuf<_CharType>* streambuf, std::ios_base::openmode mode) + : streambuf_state_manager<_CharType>(mode), m_buffer(streambuf) + { + } + + public: + /// + /// Destructor + /// + virtual ~basic_stdio_buffer() + { + this->_close_read(); + this->_close_write(); + } + + private: + // + // The functions overridden below here are documented elsewhere. + // See astreambuf.h for further information. + // + virtual bool can_seek() const { return this->is_open(); } + virtual bool has_size() const { return false; } + + virtual size_t in_avail() const { return (size_t)m_buffer->in_avail(); } + + virtual size_t buffer_size(std::ios_base::openmode) const { return 0; } + virtual void set_buffer_size(size_t, std::ios_base::openmode) { return; } + + virtual pplx::task _sync() { return pplx::task_from_result(m_buffer->pubsync() == 0); } + + virtual pplx::task _putc(_CharType ch) { return pplx::task_from_result(m_buffer->sputc(ch)); } + virtual pplx::task _putn(const _CharType *ptr, size_t size) { return pplx::task_from_result((size_t)m_buffer->sputn(ptr, size)); } + + size_t _sgetn(_Out_writes_ (size) _CharType *ptr, _In_ size_t size) const { return m_buffer->sgetn(ptr, size); } + virtual size_t _scopy(_Out_writes_ (size) _CharType *, _In_ size_t size) { (void)(size); return (size_t)-1; } + + virtual pplx::task _getn(_Out_writes_ (size) _CharType *ptr, _In_ size_t size) { return pplx::task_from_result((size_t)m_buffer->sgetn(ptr, size)); } + + virtual int_type _sbumpc() { return m_buffer->sbumpc(); } + virtual int_type _sgetc() { return m_buffer->sgetc(); } + + virtual pplx::task _bumpc() { return pplx::task_from_result(m_buffer->sbumpc()); } + virtual pplx::task _getc() { return pplx::task_from_result(m_buffer->sgetc()); } + virtual pplx::task _nextc() { return pplx::task_from_result(m_buffer->snextc()); } + virtual pplx::task _ungetc() { return pplx::task_from_result(m_buffer->sungetc()); } + + virtual pos_type getpos(std::ios_base::openmode mode) const { return m_buffer->pubseekoff(0, std::ios_base::cur, mode); } + virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode) { return m_buffer->pubseekpos(pos, mode); } + virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode mode) { return m_buffer->pubseekoff(off, dir, mode); } + + virtual _CharType* _alloc(size_t) { return nullptr; } + virtual void _commit(size_t) {} + + virtual bool acquire(_CharType*&, size_t&) { return false; } + virtual void release(_CharType *, size_t) { } + + template friend class concurrency::streams::stdio_ostream; + template friend class concurrency::streams::stdio_istream; + + std::basic_streambuf<_CharType>* m_buffer; + }; + + } // namespace details + + /// + /// stdio_ostream represents an async ostream derived from a standard synchronous stream, as + /// defined by the "std" namespace. It is constructed from a reference to a standard stream, which + /// must be valid for the lifetime of the asynchronous stream. + /// + /// + /// The data type of the basic element of the stdio_ostream. + /// + /// + /// Since std streams are not reference-counted, great care must be taken by an application to make + /// sure that the std stream does not get destroyed until all uses of the asynchronous stream are + /// done and have been serviced. + /// + template + class stdio_ostream : public basic_ostream + { + public: + /// + /// Constructor + /// + /// + /// The data type of the basic element of the source output stream. + /// + /// The synchronous stream that this is using for its I/O + template + stdio_ostream(std::basic_ostream& stream) + : basic_ostream(streams::streambuf(std::shared_ptr>(new details::basic_stdio_buffer(stream.rdbuf(), std::ios_base::out)))) + { + } + + /// + /// Copy constructor + /// + /// The source object + stdio_ostream(const stdio_ostream &other) : basic_ostream(other) { } + + /// + /// Assignment operator + /// + /// The source object + /// A reference to the output stream object that contains the result of the assignment. + stdio_ostream & operator =(const stdio_ostream &other) { basic_ostream::operator=(other); return *this; } + }; + + /// + /// stdio_istream represents an async istream derived from a standard synchronous stream, as + /// defined by the "std" namespace. It is constructed from a reference to a standard stream, which + /// must be valid for the lifetime of the asynchronous stream. + /// + /// + /// The data type of the basic element of the stdio_istream. + /// + /// + /// Since std streams are not reference-counted, great care must be taken by an application to make + /// sure that the std stream does not get destroyed until all uses of the asynchronous stream are + /// done and have been serviced. + /// + template + class stdio_istream : public basic_istream + { + public: + /// + /// Constructor + /// + /// + /// The data type of the basic element of the source istream + /// + /// The synchronous stream that this is using for its I/O + template + stdio_istream(std::basic_istream& stream) + : basic_istream(streams::streambuf(std::shared_ptr>(new details::basic_stdio_buffer(stream.rdbuf(), std::ios_base::in)))) + { + } + + /// + /// Copy constructor + /// + /// The source object + stdio_istream(const stdio_istream &other) : basic_istream(other) { } + + /// + /// Assignment operator + /// + /// The source object + /// A reference to the input stream object that contains the result of the assignment. + stdio_istream & operator =(const stdio_istream &other) { basic_istream::operator=(other); return *this; } + }; + + namespace details { + + /// + /// IO streams stream buffer implementation used to interface with an async streambuffer underneath. + /// Used for implementing the standard synchronous streams that provide interop between std:: and concurrency::streams:: + /// + template + class basic_async_streambuf : public std::basic_streambuf + { + public: + typedef concurrency::streams::char_traits traits; + typedef typename traits::int_type int_type; + typedef typename traits::pos_type pos_type; + typedef typename traits::off_type off_type; + + basic_async_streambuf(const streams::streambuf &async_buf) : m_buffer(async_buf) + { + } + protected: + + // + // The following are the functions in std::basic_streambuf that we need to override. + // + + /// + /// Writes one byte to the stream buffer. + /// + int_type overflow(int_type ch) + { + try + { + return m_buffer.putc(CharType(ch)).get(); + } + catch(...) + { + return traits::eof(); + } + } + + /// + /// Gets one byte from the stream buffer without moving the read position. + /// + int_type underflow() + { + try + { + return m_buffer.getc().get(); + } + catch(...) + { + return traits::eof(); + } + } + + /// + /// Gets one byte from the stream buffer and move the read position one character. + /// + int_type uflow() + { + try + { + return m_buffer.bumpc().get(); + } + catch(...) + { + return traits::eof(); + } + } + + /// + /// Gets a number of characters from the buffer and place it into the provided memory block. + /// + std::streamsize xsgetn(_Out_writes_ (count) CharType* ptr, _In_ std::streamsize count) + { + size_t cnt = size_t(count); + size_t read_so_far = 0; + + try + { + while (read_so_far < cnt) + { + size_t rd = m_buffer.getn(ptr+read_so_far, cnt-read_so_far).get(); + read_so_far += rd; + if ( rd == 0 ) + break; + } + return read_so_far; + } + catch(...) + { + return 0; + } + } + + /// + /// Writes a given number of characters from the provided block into the stream buffer. + /// + std::streamsize xsputn(const CharType* ptr, std::streamsize count) + { + try + { + return m_buffer.putn_nocopy(ptr, static_cast(count)).get(); + } + catch(...) + { + return 0; + } + } + + /// + /// Synchronizes with the underlying medium. + /// + int sync() // must be int as per std::basic_streambuf + { + try + { + m_buffer.sync().wait(); + } + catch(...) + { + } + return 0; + } + + /// + /// Seeks to the given offset relative to the beginning, end, or current position. + /// + pos_type seekoff(off_type offset, + std::ios_base::seekdir dir, + std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + { + try + { + if ( dir == std::ios_base::cur && offset == 0) // Special case for getting the current position. + return m_buffer.getpos(mode); + return m_buffer.seekoff(offset,dir,mode); + } + catch(...) + { + return (pos_type(-1)); + } + } + + /// + /// Seeks to the given offset relative to the beginning of the stream. + /// + pos_type seekpos(pos_type pos, + std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + { + try + { + return m_buffer.seekpos(pos, mode); + } + catch(...) + { + return (pos_type(-1)); + } + } + + private: + concurrency::streams::streambuf m_buffer; + }; + + } // namespace details + + /// + /// A concrete STL ostream which relies on an asynchronous stream for its I/O. + /// + /// + /// The data type of the basic element of the stream. + /// + template + class async_ostream : public std::basic_ostream + { + public: + /// + /// Constructor + /// + /// + /// The data type of the basic element of the source ostream. + /// + /// The asynchronous stream whose stream buffer should be used for I/O + template + async_ostream(const streams::basic_ostream &astream) + : std::basic_ostream(&m_strbuf), + m_strbuf(astream.streambuf()) + { + } + + /// + /// Constructor + /// + /// + /// The data type of the basic element of the source streambuf. + /// + /// The asynchronous stream buffer to use for I/O + template + async_ostream(const streams::streambuf &strbuf) + : std::basic_ostream(&m_strbuf), + m_strbuf(strbuf) + { + } + + private: + details::basic_async_streambuf m_strbuf; + }; + + /// + /// A concrete STL istream which relies on an asynchronous stream for its I/O. + /// + /// + /// The data type of the basic element of the stream. + /// + template + class async_istream : public std::basic_istream + { + public: + /// + /// Constructor + /// + /// + /// The data type of the basic element of the source istream. + /// + /// The asynchronous stream whose stream buffer should be used for I/O + template + async_istream(const streams::basic_istream &astream) + : std::basic_istream(&m_strbuf), + m_strbuf(astream.streambuf()) + { + } + + /// + /// Constructor + /// + /// + /// The data type of the basic element of the source streambuf. + /// + /// The asynchronous stream buffer to use for I/O + template + async_istream(const streams::streambuf &strbuf) + : std::basic_istream(&m_strbuf), + m_strbuf(strbuf) + { + } + + private: + details::basic_async_streambuf m_strbuf; + }; + + /// + /// A concrete STL istream which relies on an asynchronous stream buffer for its I/O. + /// + /// + /// The data type of the basic element of the stream. + /// + template + class async_iostream : public std::basic_iostream + { + public: + /// + /// Constructor + /// + /// The asynchronous stream buffer to use for I/O + async_iostream(const streams::streambuf &strbuf) + : std::basic_iostream(&m_strbuf), + m_strbuf(strbuf) + { + } + + private: + details::basic_async_streambuf m_strbuf; + }; + +#if defined(__cplusplus_winrt) + + /// + /// Static class containing factory functions for WinRT streams implemented on top of Casablanca async streams. + /// + /// WinRT streams are defined in terms of single-byte characters only. + class winrt_stream + { + public: + /// + /// Creates a WinRT IInputStream reference from an asynchronous stream buffer. + /// + /// A stream buffer based on a single-byte character. + /// A reference to a WinRT IInputStream. + /// + /// The stream buffer passed in must allow reading. + /// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For + /// example, using a producer_consumer_buffer, a Casablanca-based caller can pass data to a WinRT component. + /// + _ASYNCRTIMP static Windows::Storage::Streams::IInputStream^ __cdecl create_input_stream(const concurrency::streams::streambuf &buffer); + + /// + /// Creates a WinRT IOutputStream reference from an asynchronous stream buffer. + /// + /// A stream buffer based on a single-byte character. + /// A reference to a WinRT IOutputStream. + /// + /// The stream buffer passed in must allow writing. + /// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For + /// example, using a producer_consumer_buffer, a Casablanca-based caller can retrieve data from a WinRT component. + /// + _ASYNCRTIMP static Windows::Storage::Streams::IOutputStream^ __cdecl create_output_stream(const concurrency::streams::streambuf &buffer); + + /// + /// Creates a WinRT IRandomAccessStream reference from an asynchronous input stream. + /// + /// A stream based on a single-byte character. + /// A reference to a WinRT IRandomAccessStream. + /// + /// The stream buffer is shared with the caller, allowing data to be passed between the two contexts. For + /// example, using a producer_consumer_buffer, a Casablanca-based caller can pass data to and retrieve data + /// from a WinRT component. + /// + _ASYNCRTIMP static Windows::Storage::Streams::IRandomAccessStream^ __cdecl create_random_access_stream(const concurrency::streams::streambuf &buffer); + }; + +#endif + +}} // namespaces + +#if defined(_WIN32) +#pragma warning(pop) +#endif \ No newline at end of file diff --git a/3rdparty/cpprestsdk/include/cpprest/json.h b/3rdparty/cpprestsdk/include/cpprest/json.h new file mode 100644 index 00000000..c1e39c02 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/json.h @@ -0,0 +1,1936 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: JSON parser and writer +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#ifndef _CASA_JSON_H +#define _CASA_JSON_H + +#include +#include +#include +#include +#include +#include +#include "cpprest/details/basic_types.h" +#include "cpprest/asyncrt_utils.h" + +namespace web +{ +/// Library for parsing and serializing JSON values to and from C++ types. +namespace json +{ + // Various forward declarations. + namespace details + { + class _Value; + class _Number; + class _Null; + class _Boolean; + class _String; + class _Object; + class _Array; + template class JSON_Parser; + } + + namespace details + { + extern bool g_keep_json_object_unsorted; + } + + /// + /// Preserve the order of the name/value pairs when parsing a JSON object. + /// The default is false, which can yield better performance. + /// + /// true if ordering should be preserved when parsing, false otherwise. + /// Note this is a global setting and affects all JSON parsing done. + void _ASYNCRTIMP __cdecl keep_object_element_order(bool keep_order); + +#ifdef _WIN32 +#ifdef _DEBUG +#define ENABLE_JSON_VALUE_VISUALIZER +#endif +#endif + + class number; + class array; + class object; + + /// + /// A JSON value represented as a C++ class. + /// + class value + { + public: + /// + /// This enumeration represents the various kinds of JSON values. + /// + enum value_type + { + /// Number value + Number, + /// Boolean value + Boolean, + /// String value + String, + /// Object value + Object, + /// Array value + Array, + /// Null value + Null + }; + + /// + /// Constructor creating a null value + /// + _ASYNCRTIMP value(); + + /// + /// Constructor creating a JSON number value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP value(int32_t value); + + /// + /// Constructor creating a JSON number value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP value(uint32_t value); + + /// + /// Constructor creating a JSON number value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP value(int64_t value); + + /// + /// Constructor creating a JSON number value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP value(uint64_t value); + + /// + /// Constructor creating a JSON number value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP value(double value); + + /// + /// Constructor creating a JSON Boolean value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP explicit value(bool value); + + /// + /// Constructor creating a JSON string value + /// + /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character width + /// + /// This constructor has O(n) performance because it tries to determine if + /// specified string has characters that should be properly escaped in JSON. + /// + _ASYNCRTIMP explicit value(utility::string_t value); + + /// + /// Constructor creating a JSON string value specifying if the string contains characters to escape + /// + /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character width + /// Whether contains characters + /// that should be escaped in JSON value + /// + /// This constructor has O(1) performance. + /// + _ASYNCRTIMP explicit value(utility::string_t value, bool has_escape_chars); + + /// + /// Constructor creating a JSON string value + /// + /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character width + /// + /// + /// This constructor has O(n) performance because it tries to determine if + /// specified string has characters that should be properly escaped in JSON. + /// + /// + /// This constructor exists in order to avoid string literals matching another constructor, + /// as is very likely. For example, conversion to bool does not require a user-defined conversion, + /// and will therefore match first, which means that the JSON value turns up as a boolean. + /// + /// + _ASYNCRTIMP explicit value(const utility::char_t* value); + + /// + /// Constructor creating a JSON string value + /// + /// The C++ value to create a JSON value from, a C++ STL string of the platform-native character width + /// Whether contains characters + /// + /// + /// This overload has O(1) performance. + /// + /// + /// This constructor exists in order to avoid string literals matching another constructor, + /// as is very likely. For example, conversion to bool does not require a user-defined conversion, + /// and will therefore match first, which means that the JSON value turns up as a boolean. + /// + /// + _ASYNCRTIMP explicit value(const utility::char_t* value, bool has_escape_chars); + + /// + /// Copy constructor + /// + _ASYNCRTIMP value(const value &); + + /// + /// Move constructor + /// + _ASYNCRTIMP value(value &&) CPPREST_NOEXCEPT ; + + /// + /// Assignment operator. + /// + /// The JSON value object that contains the result of the assignment. + _ASYNCRTIMP value &operator=(const value &); + + /// + /// Move assignment operator. + /// + /// The JSON value object that contains the result of the assignment. + _ASYNCRTIMP value &operator=(value &&) CPPREST_NOEXCEPT ; + + // Static factories + + /// + /// Creates a null value + /// + /// A JSON null value + static _ASYNCRTIMP value __cdecl null(); + + /// + /// Creates a number value + /// + /// The C++ value to create a JSON value from + /// A JSON number value + static _ASYNCRTIMP value __cdecl number(double value); + + /// + /// Creates a number value + /// + /// The C++ value to create a JSON value from + /// A JSON number value + static _ASYNCRTIMP value __cdecl number(int32_t value); + + /// + /// Creates a number value + /// + /// The C++ value to create a JSON value from + /// A JSON number value + static _ASYNCRTIMP value __cdecl number(uint32_t value); + + /// + /// Creates a number value + /// + /// The C++ value to create a JSON value from + /// A JSON number value + static _ASYNCRTIMP value __cdecl number(int64_t value); + + /// + /// Creates a number value + /// + /// The C++ value to create a JSON value from + /// A JSON number value + static _ASYNCRTIMP value __cdecl number(uint64_t value); + + /// + /// Creates a Boolean value + /// + /// The C++ value to create a JSON value from + /// A JSON Boolean value + static _ASYNCRTIMP value __cdecl boolean(bool value); + + /// + /// Creates a string value + /// + /// The C++ value to create a JSON value from + /// A JSON string value + /// + /// This overload has O(n) performance because it tries to determine if + /// specified string has characters that should be properly escaped in JSON. + /// + static _ASYNCRTIMP value __cdecl string(utility::string_t value); + + /// + /// Creates a string value specifying if the string contains characters to escape + /// + /// The C++ value to create a JSON value from + /// Whether contains characters + /// that should be escaped in JSON value + /// A JSON string value + /// + /// This overload has O(1) performance. + /// + static _ASYNCRTIMP value __cdecl string(utility::string_t value, bool has_escape_chars); + +#ifdef _WIN32 +private: + // Only used internally by JSON parser. + static _ASYNCRTIMP value __cdecl string(const std::string &value); +public: +#endif + + /// + /// Creates an object value + /// + /// Whether to preserve the original order of the fields + /// An empty JSON object value + static _ASYNCRTIMP json::value __cdecl object(bool keep_order = false); + + /// + /// Creates an object value from a collection of field/values + /// + /// Field names associated with JSON values + /// Whether to preserve the original order of the fields + /// A non-empty JSON object value + static _ASYNCRTIMP json::value __cdecl object(std::vector> fields, bool keep_order = false); + + /// + /// Creates an empty JSON array + /// + /// An empty JSON array value + static _ASYNCRTIMP json::value __cdecl array(); + + /// + /// Creates a JSON array + /// + /// The initial number of elements of the JSON value + /// A JSON array value + static _ASYNCRTIMP json::value __cdecl array(size_t size); + + /// + /// Creates a JSON array + /// + /// A vector of JSON values + /// A JSON array value + static _ASYNCRTIMP json::value __cdecl array(std::vector elements); + + /// + /// Accesses the type of JSON value the current value instance is + /// + /// The value's type + _ASYNCRTIMP json::value::value_type type() const; + + /// + /// Is the current value a null value? + /// + /// true if the value is a null value, false otherwise + bool is_null() const { return type() == Null; }; + + /// + /// Is the current value a number value? + /// + /// true if the value is a number value, false otherwise + bool is_number() const { return type() == Number; } + + /// + /// Is the current value represented as an integer number value? + /// + /// + /// Note that if a json value is a number but represented as a double it can still + /// be retrieved as a integer using as_integer(), however the value will be truncated. + /// + /// true if the value is an integer value, false otherwise. + _ASYNCRTIMP bool is_integer() const; + + /// + /// Is the current value represented as an double number value? + /// + /// + /// Note that if a json value is a number but represented as a int it can still + /// be retrieved as a double using as_double(). + /// + /// true if the value is an double value, false otherwise. + _ASYNCRTIMP bool is_double() const; + + /// + /// Is the current value a Boolean value? + /// + /// true if the value is a Boolean value, false otherwise + bool is_boolean() const { return type() == Boolean; } + + /// + /// Is the current value a string value? + /// + /// true if the value is a string value, false otherwise + bool is_string() const { return type() == String; } + + /// + /// Is the current value an array? + /// + /// true if the value is an array, false otherwise + bool is_array() const { return type() == Array; } + + /// + /// Is the current value an object? + /// + /// true if the value is an object, false otherwise + bool is_object() const { return type() == Object; } + + /// + /// Gets the number of children of the value. + /// + /// The number of children. 0 for all non-composites. + size_t size() const; + + /// + /// Parses a string and construct a JSON value. + /// + /// The C++ value to create a JSON value from, a C++ STL double-byte string + _ASYNCRTIMP static value __cdecl parse(const utility::string_t &value); + + /// + /// Attempts to parse a string and construct a JSON value. + /// + /// The C++ value to create a JSON value from, a C++ STL double-byte string + /// If parsing fails, the error code is greater than 0 + /// The parsed object. Returns web::json::value::null if failed + _ASYNCRTIMP static value __cdecl parse(const utility::string_t &value, std::error_code &errorCode); + + /// + /// Serializes the current JSON value to a C++ string. + /// + /// A string representation of the value + _ASYNCRTIMP utility::string_t serialize() const; + + /// + /// Serializes the current JSON value to a C++ string. + /// + /// A string representation of the value + CASABLANCA_DEPRECATED("This API is deprecated and has been renamed to avoid confusion with as_string(), use ::web::json::value::serialize() instead.") + _ASYNCRTIMP utility::string_t to_string() const; + + /// + /// Parses a JSON value from the contents of an input stream using the native platform character width. + /// + /// The stream to read the JSON value from + /// The JSON value object created from the input stream. + _ASYNCRTIMP static value __cdecl parse(utility::istream_t &input); + + /// + /// Parses a JSON value from the contents of an input stream using the native platform character width. + /// + /// The stream to read the JSON value from + /// If parsing fails, the error code is greater than 0 + /// The parsed object. Returns web::json::value::null if failed + _ASYNCRTIMP static value __cdecl parse(utility::istream_t &input, std::error_code &errorCode); + + /// + /// Writes the current JSON value to a stream with the native platform character width. + /// + /// The stream that the JSON string representation should be written to. + _ASYNCRTIMP void serialize(utility::ostream_t &stream) const; + +#ifdef _WIN32 + /// + /// Parses a JSON value from the contents of a single-byte (UTF8) stream. + /// + /// The stream to read the JSON value from + _ASYNCRTIMP static value __cdecl parse(std::istream& stream); + + /// + /// Parses a JSON value from the contents of a single-byte (UTF8) stream. + /// + /// The stream to read the JSON value from + /// If parsing fails, the error code is greater than 0 + /// The parsed object. Returns web::json::value::null if failed + _ASYNCRTIMP static value __cdecl parse(std::istream& stream, std::error_code& error); + + /// + /// Serializes the content of the value into a single-byte (UTF8) stream. + /// + /// The stream that the JSON string representation should be written to. + _ASYNCRTIMP void serialize(std::ostream& stream) const; +#endif + + /// + /// Converts the JSON value to a C++ double, if and only if it is a number value. + /// Throws if the value is not a number + /// + /// A double representation of the value + _ASYNCRTIMP double as_double() const; + + /// + /// Converts the JSON value to a C++ integer, if and only if it is a number value. + /// Throws if the value is not a number + /// + /// An integer representation of the value + _ASYNCRTIMP int as_integer() const; + + /// + /// Converts the JSON value to a number class, if and only if it is a number value. + /// Throws if the value is not a number + /// + /// An instance of number class + _ASYNCRTIMP const json::number& as_number() const; + + /// + /// Converts the JSON value to a C++ bool, if and only if it is a Boolean value. + /// + /// A C++ bool representation of the value + _ASYNCRTIMP bool as_bool() const; + + /// + /// Converts the JSON value to a json array, if and only if it is an array value. + /// + /// The returned json::array should have the same or shorter lifetime as this + /// An array representation of the value + _ASYNCRTIMP json::array& as_array(); + + /// + /// Converts the JSON value to a json array, if and only if it is an array value. + /// + /// The returned json::array should have the same or shorter lifetime as this + /// An array representation of the value + _ASYNCRTIMP const json::array& as_array() const; + + /// + /// Converts the JSON value to a json object, if and only if it is an object value. + /// + /// An object representation of the value + _ASYNCRTIMP json::object& as_object(); + + /// + /// Converts the JSON value to a json object, if and only if it is an object value. + /// + /// An object representation of the value + _ASYNCRTIMP const json::object& as_object() const; + + /// + /// Converts the JSON value to a C++ STL string, if and only if it is a string value. + /// + /// A C++ STL string representation of the value + _ASYNCRTIMP const utility::string_t& as_string() const; + + /// + /// Compares two JSON values for equality. + /// + /// The JSON value to compare with. + /// True iff the values are equal. + _ASYNCRTIMP bool operator==(const value& other) const; + + /// + /// Compares two JSON values for inequality. + /// + /// The JSON value to compare with. + /// True iff the values are unequal. + bool operator!=(const value& other) const + { + return !((*this) == other); + } + + /// + /// Tests for the presence of a field. + /// + /// The name of the field + /// True if the field exists, false otherwise. + bool has_field(const utility::string_t &key) const; + + /// + /// Accesses a field of a JSON object. + /// + /// The name of the field + /// The value kept in the field; null if the field does not exist + CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release, use json::value::at() instead.") + value get(const utility::string_t &key) const; + + /// + /// Erases an element of a JSON array. Throws if index is out of bounds. + /// + /// The index of the element to erase in the JSON array. + _ASYNCRTIMP void erase(size_t index); + + /// + /// Erases an element of a JSON object. Throws if the key doesn't exist. + /// + /// The key of the element to erase in the JSON object. + _ASYNCRTIMP void erase(const utility::string_t &key); + + /// + /// Accesses an element of a JSON array. Throws when index out of bounds. + /// + /// The index of an element in the JSON array. + /// A reference to the value. + _ASYNCRTIMP json::value& at(size_t index); + + /// + /// Accesses an element of a JSON array. Throws when index out of bounds. + /// + /// The index of an element in the JSON array. + /// A reference to the value. + _ASYNCRTIMP const json::value& at(size_t index) const; + + /// + /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. + /// + /// The key of an element in the JSON object. + /// If the key exists, a reference to the value. + _ASYNCRTIMP json::value& at(const utility::string_t& key); + + /// + /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. + /// + /// The key of an element in the JSON object. + /// If the key exists, a reference to the value. + _ASYNCRTIMP const json::value& at(const utility::string_t& key) const; + + /// + /// Accesses a field of a JSON object. + /// + /// The name of the field + /// A reference to the value kept in the field. + _ASYNCRTIMP value & operator [] (const utility::string_t &key); + +#ifdef _WIN32 +private: + // Only used internally by JSON parser + _ASYNCRTIMP value & operator [] (const std::string &key) + { + // JSON object stores its field map as a unordered_map of string_t, so this conversion is hard to avoid + return operator[](utility::conversions::to_string_t(key)); + } +public: +#endif + + /// + /// Accesses an element of a JSON array. + /// + /// The index of an element in the JSON array + /// The value kept at the array index; null if outside the boundaries of the array + CASABLANCA_DEPRECATED("This API is deprecated and will be removed in a future release, use json::value::at() instead.") + value get(size_t index) const; + + /// + /// Accesses an element of a JSON array. + /// + /// The index of an element in the JSON array. + /// A reference to the value kept in the field. + _ASYNCRTIMP value & operator [] (size_t index); + + private: + friend class web::json::details::_Object; + friend class web::json::details::_Array; + template friend class web::json::details::JSON_Parser; + +#ifdef _WIN32 + /// + /// Writes the current JSON value as a double-byte string to a string instance. + /// + /// The string that the JSON representation should be written to. + _ASYNCRTIMP void format(std::basic_string &string) const; +#endif + /// + /// Serializes the content of the value into a string instance in UTF8 format + /// + /// The string that the JSON representation should be written to + _ASYNCRTIMP void format(std::basic_string& string) const; + +#ifdef ENABLE_JSON_VALUE_VISUALIZER + explicit value(std::unique_ptr v, value_type kind) : m_value(std::move(v)), m_kind(kind) +#else + explicit value(std::unique_ptr v) : m_value(std::move(v)) +#endif + {} + + std::unique_ptr m_value; +#ifdef ENABLE_JSON_VALUE_VISUALIZER + value_type m_kind; +#endif + }; + + /// + /// A single exception type to represent errors in parsing, converting, and accessing + /// elements of JSON values. + /// + class json_exception : public std::exception + { + private: + std::string _message; + public: + json_exception(const utility::char_t * const &message) : _message(utility::conversions::to_utf8string(message)) { } + + // Must be narrow string because it derives from std::exception + const char* what() const CPPREST_NOEXCEPT + { + return _message.c_str(); + } + }; + + namespace details + { + enum json_error + { + left_over_character_in_stream = 1, + malformed_array_literal, + malformed_comment, + malformed_literal, + malformed_object_literal, + malformed_numeric_literal, + malformed_string_literal, + malformed_token, + mismatched_brances, + nesting, + unexpected_token + }; + + class json_error_category_impl : public std::error_category + { + public: + virtual const char* name() const CPPREST_NOEXCEPT override + { + return "json"; + } + + virtual std::string message(int ev) const override + { + switch (ev) + { + case json_error::left_over_character_in_stream: + return "Left-over characters in stream after parsing a JSON value"; + case json_error::malformed_array_literal: + return "Malformed array literal"; + case json_error::malformed_comment: + return "Malformed comment"; + case json_error::malformed_literal: + return "Malformed literal"; + case json_error::malformed_object_literal: + return "Malformed object literal"; + case json_error::malformed_numeric_literal: + return "Malformed numeric literal"; + case json_error::malformed_string_literal: + return "Malformed string literal"; + case json_error::malformed_token: + return "Malformed token"; + case json_error::mismatched_brances: + return "Mismatched braces"; + case json_error::nesting: + return "Nesting too deep"; + case json_error::unexpected_token: + return "Unexpected token"; + default: + return "Unknown json error"; + } + } + }; + + const json_error_category_impl& json_error_category(); + } + + /// + /// A JSON array represented as a C++ class. + /// + class array + { + typedef std::vector storage_type; + + public: + typedef storage_type::iterator iterator; + typedef storage_type::const_iterator const_iterator; + typedef storage_type::reverse_iterator reverse_iterator; + typedef storage_type::const_reverse_iterator const_reverse_iterator; + typedef storage_type::size_type size_type; + + private: + array() : m_elements() { } + array(size_type size) : m_elements(size) { } + array(storage_type elements) : m_elements(std::move(elements)) { } + + public: + /// + /// Gets the beginning iterator element of the array + /// + /// An iterator to the beginning of the JSON array. + iterator begin() + { + return m_elements.begin(); + } + + /// + /// Gets the beginning const iterator element of the array. + /// + /// A const_iterator to the beginning of the JSON array. + const_iterator begin() const + { + return m_elements.cbegin(); + } + + /// + /// Gets the end iterator element of the array + /// + /// An iterator to the end of the JSON array. + iterator end() + { + return m_elements.end(); + } + + /// + /// Gets the end const iterator element of the array. + /// + /// A const_iterator to the end of the JSON array. + const_iterator end() const + { + return m_elements.cend(); + } + + /// + /// Gets the beginning reverse iterator element of the array + /// + /// An reverse_iterator to the beginning of the JSON array. + reverse_iterator rbegin() + { + return m_elements.rbegin(); + } + + /// + /// Gets the beginning const reverse iterator element of the array + /// + /// An const_reverse_iterator to the beginning of the JSON array. + const_reverse_iterator rbegin() const + { + return m_elements.rbegin(); + } + + /// + /// Gets the end reverse iterator element of the array + /// + /// An reverse_iterator to the end of the JSON array. + reverse_iterator rend() + { + return m_elements.rend(); + } + + /// + /// Gets the end const reverse iterator element of the array + /// + /// An const_reverse_iterator to the end of the JSON array. + const_reverse_iterator rend() const + { + return m_elements.crend(); + } + + /// + /// Gets the beginning const iterator element of the array. + /// + /// A const_iterator to the beginning of the JSON array. + const_iterator cbegin() const + { + return m_elements.cbegin(); + } + + /// + /// Gets the end const iterator element of the array. + /// + /// A const_iterator to the end of the JSON array. + const_iterator cend() const + { + return m_elements.cend(); + } + + /// + /// Gets the beginning const reverse iterator element of the array. + /// + /// A const_reverse_iterator to the beginning of the JSON array. + const_reverse_iterator crbegin() const + { + return m_elements.crbegin(); + } + + /// + /// Gets the end const reverse iterator element of the array. + /// + /// A const_reverse_iterator to the end of the JSON array. + const_reverse_iterator crend() const + { + return m_elements.crend(); + } + + /// + /// Deletes an element of the JSON array. + /// + /// A const_iterator to the element to delete. + /// Iterator to the new location of the element following the erased element. + /// GCC doesn't support erase with const_iterator on vector yet. In the future this should be changed. + iterator erase(iterator position) + { + return m_elements.erase(position); + } + + /// + /// Deletes the element at an index of the JSON array. + /// + /// The index of the element to delete. + void erase(size_type index) + { + if (index >= m_elements.size()) + { + throw json_exception(_XPLATSTR("index out of bounds")); + } + m_elements.erase(m_elements.begin() + index); + } + + /// + /// Accesses an element of a JSON array. Throws when index out of bounds. + /// + /// The index of an element in the JSON array. + /// A reference to the value kept in the field. + json::value& at(size_type index) + { + if (index >= m_elements.size()) + throw json_exception(_XPLATSTR("index out of bounds")); + + return m_elements[index]; + } + + /// + /// Accesses an element of a JSON array. Throws when index out of bounds. + /// + /// The index of an element in the JSON array. + /// A reference to the value kept in the field. + const json::value& at(size_type index) const + { + if (index >= m_elements.size()) + throw json_exception(_XPLATSTR("index out of bounds")); + + return m_elements[index]; + } + + /// + /// Accesses an element of a JSON array. + /// + /// The index of an element in the JSON array. + /// A reference to the value kept in the field. + json::value& operator[](size_type index) + { + msl::safeint3::SafeInt nMinSize(index); + nMinSize += 1; + msl::safeint3::SafeInt nlastSize(m_elements.size()); + if (nlastSize < nMinSize) + m_elements.resize(nMinSize); + + return m_elements[index]; + } + + /// + /// Gets the number of elements of the array. + /// + /// The number of elements. + size_type size() const + { + return m_elements.size(); + } + + private: + storage_type m_elements; + + friend class details::_Array; + template friend class json::details::JSON_Parser; + }; + + /// + /// A JSON object represented as a C++ class. + /// + class object + { + typedef std::vector> storage_type; + + public: + typedef storage_type::iterator iterator; + typedef storage_type::const_iterator const_iterator; + typedef storage_type::reverse_iterator reverse_iterator; + typedef storage_type::const_reverse_iterator const_reverse_iterator; + typedef storage_type::size_type size_type; + + private: + object(bool keep_order = false) : m_elements(), m_keep_order(keep_order) { } + object(storage_type elements, bool keep_order = false) : m_elements(std::move(elements)), m_keep_order(keep_order) + { + if (!keep_order) { + sort(m_elements.begin(), m_elements.end(), compare_pairs); + } + } + + public: + /// + /// Gets the beginning iterator element of the object + /// + /// An iterator to the beginning of the JSON object. + iterator begin() + { + return m_elements.begin(); + } + + /// + /// Gets the beginning const iterator element of the object. + /// + /// A const_iterator to the beginning of the JSON object. + const_iterator begin() const + { + return m_elements.cbegin(); + } + + /// + /// Gets the end iterator element of the object + /// + /// An iterator to the end of the JSON object. + iterator end() + { + return m_elements.end(); + } + + /// + /// Gets the end const iterator element of the object. + /// + /// A const_iterator to the end of the JSON object. + const_iterator end() const + { + return m_elements.cend(); + } + + /// + /// Gets the beginning reverse iterator element of the object + /// + /// An reverse_iterator to the beginning of the JSON object. + reverse_iterator rbegin() + { + return m_elements.rbegin(); + } + + /// + /// Gets the beginning const reverse iterator element of the object + /// + /// An const_reverse_iterator to the beginning of the JSON object. + const_reverse_iterator rbegin() const + { + return m_elements.rbegin(); + } + + /// + /// Gets the end reverse iterator element of the object + /// + /// An reverse_iterator to the end of the JSON object. + reverse_iterator rend() + { + return m_elements.rend(); + } + + /// + /// Gets the end const reverse iterator element of the object + /// + /// An const_reverse_iterator to the end of the JSON object. + const_reverse_iterator rend() const + { + return m_elements.crend(); + } + + /// + /// Gets the beginning const iterator element of the object. + /// + /// A const_iterator to the beginning of the JSON object. + const_iterator cbegin() const + { + return m_elements.cbegin(); + } + + /// + /// Gets the end const iterator element of the object. + /// + /// A const_iterator to the end of the JSON object. + const_iterator cend() const + { + return m_elements.cend(); + } + + /// + /// Gets the beginning const reverse iterator element of the object. + /// + /// A const_reverse_iterator to the beginning of the JSON object. + const_reverse_iterator crbegin() const + { + return m_elements.crbegin(); + } + + /// + /// Gets the end const reverse iterator element of the object. + /// + /// A const_reverse_iterator to the end of the JSON object. + const_reverse_iterator crend() const + { + return m_elements.crend(); + } + + /// + /// Deletes an element of the JSON object. + /// + /// A const_iterator to the element to delete. + /// Iterator to the new location of the element following the erased element. + /// GCC doesn't support erase with const_iterator on vector yet. In the future this should be changed. + iterator erase(iterator position) + { + return m_elements.erase(position); + } + + /// + /// Deletes an element of the JSON object. If the key doesn't exist, this method throws. + /// + /// The key of an element in the JSON object. + void erase(const utility::string_t &key) + { + auto iter = find_by_key(key); + if (iter == m_elements.end()) + { + throw web::json::json_exception(_XPLATSTR("Key not found")); + } + + m_elements.erase(iter); + } + + /// + /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. + /// + /// The key of an element in the JSON object. + /// If the key exists, a reference to the value kept in the field. + json::value& at(const utility::string_t& key) + { + auto iter = find_by_key(key); + if (iter == m_elements.end()) + { + throw web::json::json_exception(_XPLATSTR("Key not found")); + } + + return iter->second; + } + + /// + /// Accesses an element of a JSON object. If the key doesn't exist, this method throws. + /// + /// The key of an element in the JSON object. + /// If the key exists, a reference to the value kept in the field. + const json::value& at(const utility::string_t& key) const + { + auto iter = find_by_key(key); + if (iter == m_elements.end()) + { + throw web::json::json_exception(_XPLATSTR("Key not found")); + } + + return iter->second; + } + + /// + /// Accesses an element of a JSON object. + /// + /// The key of an element in the JSON object. + /// If the key exists, a reference to the value kept in the field, otherwise a newly created null value that will be stored for the given key. + json::value& operator[](const utility::string_t& key) + { + auto iter = find_insert_location(key); + + if (iter == m_elements.end() || key != iter->first) + { + return m_elements.insert(iter, std::pair(key, value()))->second; + } + + return iter->second; + } + + /// + /// Gets an iterator to an element of a JSON object. + /// + /// The key of an element in the JSON object. + /// A const iterator to the value kept in the field. + const_iterator find(const utility::string_t& key) const + { + return find_by_key(key); + } + + /// + /// Gets the number of elements of the object. + /// + /// The number of elements. + size_type size() const + { + return m_elements.size(); + } + + /// + /// Checks if there are any elements in the JSON object. + /// + /// True iff empty. + bool empty() const + { + return m_elements.empty(); + } + private: + + static bool compare_pairs(const std::pair& p1, const std::pair& p2) + { + return p1.first < p2.first; + } + static bool compare_with_key(const std::pair& p1, const utility::string_t& key) + { + return p1.first < key; + } + + storage_type::iterator find_insert_location(const utility::string_t &key) + { + if (m_keep_order) + { + return std::find_if(m_elements.begin(), m_elements.end(), + [&key](const std::pair& p) { + return p.first == key; + }); + } + else + { + return std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key); + } + } + + storage_type::const_iterator find_by_key(const utility::string_t& key) const + { + if (m_keep_order) + { + return std::find_if(m_elements.begin(), m_elements.end(), + [&key](const std::pair& p) { + return p.first == key; + }); + } + else + { + auto iter = std::lower_bound(m_elements.begin(), m_elements.end(), key, compare_with_key); + if (iter != m_elements.end() && key != iter->first) + { + return m_elements.end(); + } + return iter; + } + } + + storage_type::iterator find_by_key(const utility::string_t& key) + { + auto iter = find_insert_location(key); + if (iter != m_elements.end() && key != iter->first) + { + return m_elements.end(); + } + return iter; + } + + storage_type m_elements; + bool m_keep_order; + friend class details::_Object; + + template friend class json::details::JSON_Parser; + }; + + /// + /// A JSON number represented as a C++ class. + /// + class number + { + // Note that these constructors make sure that only negative integers are stored as signed int64 (while others convert to unsigned int64). + // This helps handling number objects e.g. comparing two numbers. + + number(double value) : m_value(value), m_type(double_type) { } + number(int32_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) { } + number(uint32_t value) : m_intval(value), m_type(unsigned_type) { } + number(int64_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) { } + number(uint64_t value) : m_uintval(value), m_type(unsigned_type) { } + + public: + + /// + /// Does the number fit into int32? + /// + /// true if the number fits into int32, false otherwise + _ASYNCRTIMP bool is_int32() const; + + /// + /// Does the number fit into unsigned int32? + /// + /// true if the number fits into unsigned int32, false otherwise + _ASYNCRTIMP bool is_uint32() const; + + /// + /// Does the number fit into int64? + /// + /// true if the number fits into int64, false otherwise + _ASYNCRTIMP bool is_int64() const; + + /// + /// Does the number fit into unsigned int64? + /// + /// true if the number fits into unsigned int64, false otherwise + bool is_uint64() const + { + switch (m_type) + { + case signed_type : return m_intval >= 0; + case unsigned_type : return true; + case double_type : + default : + return false; + } + } + + /// + /// Converts the JSON number to a C++ double. + /// + /// A double representation of the number + double to_double() const + { + switch (m_type) + { + case double_type : return m_value; + case signed_type : return static_cast(m_intval); + case unsigned_type : return static_cast(m_uintval); + default : return false; + } + } + + /// + /// Converts the JSON number to int32. + /// + /// An int32 representation of the number + int32_t to_int32() const + { + if (m_type == double_type) + return static_cast(m_value); + else + return static_cast(m_intval); + } + + /// + /// Converts the JSON number to unsigned int32. + /// + /// An usigned int32 representation of the number + uint32_t to_uint32() const + { + if (m_type == double_type) + return static_cast(m_value); + else + return static_cast(m_intval); + } + + /// + /// Converts the JSON number to int64. + /// + /// An int64 representation of the number + int64_t to_int64() const + { + if (m_type == double_type) + return static_cast(m_value); + else + return static_cast(m_intval); + } + + /// + /// Converts the JSON number to unsigned int64. + /// + /// An unsigned int64 representation of the number + uint64_t to_uint64() const + { + if (m_type == double_type) + return static_cast(m_value); + else + return static_cast(m_intval); + } + + /// + /// Is the number represented internally as an integral type? + /// + /// true if the number is represented as an integral type, false otherwise + bool is_integral() const + { + return m_type != double_type; + } + + /// + /// Compares two JSON numbers for equality. + /// + /// The JSON number to compare with. + /// True iff the numbers are equal. + bool operator==(const number &other) const + { + if (m_type != other.m_type) + return false; + + switch (m_type) + { + case json::number::type::signed_type : + return m_intval == other.m_intval; + case json::number::type::unsigned_type : + return m_uintval == other.m_uintval; + case json::number::type::double_type : + return m_value == other.m_value; + } + __assume(0); + } + + private: + union + { + int64_t m_intval; + uint64_t m_uintval; + double m_value; + }; + + enum type + { + signed_type=0, unsigned_type, double_type + } m_type; + + friend class details::_Number; + }; + + namespace details + { + class _Value + { + public: + virtual std::unique_ptr<_Value> _copy_value() = 0; + + virtual bool has_field(const utility::string_t &) const { return false; } + virtual value get_field(const utility::string_t &) const { throw json_exception(_XPLATSTR("not an object")); } + virtual value get_element(array::size_type) const { throw json_exception(_XPLATSTR("not an array")); } + + virtual value &index(const utility::string_t &) { throw json_exception(_XPLATSTR("not an object")); } + virtual value &index(array::size_type) { throw json_exception(_XPLATSTR("not an array")); } + + virtual const value &cnst_index(const utility::string_t &) const { throw json_exception(_XPLATSTR("not an object")); } + virtual const value &cnst_index(array::size_type) const { throw json_exception(_XPLATSTR("not an array")); } + + // Common function used for serialization to strings and streams. + virtual void serialize_impl(std::string& str) const + { + format(str); + } +#ifdef _WIN32 + virtual void serialize_impl(std::wstring& str) const + { + format(str); + } +#endif + + virtual utility::string_t to_string() const + { + utility::string_t str; + serialize_impl(str); + return str; + } + + virtual json::value::value_type type() const { return json::value::Null; } + + virtual bool is_integer() const { throw json_exception(_XPLATSTR("not a number")); } + virtual bool is_double() const { throw json_exception(_XPLATSTR("not a number")); } + + virtual const json::number& as_number() { throw json_exception(_XPLATSTR("not a number")); } + virtual double as_double() const { throw json_exception(_XPLATSTR("not a number")); } + virtual int as_integer() const { throw json_exception(_XPLATSTR("not a number")); } + virtual bool as_bool() const { throw json_exception(_XPLATSTR("not a boolean")); } + virtual json::array& as_array() { throw json_exception(_XPLATSTR("not an array")); } + virtual const json::array& as_array() const { throw json_exception(_XPLATSTR("not an array")); } + virtual json::object& as_object() { throw json_exception(_XPLATSTR("not an object")); } + virtual const json::object& as_object() const { throw json_exception(_XPLATSTR("not an object")); } + virtual const utility::string_t& as_string() const { throw json_exception(_XPLATSTR("not a string")); } + + virtual size_t size() const { return 0; } + + virtual ~_Value() {} + + protected: + _Value() {} + + virtual void format(std::basic_string& stream) const + { + stream.append("null"); + } +#ifdef _WIN32 + virtual void format(std::basic_string& stream) const + { + stream.append(L"null"); + } +#endif + private: + + friend class web::json::value; + }; + + class _Null : public _Value + { + public: + virtual std::unique_ptr<_Value> _copy_value() + { + return utility::details::make_unique<_Null>(); + } + virtual json::value::value_type type() const { return json::value::Null; } + }; + + class _Number : public _Value + { + public: + _Number(double value) : m_number(value) { } + _Number(int32_t value) : m_number(value) { } + _Number(uint32_t value) : m_number(value) { } + _Number(int64_t value) : m_number(value) { } + _Number(uint64_t value) : m_number(value) { } + + virtual std::unique_ptr<_Value> _copy_value() + { + return utility::details::make_unique<_Number>(*this); + } + + virtual json::value::value_type type() const { return json::value::Number; } + + virtual bool is_integer() const { return m_number.is_integral(); } + virtual bool is_double() const { return !m_number.is_integral(); } + + virtual double as_double() const + { + return m_number.to_double(); + } + + virtual int as_integer() const + { + return m_number.to_int32(); + } + + virtual const number& as_number() { return m_number; } + + protected: + virtual void format(std::basic_string& stream) const ; +#ifdef _WIN32 + virtual void format(std::basic_string& stream) const; +#endif + private: + template friend class json::details::JSON_Parser; + + json::number m_number; + }; + + class _Boolean : public _Value + { + public: + _Boolean(bool value) : m_value(value) { } + + virtual std::unique_ptr<_Value> _copy_value() + { + return utility::details::make_unique<_Boolean>(*this); + } + + virtual json::value::value_type type() const { return json::value::Boolean; } + + virtual bool as_bool() const { return m_value; } + + protected: + virtual void format(std::basic_string& stream) const + { + stream.append(m_value ? "true" : "false"); + } + +#ifdef _WIN32 + virtual void format(std::basic_string& stream) const + { + stream.append(m_value ? L"true" : L"false"); + } +#endif + private: + template friend class json::details::JSON_Parser; + bool m_value; + }; + + class _String : public _Value + { + public: + + _String(utility::string_t value) : m_string(std::move(value)) + { + m_has_escape_char = has_escape_chars(*this); + } + _String(utility::string_t value, bool escaped_chars) + : m_string(std::move(value)), + m_has_escape_char(escaped_chars) + { } + +#ifdef _WIN32 + _String(std::string &&value) : m_string(utility::conversions::to_utf16string(std::move(value))) + { + m_has_escape_char = has_escape_chars(*this); + } + _String(std::string &&value, bool escape_chars) + : m_string(utility::conversions::to_utf16string(std::move(value))), + m_has_escape_char(escape_chars) + { } +#endif + + virtual std::unique_ptr<_Value> _copy_value() + { + return utility::details::make_unique<_String>(*this); + } + + virtual json::value::value_type type() const { return json::value::String; } + + virtual const utility::string_t & as_string() const; + + virtual void serialize_impl(std::string& str) const + { + serialize_impl_char_type(str); + } +#ifdef _WIN32 + virtual void serialize_impl(std::wstring& str) const + { + serialize_impl_char_type(str); + } +#endif + + protected: + virtual void format(std::basic_string& str) const; +#ifdef _WIN32 + virtual void format(std::basic_string& str) const; +#endif + + private: + friend class _Object; + friend class _Array; + + size_t get_reserve_size() const + { + return m_string.size() + 2; + } + + template + void serialize_impl_char_type(std::basic_string& str) const + { + // To avoid repeated allocations reserve some space all up front. + // size of string + 2 for quotes + str.reserve(get_reserve_size()); + format(str); + } + + std::string as_utf8_string() const; + utf16string as_utf16_string() const; + + utility::string_t m_string; + + // There are significant performance gains that can be made by knowning whether + // or not a character that requires escaping is present. + bool m_has_escape_char; + static bool has_escape_chars(const _String &str); + }; + + template + _ASYNCRTIMP void append_escape_string(std::basic_string& str, const std::basic_string& escaped); + + void format_string(const utility::string_t& key, utility::string_t& str); + +#ifdef _WIN32 + void format_string(const utility::string_t& key, std::string& str); +#endif + + class _Object : public _Value + { + public: + + _Object(bool keep_order) : m_object(keep_order) { } + _Object(object::storage_type fields, bool keep_order) : m_object(std::move(fields), keep_order) { } + + virtual std::unique_ptr<_Value> _copy_value() + { + return utility::details::make_unique<_Object>(*this); + } + + virtual json::object& as_object() { return m_object; } + + virtual const json::object& as_object() const { return m_object; } + + virtual json::value::value_type type() const { return json::value::Object; } + + virtual bool has_field(const utility::string_t &) const; + + virtual json::value &index(const utility::string_t &key); + + bool is_equal(const _Object* other) const + { + if (m_object.size() != other->m_object.size()) + return false; + + return std::equal(std::begin(m_object), std::end(m_object), std::begin(other->m_object)); + } + + virtual void serialize_impl(std::string& str) const + { + // To avoid repeated allocations reserve some space all up front. + str.reserve(get_reserve_size()); + format(str); + } +#ifdef _WIN32 + virtual void serialize_impl(std::wstring& str) const + { + // To avoid repeated allocations reserve some space all up front. + str.reserve(get_reserve_size()); + format(str); + } +#endif + size_t size() const { return m_object.size(); } + + protected: + virtual void format(std::basic_string& str) const + { + format_impl(str); + } +#ifdef _WIN32 + virtual void format(std::basic_string& str) const + { + format_impl(str); + } +#endif + + private: + json::object m_object; + + template friend class json::details::JSON_Parser; + + template + void format_impl(std::basic_string& str) const + { + str.push_back('{'); + if(!m_object.empty()) + { + auto lastElement = m_object.end() - 1; + for (auto iter = m_object.begin(); iter != lastElement; ++iter) + { + format_string(iter->first, str); + str.push_back(':'); + iter->second.format(str); + str.push_back(','); + } + format_string(lastElement->first, str); + str.push_back(':'); + lastElement->second.format(str); + } + str.push_back('}'); + } + + size_t get_reserve_size() const + { + // This is a heuristic we can tune more in the future: + // Basically size of string plus + // sum size of value if an object, array, or string. + size_t reserveSize = 2; // For brackets {} + for(auto iter = m_object.begin(); iter != m_object.end(); ++iter) + { + reserveSize += iter->first.length() + 2; // 2 for quotes + size_t valueSize = iter->second.size() * 20; // Multipler by each object/array element + if(valueSize == 0) + { + if(iter->second.type() == json::value::String) + { + valueSize = static_cast<_String *>(iter->second.m_value.get())->get_reserve_size(); + } + else + { + valueSize = 5; // true, false, or null + } + } + reserveSize += valueSize; + } + return reserveSize; + } + }; + + class _Array : public _Value + { + public: + _Array() {} + _Array(array::size_type size) : m_array(size) {} + _Array(array::storage_type elements) : m_array(std::move(elements)) { } + + virtual std::unique_ptr<_Value> _copy_value() + { + return utility::details::make_unique<_Array>(*this); + } + + virtual json::value::value_type type() const { return json::value::Array; } + + virtual json::array& as_array() { return m_array; } + virtual const json::array& as_array() const { return m_array; } + + virtual json::value &index(json::array::size_type index) + { + return m_array[index]; + } + + bool is_equal(const _Array* other) const + { + if ( m_array.size() != other->m_array.size()) + return false; + + auto iterT = m_array.cbegin(); + auto iterO = other->m_array.cbegin(); + auto iterTe = m_array.cend(); + auto iterOe = other->m_array.cend(); + + for (; iterT != iterTe && iterO != iterOe; ++iterT, ++iterO) + { + if ( *iterT != *iterO ) + return false; + } + + return true; + } + + virtual void serialize_impl(std::string& str) const + { + // To avoid repeated allocations reserve some space all up front. + str.reserve(get_reserve_size()); + format(str); + } +#ifdef _WIN32 + virtual void serialize_impl(std::wstring& str) const + { + // To avoid repeated allocations reserve some space all up front. + str.reserve(get_reserve_size()); + format(str); + } +#endif + size_t size() const { return m_array.size(); } + + protected: + virtual void format(std::basic_string& str) const + { + format_impl(str); + } +#ifdef _WIN32 + virtual void format(std::basic_string& str) const + { + format_impl(str); + } +#endif + private: + json::array m_array; + + template friend class json::details::JSON_Parser; + + template + void format_impl(std::basic_string& str) const + { + str.push_back('['); + if(!m_array.m_elements.empty()) + { + auto lastElement = m_array.m_elements.end() - 1; + for (auto iter = m_array.m_elements.begin(); iter != lastElement; ++iter) + { + iter->format(str); + str.push_back(','); + } + lastElement->format(str); + } + str.push_back(']'); + } + + size_t get_reserve_size() const + { + // This is a heuristic we can tune more in the future: + // Basically sum size of each value if an object, array, or string by a multiplier. + size_t reserveSize = 2; // For brackets [] + for(auto iter = m_array.cbegin(); iter != m_array.cend(); ++iter) + { + size_t valueSize = iter->size() * 20; // Per each nested array/object + + if(valueSize == 0) + valueSize = 5; // true, false, or null + + reserveSize += valueSize; + } + return reserveSize; + } + }; + } // namespace details + + /// + /// Gets the number of children of the value. + /// + /// The number of children. 0 for all non-composites. + inline size_t json::value::size() const + { + return m_value->size(); + } + + /// + /// Test for the presence of a field. + /// + /// The name of the field + /// True if the field exists, false otherwise. + inline bool json::value::has_field(const utility::string_t& key) const + { + return m_value->has_field(key); + } + + /// + /// Access a field of a JSON object. + /// + /// The name of the field + /// The value kept in the field; null if the field does not exist + inline json::value json::value::get(const utility::string_t& key) const + { + return m_value->get_field(key); + } + + /// + /// Access an element of a JSON array. + /// + /// The index of an element in the JSON array + /// The value kept at the array index; null if outside the boundaries of the array + inline json::value json::value::get(size_t index) const + { + return m_value->get_element(index); + } + + /// + /// A standard std::ostream operator to facilitate writing JSON values to streams. + /// + /// The output stream to write the JSON value to. + /// The JSON value to be written to the stream. + /// The output stream object + _ASYNCRTIMP utility::ostream_t& __cdecl operator << (utility::ostream_t &os, const json::value &val); + + /// + /// A standard std::istream operator to facilitate reading JSON values from streams. + /// + /// The input stream to read the JSON value from. + /// The JSON value object read from the stream. + /// The input stream object. + _ASYNCRTIMP utility::istream_t& __cdecl operator >> (utility::istream_t &is, json::value &val); +}} + +#endif diff --git a/3rdparty/cpprestsdk/include/cpprest/oauth1.h b/3rdparty/cpprestsdk/include/cpprest/oauth1.h new file mode 100644 index 00000000..355c04fb --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/oauth1.h @@ -0,0 +1,577 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: Oauth 1.0 +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#ifndef _CASA_OAUTH1_H +#define _CASA_OAUTH1_H + +#include "cpprest/http_msg.h" + +namespace web +{ +namespace http +{ +namespace client +{ + // Forward declaration to avoid circular include dependency. + class http_client_config; +} + +/// oAuth 1.0 library. +namespace oauth1 +{ +namespace details +{ + +class oauth1_handler; + +// State currently used by oauth1_config to authenticate request. +// The state varies for every request (due to timestamp and nonce). +// The state also contains extra transmitted protocol parameters during +// authorization flow (i.e. 'oauth_callback' or 'oauth_verifier'). +class oauth1_state +{ +public: + oauth1_state(utility::string_t timestamp, utility::string_t nonce, + utility::string_t extra_key=utility::string_t(), + utility::string_t extra_value=utility::string_t()) : + m_timestamp(std::move(timestamp)), + m_nonce(std::move(nonce)), + m_extra_key(std::move(extra_key)), + m_extra_value(std::move(extra_value)) + {} + + const utility::string_t& timestamp() const { return m_timestamp; } + void set_timestamp(utility::string_t timestamp) { m_timestamp = std::move(timestamp); } + + const utility::string_t& nonce() const { return m_nonce; } + void set_nonce(utility::string_t nonce) { m_nonce = std::move(nonce); } + + const utility::string_t& extra_key() const { return m_extra_key; } + void set_extra_key(utility::string_t key) { m_extra_key = std::move(key); } + + const utility::string_t& extra_value() const { return m_extra_value; } + void set_extra_value(utility::string_t value) { m_extra_value = std::move(value); } + +private: + utility::string_t m_timestamp; + utility::string_t m_nonce; + utility::string_t m_extra_key; + utility::string_t m_extra_value; +}; + +// Constant strings for OAuth 1.0. +typedef utility::string_t oauth1_string; +class oauth1_strings +{ +public: +#define _OAUTH1_STRINGS +#define DAT(a_, b_) _ASYNCRTIMP static const oauth1_string a_; +#include "cpprest/details/http_constants.dat" +#undef _OAUTH1_STRINGS +#undef DAT +}; + +} // namespace web::http::oauth1::details + +/// oAuth functionality is currently in beta. +namespace experimental +{ + +/// +/// Constant strings for OAuth 1.0 signature methods. +/// +typedef utility::string_t oauth1_method; +class oauth1_methods +{ +public: +#define _OAUTH1_METHODS +#define DAT(a,b) _ASYNCRTIMP static const oauth1_method a; +#include "cpprest/details/http_constants.dat" +#undef _OAUTH1_METHODS +#undef DAT +}; + +/// +/// Exception type for OAuth 1.0 errors. +/// +class oauth1_exception : public std::exception +{ +public: + oauth1_exception(utility::string_t msg) : m_msg(utility::conversions::to_utf8string(std::move(msg))) {} + ~oauth1_exception() CPPREST_NOEXCEPT {} + const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); } + +private: + std::string m_msg; +}; + +/// +/// OAuth 1.0 token and associated information. +/// +class oauth1_token +{ +public: + + /// + /// Constructs a OAuth1 token from a given access token and secret. + /// + /// Access token string. + /// Token secret string. + oauth1_token(utility::string_t access_token, utility::string_t secret) : + m_token(std::move(access_token)), + m_secret(std::move(secret)) + {} + + /// + /// Get access token validity state. + /// If true, token is a valid access token. + /// + /// Access token validity state of the token. + bool is_valid_access_token() const { return !(access_token().empty() || secret().empty()); } + + /// + /// Get access token. + /// + /// The access token string. + const utility::string_t& access_token() const { return m_token; } + + /// + /// Set access token. + /// + /// Access token string to set. + void set_access_token(utility::string_t &&access_token) { m_token = std::move(access_token); } + + /// + /// Set access token. + /// + /// Access token string to set. + void set_access_token(const utility::string_t &access_token) { m_token = access_token; } + + /// + /// Get token secret. + /// + /// Token secret string. + const utility::string_t& secret() const { return m_secret; } + + /// + /// Set token secret. + /// + /// Token secret string to set. + void set_secret(utility::string_t &&secret) { m_secret = std::move(secret); } + + /// + /// Set token secret. + /// + /// Token secret string to set. + void set_secret(const utility::string_t &secret) { m_secret = secret; } + + /// + /// Retrieves any additional parameters. + /// + /// A map containing the additional parameters. + const std::map &additional_parameters() const { return m_additional_parameters; } + + /// + /// Sets a specific parameter additional parameter. + /// + /// Parameter name. + /// Parameter value. + void set_additional_parameter(utility::string_t &¶mName, utility::string_t &¶mValue) + { + m_additional_parameters[std::move(paramName)] = std::move(paramValue); + } + + /// + /// Sets a specific parameter additional parameter. + /// + /// Parameter name. + /// Parameter value. + void set_additional_parameter(const utility::string_t ¶mName, const utility::string_t ¶mValue) + { + m_additional_parameters[paramName] = paramValue; + } + + /// + /// Clears all additional parameters. + /// + void clear_additional_parameters() { m_additional_parameters.clear(); } + +private: + friend class oauth1_config; + + oauth1_token() {} + + utility::string_t m_token; + utility::string_t m_secret; + std::map m_additional_parameters; +}; + +/// +/// OAuth 1.0 configuration class. +/// +class oauth1_config +{ +public: + oauth1_config(utility::string_t consumer_key, utility::string_t consumer_secret, + utility::string_t temp_endpoint, utility::string_t auth_endpoint, + utility::string_t token_endpoint, utility::string_t callback_uri, + oauth1_method method, utility::string_t realm=utility::string_t()) : + m_consumer_key(std::move(consumer_key)), + m_consumer_secret(std::move(consumer_secret)), + m_temp_endpoint(std::move(temp_endpoint)), + m_auth_endpoint(std::move(auth_endpoint)), + m_token_endpoint(std::move(token_endpoint)), + m_callback_uri(std::move(callback_uri)), + m_realm(std::move(realm)), + m_method(std::move(method)), + m_is_authorization_completed(false) + {} + + /// + /// Builds an authorization URI to be loaded in a web browser/view. + /// The URI is built with auth_endpoint() as basis. + /// The method creates a task for HTTP request to first obtain a + /// temporary token. The authorization URI build based on this token. + /// + /// Authorization URI to be loaded in a web browser/view. + _ASYNCRTIMP pplx::task build_authorization_uri(); + + /// + /// Fetch an access token based on redirected URI. + /// The URI is expected to contain 'oauth_verifier' + /// parameter, which is then used to fetch an access token using the + /// token_from_verifier() method. + /// See: http://tools.ietf.org/html/rfc5849#section-2.2 + /// The received 'oauth_token' is parsed and verified to match the current token(). + /// When access token is successfully obtained, set_token() is called, and config is + /// ready for use by oauth1_handler. + /// + /// The URI where web browser/view was redirected after resource owner's authorization. + /// Task that fetches the access token based on redirected URI. + _ASYNCRTIMP pplx::task token_from_redirected_uri(const web::http::uri& redirected_uri); + + /// + /// Creates a task with HTTP request to fetch an access token from the token endpoint. + /// The request exchanges a verifier code to an access token. + /// If successful, the resulting token is set as active via set_token(). + /// See: http://tools.ietf.org/html/rfc5849#section-2.3 + /// + /// Verifier received via redirect upon successful authorization. + /// Task that fetches the access token based on the verifier. + pplx::task token_from_verifier(utility::string_t verifier) + { + return _request_token(_generate_auth_state(details::oauth1_strings::verifier, std::move(verifier)), false); + } + + /// + /// Creates a task with HTTP request to fetch an access token from the token endpoint. + /// If successful, the resulting token is set as active via set_token(). + /// + /// Task that fetches the access token based on the verifier. + pplx::task refresh_token(const utility::string_t &key) + { + return _request_token(_generate_auth_state(key, m_token.additional_parameters().at(key)), false); + } + + /// + /// Get consumer key used in authorization and authentication. + /// + /// Consumer key string. + const utility::string_t& consumer_key() const { return m_consumer_key; } + /// + /// Set consumer key used in authorization and authentication. + /// + /// Consumer key string to set. + void set_consumer_key(utility::string_t key) { m_consumer_key = std::move(key); } + + /// + /// Get consumer secret used in authorization and authentication. + /// + /// Consumer secret string. + const utility::string_t& consumer_secret() const { return m_consumer_secret; } + /// + /// Set consumer secret used in authorization and authentication. + /// + /// Consumer secret string to set. + void set_consumer_secret(utility::string_t secret) { m_consumer_secret = std::move(secret); } + + /// + /// Get temporary token endpoint URI string. + /// + /// Temporary token endpoint URI string. + const utility::string_t& temp_endpoint() const { return m_temp_endpoint; } + /// + /// Set temporary token endpoint URI string. + /// + /// Temporary token endpoint URI string to set. + void set_temp_endpoint(utility::string_t temp_endpoint) { m_temp_endpoint = std::move(temp_endpoint); } + + /// + /// Get authorization endpoint URI string. + /// + /// Authorization endpoint URI string. + const utility::string_t& auth_endpoint() const { return m_auth_endpoint; } + /// + /// Set authorization endpoint URI string. + /// + /// Authorization endpoint URI string to set. + void set_auth_endpoint(utility::string_t auth_endpoint) { m_auth_endpoint = std::move(auth_endpoint); } + + /// + /// Get token endpoint URI string. + /// + /// Token endpoint URI string. + const utility::string_t& token_endpoint() const { return m_token_endpoint; } + /// + /// Set token endpoint URI string. + /// + /// Token endpoint URI string to set. + void set_token_endpoint(utility::string_t token_endpoint) { m_token_endpoint = std::move(token_endpoint); } + + /// + /// Get callback URI string. + /// + /// Callback URI string. + const utility::string_t& callback_uri() const { return m_callback_uri; } + /// + /// Set callback URI string. + /// + /// Callback URI string to set. + void set_callback_uri(utility::string_t callback_uri) { m_callback_uri = std::move(callback_uri); } + + /// + /// Get token. + /// + /// Token. + const oauth1_token& token() const + { + if (m_is_authorization_completed) + { + // Return the token object only if authorization has been completed. + // Otherwise the token object holds a temporary token which should not be + // returned to the user. + return m_token; + } + else + { + static const oauth1_token empty_token; + return empty_token; + } + } + + /// + /// Set token. + /// + /// Token to set. + void set_token(oauth1_token token) + { + m_token = std::move(token); + m_is_authorization_completed = true; + } + + /// + /// Get signature method. + /// + /// Signature method. + const oauth1_method& method() const { return m_method; } + /// + /// Set signature method. + /// + /// Signature method. + void set_method(oauth1_method method) { m_method = std::move(method); } + + /// + /// Get authentication realm. + /// + /// Authentication realm string. + const utility::string_t& realm() const { return m_realm; } + /// + /// Set authentication realm. + /// + /// Authentication realm string to set. + void set_realm(utility::string_t realm) { m_realm = std::move(realm); } + + /// + /// Returns enabled state of the configuration. + /// The oauth1_handler will perform OAuth 1.0 authentication only if + /// this method returns true. + /// Return value is true if access token is valid (=fetched or manually set) + /// and both consumer_key() and consumer_secret() are set (=non-empty). + /// + /// The configuration enabled state. + bool is_enabled() const { return token().is_valid_access_token() && !(consumer_key().empty() || consumer_secret().empty()); } + + // Builds signature base string according to: + // http://tools.ietf.org/html/rfc5849#section-3.4.1.1 + _ASYNCRTIMP utility::string_t _build_signature_base_string(http_request request, details::oauth1_state state) const; + + // Builds HMAC-SHA1 signature according to: + // http://tools.ietf.org/html/rfc5849#section-3.4.2 + utility::string_t _build_hmac_sha1_signature(http_request request, details::oauth1_state state) const + { + auto text(_build_signature_base_string(std::move(request), std::move(state))); + auto digest(_hmac_sha1(_build_key(), std::move(text))); + auto signature(utility::conversions::to_base64(std::move(digest))); + return signature; + } + + // Builds PLAINTEXT signature according to: + // http://tools.ietf.org/html/rfc5849#section-3.4.4 + utility::string_t _build_plaintext_signature() const + { + return _build_key(); + } + + details::oauth1_state _generate_auth_state(utility::string_t extra_key, utility::string_t extra_value) + { + return details::oauth1_state(_generate_timestamp(), _generate_nonce(), std::move(extra_key), std::move(extra_value)); + } + + details::oauth1_state _generate_auth_state() + { + return details::oauth1_state(_generate_timestamp(), _generate_nonce()); + } + + /// + /// Gets map of parameters to sign. + /// + /// Map of parameters. + const std::map& parameters() const { return m_parameters_to_sign; } + + /// + /// Adds a key value parameter. + /// + /// Key as a string value. + /// Value as a string value. + void add_parameter(const utility::string_t &key, const utility::string_t &value) { m_parameters_to_sign[key] = value; } + + /// + /// Adds a key value parameter. + /// + /// Key as a string value. + /// Value as a string value. + void add_parameter(utility::string_t &&key, utility::string_t &&value) { m_parameters_to_sign[std::move(key)] = std::move(value); } + + /// + /// Sets entire map or parameters replacing all previously values. + /// + /// Map of values. + void set_parameters(const std::map ¶meters) + { + m_parameters_to_sign.clear(); + m_parameters_to_sign = parameters; + } + + /// + /// Clears all parameters. + /// + void clear_parameters() { m_parameters_to_sign.clear(); } + +private: + friend class web::http::client::http_client_config; + friend class web::http::oauth1::details::oauth1_handler; + + oauth1_config() : + m_is_authorization_completed(false) + {} + + utility::string_t _generate_nonce() + { + return m_nonce_generator.generate(); + } + + static utility::string_t _generate_timestamp() + { + return utility::conversions::print_string(utility::datetime::utc_timestamp(), std::locale::classic()); + } + + _ASYNCRTIMP static std::vector __cdecl _hmac_sha1(const utility::string_t& key, const utility::string_t& data); + + static utility::string_t _build_base_string_uri(const uri& u); + + utility::string_t _build_normalized_parameters(web::http::uri u, const details::oauth1_state& state) const; + + utility::string_t _build_signature(http_request request, details::oauth1_state state) const; + + utility::string_t _build_key() const + { + return uri::encode_data_string(consumer_secret()) + _XPLATSTR("&") + uri::encode_data_string(m_token.secret()); + } + + void _authenticate_request(http_request &req) + { + _authenticate_request(req, _generate_auth_state()); + } + + _ASYNCRTIMP void _authenticate_request(http_request &req, details::oauth1_state state); + + _ASYNCRTIMP pplx::task _request_token(details::oauth1_state state, bool is_temp_token_request); + + utility::string_t m_consumer_key; + utility::string_t m_consumer_secret; + oauth1_token m_token; + + utility::string_t m_temp_endpoint; + utility::string_t m_auth_endpoint; + utility::string_t m_token_endpoint; + utility::string_t m_callback_uri; + utility::string_t m_realm; + oauth1_method m_method; + + std::map m_parameters_to_sign; + + utility::nonce_generator m_nonce_generator; + bool m_is_authorization_completed; +}; + +} // namespace web::http::oauth1::experimental + +namespace details +{ + +class oauth1_handler : public http_pipeline_stage +{ +public: + oauth1_handler(std::shared_ptr cfg) : + m_config(std::move(cfg)) + {} + + virtual pplx::task propagate(http_request request) override + { + if (m_config) + { + m_config->_authenticate_request(request); + } + return next_stage()->propagate(request); + } + +private: + std::shared_ptr m_config; +}; + +}}}} + +#endif diff --git a/3rdparty/cpprestsdk/include/cpprest/oauth2.h b/3rdparty/cpprestsdk/include/cpprest/oauth2.h new file mode 100644 index 00000000..4650db1f --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/oauth2.h @@ -0,0 +1,523 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* HTTP Library: Oauth 2.0 +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#ifndef _CASA_OAUTH2_H +#define _CASA_OAUTH2_H + +#include "cpprest/http_msg.h" + +namespace web +{ +namespace http +{ +namespace client +{ + // Forward declaration to avoid circular include dependency. + class http_client_config; +} + +/// oAuth 2.0 library. +namespace oauth2 +{ +namespace details +{ + +class oauth2_handler; + +// Constant strings for OAuth 2.0. +typedef utility::string_t oauth2_string; +class oauth2_strings +{ +public: +#define _OAUTH2_STRINGS +#define DAT(a_, b_) _ASYNCRTIMP static const oauth2_string a_; +#include "cpprest/details/http_constants.dat" +#undef _OAUTH2_STRINGS +#undef DAT +}; + +} // namespace web::http::oauth2::details + +/// oAuth functionality is currently in beta. +namespace experimental +{ + +/// +/// Exception type for OAuth 2.0 errors. +/// +class oauth2_exception : public std::exception +{ +public: + oauth2_exception(utility::string_t msg) : m_msg(utility::conversions::to_utf8string(std::move(msg))) {} + ~oauth2_exception() CPPREST_NOEXCEPT {} + const char* what() const CPPREST_NOEXCEPT { return m_msg.c_str(); } + +private: + std::string m_msg; +}; + +/// +/// OAuth 2.0 token and associated information. +/// +class oauth2_token +{ +public: + + /// + /// Value for undefined expiration time in expires_in(). + /// + enum { undefined_expiration = -1 }; + + oauth2_token(utility::string_t access_token=utility::string_t()) : + m_access_token(std::move(access_token)), + m_expires_in(undefined_expiration) + {} + + /// + /// Get access token validity state. + /// If true, access token is a valid. + /// + /// Access token validity state. + bool is_valid_access_token() const { return !access_token().empty(); } + + /// + /// Get access token. + /// + /// Access token string. + const utility::string_t& access_token() const { return m_access_token; } + /// + /// Set access token. + /// + /// Access token string to set. + void set_access_token(utility::string_t access_token) { m_access_token = std::move(access_token); } + + /// + /// Get refresh token. + /// + /// Refresh token string. + const utility::string_t& refresh_token() const { return m_refresh_token; } + /// + /// Set refresh token. + /// + /// Refresh token string to set. + void set_refresh_token(utility::string_t refresh_token) { m_refresh_token = std::move(refresh_token); } + + /// + /// Get token type. + /// + /// Token type string. + const utility::string_t& token_type() const { return m_token_type; } + /// + /// Set token type. + /// + /// Token type string to set. + void set_token_type(utility::string_t token_type) { m_token_type = std::move(token_type); } + + /// + /// Get token scope. + /// + /// Token scope string. + const utility::string_t& scope() const { return m_scope; } + /// + /// Set token scope. + /// + /// Token scope string to set. + void set_scope(utility::string_t scope) { m_scope = std::move(scope); } + + /// + /// Get the lifetime of the access token in seconds. + /// For example, 3600 means the access token will expire in one hour from + /// the time when access token response was generated by the authorization server. + /// Value of undefined_expiration means expiration time is either + /// unset or that it was not returned by the server with the access token. + /// + /// Lifetime of the access token in seconds or undefined_expiration if not set. + int64_t expires_in() const { return m_expires_in; } + /// + /// Set lifetime of access token (in seconds). + /// + /// Lifetime of access token in seconds. + void set_expires_in(int64_t expires_in) { m_expires_in = expires_in; } + +private: + utility::string_t m_access_token; + utility::string_t m_refresh_token; + utility::string_t m_token_type; + utility::string_t m_scope; + int64_t m_expires_in; +}; + +/// +/// OAuth 2.0 configuration. +/// +/// Encapsulates functionality for: +/// - Authenticating requests with an access token. +/// - Performing the OAuth 2.0 authorization code grant authorization flow. +/// See: http://tools.ietf.org/html/rfc6749#section-4.1 +/// - Performing the OAuth 2.0 implicit grant authorization flow. +/// See: http://tools.ietf.org/html/rfc6749#section-4.2 +/// +/// Performing OAuth 2.0 authorization: +/// 1. Set service and client/app parameters: +/// - Client/app key & secret (as provided by the service). +/// - The service authorization endpoint and token endpoint. +/// - Your client/app redirect URI. +/// - Use set_state() to assign a unique state string for the authorization +/// session (default: ""). +/// - If needed, use set_bearer_auth() to control bearer token passing in either +/// query or header (default: header). See: http://tools.ietf.org/html/rfc6750#section-2 +/// - If needed, use set_access_token_key() to set "non-standard" access token +/// key (default: "access_token"). +/// - If needed, use set_implicit_grant() to enable implicit grant flow. +/// 2. Build authorization URI with build_authorization_uri() and open this in web browser/control. +/// 3. The resource owner should then clicks "Yes" to authorize your client/app, and +/// as a result the web browser/control is redirected to redirect_uri(). +/// 5. Capture the redirected URI either in web control or by HTTP listener. +/// 6. Pass the redirected URI to token_from_redirected_uri() to obtain access token. +/// - The method ensures redirected URI contains same state() as set in step 1. +/// - In implicit_grant() is false, this will create HTTP request to fetch access token +/// from the service. Otherwise access token is already included in the redirected URI. +/// +/// Usage for issuing authenticated requests: +/// 1. Perform authorization as above to obtain the access token or use an existing token. +/// - Some services provide option to generate access tokens for testing purposes. +/// 2. Pass the resulting oauth2_config with the access token to http_client_config::set_oauth2(). +/// 3. Construct http_client with this http_client_config. As a result, all HTTP requests +/// by that client will be OAuth 2.0 authenticated. +/// +/// +class oauth2_config +{ +public: + + oauth2_config(utility::string_t client_key, utility::string_t client_secret, + utility::string_t auth_endpoint, utility::string_t token_endpoint, + utility::string_t redirect_uri, utility::string_t scope=utility::string_t()) : + m_client_key(std::move(client_key)), + m_client_secret(std::move(client_secret)), + m_auth_endpoint(std::move(auth_endpoint)), + m_token_endpoint(std::move(token_endpoint)), + m_redirect_uri(std::move(redirect_uri)), + m_scope(std::move(scope)), + m_implicit_grant(false), + m_bearer_auth(true), + m_http_basic_auth(true), + m_access_token_key(details::oauth2_strings::access_token) + {} + + /// + /// Builds an authorization URI to be loaded in the web browser/view. + /// The URI is built with auth_endpoint() as basis. + /// The implicit_grant() affects the built URI by selecting + /// either authorization code or implicit grant flow. + /// You can set generate_state to generate a new random state string. + /// + /// If true, a new random state() string is generated + /// which replaces the current state(). If false, state() is unchanged and used as-is. + /// Authorization URI string. + _ASYNCRTIMP utility::string_t build_authorization_uri(bool generate_state); + + /// + /// Fetch an access token (and possibly a refresh token) based on redirected URI. + /// Behavior depends on the implicit_grant() setting. + /// If implicit_grant() is false, the URI is parsed for 'code' + /// parameter, and then token_from_code() is called with this code. + /// See: http://tools.ietf.org/html/rfc6749#section-4.1 + /// Otherwise, redirect URI fragment part is parsed for 'access_token' + /// parameter, which directly contains the token(s). + /// See: http://tools.ietf.org/html/rfc6749#section-4.2 + /// In both cases, the 'state' parameter is parsed and is verified to match state(). + /// + /// The URI where web browser/view was redirected after resource owner's authorization. + /// Task that fetches the token(s) based on redirected URI. + _ASYNCRTIMP pplx::task token_from_redirected_uri(const web::http::uri& redirected_uri); + + /// + /// Fetches an access token (and possibly a refresh token) from the token endpoint. + /// The task creates an HTTP request to the token_endpoint() which exchanges + /// the authorization code for the token(s). + /// This also sets the refresh token if one was returned. + /// See: http://tools.ietf.org/html/rfc6749#section-4.1.3 + /// + /// Code received via redirect upon successful authorization. + /// Task that fetches token(s) based on the authorization code. + pplx::task token_from_code(utility::string_t authorization_code) + { + uri_builder ub; + ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::authorization_code, false); + ub.append_query(details::oauth2_strings::code, uri::encode_data_string(std::move(authorization_code)), false); + ub.append_query(details::oauth2_strings::redirect_uri, uri::encode_data_string(redirect_uri()), false); + return _request_token(ub); + } + + /// + /// Fetches a new access token (and possibly a new refresh token) using the refresh token. + /// The task creates a HTTP request to the token_endpoint(). + /// If successful, resulting access token is set as active via set_token(). + /// See: http://tools.ietf.org/html/rfc6749#section-6 + /// This also sets a new refresh token if one was returned. + /// + /// Task that fetches the token(s) using the refresh token. + pplx::task token_from_refresh() + { + uri_builder ub; + ub.append_query(details::oauth2_strings::grant_type, details::oauth2_strings::refresh_token, false); + ub.append_query(details::oauth2_strings::refresh_token, uri::encode_data_string(token().refresh_token()), false); + return _request_token(ub); + } + + /// + /// Returns enabled state of the configuration. + /// The oauth2_handler will perform OAuth 2.0 authentication only if + /// this method returns true. + /// Return value is true if access token is valid (=fetched or manually set). + /// + /// The configuration enabled state. + bool is_enabled() const { return token().is_valid_access_token(); } + + /// + /// Get client key. + /// + /// Client key string. + const utility::string_t& client_key() const { return m_client_key; } + /// + /// Set client key. + /// + /// Client key string to set. + void set_client_key(utility::string_t client_key) { m_client_key = std::move(client_key); } + + /// + /// Get client secret. + /// + /// Client secret string. + const utility::string_t& client_secret() const { return m_client_secret; } + /// + /// Set client secret. + /// + /// Client secret string to set. + void set_client_secret(utility::string_t client_secret) { m_client_secret = std::move(client_secret); } + + /// + /// Get authorization endpoint URI string. + /// + /// Authorization endpoint URI string. + const utility::string_t& auth_endpoint() const { return m_auth_endpoint; } + /// + /// Set authorization endpoint URI string. + /// + /// Authorization endpoint URI string to set. + void set_auth_endpoint(utility::string_t auth_endpoint) { m_auth_endpoint = std::move(auth_endpoint); } + + /// + /// Get token endpoint URI string. + /// + /// Token endpoint URI string. + const utility::string_t& token_endpoint() const { return m_token_endpoint; } + /// + /// Set token endpoint URI string. + /// + /// Token endpoint URI string to set. + void set_token_endpoint(utility::string_t token_endpoint) { m_token_endpoint = std::move(token_endpoint); } + + /// + /// Get redirect URI string. + /// + /// Redirect URI string. + const utility::string_t& redirect_uri() const { return m_redirect_uri; } + /// + /// Set redirect URI string. + /// + /// Redirect URI string to set. + void set_redirect_uri(utility::string_t redirect_uri) { m_redirect_uri = std::move(redirect_uri); } + + /// + /// Get scope used in authorization for token. + /// + /// Scope string used in authorization. + const utility::string_t& scope() const { return m_scope; } + /// + /// Set scope for authorization for token. + /// + /// Scope string for authorization for token. + void set_scope(utility::string_t scope) { m_scope = std::move(scope); } + + /// + /// Get client state string used in authorization. + /// + /// Client state string used in authorization. + const utility::string_t& state() { return m_state; } + /// + /// Set client state string for authorization for token. + /// The state string is used in authorization for security reasons + /// (to uniquely identify authorization sessions). + /// If desired, suitably secure state string can be automatically generated + /// by build_authorization_uri(). + /// A good state string consist of 30 or more random alphanumeric characters. + /// + /// Client authorization state string to set. + void set_state(utility::string_t state) { m_state = std::move(state); } + + /// + /// Get token. + /// + /// Token. + const oauth2_token& token() const { return m_token; } + /// + /// Set token. + /// + /// Token to set. + void set_token(oauth2_token token) { m_token = std::move(token); } + + /// + /// Get implicit grant setting for authorization. + /// + /// Implicit grant setting for authorization. + bool implicit_grant() const { return m_implicit_grant; } + /// + /// Set implicit grant setting for authorization. + /// False means authorization code grant is used for authorization. + /// True means implicit grant is used. + /// Default: False. + /// + /// The implicit grant setting to set. + void set_implicit_grant(bool implicit_grant) { m_implicit_grant = implicit_grant; } + + /// + /// Get bearer token authentication setting. + /// + /// Bearer token authentication setting. + bool bearer_auth() const { return m_bearer_auth; } + /// + /// Set bearer token authentication setting. + /// This must be selected based on what the service accepts. + /// True means access token is passed in the request header. (http://tools.ietf.org/html/rfc6750#section-2.1) + /// False means access token in passed in the query parameters. (http://tools.ietf.org/html/rfc6750#section-2.3) + /// Default: True. + /// + /// The bearer token authentication setting to set. + void set_bearer_auth(bool bearer_auth) { m_bearer_auth = bearer_auth; } + + /// + /// Get HTTP Basic authentication setting for token endpoint. + /// + /// HTTP Basic authentication setting for token endpoint. + bool http_basic_auth() const { return m_http_basic_auth; } + /// + /// Set HTTP Basic authentication setting for token endpoint. + /// This setting must be selected based on what the service accepts. + /// True means HTTP Basic authentication is used for the token endpoint. + /// False means client key & secret are passed in the HTTP request body. + /// Default: True. + /// + /// The HTTP Basic authentication setting to set. + void set_http_basic_auth(bool http_basic_auth) { m_http_basic_auth = http_basic_auth; } + + /// + /// Get access token key. + /// + /// Access token key string. + const utility::string_t& access_token_key() const { return m_access_token_key; } + /// + /// Set access token key. + /// If the service requires a "non-standard" key you must set it here. + /// Default: "access_token". + /// + void set_access_token_key(utility::string_t access_token_key) { m_access_token_key = std::move(access_token_key); } + +private: + friend class web::http::client::http_client_config; + friend class web::http::oauth2::details::oauth2_handler; + + oauth2_config() : + m_implicit_grant(false), + m_bearer_auth(true), + m_http_basic_auth(true) + {} + + _ASYNCRTIMP pplx::task _request_token(uri_builder& request_body); + + oauth2_token _parse_token_from_json(const json::value& token_json); + + void _authenticate_request(http_request &req) const + { + if (bearer_auth()) + { + req.headers().add(header_names::authorization, _XPLATSTR("Bearer ") + token().access_token()); + } + else + { + uri_builder ub(req.request_uri()); + ub.append_query(access_token_key(), token().access_token()); + req.set_request_uri(ub.to_uri()); + } + } + + utility::string_t m_client_key; + utility::string_t m_client_secret; + utility::string_t m_auth_endpoint; + utility::string_t m_token_endpoint; + utility::string_t m_redirect_uri; + utility::string_t m_scope; + utility::string_t m_state; + + bool m_implicit_grant; + bool m_bearer_auth; + bool m_http_basic_auth; + utility::string_t m_access_token_key; + + oauth2_token m_token; + + utility::nonce_generator m_state_generator; +}; + +} // namespace web::http::oauth2::experimental + +namespace details +{ + +class oauth2_handler : public http_pipeline_stage +{ +public: + oauth2_handler(std::shared_ptr cfg) : + m_config(std::move(cfg)) + {} + + virtual pplx::task propagate(http_request request) override + { + if (m_config) + { + m_config->_authenticate_request(request); + } + return next_stage()->propagate(request); + } + +private: + std::shared_ptr m_config; +}; + +}}}} + +#endif diff --git a/3rdparty/cpprestsdk/include/cpprest/producerconsumerstream.h b/3rdparty/cpprestsdk/include/cpprest/producerconsumerstream.h new file mode 100644 index 00000000..54afc74d --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/producerconsumerstream.h @@ -0,0 +1,709 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* This file defines a basic memory-based stream buffer, which allows consumer / producer pairs to communicate +* data via a buffer. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#ifndef _CASA_PRODUCER_CONSUMER_STREAMS_H +#define _CASA_PRODUCER_CONSUMER_STREAMS_H + +#include +#include +#include +#include + +#include "pplx/pplxtasks.h" +#include "cpprest/astreambuf.h" + +namespace Concurrency { namespace streams { + + namespace details { + + /// + /// The basic_producer_consumer_buffer class serves as a memory-based steam buffer that supports both writing and reading + /// sequences of characters. It can be used as a consumer/producer buffer. + /// + template + class basic_producer_consumer_buffer : public streams::details::streambuf_state_manager<_CharType> + { + public: + typedef typename ::concurrency::streams::char_traits<_CharType> traits; + typedef typename basic_streambuf<_CharType>::int_type int_type; + typedef typename basic_streambuf<_CharType>::pos_type pos_type; + typedef typename basic_streambuf<_CharType>::off_type off_type; + + /// + /// Constructor + /// + basic_producer_consumer_buffer(size_t alloc_size) + : streambuf_state_manager<_CharType>(std::ios_base::out | std::ios_base::in), + m_alloc_size(alloc_size), + m_allocBlock(nullptr), + m_total(0), m_total_read(0), m_total_written(0), + m_synced(0) + { + } + + /// + /// Destructor + /// + virtual ~basic_producer_consumer_buffer() + { + // Note: there is no need to call 'wait()' on the result of close(), + // since we happen to know that close() will return without actually + // doing anything asynchronously. Should the implementation of _close_write() + // change in that regard, this logic may also have to change. + this->_close_read(); + this->_close_write(); + + _ASSERTE(m_requests.empty()); + m_blocks.clear(); + } + + /// + /// can_seek is used to determine whether a stream buffer supports seeking. + /// + virtual bool can_seek() const { return false; } + + /// + /// has_size is used to determine whether a stream buffer supports size(). + /// + virtual bool has_size() const { return false; } + + /// + /// Get the stream buffer size, if one has been set. + /// + /// The direction of buffering (in or out) + /// An implementation that does not support buffering will always return '0'. + virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const + { + return 0; + } + + /// + /// Sets the stream buffer implementation to buffer or not buffer. + /// + /// The size to use for internal buffering, 0 if no buffering should be done. + /// The direction of buffering (in or out) + /// An implementation that does not support buffering will silently ignore calls to this function and it will not have any effect on what is returned by subsequent calls to . + virtual void set_buffer_size(size_t , std::ios_base::openmode = std::ios_base::in) + { + return; + } + + /// + /// For any input stream, in_avail returns the number of characters that are immediately available + /// to be consumed without blocking. May be used in conjunction with to read data without + /// incurring the overhead of using tasks. + /// + virtual size_t in_avail() const { return m_total; } + + + /// + /// Gets the current read or write position in the stream. + /// + /// The I/O direction to seek (see remarks) + /// The current position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. + /// For such streams, the direction parameter defines whether to move the read or the write cursor. + virtual pos_type getpos(std::ios_base::openmode mode) const + { + if ( ((mode & std::ios_base::in) && !this->can_read()) || + ((mode & std::ios_base::out) && !this->can_write())) + return static_cast(traits::eof()); + + if (mode == std::ios_base::in) + return (pos_type)m_total_read; + else if (mode == std::ios_base::out) + return (pos_type)m_total_written; + else + return (pos_type)traits::eof(); + } + + // Seeking is not supported + virtual pos_type seekpos(pos_type, std::ios_base::openmode) { return (pos_type)traits::eof(); } + virtual pos_type seekoff(off_type , std::ios_base::seekdir , std::ios_base::openmode ) { return (pos_type)traits::eof(); } + + /// + /// Allocates a contiguous memory block and returns it. + /// + /// The number of characters to allocate. + /// A pointer to a block to write to, null if the stream buffer implementation does not support alloc/commit. + virtual _CharType* _alloc(size_t count) + { + if (!this->can_write()) + { + return nullptr; + } + + // We always allocate a new block even if the count could be satisfied by + // the current write block. While this does lead to wasted space it allows for + // easier book keeping + + _ASSERTE(!m_allocBlock); + m_allocBlock = std::make_shared<_block>(count); + return m_allocBlock->wbegin(); + } + + /// + /// Submits a block already allocated by the stream buffer. + /// + /// The number of characters to be committed. + virtual void _commit(size_t count) + { + pplx::extensibility::scoped_critical_section_t l(m_lock); + + // The count does not reflect the actual size of the block. + // Since we do not allow any more writes to this block it would suffice. + // If we ever change the algorithm to reuse blocks then this needs to be revisited. + + _ASSERTE((bool)m_allocBlock); + m_allocBlock->update_write_head(count); + m_blocks.push_back(m_allocBlock); + m_allocBlock = nullptr; + + update_write_head(count); + } + + /// + /// Gets a pointer to the next already allocated contiguous block of data. + /// + /// A reference to a pointer variable that will hold the address of the block on success. + /// The number of contiguous characters available at the address in 'ptr.' + /// true if the operation succeeded, false otherwise. + /// + /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that + /// there is no block to return immediately or that the stream buffer does not support the operation. + /// The stream buffer may not de-allocate the block until is called. + /// If the end of the stream is reached, the function will return true, a null pointer, and a count of zero; + /// a subsequent read will not succeed. + /// + virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count) + { + count = 0; + ptr = nullptr; + + if (!this->can_read()) return false; + + pplx::extensibility::scoped_critical_section_t l(m_lock); + + if (m_blocks.empty()) + { + // If the write head has been closed then have reached the end of the + // stream (return true), otherwise more data could be written later (return false). + return !this->can_write(); + } + else + { + auto block = m_blocks.front(); + + count = block->rd_chars_left(); + ptr = block->rbegin(); + + _ASSERTE(ptr != nullptr); + return true; + } + } + + /// + /// Releases a block of data acquired using . This frees the stream buffer to de-allocate the + /// memory, if it so desires. Move the read position ahead by the count. + /// + /// A pointer to the block of data to be released. + /// The number of characters that were read. + virtual void release(_Out_writes_opt_ (count) _CharType *ptr, _In_ size_t count) + { + if (ptr == nullptr) return; + + pplx::extensibility::scoped_critical_section_t l(m_lock); + auto block = m_blocks.front(); + + _ASSERTE(block->rd_chars_left() >= count); + block->m_read += count; + + update_read_head(count); + } + + protected: + + virtual pplx::task _sync() + { + pplx::extensibility::scoped_critical_section_t l(m_lock); + + m_synced = in_avail(); + + fulfill_outstanding(); + + return pplx::task_from_result(true); + } + + virtual pplx::task _putc(_CharType ch) + { + return pplx::task_from_result((this->write(&ch, 1) == 1) ? static_cast(ch) : traits::eof()); + } + + virtual pplx::task _putn(const _CharType *ptr, size_t count) + { + return pplx::task_from_result(this->write(ptr, count)); + } + + + virtual pplx::task _getn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) + { + pplx::task_completion_event tce; + enqueue_request(_request(count, [this, ptr, count, tce]() + { + // VS 2010 resolves read to a global function. Explicit + // invocation through the "this" pointer fixes the issue. + tce.set(this->read(ptr, count)); + })); + return pplx::create_task(tce); + } + + virtual size_t _sgetn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) + { + pplx::extensibility::scoped_critical_section_t l(m_lock); + return can_satisfy(count) ? this->read(ptr, count) : (size_t)traits::requires_async(); + } + + virtual size_t _scopy(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) + { + pplx::extensibility::scoped_critical_section_t l(m_lock); + return can_satisfy(count) ? this->read(ptr, count, false) : (size_t)traits::requires_async(); + } + + virtual pplx::task _bumpc() + { + pplx::task_completion_event tce; + enqueue_request(_request(1, [this, tce]() + { + tce.set(this->read_byte(true)); + })); + return pplx::create_task(tce); + } + + virtual int_type _sbumpc() + { + pplx::extensibility::scoped_critical_section_t l(m_lock); + return can_satisfy(1) ? this->read_byte(true) : traits::requires_async(); + } + + virtual pplx::task _getc() + { + pplx::task_completion_event tce; + enqueue_request(_request(1, [this, tce]() + { + tce.set(this->read_byte(false)); + })); + return pplx::create_task(tce); + } + + int_type _sgetc() + { + pplx::extensibility::scoped_critical_section_t l(m_lock); + return can_satisfy(1) ? this->read_byte(false) : traits::requires_async(); + } + + virtual pplx::task _nextc() + { + pplx::task_completion_event tce; + enqueue_request(_request(1, [this, tce]() + { + this->read_byte(true); + tce.set(this->read_byte(false)); + })); + return pplx::create_task(tce); + } + + virtual pplx::task _ungetc() + { + return pplx::task_from_result(traits::eof()); + } + + private: + + /// + /// Close the stream buffer for writing + /// + pplx::task _close_write() + { + // First indicate that there could be no more writes. + // Fulfill outstanding relies on that to flush all the + // read requests. + this->m_stream_can_write = false; + + { + pplx::extensibility::scoped_critical_section_t l(this->m_lock); + + // This runs on the thread that called close. + this->fulfill_outstanding(); + } + + return pplx::task_from_result(); + } + + /// + /// Updates the write head by an offset specified by count + /// + /// This should be called with the lock held + void update_write_head(size_t count) + { + m_total += count; + m_total_written += count; + fulfill_outstanding(); + } + + /// + /// Writes count characters from ptr into the stream buffer + /// + size_t write(const _CharType *ptr, size_t count) + { + if (!this->can_write() || (count == 0)) return 0; + + // If no one is going to read, why bother? + // Just pretend to be writing! + if (!this->can_read()) return count; + + pplx::extensibility::scoped_critical_section_t l(m_lock); + + // Allocate a new block if necessary + if ( m_blocks.empty() || m_blocks.back()->wr_chars_left() < count ) + { + msl::safeint3::SafeInt alloc = m_alloc_size.Max(count); + m_blocks.push_back(std::make_shared<_block>(alloc)); + } + + // The block at the back is always the write head + auto last = m_blocks.back(); + auto countWritten = last->write(ptr, count); + _ASSERTE(countWritten == count); + + update_write_head(countWritten); + return countWritten; + } + + /// + /// Fulfill pending requests + /// + /// This should be called with the lock held + void fulfill_outstanding() + { + while ( !m_requests.empty() ) + { + auto req = m_requests.front(); + + // If we cannot satisfy the request then we need + // to wait for the producer to write data + if (!can_satisfy(req.size())) return; + + // We have enough data to satisfy this request + req.complete(); + + // Remove it from the request queue + m_requests.pop(); + } + } + + /// + /// Represents a memory block + /// + class _block + { + public: + _block(size_t size) + : m_read(0), m_pos(0), m_size(size), m_data(new _CharType[size]) + { + } + + ~_block() + { + delete [] m_data; + } + + // Read head + size_t m_read; + + // Write head + size_t m_pos; + + // Allocation size (of m_data) + size_t m_size; + + // The data store + _CharType * m_data; + + // Pointer to the read head + _CharType * rbegin() + { + return m_data + m_read; + } + + // Pointer to the write head + _CharType * wbegin() + { + return m_data + m_pos; + } + + // Read up to count characters from the block + size_t read(_Out_writes_ (count) _CharType * dest, _In_ size_t count, bool advance = true) + { + msl::safeint3::SafeInt avail(rd_chars_left()); + auto countRead = static_cast(avail.Min(count)); + + _CharType * beg = rbegin(); + _CharType * end = rbegin() + countRead; + +#ifdef _WIN32 + // Avoid warning C4996: Use checked iterators under SECURE_SCL + std::copy(beg, end, stdext::checked_array_iterator<_CharType *>(dest, count)); +#else + std::copy(beg, end, dest); +#endif // _WIN32 + + if (advance) + { + m_read += countRead; + } + + return countRead; + } + + // Write count characters into the block + size_t write(const _CharType * src, size_t count) + { + msl::safeint3::SafeInt avail(wr_chars_left()); + auto countWritten = static_cast(avail.Min(count)); + + const _CharType * srcEnd = src + countWritten; + +#ifdef _WIN32 + // Avoid warning C4996: Use checked iterators under SECURE_SCL + std::copy(src, srcEnd, stdext::checked_array_iterator<_CharType *>(wbegin(), static_cast(avail))); +#else + std::copy(src, srcEnd, wbegin()); +#endif // _WIN32 + + update_write_head(countWritten); + return countWritten; + } + + void update_write_head(size_t count) + { + m_pos += count; + } + + size_t rd_chars_left() const { return m_pos-m_read; } + size_t wr_chars_left() const { return m_size-m_pos; } + + private: + + // Copy is not supported + _block(const _block&); + _block& operator=(const _block&); + }; + + /// + /// Represents a request on the stream buffer - typically reads + /// + class _request + { + public: + + typedef std::function func_type; + _request(size_t count, const func_type& func) + : m_func(func), m_count(count) + { + } + + void complete() + { + m_func(); + } + + size_t size() const + { + return m_count; + } + + private: + + func_type m_func; + size_t m_count; + }; + + void enqueue_request(_request req) + { + pplx::extensibility::scoped_critical_section_t l(m_lock); + + if (can_satisfy(req.size())) + { + // We can immediately fulfill the request. + req.complete(); + } + else + { + // We must wait for data to arrive. + m_requests.push(req); + } + } + + /// + /// Determine if the request can be satisfied. + /// + bool can_satisfy(size_t count) + { + return (m_synced > 0) || (this->in_avail() >= count) || !this->can_write(); + } + + /// + /// Reads a byte from the stream and returns it as int_type. + /// Note: This routine shall only be called if can_satisfy() returned true. + /// + /// This should be called with the lock held + int_type read_byte(bool advance = true) + { + _CharType value; + auto read_size = this->read(&value, 1, advance); + return read_size == 1 ? static_cast(value) : traits::eof(); + } + + /// + /// Reads up to count characters into ptr and returns the count of characters copied. + /// The return value (actual characters copied) could be <= count. + /// Note: This routine shall only be called if can_satisfy() returned true. + /// + /// This should be called with the lock held + size_t read(_Out_writes_ (count) _CharType *ptr, _In_ size_t count, bool advance = true) + { + _ASSERTE(can_satisfy(count)); + + size_t read = 0; + + for (auto iter = begin(m_blocks); iter != std::end(m_blocks); ++iter) + { + auto block = *iter; + auto read_from_block = block->read(ptr + read, count - read, advance); + + read += read_from_block; + + _ASSERTE(count >= read); + if (read == count) break; + } + + if (advance) + { + update_read_head(read); + } + + return read; + } + + /// + /// Updates the read head by the specified offset + /// + /// This should be called with the lock held + void update_read_head(size_t count) + { + m_total -= count; + m_total_read += count; + + if ( m_synced > 0 ) + m_synced = (m_synced > count) ? (m_synced-count) : 0; + + // The block at the front is always the read head. + // Purge empty blocks so that the block at the front reflects the read head + while (!m_blocks.empty()) + { + // If front block is not empty - we are done + if (m_blocks.front()->rd_chars_left() > 0) break; + + // The block has no more data to be read. Relase the block + m_blocks.pop_front(); + } + } + + // The in/out mode for the buffer + std::ios_base::openmode m_mode; + + // Default block size + msl::safeint3::SafeInt m_alloc_size; + + // Block used for alloc/commit + std::shared_ptr<_block> m_allocBlock; + + // Total available data + size_t m_total; + + size_t m_total_read; + size_t m_total_written; + + // Keeps track of the number of chars that have been flushed but still + // remain to be consumed by a read operation. + size_t m_synced; + + // The producer-consumer buffer is intended to be used concurrently by a reader + // and a writer, who are not coordinating their accesses to the buffer (coordination + // being what the buffer is for in the first place). Thus, we have to protect + // against some of the internal data elements against concurrent accesses + // and the possibility of inconsistent states. A simple non-recursive lock + // should be sufficient for those purposes. + pplx::extensibility::critical_section_t m_lock; + + // Memory blocks + std::deque> m_blocks; + + // Queue of requests + std::queue<_request> m_requests; + }; + + } // namespace details + + /// + /// The producer_consumer_buffer class serves as a memory-based steam buffer that supports both writing and reading + /// sequences of bytes. It can be used as a consumer/producer buffer. + /// + /// + /// The data type of the basic element of the producer_consumer_buffer. + /// + /// + /// This is a reference-counted version of basic_producer_consumer_buffer. + template + class producer_consumer_buffer : public streambuf<_CharType> + { + public: + typedef _CharType char_type; + + /// + /// Create a producer_consumer_buffer. + /// + /// The internal default block size. + producer_consumer_buffer(size_t alloc_size = 512) + : streambuf<_CharType>(std::make_shared>(alloc_size)) + { + } + }; + +}} // namespaces + +#endif \ No newline at end of file diff --git a/3rdparty/cpprestsdk/include/cpprest/rawptrstream.h b/3rdparty/cpprestsdk/include/cpprest/rawptrstream.h new file mode 100644 index 00000000..e5d00a1c --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/rawptrstream.h @@ -0,0 +1,642 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* This file defines a stream buffer that is based on a raw pointer and block size. Unlike a vector-based +* stream buffer, the buffer cannot be expanded or contracted, it has a fixed capacity. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#ifndef _CASA_RAWPTR_STREAMS_H +#define _CASA_RAWPTR_STREAMS_H + +#include +#include +#include +#include + +#include "pplx/pplxtasks.h" +#include "cpprest/astreambuf.h" +#include "cpprest/streams.h" + +namespace Concurrency { namespace streams { + + // Forward declarations + template class rawptr_buffer; + + namespace details { + + /// + /// The basic_rawptr_buffer class serves as a memory-based steam buffer that supports both writing and reading + /// sequences of characters to and from a fixed-size block. + /// + template + class basic_rawptr_buffer : public streams::details::streambuf_state_manager<_CharType> + { + public: + typedef _CharType char_type; + + typedef typename basic_streambuf<_CharType>::traits traits; + typedef typename basic_streambuf<_CharType>::int_type int_type; + typedef typename basic_streambuf<_CharType>::pos_type pos_type; + typedef typename basic_streambuf<_CharType>::off_type off_type; + + /// + /// Constructor + /// + basic_rawptr_buffer() + : streambuf_state_manager<_CharType>(std::ios_base::in | std::ios_base::out), + m_data(nullptr), + m_current_position(0), + m_size(0) + { + } + + /// + /// Destructor + /// + virtual ~basic_rawptr_buffer() + { + this->_close_read(); + this->_close_write(); + } + + protected: + + /// + /// can_seek is used to determine whether a stream buffer supports seeking. + /// + virtual bool can_seek() const { return this->is_open(); } + + /// + /// has_size is used to determine whether a stream buffer supports size(). + /// + virtual bool has_size() const { return this->is_open(); } + + /// + /// Gets the size of the stream, if known. Calls to has_size will determine whether + /// the result of size can be relied on. + /// + virtual utility::size64_t size() const + { + return utility::size64_t(m_size); + } + + /// + /// Get the stream buffer size, if one has been set. + /// + /// The direction of buffering (in or out) + /// An implementation that does not support buffering will always return '0'. + virtual size_t buffer_size(std::ios_base::openmode = std::ios_base::in) const + { + return 0; + } + + /// + /// Set the stream buffer implementation to buffer or not buffer. + /// + /// The size to use for internal buffering, 0 if no buffering should be done. + /// The direction of buffering (in or out) + /// An implementation that does not support buffering will silently ignore calls to this function and it will not have + /// any effect on what is returned by subsequent calls to buffer_size(). + virtual void set_buffer_size(size_t , std::ios_base::openmode = std::ios_base::in) + { + return; + } + + /// + /// For any input stream, in_avail returns the number of characters that are immediately available + /// to be consumed without blocking. May be used in conjunction with and sgetn() to + /// read data without incurring the overhead of using tasks. + /// + virtual size_t in_avail() const + { + // See the comment in seek around the restiction that we do not allow read head to + // seek beyond the current size. + _ASSERTE(m_current_position <= m_size); + + msl::safeint3::SafeInt readhead(m_current_position); + msl::safeint3::SafeInt writeend(m_size); + return (size_t)(writeend - readhead); + } + + /// + /// Closes the stream buffer, preventing further read or write operations. + /// + /// The I/O mode (in or out) to close for. + virtual pplx::task close(std::ios_base::openmode mode) + { + if (mode & std::ios_base::in) + { + this->_close_read().get(); // Safe to call get() here. + } + + if (mode & std::ios_base::out) + { + this->_close_write().get(); // Safe to call get() here. + } + + if ( !this->can_read() && !this->can_write() ) + { + m_data = nullptr; + } + + // Exceptions will be propagated out of _close_read or _close_write + return pplx::task_from_result(); + } + + virtual pplx::task _sync() + { + return pplx::task_from_result(true); + } + + virtual pplx::task _putc(_CharType ch) + { + if (m_current_position >= m_size) + return pplx::task_from_result(traits::eof()); + int_type retVal = (this->write(&ch, 1) == 1) ? static_cast(ch) : traits::eof(); + return pplx::task_from_result(retVal); + } + + virtual pplx::task _putn(const _CharType *ptr, size_t count) + { + msl::safeint3::SafeInt newSize = msl::safeint3::SafeInt(count) + m_current_position; + if ( newSize > m_size ) + return pplx::task_from_exception(std::make_exception_ptr(std::runtime_error("Writing past the end of the buffer"))); + return pplx::task_from_result(this->write(ptr, count)); + } + + /// + /// Allocates a contiguous memory block and returns it. + /// + /// The number of characters to allocate. + /// A pointer to a block to write to, null if the stream buffer implementation does not support alloc/commit. + _CharType* _alloc(size_t count) + { + if (!this->can_write()) return nullptr; + + msl::safeint3::SafeInt readhead(m_current_position); + msl::safeint3::SafeInt writeend(m_size); + size_t space_left = (size_t)(writeend - readhead); + + if (space_left < count) return nullptr; + + // Let the caller copy the data + return (_CharType*)(m_data+m_current_position); + } + + /// + /// Submits a block already allocated by the stream buffer. + /// + /// The number of characters to be committed. + void _commit(size_t actual) + { + // Update the write position and satisfy any pending reads + update_current_position(m_current_position+actual); + } + + /// + /// Gets a pointer to the next already allocated contiguous block of data. + /// + /// A reference to a pointer variable that will hold the address of the block on success. + /// The number of contiguous characters available at the address in 'ptr.' + /// true if the operation succeeded, false otherwise. + /// + /// A return of false does not necessarily indicate that a subsequent read operation would fail, only that + /// there is no block to return immediately or that the stream buffer does not support the operation. + /// The stream buffer may not de-allocate the block until is called. + /// If the end of the stream is reached, the function will return true, a null pointer, and a count of zero; + /// a subsequent read will not succeed. + /// + virtual bool acquire(_Out_ _CharType*& ptr, _Out_ size_t& count) + { + count = 0; + ptr = nullptr; + + if (!this->can_read()) return false; + + count = in_avail(); + + if (count > 0) + { + ptr = (_CharType*)(m_data+m_current_position); + return true; + } + else + { + ptr = nullptr; + + // Can only be open for read OR write, not both. If there is no data then + // we have reached the end of the stream so indicate such with true. + return true; + } + } + + /// + /// Releases a block of data acquired using . This frees the stream buffer to de-allocate the + /// memory, if it so desires. Move the read position ahead by the count. + /// + /// A pointer to the block of data to be released. + /// The number of characters that were read. + virtual void release(_Out_writes_opt_ (count) _CharType *ptr, _In_ size_t count) + { + if (ptr != nullptr) + update_current_position(m_current_position + count); + } + + virtual pplx::task _getn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) + { + return pplx::task_from_result(this->read(ptr, count)); + } + + size_t _sgetn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) + { + return this->read(ptr, count); + } + + virtual size_t _scopy(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) + { + return this->read(ptr, count, false); + } + + virtual pplx::task _bumpc() + { + return pplx::task_from_result(this->read_byte(true)); + } + + virtual int_type _sbumpc() + { + return this->read_byte(true); + } + + virtual pplx::task _getc() + { + return pplx::task_from_result(this->read_byte(false)); + } + + int_type _sgetc() + { + return this->read_byte(false); + } + + virtual pplx::task _nextc() + { + if (m_current_position >= m_size-1) + return pplx::task_from_result(basic_streambuf<_CharType>::traits::eof()); + + this->read_byte(true); + return pplx::task_from_result(this->read_byte(false)); + } + + virtual pplx::task _ungetc() + { + auto pos = seekoff(-1, std::ios_base::cur, std::ios_base::in); + if ( pos == (pos_type)traits::eof()) + return pplx::task_from_result(traits::eof()); + return this->getc(); + } + + /// + /// Gets the current read or write position in the stream. + /// + /// The I/O direction to seek (see remarks) + /// The current position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. + /// For such streams, the direction parameter defines whether to move the read or the write cursor. + virtual pos_type getpos(std::ios_base::openmode mode) const + { + if ( ((mode & std::ios_base::in) && !this->can_read()) || + ((mode & std::ios_base::out) && !this->can_write())) + return static_cast(traits::eof()); + + if (mode == std::ios_base::in) + return (pos_type)m_current_position; + else if (mode == std::ios_base::out) + return (pos_type)m_current_position; + else + return (pos_type)traits::eof(); + } + + /// + /// Seeks to the given position. + /// + /// The offset from the beginning of the stream. + /// The I/O direction to seek (see remarks). + /// The position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. For such streams, the direction parameter defines whether to move the read or the write cursor. + virtual pos_type seekpos(pos_type position, std::ios_base::openmode mode) + { + pos_type beg(0); + pos_type end(m_size); + + if (position >= beg) + { + auto pos = static_cast(position); + + // Read head + if ((mode & std::ios_base::in) && this->can_read()) + { + if (position <= end) + { + // We do not allow reads to seek beyond the end or before the start position. + update_current_position(pos); + return static_cast(m_current_position); + } + } + + // Write head + if ((mode & std::ios_base::out) && this->can_write()) + { + // Update write head and satisfy read requests if any + update_current_position(pos); + + return static_cast(m_current_position); + } + } + + return static_cast(traits::eof()); + } + + /// + /// Seeks to a position given by a relative offset. + /// + /// The relative position to seek to + /// The starting point (beginning, end, current) for the seek. + /// The I/O direction to seek (see remarks) + /// The position. EOF if the operation fails. + /// Some streams may have separate write and read cursors. + /// For such streams, the mode parameter defines whether to move the read or the write cursor. + virtual pos_type seekoff(off_type offset, std::ios_base::seekdir way, std::ios_base::openmode mode) + { + pos_type beg = 0; + pos_type cur = static_cast(m_current_position); + pos_type end = static_cast(m_size); + + switch ( way ) + { + case std::ios_base::beg: + return seekpos(beg + offset, mode); + + case std::ios_base::cur: + return seekpos(cur + offset, mode); + + case std::ios_base::end: + return seekpos(end + offset, mode); + + default: + return static_cast(traits::eof()); + } + } + + private: + template friend class ::concurrency::streams::rawptr_buffer; + + /// + /// Constructor + /// + /// The address (pointer to) the memory block. + /// The memory block size, measured in number of characters. + basic_rawptr_buffer(const _CharType* data, size_t size) + : streambuf_state_manager<_CharType>(std::ios_base::in), + m_data(const_cast<_CharType*>(data)), + m_size(size), + m_current_position(0) + { + validate_mode(std::ios_base::in); + } + + /// + /// Constructor + /// + /// The address (pointer to) the memory block. + /// The memory block size, measured in number of characters. + /// The stream mode (in, out, etc.). + basic_rawptr_buffer(_CharType* data, size_t size, std::ios_base::openmode mode) + : streambuf_state_manager<_CharType>(mode), + m_data(data), + m_size(size), + m_current_position(0) + { + validate_mode(mode); + } + + static void validate_mode(std::ios_base::openmode mode) + { + // Disallow simultaneous use of the stream buffer for writing and reading. + if ((mode & std::ios_base::in) && (mode & std::ios_base::out)) + throw std::invalid_argument("this combination of modes on raw pointer stream not supported"); + } + + /// + /// Determines if the request can be satisfied. + /// + bool can_satisfy(size_t) const + { + // We can always satisfy a read, at least partially, unless the + // read position is at the very end of the buffer. + return (in_avail() > 0); + } + + /// + /// Reads a byte from the stream and returns it as int_type. + /// Note: This routine must only be called if can_satisfy() returns true. + /// + int_type read_byte(bool advance = true) + { + _CharType value; + auto read_size = this->read(&value, 1, advance); + return read_size == 1 ? static_cast(value) : traits::eof(); + } + + /// + /// Reads up to count characters into ptr and returns the count of characters copied. + /// The return value (actual characters copied) could be <= count. + /// Note: This routine must only be called if can_satisfy() returns true. + /// + size_t read(_Out_writes_ (count) _CharType *ptr, _In_ size_t count, bool advance = true) + { + if (!can_satisfy(count)) + return 0; + + msl::safeint3::SafeInt request_size(count); + msl::safeint3::SafeInt read_size = request_size.Min(in_avail()); + + size_t newPos = m_current_position + read_size; + + auto readBegin = m_data + m_current_position; + auto readEnd = m_data + newPos; + +#ifdef _WIN32 + // Avoid warning C4996: Use checked iterators under SECURE_SCL + std::copy(readBegin, readEnd, stdext::checked_array_iterator<_CharType *>(ptr, count)); +#else + std::copy(readBegin, readEnd, ptr); +#endif // _WIN32 + + if (advance) + { + update_current_position(newPos); + } + + return (size_t) read_size; + } + + /// + /// Write count characters from the ptr into the stream buffer + /// + size_t write(const _CharType *ptr, size_t count) + { + if (!this->can_write() || (count == 0)) return 0; + + msl::safeint3::SafeInt newSize = msl::safeint3::SafeInt(count) +m_current_position; + + if ( newSize > m_size ) + throw std::runtime_error("Writing past the end of the buffer"); + + // Copy the data +#ifdef _WIN32 + // Avoid warning C4996: Use checked iterators under SECURE_SCL + std::copy(ptr, ptr + count, stdext::checked_array_iterator<_CharType *>(m_data, m_size, m_current_position)); +#else + std::copy(ptr, ptr + count, m_data+m_current_position); +#endif // _WIN32 + + // Update write head and satisfy pending reads if any + update_current_position(newSize); + + return count; + } + + /// + /// Updates the current read or write position + /// + void update_current_position(size_t newPos) + { + // The new write head + m_current_position = newPos; + + _ASSERTE(m_current_position <= m_size); + } + + // The actual memory block + _CharType* m_data; + + // The size of the memory block + size_t m_size; + + // Read/write head + size_t m_current_position; + }; + + } // namespace details + + /// + /// The rawptr_buffer class serves as a memory-based stream buffer that supports reading + /// sequences of characters to or from a fixed-size block. Note that it cannot be used simultaneously for reading as well as writing. + /// + /// + /// The data type of the basic element of the rawptr_buffer. + /// + template + class rawptr_buffer : public streambuf<_CharType> + { + public: + typedef _CharType char_type; + + /// + /// Create a rawptr_buffer given a pointer to a memory block and the size of the block. + /// + /// The address (pointer to) the memory block. + /// The memory block size, measured in number of characters. + rawptr_buffer(const char_type* data, size_t size) + : streambuf(std::shared_ptr>(new details::basic_rawptr_buffer(data, size))) + { + } + + /// + /// Create a rawptr_buffer given a pointer to a memory block and the size of the block. + /// + /// The address (pointer to) the memory block. + /// The memory block size, measured in number of characters. + rawptr_buffer(char_type* data, size_t size, std::ios_base::openmode mode = std::ios::out) + : streambuf(std::shared_ptr>(new details::basic_rawptr_buffer(data, size, mode))) + { + } + + /// + /// Default constructor. + /// + rawptr_buffer() + { + } + }; + + /// + /// The rawptr_stream class is used to create memory-backed streams that support writing or reading + /// sequences of characters to / from a fixed-size block. + /// + /// + /// The data type of the basic element of the rawptr_stream. + /// + template + class rawptr_stream + { + public: + typedef _CharType char_type; + typedef rawptr_buffer<_CharType> buffer_type; + + /// + /// Create a rawptr-stream given a pointer to a read-only memory block and the size of the block. + /// + /// The address (pointer to) the memory block. + /// The memory block size, measured in number of characters. + /// An opened input stream. + static concurrency::streams::basic_istream open_istream(const char_type* data, size_t size) + { + return concurrency::streams::basic_istream(buffer_type(data, size)); + } + + /// + /// Create a rawptr-stream given a pointer to a writable memory block and the size of the block. + /// + /// The address (pointer to) the memory block. + /// The memory block size, measured in number of characters. + /// An opened input stream. + static concurrency::streams::basic_istream open_istream(char_type* data, size_t size) + { + return concurrency::streams::basic_istream(buffer_type(data, size, std::ios::in)); + } + + /// + /// Create a rawptr-stream given a pointer to a writable memory block and the size of the block. + /// + /// The address (pointer to) the memory block. + /// The memory block size, measured in number of characters. + /// An opened output stream. + static concurrency::streams::basic_ostream open_ostream(char_type* data, size_t size) + { + return concurrency::streams::basic_ostream(buffer_type(data, size, std::ios::out)); + } + }; + +}} // namespaces + +#endif diff --git a/3rdparty/cpprestsdk/include/cpprest/streams.h b/3rdparty/cpprestsdk/include/cpprest/streams.h new file mode 100644 index 00000000..c35ee611 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/streams.h @@ -0,0 +1,1799 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Asynchronous I/O: streams API, used for formatted input and output, based on unformatted I/O using stream buffers +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#ifndef _CASA_STREAMS_H +#define _CASA_STREAMS_H + +#include "cpprest/astreambuf.h" +#include + +namespace Concurrency { namespace streams +{ + template class basic_ostream; + template class basic_istream; + + namespace details { + template + class basic_ostream_helper + { + public: + basic_ostream_helper(streams::streambuf buffer) : m_buffer(buffer) { } + + ~basic_ostream_helper() { } + + private: + template friend class streams::basic_ostream; + + concurrency::streams::streambuf m_buffer; + }; + + template + class basic_istream_helper + { + public: + basic_istream_helper(streams::streambuf buffer) : m_buffer(buffer) { } + + ~basic_istream_helper() { } + + private: + template friend class streams::basic_istream; + + concurrency::streams::streambuf m_buffer; + }; + + template + struct Value2StringFormatter + { + template + static std::basic_string format(const T &val) + { + std::basic_ostringstream ss; + ss << val; + return ss.str(); + } + }; + + template <> + struct Value2StringFormatter + { + template + static std::basic_string format(const T &val) + { + std::basic_ostringstream ss; + ss << val; + return reinterpret_cast(ss.str().c_str()); + } + + static std::basic_string format(const utf16string &val) + { + return format(utility::conversions::utf16_to_utf8(val)); + } + + }; + + static const char *_in_stream_msg = "stream not set up for input of data"; + static const char *_in_streambuf_msg = "stream buffer not set up for input of data"; + static const char *_out_stream_msg = "stream not set up for output of data"; + static const char *_out_streambuf_msg = "stream buffer not set up for output of data"; + } + + /// + /// Base interface for all asynchronous output streams. + /// + template + class basic_ostream + { + public: + + typedef Concurrency::streams::char_traits traits; + typedef typename traits::int_type int_type; + typedef typename traits::pos_type pos_type; + typedef typename traits::off_type off_type; + + /// + /// Default constructor + /// + basic_ostream() {} + + /// + /// Copy constructor + /// + /// The source object + basic_ostream(const basic_ostream &other) : m_helper(other.m_helper) { } + + /// + /// Assignment operator + /// + /// The source object + /// A reference to the stream object that contains the result of the assignment. + basic_ostream & operator =(const basic_ostream &other) { m_helper = other.m_helper; return *this; } + + /// + /// Constructor + /// + /// A stream buffer. + basic_ostream(streams::streambuf buffer) : + m_helper(std::make_shared>(buffer)) + { + _verify_and_throw(details::_out_streambuf_msg); + } + + /// + /// Close the stream, preventing further write operations. + /// + pplx::task close() const + { + return is_valid() ? + helper()->m_buffer.close(std::ios_base::out) : + pplx::task_from_result(); + } + + /// + /// Close the stream with exception, preventing further write operations. + /// + /// Pointer to the exception. + pplx::task close(std::exception_ptr eptr) const + { + return is_valid() ? + helper()->m_buffer.close(std::ios_base::out, eptr) : + pplx::task_from_result(); + } + + /// + /// Put a single character into the stream. + /// + /// A character + pplx::task write(CharType ch) const + { + pplx::task result; + if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result; + return helper()->m_buffer.putc(ch); + } + + /// + /// Write a single value of "blittable" type T into the stream. + /// + /// A value of type T. + /// + /// This is not a replacement for a proper binary serialization solution, but it may + /// form the foundation for one. Writing data bit-wise to a stream is a primitive + /// operation of binary serialization. + /// Currently, no attention is paid to byte order. All data is written in the platform's + /// native byte order, which means little-endian on all platforms that have been tested. + /// This function is only available for streams using a single-byte character size. + /// + template + CASABLANCA_DEPRECATED("Unsafe API that will be removed in future releases, use one of the other write overloads instead.") + pplx::task write(T value) const + { + static_assert(sizeof(CharType) == 1, "binary write is only supported for single-byte streams"); + static_assert(std::is_trivial::value, "unsafe to use with non-trivial types"); + + pplx::task result; + if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result; + + auto copy = std::make_shared(std::move(value)); + return helper()->m_buffer.putn_nocopy((CharType*)copy.get(), sizeof(T)).then([copy](pplx::task op) -> size_t { return op.get(); }); + } + + /// + /// Write a number of characters from a given stream buffer into the stream. + /// + /// A source stream buffer. + /// The number of characters to write. + pplx::task write(streams::streambuf source, size_t count) const + { + pplx::task result; + if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result; + if ( !source.can_read() ) + return pplx::task_from_exception(std::make_exception_ptr(std::runtime_error("source buffer not set up for input of data"))); + + if (count == 0) + return pplx::task_from_result((size_t)0); + + auto buffer = helper()->m_buffer; + auto data = buffer.alloc(count); + + if ( data != nullptr ) + { + auto post_read = + [buffer](pplx::task op)-> pplx::task + { + auto b = buffer; + b.commit(op.get()); + return op; + }; + return source.getn(data, count).then(post_read); + } + else + { + size_t available = 0; + + const bool acquired = source.acquire(data, available); + if (available >= count) + { + auto post_write = + [source,data](pplx::task op)-> pplx::task + { + auto s = source; + s.release(data,op.get()); + return op; + }; + return buffer.putn_nocopy(data, count).then(post_write); + } + else + { + // Always have to release if acquire returned true. + if(acquired) + { + source.release(data, 0); + } + + std::shared_ptr buf(new CharType[count], [](CharType *buf) { delete [] buf; }); + + auto post_write = + [buf](pplx::task op)-> pplx::task + { + return op; + }; + auto post_read = + [buf,post_write,buffer](pplx::task op) -> pplx::task + { + auto b = buffer; + return b.putn_nocopy(buf.get(), op.get()).then(post_write); + }; + + return source.getn(buf.get(), count).then(post_read); + } + } + } + + /// + /// Write the specified string to the output stream. + /// + /// Input string. + pplx::task print(const std::basic_string& str) const + { + pplx::task result; + if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result; + + if (str.empty()) + { + return pplx::task_from_result(0); + } + else + { + auto sharedStr = std::make_shared>(str); + return helper()->m_buffer.putn_nocopy(sharedStr->c_str(), sharedStr->size()).then([sharedStr](size_t size) { return size; }); + } + } + + /// + /// Write a value of type T to the output stream. + /// + /// + /// The data type of the object to be written to the stream + /// + /// Input object. + template + pplx::task print(const T& val) const + { + pplx::task result; + if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result; + // TODO in the future this could be improved to have Value2StringFormatter avoid another unnecessary copy + // by putting the string on the heap before calling the print string overload. + return print(details::Value2StringFormatter::format(val)); + } + + /// + /// Write a value of type T to the output stream and append a newline character. + /// + /// + /// The data type of the object to be written to the stream + /// + /// Input object. + template + pplx::task print_line(const T& val) const + { + pplx::task result; + if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result; + auto str = details::Value2StringFormatter::format(val); + str.push_back(CharType('\n')); + return print(str); + } + + /// + /// Flush any buffered output data. + /// + pplx::task flush() const + { + pplx::task result; + if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result; + return helper()->m_buffer.sync(); + } + + /// + /// Seeks to the specified write position. + /// + /// An offset relative to the beginning of the stream. + /// The new position in the stream. + pos_type seek(pos_type pos) const + { + _verify_and_throw(details::_out_stream_msg); + return helper()->m_buffer.seekpos(pos, std::ios_base::out); + } + + /// + /// Seeks to the specified write position. + /// + /// An offset relative to the beginning, current write position, or the end of the stream. + /// The starting point (beginning, current, end) for the seek. + /// The new position in the stream. + pos_type seek(off_type off, std::ios_base::seekdir way) const + { + _verify_and_throw(details::_out_stream_msg); + return helper()->m_buffer.seekoff(off, way, std::ios_base::out); + } + + /// + /// Get the current write position, i.e. the offset from the beginning of the stream. + /// + /// The current write position. + pos_type tell() const + { + _verify_and_throw(details::_out_stream_msg); + return helper()->m_buffer.getpos(std::ios_base::out); + } + + /// + /// can_seek is used to determine whether the stream supports seeking. + /// + /// true if the stream supports seeking, false otherwise. + bool can_seek() const { return is_valid() && m_helper->m_buffer.can_seek(); } + + /// + /// Test whether the stream has been initialized with a valid stream buffer. + /// + /// true if the stream has been initialized with a valid stream buffer, false otherwise. + bool is_valid() const { return (m_helper != nullptr) && ((bool)m_helper->m_buffer); } + + /// + /// Test whether the stream has been initialized or not. + /// + operator bool() const { return is_valid(); } + + /// + /// Test whether the stream is open for writing. + /// + /// true if the stream is open for writing, false otherwise. + bool is_open() const { return is_valid() && m_helper->m_buffer.can_write(); } + + /// + /// Get the underlying stream buffer. + /// + /// The underlying stream buffer. + concurrency::streams::streambuf streambuf() const + { + return helper()->m_buffer; + } + + protected: + + void set_helper(std::shared_ptr> helper) + { + m_helper = helper; + } + + private: + + template + bool _verify_and_return_task(const char *msg, pplx::task &tsk) const + { + auto buffer = helper()->m_buffer; + if ( !(buffer.exception() == nullptr) ) + { + tsk = pplx::task_from_exception(buffer.exception()); + return false; + } + if ( !buffer.can_write() ) + { + tsk = pplx::task_from_exception(std::make_exception_ptr(std::runtime_error(msg))); + return false; + } + return true; + } + + void _verify_and_throw(const char *msg) const + { + auto buffer = helper()->m_buffer; + if ( !(buffer.exception() == nullptr) ) + std::rethrow_exception(buffer.exception()); + if ( !buffer.can_write() ) + throw std::runtime_error(msg); + } + + std::shared_ptr> helper() const + { + if ( !m_helper ) + throw std::logic_error("uninitialized stream object"); + return m_helper; + } + + std::shared_ptr> m_helper; + }; + + template + struct _type_parser_integral_traits + { + typedef std::false_type _is_integral; + typedef std::false_type _is_unsigned; + }; + +#ifdef _WIN32 +#define _INT_TRAIT(_t,_low,_high) template<> struct _type_parser_integral_traits<_t>{typedef std::true_type _is_integral;typedef std::false_type _is_unsigned;static const int64_t _min = _low;static const int64_t _max = _high;}; +#define _UINT_TRAIT(_t,_low,_high) template<> struct _type_parser_integral_traits<_t>{typedef std::true_type _is_integral;typedef std::true_type _is_unsigned;static const uint64_t _max = _high;}; + + _INT_TRAIT(char,INT8_MIN,INT8_MAX) + _INT_TRAIT(signed char,INT8_MIN,INT8_MAX) + _INT_TRAIT(short,INT16_MIN,INT16_MAX) + _INT_TRAIT(utf16char,INT16_MIN,INT16_MAX) + _INT_TRAIT(int,INT32_MIN,INT32_MAX) + _INT_TRAIT(long, LONG_MIN, LONG_MAX) + _INT_TRAIT(long long, LLONG_MIN, LLONG_MAX) + _UINT_TRAIT(unsigned char,UINT8_MIN,UINT8_MAX) + _UINT_TRAIT(unsigned short,UINT16_MIN,UINT16_MAX) + _UINT_TRAIT(unsigned int,UINT32_MIN,UINT32_MAX) + _UINT_TRAIT(unsigned long, ULONG_MIN, ULONG_MAX) + _UINT_TRAIT(unsigned long long, ULLONG_MIN, ULLONG_MAX) +#else +#define _INT_TRAIT(_t) template<> struct _type_parser_integral_traits<_t>{typedef std::true_type _is_integral;typedef std::false_type _is_unsigned;static const int64_t _min = std::numeric_limits<_t>::min();static const int64_t _max = (std::numeric_limits<_t>::max)();}; +#define _UINT_TRAIT(_t) template<> struct _type_parser_integral_traits<_t>{typedef std::true_type _is_integral;typedef std::true_type _is_unsigned;static const uint64_t _max = (std::numeric_limits<_t>::max)();}; + + _INT_TRAIT(char) + _INT_TRAIT(signed char) + _INT_TRAIT(short) + _INT_TRAIT(utf16char) + _INT_TRAIT(int) + _INT_TRAIT(long) + _INT_TRAIT(long long) + _UINT_TRAIT(unsigned char) + _UINT_TRAIT(unsigned short) + _UINT_TRAIT(unsigned int) + _UINT_TRAIT(unsigned long) + _UINT_TRAIT(unsigned long long) +#endif + + template + class _type_parser_base + { + public: + typedef typename ::concurrency::streams::char_traits::int_type int_type; + + _type_parser_base() { } + + protected: + // Aid in parsing input: skipping whitespace characters. + static pplx::task _skip_whitespace(streams::streambuf buffer); + + // Aid in parsing input: peek at a character at a time, call type-specific code to examine, extract value when done. + // AcceptFunctor should model std::function, int_type)> + // ExtractFunctor should model std::function(std::shared_ptr)> + template + static pplx::task _parse_input(streams::streambuf buffer, AcceptFunctor accept_character, ExtractFunctor extract); + }; + + /// + /// Class used to handle asychronous parsing for basic_istream::extract. To support new + /// types create a new template specialization and implement the parse function. + /// + template + class type_parser + { + public: + static pplx::task parse(streams::streambuf buffer) + { + typename _type_parser_integral_traits::_is_integral ii; + typename _type_parser_integral_traits::_is_unsigned ui; + return _parse(buffer, ii, ui); + } + private: + static pplx::task _parse(streams::streambuf buffer, std::false_type, std::false_type) + { + _parse_floating_point(buffer); + } + + static pplx::task _parse(streams::streambuf, std::false_type, std::true_type) + { +#ifdef _WIN32 + static_assert(false, "type is not supported for extraction from a stream"); +#else + throw std::runtime_error("type is not supported for extraction from a stream"); +#endif + } + + static pplx::task _parse(streams::streambuf buffer, std::true_type, std::false_type) + { + return type_parser::parse(buffer).then( + [] (pplx::task op) -> T + { + int64_t val = op.get(); + if ( val <= _type_parser_integral_traits::_max && val >= _type_parser_integral_traits::_min ) + return (T)val; + else + throw std::range_error("input out of range for target type"); + }); + } + + static pplx::task _parse(streams::streambuf buffer, std::true_type, std::true_type) + { + return type_parser::parse(buffer).then( + [] (pplx::task op) -> T + { + uint64_t val = op.get(); + if ( val <= _type_parser_integral_traits::_max ) + return (T)val; + else + throw std::range_error("input out of range for target type"); + }); + } + }; + + /// + /// Base interface for all asynchronous input streams. + /// + template + class basic_istream + { + public: + + typedef char_traits traits; + typedef typename char_traits::int_type int_type; + typedef typename traits::pos_type pos_type; + typedef typename traits::off_type off_type; + + + /// + /// Default constructor + /// + basic_istream() {} + + /// + /// Constructor + /// + /// + /// The data type of the basic element of the stream. + /// + /// A stream buffer. + basic_istream(streams::streambuf buffer) : m_helper(std::make_shared>(buffer)) + { + _verify_and_throw(details::_in_streambuf_msg); + } + + /// + /// Copy constructor + /// + /// The source object + basic_istream(const basic_istream &other) : m_helper(other.m_helper) { } + + /// + /// Assignment operator + /// + /// The source object + /// A reference to the stream object that contains the result of the assignment. + basic_istream & operator =(const basic_istream &other) + { + m_helper = other.m_helper; + return *this; + } + + /// + /// Close the stream, preventing further read operations. + /// + pplx::task close() const + { + return is_valid() ? + helper()->m_buffer.close(std::ios_base::in) : + pplx::task_from_result(); + } + + /// + /// Close the stream with exception, preventing further read operations. + /// + /// Pointer to the exception. + pplx::task close(std::exception_ptr eptr) const + { + return is_valid() ? + m_helper->m_buffer.close(std::ios_base::in, eptr) : + pplx::task_from_result(); + } + + /// + /// Tests whether last read cause the stream reach EOF. + /// + /// True if the read head has reached the end of the stream, false otherwise. + bool is_eof() const + { + return is_valid() ? m_helper->m_buffer.is_eof() : false; + } + + /// + /// Get the next character and return it as an int_type. Advance the read position. + /// + /// A task that holds the next character as an int_type on successful completion. + pplx::task read() const + { + pplx::task result; + if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result; + return helper()->m_buffer.bumpc(); + } + + /// + /// Read a single value of "blittable" type T from the stream. + /// + /// A value of type T. + /// + /// This is not a replacement for a proper binary serialization solution, but it may + /// form the foundation for one. Reading data bit-wise to a stream is a primitive + /// operation of binary serialization. + /// Currently, no attention is paid to byte order. All data is read in the platform's + /// native byte order, which means little-endian on all platforms that have been tested. + /// This function is only available for streams using a single-byte character size. + /// + template + CASABLANCA_DEPRECATED("Unsafe API that will be removed in future releases, use one of the other read overloads instead.") + pplx::task read() const + { + static_assert(sizeof(CharType) == 1, "binary read is only supported for single-byte streams"); + static_assert(std::is_trivial::value, "unsafe to use with non-trivial types"); + + pplx::task result; + if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result; + + auto copy = std::make_shared(); + return helper()->m_buffer.getn((CharType*)copy.get(), sizeof(T)).then([copy](pplx::task op) -> T + { + return std::move(*copy); + }); + } + + /// + /// Reads up to count characters and place into the provided buffer. + /// + /// An async stream buffer supporting write operations. + /// The maximum number of characters to read + /// A task that holds the number of characters read. This number is 0 if the end of the stream is reached. + pplx::task read(streams::streambuf target, size_t count) const + { + pplx::task result; + if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result; + if ( !target.can_write() ) + return pplx::task_from_exception(std::make_exception_ptr(std::runtime_error("target not set up for output of data"))); + + // Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations. + auto buffer = helper()->m_buffer; + + auto data = target.alloc(count); + + if ( data != nullptr ) + { + auto post_read = + [target](pplx::task op)-> pplx::task + { + auto t = target; + t.commit(op.get()); + return op; + }; + return buffer.getn(data, count).then(post_read); + } + else + { + size_t available = 0; + + const bool acquired = buffer.acquire(data, available); + if (available >= count) + { + auto post_write = + [buffer,data](pplx::task op)-> pplx::task + { + auto b = buffer; + b.release(data, op.get()); + return op; + }; + return target.putn_nocopy(data, count).then(post_write); + } + else + { + // Always have to release if acquire returned true. + if(acquired) + { + buffer.release(data, 0); + } + + std::shared_ptr buf(new CharType[count], [](CharType *buf) { delete [] buf; }); + + auto post_write = + [buf](pplx::task op) -> pplx::task + { + return op; + }; + auto post_read = + [buf,target,post_write](pplx::task op) -> pplx::task + { + auto trg = target; + return trg.putn_nocopy(buf.get(), op.get()).then(post_write); + }; + + return helper()->m_buffer.getn(buf.get(), count).then(post_read); + } + } + } + + /// + /// Get the next character and return it as an int_type. Do not advance the read position. + /// + /// A task that holds the character, widened to an integer. This character is EOF when the peek operation fails. + pplx::task peek() const + { + pplx::task result; + if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result; + return helper()->m_buffer.getc(); + } + + /// + /// Read characters until a delimiter or EOF is found, and place them into the target. + /// Proceed past the delimiter, but don't include it in the target buffer. + /// + /// An async stream buffer supporting write operations. + /// The delimiting character to stop the read at. + /// A task that holds the number of characters read. + pplx::task read_to_delim(streams::streambuf target, int_type delim) const + { + pplx::task result; + if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result; + if ( !target.can_write() ) + return pplx::task_from_exception(std::make_exception_ptr(std::runtime_error("target not set up for output of data"))); + + // Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations. + auto buffer = helper()->m_buffer; + + int_type req_async = ::concurrency::streams::char_traits::requires_async(); + + std::shared_ptr<_read_helper> _locals = std::make_shared<_read_helper>(); + + auto flush = [=]() mutable + { + return target.putn_nocopy(_locals->outbuf, _locals->write_pos).then([=](size_t wrote) mutable + { + _locals->total += wrote; + _locals->write_pos = 0; + return target.sync(); + }); + }; + + auto update = [=](int_type ch) mutable + { + if (ch == ::concurrency::streams::char_traits::eof()) return false; + if (ch == delim) return false; + + _locals->outbuf[_locals->write_pos] = static_cast(ch); + _locals->write_pos += 1; + + if (_locals->is_full()) + { + // Flushing synchronously because performance is terrible if we + // schedule an empty task. This isn't on a user's thread. + flush().get(); + } + + return true; + }; + + auto loop = pplx::details::do_while([=]() mutable -> pplx::task + { + while (buffer.in_avail() > 0) + { + int_type ch = buffer.sbumpc(); + + if (ch == req_async) + { + break; + } + + if (!update(ch)) + { + return pplx::task_from_result(false); + } + } + return buffer.bumpc().then(update); + }); + + return loop.then([=](bool) mutable + { + return flush().then([=] { return _locals->total; }); + }); + } + + /// + /// Read until reaching a newline character. The newline is not included in the target. + /// + /// An asynchronous stream buffer supporting write operations. + /// A task that holds the number of characters read. This number is 0 if the end of the stream is reached. + pplx::task read_line(streams::streambuf target) const + { + pplx::task result; + if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result; + if ( !target.can_write() ) + return pplx::task_from_exception(std::make_exception_ptr(std::runtime_error("target not set up for receiving data"))); + + // Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations. + concurrency::streams::streambuf buffer = helper()->m_buffer; + + typename concurrency::streams::char_traits::int_type req_async = concurrency::streams::char_traits::requires_async(); + + std::shared_ptr<_read_helper> _locals = std::make_shared<_read_helper>(); + + auto flush = [=]() mutable + { + return target.putn_nocopy(_locals->outbuf, _locals->write_pos).then([=](size_t wrote) mutable + { + _locals->total += wrote; + _locals->write_pos = 0; + return target.sync(); + }); + }; + + auto update = [=](typename concurrency::streams::char_traits::int_type ch) mutable + { + if (ch == concurrency::streams::char_traits::eof()) return false; + if (ch == '\n') return false; + if (ch == '\r') + { + _locals->saw_CR = true; + return true; + } + + _locals->outbuf[_locals->write_pos] = static_cast(ch); + _locals->write_pos += 1; + + if (_locals->is_full()) + { + // Flushing synchronously because performance is terrible if we + // schedule an empty task. This isn't on a user's thread. + flush().wait(); + } + + return true; + }; + + auto update_after_cr = [=] (typename concurrency::streams::char_traits::int_type ch) mutable -> pplx::task + { + if (ch == concurrency::streams::char_traits::eof()) return pplx::task_from_result(false); + if (ch == '\n') + { + return buffer.bumpc().then([]( +#ifndef _WIN32 // Required by GCC + typename +#endif + concurrency::streams::char_traits::int_type) { return false; }); + } + return pplx::task_from_result(false); + }; + + auto loop = pplx::details::do_while([=]() mutable -> pplx::task + { + while ( buffer.in_avail() > 0 ) + { +#ifndef _WIN32 // Required by GCC, because concurrency::streams::char_traits is a dependent scope + typename +#endif + concurrency::streams::char_traits::int_type ch; + + if (_locals->saw_CR) + { + ch = buffer.sgetc(); + if (ch == '\n') + buffer.sbumpc(); + return pplx::task_from_result(false); + } + + ch = buffer.sbumpc(); + + if (ch == req_async) + break; + + if (!update(ch)) + { + return pplx::task_from_result(false); + } + } + + if (_locals->saw_CR) + { + return buffer.getc().then(update_after_cr); + } + return buffer.bumpc().then(update); + }); + + return loop.then([=](bool) mutable + { + return flush().then([=] { return _locals->total; }); + }); + } + + /// + /// Read until reaching the end of the stream. + /// + /// An asynchronous stream buffer supporting write operations. + /// The number of characters read. + pplx::task read_to_end(streams::streambuf target) const + { + pplx::task result; + if ( !_verify_and_return_task("stream not set up for output of data", result) ) return result; + if ( !target.can_write() ) + return pplx::task_from_exception(std::make_exception_ptr(std::runtime_error("source buffer not set up for input of data"))); + + auto l_buffer = helper()->m_buffer; + auto l_buf_size = this->buf_size; + std::shared_ptr<_read_helper> l_locals = std::make_shared<_read_helper>(); + + auto copy_to_target = [l_locals, target, l_buffer, l_buf_size]() mutable -> pplx::task + { + // We need to capture these, because the object itself may go away + // before we're done processing the data. + //auto locs = _locals; + //auto trg = target; + + return l_buffer.getn(l_locals->outbuf, l_buf_size).then([=](size_t rd) mutable -> pplx::task + { + if (rd == 0) + return pplx::task_from_result(false); + + // Must be nested to capture rd + return target.putn_nocopy(l_locals->outbuf, rd).then([target, l_locals, rd](size_t wr) mutable -> pplx::task + { + l_locals->total += wr; + + if (rd != wr) + // Number of bytes written is less than number of bytes received. + throw std::runtime_error("failed to write all bytes"); + + return target.sync().then([]() { return true; }); + }); + }); + }; + + auto loop = pplx::details::do_while(copy_to_target); + + return loop.then([=](bool) mutable -> size_t + { + return l_locals->total; + }); + } + + /// + /// Seeks to the specified write position. + /// + /// An offset relative to the beginning of the stream. + /// The new position in the stream. + pos_type seek(pos_type pos) const + { + _verify_and_throw(details::_in_stream_msg); + return helper()->m_buffer.seekpos(pos, std::ios_base::in); + } + + /// + /// Seeks to the specified write position. + /// + /// An offset relative to the beginning, current write position, or the end of the stream. + /// The starting point (beginning, current, end) for the seek. + /// The new position in the stream. + pos_type seek(off_type off, std::ios_base::seekdir way) const + { + _verify_and_throw(details::_in_stream_msg); + return helper()->m_buffer.seekoff(off, way, std::ios_base::in); + } + + /// + /// Get the current write position, i.e. the offset from the beginning of the stream. + /// + /// The current write position. + pos_type tell() const + { + _verify_and_throw(details::_in_stream_msg); + return helper()->m_buffer.getpos(std::ios_base::in); + } + + /// + /// can_seek is used to determine whether the stream supports seeking. + /// + /// true if the stream supports seeking, false otherwise. + bool can_seek() const { return is_valid() && m_helper->m_buffer.can_seek(); } + + /// + /// Test whether the stream has been initialized with a valid stream buffer. + /// + bool is_valid() const { return (m_helper != nullptr) && ((bool)m_helper->m_buffer); } + + /// + /// Test whether the stream has been initialized or not. + /// + operator bool() const { return is_valid(); } + + /// + /// Test whether the stream is open for writing. + /// + /// true if the stream is open for writing, false otherwise. + bool is_open() const { return is_valid() && m_helper->m_buffer.can_read(); } + + /// + /// Get the underlying stream buffer. + /// + concurrency::streams::streambuf streambuf() const + { + return helper()->m_buffer; + } + + /// + /// Read a value of type T from the stream. + /// + /// + /// Supports the C++ primitive types. Can be expanded to additional types + /// by adding template specializations for type_parser. + /// + /// + /// The data type of the element to be read from the stream. + /// + /// A task that holds the element read from the stream. + template + pplx::task extract() const + { + pplx::task result; + if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result; + return type_parser::parse(helper()->m_buffer); + } + + private: + + template + bool _verify_and_return_task(const char *msg, pplx::task &tsk) const + { + auto buffer = helper()->m_buffer; + if ( !(buffer.exception() == nullptr) ) + { + tsk = pplx::task_from_exception(buffer.exception()); + return false; + } + if ( !buffer.can_read() ) + { + tsk = pplx::task_from_exception(std::make_exception_ptr(std::runtime_error(msg))); + return false; + } + return true; + } + + void _verify_and_throw(const char *msg) const + { + auto buffer = helper()->m_buffer; + if ( !(buffer.exception() == nullptr) ) + std::rethrow_exception(buffer.exception()); + if ( !buffer.can_read() ) + throw std::runtime_error(msg); + } + + std::shared_ptr> helper() const + { + if ( !m_helper ) + throw std::logic_error("uninitialized stream object"); + return m_helper; + } + + static const size_t buf_size = 16*1024; + + struct _read_helper + { + size_t total; + CharType outbuf[buf_size]; + size_t write_pos; + bool saw_CR; + + bool is_full() const + { + return write_pos == buf_size; + } + + _read_helper() : total(0), write_pos(0), saw_CR(false) + { + } + }; + + std::shared_ptr> m_helper; + }; + + typedef basic_ostream ostream; + typedef basic_istream istream; + + typedef basic_ostream wostream; + typedef basic_istream wistream; + +template +pplx::task concurrency::streams::_type_parser_base::_skip_whitespace(streams::streambuf buffer) +{ + int_type req_async = concurrency::streams::char_traits::requires_async(); + + auto update = [=] (int_type ch) mutable + { + if (isspace(ch)) + { + if (buffer.sbumpc() == req_async) + { + // Synchronously because performance is terrible if we + // schedule an empty task. This isn't on a user's thread. + buffer.nextc().wait(); + } + return true; + } + + return false; + }; + + auto loop = pplx::details::do_while([=]() mutable -> pplx::task + { + while (buffer.in_avail() > 0) + { + int_type ch = buffer.sgetc(); + + if (ch == req_async) + break; + + if (!update(ch)) + { + return pplx::task_from_result(false); + } + } + return buffer.getc().then(update); + }); + + return loop.then([=](pplx::task op) + { + op.wait(); + }); +} + +template +template +pplx::task concurrency::streams::_type_parser_base::_parse_input( + concurrency::streams::streambuf buffer, + AcceptFunctor accept_character, + ExtractFunctor extract) +{ + std::shared_ptr state = std::make_shared(); + + auto update = [=] (pplx::task op) -> pplx::task + { + int_type ch = op.get(); + if (ch == concurrency::streams::char_traits::eof()) return pplx::task_from_result(false); + bool accptd = accept_character(state, ch); + if (!accptd) + return pplx::task_from_result(false); + // We peeked earlier, so now we must advance the position. + concurrency::streams::streambuf buf = buffer; + return buf.bumpc().then([](int_type) { return true; }); + }; + + auto peek_char = [=]() -> pplx::task + { + concurrency::streams::streambuf buf = buffer; + + // If task results are immediately available, there's little need to use ".then()," + // so optimize for prompt values. + + auto get_op = buf.getc(); + while (get_op.is_done()) + { + auto condition = update(get_op); + if (!condition.is_done() || !condition.get()) + return condition; + + get_op = buf.getc(); + } + + return get_op.then(update); + }; + + auto finish = + [=](pplx::task op) -> pplx::task + { + op.wait(); + pplx::task result = extract(state); + return result; + }; + + return _skip_whitespace(buffer).then([=](pplx::task op) -> pplx::task + { + op.wait(); + return pplx::details::do_while(peek_char).then(finish); + }); +} + +template +class type_parser> : public _type_parser_base +{ + typedef typename _type_parser_base::int_type int_type; +public: + static pplx::task parse(streams::streambuf buffer) + { + return concurrency::streams::_type_parser_base::template _parse_input, std::string>(buffer, _accept_char, _extract_result); + } + +private: + static bool _accept_char(std::shared_ptr> state, int_type ch) + { + if ( ch == concurrency::streams::char_traits::eof() || isspace(ch)) return false; + state->push_back(CharType(ch)); + return true; + } + static pplx::task> _extract_result(std::shared_ptr> state) + { + return pplx::task_from_result(*state); + } +}; + +template +class type_parser : public _type_parser_base +{ +public: + typedef typename _type_parser_base::int_type int_type; + static pplx::task parse(streams::streambuf buffer) + { + return _type_parser_base::template _parse_input<_int64_state, int64_t>(buffer, _accept_char, _extract_result); + } +private: + struct _int64_state + { + _int64_state() : result(0), correct(false), minus(0) {} + + int64_t result; + bool correct; + char minus; // 0 -- no sign, 1 -- plus, 2 -- minus + }; + + static bool _accept_char(std::shared_ptr<_int64_state> state, int_type ch) + { + if ( ch == concurrency::streams::char_traits::eof()) return false; + if ( state->minus == 0 ) + { + // OK to find a sign. + if ( !::isdigit(ch) && ch != int_type('+') && ch != int_type('-') ) + return false; + } + else + { + if ( !::isdigit(ch) ) return false; + } + + // At least one digit was found. + state->correct = true; + + if ( ch == int_type('+') ) + { + state->minus = 1; + } + else if ( ch == int_type('-') ) + { + state->minus = 2; + } + else + { + if (state->minus == 0) state->minus = 1; + + // Shift the existing value by 10, then add the new value. + bool positive = state->result >= 0; + + state->result *= 10; + state->result += int64_t(ch-int_type('0')); + + if ( (state->result >= 0) != positive ) + { + state->correct = false; + return false; + } + } + return true; + } + + static pplx::task _extract_result(std::shared_ptr<_int64_state> state) + { + if (!state->correct) + throw std::range_error("integer value is too large to fit in 64 bits"); + + int64_t result = (state->minus == 2) ? -state->result : state->result; + return pplx::task_from_result(result); + } +}; + +template +struct _double_state +{ + _double_state() : result(0), minus(0), after_comma(0), exponent(false), exponent_number(0), exponent_minus(0), complete(false), p_exception_string() {} + + FloatingPoint result; + char minus; // 0 -- no sign, 1 -- plus, 2 -- minus + int after_comma; + bool exponent; + int exponent_number; + char exponent_minus; // 0 -- no sign, 1 -- plus, 2 -- minus + bool complete; + std::string p_exception_string; +}; + +template +static std::string create_exception_message(int_type ch, bool exponent) +{ + std::ostringstream os; + os << "Invalid character '" << char(ch) << "'" << (exponent ? " in exponent" : ""); + return os.str(); +} + +template +static bool _accept_char(std::shared_ptr<_double_state> state, int_type ch) +{ + if ( state->minus == 0 ) + { + if ( !::isdigit(ch) && ch != int_type('.') && ch != int_type('+') && ch != int_type('-') ) + { + if (!state->complete) + state->p_exception_string = create_exception_message(ch, false); + return false; + } + } + else + { + if (!state->exponent && !::isdigit(ch) && ch != int_type('.') && ch != int_type('E') && ch != int_type('e')) + { + if (!state->complete) + state->p_exception_string = create_exception_message(ch, false); + return false; + } + + if (state->exponent && !::isdigit(ch) && ch != int_type('+') && ch != int_type('-')) + { + if (!state->complete) + state->p_exception_string = create_exception_message(ch, true); + return false; + } + } + + switch (ch) + { + case int_type('+') : + state->complete = false; + if (state->exponent) + { + if (state->exponent_minus != 0) + { + state->p_exception_string = "The exponent sign already set"; + return false; + } + state->exponent_minus = 1; + } + else + { + state->minus = 1; + } + break; + case int_type('-') : + state->complete = false; + if (state->exponent) + { + if (state->exponent_minus != 0) + { + state->p_exception_string = "The exponent sign already set"; + return false; + } + + state->exponent_minus = 2; + } + else + { + state->minus = 2; + } + break; + case int_type('.') : + state->complete = false; + if (state->after_comma > 0) + return false; + + state->after_comma = 1; + break; + case int_type('E') : case int_type('e') : + state->complete = false; + if (state->exponent) + return false; + state->exponent_number = 0; + state->exponent = true; + break; + default: + state->complete = true; + if (!state->exponent) + { + if (state->minus == 0) + state->minus = 1; + + state->result *= 10; + state->result += int64_t(ch-int_type('0')); + + if (state->after_comma > 0) + state->after_comma++; + } + else + { + if (state->exponent_minus == 0) state->exponent_minus = 1; + state->exponent_number *= 10; + state->exponent_number += int64_t(ch-int_type('0')); + } + } + return true; +} + +template +static pplx::task _extract_result(std::shared_ptr<_double_state> state) +{ + if (state->p_exception_string.length() > 0) + throw std::runtime_error(state->p_exception_string.c_str()); + + if (!state->complete && state->exponent) + throw std::runtime_error("Incomplete exponent"); + + FloatingPoint result = static_cast((state->minus == 2) ? -state->result : state->result); + if (state->exponent_minus == 2) + state->exponent_number = 0 - state->exponent_number; + + if (state->after_comma > 0) + state->exponent_number -= state->after_comma-1; + + if (state->exponent_number >= 0) + { + result *= pow(FloatingPoint(10.0), state->exponent_number); + + #pragma push_macro ("max") + #undef max + + if (result > std::numeric_limits::max() || result < -std::numeric_limits::max()) + throw std::overflow_error("The value is too big"); + #pragma pop_macro ("max") + } + else + { + bool is_zero = (result == 0); + + result /= pow(FloatingPoint(10.0), -state->exponent_number); + + if (!is_zero && + result > -std::numeric_limits::denorm_min() && + result < std::numeric_limits::denorm_min()) + throw std::underflow_error("The value is too small"); + } + + return pplx::task_from_result(result); +} + +template +class type_parser : public _type_parser_base +{ +public: + typedef typename _type_parser_base::int_type int_type; + static pplx::task parse(streams::streambuf buffer) + { + return _type_parser_base::template _parse_input<_double_state, double>(buffer, _accept_char, _extract_result); + } +protected: +}; + +template +class type_parser : public _type_parser_base +{ +public: + typedef typename _type_parser_base::int_type int_type; + static pplx::task parse(streams::streambuf buffer) + { + return _type_parser_base::template _parse_input<_double_state, float>(buffer, _accept_char, _extract_result); + } +protected: +}; + + +template +class type_parser : public _type_parser_base +{ +public: + typedef typename _type_parser_base::int_type int_type; + static pplx::task parse(streams::streambuf buffer) + { + return _type_parser_base::template _parse_input<_uint64_state,uint64_t>(buffer, _accept_char, _extract_result); + } + +private: + struct _uint64_state + { + _uint64_state() : result(0), correct(false) {} + uint64_t result; + bool correct; + }; + + static bool _accept_char(std::shared_ptr<_uint64_state> state, int_type ch) + { + if ( !::isdigit(ch) ) return false; + + // At least one digit was found. + state->correct = true; + + // Shift the existing value by 10, then add the new value. + state->result *= 10; + state->result += uint64_t(ch-int_type('0')); + + return true; + } + + static pplx::task _extract_result(std::shared_ptr<_uint64_state> state) + { + if (!state->correct) + throw std::range_error("integer value is too large to fit in 64 bits"); + return pplx::task_from_result(state->result); + } +}; + +template +class type_parser : public _type_parser_base +{ +public: + typedef typename _type_parser_base::int_type int_type; + static pplx::task parse(streams::streambuf buffer) + { + return _type_parser_base::template _parse_input<_bool_state,bool>(buffer, _accept_char, _extract_result); + } +private: + struct _bool_state + { + _bool_state() : state(0) { } + // { 0 -- not started, 1 -- 't', 2 -- 'tr', 3 -- 'tru', 4 -- 'f', 5 -- 'fa', 6 -- 'fal', 7 -- 'fals', 8 -- 'true', 9 -- 'false' } + short state; + }; + + static bool _accept_char(std::shared_ptr<_bool_state> state, int_type ch) + { + switch (state->state) + { + case 0: + if ( ch == int_type('t') ) state->state = 1; + else if ( ch == int_type('f') ) state->state = 4; + else if ( ch == int_type('1') ) state->state = 8; + else if ( ch == int_type('0') ) state->state = 9; + else return false; + break; + case 1: + if ( ch == int_type('r') ) state->state = 2; + else return false; + break; + case 2: + if ( ch == int_type('u') ) state->state = 3; + else return false; + break; + case 3: + if ( ch == int_type('e') ) state->state = 8; + else return false; + break; + case 4: + if ( ch == int_type('a') ) state->state = 5; + else return false; + break; + case 5: + if ( ch == int_type('l') ) state->state = 6; + else return false; + break; + case 6: + if ( ch == int_type('s') ) state->state = 7; + else return false; + break; + case 7: + if ( ch == int_type('e') ) state->state = 9; + else return false; + break; + case 8: + case 9: + return false; + } + return true; + } + static pplx::task _extract_result(std::shared_ptr<_bool_state> state) + { + bool correct = (state->state == 8 || state->state == 9); + if (!correct) + { + std::runtime_error exc("cannot parse as Boolean value"); + throw exc; + } + return pplx::task_from_result(state->state == 8); + } +}; + +template +class type_parser : public _type_parser_base +{ + typedef typename concurrency::streams::streambuf::int_type int_type; +public: + static pplx::task parse(streams::streambuf buffer) + { + return _type_parser_base::_skip_whitespace(buffer).then( + [=](pplx::task op) -> pplx::task + { + op.wait(); + return type_parser::_get_char(buffer); + }); + } +private: + static pplx::task _get_char(streams::streambuf buffer) + { + concurrency::streams::streambuf buf = buffer; + return buf.bumpc().then( + [=](pplx::task::int_type> op) -> signed char + { + int_type val = op.get(); + if (val == concurrency::streams::char_traits::eof()) + throw std::runtime_error("reached end-of-stream while constructing a value"); + return static_cast(val); + }); + } +}; + +template +class type_parser : public _type_parser_base +{ + typedef typename concurrency::streams::streambuf::int_type int_type; +public: + static pplx::task parse(streams::streambuf buffer) + { + return _type_parser_base::_skip_whitespace(buffer).then( + [=](pplx::task op) -> pplx::task + { + op.wait(); + return type_parser::_get_char(buffer); + }); + } +private: + static pplx::task _get_char(streams::streambuf buffer) + { + concurrency::streams::streambuf buf = buffer; + return buf.bumpc().then( + [=](pplx::task::int_type> op) -> unsigned char + { + int_type val = op.get(); + if (val == concurrency::streams::char_traits::eof()) + throw std::runtime_error("reached end-of-stream while constructing a value"); + return static_cast(val); + }); + } +}; + +template +class type_parser : public _type_parser_base +{ + typedef typename concurrency::streams::streambuf::int_type int_type; +public: + static pplx::task parse(streams::streambuf buffer) + { + return _type_parser_base::_skip_whitespace(buffer).then( + [=](pplx::task op) -> pplx::task + { + op.wait(); + return _get_char(buffer); + }); + } +private: + static pplx::task _get_char(streams::streambuf buffer) + { + concurrency::streams::streambuf buf = buffer; + return buf.bumpc().then( + [=](pplx::task::int_type> op) -> char + { + int_type val = op.get(); + if (val == concurrency::streams::char_traits::eof()) + throw std::runtime_error("reached end-of-stream while constructing a value"); + return char(val); + }); + } +}; + +#ifdef _WIN32 +template<> +class type_parser> : public _type_parser_base +{ +public: + static pplx::task parse(streams::streambuf buffer) + { + return _parse_input,std::basic_string>(buffer, _accept_char, _extract_result); + } + +private: + static bool _accept_char(const std::shared_ptr> &state, int_type ch) + { + if ( ch == concurrency::streams::char_traits::eof() || isspace(ch)) return false; + state->push_back(char(ch)); + return true; + } + static pplx::task> _extract_result(std::shared_ptr> state) + { + return pplx::task_from_result(utility::conversions::utf8_to_utf16(*state)); + } +}; + +template<> +class type_parser> : public _type_parser_base +{ +public: + static pplx::task parse(streams::streambuf buffer) + { + return _parse_input,std::basic_string>(buffer, _accept_char, _extract_result); + } + +private: + static bool _accept_char(const std::shared_ptr> &state, int_type ch) + { + if ( ch == concurrency::streams::char_traits::eof() || isspace(ch)) return false; + state->push_back(char(ch)); + return true; + } + static pplx::task> _extract_result(std::shared_ptr> state) + { + return pplx::task_from_result(utility::conversions::utf8_to_utf16(*state)); + } +}; + +template<> +class type_parser> : public _type_parser_base +{ +public: + static pplx::task parse(streams::streambuf buffer) + { + return _parse_input,std::basic_string>(buffer, _accept_char, _extract_result); + } + +private: + static bool _accept_char(const std::shared_ptr> &state, int_type ch) + { + if ( ch == concurrency::streams::char_traits::eof() || isspace(ch)) return false; + state->push_back(char(ch)); + return true; + } + static pplx::task> _extract_result(std::shared_ptr> state) + { + return pplx::task_from_result(utility::conversions::utf8_to_utf16(*state)); + } +}; +#endif //_WIN32 + +}} + +#endif diff --git a/3rdparty/cpprestsdk/include/cpprest/uri.h b/3rdparty/cpprestsdk/include/cpprest/uri.h new file mode 100644 index 00000000..a7224cc5 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/uri.h @@ -0,0 +1,35 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Protocol independent support for URIs. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#ifndef _CASA_URI_H +#define _CASA_URI_H + +#include "cpprest/base_uri.h" +#include "cpprest/uri_builder.h" + +#endif + + diff --git a/3rdparty/cpprestsdk/include/cpprest/uri_builder.h b/3rdparty/cpprestsdk/include/cpprest/uri_builder.h new file mode 100644 index 00000000..67ffce3a --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/uri_builder.h @@ -0,0 +1,266 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Builder style class for creating URIs. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include +#include +#include + +#include "cpprest/base_uri.h" + +namespace web +{ + /// + /// Builder for constructing URIs incrementally. + /// + class uri_builder + { + public: + + /// + /// Creates a builder with an initially empty URI. + /// + uri_builder() {} + + /// + /// Creates a builder with a existing URI object. + /// + /// Encoded string containing the URI. + uri_builder(const uri &uri_str): m_uri(uri_str.m_components) {} + + /// + /// Get the scheme component of the URI as an encoded string. + /// + /// The URI scheme as a string. + const utility::string_t &scheme() const { return m_uri.m_scheme; } + + /// + /// Get the user information component of the URI as an encoded string. + /// + /// The URI user information as a string. + const utility::string_t &user_info() const { return m_uri.m_user_info; } + + /// + /// Get the host component of the URI as an encoded string. + /// + /// The URI host as a string. + const utility::string_t &host() const { return m_uri.m_host; } + + /// + /// Get the port component of the URI. Returns -1 if no port is specified. + /// + /// The URI port as an integer. + int port() const { return m_uri.m_port; } + + /// + /// Get the path component of the URI as an encoded string. + /// + /// The URI path as a string. + const utility::string_t &path() const { return m_uri.m_path; } + + /// + /// Get the query component of the URI as an encoded string. + /// + /// The URI query as a string. + const utility::string_t &query() const { return m_uri.m_query; } + + /// + /// Get the fragment component of the URI as an encoded string. + /// + /// The URI fragment as a string. + const utility::string_t &fragment() const { return m_uri.m_fragment; } + + /// + /// Set the scheme of the URI. + /// + /// Uri scheme. + /// A reference to this uri_builder to support chaining. + uri_builder & set_scheme(const utility::string_t &scheme) + { + m_uri.m_scheme = scheme; + return *this; + } + + /// + /// Set the user info component of the URI. + /// + /// User info as a decoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + uri_builder & set_user_info(const utility::string_t &user_info, bool do_encoding = false) + { + m_uri.m_user_info = do_encoding ? uri::encode_uri(user_info, uri::components::user_info) : user_info; + return *this; + } + + /// + /// Set the host component of the URI. + /// + /// Host as a decoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + uri_builder & set_host(const utility::string_t &host, bool do_encoding = false) + { + m_uri.m_host = do_encoding ? uri::encode_uri(host, uri::components::host) : host; + return *this; + } + + /// + /// Set the port component of the URI. + /// + /// Port as an integer. + /// A reference to this uri_builder to support chaining. + uri_builder & set_port(int port) + { + m_uri.m_port = port; + return *this; + } + + /// + /// Set the port component of the URI. + /// + /// Port as a string. + /// A reference to this uri_builder to support chaining. + /// When string can't be converted to an integer the port is left unchanged. + uri_builder & set_port(const utility::string_t &port) + { + utility::istringstream_t portStream(port); + int port_tmp; + portStream >> port_tmp; + if(portStream.fail() || portStream.bad()) + { + throw std::invalid_argument("invalid port argument, must be non empty string containing integer value"); + } + m_uri.m_port = port_tmp; + return *this; + } + + /// + /// Set the path component of the URI. + /// + /// Path as a decoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + uri_builder & set_path(const utility::string_t &path, bool do_encoding = false) + { + m_uri.m_path = do_encoding ? uri::encode_uri(path, uri::components::path) : path; + return *this; + } + + + /// + /// Set the query component of the URI. + /// + /// Query as a decoded string. + /// Specify whether apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + uri_builder & set_query(const utility::string_t &query, bool do_encoding = false) + { + m_uri.m_query = do_encoding ? uri::encode_uri(query, uri::components::query) : query; + return *this; + } + + /// + /// Set the fragment component of the URI. + /// + /// Fragment as a decoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + uri_builder & set_fragment(const utility::string_t &fragment, bool do_encoding = false) + { + m_uri.m_fragment = do_encoding ? uri::encode_uri(fragment, uri::components::fragment) : fragment; + return *this; + } + + /// + /// Clears all components of the underlying URI in this uri_builder. + /// + void clear() + { + m_uri = details::uri_components(); + } + + /// + /// Appends another path to the path of this uri_builder. + /// + /// Path to append as a already encoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + _ASYNCRTIMP uri_builder &append_path(const utility::string_t &path, bool do_encoding = false); + + /// + /// Appends another query to the query of this uri_builder. + /// + /// Query to append as a decoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + _ASYNCRTIMP uri_builder &append_query(const utility::string_t &query, bool do_encoding = false); + + /// + /// Appends an relative uri (Path, Query and fragment) at the end of the current uri. + /// + /// The relative uri to append. + /// A reference to this uri_builder to support chaining. + _ASYNCRTIMP uri_builder &append(const uri &relative_uri); + + /// + /// Appends another query to the query of this uri_builder, encoding it first. This overload is useful when building a query segment of + /// the form "element=10", where the right hand side of the query is stored as a type other than a string, for instance, an integral type. + /// + /// The name portion of the query string + /// The value portion of the query string + /// A reference to this uri_builder to support chaining. + template + uri_builder &append_query(utility::string_t name, const T &value, bool do_encoding = true) + { + utility::ostringstream_t ss; + ss.imbue(std::locale::classic()); + ss << name << _XPLATSTR("=") << value; + return append_query(ss.str(), do_encoding); + } + + /// + /// Combine and validate the URI components into a encoded string. An exception will be thrown if the URI is invalid. + /// + /// The created URI as a string. + _ASYNCRTIMP utility::string_t to_string(); + + /// + /// Combine and validate the URI components into a URI class instance. An exception will be thrown if the URI is invalid. + /// + /// The create URI as a URI class instance. + _ASYNCRTIMP uri to_uri(); + + /// + /// Validate the generated URI from all existing components of this uri_builder. + /// + /// Whether the URI is valid. + _ASYNCRTIMP bool is_valid(); + + private: + details::uri_components m_uri; + }; +} // namespace web diff --git a/3rdparty/cpprestsdk/include/cpprest/version.h b/3rdparty/cpprestsdk/include/cpprest/version.h new file mode 100644 index 00000000..44d3f972 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/version.h @@ -0,0 +1,22 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +*/ +#define CPPREST_VERSION_REVISION 0 +#define CPPREST_VERSION_MINOR 6 +#define CPPREST_VERSION_MAJOR 2 + +#define CPPREST_VERSION (CPPREST_VERSION_MAJOR*100000+CPPREST_VERSION_MINOR*100+CPPREST_VERSION_REVISION) diff --git a/3rdparty/cpprestsdk/include/cpprest/ws_client.h b/3rdparty/cpprestsdk/include/cpprest/ws_client.h new file mode 100644 index 00000000..b6d00b80 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/ws_client.h @@ -0,0 +1,627 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Websocket client side implementation +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#ifndef _CASA_WS_CLIENT_H +#define _CASA_WS_CLIENT_H + +#include "cpprest/details/basic_types.h" + +#if !defined(CPPREST_EXCLUDE_WEBSOCKETS) + +#include +#include +#include +#include + +#include "pplx/pplxtasks.h" +#include "cpprest/uri.h" +#include "cpprest/details/web_utilities.h" +#include "cpprest/http_headers.h" +#include "cpprest/asyncrt_utils.h" +#include "cpprest/ws_msg.h" + +namespace web +{ + +// For backwards compatibility for when in the experimental namespace. +// At next major release this should be deleted. +namespace experimental = web; + +// In the past namespace was accidentally called 'web_sockets'. To avoid breaking code +// alias it. At our next major release this should be deleted. +namespace web_sockets = websockets; + +namespace websockets +{ +/// WebSocket client side library. +namespace client +{ + +/// Websocket close status values. +enum class websocket_close_status +{ + normal = 1000, + going_away = 1001, + protocol_error = 1002, + unsupported = 1003, //or data_mismatch + abnormal_close = 1006, + inconsistent_datatype = 1007, + policy_violation = 1008, + too_large = 1009, + negotiate_error = 1010, + server_terminate = 1011, +}; + +/// +/// Websocket client configuration class, used to set the possible configuration options +/// used to create an websocket_client instance. +/// +class websocket_client_config +{ +public: + + /// + /// Creates a websocket client configuration with default settings. + /// + websocket_client_config() : m_sni_enabled(true) {} + + /// + /// Get the web proxy object + /// + /// A reference to the web proxy object. + const web_proxy& proxy() const + { + return m_proxy; + } + + /// + /// Set the web proxy object + /// + /// The web proxy object. + void set_proxy(const web_proxy &proxy) + { + m_proxy = proxy; + } + + /// + /// Get the client credentials + /// + /// A reference to the client credentials. + const web::credentials& credentials() const + { + return m_credentials; + } + + /// + /// Set the client credentials + /// + /// The client credentials. + void set_credentials(const web::credentials &cred) + { + m_credentials = cred; + } + + /// + /// Disables Server Name Indication (SNI). Default is on. + /// + void disable_sni() + { + m_sni_enabled = false; + } + + /// + /// Determines if Server Name Indication (SNI) is enabled. + /// + /// True if enabled, false otherwise. + bool is_sni_enabled() const + { + return m_sni_enabled; + } + + /// + /// Sets the server host name to use for TLS Server Name Indication (SNI). + /// + /// By default the host name is set to the websocket URI host. + /// The host name to use, as a string. + void set_server_name(const utf8string &name) + { + m_sni_hostname = name; + } + + /// + /// Gets the server host name to usefor TLS Server Name Indication (SNI). + /// + /// Host name as a string. + const utf8string & server_name() const + { + return m_sni_hostname; + } + + /// + /// Gets the headers of the HTTP request message used in the WebSocket protocol handshake. + /// + /// HTTP headers for the WebSocket protocol handshake. + /// + /// Use the to fill in desired headers. + /// + web::http::http_headers &headers() { return m_headers; } + + /// + /// Gets a const reference to the headers of the WebSocket protocol handshake HTTP message. + /// + /// HTTP headers. + const web::http::http_headers &headers() const { return m_headers; } + + /// + /// Adds a subprotocol to the request headers. + /// + /// The name of the subprotocol. + /// If additional subprotocols have already been specified, the new one will just be added. + _ASYNCRTIMP void add_subprotocol(const ::utility::string_t &name); + + /// + /// Gets list of the specified subprotocols. + /// + /// Vector of all the subprotocols + /// If you want all the subprotocols in a comma separated string + /// they can be directly looked up in the headers using 'Sec-WebSocket-Protocol'. + _ASYNCRTIMP std::vector<::utility::string_t> subprotocols() const; + +private: + web::web_proxy m_proxy; + web::credentials m_credentials; + web::http::http_headers m_headers; + bool m_sni_enabled; + utf8string m_sni_hostname; +}; + +/// +/// Represents a websocket error. This class holds an error message and an optional error code. +/// +class websocket_exception : public std::exception +{ +public: + + /// + /// Creates an websocket_exception with just a string message and no error code. + /// + /// Error message string. + websocket_exception(const utility::string_t &whatArg) + : m_msg(utility::conversions::to_utf8string(whatArg)) {} + +#ifdef _WIN32 + /// + /// Creates an websocket_exception with just a string message and no error code. + /// + /// Error message string. + websocket_exception(std::string whatArg) : m_msg(std::move(whatArg)) {} +#endif + + /// + /// Creates a websocket_exception from a error code using the current platform error category. + /// The message of the error code will be used as the what() string message. + /// + /// Error code value. + websocket_exception(int errorCode) + : m_errorCode(utility::details::create_error_code(errorCode)) + { + m_msg = m_errorCode.message(); + } + + /// + /// Creates a websocket_exception from a error code using the current platform error category. + /// + /// Error code value. + /// Message to use in what() string. + websocket_exception(int errorCode, const utility::string_t &whatArg) + : m_errorCode(utility::details::create_error_code(errorCode)), + m_msg(utility::conversions::to_utf8string(whatArg)) + {} + +#ifdef _WIN32 + /// + /// Creates a websocket_exception from a error code and string message. + /// + /// Error code value. + /// Message to use in what() string. + websocket_exception(int errorCode, std::string whatArg) + : m_errorCode(utility::details::create_error_code(errorCode)), + m_msg(std::move(whatArg)) + {} + + /// + /// Creates a websocket_exception from a error code and string message to use as the what() argument. + /// Error code. + /// Message to use in what() string. + /// + websocket_exception(std::error_code code, std::string whatArg) : + m_errorCode(std::move(code)), + m_msg(std::move(whatArg)) + {} +#endif + + /// + /// Creates a websocket_exception from a error code and category. The message of the error code will be used + /// as the what string message. + /// + /// Error code value. + /// Error category for the code. + websocket_exception(int errorCode, const std::error_category &cat) : m_errorCode(std::error_code(errorCode, cat)) + { + m_msg = m_errorCode.message(); + } + + /// + /// Creates a websocket_exception from a error code and string message to use as the what() argument. + /// Error code. + /// Message to use in what() string. + /// + websocket_exception(std::error_code code, const utility::string_t &whatArg) : + m_errorCode(std::move(code)), + m_msg(utility::conversions::to_utf8string(whatArg)) + {} + + /// + /// Gets a string identifying the cause of the exception. + /// + /// A null terminated character string. + const char* what() const CPPREST_NOEXCEPT + { + return m_msg.c_str(); + } + + /// + /// Gets the underlying error code for the cause of the exception. + /// + /// The error_code object associated with the exception. + const std::error_code & error_code() const CPPREST_NOEXCEPT + { + return m_errorCode; + } + +private: + std::error_code m_errorCode; + std::string m_msg; +}; + +namespace details +{ + +// Interface to be implemented by the websocket client callback implementations. +class websocket_client_callback_impl +{ +public: + + websocket_client_callback_impl(websocket_client_config config) : + m_config(std::move(config)) {} + + virtual ~websocket_client_callback_impl() CPPREST_NOEXCEPT{} + + virtual pplx::task connect() = 0; + + virtual pplx::task send(websocket_outgoing_message &msg) = 0; + + virtual void set_message_handler(const std::function& handler) = 0; + + virtual pplx::task close() = 0; + + virtual pplx::task close(websocket_close_status close_status, const utility::string_t &close_reason = _XPLATSTR("")) = 0; + + virtual void set_close_handler(const std::function& handler) = 0; + + const web::uri& uri() const + { + return m_uri; + } + + void set_uri(const web::uri &uri) + { + m_uri = uri; + } + + const websocket_client_config& config() const + { + return m_config; + } + + static void verify_uri(const web::uri& uri) + { + // Most of the URI schema validation is taken care by URI class. + // We only need to check certain things specific to websockets. + if (uri.scheme() != _XPLATSTR("ws") && uri.scheme() != _XPLATSTR("wss")) + { + throw std::invalid_argument("URI scheme must be 'ws' or 'wss'"); + } + + if (uri.host().empty()) + { + throw std::invalid_argument("URI must contain a hostname."); + } + + // Fragment identifiers are meaningless in the context of WebSocket URIs + // and MUST NOT be used on these URIs. + if (!uri.fragment().empty()) + { + throw std::invalid_argument("WebSocket URI must not contain fragment identifiers"); + } + } + +protected: + web::uri m_uri; + websocket_client_config m_config; +}; + +// Interface to be implemented by the websocket client task implementations. +class websocket_client_task_impl +{ + +public: + _ASYNCRTIMP websocket_client_task_impl(websocket_client_config config); + + _ASYNCRTIMP virtual ~websocket_client_task_impl() CPPREST_NOEXCEPT; + + _ASYNCRTIMP pplx::task receive(); + + _ASYNCRTIMP void close_pending_tasks_with_error(const websocket_exception &exc); + + const std::shared_ptr & callback_client() const { return m_callback_client; }; + +private: + void set_handler(); + + // When a message arrives, if there are tasks waiting for a message, signal the topmost one. + // Else enqueue the message in a queue. + // m_receive_queue_lock : to guard access to the queue & m_client_closed + std::mutex m_receive_queue_lock; + // Queue to store incoming messages when there are no tasks waiting for a message + std::queue m_receive_msg_queue; + // Queue to maintain the receive tasks when there are no messages(yet). + std::queue> m_receive_task_queue; + + // Initially set to false, becomes true if a close frame is received from the server or + // if the underlying connection is aborted or terminated. + bool m_client_closed; + + std::shared_ptr m_callback_client; +}; +} + +/// +/// Websocket client class, used to maintain a connection to a remote host for an extended session. +/// +class websocket_client +{ +public: + /// + /// Creates a new websocket_client. + /// + websocket_client() : + m_client(std::make_shared(websocket_client_config())) + {} + + /// + /// Creates a new websocket_client. + /// + /// The client configuration object containing the possible configuration options to initialize the websocket_client. + websocket_client(websocket_client_config config) : + m_client(std::make_shared(std::move(config))) + {} + + /// + /// Connects to the remote network destination. The connect method initiates the websocket handshake with the + /// remote network destination, takes care of the protocol upgrade request. + /// + /// The uri address to connect. + /// An asynchronous operation that is completed once the client has successfully connected to the websocket server. + pplx::task connect(const web::uri &uri) + { + m_client->callback_client()->verify_uri(uri); + m_client->callback_client()->set_uri(uri); + auto client = m_client; + return m_client->callback_client()->connect().then([client](pplx::task result) + { + try + { + result.get(); + } + catch (const websocket_exception& ex) + { + client->close_pending_tasks_with_error(ex); + throw; + } + }); + } + + /// + /// Sends a websocket message to the server . + /// + /// An asynchronous operation that is completed once the message is sent. + pplx::task send(websocket_outgoing_message msg) + { + return m_client->callback_client()->send(msg); + } + + /// + /// Receive a websocket message. + /// + /// An asynchronous operation that is completed when a message has been received by the client endpoint. + pplx::task receive() + { + return m_client->receive(); + } + + /// + /// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the server. + /// + /// An asynchronous operation that is completed the connection has been successfully closed. + pplx::task close() + { + return m_client->callback_client()->close(); + } + + /// + /// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the server. + /// + /// Endpoint MAY use the following pre-defined status codes when sending a Close frame. + /// While closing an established connection, an endpoint may indicate the reason for closure. + /// An asynchronous operation that is completed the connection has been successfully closed. + pplx::task close(websocket_close_status close_status, const utility::string_t& close_reason=_XPLATSTR("")) + { + return m_client->callback_client()->close(close_status, close_reason); + } + + /// + /// Gets the websocket client URI. + /// + /// URI connected to. + const web::uri& uri() const + { + return m_client->callback_client()->uri(); + } + + /// + /// Gets the websocket client config object. + /// + /// A reference to the client configuration object. + const websocket_client_config& config() const + { + return m_client->callback_client()->config(); + } + +private: + std::shared_ptr m_client; +}; + +/// +/// Websocket client class, used to maintain a connection to a remote host for an extended session, uses callback APIs for handling receive and close event instead of async task. +/// For some scenarios would be a alternative for the websocket_client like if you want to special handling on close event. +/// +class websocket_callback_client +{ +public: + /// + /// Creates a new websocket_callback_client. + /// + _ASYNCRTIMP websocket_callback_client(); + + /// + /// Creates a new websocket_callback_client. + /// + /// The client configuration object containing the possible configuration options to initialize the websocket_client. + _ASYNCRTIMP websocket_callback_client(websocket_client_config client_config); + + /// + /// Connects to the remote network destination. The connect method initiates the websocket handshake with the + /// remote network destination, takes care of the protocol upgrade request. + /// + /// The uri address to connect. + /// An asynchronous operation that is completed once the client has successfully connected to the websocket server. + pplx::task connect(const web::uri &uri) + { + m_client->verify_uri(uri); + m_client->set_uri(uri); + return m_client->connect(); + } + + /// + /// Sends a websocket message to the server . + /// + /// An asynchronous operation that is completed once the message is sent. + pplx::task send(websocket_outgoing_message msg) + { + return m_client->send(msg); + } + + /// + /// Set the received handler for notification of client websocket messages. + /// + /// A function representing the incoming websocket messages handler. It's parameters are: + /// msg: a websocket_incoming_message value indicating the message received + /// + /// If this handler is not set before connecting incoming messages will be missed. + void set_message_handler(const std::function& handler) + { + m_client->set_message_handler(handler); + } + + /// + /// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the server. + /// + /// An asynchronous operation that is completed the connection has been successfully closed. + pplx::task close() + { + return m_client->close(); + } + + /// + /// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the server. + /// + /// Endpoint MAY use the following pre-defined status codes when sending a Close frame. + /// While closing an established connection, an endpoint may indicate the reason for closure. + /// An asynchronous operation that is completed the connection has been successfully closed. + pplx::task close(websocket_close_status close_status, const utility::string_t& close_reason = _XPLATSTR("")) + { + return m_client->close(close_status, close_reason); + } + + /// + /// Set the closed handler for notification of client websocket closing event. + /// + /// The handler for websocket closing event, It's parameters are: + /// close_status: The pre-defined status codes used by the endpoint when sending a Close frame. + /// reason: The reason string used by the endpoint when sending a Close frame. + /// error: The error code if the websocket is closed with abnormal error. + /// + void set_close_handler(const std::function& handler) + { + m_client->set_close_handler(handler); + } + + /// + /// Gets the websocket client URI. + /// + /// URI connected to. + const web::uri& uri() const + { + return m_client->uri(); + } + + /// + /// Gets the websocket client config object. + /// + /// A reference to the client configuration object. + const websocket_client_config& config() const + { + return m_client->config(); + } + +private: + std::shared_ptr m_client; +}; + +}}} + +#endif + +#endif \ No newline at end of file diff --git a/3rdparty/cpprestsdk/include/cpprest/ws_msg.h b/3rdparty/cpprestsdk/include/cpprest/ws_msg.h new file mode 100644 index 00000000..9b3fe713 --- /dev/null +++ b/3rdparty/cpprestsdk/include/cpprest/ws_msg.h @@ -0,0 +1,242 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Websocket incoming and outgoing message definitions. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ +#pragma once + +#include "cpprest/details/basic_types.h" + +#if !defined(CPPREST_EXCLUDE_WEBSOCKETS) + +#include +#include + +#include "pplx/pplxtasks.h" +#include "cpprest/streams.h" +#include "cpprest/containerstream.h" +#include "cpprest/uri.h" +#include "cpprest/asyncrt_utils.h" + +namespace web +{ +namespace websockets +{ +namespace client +{ + +namespace details +{ + class winrt_callback_client; + class wspp_callback_client; +#if defined(__cplusplus_winrt) + ref class ReceiveContext; +#endif +} + +/// +/// The different types of websocket message. +/// Text type contains UTF-8 encoded data. +/// Interpretation of Binary type is left to the application. +/// Note: The fragment types and control frames like close, ping, pong are not supported on WinRT. +/// +enum class websocket_message_type +{ + text_message, + binary_message, + close, + ping, + pong +}; + +/// +/// Represents an outgoing websocket message +/// +class websocket_outgoing_message +{ +public: + + /// + /// Sets a UTF-8 message as the message body. + /// + /// UTF-8 String containing body of the message. + void set_utf8_message(std::string &&data) + { + this->set_message(concurrency::streams::container_buffer(std::move(data))); + } + + /// + /// Sets a UTF-8 message as the message body. + /// + /// UTF-8 String containing body of the message. + void set_utf8_message(const std::string &data) + { + this->set_message(concurrency::streams::container_buffer(data)); + } + + /// + /// Sets a UTF-8 message as the message body. + /// + /// casablanca input stream representing the body of the message. + /// Upon sending, the entire stream may be buffered to determine the length. + void set_utf8_message(const concurrency::streams::istream &istream) + { + this->set_message(istream, SIZE_MAX, websocket_message_type::text_message); + } + + /// + /// Sets a UTF-8 message as the message body. + /// + /// casablanca input stream representing the body of the message. + /// number of bytes to send. + void set_utf8_message(const concurrency::streams::istream &istream, size_t len) + { + this->set_message(istream, len, websocket_message_type::text_message); + } + + /// + /// Sets binary data as the message body. + /// + /// casablanca input stream representing the body of the message. + /// number of bytes to send. + void set_binary_message(const concurrency::streams::istream &istream, size_t len) + { + this->set_message(istream, len, websocket_message_type::binary_message); + } + + /// + /// Sets binary data as the message body. + /// + /// Input stream representing the body of the message. + /// Upon sending, the entire stream may be buffered to determine the length. + void set_binary_message(const concurrency::streams::istream &istream) + { + this->set_message(istream, SIZE_MAX, websocket_message_type::binary_message); + } + +private: + friend class details::winrt_callback_client; + friend class details::wspp_callback_client; + + pplx::task_completion_event m_body_sent; + concurrency::streams::streambuf m_body; + websocket_message_type m_msg_type; + size_t m_length; + + void signal_body_sent() const + { + m_body_sent.set(); + } + + void signal_body_sent(const std::exception_ptr &e) const + { + m_body_sent.set_exception(e); + } + + const pplx::task_completion_event & body_sent() const { return m_body_sent; } + + void set_message(const concurrency::streams::container_buffer &buffer) + { + m_msg_type = websocket_message_type::text_message; + m_length = static_cast(buffer.size()); + m_body = buffer; + } + + void set_message(const concurrency::streams::istream &istream, size_t len, websocket_message_type msg_type) + { + m_msg_type = msg_type; + m_length = len; + m_body = istream.streambuf(); + } +}; + +/// +/// Represents an incoming websocket message +/// +class websocket_incoming_message +{ +public: + + /// + /// Extracts the body of the incoming message as a string value, only if the message type is UTF-8. + /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. + /// + /// String containing body of the message. + _ASYNCRTIMP pplx::task extract_string() const; + + /// + /// Produces a stream which the caller may use to retrieve body from an incoming message. + /// Can be used for both UTF-8 (text) and binary message types. + /// + /// A readable, open asynchronous stream. + /// + /// This cannot be used in conjunction with any other means of getting the body of the message. + /// + concurrency::streams::istream body() const + { + auto to_uint8_t_stream = [](const concurrency::streams::streambuf &buf) -> concurrency::streams::istream + { + return buf.create_istream(); + }; + return to_uint8_t_stream(m_body); + } + + /// + /// Returns the length of the received message. + /// + size_t length() const + { + return static_cast(m_body.size()); + } + + /// + /// Returns the type of the received message. + /// + CASABLANCA_DEPRECATED("Incorrectly spelled API, use message_type() instead.") + websocket_message_type messge_type() const + { + return m_msg_type; + } + + /// + /// Returns the type of the received message, either string or binary. + /// + /// websocket_message_type + websocket_message_type message_type() const + { + return m_msg_type; + } + +private: + friend class details::winrt_callback_client; + friend class details::wspp_callback_client; +#if defined(__cplusplus_winrt) + friend ref class details::ReceiveContext; +#endif + + // Store message body in a container buffer backed by a string. + // Allows for optimization in the string message cases. + concurrency::streams::container_buffer m_body; + websocket_message_type m_msg_type; +}; + +}}} + +#endif diff --git a/3rdparty/cpprestsdk/include/pplx/pplx.h b/3rdparty/cpprestsdk/include/pplx/pplx.h new file mode 100644 index 00000000..0a55e874 --- /dev/null +++ b/3rdparty/cpprestsdk/include/pplx/pplx.h @@ -0,0 +1,237 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Parallel Patterns Library +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#ifndef _PPLX_H +#define _PPLX_H + +#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX +#error This file must not be included for Visual Studio 12 or later +#endif + +#ifndef _WIN32 +#if defined(_WIN32) || defined(__cplusplus_winrt) +#define _WIN32 +#endif +#endif // _WIN32 + +#ifdef _NO_PPLXIMP +#define _PPLXIMP +#else +#ifdef _PPLX_EXPORT +#define _PPLXIMP __declspec(dllexport) +#else +#define _PPLXIMP __declspec(dllimport) +#endif +#endif + +#include "cpprest/details/cpprest_compat.h" + +// Use PPLx +#ifdef _WIN32 +#include "pplx/pplxwin.h" +#elif defined(__APPLE__) +#undef _PPLXIMP +#define _PPLXIMP +#include "pplx/pplxlinux.h" +#else +#include "pplx/pplxlinux.h" +#endif // _WIN32 + +// Common implementation across all the non-concrt versions +#include "pplx/pplxcancellation_token.h" +#include + +// conditional expression is constant +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4127) +#endif + +#pragma pack(push,_CRT_PACKING) + +/// +/// The pplx namespace provides classes and functions that give you access to the Concurrency Runtime, +/// a concurrent programming framework for C++. For more information, see . +/// +/**/ +namespace pplx +{ + +/// +/// Sets the ambient scheduler to be used by the PPL constructs. +/// +_PPLXIMP void _pplx_cdecl set_ambient_scheduler(std::shared_ptr _Scheduler); + +/// +/// Gets the ambient scheduler to be used by the PPL constructs +/// +_PPLXIMP std::shared_ptr _pplx_cdecl get_ambient_scheduler(); + +namespace details +{ + // + // An internal exception that is used for cancellation. Users do not "see" this exception except through the + // resulting stack unwind. This exception should never be intercepted by user code. It is intended + // for use by the runtime only. + // + class _Interruption_exception : public std::exception + { + public: + _Interruption_exception(){} + }; + + template + struct _AutoDeleter + { + _AutoDeleter(_T *_PPtr) : _Ptr(_PPtr) {} + ~_AutoDeleter () { delete _Ptr; } + _T *_Ptr; + }; + + struct _TaskProcHandle + { + _TaskProcHandle() + { + } + + virtual ~_TaskProcHandle() {} + virtual void invoke() const = 0; + + static void _pplx_cdecl _RunChoreBridge(void * _Parameter) + { + auto _PTaskHandle = static_cast<_TaskProcHandle *>(_Parameter); + _AutoDeleter<_TaskProcHandle> _AutoDeleter(_PTaskHandle); + _PTaskHandle->invoke(); + } + }; + + enum _TaskInliningMode + { + // Disable inline scheduling + _NoInline = 0, + // Let runtime decide whether to do inline scheduling or not + _DefaultAutoInline = 16, + // Always do inline scheduling + _ForceInline = -1, + }; + + // This is an abstraction that is built on top of the scheduler to provide these additional functionalities + // - Ability to wait on a work item + // - Ability to cancel a work item + // - Ability to inline work on invocation of RunAndWait + class _TaskCollectionImpl + { + public: + + typedef _TaskProcHandle _TaskProcHandle_t; + + _TaskCollectionImpl(scheduler_ptr _PScheduler) + : _M_pScheduler(_PScheduler) + { + } + + void _ScheduleTask(_TaskProcHandle_t* _PTaskHandle, _TaskInliningMode _InliningMode) + { + if (_InliningMode == _ForceInline) + { + _TaskProcHandle_t::_RunChoreBridge(_PTaskHandle); + } + else + { + _M_pScheduler->schedule(_TaskProcHandle_t::_RunChoreBridge, _PTaskHandle); + } + } + + void _Cancel() + { + // No cancellation support + } + + void _RunAndWait() + { + // No inlining support yet + _Wait(); + } + + void _Wait() + { + _M_Completed.wait(); + } + + void _Complete() + { + _M_Completed.set(); + } + + scheduler_ptr _GetScheduler() const + { + return _M_pScheduler; + } + + // Fire and forget + static void _RunTask(TaskProc_t _Proc, void * _Parameter, _TaskInliningMode _InliningMode) + { + if (_InliningMode == _ForceInline) + { + _Proc(_Parameter); + } + else + { + // Schedule the work on the ambient scheduler + get_ambient_scheduler()->schedule(_Proc, _Parameter); + } + } + + static bool _pplx_cdecl _Is_cancellation_requested() + { + // We do not yet have the ability to determine the current task. So return false always + return false; + } + private: + + extensibility::event_t _M_Completed; + scheduler_ptr _M_pScheduler; + }; + + // For create_async lambdas that return a (non-task) result, we oversubscriber the current task for the duration of the + // lambda. + struct _Task_generator_oversubscriber {}; + + typedef _TaskCollectionImpl _TaskCollection_t; + typedef _TaskInliningMode _TaskInliningMode_t; + typedef _Task_generator_oversubscriber _Task_generator_oversubscriber_t; + +} // namespace details + +} // namespace pplx + +#pragma pack(pop) +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif // _PPLX_H diff --git a/3rdparty/cpprestsdk/include/pplx/pplxcancellation_token.h b/3rdparty/cpprestsdk/include/pplx/pplxcancellation_token.h new file mode 100644 index 00000000..d61f07de --- /dev/null +++ b/3rdparty/cpprestsdk/include/pplx/pplxcancellation_token.h @@ -0,0 +1,1092 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Parallel Patterns Library : cancellation_token +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#ifndef _PPLX_H +#error This header must not be included directly +#endif + +#ifndef _PPLXCANCELLATION_TOKEN_H +#define _PPLXCANCELLATION_TOKEN_H + +#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX +#error This file must not be included for Visual Studio 12 or later +#endif + +#include +#include + +#pragma pack(push,_CRT_PACKING) +// All header files are required to be protected from the macro new +#pragma push_macro("new") +#undef new + +namespace pplx +{ + +/// +/// This class describes an exception thrown by the PPL tasks layer in order to force the current task +/// to cancel. It is also thrown by the get() method on task, for a +/// canceled task. +/// +/// +/// +/**/ +class task_canceled : public std::exception +{ +private: + std::string _message; + +public: + /// + /// Constructs a task_canceled object. + /// + /// + /// A descriptive message of the error. + /// + /**/ + explicit task_canceled(_In_z_ const char * _Message) throw() + : _message(_Message) + { + } + + /// + /// Constructs a task_canceled object. + /// + /**/ + task_canceled() throw() + : exception() + { + } + + ~task_canceled() throw () {} + + const char* what() const CPPREST_NOEXCEPT + { + return _message.c_str(); + } +}; + +/// +/// This class describes an exception thrown when an invalid operation is performed that is not more accurately +/// described by another exception type thrown by the Concurrency Runtime. +/// +/// +/// The various methods which throw this exception will generally document under what circumstances they will throw it. +/// +/**/ +class invalid_operation : public std::exception +{ +private: + std::string _message; + +public: + /// + /// Constructs an invalid_operation object. + /// + /// + /// A descriptive message of the error. + /// + /**/ + invalid_operation(_In_z_ const char * _Message) throw() + : _message(_Message) + { + } + + /// + /// Constructs an invalid_operation object. + /// + /**/ + invalid_operation() throw() + : exception() + { + } + + ~invalid_operation() throw () {} + + const char* what() const CPPREST_NOEXCEPT + { + return _message.c_str(); + } +}; + +namespace details +{ + + // Base class for all reference counted objects + class _RefCounter + { + public: + + virtual ~_RefCounter() + { + _ASSERTE(_M_refCount == 0); + } + + // Acquires a reference + // Returns the new reference count. + long _Reference() + { + long _Refcount = atomic_increment(_M_refCount); + + // 0 - 1 transition is illegal + _ASSERTE(_Refcount > 1); + return _Refcount; + } + + // Releases the reference + // Returns the new reference count + long _Release() + { + long _Refcount = atomic_decrement(_M_refCount); + _ASSERTE(_Refcount >= 0); + + if (_Refcount == 0) + { + _Destroy(); + } + + return _Refcount; + } + + protected: + + // Allow derived classes to provide their own deleter + virtual void _Destroy() + { + delete this; + } + + // Only allow instantiation through derived class + _RefCounter(long _InitialCount = 1) : _M_refCount(_InitialCount) + { + _ASSERTE(_M_refCount > 0); + } + + // Reference count + atomic_long _M_refCount; + }; + + class _CancellationTokenState; + + class _CancellationTokenRegistration : public _RefCounter + { + private: + + static const long _STATE_CLEAR = 0; + static const long _STATE_DEFER_DELETE = 1; + static const long _STATE_SYNCHRONIZE = 2; + static const long _STATE_CALLED = 3; + + public: + + _CancellationTokenRegistration(long _InitialRefs = 1) : + _RefCounter(_InitialRefs), + _M_state(_STATE_CALLED), + _M_pTokenState(NULL) + { + } + + _CancellationTokenState *_GetToken() const + { + return _M_pTokenState; + } + + protected: + + virtual ~_CancellationTokenRegistration() + { + _ASSERTE(_M_state != _STATE_CLEAR); + } + + virtual void _Exec() = 0; + + private: + + friend class _CancellationTokenState; + + void _Invoke() + { + long tid = ::pplx::details::platform::GetCurrentThreadId(); + _ASSERTE((tid & 0x3) == 0); // If this ever fires, we need a different encoding for this. + + long result = atomic_compare_exchange(_M_state, tid, _STATE_CLEAR); + + if (result == _STATE_CLEAR) + { + _Exec(); + + result = atomic_compare_exchange(_M_state, _STATE_CALLED, tid); + + if (result == _STATE_SYNCHRONIZE) + { + _M_pSyncBlock->set(); + } + } + _Release(); + } + + atomic_long _M_state; + extensibility::event_t *_M_pSyncBlock; + _CancellationTokenState *_M_pTokenState; + }; + + template + class _CancellationTokenCallback : public _CancellationTokenRegistration + { + public: + + _CancellationTokenCallback(const _Function& _Func) : + _M_function(_Func) + { + } + + protected: + + virtual void _Exec() + { + _M_function(); + } + + private: + + _Function _M_function; + }; + + class CancellationTokenRegistration_TaskProc : public _CancellationTokenRegistration + { + public: + + CancellationTokenRegistration_TaskProc(TaskProc_t proc, _In_ void *pData, int initialRefs) : + _CancellationTokenRegistration(initialRefs), m_proc(proc), m_pData(pData) + { + } + + protected: + + virtual void _Exec() + { + m_proc(m_pData); + } + + private: + + TaskProc_t m_proc; + void *m_pData; + + }; + + // The base implementation of a cancellation token. + class _CancellationTokenState : public _RefCounter + { + protected: + class TokenRegistrationContainer + { + private: + typedef struct _Node { + _CancellationTokenRegistration* _M_token; + _Node *_M_next; + } Node; + + public: + TokenRegistrationContainer() : _M_begin(nullptr), _M_last(nullptr) + { + } + + ~TokenRegistrationContainer() + { +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 6001) +#endif + auto node = _M_begin; + while (node != nullptr) + { + Node* tmp = node; + node = node->_M_next; + ::free(tmp); + } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + } + + void swap(TokenRegistrationContainer& list) + { + std::swap(list._M_begin, _M_begin); + std::swap(list._M_last, _M_last); + } + + bool empty() + { + return _M_begin == nullptr; + } + + template + void for_each(T lambda) + { + Node* node = _M_begin; + + while (node != nullptr) + { + lambda(node->_M_token); + node = node->_M_next; + } + } + + void push_back(_CancellationTokenRegistration* token) + { + Node* node = reinterpret_cast(::malloc(sizeof(Node))); + if (node == nullptr) + { + throw ::std::bad_alloc(); + } + + node->_M_token = token; + node->_M_next = nullptr; + + if (_M_begin == nullptr) + { + _M_begin = node; + } + else + { + _M_last->_M_next = node; + } + + _M_last = node; + } + + void remove(_CancellationTokenRegistration* token) + { + Node* node = _M_begin; + Node* prev = nullptr; + + while (node != nullptr) + { + if (node->_M_token == token) { + if (prev == nullptr) + { + _M_begin = node->_M_next; + } + else + { + prev->_M_next = node->_M_next; + } + + if (node->_M_next == nullptr) + { + _M_last = prev; + } + + ::free(node); + break; + } + + prev = node; + node = node->_M_next; + } + } + + private: + Node *_M_begin; + Node *_M_last; + }; + + public: + + static _CancellationTokenState * _NewTokenState() + { + return new _CancellationTokenState(); + } + + static _CancellationTokenState *_None() + { + return reinterpret_cast<_CancellationTokenState *>(2); + } + + static bool _IsValid(_In_opt_ _CancellationTokenState *_PToken) + { + return (_PToken != NULL && _PToken != _None()); + } + + _CancellationTokenState() : + _M_stateFlag(0) + { + } + + ~_CancellationTokenState() + { + TokenRegistrationContainer rundownList; + { + extensibility::scoped_critical_section_t _Lock(_M_listLock); + _M_registrations.swap(rundownList); + } + + rundownList.for_each([](_CancellationTokenRegistration * pRegistration) + { + pRegistration->_M_state = _CancellationTokenRegistration::_STATE_SYNCHRONIZE; + pRegistration->_Release(); + }); + } + + bool _IsCanceled() const + { + return (_M_stateFlag != 0); + } + + void _Cancel() + { + if (atomic_compare_exchange(_M_stateFlag, 1l, 0l) == 0) + { + TokenRegistrationContainer rundownList; + { + extensibility::scoped_critical_section_t _Lock(_M_listLock); + _M_registrations.swap(rundownList); + } + + rundownList.for_each([](_CancellationTokenRegistration * pRegistration) + { + pRegistration->_Invoke(); + }); + + _M_stateFlag = 2; + _M_cancelComplete.set(); + } + } + + _CancellationTokenRegistration *_RegisterCallback(TaskProc_t _PCallback, _In_ void *_PData, int _InitialRefs = 1) + { + _CancellationTokenRegistration *pRegistration = new CancellationTokenRegistration_TaskProc(_PCallback, _PData, _InitialRefs); + _RegisterCallback(pRegistration); + return pRegistration; + } + + void _RegisterCallback(_In_ _CancellationTokenRegistration *_PRegistration) + { + _PRegistration->_M_state = _CancellationTokenRegistration::_STATE_CLEAR; + _PRegistration->_Reference(); + _PRegistration->_M_pTokenState = this; + + bool invoke = true; + + if (!_IsCanceled()) + { + extensibility::scoped_critical_section_t _Lock(_M_listLock); + + if (!_IsCanceled()) + { + invoke = false; + _M_registrations.push_back(_PRegistration); + } + } + + if (invoke) + { + _PRegistration->_Invoke(); + } + } + + void _DeregisterCallback(_In_ _CancellationTokenRegistration *_PRegistration) + { + bool synchronize = false; + + { + extensibility::scoped_critical_section_t _Lock(_M_listLock); + + // + // If a cancellation has occurred, the registration list is guaranteed to be empty if we've observed it under the auspices of the + // lock. In this case, we must synchronize with the cancelling thread to guarantee that the cancellation is finished by the time + // we return from this method. + // + if (!_M_registrations.empty()) + { + _M_registrations.remove(_PRegistration); + _PRegistration->_M_state = _CancellationTokenRegistration::_STATE_SYNCHRONIZE; + _PRegistration->_Release(); + } + else + { + synchronize = true; + } + } + + // + // If the list is empty, we are in one of several situations: + // + // - The callback has already been made --> do nothing + // - The callback is about to be made --> flag it so it doesn't happen and return + // - The callback is in progress elsewhere --> synchronize with it + // - The callback is in progress on this thread --> do nothing + // + if (synchronize) + { + long result = atomic_compare_exchange( + _PRegistration->_M_state, + _CancellationTokenRegistration::_STATE_DEFER_DELETE, + _CancellationTokenRegistration::_STATE_CLEAR + ); + + switch(result) + { + case _CancellationTokenRegistration::_STATE_CLEAR: + case _CancellationTokenRegistration::_STATE_CALLED: + break; + case _CancellationTokenRegistration::_STATE_DEFER_DELETE: + case _CancellationTokenRegistration::_STATE_SYNCHRONIZE: + _ASSERTE(false); + break; + default: + { + long tid = result; + if (tid == ::pplx::details::platform::GetCurrentThreadId()) + { + // + // It is entirely legal for a caller to Deregister during a callback instead of having to provide their own synchronization + // mechanism between the two. In this case, we do *NOT* need to explicitly synchronize with the callback as doing so would + // deadlock. If the call happens during, skip any extra synchronization. + // + break; + } + + extensibility::event_t ev; + _PRegistration->_M_pSyncBlock = &ev; + + long result_1 = atomic_exchange(_PRegistration->_M_state, _CancellationTokenRegistration::_STATE_SYNCHRONIZE); + + if (result_1 != _CancellationTokenRegistration::_STATE_CALLED) + { + _PRegistration->_M_pSyncBlock->wait(::pplx::extensibility::event_t::timeout_infinite); + } + + break; + } + } + } + } + + private: + + // The flag for the token state (whether it is canceled or not) + atomic_long _M_stateFlag; + + // Notification of completion of cancellation of this token. + extensibility::event_t _M_cancelComplete; // Hmm.. where do we wait for it?? + + // Lock to protect the registrations list + extensibility::critical_section_t _M_listLock; + + // The protected list of registrations + TokenRegistrationContainer _M_registrations; + }; + +} // namespace details + +class cancellation_token_source; +class cancellation_token; + + +/// +/// The cancellation_token_registration class represents a callback notification from a cancellation_token. When the register +/// method on a cancellation_token is used to receive notification of when cancellation occurs, a cancellation_token_registration +/// object is returned as a handle to the callback so that the caller can request a specific callback no longer be made through use of +/// the deregister method. +/// +class cancellation_token_registration +{ +public: + + cancellation_token_registration() : + _M_pRegistration(NULL) + { + } + + ~cancellation_token_registration() + { + _Clear(); + } + + cancellation_token_registration(const cancellation_token_registration& _Src) + { + _Assign(_Src._M_pRegistration); + } + + cancellation_token_registration(cancellation_token_registration&& _Src) + { + _Move(_Src._M_pRegistration); + } + + cancellation_token_registration& operator=(const cancellation_token_registration& _Src) + { + if (this != &_Src) + { + _Clear(); + _Assign(_Src._M_pRegistration); + } + return *this; + } + + cancellation_token_registration& operator=(cancellation_token_registration&& _Src) + { + if (this != &_Src) + { + _Clear(); + _Move(_Src._M_pRegistration); + } + return *this; + } + + bool operator==(const cancellation_token_registration& _Rhs) const + { + return _M_pRegistration == _Rhs._M_pRegistration; + } + + bool operator!=(const cancellation_token_registration& _Rhs) const + { + return !(operator==(_Rhs)); + } + +private: + + friend class cancellation_token; + + cancellation_token_registration(_In_ details::_CancellationTokenRegistration *_PRegistration) : + _M_pRegistration(_PRegistration) + { + } + + void _Clear() + { + if (_M_pRegistration != NULL) + { + _M_pRegistration->_Release(); + } + _M_pRegistration = NULL; + } + + void _Assign(_In_ details::_CancellationTokenRegistration *_PRegistration) + { + if (_PRegistration != NULL) + { + _PRegistration->_Reference(); + } + _M_pRegistration = _PRegistration; + } + + void _Move(_In_ details::_CancellationTokenRegistration *&_PRegistration) + { + _M_pRegistration = _PRegistration; + _PRegistration = NULL; + } + + details::_CancellationTokenRegistration *_M_pRegistration; +}; + + +/// +/// The cancellation_token class represents the ability to determine whether some operation has been requested to cancel. A given token can +/// be associated with a task_group, structured_task_group, or task to provide implicit cancellation. It can also be polled for +/// cancellation or have a callback registered for if and when the associated cancellation_token_source is canceled. +/// +class cancellation_token +{ +public: + + typedef details::_CancellationTokenState * _ImplType; + + /// + /// Returns a cancellation token which can never be subject to cancellation. + /// + /// + /// A cancellation token that cannot be canceled. + /// + static cancellation_token none() + { + return cancellation_token(); + } + + cancellation_token(const cancellation_token& _Src) + { + _Assign(_Src._M_Impl); + } + + cancellation_token(cancellation_token&& _Src) + { + _Move(_Src._M_Impl); + } + + cancellation_token& operator=(const cancellation_token& _Src) + { + if (this != &_Src) + { + _Clear(); + _Assign(_Src._M_Impl); + } + return *this; + } + + cancellation_token& operator=(cancellation_token&& _Src) + { + if (this != &_Src) + { + _Clear(); + _Move(_Src._M_Impl); + } + return *this; + } + + bool operator==(const cancellation_token& _Src) const + { + return _M_Impl == _Src._M_Impl; + } + + bool operator!=(const cancellation_token& _Src) const + { + return !(operator==(_Src)); + } + + ~cancellation_token() + { + _Clear(); + } + + /// + /// Returns an indication of whether this token can be canceled or not. + /// + /// + /// An indication of whether this token can be canceled or not. + /// + bool is_cancelable() const + { + return (_M_Impl != NULL); + } + + /// + /// Returns true if the token has been canceled. + /// + /// + /// The value true if the token has been canceled; otherwise, the value false. + /// + bool is_canceled() const + { + return (_M_Impl != NULL && _M_Impl->_IsCanceled()); + } + + /// + /// Registers a callback function with the token. If and when the token is canceled, the callback will be made. Note that if the token + /// is already canceled at the point where this method is called, the callback will be made immediately and synchronously. + /// + /// + /// The type of the function object that will be called back when this cancellation_token is canceled. + /// + /// + /// The function object that will be called back when this cancellation_token is canceled. + /// + /// + /// A cancellation_token_registration object which can be utilized in the deregister method to deregister a previously registered + /// callback and prevent it from being made. The method will throw an invalid_operation exception if + /// it is called on a cancellation_token object that was created using the cancellation_token::none + /// method. + /// + template + ::pplx::cancellation_token_registration register_callback(const _Function& _Func) const + { + if (_M_Impl == NULL) + { + // A callback cannot be registered if the token does not have an associated source. + throw invalid_operation(); + } +#if defined(_MSC_VER) +#pragma warning(suppress: 28197) +#endif + details::_CancellationTokenCallback<_Function> *_PCallback = new details::_CancellationTokenCallback<_Function>(_Func); + _M_Impl->_RegisterCallback(_PCallback); + return cancellation_token_registration(_PCallback); + } + + /// + /// Removes a callback previously registered via the register method based on the cancellation_token_registration object returned + /// at the time of registration. + /// + /// + /// The cancellation_token_registration object corresponding to the callback to be deregistered. This token must have been previously + /// returned from a call to the register method. + /// + void deregister_callback(const cancellation_token_registration& _Registration) const + { + _M_Impl->_DeregisterCallback(_Registration._M_pRegistration); + } + + _ImplType _GetImpl() const + { + return _M_Impl; + } + + _ImplType _GetImplValue() const + { + return (_M_Impl == NULL) ? ::pplx::details::_CancellationTokenState::_None() : _M_Impl; + } + + static cancellation_token _FromImpl(_ImplType _Impl) + { + return cancellation_token(_Impl); + } + +private: + + friend class cancellation_token_source; + + _ImplType _M_Impl; + + void _Clear() + { + if (_M_Impl != NULL) + { + _M_Impl->_Release(); + } + _M_Impl = NULL; + } + + void _Assign(_ImplType _Impl) + { + if (_Impl != NULL) + { + _Impl->_Reference(); + } + _M_Impl = _Impl; + } + + void _Move(_ImplType &_Impl) + { + _M_Impl = _Impl; + _Impl = NULL; + } + + cancellation_token() : + _M_Impl(NULL) + { + } + + cancellation_token(_ImplType _Impl) : + _M_Impl(_Impl) + { + if (_M_Impl == ::pplx::details::_CancellationTokenState::_None()) + { + _M_Impl = NULL; + } + + if (_M_Impl != NULL) + { + _M_Impl->_Reference(); + } + } +}; + +/// +/// The cancellation_token_source class represents the ability to cancel some cancelable operation. +/// +class cancellation_token_source +{ +public: + + typedef ::pplx::details::_CancellationTokenState * _ImplType; + + /// + /// Constructs a new cancellation_token_source. The source can be used to flag cancellation of some cancelable operation. + /// + cancellation_token_source() + { + _M_Impl = new ::pplx::details::_CancellationTokenState; + } + + cancellation_token_source(const cancellation_token_source& _Src) + { + _Assign(_Src._M_Impl); + } + + cancellation_token_source(cancellation_token_source&& _Src) + { + _Move(_Src._M_Impl); + } + + cancellation_token_source& operator=(const cancellation_token_source& _Src) + { + if (this != &_Src) + { + _Clear(); + _Assign(_Src._M_Impl); + } + return *this; + } + + cancellation_token_source& operator=(cancellation_token_source&& _Src) + { + if (this != &_Src) + { + _Clear(); + _Move(_Src._M_Impl); + } + return *this; + } + + bool operator==(const cancellation_token_source& _Src) const + { + return _M_Impl == _Src._M_Impl; + } + + bool operator!=(const cancellation_token_source& _Src) const + { + return !(operator==(_Src)); + } + + ~cancellation_token_source() + { + if (_M_Impl != NULL) + { + _M_Impl->_Release(); + } + } + + /// + /// Returns a cancellation token associated with this source. The returned token can be polled for cancellation + /// or provide a callback if and when cancellation occurs. + /// + /// + /// A cancellation token associated with this source. + /// + cancellation_token get_token() const + { + return cancellation_token(_M_Impl); + } + + /// + /// Creates a cancellation_token_source which is canceled when the provided token is canceled. + /// + /// + /// A token whose cancellation will cause cancellation of the returned token source. Note that the returned token source can also be canceled + /// independently of the source contained in this parameter. + /// + /// + /// A cancellation_token_source which is canceled when the token provided by the parameter is canceled. + /// + static cancellation_token_source create_linked_source(cancellation_token& _Src) + { + cancellation_token_source newSource; + _Src.register_callback( [newSource](){ newSource.cancel(); } ); + return newSource; + } + + /// + /// Creates a cancellation_token_source which is canceled when one of a series of tokens represented by an STL iterator + /// pair is canceled. + /// + /// + /// The STL iterator corresponding to the beginning of the range of tokens to listen for cancellation of. + /// + /// + /// The STL iterator corresponding to the ending of the range of tokens to listen for cancellation of. + /// + /// + /// A cancellation_token_source which is canceled when any of the tokens provided by the range described by the STL iterators + /// contained in the and parameters is canceled. + /// + template + static cancellation_token_source create_linked_source(_Iter _Begin, _Iter _End) + { + cancellation_token_source newSource; + for (_Iter _It = _Begin; _It != _End; ++_It) + { + _It->register_callback( [newSource](){ newSource.cancel(); } ); + } + return newSource; + } + + /// + /// Cancels the token. Any task_group, structured_task_group, or task which utilizes the token will be + /// canceled upon this call and throw an exception at the next interruption point. + /// + void cancel() const + { + _M_Impl->_Cancel(); + } + + _ImplType _GetImpl() const + { + return _M_Impl; + } + + static cancellation_token_source _FromImpl(_ImplType _Impl) + { + return cancellation_token_source(_Impl); + } + +private: + + _ImplType _M_Impl; + + void _Clear() + { + if (_M_Impl != NULL) + { + _M_Impl->_Release(); + } + _M_Impl = NULL; + } + + void _Assign(_ImplType _Impl) + { + if (_Impl != NULL) + { + _Impl->_Reference(); + } + _M_Impl = _Impl; + } + + void _Move(_ImplType &_Impl) + { + _M_Impl = _Impl; + _Impl = NULL; + } + + cancellation_token_source(_ImplType _Impl) : + _M_Impl(_Impl) + { + if (_M_Impl == ::pplx::details::_CancellationTokenState::_None()) + { + _M_Impl = NULL; + } + + if (_M_Impl != NULL) + { + _M_Impl->_Reference(); + } + } +}; + +} // namespace pplx + +#pragma pop_macro("new") +#pragma pack(pop) + +#endif // _PPLXCANCELLATION_TOKEN_H diff --git a/3rdparty/cpprestsdk/include/pplx/pplxconv.h b/3rdparty/cpprestsdk/include/pplx/pplxconv.h new file mode 100644 index 00000000..fe791c1a --- /dev/null +++ b/3rdparty/cpprestsdk/include/pplx/pplxconv.h @@ -0,0 +1,84 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Utilities to convert between PPL tasks and PPLX tasks +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#ifndef _PPLXCONV_H +#define _PPLXCONV_H + +#ifndef _WIN32 +#error This is only supported on Windows +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1700) && (_MSC_VER < 1800) && !CPPREST_FORCE_PPLX + +#include +#include "pplx/pplxtasks.h" + +namespace pplx +{ +namespace _Ppl_conv_helpers +{ +template +auto _Set_value(_Tc _Tcp, const _F& _Func) -> decltype(_Tcp.set(_Func())) { return _Tcp.set(_Func()); } + +template +auto _Set_value(_Tc _Tcp, const _F& _Func, ...) -> decltype(_Tcp.set()) { _Func(); return _Tcp.set(); } + +template +_OtherTaskType _Convert_task(_TaskType _Task) +{ + _OtherTCEType _Tc; + _Task.then([_Tc](_TaskType _Task2) { + try + { + _Ppl_conv_helpers::_Set_value(_Tc, [=]{ return _Task2.get(); }); + } + catch(...) + { + _Tc.set_exception(std::current_exception()); + } + }); + _OtherTaskType _T_other(_Tc); + return _T_other; +} +} + +template +concurrency::task<_TaskType> pplx_task_to_concurrency_task(pplx::task<_TaskType> _Task) +{ + return _Ppl_conv_helpers::_Convert_task, concurrency::task<_TaskType>, concurrency::task_completion_event<_TaskType>>(_Task); +} + +template +pplx::task<_TaskType> concurrency_task_to_pplx_task(concurrency::task<_TaskType> _Task) +{ + return _Ppl_conv_helpers::_Convert_task, pplx::task<_TaskType>, pplx::task_completion_event<_TaskType>>(_Task); +} +} // namespace pplx + +#endif + +#endif // _PPLXCONV_H diff --git a/3rdparty/cpprestsdk/include/pplx/pplxinterface.h b/3rdparty/cpprestsdk/include/pplx/pplxinterface.h new file mode 100644 index 00000000..c8cead93 --- /dev/null +++ b/3rdparty/cpprestsdk/include/pplx/pplxinterface.h @@ -0,0 +1,259 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* PPL interfaces +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#ifndef _PPLXINTERFACE_H +#define _PPLXINTERFACE_H + +#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX +#error This file must not be included for Visual Studio 12 or later +#endif + +#if defined(_CRTBLD) +#elif defined(_WIN32) +#if (_MSC_VER >= 1700) +#define _USE_REAL_ATOMICS +#endif +#else // GCC compiler +#define _USE_REAL_ATOMICS +#endif + +#include +#ifdef _USE_REAL_ATOMICS +#include +#endif + +#if (defined(ANDROID) || defined(__ANDROID__)) +// This prevents users from requiring -Wno-attributes when using gcc-4.8 with the android NDK. +#define _pplx_cdecl +#else +#define _pplx_cdecl __cdecl +#endif + +namespace pplx +{ + +/// +/// An elementary abstraction for a task, defined as void (__cdecl * TaskProc_t)(void *). A TaskProc is called to +/// invoke the body of a task. +/// +/**/ +typedef void (_pplx_cdecl * TaskProc_t)(void *); + +/// +/// Scheduler Interface +/// +struct __declspec(novtable) scheduler_interface +{ + virtual void schedule( TaskProc_t, _In_ void* ) = 0; +}; + +/// +/// Represents a pointer to a scheduler. This class exists to allow the +/// the specification of a shared lifetime by using shared_ptr or just +/// a plain reference by using raw pointer. +/// +struct scheduler_ptr +{ + /// + /// Creates a scheduler pointer from shared_ptr to scheduler + /// + explicit scheduler_ptr(std::shared_ptr scheduler) : m_sharedScheduler(std::move(scheduler)) + { + m_scheduler = m_sharedScheduler.get(); + } + + /// + /// Creates a scheduler pointer from raw pointer to scheduler + /// + explicit scheduler_ptr(_In_opt_ scheduler_interface * pScheduler) : m_scheduler(pScheduler) + { + } + + /// + /// Behave like a pointer + /// + scheduler_interface *operator->() const + { + return get(); + } + + /// + /// Returns the raw pointer to the scheduler + /// + scheduler_interface * get() const + { + return m_scheduler; + } + + /// + /// Test whether the scheduler pointer is non-null + /// + operator bool() const { return get() != nullptr; } + +private: + + std::shared_ptr m_sharedScheduler; + scheduler_interface * m_scheduler; +}; + + +/// +/// Describes the execution status of a task_group or structured_task_group object. A value of this type is returned +/// by numerous methods that wait on tasks scheduled to a task group to complete. +/// +/// +/// +/// +/// +/// +/// +/**/ +enum task_group_status +{ + /// + /// The tasks queued to the task_group object have not completed. Note that this value is not presently returned by + /// the Concurrency Runtime. + /// + /**/ + not_complete, + + /// + /// The tasks queued to the task_group or structured_task_group object completed successfully. + /// + /**/ + completed, + + /// + /// The task_group or structured_task_group object was canceled. One or more tasks may not have executed. + /// + /**/ + canceled +}; + +namespace details +{ +/// +/// Atomics +/// +#ifdef _USE_REAL_ATOMICS +typedef std::atomic atomic_long; +typedef std::atomic atomic_size_t; + +template +_T atomic_compare_exchange(std::atomic<_T>& _Target, _T _Exchange, _T _Comparand) +{ + _T _Result = _Comparand; + _Target.compare_exchange_strong(_Result, _Exchange); + return _Result; +} + +template +_T atomic_exchange(std::atomic<_T>& _Target, _T _Value) +{ + return _Target.exchange(_Value); +} + +template +_T atomic_increment(std::atomic<_T>& _Target) +{ + return _Target.fetch_add(1) + 1; +} + +template +_T atomic_decrement(std::atomic<_T>& _Target) +{ + return _Target.fetch_sub(1) - 1; +} + +template +_T atomic_add(std::atomic<_T>& _Target, _T value) +{ + return _Target.fetch_add(value) + value; +} + +#else // not _USE_REAL_ATOMICS + +typedef long volatile atomic_long; +typedef size_t volatile atomic_size_t; + +template +inline T atomic_exchange(T volatile& _Target, T _Value) +{ + return _InterlockedExchange(&_Target, _Value); +} + +inline long atomic_increment(long volatile & _Target) +{ + return _InterlockedIncrement(&_Target); +} + +inline long atomic_add(long volatile & _Target, long value) +{ + return _InterlockedExchangeAdd(&_Target, value) + value; +} + +inline size_t atomic_increment(size_t volatile & _Target) +{ +#if (defined(_M_IX86) || defined(_M_ARM)) + return static_cast(_InterlockedIncrement(reinterpret_cast(&_Target))); +#else + return static_cast(_InterlockedIncrement64(reinterpret_cast<__int64 volatile *>(&_Target))); +#endif +} + +inline long atomic_decrement(long volatile & _Target) +{ + return _InterlockedDecrement(&_Target); +} + +inline size_t atomic_decrement(size_t volatile & _Target) +{ +#if (defined(_M_IX86) || defined(_M_ARM)) + return static_cast(_InterlockedDecrement(reinterpret_cast(&_Target))); +#else + return static_cast(_InterlockedDecrement64(reinterpret_cast<__int64 volatile *>(&_Target))); +#endif +} + +inline long atomic_compare_exchange(long volatile & _Target, long _Exchange, long _Comparand) +{ + return _InterlockedCompareExchange(&_Target, _Exchange, _Comparand); +} + +inline size_t atomic_compare_exchange(size_t volatile & _Target, size_t _Exchange, size_t _Comparand) +{ +#if (defined(_M_IX86) || defined(_M_ARM)) + return static_cast(_InterlockedCompareExchange(reinterpret_cast(_Target), static_cast(_Exchange), static_cast(_Comparand))); +#else + return static_cast(_InterlockedCompareExchange64(reinterpret_cast<__int64 volatile *>(_Target), static_cast<__int64>(_Exchange), static_cast<__int64>(_Comparand))); +#endif +} +#endif // _USE_REAL_ATOMICS + +}} // namespace pplx + +#endif // _PPLXINTERFACE_H diff --git a/3rdparty/cpprestsdk/include/pplx/pplxlinux.h b/3rdparty/cpprestsdk/include/pplx/pplxlinux.h new file mode 100644 index 00000000..456e8762 --- /dev/null +++ b/3rdparty/cpprestsdk/include/pplx/pplxlinux.h @@ -0,0 +1,329 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Linux specific pplx implementations +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + + +#if (defined(_MSC_VER)) +#error This file must not be included for Visual Studio +#endif + +#ifndef _WIN32 + +#include +#include "pthread.h" +#include "cpprest/details/cpprest_compat.h" + +#if defined(__APPLE__) +#include +#include +#include +#else +#include +#include +#endif + +#include "pplx/pplxinterface.h" + + +namespace pplx +{ +#if defined(__APPLE__) + namespace cpprest_synchronization = ::boost; +#else + namespace cpprest_synchronization = ::std; +#endif +namespace details +{ +namespace platform +{ + /// + /// Returns a unique identifier for the execution thread where this routine in invoked + /// + _PPLXIMP long _pplx_cdecl GetCurrentThreadId(); + + /// + /// Yields the execution of the current execution thread - typically when spin-waiting + /// + _PPLXIMP void _pplx_cdecl YieldExecution(); + + /// + /// Caputeres the callstack + /// + __declspec(noinline) inline static size_t CaptureCallstack(void **, size_t, size_t) + { + return 0; + } +} + + /// + /// Manual reset event + /// + class event_impl + { + private: + cpprest_synchronization::mutex _lock; + cpprest_synchronization::condition_variable _condition; + bool _signaled; + public: + + static const unsigned int timeout_infinite = 0xFFFFFFFF; + + event_impl() + : _signaled(false) + { + } + + void set() + { + cpprest_synchronization::lock_guard lock(_lock); + _signaled = true; + _condition.notify_all(); + } + + void reset() + { + cpprest_synchronization::lock_guard lock(_lock); + _signaled = false; + } + + unsigned int wait(unsigned int timeout) + { + cpprest_synchronization::unique_lock lock(_lock); + if (timeout == event_impl::timeout_infinite) + { + _condition.wait(lock, [this]() -> bool { return _signaled; }); + return 0; + } + else + { + cpprest_synchronization::chrono::milliseconds period(timeout); + auto status = _condition.wait_for(lock, period, [this]() -> bool { return _signaled; }); + _ASSERTE(status == _signaled); + // Return 0 if the wait completed as a result of signaling the event. Otherwise, return timeout_infinite + // Note: this must be consistent with the behavior of the Windows version, which is based on WaitForSingleObjectEx + return status ? 0: event_impl::timeout_infinite; + } + } + + unsigned int wait() + { + return wait(event_impl::timeout_infinite); + } + }; + + /// + /// Reader writer lock + /// + class reader_writer_lock_impl + { + private: + + pthread_rwlock_t _M_reader_writer_lock; + + public: + + class scoped_lock_read + { + public: + explicit scoped_lock_read(reader_writer_lock_impl &_Reader_writer_lock) : _M_reader_writer_lock(_Reader_writer_lock) + { + _M_reader_writer_lock.lock_read(); + } + + ~scoped_lock_read() + { + _M_reader_writer_lock.unlock(); + } + + private: + reader_writer_lock_impl& _M_reader_writer_lock; + scoped_lock_read(const scoped_lock_read&); // no copy constructor + scoped_lock_read const & operator=(const scoped_lock_read&); // no assignment operator + }; + + reader_writer_lock_impl() + { + pthread_rwlock_init(&_M_reader_writer_lock, nullptr); + } + + ~reader_writer_lock_impl() + { + pthread_rwlock_destroy(&_M_reader_writer_lock); + } + + void lock() + { + pthread_rwlock_wrlock(&_M_reader_writer_lock); + } + + void lock_read() + { + pthread_rwlock_rdlock(&_M_reader_writer_lock); + } + + void unlock() + { + pthread_rwlock_unlock(&_M_reader_writer_lock); + } + }; + + /// + /// Recursive mutex + /// + class recursive_lock_impl + { + public: + + recursive_lock_impl() + : _M_owner(-1), _M_recursionCount(0) + { + } + + ~recursive_lock_impl() + { + _ASSERTE(_M_owner == -1); + _ASSERTE(_M_recursionCount == 0); + } + + void lock() + { + auto id = ::pplx::details::platform::GetCurrentThreadId(); + + if ( _M_owner == id ) + { + _M_recursionCount++; + } + else + { + _M_cs.lock(); + _M_owner = id; + _M_recursionCount = 1; + } + } + + void unlock() + { + _ASSERTE(_M_owner == ::pplx::details::platform::GetCurrentThreadId()); + _ASSERTE(_M_recursionCount >= 1); + + _M_recursionCount--; + + if ( _M_recursionCount == 0 ) + { + _M_owner = -1; + _M_cs.unlock(); + } + } + + private: + cpprest_synchronization::mutex _M_cs; + volatile long _M_owner; + long _M_recursionCount; + }; + +#if defined(__APPLE__) + class apple_scheduler : public pplx::scheduler_interface +#else + class linux_scheduler : public pplx::scheduler_interface +#endif + { + public: + _PPLXIMP virtual void schedule( TaskProc_t proc, _In_ void* param); + }; + +} // namespace details + +/// +/// A generic RAII wrapper for locks that implements the critical_section interface +/// cpprest_synchronization::lock_guard +/// +template +class scoped_lock +{ +public: + explicit scoped_lock(_Lock& _Critical_section) : _M_critical_section(_Critical_section) + { + _M_critical_section.lock(); + } + + ~scoped_lock() + { + _M_critical_section.unlock(); + } + +private: + _Lock& _M_critical_section; + + scoped_lock(const scoped_lock&); // no copy constructor + scoped_lock const & operator=(const scoped_lock&); // no assignment operator +}; + +// The extensibility namespace contains the type definitions that are used internally +namespace extensibility +{ + typedef ::pplx::details::event_impl event_t; + + typedef cpprest_synchronization::mutex critical_section_t; + typedef scoped_lock scoped_critical_section_t; + + typedef ::pplx::details::reader_writer_lock_impl reader_writer_lock_t; + typedef scoped_lock scoped_rw_lock_t; + typedef ::pplx::extensibility::reader_writer_lock_t::scoped_lock_read scoped_read_lock_t; + + typedef ::pplx::details::recursive_lock_impl recursive_lock_t; + typedef scoped_lock scoped_recursive_lock_t; +} + +/// +/// Default scheduler type +/// +#if defined(__APPLE__) + typedef details::apple_scheduler default_scheduler_t; +#else + typedef details::linux_scheduler default_scheduler_t; +#endif + +namespace details +{ + /// + /// Terminate the process due to unhandled exception + /// + #ifndef _REPORT_PPLTASK_UNOBSERVED_EXCEPTION + #define _REPORT_PPLTASK_UNOBSERVED_EXCEPTION() do { \ + raise(SIGTRAP); \ + std::terminate(); \ + } while(false) + #endif //_REPORT_PPLTASK_UNOBSERVED_EXCEPTION +} + +//see: http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html +// this is critical to inline +__attribute__ ((always_inline)) +inline void* _ReturnAddress() { return __builtin_return_address(0); } + +} // namespace pplx + +#endif // !_WIN32 diff --git a/3rdparty/cpprestsdk/include/pplx/pplxtasks.h b/3rdparty/cpprestsdk/include/pplx/pplxtasks.h new file mode 100644 index 00000000..98b1e547 --- /dev/null +++ b/3rdparty/cpprestsdk/include/pplx/pplxtasks.h @@ -0,0 +1,7362 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Parallel Patterns Library - PPLx Tasks +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#ifndef _PPLXTASKS_H +#define _PPLXTASKS_H + +#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX +#include +namespace pplx = Concurrency; +#if (_MSC_VER >= 1900) +#include +namespace Concurrency { + namespace extensibility { + typedef ::std::condition_variable condition_variable_t; + typedef ::std::mutex critical_section_t; + typedef ::std::unique_lock< ::std::mutex> scoped_critical_section_t; + + typedef ::Concurrency::event event_t; + typedef ::Concurrency::reader_writer_lock reader_writer_lock_t; + typedef ::Concurrency::reader_writer_lock::scoped_lock scoped_rw_lock_t; + typedef ::Concurrency::reader_writer_lock::scoped_lock_read scoped_read_lock_t; + + typedef ::Concurrency::details::_ReentrantBlockingLock recursive_lock_t; + typedef recursive_lock_t::_Scoped_lock scoped_recursive_lock_t; + } +} +#endif // _MSC_VER >= 1900 +#else + +#include "pplx/pplx.h" + +#if defined(__ANDROID__) +#include +void cpprest_init(JavaVM*); +#endif + +// Cannot build using a compiler that is older than dev10 SP1 +#if defined(_MSC_VER) +#if _MSC_FULL_VER < 160040219 /*IFSTRIP=IGN*/ +#error ERROR: Visual Studio 2010 SP1 or later is required to build ppltasks +#endif /*IFSTRIP=IGN*/ +#endif /* defined(_MSC_VER) */ + +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#if defined(__cplusplus_winrt) +#include +#include +#include +#include +#ifndef _UITHREADCTXT_SUPPORT + +#ifdef WINAPI_FAMILY /*IFSTRIP=IGN*/ + +// It is safe to include winapifamily as WINAPI_FAMILY was defined by the user +#include + +#if WINAPI_FAMILY == WINAPI_FAMILY_APP + // UI thread context support is not required for desktop and Windows Store apps + #define _UITHREADCTXT_SUPPORT 0 +#elif WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP + // UI thread context support is not required for desktop and Windows Store apps + #define _UITHREADCTXT_SUPPORT 0 +#else /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ + #define _UITHREADCTXT_SUPPORT 1 +#endif /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ + +#else /* WINAPI_FAMILY */ + // Not supported without a WINAPI_FAMILY setting. + #define _UITHREADCTXT_SUPPORT 0 +#endif /* WINAPI_FAMILY */ + +#endif /* _UITHREADCTXT_SUPPORT */ + +#if _UITHREADCTXT_SUPPORT + #include +#endif /* _UITHREADCTXT_SUPPORT */ + + #pragma detect_mismatch("_PPLTASKS_WITH_WINRT", "1") +#else /* defined(__cplusplus_winrt) */ + #pragma detect_mismatch("_PPLTASKS_WITH_WINRT", "0") +#endif /* defined(__cplusplus_winrt) */ +#endif /* defined(_MSC_VER) */ + +#ifdef _DEBUG + #define _DBG_ONLY(X) X +#else + #define _DBG_ONLY(X) +#endif // #ifdef _DEBUG + +// std::copy_exception changed to std::make_exception_ptr from VS 2010 to VS 11. +#ifdef _MSC_VER +#if _MSC_VER < 1700 /*IFSTRIP=IGN*/ +namespace std +{ + template exception_ptr make_exception_ptr(_E _Except) + { + return copy_exception(_Except); + } +} +#endif /* _MSC_VER < 1700 */ +#ifndef _PPLTASK_ASYNC_LOGGING + #if _MSC_VER >= 1800 && defined(__cplusplus_winrt) + #define _PPLTASK_ASYNC_LOGGING 1 // Only enable async logging under dev12 winrt + #else + #define _PPLTASK_ASYNC_LOGGING 0 + #endif +#endif /* !_PPLTASK_ASYNC_LOGGING */ +#endif /* _MSC_VER */ + +#pragma pack(push,_CRT_PACKING) + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 28197) +#pragma warning(disable: 4100) // Unreferenced formal parameter - needed for document generation +#pragma warning(disable: 4127) // constant express in if condition - we use it for meta programming +#endif /* defined(_MSC_VER) */ + +// All CRT public header files are required to be protected from the macro new +#pragma push_macro("new") +#undef new + +// stuff ported from Dev11 CRT +// NOTE: this doesn't actually match std::declval. it behaves differently for void! +// so don't blindly change it to std::declval. +namespace stdx +{ + template + _T&& declval(); +} + +/// +/// The pplx namespace provides classes and functions that give you access to the Concurrency Runtime, +/// a concurrent programming framework for C++. For more information, see . +/// +/**/ +namespace pplx +{ +/// +/// A type that represents the terminal state of a task. Valid values are completed and canceled. +/// +/// +/**/ +typedef task_group_status task_status; + +template class task; +template <> class task; + +// In debug builds, default to 10 frames, unless this is overridden prior to #includ'ing ppltasks.h. In retail builds, default to only one frame. +#ifndef PPL_TASK_SAVE_FRAME_COUNT +#ifdef _DEBUG +#define PPL_TASK_SAVE_FRAME_COUNT 10 +#else +#define PPL_TASK_SAVE_FRAME_COUNT 1 +#endif +#endif + +/// +/// Helper macro to determine how many stack frames need to be saved. When any number less or equal to 1 is specified, +/// only one frame is captured and no stackwalk will be involved. Otherwise, the number of callstack frames will be captured. +/// +/// +/// This needs to be defined as a macro rather than a function so that if we're only gathering one frame, _ReturnAddress() +/// will evaluate to client code, rather than a helper function inside of _TaskCreationCallstack, itself. +/// +#if PPL_TASK_SAVE_FRAME_COUNT > 1 +#if defined(__cplusplus_winrt) && !defined(_DEBUG) +#pragma message ("WARNING: Redefinning PPL_TASK_SAVE_FRAME_COUNT under Release build for non-desktop applications is not supported; only one frame will be captured!") +#define _CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) +#else +#define _CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureMultiFramesCallstack(PPL_TASK_SAVE_FRAME_COUNT) +#endif +#else +#define _CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) +#endif + + +/// +/// Returns an indication of whether the task that is currently executing has received a request to cancel its +/// execution. Cancellation is requested on a task if the task was created with a cancellation token, and +/// the token source associated with that token is canceled. +/// +/// +/// true if the currently executing task has received a request for cancellation, false otherwise. +/// +/// +/// If you call this method in the body of a task and it returns true, you must respond with a call to +/// cancel_current_task to acknowledge the cancellation request, +/// after performing any cleanup you need. This will abort the execution of the task and cause it to enter into +/// the canceled state. If you do not respond and continue execution, or return instead of calling +/// cancel_current_task, the task will enter the completed state when it is done. +/// state. +/// A task is not cancellable if it was created without a cancellation token. +/// +/// +/// +/// +/// +/**/ +inline bool _pplx_cdecl is_task_cancellation_requested() +{ + return ::pplx::details::_TaskCollection_t::_Is_cancellation_requested(); +} + +/// +/// Cancels the currently executing task. This function can be called from within the body of a task to abort the +/// task's execution and cause it to enter the canceled state. While it may be used in response to +/// the is_task_cancellation_requested function, you may +/// also use it by itself, to initiate cancellation of the task that is currently executing. +/// It is not a supported scenario to call this function if you are not within the body of a task. +/// Doing so will result in undefined behavior such as a crash or a hang in your application. +/// +/// +/**/ +inline __declspec(noreturn) void _pplx_cdecl cancel_current_task() +{ + throw task_canceled(); +} + +namespace details +{ + /// + /// Callstack container, which is used to capture and preserve callstacks in ppltasks. + /// Members of this class is examined by vc debugger, thus there will be no public access methods. + /// Please note that names of this class should be kept stable for debugger examining. + /// + class _TaskCreationCallstack + { + private: + // If _M_SingleFrame != nullptr, there will be only one frame of callstacks, which is stored in _M_SingleFrame; + // otherwise, _M_Frame will store all the callstack frames. + void* _M_SingleFrame; + std::vector _M_frames; + public: + _TaskCreationCallstack() + { + _M_SingleFrame = nullptr; + } + + // Store one frame of callstack. This function works for both Debug / Release CRT. + static _TaskCreationCallstack _CaptureSingleFrameCallstack(void *_SingleFrame) + { + _TaskCreationCallstack _csc; + _csc._M_SingleFrame = _SingleFrame; + return _csc; + } + + // Capture _CaptureFrames number of callstack frames. This function only work properly for Desktop or Debug CRT. + __declspec(noinline) + static _TaskCreationCallstack _CaptureMultiFramesCallstack(size_t _CaptureFrames) + { + _TaskCreationCallstack _csc; + _csc._M_frames.resize(_CaptureFrames); + // skip 2 frames to make sure callstack starts from user code + _csc._M_frames.resize(::pplx::details::platform::CaptureCallstack(&_csc._M_frames[0], 2, _CaptureFrames)); + return _csc; + } + }; + typedef unsigned char _Unit_type; + + struct _TypeSelectorNoAsync {}; + struct _TypeSelectorAsyncOperationOrTask {}; + struct _TypeSelectorAsyncOperation : public _TypeSelectorAsyncOperationOrTask { }; + struct _TypeSelectorAsyncTask : public _TypeSelectorAsyncOperationOrTask { }; + struct _TypeSelectorAsyncAction {}; + struct _TypeSelectorAsyncActionWithProgress {}; + struct _TypeSelectorAsyncOperationWithProgress {}; + + template + struct _NormalizeVoidToUnitType + { + typedef _Ty _Type; + }; + + template<> + struct _NormalizeVoidToUnitType + { + typedef _Unit_type _Type; + }; + + template + struct _IsUnwrappedAsyncSelector + { + static const bool _Value = true; + }; + + template<> + struct _IsUnwrappedAsyncSelector<_TypeSelectorNoAsync> + { + static const bool _Value = false; + }; + + template + struct _UnwrapTaskType + { + typedef _Ty _Type; + }; + + template + struct _UnwrapTaskType> + { + typedef _Ty _Type; + }; + + template + _TypeSelectorAsyncTask _AsyncOperationKindSelector(task<_T>); + + _TypeSelectorNoAsync _AsyncOperationKindSelector(...); + +#if defined(__cplusplus_winrt) + template + struct _Unhat + { + typedef _Type _Value; + }; + + template + struct _Unhat<_Type^> + { + typedef _Type _Value; + }; + + value struct _NonUserType { public: int _Dummy; }; + + template + struct _ValueTypeOrRefType + { + typedef _NonUserType _Value; + }; + + template + struct _ValueTypeOrRefType<_Type, true> + { + typedef _Type _Value; + }; + + template + _T2 _ProgressTypeSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1,_T2>^); + + template + _T1 _ProgressTypeSelector(Windows::Foundation::IAsyncActionWithProgress<_T1>^); + + template + struct _GetProgressType + { + typedef decltype(_ProgressTypeSelector(stdx::declval<_Type>())) _Value; + }; + + template + struct _IsIAsyncInfo + { + static const bool _Value = __is_base_of(Windows::Foundation::IAsyncInfo, typename _Unhat<_Type>::_Value); + }; + + template + _TypeSelectorAsyncOperation _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperation<_T>^); + + _TypeSelectorAsyncAction _AsyncOperationKindSelector(Windows::Foundation::IAsyncAction^); + + template + _TypeSelectorAsyncOperationWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>^); + + template + _TypeSelectorAsyncActionWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncActionWithProgress<_T>^); + + template ::_Value> + struct _TaskTypeTraits + { + typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; + typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; + typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; + + static const bool _IsAsyncTask = _IsAsync; + static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; + }; + + template + struct _TaskTypeTraits<_Type, true > + { + typedef decltype(((_Type)nullptr)->GetResults()) _TaskRetType; + typedef _TaskRetType _NormalizedTaskRetType; + typedef decltype(_AsyncOperationKindSelector((_Type)nullptr)) _AsyncKind; + + static const bool _IsAsyncTask = true; + static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; + }; + +#else /* defined (__cplusplus_winrt) */ + template + struct _IsIAsyncInfo + { + static const bool _Value = false; + }; + + template + struct _TaskTypeTraits + { + typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; + typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; + typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; + + static const bool _IsAsyncTask = false; + static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; + }; +#endif /* defined (__cplusplus_winrt) */ + + template auto _IsCallable(_Function _Func, int) -> decltype(_Func(), std::true_type()) { (void)(_Func); return std::true_type(); } + template std::false_type _IsCallable(_Function, ...) { return std::false_type(); } + + template <> + struct _TaskTypeTraits + { + typedef void _TaskRetType; + typedef _TypeSelectorNoAsync _AsyncKind; + typedef _Unit_type _NormalizedTaskRetType; + + static const bool _IsAsyncTask = false; + static const bool _IsUnwrappedTaskOrAsync = false; + }; + + template + task<_Type> _To_task(_Type t); + + template + task _To_task_void(_Func f); + + struct _BadContinuationParamType{}; + + template auto _ReturnTypeHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t))); + template auto _ReturnTypeHelper(_Type t, _Function _Func, int, ...) -> decltype(_Func(t)); + template auto _ReturnTypeHelper(_Type t, _Function _Func, ...) -> _BadContinuationParamType; + + template auto _IsTaskHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t)), std::true_type()); + template std::false_type _IsTaskHelper(_Type t, _Function _Func, int, ...); + + template auto _VoidReturnTypeHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func))); + template auto _VoidReturnTypeHelper(_Function _Func, int, ...) -> decltype(_Func()); + + template auto _VoidIsTaskHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func)), std::true_type()); + template std::false_type _VoidIsTaskHelper(_Function _Func, int, ...); + + template + struct _FunctionTypeTraits + { + typedef decltype(_ReturnTypeHelper(stdx::declval<_ExpectedParameterType>(),stdx::declval<_Function>(), 0, 0)) _FuncRetType; + static_assert(!std::is_same<_FuncRetType,_BadContinuationParamType>::value, "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or task<_ExpectedParameterType> (see below)"); + + typedef decltype(_IsTaskHelper(stdx::declval<_ExpectedParameterType>(),stdx::declval<_Function>(), 0, 0)) _Takes_task; + }; + + template + struct _FunctionTypeTraits<_Function, void> + { + typedef decltype(_VoidReturnTypeHelper(stdx::declval<_Function>(), 0, 0)) _FuncRetType; + typedef decltype(_VoidIsTaskHelper(stdx::declval<_Function>(), 0, 0)) _Takes_task; + }; + + template + struct _ContinuationTypeTraits + { + typedef task::_FuncRetType>::_TaskRetType> _TaskOfType; + }; + + // _InitFunctorTypeTraits is used to decide whether a task constructed with a lambda should be unwrapped. Depending on how the variable is + // declared, the constructor may or may not perform unwrapping. For eg. + // + // This declaration SHOULD NOT cause unwrapping + // task> t1([]() -> task { + // task t2([]() {}); + // return t2; + // }); + // + // This declaration SHOULD cause unwrapping + // task> t1([]() -> task { + // task t2([]() {}); + // return t2; + // }); + // If the type of the task is the same as the return type of the function, no unwrapping should take place. Else normal rules apply. + template + struct _InitFunctorTypeTraits + { + typedef typename _TaskTypeTraits<_FuncRetType>::_AsyncKind _AsyncKind; + static const bool _IsAsyncTask = _TaskTypeTraits<_FuncRetType>::_IsAsyncTask; + static const bool _IsUnwrappedTaskOrAsync = _TaskTypeTraits<_FuncRetType>::_IsUnwrappedTaskOrAsync; + }; + + template + struct _InitFunctorTypeTraits + { + typedef _TypeSelectorNoAsync _AsyncKind; + static const bool _IsAsyncTask = false; + static const bool _IsUnwrappedTaskOrAsync = false; + }; + + /// + /// Helper object used for LWT invocation. + /// + struct _TaskProcThunk + { + _TaskProcThunk(const std::function & _Callback) : + _M_func(_Callback) + { + } + + static void _pplx_cdecl _Bridge(void *_PData) + { + _TaskProcThunk *_PThunk = reinterpret_cast<_TaskProcThunk *>(_PData); + _Holder _ThunkHolder(_PThunk); + _PThunk->_M_func(); + } + private: + + // RAII holder + struct _Holder + { + _Holder(_TaskProcThunk * _PThunk) : _M_pThunk(_PThunk) + { + } + + ~_Holder() + { + delete _M_pThunk; + } + + _TaskProcThunk * _M_pThunk; + + private: + _Holder& operator=(const _Holder&); + }; + + std::function _M_func; + _TaskProcThunk& operator=(const _TaskProcThunk&); + }; + + /// + /// Schedule a functor with automatic inlining. Note that this is "fire and forget" scheduling, which cannot be + /// waited on or canceled after scheduling. + /// This schedule method will perform automatic inlining base on . + /// + /// + /// The user functor need to be scheduled. + /// + /// + /// The inlining scheduling policy for current functor. + /// + static void _ScheduleFuncWithAutoInline(const std::function & _Func, _TaskInliningMode_t _InliningMode) + { + _TaskCollection_t::_RunTask(&_TaskProcThunk::_Bridge, new _TaskProcThunk(_Func), _InliningMode); + } + + class _ContextCallback + { + typedef std::function _CallbackFunction; + +#if defined (__cplusplus_winrt) + + public: + + static _ContextCallback _CaptureCurrent() + { + _ContextCallback _Context; + _Context._Capture(); + return _Context; + } + + ~_ContextCallback() + { + _Reset(); + } + + _ContextCallback(bool _DeferCapture = false) + { + if (_DeferCapture) + { + _M_context._M_captureMethod = _S_captureDeferred; + } + else + { + _M_context._M_pContextCallback = nullptr; + } + } + + // Resolves a context that was created as _S_captureDeferred based on the environment (ancestor, current context). + void _Resolve(bool _CaptureCurrent) + { + if(_M_context._M_captureMethod == _S_captureDeferred) + { + _M_context._M_pContextCallback = nullptr; + + if (_CaptureCurrent) + { + if (_IsCurrentOriginSTA()) + { + _Capture(); + } +#if _UITHREADCTXT_SUPPORT + else + { + // This method will fail if not called from the UI thread. + HRESULT _Hr = CaptureUiThreadContext(&_M_context._M_pContextCallback); + if (FAILED(_Hr)) + { + _M_context._M_pContextCallback = nullptr; + } + } +#endif /* _UITHREADCTXT_SUPPORT */ + } + } + } + + void _Capture() + { + HRESULT _Hr = CoGetObjectContext(IID_IContextCallback, reinterpret_cast(&_M_context._M_pContextCallback)); + if (FAILED(_Hr)) + { + _M_context._M_pContextCallback = nullptr; + } + } + + _ContextCallback(const _ContextCallback& _Src) + { + _Assign(_Src._M_context._M_pContextCallback); + } + + _ContextCallback(_ContextCallback&& _Src) + { + _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; + _Src._M_context._M_pContextCallback = nullptr; + } + + _ContextCallback& operator=(const _ContextCallback& _Src) + { + if (this != &_Src) + { + _Reset(); + _Assign(_Src._M_context._M_pContextCallback); + } + return *this; + } + + _ContextCallback& operator=(_ContextCallback&& _Src) + { + if (this != &_Src) + { + _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; + _Src._M_context._M_pContextCallback = nullptr; + } + return *this; + } + + bool _HasCapturedContext() const + { + _ASSERTE(_M_context._M_captureMethod != _S_captureDeferred); + return (_M_context._M_pContextCallback != nullptr); + } + + void _CallInContext(_CallbackFunction _Func) const + { + if (!_HasCapturedContext()) + { + _Func(); + } + else + { + ComCallData callData; + ZeroMemory(&callData, sizeof(callData)); + callData.pUserDefined = reinterpret_cast(&_Func); + + HRESULT _Hr = _M_context._M_pContextCallback->ContextCallback(&_Bridge, &callData, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr); + if (FAILED(_Hr)) + { + throw ::Platform::Exception::CreateException(_Hr); + } + } + } + + bool operator==(const _ContextCallback& _Rhs) const + { + return (_M_context._M_pContextCallback == _Rhs._M_context._M_pContextCallback); + } + + bool operator!=(const _ContextCallback& _Rhs) const + { + return !(operator==(_Rhs)); + } + + private: + void _Reset() + { + if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) + { + _M_context._M_pContextCallback->Release(); + } + } + + void _Assign(IContextCallback *_PContextCallback) + { + _M_context._M_pContextCallback = _PContextCallback; + if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) + { + _M_context._M_pContextCallback->AddRef(); + } + } + + static HRESULT __stdcall _Bridge(ComCallData *_PParam) + { + _CallbackFunction *pFunc = reinterpret_cast<_CallbackFunction *>(_PParam->pUserDefined); + (*pFunc)(); + return S_OK; + } + + // Returns the origin information for the caller (runtime / Windows Runtime apartment as far as task continuations need know) + static bool _IsCurrentOriginSTA() + { + APTTYPE _AptType; + APTTYPEQUALIFIER _AptTypeQualifier; + + HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); + if (SUCCEEDED(hr)) + { + // We determine the origin of a task continuation by looking at where .then is called, so we can tell whether + // to need to marshal the continuation back to the originating apartment. If an STA thread is in executing in + // a neutral aparment when it schedules a continuation, we will not marshal continuations back to the STA, + // since variables used within a neutral apartment are expected to be apartment neutral. + switch(_AptType) + { + case APTTYPE_MAINSTA: + case APTTYPE_STA: + return true; + default: + break; + } + } + return false; + } + + union + { + IContextCallback *_M_pContextCallback; + size_t _M_captureMethod; + } _M_context; + + static const size_t _S_captureDeferred = 1; +#else /* defined (__cplusplus_winrt) */ + public: + + static _ContextCallback _CaptureCurrent() + { + return _ContextCallback(); + } + + _ContextCallback(bool = false) + { + } + + _ContextCallback(const _ContextCallback&) + { + } + + _ContextCallback(_ContextCallback&&) + { + } + + _ContextCallback& operator=(const _ContextCallback&) + { + return *this; + } + + _ContextCallback& operator=(_ContextCallback&&) + { + return *this; + } + + bool _HasCapturedContext() const + { + return false; + } + + void _Resolve(bool) const + { + } + + void _CallInContext(_CallbackFunction _Func) const + { + _Func(); + } + + bool operator==(const _ContextCallback&) const + { + return true; + } + + bool operator!=(const _ContextCallback&) const + { + return false; + } + +#endif /* defined (__cplusplus_winrt) */ + }; + + template + struct _ResultHolder + { + void Set(const _Type& _type) + { + _Result = _type; + } + + _Type Get() + { + return _Result; + } + + _Type _Result; + }; + +#if defined (__cplusplus_winrt) + + template + struct _ResultHolder<_Type^> + { + void Set(_Type^ const & _type) + { + _M_Result = _type; + } + + _Type^ Get() + { + return _M_Result.Get(); + } + private: + // ::Platform::Agile handle specialization of all hats + // including ::Platform::String and ::Platform::Array + ::Platform::Agile<_Type^> _M_Result; + }; + + // + // The below are for composability with tasks auto-created from when_any / when_all / && / || constructs. + // + template + struct _ResultHolder> + { + void Set(const std::vector<_Type^>& _type) + { + _Result.reserve(_type.size()); + + for (auto _PTask = _type.begin(); _PTask != _type.end(); ++_PTask) + { + _Result.emplace_back(*_PTask); + } + } + + std::vector<_Type^> Get() + { + // Return vectory with the objects that are marshaled in the proper appartment + std::vector<_Type^> _Return; + _Return.reserve(_Result.size()); + + for (auto _PTask = _Result.begin(); _PTask != _Result.end(); ++_PTask) + { + _Return.push_back(_PTask->Get()); // Platform::Agile will marshal the object to appropriate appartment if neccessary + } + + return _Return; + } + + std::vector< ::Platform::Agile<_Type^> > _Result; + }; + + template + struct _ResultHolder > + { + void Set(const std::pair<_Type^, size_t>& _type) + { + _M_Result = _type; + } + + std::pair<_Type^, size_t> Get() + { + return std::make_pair(_M_Result.first.Get(), _M_Result.second); + } + private: + std::pair< ::Platform::Agile<_Type^>, size_t> _M_Result; + }; + +#endif /* defined (__cplusplus_winrt) */ + + // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. + // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception + // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. + struct _ExceptionHolder + { + private: + void ReportUnhandledError() + { +#if _MSC_VER >= 1800 && defined(__cplusplus_winrt) + if (_M_winRTException != nullptr) + { + ::Platform::Details::ReportUnhandledError(_M_winRTException); + } +#endif /* defined (__cplusplus_winrt) */ + } + public: + explicit _ExceptionHolder(const std::exception_ptr& _E, const _TaskCreationCallstack &_stackTrace) : + _M_exceptionObserved(0), _M_stdException(_E), _M_stackTrace(_stackTrace) +#if defined (__cplusplus_winrt) + , _M_winRTException(nullptr) +#endif /* defined (__cplusplus_winrt) */ + { + } + +#if defined (__cplusplus_winrt) + explicit _ExceptionHolder(::Platform::Exception^ _E, const _TaskCreationCallstack &_stackTrace) : + _M_exceptionObserved(0), _M_winRTException(_E), _M_stackTrace(_stackTrace) + { + } +#endif /* defined (__cplusplus_winrt) */ + + __declspec(noinline) + ~_ExceptionHolder() + { + if (_M_exceptionObserved == 0) + { + // If you are trapped here, it means an exception thrown in task chain didn't get handled. + // Please add task-based continuation to handle all exceptions coming from tasks. + // this->_M_stackTrace keeps the creation callstack of the task generates this exception. + _REPORT_PPLTASK_UNOBSERVED_EXCEPTION(); + } + } + + void _RethrowUserException() + { + if (_M_exceptionObserved == 0) + { + atomic_exchange(_M_exceptionObserved, 1l); + } + +#if defined (__cplusplus_winrt) + if (_M_winRTException != nullptr) + { + throw _M_winRTException; + } +#endif /* defined (__cplusplus_winrt) */ + std::rethrow_exception(_M_stdException); + } + + // A variable that remembers if this exception was every rethrown into user code (and hence handled by the user). Exceptions that + // are unobserved when the exception holder is destructed will terminate the process. + atomic_long _M_exceptionObserved; + + // Either _M_stdException or _M_winRTException is populated based on the type of exception encountered. + std::exception_ptr _M_stdException; +#if defined (__cplusplus_winrt) + ::Platform::Exception^ _M_winRTException; +#endif /* defined (__cplusplus_winrt) */ + + // Disassembling this value will point to a source instruction right after a call instruction. If the call is to create_task, + // a task constructor or the then method, the task created by that method is the one that encountered this exception. If the call + // is to task_completion_event::set_exception, the set_exception method was the source of the exception. + // DO NOT REMOVE THIS VARIABLE. It is extremely helpful for debugging. + _TaskCreationCallstack _M_stackTrace; + + }; + +#if defined (__cplusplus_winrt) + /// + /// Base converter class for converting asynchronous interfaces to IAsyncOperation + /// + template + ref struct _AsyncInfoImpl abstract : Windows::Foundation::IAsyncOperation<_Result> + { + internal: + // The async action, action with progress or operation with progress that this stub forwards to. + ::Platform::Agile<_AsyncOperationType> _M_asyncInfo; + + Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ _M_CompletedHandler; + + _AsyncInfoImpl( _AsyncOperationType _AsyncInfo ) : _M_asyncInfo(_AsyncInfo) {} + + public: + virtual void Cancel() { _M_asyncInfo.Get()->Cancel(); } + virtual void Close() { _M_asyncInfo.Get()->Close(); } + + virtual property Windows::Foundation::HResult ErrorCode + { + Windows::Foundation::HResult get() + { + return _M_asyncInfo.Get()->ErrorCode; + } + } + + virtual property UINT Id + { + UINT get() + { + return _M_asyncInfo.Get()->Id; + } + } + + virtual property Windows::Foundation::AsyncStatus Status + { + Windows::Foundation::AsyncStatus get() + { + return _M_asyncInfo.Get()->Status; + } + } + + virtual _Result GetResults() { throw std::runtime_error("derived class must implement"); } + + virtual property Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ Completed + { + Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ get() + { + return _M_CompletedHandler; + } + + void set(Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ value) + { + _M_CompletedHandler = value; + _M_asyncInfo.Get()->Completed = ref new _CompletionHandlerType([&](_AsyncOperationType, Windows::Foundation::AsyncStatus status) { + _M_CompletedHandler->Invoke(this, status); + }); + } + } + }; + + /// + /// Class _IAsyncOperationWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncOperationWithProgress into IAsyncOperation + /// + template + ref struct _IAsyncOperationWithProgressToAsyncOperationConverter sealed : + _AsyncInfoImpl^, + Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>, + _Result> + { + internal: + _IAsyncOperationWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncOperationWithProgress<_Result,_Progress>^ _Operation) : + _AsyncInfoImpl^, + Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>, + _Result>(_Operation) {} + + public: + virtual _Result GetResults() override { return _M_asyncInfo.Get()->GetResults(); } + }; + + /// + /// Class _IAsyncActionToAsyncOperationConverter is used to convert an instance of IAsyncAction into IAsyncOperation<_Unit_type> + /// + ref struct _IAsyncActionToAsyncOperationConverter sealed : + _AsyncInfoImpl + { + internal: + _IAsyncActionToAsyncOperationConverter(Windows::Foundation::IAsyncAction^ _Operation) : + _AsyncInfoImpl(_Operation) {} + + public: + virtual details::_Unit_type GetResults() override + { + // Invoke GetResults on the IAsyncAction to allow exceptions to be thrown to higher layers before returning a dummy value. + _M_asyncInfo.Get()->GetResults(); + return details::_Unit_type(); + } + }; + + /// + /// Class _IAsyncActionWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncActionWithProgress into IAsyncOperation<_Unit_type> + /// + template + ref struct _IAsyncActionWithProgressToAsyncOperationConverter sealed : + _AsyncInfoImpl^, + Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, + details::_Unit_type> + { + internal: + _IAsyncActionWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ _Action) : + _AsyncInfoImpl^, + Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, + details::_Unit_type>(_Action) {} + public: + virtual details::_Unit_type GetResults() override + { + // Invoke GetResults on the IAsyncActionWithProgress to allow exceptions to be thrown before returning a dummy value. + _M_asyncInfo.Get()->GetResults(); + return details::_Unit_type(); + } + }; +#endif /* defined (__cplusplus_winrt) */ +} // namespace details + +/// +/// The task_continuation_context class allows you to specify where you would like a continuation to be executed. +/// It is only useful to use this class from a Windows Store app. For non-Windows Store apps, the task continuation's +/// execution context is determined by the runtime, and not configurable. +/// +/// +/**/ +class task_continuation_context : public details::_ContextCallback +{ +public: + + /// + /// Creates the default task continuation context. + /// + /// + /// The default continuation context. + /// + /// + /// The default context is used if you don't specifiy a continuation context when you call the then method. In Windows + /// applications for Windows 7 and below, as well as desktop applications on Windows 8 and higher, the runtime determines where + /// task continuations will execute. However, in a Windows Store app, the default continuation context for a continuation on an + /// apartment aware task is the apartment where then is invoked. + /// An apartment aware task is a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such + /// a task. Therefore, if you schedule a continuation on an apartment aware task in a Windows Runtime STA, the continuation will execute in + /// that STA. + /// A continuation on a non-apartment aware task will execute in a context the Runtime chooses. + /// + /**/ + static task_continuation_context use_default() + { +#if defined (__cplusplus_winrt) + // The callback context is created with the context set to CaptureDeferred and resolved when it is used in .then() + return task_continuation_context(true); // sets it to deferred, is resolved in the constructor of _ContinuationTaskHandle +#else /* defined (__cplusplus_winrt) */ + return task_continuation_context(); +#endif /* defined (__cplusplus_winrt) */ + } + +#if defined (__cplusplus_winrt) + /// + /// Creates a task continuation context which allows the Runtime to choose the execution context for a continuation. + /// + /// + /// A task continuation context that represents an arbitrary location. + /// + /// + /// When this continuation context is used the continuation will execute in a context the runtime chooses even if the antecedent task + /// is apartment aware. + /// use_arbitrary can be used to turn off the default behavior for a continuation on an apartment + /// aware task created in an STA. + /// This method is only available to Windows Store apps. + /// + /**/ + static task_continuation_context use_arbitrary() + { + task_continuation_context _Arbitrary(true); + _Arbitrary._Resolve(false); + return _Arbitrary; + } + + /// + /// Returns a task continuation context object that represents the current execution context. + /// + /// + /// The current execution context. + /// + /// + /// This method captures the caller's Windows Runtime context so that continuations can be executed in the right apartment. + /// The value returned by use_current can be used to indicate to the Runtime that the continuation should execute in + /// the captured context (STA vs MTA) regardless of whether or not the antecedent task is apartment aware. An apartment aware task is + /// a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such a task. + /// This method is only available to Windows Store apps. + /// + /**/ + static task_continuation_context use_current() + { + task_continuation_context _Current(true); + _Current._Resolve(true); + return _Current; + } +#endif /* defined (__cplusplus_winrt) */ + +private: + + task_continuation_context(bool _DeferCapture = false) : details::_ContextCallback(_DeferCapture) + { + } +}; + +class task_options; +namespace details +{ + struct _Internal_task_options + { + bool _M_hasPresetCreationCallstack; + _TaskCreationCallstack _M_presetCreationCallstack; + + void _set_creation_callstack(const _TaskCreationCallstack &_callstack) + { + _M_hasPresetCreationCallstack = true; + _M_presetCreationCallstack = _callstack; + } + _Internal_task_options() + { + _M_hasPresetCreationCallstack = false; + } + }; + + inline _Internal_task_options &_get_internal_task_options(task_options &options); + inline const _Internal_task_options &_get_internal_task_options(const task_options &options); +} +/// +/// Represents the allowed options for creating a task +/// +class task_options +{ +public: + + + /// + /// Default list of task creation options + /// + task_options() + : _M_Scheduler(get_ambient_scheduler()), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(false) + { + } + + /// + /// Task option that specify a cancellation token + /// + task_options(cancellation_token _Token) + : _M_Scheduler(get_ambient_scheduler()), + _M_CancellationToken(_Token), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(true), + _M_HasScheduler(false) + { + } + + /// + /// Task option that specify a continuation context. This is valid only for continuations (then) + /// + task_options(task_continuation_context _ContinuationContext) + : _M_Scheduler(get_ambient_scheduler()), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(_ContinuationContext), + _M_HasCancellationToken(false), + _M_HasScheduler(false) + { + } + + /// + /// Task option that specify a cancellation token and a continuation context. This is valid only for continuations (then) + /// + task_options(cancellation_token _Token, task_continuation_context _ContinuationContext) + : _M_Scheduler(get_ambient_scheduler()), + _M_CancellationToken(_Token), + _M_ContinuationContext(_ContinuationContext), + _M_HasCancellationToken(false), + _M_HasScheduler(false) + { + } + + /// + /// Task option that specify a scheduler with shared lifetime + /// + template + task_options(std::shared_ptr<_SchedType> _Scheduler) + : _M_Scheduler(std::move(_Scheduler)), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(true) + { + } + + /// + /// Task option that specify a scheduler reference + /// + task_options(scheduler_interface& _Scheduler) + : _M_Scheduler(&_Scheduler), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(true) + { + } + + /// + /// Task option that specify a scheduler + /// + task_options(scheduler_ptr _Scheduler) + : _M_Scheduler(std::move(_Scheduler)), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(true) + { + } + + /// + /// Task option copy constructor + /// + task_options(const task_options& _TaskOptions) + : _M_Scheduler(_TaskOptions.get_scheduler()), + _M_CancellationToken(_TaskOptions.get_cancellation_token()), + _M_ContinuationContext(_TaskOptions.get_continuation_context()), + _M_HasCancellationToken(_TaskOptions.has_cancellation_token()), + _M_HasScheduler(_TaskOptions.has_scheduler()) + { + } + + /// + /// Sets the given token in the options + /// + void set_cancellation_token(cancellation_token _Token) + { + _M_CancellationToken = _Token; + _M_HasCancellationToken = true; + } + + /// + /// Sets the given continuation context in the options + /// + void set_continuation_context(task_continuation_context _ContinuationContext) + { + _M_ContinuationContext = _ContinuationContext; + } + + /// + /// Indicates whether a cancellation token was specified by the user + /// + bool has_cancellation_token() const + { + return _M_HasCancellationToken; + } + + /// + /// Returns the cancellation token + /// + cancellation_token get_cancellation_token() const + { + return _M_CancellationToken; + } + + /// + /// Returns the continuation context + /// + task_continuation_context get_continuation_context() const + { + return _M_ContinuationContext; + } + + /// + /// Indicates whether a scheduler n was specified by the user + /// + bool has_scheduler() const + { + return _M_HasScheduler; + } + + /// + /// Returns the scheduler + /// + scheduler_ptr get_scheduler() const + { + return _M_Scheduler; + } + +private: + + task_options const& operator=(task_options const& _Right); + friend details::_Internal_task_options &details::_get_internal_task_options(task_options &); + friend const details::_Internal_task_options &details::_get_internal_task_options(const task_options &); + + scheduler_ptr _M_Scheduler; + cancellation_token _M_CancellationToken; + task_continuation_context _M_ContinuationContext; + details::_Internal_task_options _M_InternalTaskOptions; + bool _M_HasCancellationToken; + bool _M_HasScheduler; +}; + +namespace details +{ + inline _Internal_task_options & _get_internal_task_options(task_options &options) + { + return options._M_InternalTaskOptions; + } + inline const _Internal_task_options & _get_internal_task_options(const task_options &options) + { + return options._M_InternalTaskOptions; + } + + struct _Task_impl_base; + template struct _Task_impl; + + template + struct _Task_ptr + { + typedef std::shared_ptr<_Task_impl<_ReturnType>> _Type; + static _Type _Make(_CancellationTokenState * _Ct, scheduler_ptr _Scheduler_arg) { return std::make_shared<_Task_impl<_ReturnType>>(_Ct, _Scheduler_arg); } + }; + + typedef _TaskCollection_t::_TaskProcHandle_t _UnrealizedChore_t; + typedef std::shared_ptr<_Task_impl_base> _Task_ptr_base; + + // The weak-typed base task handler for continuation tasks. + struct _ContinuationTaskHandleBase : _UnrealizedChore_t + { + _ContinuationTaskHandleBase * _M_next; + task_continuation_context _M_continuationContext; + bool _M_isTaskBasedContinuation; + + // This field gives inlining scheduling policy for current chore. + _TaskInliningMode_t _M_inliningMode; + + virtual _Task_ptr_base _GetTaskImplBase() const = 0; + + _ContinuationTaskHandleBase() : + _M_next(nullptr), _M_continuationContext(task_continuation_context::use_default()), _M_isTaskBasedContinuation(false), _M_inliningMode(details::_NoInline) + { + } + + virtual ~_ContinuationTaskHandleBase() {} + }; + +#if _PPLTASK_ASYNC_LOGGING + // GUID used for identifying causality logs from PPLTask + const ::Platform::Guid _PPLTaskCausalityPlatformID(0x7A76B220, 0xA758, 0x4E6E, 0xB0, 0xE0, 0xD7, 0xC6, 0xD7, 0x4A, 0x88, 0xFE); + + __declspec(selectany) volatile long _isCausalitySupported = 0; + + inline bool _IsCausalitySupported() + { +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + if (_isCausalitySupported == 0) + { + long _causality = 1; + OSVERSIONINFOEX _osvi = {}; + _osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + // The Causality is supported on Windows version higher than Windows 8 + _osvi.dwMajorVersion = 6; + _osvi.dwMinorVersion = 3; + + DWORDLONG _conditionMask = 0; + VER_SET_CONDITION( _conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL ); + VER_SET_CONDITION( _conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL ); + + if ( ::VerifyVersionInfo(&_osvi, VER_MAJORVERSION | VER_MINORVERSION, _conditionMask)) + { + _causality = 2; + } + + _isCausalitySupported = _causality; + return _causality == 2; + } + + return _isCausalitySupported == 2 ? true : false; +#else + return true; +#endif + } + + // Stateful logger rests inside task_impl_base. + struct _TaskEventLogger + { + _Task_impl_base *_M_task; + bool _M_scheduled; + bool _M_taskPostEventStarted; + + // Log before scheduling task + void _LogScheduleTask(bool _isContinuation) + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCreation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), + _isContinuation ? "pplx::PPLTask::ScheduleContinuationTask" : "pplx::PPLTask::ScheduleTask", 0); + _M_scheduled = true; + } + } + + // It will log the cancel event but not canceled state. _LogTaskCompleted will log the terminal state, which includes cancel state. + void _LogCancelTask() + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationRelation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Important, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalityRelation::Cancel); + + } + } + + // Log when task reaches terminal state. Note: the task can reach a terminal state (by cancellation or exception) without having run + void _LogTaskCompleted(); + + // Log when task body (which includes user lambda and other scheduling code) begin to run + void _LogTaskExecutionStarted() { } + + // Log when task body finish executing + void _LogTaskExecutionCompleted() + { + if (_M_taskPostEventStarted && details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); + } + } + + // Log right before user lambda being invoked + void _LogWorkItemStarted() + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); + } + } + + // Log right after user lambda being invoked + void _LogWorkItemCompleted() + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); + + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); + _M_taskPostEventStarted = true; + } + } + + _TaskEventLogger(_Task_impl_base *_task): _M_task(_task) + { + _M_scheduled = false; + _M_taskPostEventStarted = false; + } + }; + + // Exception safe logger for user lambda + struct _TaskWorkItemRAIILogger + { + _TaskEventLogger &_M_logger; + _TaskWorkItemRAIILogger(_TaskEventLogger &_taskHandleLogger): _M_logger(_taskHandleLogger) + { + _M_logger._LogWorkItemStarted(); + } + + ~_TaskWorkItemRAIILogger() + { + _M_logger._LogWorkItemCompleted(); + } + _TaskWorkItemRAIILogger &operator =(const _TaskWorkItemRAIILogger &); // cannot be assigned + }; + +#else + inline void _LogCancelTask(_Task_impl_base *) {} + struct _TaskEventLogger + { + void _LogScheduleTask(bool) {} + void _LogCancelTask() {} + void _LogWorkItemStarted() {} + void _LogWorkItemCompleted() {} + void _LogTaskExecutionStarted() {} + void _LogTaskExecutionCompleted() {} + void _LogTaskCompleted() {} + _TaskEventLogger(_Task_impl_base *) {} + }; + struct _TaskWorkItemRAIILogger + { + _TaskWorkItemRAIILogger(_TaskEventLogger &) {} + }; +#endif + + /// + /// The _PPLTaskHandle is the strong-typed task handle base. All user task functions need to be wrapped in this task handler + /// to be executable by PPL. By deriving from a different _BaseTaskHandle, it can be used for both initial tasks and continuation tasks. + /// For initial tasks, _PPLTaskHandle will be derived from _UnrealizedChore_t, and for continuation tasks, it will be derived from + /// _ContinuationTaskHandleBase. The life time of the _PPLTaskHandle object is be managed by runtime if task handle is scheduled. + /// + /// + /// The result type of the _Task_impl. + /// + /// + /// The derived task handle class. The operator () needs to be implemented. + /// + /// + /// The base class from which _PPLTaskHandle should be derived. This is either _UnrealizedChore_t or _ContinuationTaskHandleBase. + /// + template + struct _PPLTaskHandle : _BaseTaskHandle + { + _PPLTaskHandle(const typename _Task_ptr<_ReturnType>::_Type & _PTask) : _M_pTask(_PTask) + { + } + + virtual ~_PPLTaskHandle() + { + // Here is the sink of all task completion code paths + _M_pTask->_M_taskEventLogger._LogTaskCompleted(); + } + + virtual void invoke() const + { + // All exceptions should be rethrown to finish cleanup of the task collection. They will be caught and handled + // by the runtime. + _ASSERTE((bool)_M_pTask); + if (!_M_pTask->_TransitionedToStarted()) + { + static_cast(this)->_SyncCancelAndPropagateException(); + return; + } + + _M_pTask->_M_taskEventLogger._LogTaskExecutionStarted(); + try + { + // All derived task handle must implement this contract function. + static_cast(this)->_Perform(); + } + catch(const task_canceled &) + { + _M_pTask->_Cancel(true); + } + catch(const _Interruption_exception &) + { + _M_pTask->_Cancel(true); + } +#if defined (__cplusplus_winrt) + catch(::Platform::Exception^ _E) + { + _M_pTask->_CancelWithException(_E); + } +#endif /* defined (__cplusplus_winrt) */ + catch(...) + { + _M_pTask->_CancelWithException(std::current_exception()); + } + _M_pTask->_M_taskEventLogger._LogTaskExecutionCompleted(); + } + + // Cast _M_pTask pointer to "type-less" _Task_impl_base pointer, which can be used in _ContinuationTaskHandleBase. + // The return value should be automatically optimized by R-value ref. + _Task_ptr_base _GetTaskImplBase() const + { + return _M_pTask; + } + + typename _Task_ptr<_ReturnType>::_Type _M_pTask; + private: + _PPLTaskHandle const & operator=(_PPLTaskHandle const&); // no assignment operator + }; + + /// + /// The base implementation of a first-class task. This class contains all the non-type specific + /// implementation details of the task. + /// + /**/ + struct _Task_impl_base + { + enum _TaskInternalState + { + // Tracks the state of the task, rather than the task collection on which the task is scheduled + _Created, + _Started, + _PendingCancel, + _Completed, + _Canceled + }; +// _M_taskEventLogger - 'this' : used in base member initializer list +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4355) +#endif + _Task_impl_base(_CancellationTokenState * _PTokenState, scheduler_ptr _Scheduler_arg) + : _M_TaskState(_Created), + _M_fFromAsync(false), _M_fUnwrappedTask(false), + _M_pRegistration(nullptr), _M_Continuations(nullptr), _M_TaskCollection(_Scheduler_arg), + _M_taskEventLogger(this) + { + // Set cancelation token + _M_pTokenState = _PTokenState; + _ASSERTE(_M_pTokenState != nullptr); + if (_M_pTokenState != _CancellationTokenState::_None()) + _M_pTokenState->_Reference(); + } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + + virtual ~_Task_impl_base() + { + _ASSERTE(_M_pTokenState != nullptr); + if (_M_pTokenState != _CancellationTokenState::_None()) + { + _M_pTokenState->_Release(); + } + } + + task_status _Wait() + { + bool _DoWait = true; + +#if defined (__cplusplus_winrt) + if (_IsNonBlockingThread()) + { + // In order to prevent Windows Runtime STA threads from blocking the UI, calling task.wait() task.get() is illegal + // if task has not been completed. + if (!_IsCompleted() && !_IsCanceled()) + { + throw invalid_operation("Illegal to wait on a task in a Windows Runtime STA"); + } + else + { + // Task Continuations are 'scheduled' *inside* the chore that is executing on the ancestors's task group. If a continuation + // needs to be marshalled to a different apartment, instead of scheduling, we make a synchronous cross apartment COM + // call to execute the continuation. If it then happens to do something which waits on the ancestor (say it calls .get(), which + // task based continuations are wont to do), waiting on the task group results in on the chore that is making this + // synchronous callback, which causes a deadlock. To avoid this, we test the state ancestor's event , and we will NOT wait on + // if it has finished execution (which means now we are on the inline synchronous callback). + _DoWait = false; + } + } +#endif /* defined (__cplusplus_winrt) */ + if (_DoWait) + { + // If this task was created from a Windows Runtime async operation, do not attempt to inline it. The + // async operation will take place on a thread in the appropriate apartment Simply wait for the completed + // event to be set. + if (_M_fFromAsync) + { + _M_TaskCollection._Wait(); + } + else + { + // Wait on the task collection to complete. The task collection is guaranteed to still be + // valid since the task must be still within scope so that the _Task_impl_base destructor + // has not yet been called. This call to _Wait potentially inlines execution of work. + try + { + // Invoking wait on a task collection resets the state of the task collection. This means that + // if the task collection itself were canceled, or had encountered an exception, only the first + // call to wait will receive this status. However, both cancellation and exceptions flowing through + // tasks set state in the task impl itself. + + // When it returns cancelled, either work chore or the cancel thread should already have set task's state + // properly -- cancelled state or completed state (because there was no interruption point). + // For tasks with unwrapped tasks, we should not change the state of current task, since the unwrapped task are still running. + _M_TaskCollection._RunAndWait(); + } + catch(details::_Interruption_exception&) + { + // The _TaskCollection will never be an interruption point since it has a none token. + _ASSERTE(false); + } + catch(task_canceled&) + { + // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task + // must be called from code that is executed within the task (throwing it from parallel work created by and waited + // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen + // the exception and canceled the task. Swallow the exception here. + _ASSERTE(_IsCanceled()); + } +#if defined (__cplusplus_winrt) + catch(::Platform::Exception^ _E) + { + // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. + if(!_HasUserException()) + { + _CancelWithException(_E); + } + // Rethrow will mark the exception as observed. + _M_exceptionHolder->_RethrowUserException(); + } +#endif /* defined (__cplusplus_winrt) */ + catch(...) + { + // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. + if(!_HasUserException()) + { + _CancelWithException(std::current_exception()); + } + // Rethrow will mark the exception as observed. + _M_exceptionHolder->_RethrowUserException(); + } + + // If the lambda body for this task (executed or waited upon in _RunAndWait above) happened to return a task + // which is to be unwrapped and plumbed to the output of this task, we must not only wait on the lambda body, we must + // wait on the **INNER** body. It is in theory possible that we could inline such if we plumb a series of things through; + // however, this takes the tact of simply waiting upon the completion signal. + if (_M_fUnwrappedTask) + { + _M_TaskCollection._Wait(); + } + } + } + + if (_HasUserException()) + { + _M_exceptionHolder->_RethrowUserException(); + } + else if (_IsCanceled()) + { + return canceled; + } + _ASSERTE(_IsCompleted()); + return completed; + } + + /// + /// Requests cancellation on the task and schedules continuations if the task can be transitioned to a terminal state. + /// + /// + /// Set to true if the cancel takes place as a result of the task body encountering an exception, or because an ancestor or task_completion_event the task + /// was registered with were canceled with an exception. A synchronous cancel is one that assures the task could not be running on a different thread at + /// the time the cancellation is in progress. An asynchronous cancel is one where the thread performing the cancel has no control over the thread that could + /// be executing the task, that is the task could execute concurrently while the cancellation is in progress. + /// + /// + /// Whether an exception other than the internal runtime cancellation exceptions caused this cancellation. + /// + /// + /// Whether this exception came from an ancestor task or a task_completion_event as opposed to an exception that was encountered by the task itself. Only valid when + /// _UserException is set to true. + /// + /// + /// The exception holder that represents the exception. Only valid when _UserException is set to true. + /// + virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder>& _ExHolder) = 0; + + bool _Cancel(bool _SynchronousCancel) + { + // Send in a dummy value for exception. It is not used when the first parameter is false. + return _CancelAndRunContinuations(_SynchronousCancel, false, false, _M_exceptionHolder); + } + + bool _CancelWithExceptionHolder(const std::shared_ptr<_ExceptionHolder>& _ExHolder, bool _PropagatedFromAncestor) + { + // This task was canceled because an ancestor task encountered an exception. + return _CancelAndRunContinuations(true, true, _PropagatedFromAncestor, _ExHolder); + } + +#if defined (__cplusplus_winrt) + bool _CancelWithException(::Platform::Exception^ _Exception) + { + // This task was canceled because the task body encountered an exception. + _ASSERTE(!_HasUserException()); + return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); + } +#endif /* defined (__cplusplus_winrt) */ + + bool _CancelWithException(const std::exception_ptr& _Exception) + { + // This task was canceled because the task body encountered an exception. + _ASSERTE(!_HasUserException()); + return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); + } + + void _RegisterCancellation(std::weak_ptr<_Task_impl_base> _WeakPtr) + { + _ASSERTE(details::_CancellationTokenState::_IsValid(_M_pTokenState)); + + auto _CancellationCallback = [_WeakPtr](){ + // Taking ownership of the task prevents dead lock during destruction + // if the destructor waits for the cancellations to be finished + auto _task = _WeakPtr.lock(); + if (_task != nullptr) + _task->_Cancel(false); + }; + + _M_pRegistration = new details::_CancellationTokenCallback(_CancellationCallback); + _M_pTokenState->_RegisterCallback(_M_pRegistration); + } + + void _DeregisterCancellation() + { + if (_M_pRegistration != nullptr) + { + _M_pTokenState->_DeregisterCallback(_M_pRegistration); + _M_pRegistration->_Release(); + _M_pRegistration = nullptr; + } + } + + bool _IsCreated() + { + return (_M_TaskState == _Created); + } + + bool _IsStarted() + { + return (_M_TaskState == _Started); + } + + bool _IsPendingCancel() + { + return (_M_TaskState == _PendingCancel); + } + + bool _IsCompleted() + { + return (_M_TaskState == _Completed); + } + + bool _IsCanceled() + { + return (_M_TaskState == _Canceled); + } + + bool _HasUserException() + { + return static_cast(_M_exceptionHolder); + } + + const std::shared_ptr<_ExceptionHolder>& _GetExceptionHolder() + { + _ASSERTE(_HasUserException()); + return _M_exceptionHolder; + } + + bool _IsApartmentAware() + { + return _M_fFromAsync; + } + + void _SetAsync(bool _Async = true) + { + _M_fFromAsync = _Async; + } + + _TaskCreationCallstack _GetTaskCreationCallstack() + { + return _M_pTaskCreationCallstack; + } + + void _SetTaskCreationCallstack(const _TaskCreationCallstack &_Callstack) + { + _M_pTaskCreationCallstack = _Callstack; + } + + /// + /// Helper function to schedule the task on the Task Collection. + /// + /// + /// The task chore handle that need to be executed. + /// + /// + /// The inlining scheduling policy for current _PTaskHandle. + /// + void _ScheduleTask(_UnrealizedChore_t * _PTaskHandle, _TaskInliningMode_t _InliningMode) + { + try + { + _M_TaskCollection._ScheduleTask(_PTaskHandle, _InliningMode); + } + catch(const task_canceled &) + { + // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task + // must be called from code that is executed within the task (throwing it from parallel work created by and waited + // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen + // the exception and canceled the task. Swallow the exception here. + _ASSERTE(_IsCanceled()); + } + catch(const _Interruption_exception &) + { + // The _TaskCollection will never be an interruption point since it has a none token. + _ASSERTE(false); + } + catch(...) + { + // The exception could have come from two places: + // 1. From the chore body, so it already should have been caught and canceled. + // In this case swallow the exception. + // 2. From trying to actually schedule the task on the scheduler. + // In this case cancel the task with the current exception, otherwise the + // task will never be signaled leading to deadlock when waiting on the task. + if (!_HasUserException()) + { + _CancelWithException(std::current_exception()); + } + } + } + + /// + /// Function executes a continuation. This function is recorded by a parent task implementation + /// when a continuation is created in order to execute later. + /// + /// + /// The continuation task chore handle that need to be executed. + /// + /**/ + void _RunContinuation(_ContinuationTaskHandleBase * _PTaskHandle) + { + _Task_ptr_base _ImplBase = _PTaskHandle->_GetTaskImplBase(); + if (_IsCanceled() && !_PTaskHandle->_M_isTaskBasedContinuation) + { + if (_HasUserException()) + { + // If the ancestor encountered an exception, transfer the exception to the continuation + // This traverses down the tree to propagate the exception. + _ImplBase->_CancelWithExceptionHolder(_GetExceptionHolder(), true); + } + else + { + // If the ancestor was canceled, then your own execution should be canceled. + // This traverses down the tree to cancel it. + _ImplBase->_Cancel(true); + } + } + else + { + // This can only run when the ancestor has completed or it's a task based continuation that fires when a task is canceled + // (with or without a user exception). + _ASSERTE(_IsCompleted() || _PTaskHandle->_M_isTaskBasedContinuation); + _ASSERTE(!_ImplBase->_IsCanceled()); + return _ImplBase->_ScheduleContinuationTask(_PTaskHandle); + } + + // If the handle is not scheduled, we need to manually delete it. + delete _PTaskHandle; + } + + // Schedule a continuation to run + void _ScheduleContinuationTask(_ContinuationTaskHandleBase * _PTaskHandle) + { + + _M_taskEventLogger._LogScheduleTask(true); + // Ensure that the continuation runs in proper context (this might be on a Concurrency Runtime thread or in a different Windows Runtime apartment) + if (_PTaskHandle->_M_continuationContext._HasCapturedContext()) + { + // For those continuations need to be scheduled inside captured context, we will try to apply automatic inlining to their inline modes, + // if they haven't been specified as _ForceInline yet. This change will encourage those continuations to be executed inline so that reduce + // the cost of marshaling. + // For normal continuations we won't do any change here, and their inline policies are completely decided by ._ThenImpl method. + if (_PTaskHandle->_M_inliningMode != details::_ForceInline) + { + _PTaskHandle->_M_inliningMode = details::_DefaultAutoInline; + } + _ScheduleFuncWithAutoInline([_PTaskHandle]() { + // Note that we cannot directly capture "this" pointer, instead, we should use _TaskImplPtr, a shared_ptr to the _Task_impl_base. + // Because "this" pointer will be invalid as soon as _PTaskHandle get deleted. _PTaskHandle will be deleted after being scheduled. + auto _TaskImplPtr = _PTaskHandle->_GetTaskImplBase(); + if (details::_ContextCallback::_CaptureCurrent() == _PTaskHandle->_M_continuationContext) + { + _TaskImplPtr->_ScheduleTask(_PTaskHandle, details::_ForceInline); + } + else + { + // + // It's entirely possible that the attempt to marshal the call into a differing context will fail. In this case, we need to handle + // the exception and mark the continuation as canceled with the appropriate exception. There is one slight hitch to this: + // + // NOTE: COM's legacy behavior is to swallow SEH exceptions and marshal them back as HRESULTS. This will in effect turn an SEH into + // a C++ exception that gets tagged on the task. One unfortunate result of this is that various pieces of the task infrastructure will + // not be in a valid state after this in /EHsc (due to the lack of destructors running, etc...). + // + try + { + // Dev10 compiler needs this! + auto _PTaskHandle1 = _PTaskHandle; + _PTaskHandle->_M_continuationContext._CallInContext( [_PTaskHandle1, _TaskImplPtr](){ + _TaskImplPtr->_ScheduleTask(_PTaskHandle1, details::_ForceInline); + }); + } +#if defined (__cplusplus_winrt) + catch(::Platform::Exception^ _E) + { + _TaskImplPtr->_CancelWithException(_E); + } +#endif /* defined (__cplusplus_winrt) */ + catch(...) + { + _TaskImplPtr->_CancelWithException(std::current_exception()); + } + } + }, _PTaskHandle->_M_inliningMode); + } + else + { + _ScheduleTask(_PTaskHandle, _PTaskHandle->_M_inliningMode); + } + } + + /// + /// Schedule the actual continuation. This will either schedule the function on the continuation task's implementation + /// if the task has completed or append it to a list of functions to execute when the task actually does complete. + /// + /// + /// The input type of the task. + /// + /// + /// The output type of the task. + /// + /**/ + void _ScheduleContinuation(_ContinuationTaskHandleBase * _PTaskHandle) + { + enum { _Nothing, _Schedule, _Cancel, _CancelWithException } _Do = _Nothing; + + // If the task has canceled, cancel the continuation. If the task has completed, execute the continuation right away. + // Otherwise, add it to the list of pending continuations + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + if (_IsCompleted() || (_IsCanceled() && _PTaskHandle->_M_isTaskBasedContinuation)) + { + _Do = _Schedule; + } + else if (_IsCanceled()) + { + if (_HasUserException()) + { + _Do = _CancelWithException; + } + else + { + _Do = _Cancel; + } + } + else + { + // chain itself on the continuation chain. + _PTaskHandle->_M_next = _M_Continuations; + _M_Continuations = _PTaskHandle; + } + } + + // Cancellation and execution of continuations should be performed after releasing the lock. Continuations off of + // async tasks may execute inline. + switch (_Do) + { + case _Schedule: + { + _PTaskHandle->_GetTaskImplBase()->_ScheduleContinuationTask(_PTaskHandle); + break; + } + case _Cancel: + { + // If the ancestor was canceled, then your own execution should be canceled. + // This traverses down the tree to cancel it. + _PTaskHandle->_GetTaskImplBase()->_Cancel(true); + + delete _PTaskHandle; + break; + } + case _CancelWithException: + { + // If the ancestor encountered an exception, transfer the exception to the continuation + // This traverses down the tree to propagate the exception. + _PTaskHandle->_GetTaskImplBase()->_CancelWithExceptionHolder(_GetExceptionHolder(), true); + + delete _PTaskHandle; + break; + } + case _Nothing: + default: + // In this case, we have inserted continuation to continuation chain, + // nothing more need to be done, just leave. + break; + } + } + + void _RunTaskContinuations() + { + // The link list can no longer be modified at this point, + // since all following up continuations will be scheduled by themselves. + _ContinuationList _Cur = _M_Continuations, _Next; + _M_Continuations = nullptr; + while (_Cur) + { + // Current node might be deleted after running, + // so we must fetch the next first. + _Next = _Cur->_M_next; + _RunContinuation(_Cur); + _Cur = _Next; + } + } + +#if defined (__cplusplus_winrt) + static bool _IsNonBlockingThread() + { + APTTYPE _AptType; + APTTYPEQUALIFIER _AptTypeQualifier; + + HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); + // + // If it failed, it's not a Windows Runtime/COM initialized thread. This is not a failure. + // + if (SUCCEEDED(hr)) + { + switch(_AptType) + { + case APTTYPE_STA: + case APTTYPE_MAINSTA: + return true; + break; + case APTTYPE_NA: + switch(_AptTypeQualifier) + { + // A thread executing in a neutral apartment is either STA or MTA. To find out if this thread is allowed + // to wait, we check the app qualifier. If it is an STA thread executing in a neutral apartment, waiting + // is illegal, because the thread is responsible for pumping messages and waiting on a task could take the + // thread out of circulation for a while. + case APTTYPEQUALIFIER_NA_ON_STA: + case APTTYPEQUALIFIER_NA_ON_MAINSTA: + return true; + break; + } + break; + } + } + +#if _UITHREADCTXT_SUPPORT + // This method is used to throw an exepection in _Wait() if called within STA. We + // want the same behavior if _Wait is called on the UI thread. + if (SUCCEEDED(CaptureUiThreadContext(nullptr))) + { + return true; + } +#endif /* _UITHREADCTXT_SUPPORT */ + + return false; + } + + template + static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type & _OuterTask, + Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) + { + // This method is invoked either when a task is created from an existing async operation or + // when a lambda that creates an async operation executes. + + // If the outer task is pending cancel, cancel the async operation before setting the completed handler. The COM reference on + // the IAsyncInfo object will be released when all ^references to the operation go out of scope. + + // This assertion uses the existence of taskcollection to determine if the task was created from an event. + // That is no longer valid as even tasks created from a user lambda could have no underlying taskcollection + // when a custom scheduler is used. + // _ASSERTE((!_OuterTask->_M_TaskCollection._IsCreated() || _OuterTask->_M_fUnwrappedTask) && !_OuterTask->_IsCanceled()); + + // Pass the shared_ptr by value into the lambda instead of using 'this'. + _AsyncOp->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType>( + [_OuterTask](Windows::Foundation::IAsyncOperation::_Value>^ _Operation, Windows::Foundation::AsyncStatus _Status) mutable + { + if (_Status == Windows::Foundation::AsyncStatus::Canceled) + { + _OuterTask->_Cancel(true); + } + else if (_Status == Windows::Foundation::AsyncStatus::Error) + { + _OuterTask->_CancelWithException(::Platform::Exception::ReCreateException(static_cast(_Operation->ErrorCode.Value))); + } + else + { + _ASSERTE(_Status == Windows::Foundation::AsyncStatus::Completed); + _OuterTask->_FinalizeAndRunContinuations(_Operation->GetResults()); + } + + // Take away this shared pointers reference on the task instead of waiting for the delegate to be released. It could + // be released on a different thread after a delay, and not releasing the reference here could cause the tasks to hold + // on to resources longer than they should. As an example, without this reset, writing to a file followed by reading from + // it using the Windows Runtime Async APIs causes a sharing violation. + // Using const_cast is the workaround for failed mutable keywords + const_cast<_Task_ptr<_ReturnType>::_Type &>(_OuterTask).reset(); + }); + _OuterTask->_SetUnwrappedAsyncOp(_AsyncOp); + } +#endif /* defined (__cplusplus_winrt) */ + + template + static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type& _OuterTask, const task<_InternalReturnType> & _UnwrappedTask) + { + _ASSERTE(_OuterTask->_M_fUnwrappedTask && !_OuterTask->_IsCanceled()); + + // + // We must ensure that continuations off _OuterTask (especially exception handling ones) continue to function in the + // presence of an exception flowing out of the inner task _UnwrappedTask. This requires an exception handling continuation + // off the inner task which does the appropriate funnelling to the outer one. We use _Then instead of then to prevent + // the exception from being marked as observed by our internal continuation. This continuation must be scheduled regardless + // of whether or not the _OuterTask task is canceled. + // + _UnwrappedTask._Then([_OuterTask] (task<_InternalReturnType> _AncestorTask) { + + if (_AncestorTask._GetImpl()->_IsCompleted()) + { + _OuterTask->_FinalizeAndRunContinuations(_AncestorTask._GetImpl()->_GetResult()); + } + else + { + _ASSERTE(_AncestorTask._GetImpl()->_IsCanceled()); + if (_AncestorTask._GetImpl()->_HasUserException()) + { + // Set _PropagatedFromAncestor to false, since _AncestorTask is not an ancestor of _UnwrappedTask. + // Instead, it is the enclosing task. + _OuterTask->_CancelWithExceptionHolder(_AncestorTask._GetImpl()->_GetExceptionHolder(), false); + } + else + { + _OuterTask->_Cancel(true); + } + } + }, nullptr, details::_DefaultAutoInline); + + } + + scheduler_ptr _GetScheduler() const + { + return _M_TaskCollection._GetScheduler(); + } + + // Tracks the internal state of the task + volatile _TaskInternalState _M_TaskState; + // Set to true either if the ancestor task had the flag set to true, or if the lambda that does the work of this task returns an + // async operation or async action that is unwrapped by the runtime. + bool _M_fFromAsync; + // Set to true when a continuation unwraps a task or async operation. + bool _M_fUnwrappedTask; + + // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. + // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception + // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. + std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; + + ::pplx::extensibility::critical_section_t _M_ContinuationsCritSec; + + // The cancellation token state. + _CancellationTokenState * _M_pTokenState; + + // The registration on the token. + _CancellationTokenRegistration * _M_pRegistration; + + typedef _ContinuationTaskHandleBase * _ContinuationList; + _ContinuationList _M_Continuations; + + // The async task collection wrapper + ::pplx::details::_TaskCollection_t _M_TaskCollection; + + // Callstack for function call (constructor or .then) that created this task impl. + _TaskCreationCallstack _M_pTaskCreationCallstack; + + _TaskEventLogger _M_taskEventLogger; + private: + // Must not be copied by value: + _Task_impl_base(const _Task_impl_base&); + _Task_impl_base const & operator=(_Task_impl_base const&); + }; + +#if _PPLTASK_ASYNC_LOGGING + inline void _TaskEventLogger::_LogTaskCompleted() + { + if (_M_scheduled) + { + ::Windows::Foundation::AsyncStatus _State; + if (_M_task->_IsCompleted()) + _State = ::Windows::Foundation::AsyncStatus::Completed; + else if (_M_task->_HasUserException()) + _State = ::Windows::Foundation::AsyncStatus::Error; + else + _State = ::Windows::Foundation::AsyncStatus::Canceled; + + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), _State); + } + } + } +#endif + + /// + /// The implementation of a first-class task. This structure contains the task group used to execute + /// the task function and handles the scheduling. The _Task_impl is created as a shared_ptr + /// member of the the public task class, so its destruction is handled automatically. + /// + /// + /// The result type of this task. + /// + /**/ + template + struct _Task_impl : public _Task_impl_base + { +#if defined (__cplusplus_winrt) + typedef Windows::Foundation::IAsyncOperation::_Value> _AsyncOperationType; +#endif // defined(__cplusplus_winrt) + _Task_impl(_CancellationTokenState * _Ct, scheduler_ptr _Scheduler_arg) + : _Task_impl_base(_Ct, _Scheduler_arg) + { +#if defined (__cplusplus_winrt) + _M_unwrapped_async_op = nullptr; +#endif /* defined (__cplusplus_winrt) */ + } + + virtual ~_Task_impl() + { + // We must invoke _DeregisterCancellation in the derived class destructor. Calling it in the base class destructor could cause + // a partially initialized _Task_impl to be in the list of registrations for a cancellation token. + _DeregisterCancellation(); + } + + virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder> & _ExceptionHolder_arg) + { + bool _RunContinuations = false; + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + if (_UserException) + { + _ASSERTE(_SynchronousCancel && !_IsCompleted()); + // If the state is _Canceled, the exception has to be coming from an ancestor. + _ASSERTE(!_IsCanceled() || _PropagatedFromAncestor); + + // We should not be canceled with an exception more than once. + _ASSERTE(!_HasUserException()); + + // Mark _PropagatedFromAncestor as used. + (void)_PropagatedFromAncestor; + + if (_M_TaskState == _Canceled) + { + // If the task has finished cancelling there should not be any continuation records in the array. + return false; + } + else + { + _ASSERTE(_M_TaskState != _Completed); + _M_exceptionHolder = _ExceptionHolder_arg; + } + } + else + { + // Completed is a non-cancellable state, and if this is an asynchronous cancel, we're unable to do better than the last async cancel + // which is to say, cancellation is already initiated, so return early. + if (_IsCompleted() || _IsCanceled() || (_IsPendingCancel() && !_SynchronousCancel)) + { + _ASSERTE(!_IsCompleted() || !_HasUserException()); + return false; + } + _ASSERTE(!_SynchronousCancel || !_HasUserException()); + } + + if (_SynchronousCancel) + { + // Be aware that this set must be done BEFORE _M_Scheduled being set, or race will happen between this and wait() + _M_TaskState = _Canceled; + // Cancellation completes the task, so all dependent tasks must be run to cancel them + // They are canceled when they begin running (see _RunContinuation) and see that their + // ancestor has been canceled. + _RunContinuations = true; + } + else + { + _ASSERTE(!_UserException); + + if (_IsStarted()) + { +#if defined (__cplusplus_winrt) + if (_M_unwrapped_async_op != nullptr) + { + // We will only try to cancel async operation but not unwrapped tasks, since unwrapped tasks cannot be canceled without its token. + _M_unwrapped_async_op->Cancel(); + } +#endif /* defined (__cplusplus_winrt) */ + _M_TaskCollection._Cancel(); + } + + // The _M_TaskState variable transitions to _Canceled when cancellation is completed (the task is not executing user code anymore). + // In the case of a synchronous cancel, this can happen immediately, whereas with an asynchronous cancel, the task has to move from + // _Started to _PendingCancel before it can move to _Canceled when it is finished executing. + _M_TaskState = _PendingCancel; + + _M_taskEventLogger._LogCancelTask(); + } + + + } + + // Only execute continuations and mark the task as completed if we were able to move the task to the _Canceled state. + if (_RunContinuations) + { + _M_TaskCollection._Complete(); + + if (_M_Continuations) + { + // Scheduling cancellation with automatic inlining. + _ScheduleFuncWithAutoInline([=](){ _RunTaskContinuations(); }, details::_DefaultAutoInline); + } + } + return true; + } + + void _FinalizeAndRunContinuations(_ReturnType _Result) + { + _M_Result.Set(_Result); + + { + // + // Hold this lock to ensure continuations being concurrently either get added + // to the _M_Continuations vector or wait for the result + // + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + + // A task could still be in the _Created state if it was created with a task_completion_event. + // It could also be in the _Canceled state for the same reason. + _ASSERTE(!_HasUserException() && !_IsCompleted()); + if (_IsCanceled()) + { + return; + } + + // Always transition to "completed" state, even in the face of unacknowledged pending cancellation + _M_TaskState = _Completed; + } + _M_TaskCollection._Complete(); + _RunTaskContinuations(); + } + + // + // This method is invoked when the starts executing. The task returns early if this method returns true. + // + bool _TransitionedToStarted() + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + // Canceled state could only result from antecedent task's canceled state, but that code path will not reach here. + _ASSERTE(!_IsCanceled()); + if (_IsPendingCancel()) + return false; + + _ASSERTE(_IsCreated()); + _M_TaskState = _Started; + return true; + } + +#if defined (__cplusplus_winrt) + void _SetUnwrappedAsyncOp(_AsyncOperationType^ _AsyncOp) + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + // Cancel the async operation if the task itself is canceled, since the thread that canceled the task missed it. + if (_IsPendingCancel()) + { + _ASSERTE(!_IsCanceled()); + _AsyncOp->Cancel(); + } + else + { + _M_unwrapped_async_op = _AsyncOp; + } + } +#endif /* defined (__cplusplus_winrt) */ + + // Return true if the task has reached a terminal state + bool _IsDone() + { + return _IsCompleted() || _IsCanceled(); + } + + _ReturnType _GetResult() + { + return _M_Result.Get(); + } + + _ResultHolder<_ReturnType> _M_Result; // this means that the result type must have a public default ctor. +#if defined (__cplusplus_winrt) + _AsyncOperationType^ _M_unwrapped_async_op; +#endif /* defined (__cplusplus_winrt) */ + }; + + template + struct _Task_completion_event_impl + { + private: + _Task_completion_event_impl(const _Task_completion_event_impl&); + _Task_completion_event_impl& operator=(const _Task_completion_event_impl&); + + public: + + typedef std::vector::_Type> _TaskList; + + _Task_completion_event_impl() : + _M_fHasValue(false), _M_fIsCanceled(false) + { + } + + bool _HasUserException() + { + return _M_exceptionHolder != nullptr; + } + + ~_Task_completion_event_impl() + { + for( auto _TaskIt = _M_tasks.begin(); _TaskIt != _M_tasks.end(); ++_TaskIt ) + { + _ASSERTE(!_M_fHasValue && !_M_fIsCanceled); + // Cancel the tasks since the event was never signaled or canceled. + (*_TaskIt)->_Cancel(true); + } + } + + // We need to protect the loop over the array, so concurrent_vector would not have helped + _TaskList _M_tasks; + ::pplx::extensibility::critical_section_t _M_taskListCritSec; + _ResultHolder<_ResultType> _M_value; + std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; + bool _M_fHasValue; + bool _M_fIsCanceled; + }; + + // Utility method for dealing with void functions + inline std::function<_Unit_type(void)> _MakeVoidToUnitFunc(const std::function& _Func) + { + return [=]() -> _Unit_type { _Func(); return _Unit_type(); }; + } + + template + std::function<_Type(_Unit_type)> _MakeUnitToTFunc(const std::function<_Type(void)>& _Func) + { + return [=](_Unit_type) -> _Type { return _Func(); }; + } + + template + std::function<_Unit_type(_Type)> _MakeTToUnitFunc(const std::function& _Func) + { + return [=](_Type t) -> _Unit_type { _Func(t); return _Unit_type(); }; + } + + inline std::function<_Unit_type(_Unit_type)> _MakeUnitToUnitFunc(const std::function& _Func) + { + return [=](_Unit_type) -> _Unit_type { _Func(); return _Unit_type(); }; + } +} // namespace details + +/// +/// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, +/// or start a task in response to an external event. +/// +/// +/// The result type of this task_completion_event class. +/// +/// +/// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and +/// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must +/// have the same type as the task you create, and calling the set method on the task completion event with a value of that type +/// will cause the associated task to complete, and provide that value as a result to its continuations. +/// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. +/// task_completion_event behaves like a smart pointer, and should be passed by value. +/// +/// +/**/ +template +class task_completion_event +{ +public: + /// + /// Constructs a task_completion_event object. + /// + /**/ + task_completion_event() + : _M_Impl(std::make_shared>()) + { + } + + /// + /// Sets the task completion event. + /// + /// + /// The result to set this event with. + /// + /// + /// The method returns true if it was successful in setting the event. It returns false if the event is already set. + /// + /// + /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the + /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the + /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have + /// a other than void will pass the value to their continuations. + /// + /**/ + bool set(_ResultType _Result) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // Subsequent sets are ignored. This makes races to set benign: the first setter wins and all others are ignored. + if (_IsTriggered()) + { + return false; + } + + _TaskList _Tasks; + bool _RunContinuations = false; + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); + + if (!_IsTriggered()) + { + _M_Impl->_M_value.Set(_Result); + _M_Impl->_M_fHasValue = true; + + _Tasks.swap(_M_Impl->_M_tasks); + _RunContinuations = true; + } + } + + if (_RunContinuations) + { + for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) + { + // If current task was cancelled by a cancellation_token, it would be in cancel pending state. + if ((*_TaskIt)->_IsPendingCancel()) + (*_TaskIt)->_Cancel(true); + else + { + // Tasks created with task_completion_events can be marked as async, (we do this in when_any and when_all + // if one of the tasks involved is an async task). Since continuations of async tasks can execute inline, we + // need to run continuations after the lock is released. + (*_TaskIt)->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); + } + } + if (_M_Impl->_HasUserException()) + { + _M_Impl->_M_exceptionHolder.reset(); + } + return true; + } + + return false; + } + + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. + return _Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); + } + + /// + /// Propagates an exception to all tasks associated with this event. + /// + /// + /// The exception_ptr that indicates the exception to set this event with. + /// + /**/ + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. + return _Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); + } + + /// + /// Internal method to cancel the task_completion_event. Any task created using this event will be marked as canceled if it has + /// not already been set. + /// + bool _Cancel() const + { + // Cancel with the stored exception if one exists. + return _CancelInternal(); + } + + /// + /// Internal method to cancel the task_completion_event with the exception provided. Any task created using this event will be canceled + /// with the same exception. + /// + template + bool _Cancel(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack ()) const + { + bool _Canceled; + if(_StoreException(_ExHolder, _SetExceptionAddressHint)) + { + _Canceled = _CancelInternal(); + _ASSERTE(_Canceled); + } + else + { + _Canceled = false; + } + return _Canceled; + } + + /// + /// Internal method that stores an exception in the task completion event. This is used internally by when_any. + /// Note, this does not cancel the task completion event. A task completion event with a stored exception + /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. + /// + template + bool _StoreException(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack ()) const + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); + if (!_IsTriggered() && !_M_Impl->_HasUserException()) + { + // Create the exception holder only if we have ensured there we will be successful in setting it onto the + // task completion event. Failing to do so will result in an unobserved task exception. + _M_Impl->_M_exceptionHolder = _ToExceptionHolder(_ExHolder, _SetExceptionAddressHint); + return true; + } + return false; + } + + /// + /// Tests whether current event has been either Set, or Canceled. + /// + bool _IsTriggered() const + { + return _M_Impl->_M_fHasValue || _M_Impl->_M_fIsCanceled; + } + +private: + + static std::shared_ptr _ToExceptionHolder(const std::shared_ptr& _ExHolder, const details::_TaskCreationCallstack&) + { + return _ExHolder; + } + + static std::shared_ptr _ToExceptionHolder(std::exception_ptr _ExceptionPtr, const details::_TaskCreationCallstack &_SetExceptionAddressHint) + { + return std::make_shared(_ExceptionPtr, _SetExceptionAddressHint); + } + + + template friend class task; // task can register itself with the event by calling the private _RegisterTask + template friend class task_completion_event; + + typedef typename details::_Task_completion_event_impl<_ResultType>::_TaskList _TaskList; + + /// + /// Cancels the task_completion_event. + /// + bool _CancelInternal() const + { + // Cancellation of task completion events is an internal only utility. Our usage is such that _CancelInternal + // will never be invoked if the task completion event has been set. + _ASSERTE(!_M_Impl->_M_fHasValue); + if (_M_Impl->_M_fIsCanceled) + { + return false; + } + + _TaskList _Tasks; + bool _Cancel = false; + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); + _ASSERTE(!_M_Impl->_M_fHasValue); + if (!_M_Impl->_M_fIsCanceled) + { + _M_Impl->_M_fIsCanceled = true; + _Tasks.swap(_M_Impl->_M_tasks); + _Cancel = true; + } + } + + bool _UserException = _M_Impl->_HasUserException(); + + if (_Cancel) + { + for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) + { + // Need to call this after the lock is released. See comments in set(). + if (_UserException) + { + (*_TaskIt)->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); + } + else + { + (*_TaskIt)->_Cancel(true); + } + } + } + return _Cancel; + } + + /// + /// Register a task with this event. This function is called when a task is constructed using + /// a task_completion_event. + /// + void _RegisterTask(const typename details::_Task_ptr<_ResultType>::_Type & _TaskParam) + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); + + //If an exception was already set on this event, then cancel the task with the stored exception. + if(_M_Impl->_HasUserException()) + { + _TaskParam->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); + } + else if (_M_Impl->_M_fHasValue) + { + _TaskParam->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); + } + else + { + _M_Impl->_M_tasks.push_back(_TaskParam); + } + } + + std::shared_ptr> _M_Impl; +}; + +/// +/// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, +/// or start a task in response to an external event. +/// +/// +/// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and +/// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must +/// have the same type as the task you create, and calling the set method on the task completion event with a value of that type +/// will cause the associated task to complete, and provide that value as a result to its continuations. +/// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. +/// task_completion_event behaves like a smart pointer, and should be passed by value. +/// +/// +/**/ +template<> +class task_completion_event +{ +public: + /// + /// Sets the task completion event. + /// + /// + /// The method returns true if it was successful in setting the event. It returns false if the event is already set. + /// + /// + /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the + /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the + /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have + /// a other than void will pass the value to their continuations. + /// + /**/ + bool set() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + return _M_unitEvent.set(details::_Unit_type()); + } + + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + return _M_unitEvent._Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); + } + + /// + /// Propagates an exception to all tasks associated with this event. + /// + /// + /// The exception_ptr that indicates the exception to set this event with. + /// + /**/ + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK intrinsic gives us the expected result + bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. + return _M_unitEvent._Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); + } + + /// + /// Cancel the task_completion_event. Any task created using this event will be marked as canceled if it has + /// not already been set. + /// + void _Cancel() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + _M_unitEvent._Cancel(); + } + + /// + /// Cancel the task_completion_event with the exception holder provided. Any task created using this event will be canceled + /// with the same exception. + /// + void _Cancel(const std::shared_ptr& _ExHolder) const + { + _M_unitEvent._Cancel(_ExHolder); + } + + /// + /// Method that stores an exception in the task completion event. This is used internally by when_any. + /// Note, this does not cancel the task completion event. A task completion event with a stored exception + /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. + /// + bool _StoreException(const std::shared_ptr& _ExHolder) const + { + return _M_unitEvent._StoreException(_ExHolder); + } + + /// + /// Test whether current event has been either Set, or Canceled. + /// + bool _IsTriggered() const + { + return _M_unitEvent._IsTriggered(); + } + +private: + template friend class task; // task can register itself with the event by calling the private _RegisterTask + + /// + /// Register a task with this event. This function is called when a task is constructed using + /// a task_completion_event. + /// + void _RegisterTask(details::_Task_ptr::_Type _TaskParam) + { + _M_unitEvent._RegisterTask(_TaskParam); + } + + // The void event contains an event a dummy type so common code can be used for events with void and non-void results. + task_completion_event _M_unitEvent; +}; + +namespace details +{ + // + // Compile-time validation helpers + // + + // Task constructor validation: issue helpful diagnostics for common user errors. Do not attempt full validation here. + // + // Anything callable is fine + template + auto _IsValidTaskCtor(_Ty _Param, int,int,int,int) -> decltype(_Param(), std::true_type()); + +#if defined (__cplusplus_winrt) + // Anything that has GetResults is fine: this covers all async operations + template + auto _IsValidTaskCtor(_Ty _Param, int, int, int,...) -> decltype(_Param->GetResults(), std::true_type()); +#endif + + // Allow parameters with set: this covers task_completion_event + template + auto _IsValidTaskCtor(_Ty _Param, int, int, ...) -> decltype(_Param.set(stdx::declval<_ReturnType>()), std::true_type()); + + template + auto _IsValidTaskCtor(_Ty _Param, int, ...) -> decltype(_Param.set(), std::true_type()); + + // All else is invalid + template + std::false_type _IsValidTaskCtor(_Ty _Param, ...); + + template + void _ValidateTaskConstructorArgs(_Ty _Param) + { + static_assert(std::is_same(_Param,0,0,0,0)),std::true_type>::value, +#if defined (__cplusplus_winrt) + "incorrect argument for task constructor; can be a callable object, an asynchronous operation, or a task_completion_event" +#else /* defined (__cplusplus_winrt) */ + "incorrect argument for task constructor; can be a callable object or a task_completion_event" +#endif /* defined (__cplusplus_winrt) */ + ); +#if defined (__cplusplus_winrt) + static_assert(!(std::is_same<_Ty,_ReturnType>::value && details::_IsIAsyncInfo<_Ty>::_Value), + "incorrect template argument for task; consider using the return type of the async operation"); +#endif /* defined (__cplusplus_winrt) */ + } + +#if defined (__cplusplus_winrt) + // Helpers for create_async validation + // + // A parameter lambda taking no arguments is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int) -> decltype(_Param(), std::true_type()); + + // A parameter lambda taking an cancellation_token argument is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, int, int, ...) -> decltype(_Param(cancellation_token::none()), std::true_type()); + + // A parameter lambda taking a progress report argument is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, int, ...) -> decltype(_Param(details::_ProgressReporterCtorArgType()), std::true_type()); + + // A parameter lambda taking a progress report and a cancellation_token argument is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, ...) -> decltype(_Param(details::_ProgressReporterCtorArgType(), cancellation_token::none()), std::true_type()); + + // All else is invalid + template + static std::false_type _IsValidCreateAsync(_Ty _Param, ...); +#endif /* defined (__cplusplus_winrt) */ +} +/// +/// A helper class template that transforms a continuation lambda that either takes or returns void, or both, into a lambda that takes and returns a +/// non-void type (details::_Unit_type is used to substitute for void). This is to minimize the special handling required for 'void'. +/// +template +class _Continuation_func_transformer +{ +public: + static auto _Perform(std::function<_OutType(_InpType)> _Func) -> decltype(_Func) + { + return _Func; + } +}; + +template +class _Continuation_func_transformer +{ +public: + static auto _Perform(std::function<_OutType(void)> _Func) -> decltype(details::_MakeUnitToTFunc<_OutType>(_Func)) + { + return details::_MakeUnitToTFunc<_OutType>(_Func); + } +}; + +template +class _Continuation_func_transformer<_InType, void> +{ +public: + static auto _Perform(std::function _Func) -> decltype(details::_MakeTToUnitFunc<_InType>(_Func)) + { + return details::_MakeTToUnitFunc<_InType>(_Func); + } +}; + +template<> +class _Continuation_func_transformer +{ +public: + static auto _Perform(std::function _Func) -> decltype(details::_MakeUnitToUnitFunc(_Func)) + { + return details::_MakeUnitToUnitFunc(_Func); + } +}; + +// A helper class template that transforms an intial task lambda returns void into a lambda that returns a non-void type (details::_Unit_type is used +// to substitute for void). This is to minimize the special handling required for 'void'. +template +class _Init_func_transformer +{ +public: + static auto _Perform(std::function<_RetType(void)> _Func) -> decltype(_Func) + { + return _Func; + } +}; + +template<> +class _Init_func_transformer +{ +public: + static auto _Perform(std::function _Func) -> decltype(details::_MakeVoidToUnitFunc(_Func)) + { + return details::_MakeVoidToUnitFunc(_Func); + } +}; + +/// +/// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, +/// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces +/// a result of type on successful completion. Tasks of type task<void> produce no result. +/// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using +/// continuations(then), and join(when_all) and choice(when_any) patterns. +/// +/// +/// The result type of this task. +/// +/// +/// For more information, see . +/// +/**/ +template +class task +{ +public: + /// + /// The type of the result an object of this class produces. + /// + /**/ + typedef _ReturnType result_type; + + /// + /// Constructs a task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task() : _M_Impl(nullptr) + { + // The default constructor should create a task with a nullptr impl. This is a signal that the + // task is not usable and should throw if any wait(), get() or then() APIs are used. + } + + /// + /// Constructs a task object. + /// + /// + /// The type of the parameter from which the task is to be constructed. + /// + /// + /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> + /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function + /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, + /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. + /// + /// + /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives + /// the token cancellation_token::none(). + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + explicit task(_Ty _Param) + { + task_options _TaskOptions; + details::_ValidateTaskConstructorArgs<_ReturnType,_Ty>(_Param); + + _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); + // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. + _SetTaskCreationCallstack(_CAPTURE_CALLSTACK()); + + _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); + } + + /// + /// Constructs a task object. + /// + /// + /// The type of the parameter from which the task is to be constructed. + /// + /// + /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> + /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function + /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, + /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. + /// + /// + /// The task options include cancellation token, scheduler etc + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + explicit task(_Ty _Param, const task_options &_TaskOptions) + { + details::_ValidateTaskConstructorArgs<_ReturnType,_Ty>(_Param); + + _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); + // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. + _SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); + + _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); + } + + /// + /// Constructs a task object. + /// + /// + /// The source task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task(const task& _Other): _M_Impl(_Other._M_Impl) {} + + /// + /// Constructs a task object. + /// + /// + /// The source task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task(task&& _Other): _M_Impl(std::move(_Other._M_Impl)) {} + + /// + /// Replaces the contents of one task object with another. + /// + /// + /// The source task object. + /// + /// + /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same + /// actual task as does. + /// + /**/ + task& operator=(const task& _Other) + { + if (this != &_Other) + { + _M_Impl = _Other._M_Impl; + } + return *this; + } + + /// + /// Replaces the contents of one task object with another. + /// + /// + /// The source task object. + /// + /// + /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same + /// actual task as does. + /// + /**/ + task& operator=(task&& _Other) + { + if (this != &_Other) + { + _M_Impl = std::move(_Other._M_Impl); + } + return *this; + } + + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + auto then(const _Function& _Func) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + task_options _TaskOptions; + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); + } + + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The task options include cancellation token, scheduler and continuation context. By default the former 3 + /// options are inherited from the antecedent task + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + auto then(const _Function& _Func, task_options _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); + } + + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit + /// the token of its antecedent task. + /// + /// + /// A variable that specifies where the continuation should execute. This variable is only useful when used in a Windows Store + /// style app. For more information, see task_continuation_context + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + auto then(const _Function& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + task_options _TaskOptions(_CancellationToken, _ContinuationContext); + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); + } + + /// + /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks + /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. + /// + /// + /// A task_status value which could be either completed or canceled. If the task encountered an exception + /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. + /// + /**/ + task_status wait() const + { + if (!_M_Impl) + { + throw invalid_operation("wait() cannot be called on a default constructed task."); + } + + return _M_Impl->_Wait(); + } + + /// + /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to + /// finish. This method does not return a value when called on a task with a result_type of void. + /// + /// + /// The result of the task. + /// + /// + /// If the task is canceled, a call to get will throw a task_canceled exception. If the task + /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. + /// + /**/ + _ReturnType get() const + { + if (!_M_Impl) + { + throw invalid_operation("get() cannot be called on a default constructed task."); + } + + if (_M_Impl->_Wait() == canceled) + { + throw task_canceled(); + } + + return _M_Impl->_GetResult(); + } + + /// + /// Determines if the task is completed. + /// + /// + /// True if the task has completed, false otherwise. + /// + /// + /// The function returns true if the task is completed or canceled (with or without user exception). + /// + bool is_done() const + { + if (!_M_Impl) + { + throw invalid_operation("is_done() cannot be called on a default constructed task."); + } + + return _M_Impl->_IsDone(); + } + + /// + /// Returns the scheduler for this task + /// + /// + /// A pointer to the scheduler + /// + scheduler_ptr scheduler() const + { + if (!_M_Impl) + { + throw invalid_operation("scheduler() cannot be called on a default constructed task."); + } + + return _M_Impl->_GetScheduler(); + } + + /// + /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. + /// + /// + /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. + /// + /**/ + bool is_apartment_aware() const + { + if (!_M_Impl) + { + throw invalid_operation("is_apartment_aware() cannot be called on a default constructed task."); + } + return _M_Impl->_IsApartmentAware(); + } + + /// + /// Determines whether two task objects represent the same internal task. + /// + /// + /// true if the objects refer to the same underlying task, and false otherwise. + /// + /**/ + bool operator==(const task<_ReturnType>& _Rhs) const + { + return (_M_Impl == _Rhs._M_Impl); + } + + /// + /// Determines whether two task objects represent different internal tasks. + /// + /// + /// true if the objects refer to different underlying tasks, and false otherwise. + /// + /**/ + bool operator!=(const task<_ReturnType>& _Rhs) const + { + return !operator==(_Rhs); + } + + /// + /// Create an underlying task implementation. + /// + void _CreateImpl(details::_CancellationTokenState * _Ct, scheduler_ptr _Scheduler) + { + _ASSERTE(_Ct != nullptr); + _M_Impl = details::_Task_ptr<_ReturnType>::_Make(_Ct, _Scheduler); + if (_Ct != details::_CancellationTokenState::_None()) + { + _M_Impl->_RegisterCancellation(_M_Impl); + } + } + + /// + /// Return the underlying implementation for this task. + /// + const typename details::_Task_ptr<_ReturnType>::_Type & _GetImpl() const + { + return _M_Impl; + } + + /// + /// Set the implementation of the task to be the supplied implementaion. + /// + void _SetImpl(const typename details::_Task_ptr<_ReturnType>::_Type & _Impl) + { + _ASSERTE(!_M_Impl); + _M_Impl = _Impl; + } + + /// + /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy. + /// + void _SetImpl(typename details::_Task_ptr<_ReturnType>::_Type && _Impl) + { + _ASSERTE(!_M_Impl); + _M_Impl = std::move(_Impl); + } + + /// + /// Sets a property determining whether the task is apartment aware. + /// + void _SetAsync(bool _Async = true) + { + _GetImpl()->_SetAsync(_Async); + } + + /// + /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then method. + /// + void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) + { + _GetImpl()->_SetTaskCreationCallstack(_callstack); + } + + /// + /// An internal version of then that takes additional flags and always execute the continuation inline by default. + /// When _ForceInline is set to false, continuations inlining will be limited to default _DefaultAutoInline. + /// This function is Used for runtime internal continuations only. + /// + template + auto _Then(const _Function& _Func, details::_CancellationTokenState *_PTokenState, + details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + // inherit from antecedent + auto _Scheduler = _GetImpl()->_GetScheduler(); + + return _ThenImpl<_ReturnType, _Function>(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); + } + +private: + template friend class task; + + + // The task handle type used to construct an 'initial task' - a task with no dependents. + template + struct _InitialTaskHandle : + details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore_t> + { + _Function _M_function; + _InitialTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _TaskImpl, const _Function & _func) + : details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore_t>::_PPLTaskHandle(_TaskImpl) + , _M_function(_func) + { + } + + virtual ~_InitialTaskHandle() {} + + template + auto _LogWorkItemAndInvokeUserLambda(_Func && _func) const -> decltype(_func()) + { + details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); + CASABLANCA_UNREFERENCED_PARAMETER(_LogWorkItem); + return _func(); + } + + void _Perform() const + { + _Init(_TypeSelection()); + } + + void _SyncCancelAndPropagateException() const + { + this->_M_pTask->_Cancel(true); + } + + // + // Overload 0: returns _InternalReturnType + // + // This is the most basic task with no unwrapping + // + void _Init(details::_TypeSelectorNoAsync) const + { + this->_M_pTask->_FinalizeAndRunContinuations(_LogWorkItemAndInvokeUserLambda(_Init_func_transformer<_InternalReturnType>::_Perform(_M_function))); + } + + // + // Overload 1: returns IAsyncOperation<_InternalReturnType>^ (only uder /ZW) + // or + // returns task<_InternalReturnType> + // + // This is task whose functor returns an async operation or a task which will be unwrapped for continuation + // Depending on the output type, the right _AsyncInit gets invoked + // + void _Init(details::_TypeSelectorAsyncOperationOrTask) const + { + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_M_function)); + } + +#if defined (__cplusplus_winrt) + // + // Overload 2: returns IAsyncAction^ + // + // This is task whose functor returns an async action which will be unwrapped for continuation + // + void _Init(details::_TypeSelectorAsyncAction) const + { + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function))); + } + + // + // Overload 3: returns IAsyncOperationWithProgress<_InternalReturnType, _ProgressType>^ + // + // This is task whose functor returns an async operation with progress which will be unwrapped for continuation + // + void _Init(details::_TypeSelectorAsyncOperationWithProgress) const + { + typedef details::_GetProgressType::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, + ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_InternalReturnType,_ProgressType>(_LogWorkItemAndInvokeUserLambda(_M_function))); + } + + // + // Overload 4: returns IAsyncActionWithProgress<_ProgressType>^ + // + // This is task whose functor returns an async action with progress which will be unwrapped for continuation + // + void _Init(details::_TypeSelectorAsyncActionWithProgress) const + { + typedef details::_GetProgressType::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, + ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_LogWorkItemAndInvokeUserLambda(_M_function))); + } +#endif /* defined (__cplusplus_winrt) */ + }; + + + /// + /// The task handle type used to create a 'continuation task'. + /// + template + struct _ContinuationTaskHandle : + details::_PPLTaskHandle::_Type, + _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> + { + typedef typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type _NormalizedContinuationReturnType; + + typename details::_Task_ptr<_ReturnType>::_Type _M_ancestorTaskImpl; + _Function _M_function; + + _ContinuationTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _AncestorImpl, + const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type & _ContinuationImpl, + const _Function & _Func, const task_continuation_context & _Context, details::_TaskInliningMode_t _InliningMode) + : details::_PPLTaskHandle::_Type, + _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> + ::_PPLTaskHandle(_ContinuationImpl) + , _M_ancestorTaskImpl(_AncestorImpl) + , _M_function(_Func) + { + this->_M_isTaskBasedContinuation = _IsTaskBased::value; + this->_M_continuationContext = _Context; + this->_M_continuationContext._Resolve(_AncestorImpl->_IsApartmentAware()); + this->_M_inliningMode = _InliningMode; + } + + virtual ~_ContinuationTaskHandle() {} + + template + auto _LogWorkItemAndInvokeUserLambda(_Func && _func, _Arg && _value) const -> decltype(_func(std::forward<_Arg>(_value))) + { + details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); + CASABLANCA_UNREFERENCED_PARAMETER(_LogWorkItem); + return _func(std::forward<_Arg>(_value)); + } + + void _Perform() const + { + _Continue(_IsTaskBased(), _TypeSelection()); + } + + void _SyncCancelAndPropagateException() const + { + if (_M_ancestorTaskImpl->_HasUserException()) + { + // If the ancestor encountered an exception, transfer the exception to the continuation + // This traverses down the tree to propagate the exception. + this->_M_pTask->_CancelWithExceptionHolder(_M_ancestorTaskImpl->_GetExceptionHolder(), true); + } + else + { + // If the ancestor was canceled, then your own execution should be canceled. + // This traverses down the tree to cancel it. + this->_M_pTask->_Cancel(true); + } + } + + // + // Overload 0-0: _InternalReturnType -> _TaskType + // + // This is a straight task continuation which simply invokes its target with the ancestor's completion argument + // + void _Continue(std::false_type, details::_TypeSelectorNoAsync) const + { + this->_M_pTask->_FinalizeAndRunContinuations( + _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _ContinuationReturnType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult())); + } + + // + // Overload 0-1: _InternalReturnType -> IAsyncOperation<_TaskType>^ (only uder /ZW) + // or + // _InternalReturnType -> task<_TaskType> + // + // This is a straight task continuation which returns an async operation or a task which will be unwrapped for continuation + // Depending on the output type, the right _AsyncInit gets invoked + // + void _Continue(std::false_type, details::_TypeSelectorAsyncOperationOrTask) const + { + typedef typename details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + this->_M_pTask, + _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()) + ); + } + +#if defined (__cplusplus_winrt) + // + // Overload 0-2: _InternalReturnType -> IAsyncAction^ + // + // This is a straight task continuation which returns an async action which will be unwrapped for continuation + // + void _Continue(std::false_type, details::_TypeSelectorAsyncAction) const + { + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + this->_M_pTask, + ref new details::_IAsyncActionToAsyncOperationConverter( + _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()))); + } + + // + // Overload 0-3: _InternalReturnType -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^ + // + // This is a straight task continuation which returns an async operation with progress which will be unwrapped for continuation + // + void _Continue(std::false_type, details::_TypeSelectorAsyncOperationWithProgress) const + { + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()); + typedef details::_GetProgressType::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + this->_M_pTask, + ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>(_OpWithProgress)); + } + + // + // Overload 0-4: _InternalReturnType -> IAsyncActionWithProgress<_ProgressType>^ + // + // This is a straight task continuation which returns an async action with progress which will be unwrapped for continuation + // + void _Continue(std::false_type, details::_TypeSelectorAsyncActionWithProgress) const + { + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()); + typedef details::_GetProgressType::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + this->_M_pTask, + ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_OpWithProgress)); + } + +#endif /* defined (__cplusplus_winrt) */ + + // + // Overload 1-0: task<_InternalReturnType> -> _TaskType + // + // This is an exception handling type of continuation which takes the task rather than the task's result. + // + void _Continue(std::true_type, details::_TypeSelectorNoAsync) const + { + typedef task<_InternalReturnType> _FuncInputType; + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + this->_M_pTask->_FinalizeAndRunContinuations( + _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_FuncInputType, _ContinuationReturnType>::_Perform(_M_function), std::move(_ResultTask))); + } + + // + // Overload 1-1: task<_InternalReturnType> -> IAsyncOperation<_TaskType>^ + // or + // task<_TaskType> + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async operation or a task which will be unwrapped + // for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncOperationOrTask) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, + _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask))); + } + +#if defined (__cplusplus_winrt) + + // + // Overload 1-2: task<_InternalReturnType> -> IAsyncAction^ + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async action which will be unwrapped for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncAction) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, + ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); + } + + // + // Overload 1-3: task<_InternalReturnType> -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^ + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async operation with progress which will be unwrapped + // for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncOperationWithProgress) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + + typedef details::_GetProgressType::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, + ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>( + _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); + } + + // + // Overload 1-4: task<_InternalReturnType> -> IAsyncActionWithProgress<_ProgressType>^ + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async operation with progress which will be unwrapped + // for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncActionWithProgress) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + + typedef details::_GetProgressType::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, + ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>( + _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); + } +#endif /* defined (__cplusplus_winrt) */ + }; + + /// + /// Initializes a task using a lambda, function pointer or function object. + /// + template + void _TaskInitWithFunctor(const _Function& _Func) + { + typedef typename details::_InitFunctorTypeTraits<_InternalReturnType, decltype(_Func())> _Async_type_traits; + + _M_Impl->_M_fFromAsync = _Async_type_traits::_IsAsyncTask; + _M_Impl->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; + _M_Impl->_M_taskEventLogger._LogScheduleTask(false); + _M_Impl->_ScheduleTask(new _InitialTaskHandle<_InternalReturnType, _Function, typename _Async_type_traits::_AsyncKind>(_GetImpl(), _Func), details::_NoInline); + } + + /// + /// Initializes a task using a task completion event. + /// + void _TaskInitNoFunctor(task_completion_event<_ReturnType>& _Event) + { + _Event._RegisterTask(_M_Impl); + } + +#if defined (__cplusplus_winrt) + /// + /// Initializes a task using an asynchronous operation IAsyncOperation^ + /// + void _TaskInitAsyncOp(Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) + { + _M_Impl->_M_fFromAsync = true; + + // Mark this task as started here since we can set the state in the constructor without acquiring a lock. Once _AsyncInit + // returns a completion could execute concurrently and the task must be fully initialized before that happens. + _M_Impl->_M_TaskState = details::_Task_impl_base::_Started; + // Pass the shared pointer into _AsyncInit for storage in the Async Callback. + details::_Task_impl_base::_AsyncInit<_ReturnType, _ReturnType>(_M_Impl, _AsyncOp); + } + + /// + /// Initializes a task using an asynchronous operation IAsyncOperation^ + /// + void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) + { + _TaskInitAsyncOp(_AsyncOp); + } + + /// + /// Initializes a task using an asynchronous operation with progress IAsyncOperationWithProgress^ + /// + template + void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperationWithProgress::_Value, _Progress>^ _AsyncOp) + { + _TaskInitAsyncOp(ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter::_Value, _Progress>(_AsyncOp)); + } +#endif /* defined (__cplusplus_winrt) */ + + /// + /// Initializes a task using a callable object. + /// + template + void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) + { + _TaskInitWithFunctor<_ReturnType, _Function>(_Func); + } + + /// + /// Initializes a task using a non-callable object. + /// + template + void _TaskInitMaybeFunctor(_Ty & _Param, std::false_type) + { + _TaskInitNoFunctor(_Param); + } + + template + auto _ThenImpl(const _Function& _Func, const task_options& _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType + { + if (!_M_Impl) + { + throw invalid_operation("then() cannot be called on a default constructed task."); + } + + details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + auto _Scheduler = _TaskOptions.has_scheduler() ? _TaskOptions.get_scheduler() : _GetImpl()->_GetScheduler(); + auto _CreationStack = details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : details::_TaskCreationCallstack(); + return _ThenImpl<_InternalReturnType, _Function>(_Func, _PTokenState, _TaskOptions.get_continuation_context(), _Scheduler, _CreationStack); + } + + /// + /// The one and only implementation of then for void and non-void tasks. + /// + template + auto _ThenImpl(const _Function& _Func, details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, scheduler_ptr _Scheduler, details::_TaskCreationCallstack _CreationStack, + details::_TaskInliningMode_t _InliningMode = details::_NoInline) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType + { + if (!_M_Impl) + { + throw invalid_operation("then() cannot be called on a default constructed task."); + } + + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType> _Function_type_traits; + typedef details::_TaskTypeTraits _Async_type_traits; + typedef typename _Async_type_traits::_TaskRetType _TaskType; + + // + // A **nullptr** token state indicates that it was not provided by the user. In this case, we inherit the antecedent's token UNLESS this is a + // an exception handling continuation. In that case, we break the chain with a _None. That continuation is never canceled unless the user + // explicitly passes the same token. + // + if (_PTokenState == nullptr) + { + if (_Function_type_traits::_Takes_task::value) + { + _PTokenState = details::_CancellationTokenState::_None(); + } + else + { + _PTokenState = _GetImpl()->_M_pTokenState; + } + } + + task<_TaskType> _ContinuationTask; + _ContinuationTask._CreateImpl(_PTokenState, _Scheduler); + + _ContinuationTask._GetImpl()->_M_fFromAsync = (_GetImpl()->_M_fFromAsync || _Async_type_traits::_IsAsyncTask); + _ContinuationTask._GetImpl()->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; + _ContinuationTask._SetTaskCreationCallstack(_CreationStack); + + _GetImpl()->_ScheduleContinuation(new _ContinuationTaskHandle<_InternalReturnType, _TaskType, _Function, typename _Function_type_traits::_Takes_task, typename _Async_type_traits::_AsyncKind>( + _GetImpl(), _ContinuationTask._GetImpl(), _Func, _ContinuationContext, _InliningMode)); + + return _ContinuationTask; + } + + // The underlying implementation for this task + typename details::_Task_ptr<_ReturnType>::_Type _M_Impl; +}; + +/// +/// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, +/// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces +/// a result of type on successful completion. Tasks of type task<void> produce no result. +/// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using +/// continuations(then), and join(when_all) and choice(when_any) patterns. +/// +/// +/// For more information, see . +/// +/**/ +template<> +class task +{ +public: + /// + /// The type of the result an object of this class produces. + /// + /**/ + typedef void result_type; + + /// + /// Constructs a task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task() : _M_unitTask() + { + // The default constructor should create a task with a nullptr impl. This is a signal that the + // task is not usable and should throw if any wait(), get() or then() APIs are used. + } + + /// + /// Constructs a task object. + /// + /// + /// The type of the parameter from which the task is to be constructed. + /// + /// + /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> + /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function + /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, + /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + explicit task(_Ty _Param, const task_options& _TaskOptions = task_options()) + { + details::_ValidateTaskConstructorArgs(_Param); + + _M_unitTask._CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); + // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. + _M_unitTask._SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); + + _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); + } + + /// + /// Constructs a task object. + /// + /// + /// The source task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task(const task& _Other): _M_unitTask(_Other._M_unitTask){} + + /// + /// Constructs a task object. + /// + /// + /// The source task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task(task&& _Other) : _M_unitTask(std::move(_Other._M_unitTask)) {} + + /// + /// Replaces the contents of one task object with another. + /// + /// + /// The source task object. + /// + /// + /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same + /// actual task as does. + /// + /**/ + task& operator=(const task& _Other) + { + if (this != &_Other) + { + _M_unitTask = _Other._M_unitTask; + } + return *this; + } + + /// + /// Replaces the contents of one task object with another. + /// + /// + /// The source task object. + /// + /// + /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same + /// actual task as does. + /// + /**/ + task& operator=(task&& _Other) + { + if (this != &_Other) + { + _M_unitTask = std::move(_Other._M_unitTask); + } + return *this; + } + + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit + /// the token of its antecedent task. + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + auto then(const _Function& _Func, task_options _TaskOptions = task_options()) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _M_unitTask._ThenImpl(_Func, _TaskOptions); + } + + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit + /// the token of its antecedent task. + /// + /// + /// A variable that specifies where the continuation should execute. This variable is only useful when used in a Windows Store + /// style app. For more information, see task_continuation_context + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result + auto then(const _Function& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + task_options _TaskOptions(_CancellationToken, _ContinuationContext); + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _M_unitTask._ThenImpl(_Func, _TaskOptions); + } + + /// + /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks + /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. + /// + /// + /// A task_status value which could be either completed or canceled. If the task encountered an exception + /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. + /// + /**/ + task_status wait() const + { + return _M_unitTask.wait(); + } + + /// + /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to + /// finish. This method does not return a value when called on a task with a result_type of void. + /// + /// + /// If the task is canceled, a call to get will throw a task_canceled exception. If the task + /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. + /// + /**/ + void get() const + { + _M_unitTask.get(); + } + + /// + /// Determines if the task is completed. + /// + /// + /// True if the task has completed, false otherwise. + /// + /// + /// The function returns true if the task is completed or canceled (with or without user exception). + /// + bool is_done() const + { + return _M_unitTask.is_done(); + } + + /// + /// Returns the scheduler for this task + /// + /// + /// A pointer to the scheduler + /// + scheduler_ptr scheduler() const + { + return _M_unitTask.scheduler(); + } + + /// + /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. + /// + /// + /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. + /// + /**/ + bool is_apartment_aware() const + { + return _M_unitTask.is_apartment_aware(); + } + + /// + /// Determines whether two task objects represent the same internal task. + /// + /// + /// true if the objects refer to the same underlying task, and false otherwise. + /// + /**/ + bool operator==(const task& _Rhs) const + { + return (_M_unitTask == _Rhs._M_unitTask); + } + + /// + /// Determines whether two task objects represent different internal tasks. + /// + /// + /// true if the objects refer to different underlying tasks, and false otherwise. + /// + /**/ + bool operator!=(const task& _Rhs) const + { + return !operator==(_Rhs); + } + + /// + /// Create an underlying task implementation. + /// + void _CreateImpl(details::_CancellationTokenState * _Ct, scheduler_ptr _Scheduler) + { + _M_unitTask._CreateImpl(_Ct, _Scheduler); + } + + /// + /// Return the underlying implementation for this task. + /// + const details::_Task_ptr::_Type & _GetImpl() const + { + return _M_unitTask._M_Impl; + } + + /// + /// Set the implementation of the task to be the supplied implementaion. + /// + void _SetImpl(const details::_Task_ptr::_Type & _Impl) + { + _M_unitTask._SetImpl(_Impl); + } + + /// + /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy. + /// + void _SetImpl(details::_Task_ptr::_Type && _Impl) + { + _M_unitTask._SetImpl(std::move(_Impl)); + } + + /// + /// Sets a property determining whether the task is apartment aware. + /// + void _SetAsync(bool _Async = true) + { + _M_unitTask._SetAsync(_Async); + } + + /// + /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then method. + /// + void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) + { + _M_unitTask._SetTaskCreationCallstack(_callstack); + } + + /// + /// An internal version of then that takes additional flags and executes the continuation inline. Used for runtime internal continuations only. + /// + template + auto _Then(const _Function& _Func, details::_CancellationTokenState *_PTokenState, + details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + // inherit from antecedent + auto _Scheduler = _GetImpl()->_GetScheduler(); + + return _M_unitTask._ThenImpl(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); + } + +private: + template friend class task; + template friend class task_completion_event; + + /// + /// Initializes a task using a task completion event. + /// + void _TaskInitNoFunctor(task_completion_event& _Event) + { + _M_unitTask._TaskInitNoFunctor(_Event._M_unitEvent); + } + +#if defined (__cplusplus_winrt) + /// + /// Initializes a task using an asynchronous action IAsyncAction^ + /// + void _TaskInitNoFunctor(Windows::Foundation::IAsyncAction^ _AsyncAction) + { + _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionToAsyncOperationConverter(_AsyncAction)); + } + + /// + /// Initializes a task using an asynchronous action with progress IAsyncActionWithProgress<_P>^ + /// + template + void _TaskInitNoFunctor(Windows::Foundation::IAsyncActionWithProgress<_P>^ _AsyncActionWithProgress) + { + _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_P>(_AsyncActionWithProgress)); + } +#endif /* defined (__cplusplus_winrt) */ + + /// + /// Initializes a task using a callable object. + /// + template + void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) + { + _M_unitTask._TaskInitWithFunctor(_Func); + } + + /// + /// Initializes a task using a non-callable object. + /// + template + void _TaskInitMaybeFunctor(_T & _Param, std::false_type) + { + _TaskInitNoFunctor(_Param); + } + + // The void task contains a task of a dummy type so common code can be used for tasks with void and non-void results. + task _M_unitTask; +}; + +namespace details +{ + /// + /// The following type traits are used for the create_task function. + /// + +#if defined (__cplusplus_winrt) + // Unwrap functions for asyncOperations + template + _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperation<_Ty>^); + + void _GetUnwrappedType(Windows::Foundation::IAsyncAction^); + + template + _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperationWithProgress<_Ty, _Progress>^); + + template + void _GetUnwrappedType(Windows::Foundation::IAsyncActionWithProgress<_Progress>^); +#endif /* defined (__cplusplus_winrt) */ + + // Unwrap task + template + _Ty _GetUnwrappedType(task<_Ty>); + + // Unwrap all supportted types + template + auto _GetUnwrappedReturnType(_Ty _Arg, int) -> decltype(_GetUnwrappedType(_Arg)); + // fallback + template + _Ty _GetUnwrappedReturnType(_Ty, ...); + + /// + /// _GetTaskType functions will retrieve task type T in task[T](Arg), + /// for given constructor argument Arg and its property "callable". + /// It will automatically unwrap argument to get the final return type if necessary. + /// + + // Non-Callable + template + _Ty _GetTaskType(task_completion_event<_Ty>, std::false_type); + + // Non-Callable + template + auto _GetTaskType(_Ty _NonFunc, std::false_type) -> decltype(_GetUnwrappedType(_NonFunc)); + + // Callable + template + auto _GetTaskType(_Ty _Func, std::true_type) -> decltype(_GetUnwrappedReturnType(_Func(), 0)); + + // Special callable returns void + void _GetTaskType(std::function, std::true_type); + struct _BadArgType{}; + + template + auto _FilterValidTaskType(_Ty _Param, int) -> decltype(_GetTaskType(_Param, _IsCallable(_Param, 0))); + + template + _BadArgType _FilterValidTaskType(_Ty _Param, ...); + + template + struct _TaskTypeFromParam + { + typedef decltype(_FilterValidTaskType(stdx::declval<_Ty>(), 0)) _Type; + }; +} // namespace details + +/// +/// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. +/// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. +/// +/// +/// The type of the parameter from which the task is to be constructed. +/// +/// +/// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event +/// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. +/// +/// +/// A new task of type T, that is inferred from . +/// +/// +/// The first overload behaves like a task constructor that takes a single parameter. +/// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not +/// allowed to pass in a different task object as the first parameter. +/// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, +/// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. +/// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or +/// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. +/// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor +/// that returns either of those types, the created task will have type task<void>. +/// +/// +/// +/**/ +template +__declspec(noinline) +auto create_task(_Ty _Param, task_options _TaskOptions = task_options()) -> task::_Type> +{ + static_assert(!std::is_same::_Type,details::_BadArgType>::value, +#if defined (__cplusplus_winrt) + "incorrect argument for create_task; can be a callable object, an asynchronous operation, or a task_completion_event" +#else /* defined (__cplusplus_winrt) */ + "incorrect argument for create_task; can be a callable object or a task_completion_event" +#endif /* defined (__cplusplus_winrt) */ + ); + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + task::_Type> _CreatedTask(_Param, _TaskOptions); + return _CreatedTask; +} + +/// +/// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. +/// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. +/// +/// +/// The type of the parameter from which the task is to be constructed. +/// +/// +/// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event +/// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. +/// +/// +/// The cancellation token to associate with the task. When the source for this token is canceled, cancellation will be requested on the task. +/// +/// +/// A new task of type T, that is inferred from . +/// +/// +/// The first overload behaves like a task constructor that takes a single parameter. +/// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not +/// allowed to pass in a different task object as the first parameter. +/// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, +/// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. +/// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or +/// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. +/// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor +/// that returns either of those types, the created task will have type task<void>. +/// +/// +/// +/**/ +template +__declspec(noinline) +task<_ReturnType> create_task(const task<_ReturnType>& _Task) +{ + task<_ReturnType> _CreatedTask(_Task); + return _CreatedTask; +} + +#if defined (__cplusplus_winrt) +namespace details +{ + template + task<_T> _To_task_helper(Windows::Foundation::IAsyncOperation<_T>^ op) + { + return task<_T>(op); + } + + template + task<_T> _To_task_helper(Windows::Foundation::IAsyncOperationWithProgress<_T, _Progress>^ op) + { + return task<_T>(op); + } + + inline task _To_task_helper(Windows::Foundation::IAsyncAction^ op) + { + return task(op); + } + + template + task _To_task_helper(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ op) + { + return task(op); + } + + template + class _ProgressDispatcherBase + { + public: + + virtual ~_ProgressDispatcherBase() + { + } + + virtual void _Report(const _ProgressType& _Val) = 0; + }; + + template + class _ProgressDispatcher : public _ProgressDispatcherBase<_ProgressType> + { + public: + + virtual ~_ProgressDispatcher() + { + } + + _ProgressDispatcher(_ClassPtrType _Ptr) : _M_ptr(_Ptr) + { + } + + virtual void _Report(const _ProgressType& _Val) + { + _M_ptr->_FireProgress(_Val); + } + + private: + + _ClassPtrType _M_ptr; + }; + class _ProgressReporterCtorArgType{}; +} // namespace details + +/// +/// The progress reporter class allows reporting progress notifications of a specific type. Each progress_reporter object is bound +/// to a particular asynchronous action or operation. +/// +/// +/// The payload type of each progress notification reported through the progress reporter. +/// +/// +/// This type is only available to Windows Store apps. +/// +/// +/**/ +template +class progress_reporter +{ + typedef std::shared_ptr> _PtrType; + +public: + + /// + /// Sends a progress report to the asynchronous action or operation to which this progress reporter is bound. + /// + /// + /// The payload to report through a progress notification. + /// + /**/ + void report(const _ProgressType& _Val) const + { + _M_dispatcher->_Report(_Val); + } + + template + static progress_reporter _CreateReporter(_ClassPtrType _Ptr) + { + progress_reporter _Reporter; + details::_ProgressDispatcherBase<_ProgressType> *_PDispatcher = new details::_ProgressDispatcher<_ProgressType, _ClassPtrType>(_Ptr); + _Reporter._M_dispatcher = _PtrType(_PDispatcher); + return _Reporter; + } + progress_reporter() {} + +private: + progress_reporter(details::_ProgressReporterCtorArgType); + + _PtrType _M_dispatcher; +}; + +namespace details +{ + // + // maps internal definitions for AsyncStatus and defines states that are not client visible + // + enum _AsyncStatusInternal + { + _AsyncCreated = -1, // externally invisible + // client visible states (must match AsyncStatus exactly) + _AsyncStarted = 0, // Windows::Foundation::AsyncStatus::Started, + _AsyncCompleted = 1, // Windows::Foundation::AsyncStatus::Completed, + _AsyncCanceled = 2, // Windows::Foundation::AsyncStatus::Canceled, + _AsyncError = 3, // Windows::Foundation::AsyncStatus::Error, + // non-client visible internal states + _AsyncCancelPending, + _AsyncClosed, + _AsyncUndefined + }; + + // + // designates whether the "GetResults" method returns a single result (after complete fires) or multiple results + // (which are progressively consumable between Start state and before Close is called) + // + enum _AsyncResultType + { + SingleResult = 0x0001, + MultipleResults = 0x0002 + }; + + // *************************************************************************** + // Template type traits and helpers for async production APIs: + // + + struct _ZeroArgumentFunctor { }; + struct _OneArgumentFunctor { }; + struct _TwoArgumentFunctor { }; + + // **************************************** + // CLASS TYPES: + + // ******************** + // TWO ARGUMENTS: + + // non-void arg: + template + _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); + + // non-void arg: + template + _Arg2 _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); + + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); + + template + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1, _Arg2) const); + + // ******************** + // ONE ARGUMENT: + + // non-void arg: + template + _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); + + // non-void arg: + template + void _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); + + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); + + template + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1) const); + + // ******************** + // ZERO ARGUMENT: + + // void arg: + template + void _Arg1ClassHelperThunk(_ReturnType (_Class::*)() const); + + // void arg: + template + void _Arg2ClassHelperThunk(_ReturnType (_Class::*)() const); + + // void arg: + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)() const); + + template + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)() const); + + // **************************************** + // POINTER TYPES: + + // ******************** + // TWO ARGUMENTS: + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + template + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + template + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + // ******************** + // ONE ARGUMENT: + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); + + template + void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); + + template + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); + + template + void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); + + template + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); + + template + void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); + + template + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1)); + + // ******************** + // ZERO ARGUMENT: + + template + void _Arg1PFNHelperThunk(_ReturnType(__cdecl *)()); + + template + void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)()); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)()); + + template + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)()); + + template + void _Arg1PFNHelperThunk(_ReturnType(__stdcall *)()); + + template + void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)()); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)()); + + template + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)()); + + template + void _Arg1PFNHelperThunk(_ReturnType(__fastcall *)()); + + template + void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)()); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)()); + + template + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)()); + + template + struct _FunctorArguments + { + static const size_t _Count = 0; + }; + + template<> + struct _FunctorArguments<_OneArgumentFunctor> + { + static const size_t _Count = 1; + }; + + template<> + struct _FunctorArguments<_TwoArgumentFunctor> + { + static const size_t _Count = 2; + }; + + template + struct _FunctorTypeTraits + { + typedef decltype(_ArgumentCountHelper(&(_T::operator()))) _ArgumentCountType; + static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; + + typedef decltype(_ReturnTypeClassHelperThunk(&(_T::operator()))) _ReturnType; + typedef decltype(_Arg1ClassHelperThunk(&(_T::operator()))) _Argument1Type; + typedef decltype(_Arg2ClassHelperThunk(&(_T::operator()))) _Argument2Type; + }; + + template + struct _FunctorTypeTraits<_T *> + { + typedef decltype(_ArgumentCountHelper(stdx::declval<_T*>())) _ArgumentCountType; + static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; + + typedef decltype(_ReturnTypePFNHelperThunk(stdx::declval<_T*>())) _ReturnType; + typedef decltype(_Arg1PFNHelperThunk(stdx::declval<_T*>())) _Argument1Type; + typedef decltype(_Arg2PFNHelperThunk(stdx::declval<_T*>())) _Argument2Type; + }; + + template + struct _ProgressTypeTraits + { + static const bool _TakesProgress = false; + typedef void _ProgressType; + }; + + template + struct _ProgressTypeTraits> + { + static const bool _TakesProgress = true; + typedef typename _T _ProgressType; + }; + + + template::_ArgumentCount> + struct _CAFunctorOptions + { + static const bool _TakesProgress = false; + static const bool _TakesToken = false; + typedef void _ProgressType; + }; + + template + struct _CAFunctorOptions<_T, 1> + { + private: + + typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; + + public: + + static const bool _TakesProgress = _ProgressTypeTraits<_Argument1Type>::_TakesProgress; + static const bool _TakesToken = !_TakesProgress; + typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; + }; + + template + struct _CAFunctorOptions<_T, 2> + { + private: + + typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; + + public: + + static const bool _TakesProgress = true; + static const bool _TakesToken = true; + typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; + }; + + ref class _Zip + { + }; + + // *************************************************************************** + // Async Operation Task Generators + // + + // + // Functor returns an IAsyncInfo - result needs to be wrapped in a task: + // + template + struct _SelectorTaskGenerator + { + template + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(), _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(_Cts.get_token()), _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(_Progress), _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(_Progress, _Cts.get_token()), _taskOptinos); + } + }; + + template + struct _SelectorTaskGenerator<_AsyncSelector, void> + { + template + static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task(_Func(), _taskOptinos); + } + + template + static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task(_Func(_Cts.get_token()), _taskOptinos); + } + + template + static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task(_Func(_Progress), _taskOptinos); + } + + template + static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task(_Func(_Progress, _Cts.get_token()), _taskOptinos); + } + }; + + // + // Functor returns a result - it needs to be wrapped in a task: + // + template + struct _SelectorTaskGenerator<_TypeSelectorNoAsync, _ReturnType> + { + +#pragma warning(push) +#pragma warning(disable: 4702) + template + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>( [=]() -> _ReturnType { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(); + }, _taskOptinos); + } +#pragma warning(pop) + + template + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>( [=]() -> _ReturnType { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(_Cts.get_token()); + }, _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>( [=]() -> _ReturnType { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(_Progress); + }, _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>( [=]() -> _ReturnType { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(_Progress, _Cts.get_token()); + }, _taskOptinos); + } + }; + + template<> + struct _SelectorTaskGenerator<_TypeSelectorNoAsync, void> + { + template + static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task( [=]() { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + _Func(); + }, _taskOptinos); + } + + template + static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task( [=]() { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + _Func(_Cts.get_token()); + }, _taskOptinos); + } + + template + static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task( [=]() { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + _Func(_Progress); + }, _taskOptinos); + } + + template + static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task( [=]() { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + _Func(_Progress, _Cts.get_token()); + }, _taskOptinos); + } + }; + + // + // Functor returns a task - the task can directly be returned: + // + template + struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, _ReturnType> + { + template + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(); + } + + template + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Cts.get_token()); + } + + template + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Progress); + } + + template + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Progress, _Cts.get_token()); + } + }; + + template<> + struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, void> + { + template + static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(); + } + + template + static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Cts.get_token()); + } + + template + static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Progress); + } + + template + static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Progress, _Cts.get_token()); + } + }; + + template + struct _TaskGenerator + { + }; + + template + struct _TaskGenerator<_Generator, false, false> + { + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_0(_Func, _Cts, _callstack); + } + }; + + template + struct _TaskGenerator<_Generator, true, false> + { + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_1C(_Func, _Cts, _callstack); + } + }; + + template + struct _TaskGenerator<_Generator, false, true> + { + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); + } + }; + + template + struct _TaskGenerator<_Generator, true, true> + { + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); + } + }; + + // *************************************************************************** + // Async Operation Attributes Classes + // + // These classes are passed through the hierarchy of async base classes in order to hold multiple attributes of a given async construct in + // a single container. An attribute class must define: + // + // Mandatory: + // ------------------------- + // + // _AsyncBaseType : The Windows Runtime interface which is being implemented. + // _CompletionDelegateType : The Windows Runtime completion delegate type for the interface. + // _ProgressDelegateType : If _TakesProgress is true, the Windows Runtime progress delegate type for the interface. If it is false, an empty Windows Runtime type. + // _ReturnType : The return type of the async construct (void for actions / non-void for operations) + // + // _TakesProgress : An indication as to whether or not + // + // _Generate_Task : A function adapting the user's function into what's necessary to produce the appropriate task + // + // Optional: + // ------------------------- + // + + template + struct _AsyncAttributes + { + }; + + template + struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, true> + { + typedef typename Windows::Foundation::IAsyncOperationWithProgress<_ReturnType, _ProgressType> _AsyncBaseType; + typedef typename Windows::Foundation::AsyncOperationProgressHandler<_ReturnType, _ProgressType> _ProgressDelegateType; + typedef typename Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_ReturnType, _ProgressType> _CompletionDelegateType; + typedef typename _ReturnType _ReturnType; + typedef typename _ProgressType _ProgressType; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; + + static const bool _TakesProgress = true; + static const bool _TakesToken = _TakesToken; + + template + static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); + } + }; + + template + struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, false> + { + typedef typename Windows::Foundation::IAsyncOperation<_ReturnType> _AsyncBaseType; + typedef _Zip _ProgressDelegateType; + typedef typename Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType> _CompletionDelegateType; + typedef typename _ReturnType _ReturnType; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; + + static const bool _TakesProgress = false; + static const bool _TakesToken = _TakesToken; + + template + static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); + } + }; + + template + struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, true> + { + typedef typename Windows::Foundation::IAsyncActionWithProgress<_ProgressType> _AsyncBaseType; + typedef typename Windows::Foundation::AsyncActionProgressHandler<_ProgressType> _ProgressDelegateType; + typedef typename Windows::Foundation::AsyncActionWithProgressCompletedHandler<_ProgressType> _CompletionDelegateType; + typedef void _ReturnType; + typedef typename _ProgressType _ProgressType; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; + + static const bool _TakesProgress = true; + static const bool _TakesToken = _TakesToken; + + template + static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); + } + }; + + template + struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, false> + { + typedef typename Windows::Foundation::IAsyncAction _AsyncBaseType; + typedef _Zip _ProgressDelegateType; + typedef typename Windows::Foundation::AsyncActionCompletedHandler _CompletionDelegateType; + typedef void _ReturnType; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; + + static const bool _TakesProgress = false; + static const bool _TakesToken = _TakesToken; + + template + static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); + } + }; + + template + struct _AsyncLambdaTypeTraits + { + typedef typename _FunctorTypeTraits<_Function>::_ReturnType _ReturnType; + typedef typename _FunctorTypeTraits<_Function>::_Argument1Type _Argument1Type; + typedef typename _CAFunctorOptions<_Function>::_ProgressType _ProgressType; + + static const bool _TakesProgress = _CAFunctorOptions<_Function>::_TakesProgress; + static const bool _TakesToken = _CAFunctorOptions<_Function>::_TakesToken; + + typedef typename _TaskTypeTraits<_ReturnType> _TaskTraits; + typedef typename _AsyncAttributes<_Function, _ProgressType, typename _TaskTraits::_TaskRetType, _TaskTraits, _TakesToken, _TakesProgress> _AsyncAttributes; + }; + + // *************************************************************************** + // AsyncInfo (and completion) Layer: + // + + // + // Internal base class implementation for async operations (based on internal Windows representation for ABI level async operations) + // + template < typename _Attributes, _AsyncResultType resultType = SingleResult > + ref class _AsyncInfoBase abstract : _Attributes::_AsyncBaseType + { + internal: + + _AsyncInfoBase() : + _M_currentStatus(_AsyncStatusInternal::_AsyncCreated), + _M_errorCode(S_OK), + _M_completeDelegate(nullptr), + _M_CompleteDelegateAssigned(0), + _M_CallbackMade(0) + { + _M_id = ::pplx::details::platform::GetNextAsyncId(); + } + + public: + virtual typename _Attributes::_ReturnType GetResults() + { + throw ::Platform::Exception::CreateException(E_UNEXPECTED); + } + + virtual property unsigned int Id + { + unsigned int get() + { + _CheckValidStateForAsyncInfoCall(); + + return _M_id; + } + + void set(unsigned int id) + { + _CheckValidStateForAsyncInfoCall(); + + if (id == 0) + { + throw ::Platform::Exception::CreateException(E_INVALIDARG); + } + else if (_M_currentStatus != _AsyncStatusInternal::_AsyncCreated) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + + _M_id = id; + } + } + + virtual property Windows::Foundation::AsyncStatus Status + { + Windows::Foundation::AsyncStatus get() + { + _CheckValidStateForAsyncInfoCall(); + + _AsyncStatusInternal _Current = _M_currentStatus; + + // + // Map our internal cancel pending to cancelled. This way "pending cancelled" looks to the outside as "cancelled" but + // can still transition to "completed" if the operation completes without acknowledging the cancellation request + // + switch(_Current) + { + case _AsyncCancelPending: + _Current = _AsyncCanceled; + break; + case _AsyncCreated: + _Current = _AsyncStarted; + break; + default: + break; + } + + return static_cast(_Current); + } + } + + virtual property Windows::Foundation::HResult ErrorCode + { + Windows::Foundation::HResult get() + { + _CheckValidStateForAsyncInfoCall(); + + Windows::Foundation::HResult _Hr; + _Hr.Value = _M_errorCode; + return _Hr; + } + } + + virtual property typename _Attributes::_ProgressDelegateType^ Progress + { + typename typename _Attributes::_ProgressDelegateType^ get() + { + return _GetOnProgress(); + } + + void set(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) + { + _PutOnProgress(_ProgressHandler); + } + } + + virtual void Cancel() + { + if (_TransitionToState(_AsyncCancelPending)) + { + _OnCancel(); + } + } + + virtual void Close() + { + if (_TransitionToState(_AsyncClosed)) + { + _OnClose(); + } + else + { + if (_M_currentStatus != _AsyncClosed) // Closed => Closed transition is just ignored + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE); + } + } + } + + virtual property typename _Attributes::_CompletionDelegateType^ Completed + { + typename _Attributes::_CompletionDelegateType^ get() + { + _CheckValidStateForDelegateCall(); + return _M_completeDelegate; + } + + void set(typename _Attributes::_CompletionDelegateType^ _CompleteHandler) + { + _CheckValidStateForDelegateCall(); + // this delegate property is "write once" + if (InterlockedIncrement(&_M_CompleteDelegateAssigned) == 1) + { + _M_completeDelegateContext = _ContextCallback::_CaptureCurrent(); + _M_completeDelegate = _CompleteHandler; + // Guarantee that the write of _M_completeDelegate is ordered with respect to the read of state below + // as perceived from _FireCompletion on another thread. + MemoryBarrier(); + if (_IsTerminalState()) + { + _FireCompletion(); + } + } + else + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_DELEGATE_ASSIGNMENT); + } + } + } + + + protected private: + + // _Start - this is not externally visible since async operations "hot start" before returning to the caller + void _Start() + { + if (_TransitionToState(_AsyncStarted)) + { + _OnStart(); + } + else + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE); + } + } + + + void _FireCompletion() + { + _TryTransitionToCompleted(); + + // we guarantee that completion can only ever be fired once + if (_M_completeDelegate != nullptr && InterlockedIncrement(&_M_CallbackMade) == 1) + { + _M_completeDelegateContext._CallInContext([=] { + _M_completeDelegate((_Attributes::_AsyncBaseType^)this, this->Status); + _M_completeDelegate = nullptr; + }); + } + } + + virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() + { + throw ::Platform::Exception::CreateException(E_UNEXPECTED); + } + + virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) + { + throw ::Platform::Exception::CreateException(E_UNEXPECTED); + } + + bool _TryTransitionToCompleted() + { + return _TransitionToState(_AsyncStatusInternal::_AsyncCompleted); + } + + bool _TryTransitionToCancelled() + { + return _TransitionToState(_AsyncStatusInternal::_AsyncCanceled); + } + + bool _TryTransitionToError(const HRESULT error) + { + _InterlockedCompareExchange(reinterpret_cast(&_M_errorCode), error, S_OK); + return _TransitionToState(_AsyncStatusInternal::_AsyncError); + } + + // This method checks to see if the delegate properties can be + // modified in the current state and generates the appropriate + // error hr in the case of violation. + inline void _CheckValidStateForDelegateCall() + { + if (_M_currentStatus == _AsyncClosed) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + } + + // This method checks to see if results can be collected in the + // current state and generates the appropriate error hr in + // the case of a violation. + inline void _CheckValidStateForResultsCall() + { + _AsyncStatusInternal _Current = _M_currentStatus; + + if (_Current == _AsyncError) + { + throw ::Platform::Exception::CreateException(_M_errorCode); + } +#pragma warning(push) +#pragma warning(disable: 4127) // Conditional expression is constant + // single result illegal before transition to Completed or Cancelled state + if (resultType == SingleResult) +#pragma warning(pop) + { + if (_Current != _AsyncCompleted) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + } + // multiple results can be called after Start has been called and before/after Completed + else if (_Current != _AsyncStarted && + _Current != _AsyncCancelPending && + _Current != _AsyncCanceled && + _Current != _AsyncCompleted) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + } + + // This method can be called by derived classes periodically to determine + // whether the asynchronous operation should continue processing or should + // be halted. + inline bool _ContinueAsyncOperation() + { + return (_M_currentStatus == _AsyncStarted); + } + + // These two methods are used to allow the async worker implementation do work on + // state transitions. No real "work" should be done in these methods. In other words + // they should not block for a long time on UI timescales. + virtual void _OnStart() = 0; + virtual void _OnClose() = 0; + virtual void _OnCancel() = 0; + + private: + + // This method is used to check if calls to the AsyncInfo properties + // (id, status, errorcode) are legal in the current state. It also + // generates the appropriate error hr to return in the case of an + // illegal call. + inline void _CheckValidStateForAsyncInfoCall() + { + _AsyncStatusInternal _Current = _M_currentStatus; + if (_Current == _AsyncClosed) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + else if (_Current == _AsyncCreated) + { + throw ::Platform::Exception::CreateException(E_ASYNC_OPERATION_NOT_STARTED); + } + + } + + inline bool _TransitionToState(const _AsyncStatusInternal _NewState) + { + _AsyncStatusInternal _Current = _M_currentStatus; + + // This enforces the valid state transitions of the asynchronous worker object + // state machine. + switch(_NewState) + { + case _AsyncStatusInternal::_AsyncStarted: + if (_Current != _AsyncCreated) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncCompleted: + if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncCancelPending: + if (_Current != _AsyncStarted) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncCanceled: + if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncError: + if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncClosed: + if (!_IsTerminalState(_Current)) + { + return false; + } + break; + default: + return false; + break; + } + + // attempt the transition to the new state + // Note: if currentStatus_ == _Current, then there was no intervening write + // by the async work object and the swap succeeded. + _AsyncStatusInternal _RetState = static_cast<_AsyncStatusInternal>( + _InterlockedCompareExchange(reinterpret_cast(&_M_currentStatus), + _NewState, + static_cast(_Current))); + + // ICE returns the former state, if the returned state and the + // state we captured at the beginning of this method are the same, + // the swap succeeded. + return (_RetState == _Current); + } + + inline bool _IsTerminalState() + { + return _IsTerminalState(_M_currentStatus); + } + + inline bool _IsTerminalState(_AsyncStatusInternal status) + { + return (status == _AsyncError || + status == _AsyncCanceled || + status == _AsyncCompleted || + status == _AsyncClosed); + } + + private: + + _ContextCallback _M_completeDelegateContext; + typename _Attributes::_CompletionDelegateType^ volatile _M_completeDelegate; + _AsyncStatusInternal volatile _M_currentStatus; + HRESULT volatile _M_errorCode; + unsigned int _M_id; + long volatile _M_CompleteDelegateAssigned; + long volatile _M_CallbackMade; + }; + + // *************************************************************************** + // Progress Layer (optional): + // + + template< typename _Attributes, bool _HasProgress, _AsyncResultType _ResultType = SingleResult > + ref class _AsyncProgressBase abstract : _AsyncInfoBase<_Attributes, _ResultType> + { + }; + + template< typename _Attributes, _AsyncResultType _ResultType> + ref class _AsyncProgressBase<_Attributes, true, _ResultType> abstract : _AsyncInfoBase<_Attributes, _ResultType> + { + internal: + + _AsyncProgressBase() : _AsyncInfoBase<_Attributes, _ResultType>(), + _M_progressDelegate(nullptr) + { + } + + virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() override + { + _CheckValidStateForDelegateCall(); + return _M_progressDelegate; + } + + virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) override + { + _CheckValidStateForDelegateCall(); + _M_progressDelegate = _ProgressHandler; + _M_progressDelegateContext = _ContextCallback::_CaptureCurrent(); + } + + void _FireProgress(const typename _Attributes::_ProgressType& _ProgressValue) + { + if (_M_progressDelegate != nullptr) + { + _M_progressDelegateContext._CallInContext([=] { + _M_progressDelegate((_Attributes::_AsyncBaseType^)this, _ProgressValue); + }); + } + } + + private: + + _ContextCallback _M_progressDelegateContext; + typename _Attributes::_ProgressDelegateType^ _M_progressDelegate; + }; + + template + ref class _AsyncBaseProgressLayer abstract : _AsyncProgressBase<_Attributes, _Attributes::_TakesProgress, _ResultType> + { + }; + + // *************************************************************************** + // Task Adaptation Layer: + // + + // + // _AsyncTaskThunkBase provides a bridge between IAsync and task. + // + template + ref class _AsyncTaskThunkBase abstract : _AsyncBaseProgressLayer<_Attributes> + { + public: + + virtual _ReturnType GetResults() override + { + _CheckValidStateForResultsCall(); + return _M_task.get(); + } + + internal: + + typedef task<_ReturnType> _TaskType; + + _AsyncTaskThunkBase(const _TaskType& _Task) + : _M_task(_Task) + { + } + + _AsyncTaskThunkBase() + { + } + + protected: + + virtual void _OnStart() override + { + _M_task.then( [=](_TaskType _Antecedent) { + try + { + _Antecedent.get(); + } + catch(task_canceled&) + { + _TryTransitionToCancelled(); + } + catch(::Platform::Exception^ _Ex) + { + _TryTransitionToError(_Ex->HResult); + } + catch(...) + { + _TryTransitionToError(E_FAIL); + } + _FireCompletion(); + }); + } + + internal: + + _TaskType _M_task; + cancellation_token_source _M_cts; + }; + + template + ref class _AsyncTaskThunk : _AsyncTaskThunkBase<_Attributes, typename _Attributes::_ReturnType> + { + internal: + + _AsyncTaskThunk(const _TaskType& _Task) : + _AsyncTaskThunkBase(_Task) + { + } + + _AsyncTaskThunk() + { + } + + protected: + + virtual void _OnClose() override + { + } + + virtual void _OnCancel() override + { + _M_cts.cancel(); + } + }; + + // *************************************************************************** + // Async Creation Layer: + // + template + ref class _AsyncTaskGeneratorThunk sealed : _AsyncTaskThunk::_AsyncAttributes> + { + internal: + + typedef typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes _Attributes; + typedef typename _AsyncTaskThunk<_Attributes> _Base; + typedef typename _Attributes::_AsyncBaseType _AsyncBaseType; + + _AsyncTaskGeneratorThunk(const _Function& _Func, const _TaskCreationCallstack &_callstack) : _M_func(_Func), _M_creationCallstack(_callstack) + { + // Virtual call here is safe as the class is declared 'sealed' + _Start(); + } + + protected: + + // + // The only thing we must do different from the base class is we must spin the hot task on transition from Created->Started. Otherwise, + // let the base thunk handle everything. + // + + virtual void _OnStart() override + { + // + // Call the appropriate task generator to actually produce a task of the expected type. This might adapt the user lambda for progress reports, + // wrap the return result in a task, or allow for direct return of a task depending on the form of the lambda. + // + _M_task = _Attributes::_Generate_Task(_M_func, this, _M_cts, _M_creationCallstack); + _Base::_OnStart(); + } + + virtual void _OnCancel() override + { + _Base::_OnCancel(); + } + + private: + _TaskCreationCallstack _M_creationCallstack; + _Function _M_func; + }; +} // namespace details + +/// +/// Creates a Windows Runtime asynchronous construct based on a user supplied lambda or function object. The return type of create_async is +/// one of either IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or +/// IAsyncOperationWithProgress<TResult, TProgress>^ based on the signature of the lambda passed to the method. +/// +/// +/// The lambda or function object from which to create a Windows Runtime asynchronous construct. +/// +/// +/// An asynchronous construct represented by an IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or an +/// IAsyncOperationWithProgress<TResult, TProgress>^. The interface returned depends on the signature of the lambda passed into the function. +/// +/// +/// The return type of the lambda determines whether the construct is an action or an operation. +/// Lambdas that return void cause the creation of actions. Lambdas that return a result of type TResult cause the creation of +/// operations of TResult. +/// The lambda may also return a task<TResult> which encapsulates the aysnchronous work within itself or is the continuation of +/// a chain of tasks that represent the asynchronous work. In this case, the lambda itself is executed inline, since the tasks are the ones that +/// execute asynchronously, and the return type of the lambda is unwrapped to produce the asynchronous construct returned by create_async. +/// This implies that a lambda that returns a task<void> will cause the creation of actions, and a lambda that returns a task<TResult> will +/// cause the creation of operations of TResult. +/// The lambda may take either zero, one or two arguments. The valid arguments are progress_reporter<TProgress> and +/// cancellation_token, in that order if both are used. A lambda without arguments causes the creation of an asynchronous construct without +/// the capability for progress reporting. A lambda that takes a progress_reporter<TProgress> will cause create_async to return an asynchronous +/// construct which reports progress of type TProgress each time the report method of the progress_reporter object is called. A lambda that +/// takes a cancellation_token may use that token to check for cancellation, or pass it to tasks that it creates so that cancellation of the +/// asynchronous construct causes cancellation of those tasks. +/// If the body of the lambda or function object returns a result (and not a task<TResult>), the lamdba will be executed +/// asynchronously within the process MTA in the context of a task the Runtime implicitly creates for it. The IAsyncInfo::Cancel method will +/// cause cancellation of the implicit task. +/// If the body of the lambda returns a task, the lamba executes inline, and by declaring the lambda to take an argument of type +/// cancellation_token you can trigger cancellation of any tasks you create within the lambda by passing that token in when you create them. +/// You may also use the register_callback method on the token to cause the Runtime to invoke a callback when you call IAsyncInfo::Cancel on +/// the async operation or action produced.. +/// This function is only available to Windows Store apps. +/// +/// +/// +/// +/**/ +template +__declspec(noinline) +details::_AsyncTaskGeneratorThunk<_Function> ^create_async(const _Function& _Func) +{ + static_assert(std::is_same::value, + "argument to create_async must be a callable object taking zero, one or two arguments"); + return ref new details::_AsyncTaskGeneratorThunk<_Function>(_Func, _CAPTURE_CALLSTACK()); +} + +#endif /* defined (__cplusplus_winrt) */ + +namespace details +{ + // Helper struct for when_all operators to know when tasks have completed + template + struct _RunAllParam + { + _RunAllParam() : _M_completeCount(0), _M_numTasks(0) + { + } + + void _Resize(size_t _Len, bool _SkipVector = false) + { + _M_numTasks = _Len; + if (!_SkipVector) + { + _M_vector._Result.resize(_Len); + } + } + + task_completion_event<_Unit_type> _M_completed; + _ResultHolder > _M_vector; + _ResultHolder<_Type> _M_mergeVal; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + }; + + template + struct _RunAllParam > + { + _RunAllParam() : _M_completeCount(0), _M_numTasks(0) + { + } + + void _Resize(size_t _Len, bool _SkipVector = false) + { + _M_numTasks = _Len; + + if (!_SkipVector) + { + _M_vector.resize(_Len); + } + } + + task_completion_event<_Unit_type> _M_completed; + std::vector<_ResultHolder > > _M_vector; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + }; + + // Helper struct specialization for void + template<> + struct _RunAllParam<_Unit_type> + { + _RunAllParam() : _M_completeCount(0), _M_numTasks(0) + { + } + + void _Resize(size_t _Len) + { + _M_numTasks = _Len; + } + + task_completion_event<_Unit_type> _M_completed; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + }; + + inline void _JoinAllTokens_Add(const cancellation_token_source& _MergedSrc, _CancellationTokenState *_PJoinedTokenState) + { + if (_PJoinedTokenState != nullptr && _PJoinedTokenState != _CancellationTokenState::_None()) + { + cancellation_token _T = cancellation_token::_FromImpl(_PJoinedTokenState); + _T.register_callback( [=](){ + _MergedSrc.cancel(); + }); + } + } + + template + void _WhenAllContinuationWrapper(_RunAllParam<_ElementType>* _PParam, _Function _Func, task<_TaskType>& _Task) + { + if (_Task._GetImpl()->_IsCompleted()) + { + _Func(); + if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) + { + // Inline execute its direct continuation, the _ReturnTask + _PParam->_M_completed.set(_Unit_type()); + // It's safe to delete it since all usage of _PParam in _ReturnTask has been finished. + delete _PParam; + } + } + else + { + _ASSERTE(_Task._GetImpl()->_IsCanceled()); + if (_Task._GetImpl()->_HasUserException()) + { + // _Cancel will return false if the TCE is already canceled with or without exception + _PParam->_M_completed._Cancel(_Task._GetImpl()->_GetExceptionHolder()); + } + else + { + _PParam->_M_completed._Cancel(); + } + + if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) + { + delete _PParam; + } + } + } + + template + struct _WhenAllImpl + { + static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + + auto _PParam = new _RunAllParam<_ElementType>(); + cancellation_token_source _MergedSource; + + // Step1: Create task completion event. + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_MergedSource.get_token()); + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { + return _PParam->_M_vector.Get(); + }, nullptr); + + // Step2: Combine and check tokens, and count elements in range. + if (_PTokenState) + { + _JoinAllTokens_Add(_MergedSource, _PTokenState); + _PParam->_Resize(static_cast(std::distance(_Begin, _End))); + } + else + { + size_t _TaskNum = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + _TaskNum++; + _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); + } + _PParam->_Resize(_TaskNum); + } + + // Step3: Check states of previous tasks. + if( _Begin == _End ) + { + _PParam->_M_completed.set(_Unit_type()); + delete _PParam; + } + else + { + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) { + + auto _PParamCopy = _PParam; + auto _IndexCopy = _Index; + auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask](){ + _PParamCopy->_M_vector._Result[_IndexCopy] = _ResultTask._GetImpl()->_GetResult(); + }; + + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + _Index++; + } + } + + return _ReturnTask; + } + }; + + template + struct _WhenAllImpl, _Iterator> + { + static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + + auto _PParam = new _RunAllParam>(); + cancellation_token_source _MergedSource; + + // Step1: Create task completion event. + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_MergedSource.get_token()); + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { + _ASSERTE(_PParam->_M_completeCount == _PParam->_M_numTasks); + std::vector<_ElementType> _Result; + for(size_t _I = 0; _I < _PParam->_M_numTasks; _I++) + { + const std::vector<_ElementType>& _Vec = _PParam->_M_vector[_I].Get(); + _Result.insert(_Result.end(), _Vec.begin(), _Vec.end()); + } + return _Result; + }, nullptr); + + // Step2: Combine and check tokens, and count elements in range. + if (_PTokenState) + { + _JoinAllTokens_Add(_MergedSource, _PTokenState); + _PParam->_Resize(static_cast(std::distance(_Begin, _End))); + } + else + { + size_t _TaskNum = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + _TaskNum++; + _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); + } + _PParam->_Resize(_TaskNum); + } + + // Step3: Check states of previous tasks. + if( _Begin == _End ) + { + _PParam->_M_completed.set(_Unit_type()); + delete _PParam; + } + else + { + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task> _ResultTask) { + + auto _PParamCopy = _PParam; + auto _IndexCopy = _Index; + auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask]() { + _PParamCopy->_M_vector[_IndexCopy].Set(_ResultTask._GetImpl()->_GetResult()); + }; + + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + _Index++; + } + } + + return _ReturnTask; + } + }; + + template + struct _WhenAllImpl + { + static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + + auto _PParam = new _RunAllParam<_Unit_type>(); + cancellation_token_source _MergedSource; + + // Step1: Create task completion event. + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_MergedSource.get_token()); + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) { + }, nullptr); + + // Step2: Combine and check tokens, and count elements in range. + if (_PTokenState) + { + _JoinAllTokens_Add(_MergedSource, _PTokenState); + _PParam->_Resize(static_cast(std::distance(_Begin, _End))); + } + else + { + size_t _TaskNum = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + _TaskNum++; + _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); + } + _PParam->_Resize(_TaskNum); + } + + // Step3: Check states of previous tasks. + if( _Begin == _End ) + { + _PParam->_M_completed.set(_Unit_type()); + delete _PParam; + } + else + { + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PTask->_Then([_PParam](task _ResultTask) { + auto _Func = [](){}; + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + } + } + + return _ReturnTask; + } + }; + + template + task> _WhenAllVectorAndValue(const task>& _VectorTask, const task<_ReturnType>& _ValueTask, + bool _OutputVectorFirst) + { + auto _PParam = new _RunAllParam<_ReturnType>(); + cancellation_token_source _MergedSource; + + // Step1: Create task completion event. + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ReturnType> { + _ASSERTE(_PParam->_M_completeCount == 2); + auto _Result = _PParam->_M_vector.Get(); // copy by value + auto _mergeVal = _PParam->_M_mergeVal.Get(); + + if (_OutputVectorFirst == true) + { + _Result.push_back(_mergeVal); + } + else + { + _Result.insert(_Result.begin(), _mergeVal); + } + return _Result; + }, nullptr); + + // Step2: Combine and check tokens. + _JoinAllTokens_Add(_MergedSource, _VectorTask._GetImpl()->_M_pTokenState); + _JoinAllTokens_Add(_MergedSource, _ValueTask._GetImpl()->_M_pTokenState); + + // Step3: Check states of previous tasks. + _PParam->_Resize(2, true); + + if (_VectorTask.is_apartment_aware() || _ValueTask.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + _VectorTask._Then([_PParam](task> _ResultTask) { + auto _PParamCopy = _PParam; + auto _Func = [_PParamCopy, &_ResultTask]() { + auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); + _PParamCopy->_M_vector.Set(_ResultLocal); + }; + + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + _ValueTask._Then([_PParam](task<_ReturnType> _ResultTask) { + auto _PParamCopy = _PParam; + auto _Func = [_PParamCopy, &_ResultTask]() { + auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); + _PParamCopy->_M_mergeVal.Set(_ResultLocal); + }; + + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + return _ReturnTask; + } +} // namespace details + +/// +/// Creates a task that will complete successfully when all of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the input iterator. +/// +/// +/// The position of the first element in the range of elements to be combined into the resulting task. +/// +/// +/// The position of the first element beyond the range of elements to be combined into the resulting task. +/// +/// +/// A task that completes sucessfully when all of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +auto when_all(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) + -> decltype (details::_WhenAllImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); +} + +/// +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +task> operator&&(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) +{ + task<_ReturnType> _PTasks[2] = {_Lhs, _Rhs}; + return when_all(_PTasks, _PTasks+2); +} + +/// +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +task> operator&&(const task> & _Lhs, const task<_ReturnType> & _Rhs) +{ + return details::_WhenAllVectorAndValue(_Lhs, _Rhs, true); +} + +/// +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +task> operator&&(const task<_ReturnType> & _Lhs, const task> & _Rhs) +{ + return details::_WhenAllVectorAndValue(_Rhs, _Lhs, false); +} + +/// +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +task> operator&&(const task> & _Lhs, const task> & _Rhs) +{ + task> _PTasks[2] = {_Lhs, _Rhs}; + return when_all(_PTasks, _PTasks+2); +} + +/// +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +inline task operator&&(const task & _Lhs, const task & _Rhs) +{ + task _PTasks[2] = {_Lhs, _Rhs}; + return when_all(_PTasks, _PTasks+2); +} + +namespace details +{ + // Helper struct for when_any operators to know when tasks have completed + template + struct _RunAnyParam + { + _RunAnyParam() : _M_exceptionRelatedToken(nullptr), _M_completeCount(0), _M_numTasks(0), _M_fHasExplicitToken(false) + { + } + ~_RunAnyParam() + { + if (_CancellationTokenState::_IsValid(_M_exceptionRelatedToken)) + _M_exceptionRelatedToken->_Release(); + } + task_completion_event<_CompletionType> _M_Completed; + cancellation_token_source _M_cancellationSource; + _CancellationTokenState * _M_exceptionRelatedToken; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + bool _M_fHasExplicitToken; + }; + + template + void _WhenAnyContinuationWrapper(_RunAnyParam<_CompletionType> * _PParam, const _Function & _Func, task<_TaskType>& _Task) + { + bool _IsTokenCancled = !_PParam->_M_fHasExplicitToken && _Task._GetImpl()->_M_pTokenState != _CancellationTokenState::_None() && _Task._GetImpl()->_M_pTokenState->_IsCanceled(); + if (_Task._GetImpl()->_IsCompleted() && !_IsTokenCancled) + { + _Func(); + if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) + { + delete _PParam; + } + } + else + { + _ASSERTE(_Task._GetImpl()->_IsCanceled() || _IsTokenCancled); + if (_Task._GetImpl()->_HasUserException() && !_IsTokenCancled) + { + if (_PParam->_M_Completed._StoreException(_Task._GetImpl()->_GetExceptionHolder())) + { + // This can only enter once. + _PParam->_M_exceptionRelatedToken = _Task._GetImpl()->_M_pTokenState; + _ASSERTE(_PParam->_M_exceptionRelatedToken); + // Deref token will be done in the _PParam destructor. + if (_PParam->_M_exceptionRelatedToken != _CancellationTokenState::_None()) + { + _PParam->_M_exceptionRelatedToken->_Reference(); + } + } + } + + if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) + { + // If no one has be completed so far, we need to make some final cancellation decision. + if (!_PParam->_M_Completed._IsTriggered()) + { + // If we already explicit token, we can skip the token join part. + if (!_PParam->_M_fHasExplicitToken) + { + if (_PParam->_M_exceptionRelatedToken) + { + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PParam->_M_exceptionRelatedToken); + } + else + { + // If haven't captured any exception token yet, there was no exception for all those tasks, + // so just pick a random token (current one) for normal cancellation. + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Task._GetImpl()->_M_pTokenState); + } + } + // Do exception cancellation or normal cancellation based on whether it has stored exception. + _PParam->_M_Completed._Cancel(); + } + delete _PParam; + } + } + } + + template + struct _WhenAnyImpl + { + static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + if( _Begin == _End ) + { + throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); + } + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + auto _PParam = new _RunAnyParam, _CancellationTokenState *>>(); + + if (_PTokenState) + { + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); + _PParam->_M_fHasExplicitToken = true; + } + + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); + task, _CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _Options); + + // Keep a copy ref to the token source + auto _CancellationSource = _PParam->_M_cancellationSource; + + _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _Any_tasks_completed._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) { + auto _PParamCopy = _PParam; // Dev10 + auto _IndexCopy = _Index; // Dev10 + auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { + _PParamCopy->_M_Completed.set(std::make_pair(std::make_pair(_ResultTask._GetImpl()->_GetResult(), _IndexCopy), _ResultTask._GetImpl()->_M_pTokenState)); + }; + + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + _Index++; + } + + // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. + return _Any_tasks_completed._Then([=](std::pair, _CancellationTokenState *> _Result) -> std::pair<_ElementType, size_t> { + _ASSERTE(_Result.second); + if (!_PTokenState) + { + _JoinAllTokens_Add(_CancellationSource, _Result.second); + } + return _Result.first; + }, nullptr); + } + }; + + template + struct _WhenAnyImpl + { + static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + if( _Begin == _End ) + { + throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); + } + + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + auto _PParam = new _RunAnyParam>(); + + if (_PTokenState) + { + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); + _PParam->_M_fHasExplicitToken = true; + } + + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); + task> _Any_tasks_completed(_PParam->_M_Completed, _Options); + + // Keep a copy ref to the token source + auto _CancellationSource = _PParam->_M_cancellationSource; + + _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _Any_tasks_completed._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task _ResultTask) { + auto _PParamCopy = _PParam; // Dev10 + auto _IndexCopy = _Index; // Dev10 + auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { + _PParamCopy->_M_Completed.set(std::make_pair(_IndexCopy, _ResultTask._GetImpl()->_M_pTokenState)); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + _Index++; + } + + // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. + return _Any_tasks_completed._Then([=](std::pair _Result) -> size_t { + _ASSERTE(_Result.second); + if (!_PTokenState) + { + _JoinAllTokens_Add(_CancellationSource, _Result.second); + } + return _Result.first; + }, nullptr); + } + }; +} // namespace details + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the input iterator. +/// +/// +/// The position of the first element in the range of elements to be combined into the resulting task. +/// +/// +/// The position of the first element beyond the range of elements to be combined into the resulting task. +/// +/// +/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result +/// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void +/// the output is a task<size_t>, where the result is the index of the completing task. +/// +/// +/**/ +template +auto when_any(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) + -> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); +} + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the input iterator. +/// +/// +/// The position of the first element in the range of elements to be combined into the resulting task. +/// +/// +/// The position of the first element beyond the range of elements to be combined into the resulting task. +/// +/// +/// The cancellation token which controls cancellation of the returned task. If you do not provide a cancellation token, the resulting +/// task will receive the cancellation token of the task that causes it to complete. +/// +/// +/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result +/// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void +/// the output is a task<size_t>, where the result is the index of the completing task. +/// +/// +/**/ +template +auto when_any(_Iterator _Begin, _Iterator _End, cancellation_token _CancellationToken) + -> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End); +} + +/// +/// Creates a task that will complete successfully when either of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task +/// will also be a task<void>. +/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> +/// and the other one is of type task<T>. +/// +/// +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call get() or wait() on that task. +/// +/// +/**/ +template +task<_ReturnType> operator||(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) +{ + auto _PParam = new details::_RunAnyParam>(); + + task> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); + // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, + // So that _PParam can be used before it getting deleted. + auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair<_ReturnType, size_t> _Ret) -> _ReturnType { + _ASSERTE(_Ret.second); + _JoinAllTokens_Add(_PParam->_M_cancellationSource, reinterpret_cast(_Ret.second)); + return _Ret.first; + }, nullptr); + + if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PParam->_M_numTasks = 2; + auto _Continuation = [_PParam](task<_ReturnType> _ResultTask) { + // Dev10 compiler bug + auto _PParamCopy = _PParam; + auto _Func = [&_ResultTask, _PParamCopy]() { + _PParamCopy->_M_Completed.set(std::make_pair(_ResultTask._GetImpl()->_GetResult(), reinterpret_cast(_ResultTask._GetImpl()->_M_pTokenState))); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }; + + _Lhs._Then(_Continuation, details::_CancellationTokenState::_None()); + _Rhs._Then(_Continuation, details::_CancellationTokenState::_None()); + + return _ReturnTask; +} + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task +/// will also be a task<void>. +/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> +/// and the other one is of type task<T>. +/// +/// +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call get() or wait() on that task. +/// +/// +/**/ +template +task> operator||(const task> & _Lhs, const task<_ReturnType> & _Rhs) +{ + auto _PParam = new details::_RunAnyParam, details::_CancellationTokenState *>>(); + + task, details::_CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); + + // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, + // So that _PParam can be used before it getting deleted. + auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair, details::_CancellationTokenState *> _Ret) -> std::vector<_ReturnType> { + _ASSERTE(_Ret.second); + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); + return _Ret.first; + }, nullptr); + + if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PParam->_M_numTasks = 2; + _Lhs._Then([_PParam](task> _ResultTask) { + // Dev10 compiler bug + auto _PParamCopy = _PParam; + auto _Func = [&_ResultTask, _PParamCopy]() { + auto _Result = _ResultTask._GetImpl()->_GetResult(); + _PParamCopy->_M_Completed.set(std::make_pair(_Result, _ResultTask._GetImpl()->_M_pTokenState)); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }, details::_CancellationTokenState::_None()); + + + _Rhs._Then([_PParam](task<_ReturnType> _ResultTask) + { + auto _PParamCopy = _PParam; + auto _Func = [&_ResultTask, _PParamCopy]() { + auto _Result = _ResultTask._GetImpl()->_GetResult(); + + std::vector<_ReturnType> _Vec; + _Vec.push_back(_Result); + _PParamCopy->_M_Completed.set(std::make_pair(_Vec, _ResultTask._GetImpl()->_M_pTokenState)); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }, details::_CancellationTokenState::_None()); + + return _ReturnTask; +} + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task +/// will also be a task<void>. +/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> +/// and the other one is of type task<T>. +/// +/// +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call get() or wait() on that task. +/// +/// +/**/ +template +task> operator||(const task<_ReturnType> & _Lhs, const task> & _Rhs) +{ + return _Rhs || _Lhs; +} + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task +/// will also be a task<void>. +/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> +/// and the other one is of type task<T>. +/// +/// +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call get() or wait() on that task. +/// +/// +/**/ +inline task operator||(const task & _Lhs, const task & _Rhs) +{ + auto _PParam = new details::_RunAnyParam>(); + + task> _Any_task_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); + // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, + // So that _PParam can be used before it getting deleted. + auto _ReturnTask = _Any_task_completed._Then([=](std::pair _Ret) { + _ASSERTE(_Ret.second); + details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); + }, nullptr); + + if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PParam->_M_numTasks = 2; + auto _Continuation = [_PParam](task _ResultTask) mutable { + // Dev10 compiler needs this. + auto _PParam1 = _PParam; + auto _Func = [&_ResultTask, _PParam1]() { + _PParam1->_M_Completed.set(std::make_pair(details::_Unit_type(), _ResultTask._GetImpl()->_M_pTokenState)); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }; + + _Lhs._Then(_Continuation, details::_CancellationTokenState::_None()); + _Rhs._Then(_Continuation, details::_CancellationTokenState::_None()); + + return _ReturnTask; +} + +template +task<_Ty> task_from_result(_Ty _Param, const task_options& _TaskOptions = task_options()) +{ + task_completion_event<_Ty> _Tce; + _Tce.set(_Param); + return create_task(_Tce, _TaskOptions); +} + +// Work around VS 2010 compiler bug +#if _MSC_VER == 1600 +inline task task_from_result(bool _Param) +{ + task_completion_event _Tce; + _Tce.set(_Param); + return create_task(_Tce, task_options()); +} +#endif +inline task task_from_result(const task_options& _TaskOptions = task_options()) +{ + task_completion_event _Tce; + _Tce.set(); + return create_task(_Tce, _TaskOptions); +} + +template +task<_TaskType> task_from_exception(_ExType _Exception, const task_options& _TaskOptions = task_options()) +{ + task_completion_event<_TaskType> _Tce; + _Tce.set_exception(_Exception); + return create_task(_Tce, _TaskOptions); +} + +namespace details +{ + /// + /// A convenient extension to Concurrency: loop until a condition is no longer met + /// + /// + /// A function representing the body of the loop. It will be invoked at least once and + /// then repetitively as long as it returns true. + /// + inline + task do_while(std::function(void)> func) + { + task first = func(); + return first.then([=](bool guard) -> task { + if (guard) + return do_while(func); + else + return first; + }); + } + +} // namespace details + +} // namespace Concurrency + +#pragma pop_macro("new") + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#pragma pack(pop) + +#endif // (defined(_MSC_VER) && (_MSC_VER >= 1800)) + +#ifndef _CONCRT_H +#ifndef _LWRCASE_CNCRRNCY +#define _LWRCASE_CNCRRNCY +// Note to reader: we're using lower-case namespace names everywhere, but the 'Concurrency' namespace +// is capitalized for historical reasons. The alias let's us pretend that style issue doesn't exist. +namespace Concurrency {} +namespace concurrency = Concurrency; +#endif +#endif + +#endif // _PPLXTASKS_H diff --git a/3rdparty/cpprestsdk/include/pplx/pplxwin.h b/3rdparty/cpprestsdk/include/pplx/pplxwin.h new file mode 100644 index 00000000..9b66e11e --- /dev/null +++ b/3rdparty/cpprestsdk/include/pplx/pplxwin.h @@ -0,0 +1,297 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Windows specific pplx implementations +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#if !defined(_WIN32) || _MSC_VER < 1800 || CPPREST_FORCE_PPLX + +#include "cpprest/details/cpprest_compat.h" +#include "pplx/pplxinterface.h" + +namespace pplx +{ + +namespace details +{ + namespace platform + { + /// + /// Returns a unique identifier for the execution thread where this routine in invoked + /// + _PPLXIMP long __cdecl GetCurrentThreadId(); + + /// + /// Yields the execution of the current execution thread - typically when spin-waiting + /// + _PPLXIMP void __cdecl YieldExecution(); + + /// + /// Captures the callstack + /// + __declspec(noinline) _PPLXIMP size_t __cdecl CaptureCallstack(void **, size_t, size_t); + +#if defined(__cplusplus_winrt) + /// + // Internal API which retrieves the next async id. + /// + _PPLXIMP unsigned int __cdecl GetNextAsyncId(); +#endif + } + + /// + /// Manual reset event + /// + class event_impl + { + public: + + static const unsigned int timeout_infinite = 0xFFFFFFFF; + + _PPLXIMP event_impl(); + + _PPLXIMP ~event_impl(); + + _PPLXIMP void set(); + + _PPLXIMP void reset(); + + _PPLXIMP unsigned int wait(unsigned int timeout); + + unsigned int wait() + { + return wait(event_impl::timeout_infinite); + } + + private: + // Windows events + void * _M_impl; + + event_impl(const event_impl&); // no copy constructor + event_impl const & operator=(const event_impl&); // no assignment operator + }; + + /// + /// Mutex - lock for mutual exclusion + /// + class critical_section_impl + { + public: + + _PPLXIMP critical_section_impl(); + + _PPLXIMP ~critical_section_impl(); + + _PPLXIMP void lock(); + + _PPLXIMP void unlock(); + + private: + + typedef void * _PPLX_BUFFER; + + // Windows critical section + _PPLX_BUFFER _M_impl[8]; + + critical_section_impl(const critical_section_impl&); // no copy constructor + critical_section_impl const & operator=(const critical_section_impl&); // no assignment operator + }; + +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA + /// + /// Reader writer lock + /// + class reader_writer_lock_impl + { + public: + + class scoped_lock_read + { + public: + explicit scoped_lock_read(reader_writer_lock_impl &_Reader_writer_lock) : _M_reader_writer_lock(_Reader_writer_lock) + { + _M_reader_writer_lock.lock_read(); + } + + ~scoped_lock_read() + { + _M_reader_writer_lock.unlock(); + } + + private: + reader_writer_lock_impl& _M_reader_writer_lock; + scoped_lock_read(const scoped_lock_read&); // no copy constructor + scoped_lock_read const & operator=(const scoped_lock_read&); // no assignment operator + }; + + _PPLXIMP reader_writer_lock_impl(); + + _PPLXIMP void lock(); + + _PPLXIMP void lock_read(); + + _PPLXIMP void unlock(); + + private: + + // Windows slim reader writer lock + void * _M_impl; + + // Slim reader writer lock doesn't have a general 'unlock' method. + // We need to track how it was acquired and release accordingly. + // true - lock exclusive + // false - lock shared + bool m_locked_exclusive; + }; +#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA + + /// + /// Recursive mutex + /// + class recursive_lock_impl + { + public: + + recursive_lock_impl() + : _M_owner(-1), _M_recursionCount(0) + { + } + + ~recursive_lock_impl() + { + _ASSERTE(_M_owner == -1); + _ASSERTE(_M_recursionCount == 0); + } + + void recursive_lock_impl::lock() + { + auto id = ::pplx::details::platform::GetCurrentThreadId(); + + if ( _M_owner == id ) + { + _M_recursionCount++; + } + else + { + _M_cs.lock(); + _M_owner = id; + _M_recursionCount = 1; + } + } + + void recursive_lock_impl::unlock() + { + _ASSERTE(_M_owner == ::pplx::details::platform::GetCurrentThreadId()); + _ASSERTE(_M_recursionCount >= 1); + + _M_recursionCount--; + + if ( _M_recursionCount == 0 ) + { + _M_owner = -1; + _M_cs.unlock(); + } + } + + private: + pplx::details::critical_section_impl _M_cs; + long _M_recursionCount; + volatile long _M_owner; + }; + + class windows_scheduler : public pplx::scheduler_interface + { + public: + _PPLXIMP virtual void schedule( TaskProc_t proc, _In_ void* param); + }; + +} // namespace details + +/// +/// A generic RAII wrapper for locks that implement the critical_section interface +/// std::lock_guard +/// +template +class scoped_lock +{ +public: + explicit scoped_lock(_Lock& _Critical_section) : _M_critical_section(_Critical_section) + { + _M_critical_section.lock(); + } + + ~scoped_lock() + { + _M_critical_section.unlock(); + } + +private: + _Lock& _M_critical_section; + + scoped_lock(const scoped_lock&); // no copy constructor + scoped_lock const & operator=(const scoped_lock&); // no assignment operator +}; + +// The extensibility namespace contains the type definitions that are used internally +namespace extensibility +{ + typedef ::pplx::details::event_impl event_t; + + typedef ::pplx::details::critical_section_impl critical_section_t; + typedef scoped_lock scoped_critical_section_t; + +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA + typedef ::pplx::details::reader_writer_lock_impl reader_writer_lock_t; + typedef scoped_lock scoped_rw_lock_t; + typedef reader_writer_lock_t::scoped_lock_read scoped_read_lock_t; +#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA + + + typedef ::pplx::details::recursive_lock_impl recursive_lock_t; + typedef scoped_lock scoped_recursive_lock_t; +} + +/// +/// Default scheduler type +/// +typedef details::windows_scheduler default_scheduler_t; + +namespace details +{ + /// + /// Terminate the process due to unhandled exception + /// + + #ifndef _REPORT_PPLTASK_UNOBSERVED_EXCEPTION + #define _REPORT_PPLTASK_UNOBSERVED_EXCEPTION() do { \ + __debugbreak(); \ + std::terminate(); \ + } while(false) + #endif // _REPORT_PPLTASK_UNOBSERVED_EXCEPTION + +} // namespace details + +} // namespace pplx + +#endif \ No newline at end of file diff --git a/3rdparty/cpprestsdk/include/pplx/threadpool.h b/3rdparty/cpprestsdk/include/pplx/threadpool.h new file mode 100644 index 00000000..13b1e040 --- /dev/null +++ b/3rdparty/cpprestsdk/include/pplx/threadpool.h @@ -0,0 +1,161 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* +* Simple Linux implementation of a static thread pool. +* +* For the latest on this and related APIs, please see http://casablanca.codeplex.com. +* +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +***/ +#pragma once + +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif +#include "boost/asio.hpp" +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#if (defined(ANDROID) || defined(__ANDROID__)) +#include +#include +#include "pplx/pplx.h" +#endif + +namespace crossplat { + +#if (defined(ANDROID) || defined(__ANDROID__)) +// IDEA: Break this section into a separate android/jni header +extern std::atomic JVM; +JNIEnv* get_jvm_env(); + +struct java_local_ref_deleter +{ + void operator()(jobject lref) const + { + crossplat::get_jvm_env()->DeleteLocalRef(lref); + } +}; + +template +using java_local_ref = std::unique_ptr::type, java_local_ref_deleter>; +#endif + +class threadpool +{ +public: + + threadpool(size_t n) + : m_service(n), + m_work(m_service) + { + for (size_t i = 0; i < n; i++) + add_thread(); + } + + static threadpool& shared_instance(); + + ~threadpool() + { + m_service.stop(); + for (auto iter = m_threads.begin(); iter != m_threads.end(); ++iter) + { + pthread_t t = *iter; + void* res; + pthread_join(t, &res); + } + } + + template + void schedule(T task) + { + m_service.post(task); + } + + boost::asio::io_service& service() + { + return m_service; + } + +private: + struct _cancel_thread { }; + + void add_thread() + { + pthread_t t; + auto result = pthread_create(&t, nullptr, &thread_start, this); + if (result == 0) + m_threads.push_back(t); + } + + void remove_thread() + { + schedule([]() -> void { throw _cancel_thread(); }); + } + +#if (defined(ANDROID) || defined(__ANDROID__)) + static void detach_from_java(void*) + { + JVM.load()->DetachCurrentThread(); + } +#endif + + static void* thread_start(void *arg) + { +#if (defined(ANDROID) || defined(__ANDROID__)) + // Calling get_jvm_env() here forces the thread to be attached. + get_jvm_env(); + pthread_cleanup_push(detach_from_java, nullptr); +#endif + threadpool* _this = reinterpret_cast(arg); + try + { + _this->m_service.run(); + } + catch (const _cancel_thread&) + { + // thread was cancelled + } + catch (...) + { + // Something bad happened +#if (defined(ANDROID) || defined(__ANDROID__)) + // Reach into the depths of the 'droid! + // NOTE: Uses internals of the bionic library + // Written against android ndk r9d, 7/26/2014 + __pthread_cleanup_pop(&__cleanup, true); + throw; +#endif + } +#if (defined(ANDROID) || defined(__ANDROID__)) + pthread_cleanup_pop(true); +#endif + return arg; + } + + std::vector m_threads; + boost::asio::io_service m_service; + boost::asio::io_service::work m_work; +}; + +} diff --git a/3rdparty/cpprestsdk/libs/websocketpp/.gitattributes b/3rdparty/cpprestsdk/libs/websocketpp/.gitattributes new file mode 100644 index 00000000..a9e4fc78 --- /dev/null +++ b/3rdparty/cpprestsdk/libs/websocketpp/.gitattributes @@ -0,0 +1,18 @@ +# Lineendings +*.sln eol=crlf +*.vcproj eol=crlf +*.vcxproj* eol=crlf + +# Whitespace rules +# strict (no trailing, no tabs) +*.cpp whitespace=trailing-space,space-before-tab,tab-in-indent,cr-at-eol +*.hpp whitespace=trailing-space,space-before-tab,tab-in-indent,cr-at-eol +*.c whitespace=trailing-space,space-before-tab,tab-in-indent,cr-at-eol +*.h whitespace=trailing-space,space-before-tab,tab-in-indent,cr-at-eol + +# normal (no trailing) +*.sql whitespace=trailing-space,space-before-tab,cr-at-eol +*.txt whitespace=trailing-space,space-before-tab,cr-at-eol + +# special files which must ignore whitespace +*.patch whitespace=-trailing-space diff --git a/3rdparty/cpprestsdk/libs/websocketpp/.gitignore b/3rdparty/cpprestsdk/libs/websocketpp/.gitignore new file mode 100644 index 00000000..558a1b3d --- /dev/null +++ b/3rdparty/cpprestsdk/libs/websocketpp/.gitignore @@ -0,0 +1,90 @@ +# make .git* files visible to git +!.gitignore +!.gitattributes + +.DS_Store + +#vim stuff +*~ +*.swp + +*.o +*.so +*.so.? +*.so.?.?.? +*.a +*.dylib +lib/* + +# CMake +*.cmake +*.dir +CMakeFiles +INSTALL.* +ZERO_CHECK.* +CMakeCache.txt +install_manifest.txt + +# Windows/Visual Studio +*.vcproj* +*.sln +*.suo +*.ncb +*/Debug/* +*/*/Debug/* +bin/Debug +*/Release/* +*/*/Release/* +*/RelWithDebInfo/* +*/*/RelWithDebInfo/* + +# explicitly allow this path with /debug/ in it +!websocketpp/transport/debug/* + +objs_shared/ +objs_static/ + +examples/chat_server/chat_server +examples/echo_server/echo_server +examples/chat_client/chat_client +examples/echo_client/echo_client +test/basic/tests +libwebsocketpp.dylib.0.1.0 + +websocketpp.xcodeproj/xcuserdata/* +websocketpp.xcodeproj/project.xcworkspace/xcuserdata/* +policy_based_notes.hpp + +examples/echo_server_tls/echo_server_tls + +examples/fuzzing_client/fuzzing_client + +examples/stress_client/stress_client + +examples/broadcast_server_tls/broadcast_server + +test/basic/perf + +examples/echo_server_tls/echo_server_tls + +examples/concurrent_server/concurrent_server + +examples/fuzzing_server_tls/fuzzing_server + +examples/wsperf/wsperf + +.sconsign.dblite + +build/ +doxygen/ +examples/wsperf/wsperf_client + +*.out + +*.log +*.opensdf +*.sdf +*.vcxproj +*.vcxproj.filters +*.user +install diff --git a/3rdparty/cpprestsdk/libs/websocketpp/.travis.yml b/3rdparty/cpprestsdk/libs/websocketpp/.travis.yml new file mode 100644 index 00000000..027ac560 --- /dev/null +++ b/3rdparty/cpprestsdk/libs/websocketpp/.travis.yml @@ -0,0 +1,23 @@ +language: cpp +compiler: + - gcc +before_install: + - sudo apt-get install libboost-regex1.48-dev libboost-system1.48-dev libboost-thread1.48-dev libboost-test1.48-dev libboost-random1.48-dev -y +env: + global: + - BOOST_INCLUDES=/usr/include + - BOOST_LIBS=/usr/lib +script: scons -j 2 && scons test +branches: + only: + - master + - permessage-deflate + - experimental + - 0.3.x-cmake + - develop +notifications: + recipients: + - travis@zaphoyd.com + email: + on_success: change + on_failure: always diff --git a/3rdparty/cpprestsdk/libs/websocketpp/CMakeLists.txt b/3rdparty/cpprestsdk/libs/websocketpp/CMakeLists.txt new file mode 100644 index 00000000..82791560 --- /dev/null +++ b/3rdparty/cpprestsdk/libs/websocketpp/CMakeLists.txt @@ -0,0 +1,244 @@ + +############ Setup project and cmake + +# Project name +project (websocketpp) + +# Minimum cmake requirement. We should require a quite recent +# cmake for the dependency find macros etc. to be up to date. +cmake_minimum_required (VERSION 2.6) + +set (WEBSOCKETPP_MAJOR_VERSION 0) +set (WEBSOCKETPP_MINOR_VERSION 5) +set (WEBSOCKETPP_PATCH_VERSION 1) +set (WEBSOCKETPP_VERSION ${WEBSOCKETPP_MAJOR_VERSION}.${WEBSOCKETPP_MINOR_VERSION}.${WEBSOCKETPP_PATCH_VERSION}) + +set(INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files") +if (WIN32 AND NOT CYGWIN) + set (DEF_INSTALL_CMAKE_DIR cmake) +else () + set (DEF_INSTALL_CMAKE_DIR lib/cmake/websocketpp) +endif () +set (INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files") + +# Make relative paths absolute (needed later on) +foreach (p INCLUDE CMAKE) + set (var INSTALL_${p}_DIR) + if (NOT IS_ABSOLUTE "${${var}}") + set (${var} "${CMAKE_INSTALL_PREFIX}/${${var}}") + endif () +endforeach () + +# Set CMake library search policy +if (COMMAND cmake_policy) + cmake_policy (SET CMP0003 NEW) + cmake_policy (SET CMP0005 NEW) +endif () + +# Disable unnecessary build types +set (CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo;Debug" CACHE STRING "Configurations" FORCE) + +# Include our cmake macros +set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +include (CMakeHelpers) + +############ Paths + +set (WEBSOCKETPP_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) +set (WEBSOCKETPP_INCLUDE ${WEBSOCKETPP_ROOT}/websocketpp) +set (WEBSOCKETPP_BUILD_ROOT ${CMAKE_CURRENT_BINARY_DIR}) +set (WEBSOCKETPP_BIN ${WEBSOCKETPP_BUILD_ROOT}/bin) +set (WEBSOCKETPP_LIB ${WEBSOCKETPP_BUILD_ROOT}/lib) + +# CMake install step prefix. I assume linux users want the prefix to +# be the default /usr or /usr/local so this is only adjusted on Windows. +# - Windows: Build the INSTALL project in your solution file. +# - Linux/OSX: make install. +if (MSVC) + set (CMAKE_INSTALL_PREFIX "${WEBSOCKETPP_ROOT}/install") +endif () + +############ Build customization + +# Override from command line "CMake -D