diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..bab911a79f --- /dev/null +++ b/.clang-format @@ -0,0 +1,46 @@ +# https://releases.llvm.org/7.0.0/tools/clang/docs/ClangFormatStyleOptions.html + +--- +Language: Cpp + +BasedOnStyle: WebKit + +AlignAfterOpenBracket: Align +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Allman +BreakConstructorInitializersBeforeComma: true +ColumnLimit: 120 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +Cpp11BracedListStyle: true +FixNamespaceComments: true +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^["<](stdafx|pch)\.h[">]$' + Priority: -1 + - Regex: '^$' + Priority: 3 + - Regex: '^<(WinIoCtl|winhttp|Shellapi)\.h>$' + Priority: 4 + - Regex: '.*' + Priority: 2 +IndentCaseLabels: true +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +PenaltyReturnTypeOnItsOwnLine: 1000 +PointerAlignment: Left +SpaceAfterTemplateKeyword: false +Standard: Cpp11 +UseTab: Never diff --git a/.gitignore b/.gitignore index 49f694a391..3a3fea4411 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -#Visual Studio files +# Visual Studio files *.o *.d *.so @@ -38,7 +38,7 @@ *~ ipch/ obj/ -#OSX files +# OSX files *.xccheckout *.pbxuser *.mode1v3 @@ -67,7 +67,11 @@ Intermediate/ # Ignore cmake building directories build.*/ docs/ -# ignore NuGet artifacts +# Ignore NuGet artifacts .nuget/ - -Generated Files/ \ No newline at end of file +Build_android/build/ +Generated Files/ +# Ignore iOS temp build directories +Build_iOS/Apple-Boost-BuildScript + +/out/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..f9d0b58a34 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,7 @@ +[submodule "vcpkg"] + path = vcpkg + url = https://github.com/Microsoft/vcpkg +[submodule "websocketpp"] + path = Release/libs/websocketpp + url = https://github.com/zaphoyd/websocketpp + fetchRecurseSubmodules = false diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..655ea37edf --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(Windows) Launch Debug Tests", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/build.debug/Release/Binaries/test_runner.exe", + "args": ["*testd.dll"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/build.debug/Release/Binaries", + "environment": [], + "externalConsole": true + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..9bad9cb7c7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,20 @@ +{ + "files.watcherExclude": { + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, + "**/node_modules/*/**": true, + "**/vcpkg/**": true, + "build.x86.debug": true, + "build.x86.release": true, + "build.x64.debug": true, + "build.x64.release": true, + "out": true, + }, + "cSpell.words": [ + "XPLATSTR", + "blittable", + "pplx", + "rdpos", + "rgpsz" + ] +} diff --git a/Build/Common.Build.Traversal.targets b/Build/Common.Build.Traversal.targets deleted file mode 100644 index db26ed7ada..0000000000 --- a/Build/Common.Build.Traversal.targets +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - - true - - - - - - - - - - - - - - - - diff --git a/Build/Common.Build.settings b/Build/Common.Build.settings deleted file mode 100644 index 16ba09aa54..0000000000 --- a/Build/Common.Build.settings +++ /dev/null @@ -1,242 +0,0 @@ - - - - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - - - Debug - Win32 - - - - - $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), build.root)) - $(BuildRoot)\Build - $(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion@CurrentVersion) - - 140 - 120 - 110 - - $(WindowsSdkDir) - true - true - false - - - - - - $(BuildRoot)\Binaries\$(Platform)\$(Configuration)\ - $(OutputPath) - $(BuildRoot)\Intermediate\$(MSBuildProjectName)\$(Platform)\$(Configuration)\ - $(BuildRoot)\Release\Tests - $(BuildRoot)\Release\src - $(BuildRoot)\Release\Resource - $(BuildRoot)\Release\include - $(BuildRoot)\Release\libs\websocketpp - $(BuildRoot)\packages - $(BuildRoot)\..\Tools\packages - - - - - $(OutDir)\ - - - - $(VS110COMNTOOLS)..\IDE - - - - $(VS120COMNTOOLS)..\IDE - - - - $(TargetsPath)\BinaryDependencies - prompt - 4 - - - false - - - false - $(RunCodeAnalysis) - true - - true - - - true - false - false - true - - - - - Level4 - Use - true - $(EnableCPPAnalysis) - true - /d2notypeopt %(AdditionalOptions) - - - /DTARGET_NAME="$(TARGETNAME)" %(AdditionalOptions) - - - - - - Disabled - _DEBUG;%(PreprocessorDefinitions) - - - true - - - - - - _WIN64;_DEBUG;%(PreprocessorDefinitions) - - - true - - - - - - _DEBUG;%(PreprocessorDefinitions) - - - true - - - - - - _WIN64;NDEBUG;%(PreprocessorDefinitions) - - - true - - - - - - NDEBUG;%(PreprocessorDefinitions) - - - true - - - - - - NDEBUG;%(PreprocessorDefinitions) - - - true - - - - - true - full - false - DEBUG;TRACE - x86 - - - - true - full - false - DEBUG;TRACE;X64 - - - - pdbonly - true - TRACE - x86 - - - - pdbonly - true - TRACE;X64 - - - - true - full - false - DEBUG;TRACE;ARM - - - - pdbonly - true - TRACE;ARM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_ResolvedProjectReferencePaths> - Undefined - - - - - - - - <_ResolvedProjectReferencePaths Condition="'%(CopyToOutputDirectory)' != 'Undefined'" - Remove="@(_ResolvedProjectReferencePaths)" /> - - - - - - - diff --git a/Build/Config.Definitions.props b/Build/Config.Definitions.props deleted file mode 100644 index 9eaabbb298..0000000000 --- a/Build/Config.Definitions.props +++ /dev/null @@ -1,29 +0,0 @@ - - - - - Debug - ARM - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM - - - Release - Win32 - - - Release - x64 - - - \ No newline at end of file diff --git a/Build/Release.Product.settings b/Build/Release.Product.settings deleted file mode 100644 index 30d0bc9a4f..0000000000 --- a/Build/Release.Product.settings +++ /dev/null @@ -1,64 +0,0 @@ - - - - - true - false - - - - false - true - - - - Unicode - - - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - - false - - $(RunCodeAnalysis) - - - - - - $(CasablancaIncludeDir) - - - - - false - - - Windows - false - true - /SAFESEH%(AdditionalOptions) - - - - - - Disabled - - - - - - NDEBUG;%(PreprocessorDefinitions) - MaxSpeed - true - true - - - - diff --git a/Build/Release.Tests.settings b/Build/Release.Tests.settings deleted file mode 100644 index 2bbff91192..0000000000 --- a/Build/Release.Tests.settings +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - false - - - - - - - $(TestRoot)\Common\utilities\include;$(TestRoot)\Common\UnitTestpp - $(BinariesDirectory)\inc;$(CasablancaIncludeDir) - - - - - - - - diff --git a/Build/version.props b/Build/version.props deleted file mode 100644 index 81b45b0316..0000000000 --- a/Build/version.props +++ /dev/null @@ -1,11 +0,0 @@ - - - - cpprest - 2 - 10 - 2 - $(CppRestSDKVersionMajor)_$(CppRestSDKVersionMinor) - $(CppRestSDKVersionMajor).$(CppRestSDKVersionMinor) - - diff --git a/Build_android/boost-for-android-x86.patch b/Build_android/boost-for-android-x86.patch deleted file mode 100644 index 4e24b988e3..0000000000 --- a/Build_android/boost-for-android-x86.patch +++ /dev/null @@ -1,173 +0,0 @@ -diff --git a/build-android.sh b/build-android.sh -index 40453f7..5902fe2 100755 ---- a/build-android.sh -+++ b/build-android.sh -@@ -195,8 +195,12 @@ case "$HOST_OS" in - PlatformOS=linux - esac - -+NDK_SOURCE_PROPERTIES=$AndroidNDKRoot"/source.properties" - NDK_RELEASE_FILE=$AndroidNDKRoot"/RELEASE.TXT" --if [ -f "${NDK_RELEASE_FILE}" ]; then -+if [ -f "${NDK_SOURCE_PROPERTIES}" ]; then -+ version=$(grep -i '^Pkg.Revision =' $NDK_SOURCE_PROPERTIES | cut -f2- -d=) -+ NDK_RN=$(echo $version | awk -F. '{print $1}') -+elif [ -f "${NDK_RELEASE_FILE}" ]; then - NDK_RN=`cat $NDK_RELEASE_FILE | sed 's/^r\(.*\)$/\1/g'` - elif [ -n "${AndroidSourcesDetected}" ]; then - if [ -f "${ANDROID_BUILD_TOP}/ndk/docs/CHANGES.html" ]; then -@@ -258,10 +262,20 @@ case "$NDK_RN" in - TOOLSET=gcc-androidR8e - ;; - "10 (64-bit)") -- TOOLCHAIN=${TOOLCHAIN:-arm-linux-androideabi-4.6} -- CXXPATH=$AndroidNDKRoot/toolchains/${TOOLCHAIN}/prebuilt/${PlatformOS}-x86_64/bin/arm-linux-androideabi-g++ -- TOOLSET=gcc-androidR8e -+ TOOLCHAIN=llvm-3.4 -+ CXXPATH=$AndroidNDKRoot/toolchains/${TOOLCHAIN}/prebuilt/${PlatformOS}-x86_64/bin/clang++ -+ TOOLSET=clang-androidR8e -+ ;; -+ "10e-rc4 (64-bit)"|"10e (64-bit)") -+ TOOLCHAIN=llvm-3.6 -+ CXXPATH=$AndroidNDKRoot/toolchains/${TOOLCHAIN}/prebuilt/${PlatformOS}-x86_64/bin/clang++ -+ TOOLSET=clang-androidR8e - ;; -+ 11) -+ TOOLCHAIN=llvm -+ CXXPATH=$AndroidNDKRoot/toolchains/${TOOLCHAIN}/prebuilt/${PlatformOS}-x86_64/bin/clang++ -+ TOOLSET=clang-androidR8e -+ ;; - *) - echo "Undefined or not supported Android NDK version!" - exit 1 -@@ -391,6 +405,7 @@ echo "Building boost for android" - export AndroidBinariesPath=`dirname $CXXPATH` - export PATH=$AndroidBinariesPath:$PATH - export AndroidNDKRoot -+ export PlatformOS - export NO_BZIP2=1 - - cxxflags="" -@@ -405,7 +420,7 @@ echo "Building boost for android" - --layout=versioned \ - --prefix="./../$BUILD_DIR/" \ - $LIBRARIES \ -- install 2>&1 \ -+ release debug install 2>&1 \ - || { dump "ERROR: Failed to build boost for android!" ; exit 1 ; } - } | tee -a $PROGDIR/build.log - -diff --git a/configs/user-config-boost-1_55_0.jam b/configs/user-config-boost-1_55_0.jam -index 666d4c8..93aba68 100644 ---- a/configs/user-config-boost-1_55_0.jam -+++ b/configs/user-config-boost-1_55_0.jam -@@ -39,93 +39,47 @@ - - import os ; - local AndroidNDKRoot = [ os.environ AndroidNDKRoot ] ; -+local PlatformOS = [ os.environ PlatformOS ] ; - - # -------------------------------------------------------------------- --# Is same for 8b, 8c and 8d --using gcc : androidR8b -+using clang : androidR8e - : --arm-linux-androideabi-g++ -+$(AndroidNDKRoot)/toolchains/llvm/prebuilt/$(PlatformOS)-x86_64/bin/clang++ - : --arm-linux-androideabi-ar -+--gcc-toolchain="$(AndroidNDKRoot)/toolchains/x86-4.9/prebuilt/$(PlatformOS)-x86_64" - -fexceptions - -frtti - -fpic - -ffunction-sections - -funwind-tables ---D__ARM_ARCH_5__ ---D__ARM_ARCH_5T__ ---D__ARM_ARCH_5E__ ---D__ARM_ARCH_5TE__ ---Wno-psabi ---march=armv5te ---mtune=xscale ---msoft-float ---mthumb -+-fstack-protector -+-no-canonical-prefixes -+--target=i686-none-linux-android -+--sysroot="$(AndroidNDKRoot)/platforms/android-9/arch-x86" - -Os - -fomit-frame-pointer ---fno-strict-aliasing ---finline-limit=64 ---I$(AndroidNDKRoot)/platforms/android-9/arch-arm/usr/include -+-fstrict-aliasing - -Wa,--noexecstack - -DANDROID - -D__ANDROID__ - -DNDEBUG - -O2 - -g ---I$(AndroidNDKRoot)/sources/cxx-stl/gnu-libstdc++/4.6/include ---I$(AndroidNDKRoot)/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi/include -+-I$(AndroidNDKRoot)/sources/cxx-stl/gnu-libstdc++/4.9/include -+-I$(AndroidNDKRoot)/sources/cxx-stl/gnu-libstdc++/4.9/libs/x86/include -+--target=i686-none-linux-android -+--gcc-toolchain="$(AndroidNDKRoot)/toolchains/x86-4.9/prebuilt/$(PlatformOS)-x86_64" -+--sysroot=$(AndroidNDKRoot)/platforms/android-9/arch-x86 - # @Moss - Above are the 'oficial' android flags --arm -+i686 - -fvisibility=hidden - -fvisibility-inlines-hidden - -fdata-sections ---D__arm__ - -D_REENTRANT - -D_GLIBCXX__PTHREADS -+-std=c++11 - ; - --# -------------------------------------------------------------------- --using gcc : androidR8e --: --arm-linux-androideabi-g++ --: --arm-linux-androideabi-ar ---fexceptions ---frtti ---fpic ---ffunction-sections ---funwind-tables ---D__ARM_ARCH_5__ ---D__ARM_ARCH_5T__ ---D__ARM_ARCH_5E__ ---D__ARM_ARCH_5TE__ ---Wno-psabi ---march=armv5te ---mtune=xscale ---msoft-float ---mthumb ---Os ---fomit-frame-pointer ---fno-strict-aliasing ---finline-limit=64 ---I$(AndroidNDKRoot)/platforms/android-9/arch-arm/usr/include ---Wa,--noexecstack ---DANDROID ---D__ANDROID__ ---DNDEBUG ---O2 ---g ---I$(AndroidNDKRoot)/sources/cxx-stl/gnu-libstdc++/4.6/include ---I$(AndroidNDKRoot)/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi/include --# @Moss - Above are the 'oficial' android flags --arm ---fvisibility=hidden ---fvisibility-inlines-hidden ---fdata-sections ---D__arm__ ---D_REENTRANT ---D_GLIBCXX__PTHREADS --; - - - # ------------------ diff --git a/Build_android/boost-for-android.patch b/Build_android/boost-for-android.patch deleted file mode 100644 index 2b86e8f833..0000000000 --- a/Build_android/boost-for-android.patch +++ /dev/null @@ -1,175 +0,0 @@ -diff --git a/build-android.sh b/build-android.sh -index 40453f7..5902fe2 100755 ---- a/build-android.sh -+++ b/build-android.sh -@@ -195,8 +195,12 @@ case "$HOST_OS" in - PlatformOS=linux - esac - -+NDK_SOURCE_PROPERTIES=$AndroidNDKRoot"/source.properties" - NDK_RELEASE_FILE=$AndroidNDKRoot"/RELEASE.TXT" --if [ -f "${NDK_RELEASE_FILE}" ]; then -+if [ -f "${NDK_SOURCE_PROPERTIES}" ]; then -+ version=$(grep -i '^Pkg.Revision =' $NDK_SOURCE_PROPERTIES | cut -f2- -d=) -+ NDK_RN=$(echo $version | awk -F. '{print $1}') -+elif [ -f "${NDK_RELEASE_FILE}" ]; then - NDK_RN=`cat $NDK_RELEASE_FILE | sed 's/^r\(.*\)$/\1/g'` - elif [ -n "${AndroidSourcesDetected}" ]; then - if [ -f "${ANDROID_BUILD_TOP}/ndk/docs/CHANGES.html" ]; then -@@ -258,10 +262,20 @@ case "$NDK_RN" in - TOOLSET=gcc-androidR8e - ;; - "10 (64-bit)") -- TOOLCHAIN=${TOOLCHAIN:-arm-linux-androideabi-4.6} -- CXXPATH=$AndroidNDKRoot/toolchains/${TOOLCHAIN}/prebuilt/${PlatformOS}-x86_64/bin/arm-linux-androideabi-g++ -- TOOLSET=gcc-androidR8e -+ TOOLCHAIN=llvm-3.4 -+ CXXPATH=$AndroidNDKRoot/toolchains/${TOOLCHAIN}/prebuilt/${PlatformOS}-x86_64/bin/clang++ -+ TOOLSET=clang-androidR8e -+ ;; -+ "10e-rc4 (64-bit)"|"10e (64-bit)") -+ TOOLCHAIN=llvm-3.6 -+ CXXPATH=$AndroidNDKRoot/toolchains/${TOOLCHAIN}/prebuilt/${PlatformOS}-x86_64/bin/clang++ -+ TOOLSET=clang-androidR8e - ;; -+ 11) -+ TOOLCHAIN=llvm -+ CXXPATH=$AndroidNDKRoot/toolchains/${TOOLCHAIN}/prebuilt/${PlatformOS}-x86_64/bin/clang++ -+ TOOLSET=clang-androidR8e -+ ;; - *) - echo "Undefined or not supported Android NDK version!" - exit 1 -@@ -391,6 +405,7 @@ echo "Building boost for android" - export AndroidBinariesPath=`dirname $CXXPATH` - export PATH=$AndroidBinariesPath:$PATH - export AndroidNDKRoot -+ export PlatformOS - export NO_BZIP2=1 - - cxxflags="" -@@ -405,7 +420,7 @@ echo "Building boost for android" - --layout=versioned \ - --prefix="./../$BUILD_DIR/" \ - $LIBRARIES \ -- install 2>&1 \ -+ release debug install 2>&1 \ - || { dump "ERROR: Failed to build boost for android!" ; exit 1 ; } - } | tee -a $PROGDIR/build.log - -diff --git a/configs/user-config-boost-1_55_0.jam b/configs/user-config-boost-1_55_0.jam -index 666d4c8..cdab118 100644 ---- a/configs/user-config-boost-1_55_0.jam -+++ b/configs/user-config-boost-1_55_0.jam -@@ -39,84 +39,44 @@ - - import os ; - local AndroidNDKRoot = [ os.environ AndroidNDKRoot ] ; -+local PlatformOS = [ os.environ PlatformOS ] ; - - # -------------------------------------------------------------------- --# Is same for 8b, 8c and 8d --using gcc : androidR8b -+using clang : androidR8e - : --arm-linux-androideabi-g++ -+$(AndroidNDKRoot)/toolchains/llvm/prebuilt/$(PlatformOS)-x86_64/bin/clang++ - : --arm-linux-androideabi-ar -+--gcc-toolchain="$(AndroidNDKRoot)/toolchains/arm-linux-androideabi-4.9/prebuilt/$(PlatformOS)-x86_64" - -fexceptions - -frtti - -fpic - -ffunction-sections - -funwind-tables ---D__ARM_ARCH_5__ ---D__ARM_ARCH_5T__ ---D__ARM_ARCH_5E__ ---D__ARM_ARCH_5TE__ ---Wno-psabi ---march=armv5te ---mtune=xscale ---msoft-float -+-no-canonical-prefixes -+--target=armv7-none-linux-androideabi -+-march=armv7-a -+-mfloat-abi=softfp -+-mfpu=vfpv3-d16 - -mthumb ---Os ---fomit-frame-pointer ---fno-strict-aliasing ---finline-limit=64 ---I$(AndroidNDKRoot)/platforms/android-9/arch-arm/usr/include ---Wa,--noexecstack ---DANDROID ---D__ANDROID__ ---DNDEBUG ---O2 ---g ---I$(AndroidNDKRoot)/sources/cxx-stl/gnu-libstdc++/4.6/include ---I$(AndroidNDKRoot)/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi/include --# @Moss - Above are the 'oficial' android flags --arm ---fvisibility=hidden ---fvisibility-inlines-hidden ---fdata-sections ---D__arm__ ---D_REENTRANT ---D_GLIBCXX__PTHREADS --; -- --# -------------------------------------------------------------------- --using gcc : androidR8e --: --arm-linux-androideabi-g++ --: --arm-linux-androideabi-ar ---fexceptions ---frtti ---fpic ---ffunction-sections ---funwind-tables -+--sysroot="$(AndroidNDKRoot)/platforms/android-9/arch-arm" - -D__ARM_ARCH_5__ - -D__ARM_ARCH_5T__ - -D__ARM_ARCH_5E__ - -D__ARM_ARCH_5TE__ ---Wno-psabi ---march=armv5te ---mtune=xscale ---msoft-float ---mthumb - -Os - -fomit-frame-pointer - -fno-strict-aliasing ---finline-limit=64 ---I$(AndroidNDKRoot)/platforms/android-9/arch-arm/usr/include - -Wa,--noexecstack - -DANDROID - -D__ANDROID__ - -DNDEBUG - -O2 - -g ---I$(AndroidNDKRoot)/sources/cxx-stl/gnu-libstdc++/4.6/include ---I$(AndroidNDKRoot)/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi/include -+-I$(AndroidNDKRoot)/sources/cxx-stl/gnu-libstdc++/4.9/include -+-I$(AndroidNDKRoot)/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi/include -+--target=armv7-none-linux-androideabi -+--gcc-toolchain="$(AndroidNDKRoot)/toolchains/arm-linux-androideabi-4.9/prebuilt/$(PlatformOS)-x86_64" -+--sysroot=$(AndroidNDKRoot)/platforms/android-9/arch-arm - # @Moss - Above are the 'oficial' android flags - arm - -fvisibility=hidden -@@ -125,9 +85,11 @@ arm-linux-androideabi-g++ - -D__arm__ - -D_REENTRANT - -D_GLIBCXX__PTHREADS -+-std=c++11 - ; - - -+ - # ------------------ - # GCC configuration. - # ------------------ diff --git a/Build_android/configure.sh b/Build_android/configure.sh index fd17fb7007..5088f44d8f 100755 --- a/Build_android/configure.sh +++ b/Build_android/configure.sh @@ -1,7 +1,7 @@ #!/bin/bash # Copyright (C) Microsoft. All rights reserved. # Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -# =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +# =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ # # configure.sh # @@ -9,13 +9,12 @@ # # For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk # -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- set -e -# Note: we require android ndk r10e available from -# http://dl.google.com/android/ndk/android-ndk-r10e-linux-x86_64.tar.bz2 -# http://dl.google.com/android/ndk/android-ndk-r10e-windows-x86_64.zip +# The Android NDK r10e or later may work, but we test with r18b. To download, see the following link: +# https://developer.android.com/ndk/downloads/index.html # ----------------- # Parse args @@ -23,48 +22,78 @@ set -e DO_BOOST=1 DO_OPENSSL=1 +DO_CMAKE=1 DO_CPPRESTSDK=1 +BOOSTVER=1.69.0 +OPENSSLVER=1.1.0j +CMAKEVER=3.14.0 + +API=15 +STL=c++_shared + function usage { echo "Usage: $0 [--skip-boost] [--skip-openssl] [--skip-cpprestsdk] [-h] [--ndk ]" echo "" echo " --skip-boost Skip fetching and compiling boost" echo " --skip-openssl Skip fetching and compiling openssl" echo " --skip-cpprestsdk Skip compiling cpprestsdk" - echo " -h,--help,-? Display this information" + echo " --boost Override the Boost version to build (default is ${BOOSTVER})" + echo " --openssl Override the OpenSSL version to build (default is ${OPENSSLVER})" echo " --ndk If specified, overrides the ANDROID_NDK environment variable" + echo " -h,--help,-? Display this information" } while [[ $# > 0 ]] do case $1 in - "--skip-boost") - DO_BOOST=0 - ;; - "--skip-openssl") - DO_OPENSSL=0 - ;; - "--skip-cpprestsdk") - DO_CPPRESTSDK=0 - ;; - "-?"|"-h"|"--help") - usage - exit - ;; - "--ndk") - shift - export ANDROID_NDK=$1 - ;; - *) - usage - exit 1 - ;; + "--skip-boost") + DO_BOOST=0 + ;; + "--skip-openssl") + DO_OPENSSL=0 + ;; + "--skip-cmake") + DO_CMAKE=0 + ;; + "--skip-cpprestsdk") + DO_CPPRESTSDK=0 + ;; + "--boost") + shift + DO_BOOST=1 + BOOSTVER=$1 + ;; + "--cmake") + shift + DO_CMAKE=1 + CMAKEVER=$1 + ;; + "--openssl") + shift + DO_OPENSSL=1 + OPENSSLVER=$1 + ;; + "--ndk") + shift + export ANDROID_NDK=$1 + ;; + "-?"|"-h"|"--help") + usage + exit + ;; + *) + usage + exit 1 + ;; esac shift done # Variables setup +unset BOOST_ROOT + if [ ! -e "${ANDROID_NDK}/ndk-build" ] then echo "ANDROID_NDK does not point to a valid NDK." @@ -74,6 +103,13 @@ fi NDK_DIR=`cd "${ANDROID_NDK}" && pwd` SRC_DIR=`pwd` +if [ -z "$NCPU" ]; then + NCPU=4 + if uname -s | grep -i "linux" > /dev/null ; then + NCPU=`cat /proc/cpuinfo | grep -c -i processor` + fi +fi + # ----------------------- # Identify the script dir # ----------------------- @@ -102,140 +138,70 @@ fi # This steps are based on the official openssl build instructions # http://wiki.openssl.org/index.php/Android -if [ "${DO_OPENSSL}" == "1" ] -then -( +if [ "${DO_OPENSSL}" == "1" ]; then ( if [ ! -d "openssl" ]; then mkdir openssl; fi cd openssl - cp "${DIR}/openssl/Makefile" . - export ANDROID_NDK_ROOT="${NDK_DIR}" - make all -) -fi - + cp -af "${DIR}/openssl/." . + make all ANDROID_NDK="${NDK_DIR}" ANDROID_TOOLCHAIN=clang ANDROID_ABI=armeabi-v7a OPENSSL_PREFIX=armeabi-v7a OPENSSL_VERSION=$OPENSSLVER -j $NCPU + make all ANDROID_NDK="${NDK_DIR}" ANDROID_TOOLCHAIN=clang ANDROID_ABI=x86 OPENSSL_PREFIX=x86 OPENSSL_VERSION=$OPENSSLVER -j $NCPU +) fi # ----- # Boost # ----- -# Uses the script from MysticTreeGames - -if [ "${DO_BOOST}" == "1" ] -then -( - ( - if [ ! -d "Boost-for-Android" ] - then - git clone https://github.com/MysticTreeGames/Boost-for-Android.git - fi - cd Boost-for-Android - if [ ! -e "cpprestsdk.patched.stamp" ] - then - git checkout 1c95d349d5f92c5ac1c24e0ec6985272a3e3883c - git reset --hard HEAD - git apply "$DIR/boost-for-android.patch" - touch cpprestsdk.patched.stamp - fi - - PATH="$PATH:$NDK_DIR" ./build-android.sh --boost=1.55.0 --with-libraries=random,date_time,filesystem,system,thread,chrono "${NDK_DIR}" || exit 1 - ) - - ( - if [ ! -d "Boost-for-Android-x86" ] - then - git clone Boost-for-Android Boost-for-Android-x86 - fi - cd Boost-for-Android-x86 - if [ ! -e "cpprestsdk.patched.stamp" ] - then - git checkout 1c95d349d5f92c5ac1c24e0ec6985272a3e3883c - git reset --hard HEAD - git apply "$DIR/boost-for-android-x86.patch" - ln -s ../Boost-for-Android/boost_1_55_0.tar.bz2 . - touch cpprestsdk.patched.stamp - fi - - PATH="$PATH:$NDK_DIR" ./build-android.sh --boost=1.55.0 --with-libraries=atomic,random,date_time,filesystem,system,thread,chrono "${NDK_DIR}" || exit 1 - ) -) -fi - -if [ "${DO_CPPRESTSDK}" == "1" ] -then -( -# ------------- -# android-cmake -# ------------- - if [ ! -e android-cmake ] - then - git clone https://github.com/taka-no-me/android-cmake.git - fi +# Uses the build script from Moritz Wundke (formerly MysticTreeGames) +# https://github.com/moritz-wundke/Boost-for-Android + +if [ "${DO_BOOST}" == "1" ]; then ( + if [ ! -d 'Boost-for-Android' ]; then git clone https://github.com/moritz-wundke/Boost-for-Android; fi + cd Boost-for-Android + git checkout aed656a97fb3af7322fd2c3da5995a2d09d87d4b + PATH="$PATH:$NDK_DIR" \ + CXXFLAGS="-std=gnu++11" \ + ./build-android.sh \ + --boost=$BOOSTVER \ + --arch=armeabi-v7a,x86 \ + --with-libraries=atomic,random,date_time,filesystem,system,thread,chrono \ + "${NDK_DIR}" || exit 1 +) fi + +# ------ +# CMake +# ------ +# We update CMake because the version included with Ubuntu is too old to handle Boost 1.69. + +if [ "${DO_CMAKE}" == "1" ]; then ( + if [ ! -d "cmake-${CMAKEVER}" ]; then wget https://github.com/Kitware/CMake/releases/download/v${CMAKEVER}/cmake-${CMAKEVER}-Linux-x86_64.sh; fi + chmod +x cmake-${CMAKEVER}-Linux-x86_64.sh + rm -rf cmake-${CMAKEVER} + mkdir cmake-${CMAKEVER} + cd cmake-${CMAKEVER} + ../cmake-${CMAKEVER}-Linux-x86_64.sh --skip-license +) fi # ---------- # casablanca # ---------- - ( - mkdir -p build.armv7.debug - cd build.armv7.debug - cmake "$DIR/../Release/" \ - -DCMAKE_TOOLCHAIN_FILE=../android-cmake/android.toolchain.cmake \ - -DANDROID_ABI=armeabi-v7a \ - -DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang3.8 \ - -DANDROID_STL=none \ - -DANDROID_STL_FORCE_FEATURES=ON \ - -DANDROID_NATIVE_API_LEVEL=android-9 \ - -DANDROID_GOLD_LINKER=OFF \ - -DCMAKE_BUILD_TYPE=Debug \ - -DANDROID_NDK="${ANDROID_NDK}" - make -j 1 - ) - - ( - mkdir -p build.armv7.release - cd build.armv7.release - cmake "$DIR/../Release/" \ - -DCMAKE_TOOLCHAIN_FILE=../android-cmake/android.toolchain.cmake \ - -DANDROID_ABI=armeabi-v7a \ - -DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang3.8 \ - -DANDROID_STL=none \ - -DANDROID_STL_FORCE_FEATURES=ON \ - -DANDROID_NDK="${ANDROID_NDK}" \ - -DANDROID_NATIVE_API_LEVEL=android-9 \ - -DANDROID_GOLD_LINKER=OFF \ - -DCMAKE_BUILD_TYPE=Release - make -j 1 - ) - - ( - mkdir -p build.x86.debug - cd build.x86.debug - cmake "$DIR/../Release/" \ - -DCMAKE_TOOLCHAIN_FILE=../android-cmake/android.toolchain.cmake \ - -DANDROID_ABI=x86 \ - -DANDROID_TOOLCHAIN_NAME=x86-clang3.8 \ - -DANDROID_STL=none \ - -DANDROID_STL_FORCE_FEATURES=ON \ - -DANDROID_NATIVE_API_LEVEL=android-9 \ - -DANDROID_GOLD_LINKER=OFF \ - -DCMAKE_BUILD_TYPE=Debug \ - -DANDROID_NDK="${ANDROID_NDK}" - make -j 1 - ) - - ( - mkdir -p build.x86.release - cd build.x86.release - cmake "$DIR/../Release/" \ - -DCMAKE_TOOLCHAIN_FILE=../android-cmake/android.toolchain.cmake \ - -DANDROID_ABI=x86 \ - -DANDROID_TOOLCHAIN_NAME=x86-clang3.8 \ - -DANDROID_STL=none \ - -DANDROID_STL_FORCE_FEATURES=ON \ - -DANDROID_NDK="${ANDROID_NDK}" \ - -DANDROID_NATIVE_API_LEVEL=android-9 \ - -DANDROID_GOLD_LINKER=OFF \ - -DCMAKE_BUILD_TYPE=Release - make -j 1 - ) -) +if [ "${DO_CPPRESTSDK}" == "1" ]; then + # Use the builtin CMake toolchain configuration that comes with the NDK + function build_cpprestsdk { ( + rm -rf $1 + ./cmake-${CMAKEVER}/bin/cmake \ + -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK}/build/cmake/android.toolchain.cmake" \ + -DANDROID_NDK="${ANDROID_NDK}" \ + -DANDROID_TOOLCHAIN=clang \ + -DANDROID_ABI=$2 \ + -DBOOST_VERSION="${BOOSTVER}" \ + -DCMAKE_BUILD_TYPE=$3 \ + -S "${DIR}/.." \ + -B $1 + make -j $NCPU -C $1 + ) } + + # Build the cpprestsdk for each target configuration + build_cpprestsdk build.armv7.debug armeabi-v7a Debug + build_cpprestsdk build.armv7.release armeabi-v7a Release + build_cpprestsdk build.x86.debug x86 Debug + build_cpprestsdk build.x86.release x86 Release fi diff --git a/Build_android/openssl/Makefile b/Build_android/openssl/Makefile index 71a992164d..2da8fa48c9 100644 --- a/Build_android/openssl/Makefile +++ b/Build_android/openssl/Makefile @@ -1,60 +1,176 @@ -SHELL := /bin/bash -OPENSSL_VER = openssl-1.0.2k +# Configuration parameters +ANDROID_API = 18 +ANDROID_ABI = armeabi-v7a +ANDROID_HOST = linux-x86_64 +ANDROID_TOOLCHAIN = gcc +ANDROID_GCC_VERSION = 4.8 +OPENSSL_VERSION = 1.0.2k +OPENSSL_PACKAGE = openssl-$(OPENSSL_VERSION) +OPENSSL_PATCH = $(OPENSSL_PACKAGE).patch +OPENSSL_TARBALL = $(OPENSSL_PACKAGE).tar.gz +OPENSSL_URL = https://www.openssl.org/source/$(OPENSSL_TARBALL) +OPENSSL_OPTIONS = -no-ssl2 -no-ssl3 -no-comp -no-hw -no-engine +OPENSSL_PREFIX = android-$(ANDROID_API)-$(ANDROID_ABI)-$(ANDROID_TOOLCHAIN) +OPENSSL_SOURCE = $(OPENSSL_PACKAGE)-$(ANDROID_ABI) -all: armeabi-v7a/lib/libssl.a x86/lib/libssl.a +# Setup target parameters from ABI +ifneq ($(findstring armeabi,$(ANDROID_ABI)),) +ANDROID_ARCH := arm +ANDROID_TRIPLE := arm-linux-androideabi +ANDROID_TOOLARCH := $(ANDROID_TRIPLE) +OPENSSL_MACHINE := armv7 +OPENSSL_SYSTEM := android +OPENSSL_TARGET := android-armeabi +endif -clean: - rm -rf $(OPENSSL_VER) - rm -rf $(OPENSSL_VER)-armeabi-v7a - rm -rf armeabi-v7a - rm -rf setenv-android-x86.sh - -setenv-android.sh: - wget https://wiki.openssl.org/images/7/70/Setenv-android.sh - mv Setenv-android.sh setenv-android.sh - chmod a+x setenv-android.sh - -setenv-android-x86.sh: setenv-android.sh - cp setenv-android.sh setenv-android-x86.sh.tmp - sed -i -e 's/_ANDROID_EABI="arm-linux-androideabi-4.8"/_ANDROID_EABI="x86-4.8"/g' setenv-android-x86.sh.tmp - sed -i -e 's/_ANDROID_ARCH=arch-arm/_ANDROID_ARCH=arch-x86/g' setenv-android-x86.sh.tmp - mv setenv-android-x86.sh.tmp setenv-android-x86.sh - -$(OPENSSL_VER).tar.gz: - wget https://www.openssl.org/source/$(OPENSSL_VER).tar.gz - -armeabi-v7a/lib/libssl.a: setenv-android.sh $(OPENSSL_VER).tar.gz - [ -d "$(ANDROID_NDK_ROOT)" ] - export ANDROID_NDK_ROOT="$(ANDROID_NDK_ROOT)"; \ - . ./setenv-android.sh; \ +ifneq ($(findstring aarch64,$(ANDROID_ABI)),) +ANDROID_ARCH := arm64 +ANDROID_TRIPLE := aarch64-linux-android +ANDROID_TOOLARCH := $(ANDROID_TRIPLE) +OPENSSL_MACHINE := aarch64 +OPENSSL_SYSTEM := android64 +OPENSSL_TARGET := android64-aarch64 +endif + +ifneq ($(findstring x86,$(ANDROID_ABI)),) +ANDROID_ARCH := x86 +ANDROID_TRIPLE := i686-linux-android +ANDROID_TOOLARCH := $(ANDROID_ARCH) +OPENSSL_MACHINE := i686 +OPENSSL_SYSTEM := android +OPENSSL_TARGET := android-x86 +endif + +# Validate Android NDK directory paths and use fallback directories where applicable +define direxists = +$(if $(wildcard $(1)),$(strip $(1)),"") +endef + +define findfirstdir = +$(call direxists,$(firstword $(filter-out "",$(foreach val,$(3),$(call direxists,$(subst $(2),$(strip $(val)),$(1))))))) +endef + +ifeq ($(call direxists,$(ANDROID_NDK)),"") +$(error invalid Android NDK root directory) +endif + +ANDROID_LINK_SYSROOT := $(ANDROID_NDK)/platforms/android-$(ANDROID_API)/arch-$(ANDROID_ARCH) +ifeq ($(call direxists,$(ANDROID_LINK_SYSROOT)),"") +$(error invalid Android ABI or API level, could not locate Android NDK sysroot directory) +endif + +ANDROID_SYSROOT := $(ANDROID_NDK)/sysroot +ifeq ($(call direxists,$(ANDROID_SYSROOT)),"") +ANDROID_SYSROOT := $(ANDROID_LINK_SYSROOT) +endif + +ANDROID_GCC_VERSIONS := $(ANDROID_GCC_VERSION) 4.9 4.8 +ANDROID_GCC_PREBUILT_template := $(ANDROID_NDK)/toolchains/$(ANDROID_TOOLARCH)-<>/prebuilt +ANDROID_GCC_PREBUILT := $(call findfirstdir,$(ANDROID_GCC_PREBUILT_template),<>,$(ANDROID_GCC_VERSIONS)) +ifeq ($(ANDROID_GCC_PREBUILT),"") +$(error could not determine Android NDK GCC toolchain prebuilt directory) +endif + +ANDROID_HOSTS := $(ANDROID_HOST) linux-x86_64 linux-x86 darwin-x86_64 darwin-x86 +ANDROID_GCC_TOOLCHAIN_template := $(ANDROID_GCC_PREBUILT)/<> +ANDROID_GCC_TOOLCHAIN := $(call findfirstdir,$(ANDROID_GCC_TOOLCHAIN_template),<>,$(ANDROID_HOSTS)) +ifeq ($(ANDROID_GCC_TOOLCHAIN),"") +$(error could not determine Android NDK GCC toolchain host directory) +endif + +ANDROID_LLVM_VERSIONS := llvm llvm-3.6 llvm-3.5 llvm-3.4 +ANDROID_LLVM_TOOLCHAIN_template := $(ANDROID_NDK)/toolchains/<>/prebuilt/$(notdir $(ANDROID_GCC_TOOLCHAIN)) +ANDROID_LLVM_TOOLCHAIN := $(call findfirstdir,$(ANDROID_LLVM_TOOLCHAIN_template),<>,$(ANDROID_LLVM_VERSIONS)) +ifeq ($(ANDROID_LLVM_TOOLCHAIN),"") +$(error could not determine Android NDK LLVM toolchain directory) +endif + +# Configure toolchain +OPENSSL_CROSS_COMPILE := +OPENSSL_CC := +OPENSSL_RANLIB := $(ANDROID_GCC_TOOLCHAIN)/bin/$(ANDROID_TRIPLE)-ranlib + +ifneq ($(findstring clang,$(ANDROID_TOOLCHAIN)),) +OPENSSL_TARGET := $(OPENSSL_TARGET)-clang +OPENSSL_CC := $(ANDROID_LLVM_TOOLCHAIN)/bin/clang +endif + +ifneq ($(findstring gcc,$(ANDROID_TOOLCHAIN)),) +OPENSSL_CROSS_COMPILE := $(ANDROID_TRIPLE)- +OPENSSL_CC := $(ANDROID_GCC_TOOLCHAIN)/bin/$(ANDROID_TRIPLE)-gcc +endif + +ifeq ($(OPENSSL_CC),) +$(error invalid toolchain specified for ANDROID_TOOLCHAIN) +endif + +all: info $(OPENSSL_PREFIX)/lib/libssl.a + +$(OPENSSL_TARBALL): + @echo "Downloading OpenSSL tarball" + wget $(OPENSSL_URL) + +$(OPENSSL_PREFIX)/lib/libssl.a: $(OPENSSL_TARBALL) + @echo "Decompressing OpenSSL package" && \ ( \ set -e; \ - rm -rf $(OPENSSL_VER)/; \ - tar xzf $(OPENSSL_VER).tar.gz; \ - rm -rf $(OPENSSL_VER)-armeabi-v7a/ \ + rm -rf $(OPENSSL_SOURCE); \ + rm -rf $(OPENSSL_PACKAGE); \ + tar xzf $(OPENSSL_TARBALL); \ ) && \ - mv $(OPENSSL_VER) $(OPENSSL_VER)-armeabi-v7a && \ - cd $(OPENSSL_VER)-armeabi-v7a && \ - perl -pi -e 's/install: all install_docs install_sw/install: install_docs install_sw/g' Makefile.org && \ - ./config shared -no-ssl2 -no-ssl3 -no-comp -no-hw -no-engine --openssldir="`pwd`/../armeabi-v7a" && \ - make depend && \ - make all && \ - make install CC=$${ANDROID_TOOLCHAIN}/arm-linux-androideabi-gcc RANLIB=$${ANDROID_TOOLCHAIN}/arm-linux-androideabi-ranlib - -x86/lib/libssl.a: setenv-android-x86.sh $(OPENSSL_VER).tar.gz - [ -d "$(ANDROID_NDK_ROOT)" ] - export ANDROID_NDK_ROOT="$(ANDROID_NDK_ROOT)"; \ - . ./setenv-android-x86.sh; \ + mv $(OPENSSL_PACKAGE) $(OPENSSL_SOURCE) + @if test -f $(OPENSSL_PATCH); then \ + echo "Patching OpenSSL source tree"; \ + ( cd $(OPENSSL_SOURCE) && patch -p1 < ../$(OPENSSL_PATCH) ); \ + fi + @echo "Building OpenSSL" && \ + export ANDROID_NDK="$(ANDROID_NDK)" && \ + export ANDROID_API="$(ANDROID_API)" && \ + export ANDROID_ARCH="$(ANDROID_ARCH)" && \ + export ANDROID_TRIPLE="$(ANDROID_TRIPLE)" && \ + export ANDROID_SYSROOT="$(ANDROID_SYSROOT)" && \ + export ANDROID_LINK_SYSROOT="$(ANDROID_LINK_SYSROOT)" && \ + export ANDROID_GCC_TOOLCHAIN="$(ANDROID_GCC_TOOLCHAIN)" && \ + export CROSS_SYSROOT="$(ANDROID_SYSROOT)" && \ + export SYSROOT="$(ANDROID_SYSROOT)" && \ + export ARCH="$(ANDROID_ARCH)" && \ + export MACHINE="$(OPENSSL_MACHINE)" && \ + export SYSTEM="$(OPENSSL_SYSTEM)" && \ + export CROSS_COMPILE="$(OPENSSL_CROSS_COMPILE)" && \ + export HOSTCC="$(ANDROID_TOOLCHAIN)" && \ + export PATH="$(ANDROID_GCC_TOOLCHAIN)/bin:$(ANDROID_LLVM_TOOLCHAIN)/bin:$(PATH)" && \ ( \ - set -e; \ - rm -rf $(OPENSSL_VER)/; \ - tar xzf $(OPENSSL_VER).tar.gz; \ - rm -rf $(OPENSSL_VER)-x86/ \ - ) && \ - mv $(OPENSSL_VER) $(OPENSSL_VER)-x86 && \ - cd $(OPENSSL_VER)-x86 && \ - perl -pi -e 's/install: all install_docs install_sw/install: install_docs install_sw/g' Makefile.org && \ - ./config shared -no-ssl2 -no-ssl3 -no-comp -no-hw -no-engine --openssldir="`pwd`/../x86" && \ - make depend && \ - make all && \ - make install CC=$${ANDROID_TOOLCHAIN}/i686-linux-android-gcc RANLIB=$${ANDROID_TOOLCHAIN}/i686-linux-android-ranlib + cd $(OPENSSL_SOURCE); \ + perl Configure $(OPENSSL_TARGET) shared $(OPENSSL_OPTIONS) --prefix="`pwd`/../$(OPENSSL_PREFIX)" $(OPENSSL_CFLAGS) && \ + make depend && \ + make all && \ + make install CC=$(OPENSSL_CC) RANLIB=$(OPENSSL_RANLIB); \ + ) + +clean: + @echo "Cleaning" + rm -rf $(OPENSSL_SOURCE) + rm -rf $(OPENSSL_PACKAGE) + rm -rf $(OPENSSL_PREFIX) + +info: + @echo "OpenSSL build options" + @echo "ANDROID_NDK = $(ANDROID_NDK)" + @echo "ANDROID_API = $(ANDROID_API)" + @echo "ANDROID_ABI = $(ANDROID_ABI)" + @echo "ANDROID_HOST = $(ANDROID_HOST)" + @echo "ANDROID_TOOLCHAIN = $(ANDROID_TOOLCHAIN)" + @echo "ANDROID_TRIPLE = $(ANDROID_TRIPLE)" + @echo "ANDROID_SYSROOT = $(ANDROID_SYSROOT)" + @echo "ANDROID_LINK_SYSROOT = $(ANDROID_LINK_SYSROOT)" + @echo "ANDROID_GCC_TOOLCHAIN = $(ANDROID_GCC_TOOLCHAIN)" + @echo "ANDROID_LLVM_TOOLCHAIN = $(ANDROID_LLVM_TOOLCHAIN)" + @echo "OPENSSL_VERSION = $(OPENSSL_VERSION)" + @echo "OPENSSL_URL = $(OPENSSL_URL)" + @echo "OPENSSL_OPTIONS = $(OPENSSL_OPTIONS)" + @echo "OPENSSL_PREFIX = $(OPENSSL_PREFIX)" + @echo "OPENSSL_CFLAGS = $(OPENSSL_CFLAGS)" + @echo "OPENSSL_CC = $(OPENSSL_CC)" + @echo "OPENSSL_RANLIB = $(OPENSSL_RANLIB)" + +.PHONY: all clean info diff --git a/Build_android/openssl/openssl-1.0.2k.patch b/Build_android/openssl/openssl-1.0.2k.patch new file mode 100644 index 0000000000..992a27e9f9 --- /dev/null +++ b/Build_android/openssl/openssl-1.0.2k.patch @@ -0,0 +1,108 @@ +This patch applies several changes that enable OpenSSL 1.0.2k to be built +for Android using either Clang or GCC toolchains. + +An alias for the android-armv7 target, named android-armeabi, is added for +compatability with the OpenSSL 1.1.0 configuration target names. Support for +the AArch64 archicture is also added, as well as targets using the Clang +compiler. + +Clang does not recognize some of the ARM assembly nmenonics that are used in +OpenSSL. In particular, the 'adrl' pseudo instruction is not supported. To +further complicate matters, Clang doesn't support immediate fixup values so +the alternative adr/sub sequence used for the Thumb2 code path cannot be +used either. Instead it is replaced with a sequence of instructions that +computes the offset at runtime. It utilizes register r4 for computing the +intermediate result, which is first saved to and later restored from the +stack. The upstream bug in LLVM can be found here: +https://llvm.org/bugs/show_bug.cgi?id=24350 + +diff -Naur org/Configure mod/Configure +--- org/Configure 2017-01-26 05:22:03.000000000 -0800 ++++ mod/Configure 2018-01-17 14:25:44.712943600 -0800 +@@ -471,10 +471,17 @@ + "linux-alpha+bwx-ccc","ccc:-fast -readonly_strings -DL_ENDIAN::-D_REENTRANT:::SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_PTR DES_RISC1 DES_UNROLL:${alpha_asm}", + + # Android: linux-* but without pointers to headers and libs. +-"android","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +-"android-x86","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +-"android-armv7","gcc:-march=armv7-a -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +-"android-mips","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-x86","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armv7","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armeabi","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android64-aarch64","gcc:-march=armv8-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-mips","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-x86-clang","clang:-target i686-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armv7-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armeabi-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android64-aarch64-clang","clang:-target aarch64-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-mips-clang","clang:-target mipsel-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + + #### *BSD [do see comment about ${BSDthreads} above!] + "BSD-generic32","gcc:-O3 -fomit-frame-pointer -Wall::${BSDthreads}:::BN_LLONG RC2_CHAR RC4_INDEX DES_INT DES_UNROLL:${no_asm}:dlfcn:bsd-gcc-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +diff -Naur org/crypto/bn/asm/armv4-gf2m.pl mod/crypto/bn/asm/armv4-gf2m.pl +--- org/crypto/bn/asm/armv4-gf2m.pl 2017-01-26 05:22:03.000000000 -0800 ++++ mod/crypto/bn/asm/armv4-gf2m.pl 2018-01-17 11:38:57.297482700 -0800 +@@ -213,8 +213,8 @@ + .align 5 + .LNEON: + ldr r12, [sp] @ 5th argument +- vmov.32 $a, r2, r1 +- vmov.32 $b, r12, r3 ++ vmov $a, r2, r1 ++ vmov $b, r12, r3 + vmov.i64 $k48, #0x0000ffffffffffff + vmov.i64 $k32, #0x00000000ffffffff + vmov.i64 $k16, #0x000000000000ffff +diff -Naur org/crypto/sha/asm/sha256-armv4.pl mod/crypto/sha/asm/sha256-armv4.pl +--- org/crypto/sha/asm/sha256-armv4.pl 2017-01-26 05:22:03.000000000 -0800 ++++ mod/crypto/sha/asm/sha256-armv4.pl 2018-01-17 11:38:57.306342000 -0800 +@@ -576,6 +576,7 @@ + my @MSG=map("q$_",(8..11)); + my ($W0,$W1,$ABCD_SAVE,$EFGH_SAVE)=map("q$_",(12..15)); + my $Ktbl="r3"; ++my $Temp="r4"; + + $code.=<<___; + #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) +@@ -591,7 +592,13 @@ + sha256_block_data_order_armv8: + .LARMv8: + vld1.32 {$ABCD,$EFGH},[$ctx] +-# ifdef __thumb2__ ++# if defined(__clang__) ++ stmdb sp!,{r4,lr} ++ adr $Ktbl,.LARMv8 ++ ldr $Temp,=K256 ++ sub $Temp,$Ktbl,$Temp ++ sub $Ktbl,$Ktbl,$Temp ++# elif defined(__thumb2__) + adr $Ktbl,.LARMv8 + sub $Ktbl,$Ktbl,#.LARMv8-K256 + # else +@@ -655,7 +662,12 @@ + + vst1.32 {$ABCD,$EFGH},[$ctx] + ++# ifdef __clang__ ++ ldmia sp!,{r4,pc} ++# else + ret @ bx lr ++# endif ++ + .size sha256_block_data_order_armv8,.-sha256_block_data_order_armv8 + #endif + ___ +diff -Naur org/Makefile.org mod/Makefile.org +--- org/Makefile.org 2017-01-26 05:22:03.000000000 -0800 ++++ mod/Makefile.org 2018-01-17 14:26:20.623418300 -0800 +@@ -532,7 +532,7 @@ + @$(MAKE) SDIRS='$(SDIRS)' clean + @$(MAKE) TAR='$(TAR)' TARFLAGS='$(TARFLAGS)' $(DISTTARVARS) tar + +-install: all install_docs install_sw ++install: install_docs install_sw + + install_sw: + @$(PERL) $(TOP)/util/mkdir-p.pl $(INSTALL_PREFIX)$(INSTALLTOP)/bin \ diff --git a/Build_android/openssl/openssl-1.0.2l.patch b/Build_android/openssl/openssl-1.0.2l.patch new file mode 100644 index 0000000000..03acbee7c0 --- /dev/null +++ b/Build_android/openssl/openssl-1.0.2l.patch @@ -0,0 +1,108 @@ +This patch applies several changes that enable OpenSSL 1.0.2l to be built +for Android using either Clang or GCC toolchains. + +An alias for the android-armv7 target, named android-armeabi, is added for +compatability with the OpenSSL 1.1.0 configuration target names. Support for +the AArch64 archicture is also added, as well as targets using the Clang +compiler. + +Clang does not recognize some of the ARM assembly nmenonics that are used in +OpenSSL. In particular, the 'adrl' pseudo instruction is not supported. To +further complicate matters, Clang doesn't support immediate fixup values so +the alternative adr/sub sequence used for the Thumb2 code path cannot be +used either. Instead it is replaced with a sequence of instructions that +computes the offset at runtime. It utilizes register r4 for computing the +intermediate result, which is first saved to and later restored from the +stack. The upstream bug in LLVM can be found here: +https://llvm.org/bugs/show_bug.cgi?id=24350 + +diff -Naur org/Configure mod/Configure +--- org/Configure 2017-05-25 05:54:38.000000000 -0700 ++++ mod/Configure 2018-01-17 20:36:12.497485400 -0800 +@@ -471,10 +471,17 @@ + "linux-alpha+bwx-ccc","ccc:-fast -readonly_strings -DL_ENDIAN::-D_REENTRANT:::SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_PTR DES_RISC1 DES_UNROLL:${alpha_asm}", + + # Android: linux-* but without pointers to headers and libs. +-"android","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +-"android-x86","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +-"android-armv7","gcc:-march=armv7-a -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +-"android-mips","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-x86","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armv7","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armeabi","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android64-aarch64","gcc:-march=armv8-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-mips","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-x86-clang","clang:-target i686-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armv7-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armeabi-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android64-aarch64-clang","clang:-target aarch64-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-mips-clang","clang:-target mipsel-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + + #### *BSD [do see comment about ${BSDthreads} above!] + "BSD-generic32","gcc:-O3 -fomit-frame-pointer -Wall::${BSDthreads}:::BN_LLONG RC2_CHAR RC4_INDEX DES_INT DES_UNROLL:${no_asm}:dlfcn:bsd-gcc-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +diff -Naur org/crypto/bn/asm/armv4-gf2m.pl mod/crypto/bn/asm/armv4-gf2m.pl +--- org/crypto/bn/asm/armv4-gf2m.pl 2017-05-25 05:54:34.000000000 -0700 ++++ mod/crypto/bn/asm/armv4-gf2m.pl 2018-01-17 20:36:12.508421200 -0800 +@@ -213,8 +213,8 @@ + .align 5 + .LNEON: + ldr r12, [sp] @ 5th argument +- vmov.32 $a, r2, r1 +- vmov.32 $b, r12, r3 ++ vmov $a, r2, r1 ++ vmov $b, r12, r3 + vmov.i64 $k48, #0x0000ffffffffffff + vmov.i64 $k32, #0x00000000ffffffff + vmov.i64 $k16, #0x000000000000ffff +diff -Naur org/crypto/sha/asm/sha256-armv4.pl mod/crypto/sha/asm/sha256-armv4.pl +--- org/crypto/sha/asm/sha256-armv4.pl 2017-05-25 05:54:34.000000000 -0700 ++++ mod/crypto/sha/asm/sha256-armv4.pl 2018-01-17 20:36:12.518242800 -0800 +@@ -576,6 +576,7 @@ + my @MSG=map("q$_",(8..11)); + my ($W0,$W1,$ABCD_SAVE,$EFGH_SAVE)=map("q$_",(12..15)); + my $Ktbl="r3"; ++my $Temp="r4"; + + $code.=<<___; + #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) +@@ -591,7 +592,13 @@ + sha256_block_data_order_armv8: + .LARMv8: + vld1.32 {$ABCD,$EFGH},[$ctx] +-# ifdef __thumb2__ ++# if defined(__clang__) ++ stmdb sp!,{r4,lr} ++ adr $Ktbl,.LARMv8 ++ ldr $Temp,=K256 ++ sub $Temp,$Ktbl,$Temp ++ sub $Ktbl,$Ktbl,$Temp ++# elif defined(__thumb2__) + adr $Ktbl,.LARMv8 + sub $Ktbl,$Ktbl,#.LARMv8-K256 + # else +@@ -655,7 +662,12 @@ + + vst1.32 {$ABCD,$EFGH},[$ctx] + ++# ifdef __clang__ ++ ldmia sp!,{r4,pc} ++# else + ret @ bx lr ++# endif ++ + .size sha256_block_data_order_armv8,.-sha256_block_data_order_armv8 + #endif + ___ +diff -Naur org/Makefile.org mod/Makefile.org +--- org/Makefile.org 2017-05-25 05:54:38.000000000 -0700 ++++ mod/Makefile.org 2018-01-17 20:36:12.532553700 -0800 +@@ -540,7 +540,7 @@ + @$(MAKE) SDIRS='$(SDIRS)' clean + @$(MAKE) TAR='$(TAR)' TARFLAGS='$(TARFLAGS)' $(DISTTARVARS) tar + +-install: all install_docs install_sw ++install: install_docs install_sw + + install_sw: + @$(PERL) $(TOP)/util/mkdir-p.pl $(INSTALL_PREFIX)$(INSTALLTOP)/bin \ diff --git a/Build_android/openssl/openssl-1.0.2m.patch b/Build_android/openssl/openssl-1.0.2m.patch new file mode 100644 index 0000000000..8314b1e7fd --- /dev/null +++ b/Build_android/openssl/openssl-1.0.2m.patch @@ -0,0 +1,108 @@ +This patch applies several changes that enable OpenSSL 1.0.2m to be built +for Android using either Clang or GCC toolchains. + +An alias for the android-armv7 target, named android-armeabi, is added for +compatability with the OpenSSL 1.1.0 configuration target names. Support for +the AArch64 archicture is also added, as well as targets using the Clang +compiler. + +Clang does not recognize some of the ARM assembly nmenonics that are used in +OpenSSL. In particular, the 'adrl' pseudo instruction is not supported. To +further complicate matters, Clang doesn't support immediate fixup values so +the alternative adr/sub sequence used for the Thumb2 code path cannot be +used either. Instead it is replaced with a sequence of instructions that +computes the offset at runtime. It utilizes register r4 for computing the +intermediate result, which is first saved to and later restored from the +stack. The upstream bug in LLVM can be found here: +https://llvm.org/bugs/show_bug.cgi?id=24350 + +diff -Naur org/Configure mod/Configure +--- org/Configure 2017-11-02 07:32:57.000000000 -0700 ++++ mod/Configure 2018-01-17 20:39:03.152448900 -0800 +@@ -471,10 +471,17 @@ + "linux-alpha+bwx-ccc","ccc:-fast -readonly_strings -DL_ENDIAN::-D_REENTRANT:::SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_PTR DES_RISC1 DES_UNROLL:${alpha_asm}", + + # Android: linux-* but without pointers to headers and libs. +-"android","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +-"android-x86","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +-"android-armv7","gcc:-march=armv7-a -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +-"android-mips","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-x86","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armv7","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armeabi","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android64-aarch64","gcc:-march=armv8-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-mips","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-x86-clang","clang:-target i686-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armv7-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armeabi-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android64-aarch64-clang","clang:-target aarch64-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-mips-clang","clang:-target mipsel-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + + #### *BSD [do see comment about ${BSDthreads} above!] + "BSD-generic32","gcc:-O3 -fomit-frame-pointer -Wall::${BSDthreads}:::BN_LLONG RC2_CHAR RC4_INDEX DES_INT DES_UNROLL:${no_asm}:dlfcn:bsd-gcc-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +diff -Naur org/crypto/bn/asm/armv4-gf2m.pl mod/crypto/bn/asm/armv4-gf2m.pl +--- org/crypto/bn/asm/armv4-gf2m.pl 2017-11-02 07:32:57.000000000 -0700 ++++ mod/crypto/bn/asm/armv4-gf2m.pl 2018-01-17 20:39:03.163187500 -0800 +@@ -213,8 +213,8 @@ + .align 5 + .LNEON: + ldr r12, [sp] @ 5th argument +- vmov.32 $a, r2, r1 +- vmov.32 $b, r12, r3 ++ vmov $a, r2, r1 ++ vmov $b, r12, r3 + vmov.i64 $k48, #0x0000ffffffffffff + vmov.i64 $k32, #0x00000000ffffffff + vmov.i64 $k16, #0x000000000000ffff +diff -Naur org/crypto/sha/asm/sha256-armv4.pl mod/crypto/sha/asm/sha256-armv4.pl +--- org/crypto/sha/asm/sha256-armv4.pl 2017-11-02 07:32:58.000000000 -0700 ++++ mod/crypto/sha/asm/sha256-armv4.pl 2018-01-17 20:39:03.173547800 -0800 +@@ -576,6 +576,7 @@ + my @MSG=map("q$_",(8..11)); + my ($W0,$W1,$ABCD_SAVE,$EFGH_SAVE)=map("q$_",(12..15)); + my $Ktbl="r3"; ++my $Temp="r4"; + + $code.=<<___; + #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) +@@ -591,7 +592,13 @@ + sha256_block_data_order_armv8: + .LARMv8: + vld1.32 {$ABCD,$EFGH},[$ctx] +-# ifdef __thumb2__ ++# if defined(__clang__) ++ stmdb sp!,{r4,lr} ++ adr $Ktbl,.LARMv8 ++ ldr $Temp,=K256 ++ sub $Temp,$Ktbl,$Temp ++ sub $Ktbl,$Ktbl,$Temp ++# elif defined(__thumb2__) + adr $Ktbl,.LARMv8 + sub $Ktbl,$Ktbl,#.LARMv8-K256 + # else +@@ -655,7 +662,12 @@ + + vst1.32 {$ABCD,$EFGH},[$ctx] + ++# ifdef __clang__ ++ ldmia sp!,{r4,pc} ++# else + ret @ bx lr ++# endif ++ + .size sha256_block_data_order_armv8,.-sha256_block_data_order_armv8 + #endif + ___ +diff -Naur org/Makefile.org mod/Makefile.org +--- org/Makefile.org 2017-11-02 07:32:57.000000000 -0700 ++++ mod/Makefile.org 2018-01-17 20:39:03.187911200 -0800 +@@ -540,7 +540,7 @@ + @$(MAKE) SDIRS='$(SDIRS)' clean + @$(MAKE) TAR='$(TAR)' TARFLAGS='$(TARFLAGS)' $(DISTTARVARS) tar + +-install: all install_docs install_sw ++install: install_docs install_sw + + install_sw: + @$(PERL) $(TOP)/util/mkdir-p.pl $(INSTALL_PREFIX)$(INSTALLTOP)/bin \ diff --git a/Build_android/openssl/openssl-1.0.2n.patch b/Build_android/openssl/openssl-1.0.2n.patch new file mode 100644 index 0000000000..7e96205923 --- /dev/null +++ b/Build_android/openssl/openssl-1.0.2n.patch @@ -0,0 +1,108 @@ +This patch applies several changes that enable OpenSSL 1.0.2n to be built +for Android using either Clang or GCC toolchains. + +An alias for the android-armv7 target, named android-armeabi, is added for +compatability with the OpenSSL 1.1.0 configuration target names. Support for +the AArch64 archicture is also added, as well as targets using the Clang +compiler. + +Clang does not recognize some of the ARM assembly nmenonics that are used in +OpenSSL. In particular, the 'adrl' pseudo instruction is not supported. To +further complicate matters, Clang doesn't support immediate fixup values so +the alternative adr/sub sequence used for the Thumb2 code path cannot be +used either. Instead it is replaced with a sequence of instructions that +computes the offset at runtime. It utilizes register r4 for computing the +intermediate result, which is first saved to and later restored from the +stack. The upstream bug in LLVM can be found here: +https://llvm.org/bugs/show_bug.cgi?id=24350 + +diff -Naur org/Configure mod/Configure +--- org/Configure 2017-12-07 05:16:38.000000000 -0800 ++++ mod/Configure 2018-01-17 20:41:03.880613500 -0800 +@@ -471,10 +471,17 @@ + "linux-alpha+bwx-ccc","ccc:-fast -readonly_strings -DL_ENDIAN::-D_REENTRANT:::SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_PTR DES_RISC1 DES_UNROLL:${alpha_asm}", + + # Android: linux-* but without pointers to headers and libs. +-"android","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +-"android-x86","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +-"android-armv7","gcc:-march=armv7-a -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +-"android-mips","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-x86","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armv7","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armeabi","gcc:-march=armv7-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android64-aarch64","gcc:-march=armv8-a -mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-mips","gcc:-mandroid --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-x86-clang","clang:-target i686-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armv7-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-armeabi-clang","clang:-target armv7-none-linux-androideabi --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android64-aarch64-clang","clang:-target aarch64-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:SIXTY_FOUR_BIT_LONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${aarch64_asm}:linux64:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", ++"android-mips-clang","clang:-target mipsel-none-linux-android --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot \$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -B\$(ANDROID_LINK_SYSROOT)/lib -D__ANDROID_API__=\$(ANDROID_API) -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", + + #### *BSD [do see comment about ${BSDthreads} above!] + "BSD-generic32","gcc:-O3 -fomit-frame-pointer -Wall::${BSDthreads}:::BN_LLONG RC2_CHAR RC4_INDEX DES_INT DES_UNROLL:${no_asm}:dlfcn:bsd-gcc-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", +diff -Naur org/crypto/bn/asm/armv4-gf2m.pl mod/crypto/bn/asm/armv4-gf2m.pl +--- org/crypto/bn/asm/armv4-gf2m.pl 2017-12-07 05:16:38.000000000 -0800 ++++ mod/crypto/bn/asm/armv4-gf2m.pl 2018-01-17 20:41:03.891956700 -0800 +@@ -213,8 +213,8 @@ + .align 5 + .LNEON: + ldr r12, [sp] @ 5th argument +- vmov.32 $a, r2, r1 +- vmov.32 $b, r12, r3 ++ vmov $a, r2, r1 ++ vmov $b, r12, r3 + vmov.i64 $k48, #0x0000ffffffffffff + vmov.i64 $k32, #0x00000000ffffffff + vmov.i64 $k16, #0x000000000000ffff +diff -Naur org/crypto/sha/asm/sha256-armv4.pl mod/crypto/sha/asm/sha256-armv4.pl +--- org/crypto/sha/asm/sha256-armv4.pl 2017-12-07 05:16:38.000000000 -0800 ++++ mod/crypto/sha/asm/sha256-armv4.pl 2018-01-17 20:41:03.901983600 -0800 +@@ -576,6 +576,7 @@ + my @MSG=map("q$_",(8..11)); + my ($W0,$W1,$ABCD_SAVE,$EFGH_SAVE)=map("q$_",(12..15)); + my $Ktbl="r3"; ++my $Temp="r4"; + + $code.=<<___; + #if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) +@@ -591,7 +592,13 @@ + sha256_block_data_order_armv8: + .LARMv8: + vld1.32 {$ABCD,$EFGH},[$ctx] +-# ifdef __thumb2__ ++# if defined(__clang__) ++ stmdb sp!,{r4,lr} ++ adr $Ktbl,.LARMv8 ++ ldr $Temp,=K256 ++ sub $Temp,$Ktbl,$Temp ++ sub $Ktbl,$Ktbl,$Temp ++# elif defined(__thumb2__) + adr $Ktbl,.LARMv8 + sub $Ktbl,$Ktbl,#.LARMv8-K256 + # else +@@ -655,7 +662,12 @@ + + vst1.32 {$ABCD,$EFGH},[$ctx] + ++# ifdef __clang__ ++ ldmia sp!,{r4,pc} ++# else + ret @ bx lr ++# endif ++ + .size sha256_block_data_order_armv8,.-sha256_block_data_order_armv8 + #endif + ___ +diff -Naur org/Makefile.org mod/Makefile.org +--- org/Makefile.org 2017-12-07 05:16:38.000000000 -0800 ++++ mod/Makefile.org 2018-01-17 20:41:03.916748800 -0800 +@@ -540,7 +540,7 @@ + @$(MAKE) SDIRS='$(SDIRS)' clean + @$(MAKE) TAR='$(TAR)' TARFLAGS='$(TARFLAGS)' $(DISTTARVARS) tar + +-install: all install_docs install_sw ++install: install_docs install_sw + + install_sw: + @$(PERL) $(TOP)/util/mkdir-p.pl $(INSTALL_PREFIX)$(INSTALLTOP)/bin \ diff --git a/Build_android/openssl/openssl-1.1.0g.patch b/Build_android/openssl/openssl-1.1.0g.patch new file mode 100644 index 0000000000..77a92779d7 --- /dev/null +++ b/Build_android/openssl/openssl-1.1.0g.patch @@ -0,0 +1,76 @@ +This patch applies several changes that enable OpenSSL 1.1.0g to be built +for Android using either Clang or GCC toolchains. + +diff -Naur org/Configurations/10-main.conf mod/Configurations/10-main.conf +--- org/Configurations/10-main.conf 2017-11-02 07:29:01.000000000 -0700 ++++ mod/Configurations/10-main.conf 2018-01-18 10:59:41.675138500 -0800 +@@ -910,15 +910,27 @@ + # systems are perfectly capable of executing binaries targeting + # Froyo. Keep in mind that in the nutshell Android builds are + # about JNI, i.e. shared libraries, not applications. +- cflags => add(picker(default => "-mandroid -fPIC --sysroot=\$(CROSS_SYSROOT) -Wa,--noexecstack")), ++ cflags => add(picker(default => "-mandroid -fPIC --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wa,--noexecstack")), + bin_cflags => "-pie", + }, ++ "android-clang" => { ++ inherit_from => [ "linux-generic32" ], ++ cc => "clang", ++ cflags => add(picker(default => "-fPIC --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Qunused-arguments -Wa,--noexecstack")), ++}, + "android-x86" => { + inherit_from => [ "android", asm("x86_asm") ], + cflags => add(picker(release => "-fomit-frame-pointer")), + bn_ops => "BN_LLONG", + perlasm_scheme => "android", + }, ++ "android-x86-clang" => { ++ inherit_from => [ "android-clang", asm("x86_asm") ], ++ cflags => add(picker(default => "-target i686-none-linux-android", ++ release => "-fomit-frame-pointer")), ++ bn_ops => "BN_LLONG", ++ perlasm_scheme => "android", ++ }, + ################################################################ + # Contemporary Android applications can provide multiple JNI + # providers in .apk, targeting multiple architectures. Among +@@ -943,20 +955,38 @@ + "android-armeabi" => { + inherit_from => [ "android", asm("armv4_asm") ], + }, ++ "android-armeabi-clang" => { ++ inherit_from => [ "android-clang", asm("armv4_asm") ], ++ cflags => add("-target armv7-none-linux-androideabi"), ++ }, + "android-mips" => { + inherit_from => [ "android", asm("mips32_asm") ], + perlasm_scheme => "o32", + }, +- ++ "android-mips-clang" => { ++ inherit_from => [ "android-clang", asm("mips32_asm") ], ++ cflags => add("-target mipsel-none-linux-android"), ++ perlasm_scheme => "o32", ++ }, + "android64" => { + inherit_from => [ "linux-generic64" ], +- cflags => add(picker(default => "-mandroid -fPIC --sysroot=\$(CROSS_SYSROOT) -Wa,--noexecstack")), ++ cflags => add(picker(default => "-mandroid -fPIC --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wa,--noexecstack")), + bin_cflags => "-pie", + }, ++ "android64-clang" => { ++ inherit_from => [ "linux-generic64" ], ++ cc => "clang", ++ cflags => add(picker(default => "-fPIC --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Qunused-arguments -Wa,--noexecstack")), ++ }, + "android64-aarch64" => { + inherit_from => [ "android64", asm("aarch64_asm") ], + perlasm_scheme => "linux64", + }, ++ "android64-aarch64-clang" => { ++ inherit_from => [ "android64-clang", asm("aarch64_asm") ], ++ cflags => add("-target aarch64-none-linux-android"), ++ perlasm_scheme => "linux64", ++ }, + + #### *BSD + "BSD-generic32" => { diff --git a/Build_android/openssl/openssl-1.1.0j.patch b/Build_android/openssl/openssl-1.1.0j.patch new file mode 100644 index 0000000000..77a92779d7 --- /dev/null +++ b/Build_android/openssl/openssl-1.1.0j.patch @@ -0,0 +1,76 @@ +This patch applies several changes that enable OpenSSL 1.1.0g to be built +for Android using either Clang or GCC toolchains. + +diff -Naur org/Configurations/10-main.conf mod/Configurations/10-main.conf +--- org/Configurations/10-main.conf 2017-11-02 07:29:01.000000000 -0700 ++++ mod/Configurations/10-main.conf 2018-01-18 10:59:41.675138500 -0800 +@@ -910,15 +910,27 @@ + # systems are perfectly capable of executing binaries targeting + # Froyo. Keep in mind that in the nutshell Android builds are + # about JNI, i.e. shared libraries, not applications. +- cflags => add(picker(default => "-mandroid -fPIC --sysroot=\$(CROSS_SYSROOT) -Wa,--noexecstack")), ++ cflags => add(picker(default => "-mandroid -fPIC --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wa,--noexecstack")), + bin_cflags => "-pie", + }, ++ "android-clang" => { ++ inherit_from => [ "linux-generic32" ], ++ cc => "clang", ++ cflags => add(picker(default => "-fPIC --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Qunused-arguments -Wa,--noexecstack")), ++}, + "android-x86" => { + inherit_from => [ "android", asm("x86_asm") ], + cflags => add(picker(release => "-fomit-frame-pointer")), + bn_ops => "BN_LLONG", + perlasm_scheme => "android", + }, ++ "android-x86-clang" => { ++ inherit_from => [ "android-clang", asm("x86_asm") ], ++ cflags => add(picker(default => "-target i686-none-linux-android", ++ release => "-fomit-frame-pointer")), ++ bn_ops => "BN_LLONG", ++ perlasm_scheme => "android", ++ }, + ################################################################ + # Contemporary Android applications can provide multiple JNI + # providers in .apk, targeting multiple architectures. Among +@@ -943,20 +955,38 @@ + "android-armeabi" => { + inherit_from => [ "android", asm("armv4_asm") ], + }, ++ "android-armeabi-clang" => { ++ inherit_from => [ "android-clang", asm("armv4_asm") ], ++ cflags => add("-target armv7-none-linux-androideabi"), ++ }, + "android-mips" => { + inherit_from => [ "android", asm("mips32_asm") ], + perlasm_scheme => "o32", + }, +- ++ "android-mips-clang" => { ++ inherit_from => [ "android-clang", asm("mips32_asm") ], ++ cflags => add("-target mipsel-none-linux-android"), ++ perlasm_scheme => "o32", ++ }, + "android64" => { + inherit_from => [ "linux-generic64" ], +- cflags => add(picker(default => "-mandroid -fPIC --sysroot=\$(CROSS_SYSROOT) -Wa,--noexecstack")), ++ cflags => add(picker(default => "-mandroid -fPIC --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wa,--noexecstack")), + bin_cflags => "-pie", + }, ++ "android64-clang" => { ++ inherit_from => [ "linux-generic64" ], ++ cc => "clang", ++ cflags => add(picker(default => "-fPIC --gcc-toolchain=\$(ANDROID_GCC_TOOLCHAIN) --sysroot=\$(ANDROID_LINK_SYSROOT) -isystem \$(ANDROID_SYSROOT)/usr/include -isystem \$(ANDROID_SYSROOT)/usr/include/\$(ANDROID_TRIPLE) -D__ANDROID_API__=\$(ANDROID_API) -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Qunused-arguments -Wa,--noexecstack")), ++ }, + "android64-aarch64" => { + inherit_from => [ "android64", asm("aarch64_asm") ], + perlasm_scheme => "linux64", + }, ++ "android64-aarch64-clang" => { ++ inherit_from => [ "android64-clang", asm("aarch64_asm") ], ++ cflags => add("-target aarch64-none-linux-android"), ++ perlasm_scheme => "linux64", ++ }, + + #### *BSD + "BSD-generic32" => { diff --git a/Build_iOS/.gitignore b/Build_iOS/.gitignore index 525689cf70..93f4744bb6 100644 --- a/Build_iOS/.gitignore +++ b/Build_iOS/.gitignore @@ -1,5 +1,6 @@ # iOS folders that dependencies get stored in boostoniphone/ +boost boost.framework/ ios-cmake/ openssl/ diff --git a/Build_iOS/CMakeLists.txt b/Build_iOS/CMakeLists.txt index 16782b7d83..0cf1b62467 100644 --- a/Build_iOS/CMakeLists.txt +++ b/Build_iOS/CMakeLists.txt @@ -1,9 +1,16 @@ project(casablanca-ios NONE) -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.1) enable_testing() -set(TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ios-cmake/toolchain/iOS.cmake") +set(LIB_CPPREST libcpprest.a) +set(LIB_CPPREST_LIB_DIR "${CMAKE_CURRENT_BINARY_DIR}/lib") + +if (CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET) + set (ENV{CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET} ${CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET}) +endif() + +set(TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ios-cmake/ios.toolchain.cmake") set(SIM_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.i386" CACHE INTERNAL "") set(SIM_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Release" CACHE INTERNAL "") @@ -13,11 +20,28 @@ set(SIM64_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Release" CACHE INTERNAL "") set(ARM_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.arm" CACHE INTERNAL "") set(ARM_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Release" CACHE INTERNAL "") + +if (DISABLE_BITCODE) + set (ENABLE_BITCODE_ARG -DENABLE_BITCODE=OFF) +endif() + +if (INCLUDE_32BIT) + set (IOS_PLATFORM_VALUE OS) +else() + set (IOS_PLATFORM_VALUE OS64) +endif() + +if (DEPLOYMENT_TARGET) + set (DEPLOYMENT_TARGET -DIOS_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET}) +endif() + add_test(NAME ios_runner WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../Release/tests/common/testrunner/ios - COMMAND xcodebuild test -project ios_runner.xcodeproj -configuration=${CMAKE_BUILD_TYPE} -scheme ios_runner -destination "platform=iOS Simulator,name=iPhone 6" LIBRARY_SEARCH_PATH=${SIM_BINARY_DIR} + COMMAND xcodebuild test -project ios_runner.xcodeproj -configuration=${CMAKE_BUILD_TYPE} -scheme ios_runner -destination "platform=iOS Simulator,name=iPhone 6" LIBRARY_SEARCH_PATH=${SIM64_BINARY_DIR} ) +if (INCLUDE_32BIT) +set (SIM_BINARY_LIB ${SIM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_CPPREST}) file(MAKE_DIRECTORY ${SIM_BINARY_DIR}) execute_process(WORKING_DIRECTORY ${SIM_BINARY_DIR} COMMAND ${CMAKE_COMMAND} @@ -25,10 +49,12 @@ execute_process(WORKING_DIRECTORY ${SIM_BINARY_DIR} -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE} -DIOS_PLATFORM=SIMULATOR -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_C_COMPILER=${CLANG_C_COMPILER} - -DCMAKE_CXX_COMPILER=${CLANG_CXX_COMPILER} + "${DEPLOYMENT_TARGET}" "${SIM_SOURCE_DIR}" - ) +) +else() +set (SIM_BINARY_LIB "") +endif() file(MAKE_DIRECTORY ${SIM64_BINARY_DIR}) execute_process(WORKING_DIRECTORY ${SIM64_BINARY_DIR} @@ -37,8 +63,7 @@ execute_process(WORKING_DIRECTORY ${SIM64_BINARY_DIR} -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE} -DIOS_PLATFORM=SIMULATOR64 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_C_COMPILER=${CLANG_C_COMPILER} - -DCMAKE_CXX_COMPILER=${CLANG_CXX_COMPILER} + "${DEPLOYMENT_TARGET}" "${SIM64_SOURCE_DIR}" ) @@ -47,22 +72,26 @@ execute_process(WORKING_DIRECTORY ${ARM_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -GXcode -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE} - -DIOS_PLATFORM=OS + -DIOS_PLATFORM=${IOS_PLATFORM_VALUE} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_C_COMPILER=${CLANG_C_COMPILER} - -DCMAKE_CXX_COMPILER=${CLANG_CXX_COMPILER} + "${DEPLOYMENT_TARGET}" + "${ENABLE_BITCODE_ARG}" "${ARM_SOURCE_DIR}" ) - +if (INCLUDE_32BIT) +set (SIM_TARGET sim) ## Simulator i386 version add_custom_target(sim COMMAND ${CMAKE_COMMAND} --build ${SIM_BINARY_DIR} --config ${CMAKE_BUILD_TYPE} - COMMENT "Building for i386 (simulator)" - VERBATIM + COMMENT "Building for i386 (simulator)" +VERBATIM ) +else() +set (SIM_TARGET "") +endif() ## Simulator x86_64 version add_custom_target(sim64 @@ -78,26 +107,27 @@ add_custom_target(arm COMMAND ${CMAKE_COMMAND} --build ${ARM_BINARY_DIR} --config ${CMAKE_BUILD_TYPE} - COMMENT "Building for armv7, armv7s, arm64" + COMMENT "Building for arm" VERBATIM ) -set(LIB_CPPREST libcpprest.a) add_custom_command( - OUTPUT ${LIB_CPPREST} + OUTPUT ${LIB_CPPREST_LIB_DIR}/${LIB_CPPREST} + COMMAND mkdir -p "${LIB_CPPREST_LIB_DIR}" COMMAND lipo -create - -output "${CMAKE_CURRENT_BINARY_DIR}/${LIB_CPPREST}" - ${SIM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_CPPREST} + -output "${LIB_CPPREST_LIB_DIR}/${LIB_CPPREST}" + ${SIM_BINARY_LIB} ${SIM64_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_CPPREST} ${ARM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_CPPREST} + COMMAND cp -R "${CMAKE_CURRENT_SOURCE_DIR}/../Release/include" "${CMAKE_CURRENT_BINARY_DIR}" DEPENDS - sim + ${SIM_TARGET} sim64 arm - "${SIM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_CPPREST}" + ${SIM_BINARY_LIB} "${SIM64_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_CPPREST}" "${ARM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_CPPREST}" VERBATIM ) -add_custom_target(cpprest ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${LIB_CPPREST}) +add_custom_target(cpprest ALL DEPENDS "${LIB_CPPREST_LIB_DIR}/${LIB_CPPREST}") diff --git a/Build_iOS/configure.sh b/Build_iOS/configure.sh index 8b0599512c..722128e3d9 100755 --- a/Build_iOS/configure.sh +++ b/Build_iOS/configure.sh @@ -1,44 +1,165 @@ -#!/bin/bash +#!/usr/bin/env bash set -e -if [ ! -e boost.framework ] - then - git clone -n https://github.com/faithfracture/Apple-Boost-BuildScript Apple-Boost-BuildScript - pushd Apple-Boost-BuildScript - git checkout 86f7570fceaef00846cc75f59c61474758fc65cb - BOOST_LIBS="thread chrono filesystem regex system random" ./boost.sh - popd - mv Apple-Boost-BuildScript/build/boost/1.63.0/ios/framework/boost.framework . - mv boost.framework/Versions/A/Headers boost.headers - mkdir -p boost.framework/Versions/A/Headers - mv boost.headers boost.framework/Versions/A/Headers/boost +usage() { + echo "Usage: configure.sh [-build_type type] [-deployment_target version] [-config_only] [-include_32bit] [-no_bitcode]" + echo " -build_type defines the CMAKE_BUILD_TYPE used. Defaults to Release." + echo " -deployment_target defines minimum iOS Deployment Target. The default is dependent on ios.toolchain.cmake and currently defaults to 8.0" + echo " -config_only only configures cmake (no make invoked)." + echo " -include_32bit includes the 32-bit arm architectures." + echo " -no_bitcode disables bitcode" + echo " -clean deletes build directory prior to configuring" +} + +ABS_PATH="`dirname \"$0\"`" # relative +ABS_PATH="`( cd \"${ABS_PATH}\" && pwd )`" # absolutized and normalized +# Make sure that the path to this file exists and can be retrieved! +if [ -z "${ABS_PATH}" ]; then + echo "Could not fetch the ABS_PATH." + exit 1 +fi + +CONFIG_ONLY=0 +INCLUDE_32BIT="" +DISABLE_BITCODE="" +DEPLOYMENT_TARGET="" +CLEAN=0 + +# Command line argument parsing +while (( "$#" )); do + case "$1" in + -build_type) + if [ "$#" -lt 2 ] || [[ "$2" == -* ]] ; then + usage + echo "Error: argument $1 expecting a value to follow." + exit 1 + fi + + CPPRESTSDK_BUILD_TYPE=$2 + shift 2 + ;; + -deployment_target) + if [ "$#" -lt 2 ] || [[ "$2" == -* ]] ; then + usage + echo "Error: argument $1 expecting a value to follow." + exit 1 + fi + + DEPLOYMENT_TARGET="-DDEPLOYMENT_TARGET=$2" + shift 2 + ;; + -config_only) + CONFIG_ONLY=1 + shift 1 + ;; + -include_32bit) + INCLUDE_32BIT="-DINCLUDE_32BIT=ON" + shift 1 + ;; + -no_bitcode) + DISABLE_BITCODE="-DDISABLE_BITCODE=ON" + shift 1 + ;; + -clean) + CLEAN=1 + shift 1 + ;; + *) + usage + echo "Error: unsupported argument $1" + exit 1 + ;; + esac +done + +## Configuration +DEFAULT_BOOST_VERSION=1.69.0 +DEFAULT_OPENSSL_VERSION=1.1.0k +BOOST_VERSION=${BOOST_VERSION:-${DEFAULT_BOOST_VERSION}} +OPENSSL_VERSION=${OPENSSL_VERSION:-${DEFAULT_OPENSSL_VERSION}} +CPPRESTSDK_BUILD_TYPE=${CPPRESTSDK_BUILD_TYPE:-Release} + +############################ No need to edit anything below this line + +## Set some needed variables +IOS_SDK_VERSION=`xcrun --sdk iphoneos --show-sdk-version` + +## Buildsteps below + +## Fetch submodules just in case +git submodule update --init + +## Build Boost + +if [ ! -e $ABS_PATH/boost.framework ] && [ ! -d $ABS_PATH/boost ]; then + if [ ! -d "${ABS_PATH}/Apple-Boost-BuildScript" ]; then + git clone https://github.com/faithfracture/Apple-Boost-BuildScript ${ABS_PATH}/Apple-Boost-BuildScript + fi + pushd ${ABS_PATH}/Apple-Boost-BuildScript + git checkout 1ebe6e7654d9c9e1792076ee3827a45d5d2f34c5 + BOOST_LIBS="thread chrono filesystem regex system random" ./boost.sh -ios -tvos --boost-version $BOOST_VERSION + popd + mv ${ABS_PATH}/Apple-Boost-BuildScript/build/boost/${BOOST_VERSION}/ios/framework/boost.framework ${ABS_PATH} + mv ${ABS_PATH}/boost.framework/Versions/A/Headers ${ABS_PATH}/boost.headers + mkdir -p ${ABS_PATH}/boost.framework/Versions/A/Headers + mv ${ABS_PATH}/boost.headers ${ABS_PATH}/boost.framework/Versions/A/Headers/boost fi -if [ ! -e openssl/lib/libcrypto.a ] - then - git clone --depth=1 https://github.com/x2on/OpenSSL-for-iPhone.git - pushd OpenSSL-for-iPhone - ./build-libssl.sh - popd - mkdir -p openssl/lib - cp -r OpenSSL-for-iPhone/bin/iPhoneOS9.2-armv7.sdk/include openssl - cp OpenSSL-for-iPhone/include/LICENSE openssl - lipo -create -output openssl/lib/libssl.a OpenSSL-for-iPhone/bin/iPhone*/lib/libssl.a - lipo -create -output openssl/lib/libcrypto.a OpenSSL-for-iPhone/bin/iPhone*/lib/libcrypto.a +## Build OpenSSL + +if [ ! -e ${ABS_PATH}/openssl/lib/libcrypto.a ]; then + if [ ! -d "${ABS_PATH}/OpenSSL-for-iPhone" ]; then + git clone --depth=1 https://github.com/x2on/OpenSSL-for-iPhone.git ${ABS_PATH}/OpenSSL-for-iPhone + fi + pushd ${ABS_PATH}/OpenSSL-for-iPhone + git checkout 10019638e80e8a8a5fc19642a840d8a69fac7349 + ./build-libssl.sh --version=${OPENSSL_VERSION} + popd + mkdir -p ${ABS_PATH}/openssl/lib + if [ -e ${ABS_PATH}/OpenSSL-for-iPhone/bin/iPhoneOS${IOS_SDK_VERSION}-arm64.sdk/include ] + then + cp -r ${ABS_PATH}/OpenSSL-for-iPhone/bin/iPhoneOS${IOS_SDK_VERSION}-arm64.sdk/include ${ABS_PATH}/openssl + else + echo 'Could not find OpenSSL for iPhone' + exit 1 + fi + cp ${ABS_PATH}/OpenSSL-for-iPhone/include/LICENSE ${ABS_PATH}/openssl + lipo -create -output ${ABS_PATH}/openssl/lib/libssl.a ${ABS_PATH}/OpenSSL-for-iPhone/bin/iPhone*/lib/libssl.a + lipo -create -output ${ABS_PATH}/openssl/lib/libcrypto.a ${ABS_PATH}/OpenSSL-for-iPhone/bin/iPhone*/lib/libcrypto.a fi -if [ ! -e ios-cmake/toolchain/iOS.cmake ] - then - git clone https://github.com/cristeab/ios-cmake.git - pushd ios-cmake - git apply ../fix_ios_cmake_compiler.patch - popd +## Fetch CMake toolchain + +if [ ! -e ${ABS_PATH}/ios-cmake/ios.toolchain.cmake ]; then + if [ ! -d "${ABS_PATH}/ios-cmake" ]; then + git clone https://github.com/leetal/ios-cmake ${ABS_PATH}/ios-cmake + fi + pushd ${ABS_PATH}/ios-cmake + git checkout 2.1.2 + popd +fi + +## Build CPPRestSDK +if [ -d "${ABS_PATH}/build.${CPPRESTSDK_BUILD_TYPE}.ios" ]; then + if [ "$CLEAN" -eq 1 ]; then + echo "Removing directory ${ABS_PATH}/build.${CPPRESTSDK_BUILD_TYPE}.ios prior to configuring." + rm -rf "${ABS_PATH}/build.${CPPRESTSDK_BUILD_TYPE}.ios" + else + printf "WARNING: Running configure on an already existing configuration.\nAny changes to the existing configuration will not be picked up.\nEither remove the directory and re-run configure or run configure with the -clean flag.\n\n" + fi fi -mkdir -p build.ios -pushd build.ios -cmake .. -DCMAKE_BUILD_TYPE=Release -make +mkdir -p ${ABS_PATH}/build.${CPPRESTSDK_BUILD_TYPE}.ios +pushd ${ABS_PATH}/build.${CPPRESTSDK_BUILD_TYPE}.ios +cmake -DCMAKE_BUILD_TYPE=${CPPRESTSDK_BUILD_TYPE} .. ${INCLUDE_32BIT} ${DISABLE_BITCODE} ${DEPLOYMENT_TARGET} +if [ "$CONFIG_ONLY" -eq 0 ]; then + make + printf "\n\n===================================================================================\n" + echo ">>>> The final library is available in 'build.${CPPRESTSDK_BUILD_TYPE}.ios/lib/libcpprest.a'" + printf "===================================================================================\n\n" +else + printf "\n\n===================================================================================\n" + echo ">>>> Configuration complete. Run 'make' in 'build.${CPPRESTSDK_BUILD_TYPE}.ios' to build." + printf "===================================================================================\n\n" +fi popd -echo "====" -echo "The final library is available in 'build.ios/libcpprest.a'" diff --git a/Build_iOS/fix_boost_building_script.patch b/Build_iOS/fix_boost_building_script.patch deleted file mode 100644 index a7f7b432a6..0000000000 --- a/Build_iOS/fix_boost_building_script.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/boost.sh b/boost.sh -index 2d5c05d..5c10ee9 100644 ---- a/boost.sh -+++ b/boost.sh -@@ -26,7 +26,7 @@ - # - #=============================================================================== - --BOOST_LIBS="atomic chrono date_time exception filesystem program_options random signals system test thread" -+BOOST_LIBS="chrono filesystem random regex system thread" - - BUILD_IOS= - BUILD_OSX= diff --git a/Build_iOS/fix_ios_cmake_compiler.patch b/Build_iOS/fix_ios_cmake_compiler.patch deleted file mode 100644 index 85eeec18c4..0000000000 --- a/Build_iOS/fix_ios_cmake_compiler.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git a/toolchain/iOS.cmake b/toolchain/iOS.cmake -index 195e3fc..567a8d3 100644 ---- a/toolchain/iOS.cmake -+++ b/toolchain/iOS.cmake -@@ -45,10 +45,10 @@ if (CMAKE_UNAME) - string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}") - endif (CMAKE_UNAME) - --# Force the compilers to gcc for iOS -+# Force the compilers to clang for iOS - include (CMakeForceCompiler) --CMAKE_FORCE_C_COMPILER (/usr/bin/gcc Apple) --CMAKE_FORCE_CXX_COMPILER (/usr/bin/g++ Apple) -+CMAKE_FORCE_C_COMPILER (/usr/bin/clang Apple) -+CMAKE_FORCE_CXX_COMPILER (/usr/bin/clang++ Apple) - set(CMAKE_AR ar CACHE FILEPATH "" FORCE) - - # Skip the platform compiler checks for cross compiling diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..d010e21fcd --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.1) +project(cpprestsdk-root NONE) +enable_testing() +add_subdirectory(Release) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index ca43881905..36348fa544 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -1,4 +1,4 @@ -Contributors should submit an update to this file with a commit in order to receive recognition. Thank you for your contributions. +Contributors should submit an update to this file with a commit in order to receive recognition. Thank you for your contributions. List of Contributors @@ -17,11 +17,12 @@ intercommiura halex2005 simonlep jracle -gandziej +gandziej adish LeonidCSIT kreuzerkrieg evanc +Jesse Towner (jwtowner) Abinsula s.r.l. Gianfranco Costamagna (LocutusOfBorg) @@ -44,4 +45,15 @@ thomasschaub Trimble Tim Boundy (gigaplex) -Rami Abughazaleh (icnocop) \ No newline at end of file +Rami Abughazaleh (icnocop) + +TastenTrick +Christian Deneke (chris0x44) + +leetal + +Benjamin Lee (mobileben) +René Meusel (reneme) + +Sony Corporation +Gareth Sylvester-Bradley (garethsb-sony) diff --git a/NuGet.Config b/NuGet.Config deleted file mode 100644 index abc5b1378c..0000000000 --- a/NuGet.Config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index a63acfffef..1e5ab05a77 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ The C++ REST SDK is a Microsoft project for cloud-based client-server communicat [![openSUSE Tumbleweed package](https://repology.org/badge/version-for-repo/opensuse_tumbleweed/cpprestsdk.svg)](https://repology.org/metapackage/cpprestsdk)
[![Debian Testing package](https://repology.org/badge/version-for-repo/debian_testing/cpprestsdk.svg)](https://repology.org/metapackage/cpprestsdk)
+[![Build Status](https://dev.azure.com/vclibs/cpprestsdk/_apis/build/status/Microsoft.cpprestsdk.Ubuntu)](https://dev.azure.com/vclibs/cpprestsdk/_build/latest?definitionId=1) + With [vcpkg](https://github.com/Microsoft/vcpkg) on Windows ``` PS> vcpkg install cpprestsdk cpprestsdk:x64-windows @@ -21,6 +23,10 @@ With [apt-get](https://launchpad.net/ubuntu/+source/casablanca/2.8.0-2build2) on ``` $ sudo apt-get install libcpprest-dev ``` +With [dnf](https://apps.fedoraproject.org/packages/cpprest) on Fedora +``` +$ sudo dnf install cpprest-devel +``` With [brew](https://github.com/Homebrew/homebrew-core/blob/master/Formula/cpprestsdk.rb) on OSX ``` $ brew install cpprestsdk @@ -49,7 +55,7 @@ target_link_libraries(main PRIVATE cpprestsdk::cpprest) * Features - HTTP client/server, JSON, URI, asynchronous streams, WebSockets client, oAuth * PPL Tasks - A powerful model for composing asynchronous operations based on C++ 11 features * Platforms - Windows desktop, Windows Store (UWP), Linux, OS X, Unix, iOS, and Android -* Support for Visual Studio 2015 and 2017 with debugger visualizers +* Support for [Visual Studio 2015 and 2017](https://visualstudio.microsoft.com/) with debugger visualizers ## Contribute Back! @@ -59,7 +65,7 @@ Big or small we'd like to take your [contributions](https://github.com/Microsoft ## Having Trouble? -We'd love to get your review score, whether good or bad, but even more than that, we want to fix your problem. If you submit your issue as a Review, we won't be able to respond to your problem and ask any follow-up questions that may be necessary. The most efficient way to do that is to open a an issue in our [issue tracker](https://github.com/Microsoft/cpprestsdk/issues). +We'd love to get your review score, whether good or bad, but even more than that, we want to fix your problem. If you submit your issue as a Review, we won't be able to respond to your problem and ask any follow-up questions that may be necessary. The most efficient way to do that is to open an issue in our [issue tracker](https://github.com/Microsoft/cpprestsdk/issues). ### Quick Links @@ -69,3 +75,4 @@ We'd love to get your review score, whether good or bad, but even more than that * Directly contact us: This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + diff --git a/Release/CMakeLists.txt b/Release/CMakeLists.txt index 5362d1cd9e..000516e1be 100644 --- a/Release/CMakeLists.txt +++ b/Release/CMakeLists.txt @@ -11,14 +11,15 @@ endif() set(CPPREST_VERSION_MAJOR 2) set(CPPREST_VERSION_MINOR 10) -set(CPPREST_VERSION_REVISION 2) +set(CPPREST_VERSION_REVISION 14) enable_testing() set(WERROR ON CACHE BOOL "Treat Warnings as Errors.") set(CPPREST_EXCLUDE_WEBSOCKETS OFF CACHE BOOL "Exclude websockets functionality.") set(CPPREST_EXCLUDE_COMPRESSION OFF CACHE BOOL "Exclude compression functionality.") -set(CPPREST_EXPORT_DIR lib/cpprestsdk CACHE STRING "Directory to install CMake config files.") +set(CPPREST_EXCLUDE_BROTLI ON CACHE BOOL "Exclude Brotli compression functionality.") +set(CPPREST_EXPORT_DIR cpprestsdk CACHE STRING "Directory to install CMake config files.") set(CPPREST_INSTALL_HEADERS ON CACHE BOOL "Install header files.") set(CPPREST_INSTALL ON CACHE BOOL "Add install commands.") @@ -60,9 +61,12 @@ endif() include(cmake/cpprest_find_boost.cmake) include(cmake/cpprest_find_zlib.cmake) +include(cmake/cpprest_find_winhttppal.cmake) include(cmake/cpprest_find_openssl.cmake) include(cmake/cpprest_find_websocketpp.cmake) +include(cmake/cpprest_find_brotli.cmake) include(CheckIncludeFiles) +include(GNUInstallDirs) find_package(Threads REQUIRED) if(THREADS_HAVE_PTHREAD_ARG) @@ -110,34 +114,12 @@ else() endif() set(WARNINGS) -set(ANDROID_STL_FLAGS) +set(ANDROID_LIBS) # Platform (not compiler) specific settings -if(IOS) - # 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") -elseif(ANDROID) - 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() +if(ANDROID) # These are used in the shared library case - set(ANDROID_STL_FLAGS - ${LIBCXX_STL} - atomic - dl - gcc - c - m - -nodefaultlibs - ) - - include_directories(SYSTEM - "${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" - ) + set(ANDROID_LIBS atomic dl) elseif(UNIX) # This includes OSX elseif(WIN32) add_definitions(-DUNICODE -D_UNICODE -DWIN32 -D_SCL_SECURE_NO_WARNINGS) @@ -162,16 +144,15 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR IOS) message("-- Setting clang options") if(ANDROID) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pedantic") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes -Wno-pointer-arith") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pedantic -Wno-attributes -Wno-pointer-arith") elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") set(WARNINGS -Wall -Wextra -Wcast-qual -Wconversion -Wformat=2 -Winit-self -Winvalid-pch -Wmissing-format-attribute -Wmissing-include-dirs -Wpacked -Wredundant-decls) - set(LINUX_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 -Wno-unused-local-typedefs) + set(LINUX_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-unused-local-typedefs) set(WARNINGS ${WARNINGS} ${LINUX_SUPPRESSIONS}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-return-type-c-linkage -Wno-unneeded-internal-declaration") else() 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 -Wno-unused-local-typedefs) + 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-unused-local-typedefs) set(WARNINGS ${WARNINGS} ${OSX_SUPPRESSIONS}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -Wno-return-type-c-linkage -Wno-unneeded-internal-declaration") @@ -197,8 +178,20 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(WARNINGS) set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4264") add_compile_options(/bigobj) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MP") + set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /MP") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MP") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /MP") + + set(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL} /profile /OPT:REF /OPT:ICF") + set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /profile /OPT:REF /OPT:ICF") + if (WINDOWS_STORE OR WINDOWS_PHONE) add_compile_options(/ZW) + else() + if (NOT (MSVC_VERSION LESS 1920)) + add_compile_options(/permissive-) + endif() endif() else() message("-- Unknown compiler, success is doubtful.") @@ -210,6 +203,28 @@ 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) +function(configure_pch target precompile_header precomile_source) # optional additional compile arguments + if(MSVC) + get_target_property(_srcs ${target} SOURCES) + + set(pch_output_filepath_arg) + if(NOT CMAKE_GENERATOR MATCHES "Visual Studio .*") + set_property(SOURCE ${precomile_source} APPEND PROPERTY OBJECT_OUTPUTS "${CMAKE_CURRENT_BINARY_DIR}/${target}.pch") + set_property(SOURCE ${_srcs} APPEND PROPERTY OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${target}.pch") + set(pch_output_filepath_arg "/Fp${CMAKE_CURRENT_BINARY_DIR}/${target}.pch") + else() + # Don't specify output file so that VS may choose a config spefic location. + # Otherwise Debug/Release builds will interfere with one another. + endif() + + set_source_files_properties(${precomile_source} PROPERTIES COMPILE_FLAGS "/Yc${precompile_header}") + target_sources(${target} PRIVATE ${precomile_source}) + # Note: as ${precomile_source} is also a SOURCE for ${target}, the below options will also be applied. + # ${precomile_source} has /Yc option that will cause the shared /Yu to be ignored. + target_compile_options(${target} PRIVATE /Yu${precompile_header} ${pch_output_filepath_arg} ${ARGN}) + endif() +endfunction() + # These settings can be used by the test targets set(Casablanca_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include) set(Casablanca_LIBRARY cpprest) @@ -233,7 +248,7 @@ function(add_casablanca_test NAME SOURCES_VAR) cpprest common_utilities unittestpp - ${ANDROID_STL_FLAGS} + ${ANDROID_LIBS} ) if (BUILD_SHARED_LIBS) add_test(NAME ${NAME} diff --git a/Release/cmake/cpprest_find_boost.cmake b/Release/cmake/cpprest_find_boost.cmake index 298b080aae..3c857bafa0 100644 --- a/Release/cmake/cpprest_find_boost.cmake +++ b/Release/cmake/cpprest_find_boost.cmake @@ -1,22 +1,50 @@ +macro(cpprestsdk_find_boost_android_package) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + if(CMAKE_HOST_WIN32) + set(WIN32 1) + set(UNIX) + elseif(CMAKE_HOST_APPLE) + set(APPLE 1) + set(UNIX) + endif() + find_package(${ARGN}) + set(APPLE) + set(WIN32) + set(UNIX 1) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) +endmacro() + function(cpprest_find_boost) if(TARGET cpprestsdk_boost_internal) return() endif() if(IOS) - set(IOS_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../Build_iOS") - set(Boost_LIBRARIES "${IOS_SOURCE_DIR}/boost.framework/boost" CACHE INTERNAL "") - set(Boost_INCLUDE_DIR "${IOS_SOURCE_DIR}/boost.framework/Headers" CACHE INTERNAL "") + if (EXISTS "${PROJECT_SOURCE_DIR}/../Build_iOS/boost") + set(IOS_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../Build_iOS") + set(Boost_LIBRARIES "${IOS_SOURCE_DIR}/boost/lib" CACHE INTERNAL "") + set(Boost_INCLUDE_DIR "${IOS_SOURCE_DIR}/boost/include" CACHE INTERNAL "") + else() + set(IOS_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../Build_iOS") + set(Boost_LIBRARIES "${IOS_SOURCE_DIR}/boost.framework/boost" CACHE INTERNAL "") + set(Boost_INCLUDE_DIR "${IOS_SOURCE_DIR}/boost.framework/Headers" CACHE INTERNAL "") + endif() elseif(ANDROID) set(Boost_COMPILER "-clang") - if(ARM) - set(BOOST_ROOT "${CMAKE_BINARY_DIR}/../Boost-for-Android/build" CACHE INTERNAL "") - set(BOOST_LIBRARYDIR "${CMAKE_BINARY_DIR}/../Boost-for-Android/build/lib" CACHE INTERNAL "") + if(ANDROID_ABI STREQUAL "armeabi-v7a") + set(BOOST_ROOT "${CMAKE_BINARY_DIR}/../Boost-for-Android/build/out/armeabi-v7a" CACHE INTERNAL "") + set(BOOST_LIBRARYDIR "${CMAKE_BINARY_DIR}/../Boost-for-Android/build/out/armeabi-v7a/lib" CACHE INTERNAL "") + set(Boost_ARCHITECTURE "-a32" CACHE INTERNAL "") else() - set(BOOST_ROOT "${CMAKE_BINARY_DIR}/../Boost-for-Android-x86/build" CACHE INTERNAL "") - set(BOOST_LIBRARYDIR "${CMAKE_BINARY_DIR}/../Boost-for-Android-x86/build/lib" CACHE INTERNAL "") + set(BOOST_ROOT "${CMAKE_BINARY_DIR}/../Boost-for-Android/build/out/x86" CACHE INTERNAL "") + set(BOOST_LIBRARYDIR "${CMAKE_BINARY_DIR}/../Boost-for-Android/build/out/x86/lib" CACHE INTERNAL "") + set(Boost_ARCHITECTURE "-x32" CACHE INTERNAL "") endif() - find_host_package(Boost 1.55 EXACT REQUIRED COMPONENTS random system thread filesystem chrono atomic) + cpprestsdk_find_boost_android_package(Boost ${BOOST_VERSION} EXACT REQUIRED COMPONENTS random system thread filesystem chrono atomic) elseif(UNIX) find_package(Boost REQUIRED COMPONENTS random system thread filesystem chrono atomic date_time regex) else() @@ -42,8 +70,9 @@ function(cpprest_find_boost) endif() set(_prev "${_lib}") endforeach() - target_link_libraries(cpprestsdk_boost_internal INTERFACE "$") - + if (NOT IOS OR NOT EXISTS "${PROJECT_SOURCE_DIR}/../Build_iOS/boost") + target_link_libraries(cpprestsdk_boost_internal INTERFACE "$") + endif() else() if(ANDROID) target_link_libraries(cpprestsdk_boost_internal INTERFACE diff --git a/Release/cmake/cpprest_find_brotli.cmake b/Release/cmake/cpprest_find_brotli.cmake new file mode 100644 index 0000000000..5485d699ec --- /dev/null +++ b/Release/cmake/cpprest_find_brotli.cmake @@ -0,0 +1,19 @@ +function(cpprest_find_brotli) + if(TARGET cpprestsdk_brotli_internal) + return() + endif() + + + find_package(PkgConfig) + pkg_check_modules(BROTLIENC libbrotlienc) + pkg_check_modules(BROTLIDEC libbrotlidec) + if(BROTLIDEC_FOUND AND BROTLIENC_FOUND) + target_link_libraries(cpprest PRIVATE ${BROTLIDEC_LDFLAGS} ${BROTLIENC_LDFLAGS}) + else(BROTLIDEC_FOUND AND BROTLIENC_FOUND) + find_package(unofficial-brotli REQUIRED) + add_library(cpprestsdk_brotli_internal INTERFACE) + target_link_libraries(cpprestsdk_brotli_internal INTERFACE unofficial::brotli::brotlienc unofficial::brotli::brotlidec unofficial::brotli::brotlicommon) + target_link_libraries(cpprest PRIVATE cpprestsdk_brotli_internal) + endif(BROTLIDEC_FOUND AND BROTLIENC_FOUND) + +endfunction() diff --git a/Release/cmake/cpprest_find_websocketpp.cmake b/Release/cmake/cpprest_find_websocketpp.cmake index 260311f0a3..94ea81a473 100644 --- a/Release/cmake/cpprest_find_websocketpp.cmake +++ b/Release/cmake/cpprest_find_websocketpp.cmake @@ -7,9 +7,11 @@ function(cpprest_find_websocketpp) if(WEBSOCKETPP_FOUND) message("-- Found websocketpp version " ${WEBSOCKETPP_VERSION} " on system") set(WEBSOCKETPP_INCLUDE_DIR ${WEBSOCKETPP_INCLUDE_DIR} CACHE INTERNAL "") - else() + elseif(EXISTS ${PROJECT_SOURCE_DIR}/libs/websocketpp/CMakeLists.txt) message("-- websocketpp not found, using the embedded version") set(WEBSOCKETPP_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/libs/websocketpp CACHE INTERNAL "") + else() + message(FATAL_ERROR "-- websocketpp not found and embedded version not present; try `git submodule update --init` and run CMake again") endif() cpprest_find_boost() @@ -22,4 +24,4 @@ function(cpprest_find_websocketpp) cpprestsdk_boost_internal cpprestsdk_openssl_internal ) -endfunction() \ No newline at end of file +endfunction() diff --git a/Release/cmake/cpprest_find_winhttppal.cmake b/Release/cmake/cpprest_find_winhttppal.cmake new file mode 100644 index 0000000000..9a6840fba2 --- /dev/null +++ b/Release/cmake/cpprest_find_winhttppal.cmake @@ -0,0 +1,17 @@ +function(cpprest_find_winhttppal) + if(TARGET cpprestsdk_winhttppal_internal) + return() + endif() + + if(NOT WINHTTPPAL_LIBRARY OR NOT WINHTTPPAL_INCLUDE_DIRS) + find_package(winhttppal REQUIRED) + endif() + + add_library(cpprestsdk_winhttppal_internal INTERFACE) + if(TARGET winhttppal::winhttppal) + target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE winhttppal::winhttppal) + else() + target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE "$") + target_include_directories(cpprestsdk_winhttppal_internal INTERFACE "$") + endif() +endfunction() diff --git a/Release/cmake/cpprestsdk-config.in.cmake b/Release/cmake/cpprestsdk-config.in.cmake index 522f8f7db1..be95abf99a 100644 --- a/Release/cmake/cpprestsdk-config.in.cmake +++ b/Release/cmake/cpprestsdk-config.in.cmake @@ -3,10 +3,18 @@ if(@CPPREST_USES_ZLIB@) find_dependency(ZLIB) endif() +if(@CPPREST_USES_BROTLI@) + find_dependency(unofficial-brotli) +endif() + if(@CPPREST_USES_OPENSSL@) find_dependency(OpenSSL) endif() +if(@CPPREST_USES_WINHTTPPAL@) + find_dependency(WINHTTPPAL) +endif() + if(@CPPREST_USES_BOOST@ AND OFF) if(UNIX) find_dependency(Boost COMPONENTS random system thread filesystem chrono atomic date_time regex) diff --git a/Release/include/cpprest/astreambuf.h b/Release/include/cpprest/astreambuf.h index baff9a934d..1dcb285ded 100644 --- a/Release/include/cpprest/astreambuf.h +++ b/Release/include/cpprest/astreambuf.h @@ -1,26 +1,26 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* 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: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * 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: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#include -#include +#include "cpprest/asyncrt_utils.h" +#include "cpprest/details/basic_types.h" +#include "pplx/pplxtasks.h" +#include #include +#include #include - -#include "pplx/pplxtasks.h" -#include "cpprest/details/basic_types.h" -#include "cpprest/asyncrt_utils.h" +#include #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX namespace Concurrency // since namespace pplx = Concurrency @@ -28,1185 +28,1153 @@ namespace Concurrency // since namespace pplx = Concurrency namespace pplx #endif { - namespace details - { - template - pplx::task _do_while(F func) - { - pplx::task first = func(); - return first.then([=](bool guard) -> pplx::task { - if (guard) - return pplx::details::_do_while(func); - else - return first; - }); - } - } +namespace details +{ +template +pplx::task _do_while(F func) +{ + pplx::task first = func(); + return first.then([=](bool guard) -> pplx::task { + if (guard) + return pplx::details::_do_while(func); + else + return first; + }); +} +} // namespace details } 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> { /// - /// Extending the standard char_traits type with one that adds values and types - /// that are unique to "C++ REST SDK" streams. + /// Some synchronous functions will return this value if the operation + /// requires an asynchronous call in a given situation. /// - /// - /// The data type of the basic element of the stream. - /// - template - struct char_traits : std::char_traits<_CharType> + /// An int_type value which implies that an asynchronous call is required. + static typename std::char_traits<_CharType>::int_type requires_async() { - /// - /// 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; } - }; + return std::char_traits<_CharType>::eof() - 1; + } +}; #if !defined(_WIN32) - template<> - struct char_traits : private std::char_traits - { - public: - typedef unsigned char char_type; +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; + 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 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 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* 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 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; } - }; + static int_type requires_async() { return eof() - 1; } +}; #endif - namespace details { +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; /// - /// Stream buffer base class. + /// Virtual constructor for stream buffers. /// - 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; - } + virtual ~basic_streambuf() {} - /// - /// 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; - } + /// + /// can_read is used to determine whether a stream buffer will support read operations (get). + /// + virtual bool can_read() 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 - { - return can_read() || can_write(); - } + /// + /// can_write is used to determine whether a stream buffer will support write operations (put). + /// + virtual bool can_write() 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) - { - pplx::task closeOp = pplx::task_from_result(); + /// + /// can_seek is used to determine whether a stream buffer supports seeking. + /// + virtual bool can_seek() const = 0; - if (mode & std::ios_base::in && can_read()) { - closeOp = _close_read(); - } + /// + /// has_size is used to determine whether a stream buffer supports size(). + /// + virtual bool has_size() const = 0; - // 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()); + /// + /// is_eof is used to determine whether a read head has reached the end of the buffer. + /// + virtual bool is_eof() const = 0; - 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]{}); }); - } + /// + /// 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; - return closeOp; - } + /// + /// 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; - /// - /// 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); - } + /// + /// 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; - /// - /// 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; - } + /// + /// 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; - /// - /// 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()); + /// + /// 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; - return create_exception_checked_task(_putc(ch), [](int_type) { - return false; // no EOF for write - }); - } + /// + /// 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 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 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. 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 - }); - } + /// + /// 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; - /// - /// 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()); + /// + /// 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; - 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. + /// + /// 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() - { - 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 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. - /// - /// 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()); + /// + /// 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; - 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() = 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() - { - 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. + /// + /// A task that holds the value of the character. This value is EOF if the read fails. + virtual pplx::task nextc() = 0; - /// - /// 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()); + /// + /// 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; - return create_exception_checked_task(_nextc(), [](int_type val) { - return val == streambuf_state_manager<_CharType>::traits::eof(); - }); - } + /// + /// 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; - /// - /// 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()); + /// + /// 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; - return create_exception_checked_task(_ungetc(), [](int_type) { - return false; - }); - } + /// + /// 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; - /// - /// 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; - }); - } + /// + /// 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; - /// - /// 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; + /// + /// 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; - return _scopy(ptr, count); - } + /// + /// 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. - /// - /// 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){}); - } + /// + /// 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) + // - /// - /// 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. + virtual _CharType* alloc(_In_ size_t count) = 0; - /// - /// 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"); + /// + /// Submits a block already allocated by the stream buffer. + /// + /// The number of characters to be committed. + virtual void commit(_In_ size_t count) = 0; - _CharType* alloc_result = _alloc(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) = 0; - if (alloc_result) - m_alloced = 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_(count) _CharType* ptr, _In_ size_t count) = 0; - return alloc_result; - } + /// + /// 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; +}; - /// - /// 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"); +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; - _commit(count); - m_alloced = false; - } + /// + /// 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; } - 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(); - } + /// + /// 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(); } - /// - /// The real write head close operation, implementation should override it if there is any resource to be released. - /// - virtual pplx::task _close_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()) { - m_stream_can_write = false; - return pplx::task_from_result(); + closeOp = _close_read(); } - protected: - streambuf_state_manager(std::ios_base::openmode mode) + // 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()) { - 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; + 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] {}); }); } - 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; + 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()); + } - private: - template - pplx::task<_CharType1> create_exception_checked_value_task(const _CharType1 &val) const + /// + /// 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 (this->exception() == nullptr) - return pplx::task_from_result<_CharType1>(static_cast<_CharType1>(val)); + if (m_currentException == nullptr) + return pplx::task_from_result(); else - return pplx::task_from_exception<_CharType1>(this->exception()); + return pplx::task_from_exception(m_currentException); } + return create_exception_checked_task(_sync(), [](bool) { return false; }).then([](bool) {}); + } - // 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() ) + /// + /// 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 + std::atomic m_stream_can_read; + std::atomic m_stream_can_write; + std::atomic m_stream_read_eof; + std::atomic 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 { - // If the data is already available, we should avoid scheduling a continuation, so we do it inline. - return func1(result); + thisPointer->m_stream_read_eof = eof_test(t1.get()); } - else + catch (...) { - return result.then(func1); + 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; + }; - // Set eof states for sync read - int_type check_sync_read_eof(int_type ch) + 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 { - m_stream_read_eof = ch == traits::eof(); - return ch; + 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; - } // namespace details + template + friend class streambuf; - // Forward declarations - template class basic_istream; - template class basic_ostream; + /// + /// Constructor. + /// + /// A pointer to the concrete stream buffer implementation. + streambuf(_In_ const std::shared_ptr>& ptr) : m_buffer(ptr) {} /// - /// Reference-counted stream buffer. + /// Default constructor. /// - /// - /// The data type of the basic element of the streambuf. - /// - /// - /// The data type of the basic element of the streambuf. + streambuf() {} + + /// + /// Converter Constructor. + /// + /// + /// The data type of the basic element of the source streambuf. /// - template - class streambuf : public details::basic_streambuf<_CharType> + /// The source buffer to be converted. + template + streambuf(const streambuf& other) + : m_buffer(std::static_pointer_cast>( + std::static_pointer_cast(other.m_buffer))) { - 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"); - } + 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 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() {} - /// - /// Constructs an output stream for this stream buffer. - /// - /// basic_ostream - concurrency::streams::basic_ostream<_CharType> create_ostream() const + const std::shared_ptr>& get_base() const + { + if (!m_buffer) { - if (!can_write()) throw std::runtime_error("stream buffer not set up for output of data"); - return concurrency::streams::basic_ostream<_CharType>(*this); + throw std::invalid_argument("Invalid streambuf object"); } - /// - /// Checks if the stream buffer has been initialized or not. - /// - operator bool() const { return (bool)m_buffer; } + return m_buffer; + } - /// - /// Destructor - /// - virtual ~streambuf() { } + /// + /// 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(); } - const std::shared_ptr> & get_base() const - { - if (!m_buffer) - { - throw std::invalid_argument("Invalid streambuf object"); - } + /// + /// 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(); } - return m_buffer; - } + /// + /// 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(); } - /// - /// 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(); - } + /// + /// 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(); } - /// - /// 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(); - } + /// + /// 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(); } - /// - /// 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); - } + /// + /// 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); + } - /// - /// 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); - } + /// + /// 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); + } - /// - /// 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); - } + /// + /// 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(); } - /// - /// 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); - } + /// + /// 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(); } - /// - /// 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); - } + /// + /// 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(); } - /// - /// 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); - } + /// + /// 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(); + } - /// - /// 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); - } + /// + /// 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(); + } - /// - /// 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(); - } + /// + /// 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); } - /// - /// 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(); - } + /// + /// 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); } - /// - /// 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(); - } + /// + /// 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); } - /// - /// 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(); - } + /// + /// 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); + } - /// - /// 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(); - } + /// + /// 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); } - /// - /// 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(); - } + /// + /// 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); } - /// - /// 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); - } + /// + /// 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); + } - /// - /// 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); - } + /// + /// 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(); } - /// - /// 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); - } + /// + /// 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(); } - /// - /// 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); - } + /// + /// 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(); } - /// - /// 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); - } + /// + /// 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(); } - /// - /// 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(); - } + /// + /// 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(); } - /// - /// 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(); - } + /// + /// 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(); } - private: - std::shared_ptr> m_buffer; + /// + /// 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; +}; +} // namespace streams +} // namespace Concurrency diff --git a/Release/include/cpprest/asyncrt_utils.h b/Release/include/cpprest/asyncrt_utils.h index 68aa738be0..4166eb8c48 100644 --- a/Release/include/cpprest/asyncrt_utils.h +++ b/Release/include/cpprest/asyncrt_utils.h @@ -1,34 +1,31 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Various common utilities. -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Various common utilities. + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #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 "pplx/pplxtasks.h" #include -#endif +#include +#include +#include +#include +#include +#include +#include #ifndef _WIN32 #include -#include #if !defined(ANDROID) && !defined(__ANDROID__) && defined(HAVE_XLOCALE_H) // CodePlex 269 /* Systems using glibc: xlocale.h has been removed from glibc 2.26 The above include of locale.h is sufficient @@ -40,372 +37,464 @@ /// 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 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); -} +/// +/// 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& timespanString); +} // namespace timespan /// 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-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 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 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-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 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. +/// +/// Converts to a platform dependent Unicode string type. +/// +/// A single byte character UTF-8 string. +/// A platform dependent string type. #ifdef _UTF16_STRINGS - _ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string &&s); +_ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string&& s); #else - inline utility::string_t&& to_string_t(std::string &&s) { return std::move(s); } +inline utility::string_t&& to_string_t(std::string&& s) { return std::move(s); } #endif - /// - /// Converts to a platform dependent Unicode string type. - /// - /// A two byte character UTF-16 string. - /// A platform dependent string type. +/// +/// Converts to a platform dependent Unicode string type. +/// +/// A two byte character UTF-16 string. +/// A platform dependent string type. #ifdef _UTF16_STRINGS - inline utility::string_t&& to_string_t(utf16string &&s) { return std::move(s); } +inline utility::string_t&& to_string_t(utf16string&& s) { return std::move(s); } #else - _ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string &&s); +_ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string&& s); #endif - /// - /// Converts to a platform dependent Unicode string type. - /// - /// A single byte character UTF-8 string. - /// A platform dependent string type. +/// +/// Converts to a platform dependent Unicode string type. +/// +/// A single byte character UTF-8 string. +/// A platform dependent string type. #ifdef _UTF16_STRINGS - _ASYNCRTIMP utility::string_t __cdecl to_string_t(const std::string &s); +_ASYNCRTIMP utility::string_t __cdecl to_string_t(const std::string& s); #else - inline const utility::string_t& to_string_t(const std::string &s) { return s; } +inline const utility::string_t& to_string_t(const std::string& s) { return s; } #endif - /// - /// Converts to a platform dependent Unicode string type. - /// - /// A two byte character UTF-16 string. - /// A platform dependent string type. +/// +/// Converts to a platform dependent Unicode string type. +/// +/// A two byte character UTF-16 string. +/// A platform dependent string type. #ifdef _UTF16_STRINGS - inline const utility::string_t& to_string_t(const utf16string &s) { return s; } +inline const utility::string_t& to_string_t(const utf16string& s) { return s; } #else - _ASYNCRTIMP utility::string_t __cdecl to_string_t(const utf16string &s); +_ASYNCRTIMP utility::string_t __cdecl to_string_t(const utf16string& s); #endif - /// - /// 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. - inline const utf16string& to_utf16string(const utf16string& value) - { - return value; - } - /// - /// Converts to a UTF-16 from string. - /// - /// A two byte character UTF-16 string. - /// A two byte character UTF-16 string. - inline utf16string&& to_utf16string(utf16string&& value) - { - return std::move(value); - } +/// +/// 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-8 string. - /// - /// A single byte character UTF-8 string. - /// A single byte character UTF-8 string. - inline std::string&& to_utf8string(std::string&& value) { return std::move(value); } +/// +/// Converts to a UTF-16 from string. +/// +/// A two byte character UTF-16 string. +/// A two byte character UTF-16 string. +inline const utf16string& to_utf16string(const utf16string& value) { return value; } +/// +/// Converts to a UTF-16 from string. +/// +/// A two byte character UTF-16 string. +/// A two byte character UTF-16 string. +inline utf16string&& to_utf16string(utf16string&& value) { return std::move(value); } - /// - /// Converts to a UTF-8 string. - /// - /// A single byte character UTF-8 string. - /// A single byte character UTF-8 string. - inline const std::string& to_utf8string(const std::string& value) { return value; } +/// +/// Converts to a UTF-8 string. +/// +/// A single byte character UTF-8 string. +/// A single byte character UTF-8 string. +inline std::string&& to_utf8string(std::string&& value) { return std::move(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); +/// +/// Converts to a UTF-8 string. +/// +/// A single byte character UTF-8 string. +/// A single byte character UTF-8 string. +inline const std::string& to_utf8string(const std::string& value) { return value; } - /// - /// Encode the given byte array into a base64 string - /// - _ASYNCRTIMP utility::string_t __cdecl to_base64(const std::vector& data); +/// +/// 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 8-byte integer into a base64 string - /// - _ASYNCRTIMP utility::string_t __cdecl to_base64(uint64_t data); +/// +/// Encode the given byte array into a base64 string +/// +_ASYNCRTIMP utility::string_t __cdecl to_base64(const std::vector& data); - /// - /// Decode the given base64 string to a byte array - /// - _ASYNCRTIMP std::vector __cdecl from_base64(const utility::string_t& str); +/// +/// Encode the given 8-byte integer into a base64 string +/// +_ASYNCRTIMP utility::string_t __cdecl to_base64(uint64_t data); - template - CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if locale support is required.") - utility::string_t print_string(const Source &val, const std::locale& loc = std::locale()) - { - utility::ostringstream_t oss; - oss.imbue(loc); - oss << val; - if (oss.bad()) - { - throw std::bad_cast(); - } - return oss.str(); - } +/// +/// Decode the given base64 string to a byte array +/// +_ASYNCRTIMP std::vector __cdecl from_base64(const utility::string_t& str); - CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if locale support is required.") - inline utility::string_t print_string(const utility::string_t &val) +template +CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " + "locale support is required.") +utility::string_t print_string(const Source& val, const std::locale& loc = std::locale()) +{ + utility::ostringstream_t oss; + oss.imbue(loc); + oss << val; + if (oss.bad()) { - return val; + throw std::bad_cast(); } + return oss.str(); +} - namespace details - { +CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " + "locale support is required.") +inline utility::string_t print_string(const utility::string_t& val) { return val; } +namespace details +{ #if defined(__ANDROID__) - template - inline std::string to_string(const T& t) - { - std::ostringstream os; - os.imbue(std::locale::classic()); - os << t; - return os.str(); - } +template +inline std::string to_string(const T t) +{ + std::ostringstream os; + os.imbue(std::locale::classic()); + os << t; + return os.str(); +} #endif - template - inline utility::string_t to_string_t(T&& t) - { +template +inline utility::string_t to_string_t(const T t) +{ #ifdef _UTF16_STRINGS - using std::to_wstring; - return to_wstring(std::forward(t)); + using std::to_wstring; + return to_wstring(t); #else #if !defined(__ANDROID__) - using std::to_string; + using std::to_string; #endif - return to_string(std::forward(t)); + return to_string(t); #endif - } - - template - utility::string_t print_string(const Source &val) - { - utility::ostringstream_t oss; - oss.imbue(std::locale::classic()); - oss << val; - if (oss.bad()) - { - throw std::bad_cast(); - } - return oss.str(); - } - - inline const utility::string_t& print_string(const utility::string_t &val) - { - return val; - } +} - template - utf8string print_utf8string(const Source& val) - { - return conversions::to_utf8string(print_string(val)); - } - inline const utf8string& print_utf8string(const utf8string& val) - { - return val; - } +template +utility::string_t print_string(const Source& val) +{ + utility::ostringstream_t oss; + oss.imbue(std::locale::classic()); + oss << val; + if (oss.bad()) + { + throw std::bad_cast(); + } + return oss.str(); +} - template - Target scan_string(const utility::string_t &str) - { - Target t; - utility::istringstream_t iss(str); - iss.imbue(std::locale::classic()); - iss >> t; - if (iss.bad()) - { - throw std::bad_cast(); - } - return t; - } +inline const utility::string_t& print_string(const utility::string_t& val) { return val; } - inline const utility::string_t& scan_string(const utility::string_t &str) - { - return str; - } - } +template +utf8string print_utf8string(const Source& val) +{ + return conversions::to_utf8string(print_string(val)); +} +inline const utf8string& print_utf8string(const utf8string& val) { return val; } - template - CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if locale support is required.") - Target scan_string(const utility::string_t &str, const std::locale &loc = std::locale()) +template +Target scan_string(const utility::string_t& str) +{ + Target t; + utility::istringstream_t iss(str); + iss.imbue(std::locale::classic()); + iss >> t; + if (iss.bad()) { - Target t; - utility::istringstream_t iss(str); - iss.imbue(loc); - iss >> t; - if (iss.bad()) - { - throw std::bad_cast(); - } - return t; + throw std::bad_cast(); } + return t; +} + +inline const utility::string_t& scan_string(const utility::string_t& str) { return str; } +} // namespace details - CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if locale support is required.") - inline utility::string_t scan_string(const utility::string_t &str) +template +CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " + "locale support is required.") +Target scan_string(const utility::string_t& str, const std::locale& loc = std::locale()) +{ + Target t; + utility::istringstream_t iss(str); + iss.imbue(loc); + iss >> t; + if (iss.bad()) { - return str; + throw std::bad_cast(); } + return t; } +CASABLANCA_DEPRECATED("All locale-sensitive APIs will be removed in a future update. Use stringstreams directly if " + "locale support is required.") +inline utility::string_t scan_string(const utility::string_t& str) { return str; } +} // namespace conversions + 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(); +/// +/// 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; + typedef _locale_t xplat_locale; #else - typedef locale_t xplat_locale; + typedef locale_t xplat_locale; #endif - static _ASYNCRTIMP xplat_locale __cdecl c_locale(); + static _ASYNCRTIMP xplat_locale __cdecl c_locale(); #endif - private: +private: #ifdef _WIN32 - std::string m_prevLocale; - int m_prevThreadSetting; + std::string m_prevLocale; + int m_prevThreadSetting; #elif !(defined(ANDROID) || defined(__ANDROID__)) - locale_t m_prevLocale; + locale_t m_prevLocale; #endif - scoped_c_thread_locale(const scoped_c_thread_locale &); - scoped_c_thread_locale & operator=(const scoped_c_thread_locale &); + 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(const unsigned char uch) CPPREST_NOEXCEPT +{ // test if uch is an alnum character + // special casing char to avoid branches + // clang-format off + static CPPREST_CONSTEXPR bool is_alnum_table[UCHAR_MAX + 1] = { + /* X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */ + /* 0X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 1X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 2X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 3X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0-9 */ + /* 4X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A-Z */ + /* 5X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + /* 6X */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a-z */ + /* 7X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 + /* non-ASCII values initialized to 0 */ }; + // clang-format on + return (is_alnum_table[uch]); +} - /// - /// 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'); - } +/// +/// Our own implementation of alpha numeric instead of std::isalnum to avoid +/// taking global lock for performance reasons. +/// +inline bool __cdecl is_alnum(const char ch) CPPREST_NOEXCEPT { return (is_alnum(static_cast(ch))); } - /// - /// 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()); - } +/// +/// Our own implementation of alpha numeric instead of std::isalnum to avoid +/// taking global lock for performance reasons. +/// +template +inline bool __cdecl is_alnum(Elem ch) CPPREST_NOEXCEPT +{ + // assumes 'x' == L'x' for the ASCII range + typedef typename std::make_unsigned::type UElem; + const auto uch = static_cast(ch); + return (uch <= static_cast('z') && is_alnum(static_cast(uch))); +} - template - std::unique_ptr<_Type> make_unique(_Arg1&& arg1) { - return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1))); - } +/// +/// Our own implementation of whitespace test instead of std::isspace to avoid +/// taking global lock for performance reasons. +/// The following characters are considered whitespace: +/// 0x09 == Horizontal Tab +/// 0x0A == Line Feed +/// 0x0B == Vertical Tab +/// 0x0C == Form Feed +/// 0x0D == Carrage Return +/// 0x20 == Space +/// +template +inline bool __cdecl is_space(Elem ch) CPPREST_NOEXCEPT +{ + // assumes 'x' == L'x' for the ASCII range + typedef typename std::make_unsigned::type UElem; + const auto uch = static_cast(ch); + return uch == 0x20u || (uch >= 0x09u && uch <= 0x0Du); +} - 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))); - } +/// +/// 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, _Arg2&& arg2, _Arg3&& arg3) { - return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3))); - } +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, _Arg3&& arg3, _Arg4&& arg4) { - return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4))); - } +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))); +} - /// - /// 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 - } +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))); +} + +template +std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4) +{ + return std::unique_ptr<_Type>(new _Type( + std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4))); +} + +template +std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5) +{ + return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), + std::forward<_Arg2>(arg2), + std::forward<_Arg3>(arg3), + std::forward<_Arg4>(arg4), + std::forward<_Arg5>(arg5))); +} + +template +std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5, _Arg6&& arg6) +{ + return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), + std::forward<_Arg2>(arg2), + std::forward<_Arg3>(arg3), + std::forward<_Arg4>(arg4), + std::forward<_Arg5>(arg5), + std::forward<_Arg6>(arg6))); +} + +/// +/// Cross platform utility function for performing case insensitive string equality comparison. +/// +/// First string to compare. +/// Second strong to compare. +/// true if the strings are equivalent, false otherwise +_ASYNCRTIMP bool __cdecl str_iequal(const std::string& left, const std::string& right) CPPREST_NOEXCEPT; + +/// +/// Cross platform utility function for performing case insensitive string equality comparison. +/// +/// First string to compare. +/// Second strong to compare. +/// true if the strings are equivalent, false otherwise +_ASYNCRTIMP bool __cdecl str_iequal(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT; + +/// +/// Cross platform utility function for performing case insensitive string less-than comparison. +/// +/// First string to compare. +/// Second strong to compare. +/// true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise, +/// false. +_ASYNCRTIMP bool __cdecl str_iless(const std::string& left, const std::string& right) CPPREST_NOEXCEPT; + +/// +/// Cross platform utility function for performing case insensitive string less-than comparison. +/// +/// First string to compare. +/// Second strong to compare. +/// true if a lowercase view of left is lexicographically less than a lowercase view of right; otherwise, +/// false. +_ASYNCRTIMP bool __cdecl str_iless(const std::wstring& left, const std::wstring& right) CPPREST_NOEXCEPT; + +/// +/// Convert a string to lowercase in place. +/// +/// The string to convert to lowercase. +_ASYNCRTIMP void __cdecl inplace_tolower(std::string& target) CPPREST_NOEXCEPT; + +/// +/// Convert a string to lowercase in place. +/// +/// The string to convert to lowercase. +_ASYNCRTIMP void __cdecl inplace_tolower(std::wstring& target) CPPREST_NOEXCEPT; #ifdef _WIN32 @@ -415,7 +504,7 @@ namespace details class windows_category_impl : public std::error_category { public: - virtual const char *name() const CPPREST_NOEXCEPT { return "windows"; } + virtual const char* name() const CPPREST_NOEXCEPT { return "windows"; } virtual std::string message(int errorCode) const CPPREST_NOEXCEPT; @@ -426,7 +515,7 @@ class windows_category_impl : public std::error_category /// Gets the one global instance of the windows error category. /// /// An error category instance. -_ASYNCRTIMP const std::error_category & __cdecl windows_category(); +_ASYNCRTIMP const std::error_category& __cdecl windows_category(); #else @@ -434,14 +523,14 @@ _ASYNCRTIMP const std::error_category & __cdecl windows_category(); /// Gets the one global instance of the linux error category. /// /// An error category instance. -_ASYNCRTIMP const std::error_category & __cdecl linux_category(); +_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(); +/// +_ASYNCRTIMP const std::error_category& __cdecl platform_category(); /// /// Creates an instance of std::system_error from a OS error code. @@ -468,7 +557,7 @@ inline utility::string_t __cdecl create_error_message(unsigned long errorCode) return utility::conversions::to_string_t(create_error_code(errorCode).message()); } -} +} // namespace details class datetime { @@ -478,7 +567,11 @@ class datetime /// /// Defines the supported date and time string formats. /// - enum date_format { RFC_1123, ISO_8601 }; + enum date_format + { + RFC_1123, + ISO_8601 + }; /// /// Returns the current UTC time. @@ -488,7 +581,10 @@ class datetime /// /// An invalid UTC timestamp value. /// - enum:interval_type { utc_timestamp_invalid = static_cast(-1) }; + enum : interval_type + { + utc_timestamp_invalid = static_cast(-1) + }; /// /// Returns seconds since Unix/POSIX time epoch at 01-01-1970 00:00:00. @@ -507,9 +603,7 @@ class datetime } } - datetime() : m_interval(0) - { - } + datetime() : m_interval(0) {} /// /// Creates datetime from a string representing time in UTC in RFC 1123 format. @@ -525,124 +619,45 @@ class datetime /// /// Returns the integral time value. /// - interval_type to_interval() const - { - return m_interval; - } + 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); } - 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==(datetime dt) const { return m_interval == dt.m_interval; } - bool operator!= (const datetime& dt) const - { - return !(*this == dt); - } + bool operator!=(const datetime& dt) const { return !(*this == dt); } - static interval_type from_milliseconds(unsigned int milliseconds) - { - return milliseconds*_msTicks; - } + 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_seconds(unsigned int seconds) { return seconds * _secondTicks; } - static interval_type from_minutes(unsigned int minutes) - { - return minutes*_minuteTicks; - } + 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_hours(unsigned int hours) { return hours * _hourTicks; } - static interval_type from_days(unsigned int days) - { - return days*_dayTicks; - } + static interval_type from_days(unsigned int days) { return days * _dayTicks; } - bool is_initialized() const - { - return m_interval != 0; - } + bool is_initialized() const { return m_interval != 0; } private: - - friend int operator- (datetime t1, datetime t2); + 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 system_type_to_datetime(/*SYSTEMTIME*/ void* psysTime, uint64_t seconds, datetime * pdt); -#else - static datetime timeval_to_datetime(const timeval &time); -#endif + 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; // Private constructor. Use static methods to create an instance. - datetime(interval_type interval) : m_interval(interval) - { - } + 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) +inline int operator-(datetime t1, datetime t2) { auto diff = (t1.m_interval - t2.m_interval); @@ -658,20 +673,22 @@ inline int operator- (datetime t1, datetime t2) class nonce_generator { public: - /// /// Define default nonce length. /// - enum { default_length = 32 }; + 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) - {} + 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). @@ -693,9 +710,8 @@ class nonce_generator 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; +} // namespace utility diff --git a/Release/include/cpprest/base_uri.h b/Release/include/cpprest/base_uri.h index b5fc8fcfd0..7c6943119c 100644 --- a/Release/include/cpprest/base_uri.h +++ b/Release/include/cpprest/base_uri.h @@ -1,416 +1,391 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Protocol independent support for URIs. -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Protocol independent support for URIs. + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once +#include "cpprest/asyncrt_utils.h" +#include "cpprest/details/basic_types.h" #include -#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&) = default; + uri_components& operator=(const uri_components&) = default; + + // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped. + 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) + { + } -namespace web { - namespace details + // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped. + uri_components& operator=(uri_components&& other) CPPREST_NOEXCEPT { - struct uri_components + if (this != &other) { - uri_components() : m_path(_XPLATSTR("/")), m_port(-1) {} - - uri_components(const uri_components &other) = default; - uri_components & operator=(const uri_components &other) = default; - - // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped. - 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) - {} - - // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped. - 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; - }; + 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; +}; +} // namespace details + +/// +/// 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 empty 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: /// - /// A single exception type to represent errors in parsing, encoding, and decoding URIs. + /// 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 uri_exception : public std::exception + class components { public: + enum component + { + user_info, + host, + path, + query, + fragment, + full_uri + }; + }; - uri_exception(std::string msg) : m_msg(std::move(msg)) {} + /// + /// 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); - ~uri_exception() CPPREST_NOEXCEPT {} + /// + /// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their + /// hexadecimal representation. + /// + /// The encoded string. + _ASYNCRTIMP static utility::string_t __cdecl encode_data_string(const utility::string_t& data); - const char* what() const CPPREST_NOEXCEPT - { - return m_msg.c_str(); - } + /// + /// Decodes an encoded string. + /// + /// The URI as a string. + /// The decoded string. + _ASYNCRTIMP static utility::string_t __cdecl decode(const utility::string_t& encoded); - private: - std::string m_msg; - }; + /// + /// 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); /// - /// 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. + /// 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. /// /// - /// 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. + /// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query'). /// - class uri - { - public: + /// 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); - /// - /// 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 - }; - }; + /// + /// Creates an empty uri + /// + uri() : m_uri(_XPLATSTR("/")) {} - /// - /// 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 encoded string. - _ASYNCRTIMP static utility::string_t __cdecl encode_data_string(const utility::string_t &data); - - /// - /// 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. - /// - /// - /// This function accepts both uris ('http://msn.com') and uri relative-references ('path1/path2?query'). - /// - /// 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) = default; - - /// - /// Copy assignment operator. - /// - uri & operator=(const uri &other) = default; - - /// - /// Move constructor. - /// - // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped. - uri(uri &&other) CPPREST_NOEXCEPT : - m_uri(std::move(other.m_uri)), - m_components(std::move(other.m_components)) - {} - - /// - /// Move assignment operator - /// - // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped. - 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; - } + /// + /// 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); - /// - /// 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("/"); - } + /// + /// 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); - /// - /// 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."))); - } + /// + /// Copy constructor. + /// + uri(const uri&) = default; - /// - /// 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("+")); - } + /// + /// Copy assignment operator. + /// + uri& operator=(const uri&) = default; - /// - /// 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()); - } + /// + /// Move constructor. + /// + // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped. + uri(uri&& other) CPPREST_NOEXCEPT : m_uri(std::move(other.m_uri)), m_components(std::move(other.m_components)) {} - /// - /// 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 + /// + /// Move assignment operator + /// + // This is for VS2013 compatibility -- replace with '= default' when VS2013 is completely dropped. + uri& operator=(uri&& other) CPPREST_NOEXCEPT + { + if (this != &other) { - return !is_empty() && this->port() == 0; + m_uri = std::move(other.m_uri); + m_components = std::move(other.m_components); } + return *this; + } - /// - /// 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(); - } + /// + /// 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; } - /// - /// 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(); - } + /// + /// 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; } - /// - /// 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("/"); - } + /// + /// 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; } - /// - /// Returns the full (encoded) URI as a string. - /// - /// The full encoded URI string. - utility::string_t to_string() const - { - return m_uri; - } + /// + /// 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; } - _ASYNCRTIMP bool operator == (const uri &other) const; + /// + /// 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; } - bool operator < (const uri &other) const - { - return m_uri < other.m_uri; - } + /// + /// 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; } - bool operator != (const uri &other) const - { - return !(this->operator == (other)); - } + /// + /// 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; } - private: - friend class uri_builder; + /// + /// 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; - /// - /// Creates a URI from the given URI components. - /// - /// A URI components object to create the URI instance. - _ASYNCRTIMP uri(const details::uri_components &components); + /// + /// 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; - // Used by uri_builder - static utility::string_t __cdecl encode_query_impl(const utf8string& raw); + /// + /// 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("/"); } - utility::string_t m_uri; - details::uri_components m_components; - }; + /// + /// A loopback URI is one which refers to a hostname or ip address with meaning only on the local machine. + /// + /// + /// Examples include "localhost", 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; } + + /// + /// Returns an URI resolved against this as the base URI + /// according to RFC3986, Section 5 (https://tools.ietf.org/html/rfc3986#section-5). + /// + /// The relative URI to be resolved against this as base. + /// The new resolved URI string. + _ASYNCRTIMP utility::string_t resolve_uri(const utility::string_t& relativeUri) const; + + _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; + + /// + /// Creates a URI from the given URI components. + /// + /// A URI components object to create the URI instance. + _ASYNCRTIMP uri(const details::uri_components& components); + + // Used by uri_builder + static utility::string_t __cdecl encode_query_impl(const utf8string& raw); + + utility::string_t m_uri; + details::uri_components m_components; +}; } // namespace web diff --git a/Release/include/cpprest/containerstream.h b/Release/include/cpprest/containerstream.h index 63ead19f9b..6e949a75e0 100644 --- a/Release/include/cpprest/containerstream.h +++ b/Release/include/cpprest/containerstream.h @@ -1,620 +1,590 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* 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: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * 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: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#include -#include -#include -#include - -#include "pplx/pplxtasks.h" #include "cpprest/astreambuf.h" #include "cpprest/streams.h" +#include "pplx/pplxtasks.h" +#include +#include +#include +#include -namespace Concurrency { namespace streams { +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; - // Forward declarations + /// + /// Returns the underlying data container + /// + _CollectionType& collection() { return m_data; } - template class container_buffer; + /// + /// 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(); + } - namespace details { +protected: + /// + /// can_seek is used to determine whether a stream buffer supports seeking. + /// + virtual bool can_seek() const { return this->is_open(); } /// - /// 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. + /// has_size is used to determine whether a stream buffer supports size(). /// - /// 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; - } + virtual bool has_size() const { return this->is_open(); } - /// - /// 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(); - } + /// + /// 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; } - protected: - /// - /// can_seek is used to determine whether a stream buffer supports seeking. - /// - virtual bool can_seek() const { return this->is_open(); } + /// + /// 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; } - /// - /// has_size is used to determine whether a stream buffer supports size(). - /// - virtual bool has_size() const { return this->is_open(); } + /// + /// 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()); - /// - /// 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()); - } + msl::safeint3::SafeInt readhead(m_current_position); + msl::safeint3::SafeInt writeend(m_data.size()); + return (size_t)(writeend - readhead); + } - /// - /// 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; - } + virtual pplx::task _sync() { return pplx::task_from_result(true); } - /// - /// 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; - } + 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); + } - /// - /// 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()); + virtual pplx::task _putn(const _CharType* ptr, size_t count) + { + return pplx::task_from_result(this->write(ptr, count)); + } - msl::safeint3::SafeInt readhead(m_current_position); - msl::safeint3::SafeInt writeend(m_data.size()); - return (size_t)(writeend - readhead); - } + /// + /// 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; - virtual pplx::task _sync() - { - return pplx::task_from_result(true); - } + // Allocate space + resize_for_write(m_current_position + count); - 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); - } + // Let the caller copy the data + return (_CharType*)&m_data[m_current_position]; + } - virtual pplx::task _putn(const _CharType *ptr, size_t count) - { - return pplx::task_from_result(this->write(ptr, count)); - } + /// + /// 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); + } - /// - /// 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; + /// + /// 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; - // Allocate space - resize_for_write(m_current_position+count); + if (!this->can_read()) return false; - // Let the caller copy the data - return (_CharType*)&m_data[m_current_position]; - } + count = in_avail(); - /// - /// Submits a block already allocated by the stream buffer. - /// - /// The number of characters to be committed. - void _commit(size_t actual ) + if (count > 0) { - // Update the write position and satisfy any pending reads - update_current_position(m_current_position+actual); + ptr = (_CharType*)&m_data[m_current_position]; + return true; } - - /// - /// 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) + else { - ptr = nullptr; - count = 0; + // 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; + } + } - if (!this->can_read()) 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_opt_(count) _CharType* ptr, _In_ size_t count) + { + if (ptr != nullptr) update_current_position(m_current_position + count); + } - count = in_avail(); + virtual pplx::task _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) + { + return pplx::task_from_result(this->read(ptr, count)); + } - 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; - } - } + size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { return this->read(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_opt_ (count) _CharType *ptr, _In_ size_t count) - { - if (ptr != nullptr) - update_current_position(m_current_position + count); - } + virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count) + { + return this->read(ptr, count, false); + } - virtual pplx::task _getn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) - { - return pplx::task_from_result(this->read(ptr, count)); - } + virtual pplx::task _bumpc() { return pplx::task_from_result(this->read_byte(true)); } - size_t _sgetn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) - { - return this->read(ptr, count); - } + virtual int_type _sbumpc() { return this->read_byte(true); } - virtual size_t _scopy(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) - { - return this->read(ptr, count, false); - } + virtual pplx::task _getc() { return pplx::task_from_result(this->read_byte(false)); } - virtual pplx::task _bumpc() - { - return pplx::task_from_result(this->read_byte(true)); - } + int_type _sgetc() { return this->read_byte(false); } - virtual int_type _sbumpc() - { - return this->read_byte(true); - } + virtual pplx::task _nextc() + { + this->read_byte(true); + return pplx::task_from_result(this->read_byte(false)); + } - virtual pplx::task _getc() - { - 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(); + } - int_type _sgetc() - { - return this->read_byte(false); - } + /// + /// 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()); - virtual pplx::task _nextc() - { - this->read_byte(true); - return pplx::task_from_result(this->read_byte(false)); - } + return static_cast(m_current_position); + } - 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(); - } + /// + /// 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); - /// - /// 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()); + // 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. - return static_cast(m_current_position); - } + pos_type end(m_data.size()); - /// - /// 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) + if (position >= beg) { - pos_type beg(0); + auto pos = static_cast(position); - // 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) + // Read head + if ((mode & std::ios_base::in) && this->can_read()) { - auto pos = static_cast(position); - - // Read head - if ((mode & std::ios_base::in) && this->can_read()) + if (position <= end) { - 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 + // 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); } } - 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 ) + // Write head + if ((mode & std::ios_base::out) && this->can_write()) { - case std::ios_base::beg: - return seekpos(beg + offset, mode); + // Allocate space + resize_for_write(pos); - case std::ios_base::cur: - return seekpos(cur + offset, mode); + // Nothing to really copy - case std::ios_base::end: - return seekpos(end + offset, mode); + // Update write head and satisfy read requests if any + update_current_position(pos); - default: - return static_cast(traits::eof()); + return static_cast(m_current_position); } } - private: - template friend class streams::container_buffer; + return static_cast(traits::eof()); + } - /// - /// Constructor - /// - basic_container_buffer(std::ios_base::openmode mode) - : streambuf_state_manager(mode), - m_current_position(0) - { - validate_mode(mode); - } + /// + /// 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()); - /// - /// 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()) + switch (way) { - validate_mode(mode); - } + case std::ios_base::beg: return seekpos(beg + offset, 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"); - } + case std::ios_base::cur: return seekpos(cur + offset, mode); - /// - /// 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); - } + case std::ios_base::end: return seekpos(end + offset, mode); - /// - /// 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(); + default: return static_cast(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; +private: + template + friend class streams::container_buffer; - auto readBegin = std::begin(m_data) + m_current_position; - auto readEnd = std::begin(m_data) + newPos; + /// + /// Constructor + /// + basic_container_buffer(std::ios_base::openmode mode) + : streambuf_state_manager(mode), m_current_position(0) + { + validate_mode(mode); + } -#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 + /// + /// 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); + } - if (advance) - { - update_current_position(newPos); - } + 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"); + } - return (size_t) read_size; - } + /// + /// 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); + } - /// - /// 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; + /// + /// 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(); + } - auto newSize = m_current_position + count; + /// + /// 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; - // Allocate space - resize_for_write(newSize); + msl::safeint3::SafeInt request_size(count); + msl::safeint3::SafeInt read_size = request_size.Min(in_avail()); - // Copy the data - std::copy(ptr, ptr + count, std::begin(m_data) + m_current_position); + size_t newPos = m_current_position + read_size; - // Update write head and satisfy pending reads if any - update_current_position(newSize); + auto readBegin = std::begin(m_data) + m_current_position; + auto readEnd = std::begin(m_data) + newPos; - return count; - } +#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0 + // 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 - /// - /// Resize the underlying container to match the new write head - /// - void resize_for_write(size_t newPos) + if (advance) { - // Resize the container if required - if (newPos > m_data.size()) - { - m_data.resize(newPos); - } + update_current_position(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()); - } + return (size_t)read_size; + } - // The actual data store - _CollectionType m_data; + /// + /// 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); - // Read/write head - size_t m_current_position; - }; + // Copy the data + std::copy(ptr, ptr + count, std::begin(m_data) + m_current_position); - } // namespace details + // Update write head and satisfy pending reads if any + update_current_position(newSize); + + return count; + } /// - /// 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. + /// Resize the underlying container to match the new write head /// - /// - /// The type of the container. - /// - /// - /// This is a reference-counted version of basic_container_buffer. - /// - template - class container_buffer : public streambuf + void resize_for_write(size_t newPos) { - 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))) + // Resize the container if required + if (newPos > m_data.size()) { + m_data.resize(newPos); } + } - /// - /// 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))) - { - } + /// + /// 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()); + } - _CollectionType& collection() const - { - auto listBuf = static_cast *>(this->get_base().get()); - return listBuf->collection(); - } - }; + // 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; /// - /// 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. + /// Creates a container_buffer given a collection, copying its data into the buffer. /// - /// The type of the STL collection. - template - class container_stream + /// 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))) { - public: + } - typedef typename _CollectionType::value_type char_type; - typedef container_buffer<_CollectionType> buffer_type; + /// + /// 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))) + { + } - /// - /// 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)); - } + _CollectionType& collection() const + { + auto listBuf = static_cast*>(this->get_base().get()); + return listBuf->collection(); + } +}; - /// - /// 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)); - } - }; +/// +/// 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; /// - /// 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(). + /// Creates an input stream given an STL container. /// - typedef container_stream> stringstream; - typedef stringstream::buffer_type stringstreambuf; + /// 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)); + } - typedef container_stream wstringstream; - typedef wstringstream::buffer_type wstringstreambuf; + /// + /// 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: /// - /// The bytestream is a static class that allows an input stream to be constructed from any STL container. + /// Creates a single byte character input stream given an STL container. /// - class bytestream + /// 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) { - 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)); - } + 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>()); - } + /// + /// 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 +} // namespace streams +} // namespace Concurrency diff --git a/Release/include/cpprest/details/SafeInt3.hpp b/Release/include/cpprest/details/SafeInt3.hpp index 798012bedf..0a9dbdd76a 100644 --- a/Release/include/cpprest/details/SafeInt3.hpp +++ b/Release/include/cpprest/details/SafeInt3.hpp @@ -74,9 +74,9 @@ Please read the leading comments before using the class. // Enable compiling with /Wall under VC #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER -#pragma warning( push ) +#pragma warning(push) // Disable warnings coming from headers -#pragma warning( disable:4987 4820 4987 4820 ) +#pragma warning(disable : 4987 4820 4987 4820) #endif @@ -85,14 +85,14 @@ Please read the leading comments before using the class. #include #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER && defined _M_AMD64 - #include - #define SAFEINT_USE_INTRINSICS 1 +#include +#define SAFEINT_USE_INTRINSICS 1 #else - #define SAFEINT_USE_INTRINSICS 0 +#define SAFEINT_USE_INTRINSICS 0 #endif #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER -#pragma warning( pop ) +#pragma warning(pop) #endif // Various things needed for GCC @@ -116,7 +116,7 @@ Please read the leading comments before using the class. #if SAFEINT_COMPILER == CLANG_COMPILER #if __has_feature(cxx_nullptr) - #define NEEDS_NULLPTR_DEFINED 0 +#define NEEDS_NULLPTR_DEFINED 0 #endif #pragma clang diagnostic push @@ -129,21 +129,21 @@ Please read the leading comments before using the class. // 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 +// 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 @@ -151,37 +151,37 @@ Please read the leading comments before using the class. #endif #ifndef C_ASSERT -#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] +#define C_ASSERT_DEFINED_SAFEINT +#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) ); +C_ASSERT(-1 == static_cast(0xffffffff)); -/************* Compiler Options ***************************************************************************************************** +/************* 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_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. +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. ************************************************************************************************************************************/ @@ -509,9 +509,12 @@ SAFEINT_DISABLE_SHIFT_ASSERT - Set this option if you don't want to assert * 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. +* 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() @@ -528,9 +531,9 @@ SAFEINT_DISABLE_SHIFT_ASSERT - Set this option if you don't want to assert * */ -//use these if the compiler does not support _intXX +// use these if the compiler does not support _intXX #ifdef NEEDS_INT_DEFINED -#define __int8 char +#define __int8 char #define __int16 short #define __int32 int #define __int64 long long @@ -538,10 +541,8 @@ SAFEINT_DISABLE_SHIFT_ASSERT - Set this option if you don't want to assert namespace msl { - namespace safeint3 { - // catch these to handle errors // Currently implemented code values: // ERROR_ARITHMETIC_OVERFLOW @@ -553,76 +554,75 @@ enum SafeIntError SafeIntDivideByZero }; -} // safeint3 -} // msl - +} // namespace safeint3 +} // namespace 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. -* */ + * 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. @@ -635,10 +635,8 @@ enum SafeIntError namespace msl { - namespace safeint3 { - // If you would like to use your own custom assert // Define SAFEINT_ASSERT #if !defined SAFEINT_ASSERT @@ -647,119 +645,117 @@ namespace safeint3 #endif #if defined SAFEINT_ASSERT_ON_EXCEPTION - inline void SafeIntExceptionAssert() SAFEINT_NOTHROW { SAFEINT_ASSERT(false); } +inline void SafeIntExceptionAssert() SAFEINT_NOTHROW { SAFEINT_ASSERT(false); } #else - inline void SafeIntExceptionAssert() SAFEINT_NOTHROW {} +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)) +#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 +#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; - } + 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; +// 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 +class SafeIntExceptionHandler; - template <> class SafeIntExceptionHandler < SafeIntException > +template<> +class SafeIntExceptionHandler +{ +public: + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() { - public: - - static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() - { - SafeIntExceptionAssert(); - throw SafeIntException( SafeIntArithmeticOverflow ); - } + SafeIntExceptionAssert(); + throw SafeIntException(SafeIntArithmeticOverflow); + } - static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() - { - SafeIntExceptionAssert(); - throw SafeIntException( SafeIntDivideByZero ); - } - }; + 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() +// 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"); - } +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"); - } - }; + static SAFEINT_NORETURN void SafeIntOnDivZero() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + _CRT_SECURE_INVALID_PARAMETER("SafeInt Divide By Zero"); + } +}; -#if defined _WINDOWS_ +#if defined _WINDOWS_ - class SafeIntWin32ExceptionHandler +class SafeIntWin32ExceptionHandler +{ +public: + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() SAFEINT_NOTHROW { - public: - static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() SAFEINT_NOTHROW - { - SafeIntExceptionAssert(); - RaiseException( static_cast(EXCEPTION_INT_OVERFLOW), EXCEPTION_NONCONTINUABLE, 0, 0); - } + 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); - } - }; + 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::SafeIntExceptionHandler 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_ +#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; +#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, @@ -768,27 +764,27 @@ typedef SafeIntInternal::SafeIntWin32ExceptionHandler Win32ExceptionHandler; // 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 +#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, +// 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 @@ -799,9 +795,20 @@ typedef SafeIntInternal::SafeIntWin32ExceptionHandler Win32ExceptionHandler; // 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; } }; +template +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 @@ -811,49 +818,222 @@ template <> class CompileConst { public: static bool Value() SAFEINT_NOTH // that follows. // If we have support for std, then we can do this easily, and detect enums as well -template < typename T > class NumericType; +template +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 +template<> +class NumericType +{ +public: + enum + { + isBool = true, + isFloat = false, + isInt = false + }; +}; +template +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 - }; +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 }; }; +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 }; }; +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 }; }; +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: +template +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, @@ -861,129 +1041,176 @@ template < typename T > class NumericType // interact with a SafeInt enum - { - isBool = false, - isFloat = false, - isInt = static_cast( static_cast(0) ) == 0 - }; + { + 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 ); +template +class SafeIntMinMax; + +template<> +class SafeIntMinMax +{ +public: + const static signed __int8 min = (-0x7f - 1); + const static signed __int8 max = 0x7f; +}; +template<> +class SafeIntMinMax +{ +public: + const static __int16 min = (-0x7fff - 1); + const static __int16 max = 0x7fff; +}; +template<> +class SafeIntMinMax +{ +public: + const static __int32 min = (-0x7fffffff - 1); + const static __int32 max = 0x7fffffff; +}; +template<> +class SafeIntMinMax +{ +public: + const static __int64 min = static_cast<__int64>(0x8000000000000000LL); + const static __int64 max = 0x7fffffffffffffffLL; +}; + +template<> +class SafeIntMinMax +{ +public: + const static unsigned __int8 min = 0; + const static unsigned __int8 max = 0xff; +}; +template<> +class SafeIntMinMax +{ +public: + const static unsigned __int16 min = 0; + const static unsigned __int16 max = 0xffff; +}; +template<> +class SafeIntMinMax +{ +public: + const static unsigned __int32 min = 0; + const static unsigned __int32 max = 0xffffffff; +}; +template<> +class SafeIntMinMax +{ +public: + const static unsigned __int64 min = 0; + const static unsigned __int64 max = 0xffffffffffffffffULL; +}; + +template +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 ) + 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); + const static T maxInt = static_cast(SafeIntMinMax::max); + const static T minInt = static_cast(SafeIntMinMax::min); }; -template < typename T > -const T IntTraits< T >::maxInt; -template < typename T > -const T IntTraits< T >::minInt; +template +const T IntTraits::maxInt; +template +const T IntTraits::minInt; -template < typename T, typename U > class SafeIntCompare +template +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) + isBothSigned = (IntTraits::isSigned && IntTraits::isSigned), + isBothUnsigned = (!IntTraits::isSigned && !IntTraits::isSigned), + isLikeSigned = ((bool)(IntTraits::isSigned) == (bool)(IntTraits::isSigned)), + isCastOK = ((isLikeSigned && sizeof(T) >= sizeof(U)) || (IntTraits::isSigned && sizeof(T) > sizeof(U))), + isBothLT32Bit = (IntTraits::isLT32Bit && IntTraits::isLT32Bit), + isBothLT64Bit = (IntTraits::isLT64Bit && IntTraits::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 +// 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 +template +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 + // unsigned-unsigned zone + IntZone_UintLT32_UintLT32 = SafeIntCompare::isBothUnsigned && SafeIntCompare::isBothLT32Bit, + IntZone_Uint32_UintLT64 = + SafeIntCompare::isBothUnsigned && IntTraits::is32Bit && IntTraits::isLT64Bit, + IntZone_UintLT32_Uint32 = + SafeIntCompare::isBothUnsigned && IntTraits::isLT32Bit && IntTraits::is32Bit, + IntZone_Uint64_Uint = SafeIntCompare::isBothUnsigned && IntTraits::is64Bit, + IntZone_UintLT64_Uint64 = + SafeIntCompare::isBothUnsigned && IntTraits::isLT64Bit && IntTraits::is64Bit, + // unsigned-signed + IntZone_UintLT32_IntLT32 = + !IntTraits::isSigned && IntTraits::isSigned && SafeIntCompare::isBothLT32Bit, + IntZone_Uint32_IntLT64 = IntTraits::isUint32 && IntTraits::isSigned && IntTraits::isLT64Bit, + IntZone_UintLT32_Int32 = !IntTraits::isSigned && IntTraits::isLT32Bit && IntTraits::isInt32, + IntZone_Uint64_Int = IntTraits::isUint64 && IntTraits::isSigned && IntTraits::isLT64Bit, + IntZone_UintLT64_Int64 = !IntTraits::isSigned && IntTraits::isLT64Bit && IntTraits::isInt64, + IntZone_Uint64_Int64 = IntTraits::isUint64 && IntTraits::isInt64, + // signed-signed + IntZone_IntLT32_IntLT32 = SafeIntCompare::isBothSigned && SafeIntCompare::isBothLT32Bit, + IntZone_Int32_IntLT64 = SafeIntCompare::isBothSigned && IntTraits::is32Bit && IntTraits::isLT64Bit, + IntZone_IntLT32_Int32 = SafeIntCompare::isBothSigned && IntTraits::isLT32Bit && IntTraits::is32Bit, + IntZone_Int64_Int64 = SafeIntCompare::isBothSigned && IntTraits::isInt64 && IntTraits::isInt64, + IntZone_Int64_Int = SafeIntCompare::isBothSigned && IntTraits::is64Bit && IntTraits::isLT64Bit, + IntZone_IntLT64_Int64 = SafeIntCompare::isBothSigned && IntTraits::isLT64Bit && IntTraits::is64Bit, + // signed-unsigned + IntZone_IntLT32_UintLT32 = + IntTraits::isSigned && !IntTraits::isSigned && SafeIntCompare::isBothLT32Bit, + IntZone_Int32_UintLT32 = IntTraits::isInt32 && !IntTraits::isSigned && IntTraits::isLT32Bit, + IntZone_IntLT64_Uint32 = IntTraits::isSigned && IntTraits::isLT64Bit && IntTraits::isUint32, + IntZone_Int64_UintLT64 = IntTraits::isInt64 && !IntTraits::isSigned && IntTraits::isLT64Bit, + IntZone_Int_Uint64 = IntTraits::isSigned && IntTraits::isUint64 && IntTraits::isLT64Bit, + IntZone_Int64_Uint64 = IntTraits::isInt64 && IntTraits::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. @@ -992,7 +1219,6 @@ template < typename T, typename U > class IntRegion // 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 { @@ -1001,54 +1227,60 @@ enum AbsMethod AbsMethodNoop }; -template < typename T > +template class GetAbsMethod { public: enum { - method = IntTraits< T >::isLT64Bit && IntTraits< T >::isSigned ? AbsMethodInt : - IntTraits< T >::isInt64 ? AbsMethodInt64 : AbsMethodNoop + method = IntTraits::isLT64Bit && IntTraits::isSigned + ? AbsMethodInt + : IntTraits::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 +class AbsValueHelper; -template < typename T > class AbsValueHelper < T, AbsMethodInt> +template +class AbsValueHelper { public: - static unsigned __int32 Abs( T t ) SAFEINT_NOTHROW + static unsigned __int32 Abs(T t) SAFEINT_NOTHROW { - SAFEINT_ASSERT( t < 0 ); + SAFEINT_ASSERT(t < 0); return ~(unsigned __int32)t + 1; } }; -template < typename T > class AbsValueHelper < T, AbsMethodInt64 > +template +class AbsValueHelper { public: - static unsigned __int64 Abs( T t ) SAFEINT_NOTHROW + static unsigned __int64 Abs(T t) SAFEINT_NOTHROW { - SAFEINT_ASSERT( t < 0 ); + SAFEINT_ASSERT(t < 0); return ~(unsigned __int64)t + 1; } }; -template < typename T > class AbsValueHelper < T, AbsMethodNoop > +template +class AbsValueHelper { public: - static T Abs( T t ) SAFEINT_NOTHROW + static T Abs(T t) SAFEINT_NOTHROW { // Why are you calling Abs on an unsigned number ??? - SAFEINT_ASSERT( false ); + SAFEINT_ASSERT(false); return t; } }; -template < typename T, bool > class NegationHelper; +template +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 @@ -1061,14 +1293,15 @@ template < typename T, bool > class NegationHelper; // -(SafeInt(ss)) // will then emit a signed int with the correct value and bitfield -template < typename T > class NegationHelper // Signed +template +class NegationHelper // Signed { public: - template - static T NegativeThrow( T t ) SAFEINT_CPP_THROW + template + static T NegativeThrow(T t) SAFEINT_CPP_THROW { // corner case - if( t != IntTraits< T >::minInt ) + if (t != IntTraits::minInt) { // cast prevents unneeded checks in the case of small ints return -t; @@ -1076,10 +1309,10 @@ template < typename T > class NegationHelper // Signed E::SafeIntOnOverflow(); } - static bool Negative( T t, T& ret ) SAFEINT_NOTHROW + static bool Negative(T t, T& ret) SAFEINT_NOTHROW { // corner case - if( t != IntTraits< T >::minInt ) + if (t != IntTraits::minInt) { // cast prevents unneeded checks in the case of small ints ret = -t; @@ -1091,10 +1324,11 @@ template < typename T > class NegationHelper // Signed // Helper classes to work keep compilers from // optimizing away negation -template < typename T > class SignedNegation; +template +class SignedNegation; -template <> -class SignedNegation +template<> +class SignedNegation { public: static signed __int32 Value(unsigned __int64 in) SAFEINT_NOTHROW @@ -1102,36 +1336,31 @@ class SignedNegation return (signed __int32)(~(unsigned __int32)in + 1); } - static signed __int32 Value(unsigned __int32 in) SAFEINT_NOTHROW - { - return (signed __int32)(~in + 1); - } + static signed __int32 Value(unsigned __int32 in) SAFEINT_NOTHROW { return (signed __int32)(~in + 1); } }; -template <> -class SignedNegation +template<> +class SignedNegation { public: - static signed __int64 Value(unsigned __int64 in) SAFEINT_NOTHROW - { - return (signed __int64)(~in + 1); - } + static signed __int64 Value(unsigned __int64 in) SAFEINT_NOTHROW { return (signed __int64)(~in + 1); } }; -template < typename T > class NegationHelper // unsigned +template +class NegationHelper // unsigned { public: - template - static T NegativeThrow( T t ) SAFEINT_CPP_THROW + template + static T NegativeThrow(T t) SAFEINT_CPP_THROW { #if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION - C_ASSERT( sizeof(T) == 0 ); + 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) +// 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: @@ -1144,15 +1373,15 @@ template < typename T > class NegationHelper // unsigned #endif } - static bool Negative( T t, T& ret ) SAFEINT_NOTHROW + static bool Negative(T t, T& ret) SAFEINT_NOTHROW { - if( IntTraits::isLT32Bit ) + if (IntTraits::isLT32Bit) { // See above - SAFEINT_ASSERT( false ); + SAFEINT_ASSERT(false); } #if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION - C_ASSERT( sizeof(T) == 0 ); + C_ASSERT(sizeof(T) == 0); #endif // Do it this way to avoid warning ret = -t; @@ -1160,7 +1389,7 @@ template < typename T > class NegationHelper // unsigned } }; -//core logic to determine casting behavior +// core logic to determine casting behavior enum CastMethod { CastOK = 0, @@ -1174,85 +1403,115 @@ enum CastMethod CastFromBool }; - -template < typename ToType, typename FromType > +template class GetCastMethod { public: enum { - method = ( IntTraits< FromType >::isBool && - !IntTraits< ToType >::isBool ) ? CastFromBool : + method = (IntTraits::isBool && !IntTraits::isBool) + ? CastFromBool + : - ( !IntTraits< FromType >::isBool && - IntTraits< ToType >::isBool ) ? CastToBool : + (!IntTraits::isBool && IntTraits::isBool) + ? CastToBool + : - ( SafeIntCompare< ToType, FromType >::isCastOK ) ? CastOK : + (SafeIntCompare::isCastOK) + ? CastOK + : - ( ( IntTraits< ToType >::isSigned && - !IntTraits< FromType >::isSigned && - sizeof( FromType ) >= sizeof( ToType ) ) || - ( SafeIntCompare< ToType, FromType >::isBothUnsigned && - sizeof( FromType ) > sizeof( ToType ) ) ) ? CastCheckGTMax : + ((IntTraits::isSigned && !IntTraits::isSigned && + sizeof(FromType) >= sizeof(ToType)) || + (SafeIntCompare::isBothUnsigned && sizeof(FromType) > sizeof(ToType))) + ? CastCheckGTMax + : - ( !IntTraits< ToType >::isSigned && - IntTraits< FromType >::isSigned && - sizeof( ToType ) >= sizeof( FromType ) ) ? CastCheckLTZero : + (!IntTraits::isSigned && IntTraits::isSigned && + sizeof(ToType) >= sizeof(FromType)) + ? CastCheckLTZero + : - ( !IntTraits< ToType >::isSigned ) ? CastCheckSafeIntMinMaxUnsigned - : CastCheckSafeIntMinMaxSigned + (!IntTraits::isSigned) ? CastCheckSafeIntMinMaxUnsigned + : CastCheckSafeIntMinMaxSigned }; }; -template < typename FromType > class GetCastMethod < float, FromType > +template +class GetCastMethod { public: - enum{ method = CastOK }; + enum + { + method = CastOK + }; }; -template < typename FromType > class GetCastMethod < double, FromType > +template +class GetCastMethod { public: - enum{ method = CastOK }; + enum + { + method = CastOK + }; }; -template < typename FromType > class GetCastMethod < long double, FromType > +template +class GetCastMethod { public: - enum{ method = CastOK }; + enum + { + method = CastOK + }; }; -template < typename ToType > class GetCastMethod < ToType, float > +template +class GetCastMethod { public: - enum{ method = CastFromFloat }; + enum + { + method = CastFromFloat + }; }; -template < typename ToType > class GetCastMethod < ToType, double > +template +class GetCastMethod { public: - enum{ method = CastFromFloat }; + enum + { + method = CastFromFloat + }; }; -template < typename ToType > class GetCastMethod < ToType, long double > +template +class GetCastMethod { public: - enum{ method = CastFromFloat }; + enum + { + method = CastFromFloat + }; }; -template < typename T, typename U, int > class SafeCastHelper; +template +class SafeCastHelper; -template < typename T, typename U > class SafeCastHelper < T, U, CastOK > +template +class SafeCastHelper { public: - static bool Cast( U u, T& t ) SAFEINT_NOTHROW + 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 + template + static void CastThrow(U u, T& t) SAFEINT_CPP_THROW { t = (T)u; } @@ -1260,13 +1519,13 @@ template < typename T, typename U > class SafeCastHelper < T, U, CastOK > // special case floats and doubles // tolerate loss of precision -template < typename T, typename U > class SafeCastHelper < T, U, CastFromFloat > +template +class SafeCastHelper { public: - static bool Cast( U u, T& t ) SAFEINT_NOTHROW + static bool Cast(U u, T& t) SAFEINT_NOTHROW { - if( u <= (U)IntTraits< T >::maxInt && - u >= (U)IntTraits< T >::minInt ) + if (u <= (U)IntTraits::maxInt && u >= (U)IntTraits::minInt) { t = (T)u; return true; @@ -1274,11 +1533,10 @@ template < typename T, typename U > class SafeCastHelper < T, U, CastFromFloat > return false; } - template < typename E > - static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + template + static void CastThrow(U u, T& t) SAFEINT_CPP_THROW { - if( u <= (U)IntTraits< T >::maxInt && - u >= (U)IntTraits< T >::minInt ) + if (u <= (U)IntTraits::maxInt && u >= (U)IntTraits::minInt) { t = (T)u; return; @@ -1288,131 +1546,129 @@ template < typename T, typename U > class SafeCastHelper < T, U, CastFromFloat > }; // Match on any method where a bool is cast to type T -template < typename T > class SafeCastHelper < T, bool, CastFromBool > +template +class SafeCastHelper { public: - static bool Cast( bool b, T& t ) SAFEINT_NOTHROW + static bool Cast(bool b, T& t) SAFEINT_NOTHROW { - t = (T)( b ? 1 : 0 ); + t = (T)(b ? 1 : 0); return true; } - template < typename E > - static void CastThrow( bool b, T& t ) SAFEINT_CPP_THROW + template + static void CastThrow(bool b, T& t) SAFEINT_CPP_THROW { - t = (T)( b ? 1 : 0 ); + t = (T)(b ? 1 : 0); } }; -template < typename T > class SafeCastHelper < bool, T, CastToBool > +template +class SafeCastHelper { public: - static bool Cast( T t, bool& b ) SAFEINT_NOTHROW + 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 + template + static void CastThrow(bool b, T& t) SAFEINT_CPP_THROW { b = !!t; } }; -template < typename T, typename U > class SafeCastHelper < T, U, CastCheckLTZero > +template +class SafeCastHelper { public: - static bool Cast( U u, T& t ) SAFEINT_NOTHROW + static bool Cast(U u, T& t) SAFEINT_NOTHROW { - if( u < 0 ) - return false; + if (u < 0) return false; t = (T)u; return true; } - template < typename E > - static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + template + static void CastThrow(U u, T& t) SAFEINT_CPP_THROW { - if( u < 0 ) - E::SafeIntOnOverflow(); + if (u < 0) E::SafeIntOnOverflow(); t = (T)u; } }; -template < typename T, typename U > class SafeCastHelper < T, U, CastCheckGTMax > +template +class SafeCastHelper { public: - static bool Cast( U u, T& t ) SAFEINT_NOTHROW + static bool Cast(U u, T& t) SAFEINT_NOTHROW { - if( u > (U)IntTraits< T >::maxInt ) - return false; + if (u > (U)IntTraits::maxInt) return false; t = (T)u; return true; } - template < typename E > - static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + template + static void CastThrow(U u, T& t) SAFEINT_CPP_THROW { - if( u > (U)IntTraits< T >::maxInt ) - E::SafeIntOnOverflow(); + if (u > (U)IntTraits::maxInt) E::SafeIntOnOverflow(); t = (T)u; } }; -template < typename T, typename U > class SafeCastHelper < T, U, CastCheckSafeIntMinMaxUnsigned > +template +class SafeCastHelper { public: - static bool Cast( U u, T& t ) SAFEINT_NOTHROW + 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; + if (u > IntTraits::maxInt || u < 0) return false; t = (T)u; return true; } - template < typename E > - static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + template + 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(); + if (u > IntTraits::maxInt || u < 0) E::SafeIntOnOverflow(); t = (T)u; } }; -template < typename T, typename U > class SafeCastHelper < T, U, CastCheckSafeIntMinMaxSigned > +template +class SafeCastHelper { public: - static bool Cast( U u, T& t ) SAFEINT_NOTHROW + static bool Cast(U u, T& t) SAFEINT_NOTHROW { // T, U are signed - if( u > IntTraits< T >::maxInt || u < IntTraits< T >::minInt ) - return false; + if (u > IntTraits::maxInt || u < IntTraits::minInt) return false; t = (T)u; return true; } - template < typename E > - static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + template + 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, U are signed + if (u > IntTraits::maxInt || u < IntTraits::minInt) E::SafeIntOnOverflow(); t = (T)u; } }; -//core logic to determine whether a comparison is valid, or needs special treatment +// core logic to determine whether a comparison is valid, or needs special treatment enum ComparisonMethod { ComparisonMethod_Ok = 0, @@ -1422,26 +1678,26 @@ enum ComparisonMethod 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 > +// 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 class ValidComparison { public: @@ -1450,131 +1706,141 @@ class ValidComparison #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 ) + method = ((SafeIntCompare::isLikeSigned) + ? ComparisonMethod_Ok + : ((IntTraits::isSigned && sizeof(T) < 8 && sizeof(U) < 4) || + (IntTraits::isSigned && sizeof(T) < 4 && sizeof(U) < 8)) + ? ComparisonMethod_CastInt + : ((IntTraits::isSigned && sizeof(U) < 8) || (IntTraits::isSigned && sizeof(T) < 8)) + ? ComparisonMethod_CastInt64 + : (!IntTraits::isSigned) ? ComparisonMethod_UnsignedT : ComparisonMethod_UnsignedU) #endif }; }; -template class EqualityTest; +template +class EqualityTest; -template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_Ok > +template +class EqualityTest { public: - static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( t == u ); } + 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 > +template +class EqualityTest { public: - static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( (int)t == (int)u ); } + 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 > +template +class EqualityTest { public: - static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( (__int64)t == (__int64)u ); } + 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 > +template +class EqualityTest { public: - static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW + 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; + // 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 ); + // else safe to cast to type T + return (t == (T)u); } }; -template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedU> +template +class EqualityTest { public: - static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW + 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; + // 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 ); + // else safe to cast to type U + return ((U)t == u); } }; -template class GreaterThanTest; +template +class GreaterThanTest; -template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_Ok > +template +class GreaterThanTest { public: - static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( t > u ); } + 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 > +template +class GreaterThanTest { public: - static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( (int)t > (int)u ); } + 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 > +template +class GreaterThanTest { public: - static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( (__int64)t > (__int64)u ); } + 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 > +template +class GreaterThanTest { public: - static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW + 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; + if (u < 0) return true; // else safe to cast to type T - return ( t > (T)u ); + return (t > (T)u); } }; -template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedU > +template +class GreaterThanTest { public: - static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW + 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; + if (t < 0) return false; // else safe to cast to type U - return ( (U)t > 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; -template class ModulusHelper +template +class ModulusHelper { public: - static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW { - if(u == 0) - return SafeIntDivideByZero; + if (u == 0) return SafeIntDivideByZero; - //trap corner case - if( CompileConst< IntTraits< U >::isSigned >::Value() ) + // trap corner case + if (CompileConst::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 ) + if (u == (U)-1) { result = 0; return SafeIntNoError; @@ -1585,16 +1851,15 @@ template class ModulusHelper - static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + template + static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW { - if(u == 0) - E::SafeIntOnDivZero(); + if (u == 0) E::SafeIntOnDivZero(); - //trap corner case - if( CompileConst< IntTraits< U >::isSigned >::Value() ) + // trap corner case + if (CompileConst::isSigned>::Value()) { - if( u == (U)-1 ) + if (u == (U)-1) { result = 0; return; @@ -1605,18 +1870,18 @@ template class ModulusHelper class ModulusHelper +template +class ModulusHelper { public: - static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW { - if(u == 0) - return SafeIntDivideByZero; + if (u == 0) return SafeIntDivideByZero; - //trap corner case - if( CompileConst< IntTraits< U >::isSigned >::Value() ) + // trap corner case + if (CompileConst::isSigned>::Value()) { - if( u == (U)-1 ) + if (u == (U)-1) { result = 0; return SafeIntNoError; @@ -1627,16 +1892,15 @@ template class ModulusHelper - static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + template + static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW { - if(u == 0) - E::SafeIntOnDivZero(); + if (u == 0) E::SafeIntOnDivZero(); - //trap corner case - if( CompileConst< IntTraits< U >::isSigned >::Value() ) + // trap corner case + if (CompileConst::isSigned>::Value()) { - if( u == (U)-1 ) + if (u == (U)-1) { result = 0; return; @@ -1647,18 +1911,18 @@ template class ModulusHelper class ModulusHelper< T, U, ComparisonMethod_CastInt64> +template +class ModulusHelper { public: - static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW { - if(u == 0) - return SafeIntDivideByZero; + if (u == 0) return SafeIntDivideByZero; - //trap corner case - if( CompileConst< IntTraits< U >::isSigned >::Value() ) + // trap corner case + if (CompileConst::isSigned>::Value()) { - if( u == (U)-1 ) + if (u == (U)-1) { result = 0; return SafeIntNoError; @@ -1669,15 +1933,14 @@ template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_ return SafeIntNoError; } - template < typename E > - static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + template + static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW { - if(u == 0) - E::SafeIntOnDivZero(); + if (u == 0) E::SafeIntOnDivZero(); - if( CompileConst< IntTraits< U >::isSigned >::Value() ) + if (CompileConst::isSigned>::Value()) { - if( u == (U)-1 ) + if (u == (U)-1) { result = 0; return; @@ -1689,71 +1952,69 @@ template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_ }; // T is unsigned __int64, U is any signed int -template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_UnsignedT> +template +class ModulusHelper { public: - static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW { - if(u == 0) - return SafeIntDivideByZero; + 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)); + if (u < 0) + result = (T)(t % AbsValueHelper::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 + template + static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW { - if(u == 0) - E::SafeIntOnDivZero(); + 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 )); + if (u < 0) + result = (T)(t % AbsValueHelper::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> +template +class ModulusHelper { public: - static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + static SafeIntError Modulus(const T& t, const U& u, T& result) SAFEINT_NOTHROW { - if(u == 0) - return SafeIntDivideByZero; + 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 ); + // t could be negative - if so, need to convert to positive + if (t < 0) + result = (T)(~(AbsValueHelper::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 + template + static void ModulusThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW { - if(u == 0) - E::SafeIntOnDivZero(); + 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); + // t could be negative - if so, need to convert to positive + if (t < 0) + result = (T)(~(AbsValueHelper::method>::Abs(t) % u) + 1); else - result = (T)( (T)t % u ); + result = (T)((T)t % u); } }; -//core logic to determine method to check multiplication +// core logic to determine method to check multiplication enum MultiplicationState { MultiplicationState_CastInt = 0, // One or both signed, smaller than 32-bit @@ -1775,150 +2036,182 @@ enum MultiplicationState MultiplicationState_Error }; -template < typename T, typename U > +template 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 ) ) + // unsigned-unsigned + method = + (IntRegion::IntZone_UintLT32_UintLT32 + ? MultiplicationState_CastUint + : (IntRegion::IntZone_Uint32_UintLT64 || IntRegion::IntZone_UintLT32_Uint32) + ? MultiplicationState_CastUint64 + : SafeIntCompare::isBothUnsigned && IntTraits::isUint64 && IntTraits::isUint64 + ? MultiplicationState_Uint64Uint64 + : (IntRegion::IntZone_Uint64_Uint) + ? MultiplicationState_Uint64Uint + : (IntRegion::IntZone_UintLT64_Uint64) ? MultiplicationState_UintUint64 : + // unsigned-signed + (IntRegion::IntZone_UintLT32_IntLT32) + ? MultiplicationState_CastInt + : (IntRegion::IntZone_Uint32_IntLT64 || + IntRegion::IntZone_UintLT32_Int32) + ? MultiplicationState_CastInt64 + : (IntRegion::IntZone_Uint64_Int) + ? MultiplicationState_Uint64Int + : (IntRegion::IntZone_UintLT64_Int64) + ? MultiplicationState_UintInt64 + : (IntRegion::IntZone_Uint64_Int64) + ? MultiplicationState_Uint64Int64 + : + // signed-signed + (IntRegion::IntZone_IntLT32_IntLT32) + ? MultiplicationState_CastInt + : (IntRegion::IntZone_Int32_IntLT64 || + IntRegion::IntZone_IntLT32_Int32) + ? MultiplicationState_CastInt64 + : (IntRegion::IntZone_Int64_Int64) + ? MultiplicationState_Int64Int64 + : (IntRegion::IntZone_Int64_Int) + ? MultiplicationState_Int64Int + : (IntRegion:: + IntZone_IntLT64_Int64) + ? MultiplicationState_IntInt64 + : + // signed-unsigned + (IntRegion:: + IntZone_IntLT32_UintLT32) + ? MultiplicationState_CastInt + : (IntRegion:: + IntZone_Int32_UintLT32 || + IntRegion:: + 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 +class MultiplicationHelper; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastInt> +template +class MultiplicationHelper { public: - //accepts signed, both less than 32-bit - static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + // 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; + if (tmp > IntTraits::maxInt || tmp < IntTraits::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 + template + 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(); + if (tmp > IntTraits::maxInt || tmp < IntTraits::minInt) E::SafeIntOnOverflow(); ret = (T)tmp; } }; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastUint > +template +class MultiplicationHelper { public: - //accepts unsigned, both less than 32-bit - static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + // 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; + if (tmp > IntTraits::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 + template + static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW { - unsigned int tmp = (unsigned int)( t * u ); + unsigned int tmp = (unsigned int)(t * u); - if( tmp > IntTraits< T >::maxInt ) - E::SafeIntOnOverflow(); + if (tmp > IntTraits::maxInt) E::SafeIntOnOverflow(); ret = (T)tmp; } }; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastInt64> +template +class MultiplicationHelper { 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 + // 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; + if (tmp > (__int64)IntTraits::maxInt || tmp < (__int64)IntTraits::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 + template + 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(); + if (tmp > (__int64)IntTraits::maxInt || tmp < (__int64)IntTraits::minInt) E::SafeIntOnOverflow(); ret = (T)tmp; } }; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastUint64> +template +class MultiplicationHelper { 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 + // 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; + if (tmp > (unsigned __int64)IntTraits::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 + template + 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(); + if (tmp > (unsigned __int64)IntTraits::maxInt) E::SafeIntOnOverflow(); ret = (T)tmp; } @@ -1926,32 +2219,36 @@ template < typename T, typename U > class MultiplicationHelper< T, U, Multiplica // T = left arg and return type // U = right arg -template < typename T, typename U > class LargeIntRegMultiply; +template +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 +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); + *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 +inline bool IntrinsicMultiplyInt64(const signed __int64& a, + const signed __int64& b, + signed __int64* pRet) SAFEINT_NOTHROW { __int64 llHigh = 0; - *pRet = _mul128(a , b, &llHigh); + *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 ) + if ((a ^ b) < 0) { // Negative result expected - if( llHigh == -1 && *pRet < 0 || - llHigh == 0 && *pRet == 0 ) + if (llHigh == -1 && *pRet < 0 || llHigh == 0 && *pRet == 0) { // Everything is within range return true; @@ -1961,21 +2258,23 @@ inline bool IntrinsicMultiplyInt64( const signed __int64& a, const signed __int6 { // Result should be positive // Check for overflow - if( llHigh == 0 && (unsigned __int64)*pRet <= IntTraits< signed __int64 >::maxInt ) - return true; + if (llHigh == 0 && (unsigned __int64)*pRet <= IntTraits::maxInt) return true; } return false; } #endif -template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int64 > +template<> +class LargeIntRegMultiply { public: - static bool RegMultiply( const unsigned __int64& a, const unsigned __int64& b, unsigned __int64* pRet ) SAFEINT_NOTHROW + 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 ); + return IntrinsicMultiplyUint64(a, b, pRet); #else unsigned __int32 aHigh, aLow, bHigh, bLow; @@ -1985,22 +2284,22 @@ template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int64 > // Note - same approach applies for 128 bit math on a 64-bit system aHigh = (unsigned __int32)(a >> 32); - aLow = (unsigned __int32)a; + aLow = (unsigned __int32)a; bHigh = (unsigned __int32)(b >> 32); - bLow = (unsigned __int32)b; + bLow = (unsigned __int32)b; *pRet = 0; - if(aHigh == 0) + if (aHigh == 0) { - if(bHigh != 0) + if (bHigh != 0) { *pRet = (unsigned __int64)aLow * (unsigned __int64)bHigh; } } - else if(bHigh == 0) + else if (bHigh == 0) { - if(aHigh != 0) + if (aHigh != 0) { *pRet = (unsigned __int64)aHigh * (unsigned __int64)bLow; } @@ -2010,19 +2309,17 @@ template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int64 > return false; } - if(*pRet != 0) + if (*pRet != 0) { unsigned __int64 tmp; - if((unsigned __int32)(*pRet >> 32) != 0) - return false; + if ((unsigned __int32)(*pRet >> 32) != 0) return false; *pRet <<= 32; tmp = (unsigned __int64)aLow * (unsigned __int64)bLow; *pRet += tmp; - if(*pRet < tmp) - return false; + if (*pRet < tmp) return false; return true; } @@ -2032,12 +2329,13 @@ template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int64 > #endif } - template < typename E > - static void RegMultiplyThrow( const unsigned __int64& a, const unsigned __int64& b, unsigned __int64* pRet ) SAFEINT_CPP_THROW + template + 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(); + if (!IntrinsicMultiplyUint64(a, b, pRet)) E::SafeIntOnOverflow(); #else unsigned __int32 aHigh, aLow, bHigh, bLow; @@ -2047,22 +2345,22 @@ template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int64 > // Note - same approach applies for 128 bit math on a 64-bit system aHigh = (unsigned __int32)(a >> 32); - aLow = (unsigned __int32)a; + aLow = (unsigned __int32)a; bHigh = (unsigned __int32)(b >> 32); - bLow = (unsigned __int32)b; + bLow = (unsigned __int32)b; *pRet = 0; - if(aHigh == 0) + if (aHigh == 0) { - if(bHigh != 0) + if (bHigh != 0) { *pRet = (unsigned __int64)aLow * (unsigned __int64)bHigh; } } - else if(bHigh == 0) + else if (bHigh == 0) { - if(aHigh != 0) + if (aHigh != 0) { *pRet = (unsigned __int64)aHigh * (unsigned __int64)bLow; } @@ -2072,19 +2370,17 @@ template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int64 > E::SafeIntOnOverflow(); } - if(*pRet != 0) + if (*pRet != 0) { unsigned __int64 tmp; - if((unsigned __int32)(*pRet >> 32) != 0) - E::SafeIntOnOverflow(); + if ((unsigned __int32)(*pRet >> 32) != 0) E::SafeIntOnOverflow(); *pRet <<= 32; tmp = (unsigned __int64)aLow * (unsigned __int64)bLow; *pRet += tmp; - if(*pRet < tmp) - E::SafeIntOnOverflow(); + if (*pRet < tmp) E::SafeIntOnOverflow(); return; } @@ -2094,13 +2390,14 @@ template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int64 > } }; -template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int32 > +template<> +class LargeIntRegMultiply { public: - static bool RegMultiply( const unsigned __int64& a, unsigned __int32 b, unsigned __int64* pRet ) SAFEINT_NOTHROW + 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 ); + return IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet); #else unsigned __int32 aHigh, aLow; @@ -2109,25 +2406,23 @@ template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int32 > // => (aHigh * b * 2^32) + (aLow * b) aHigh = (unsigned __int32)(a >> 32); - aLow = (unsigned __int32)a; + aLow = (unsigned __int32)a; *pRet = 0; - if(aHigh != 0) + if (aHigh != 0) { *pRet = (unsigned __int64)aHigh * (unsigned __int64)b; unsigned __int64 tmp; - if((unsigned __int32)(*pRet >> 32) != 0) - return false; + if ((unsigned __int32)(*pRet >> 32) != 0) return false; *pRet <<= 32; tmp = (unsigned __int64)aLow * (unsigned __int64)b; *pRet += tmp; - if(*pRet < tmp) - return false; + if (*pRet < tmp) return false; return true; } @@ -2137,12 +2432,13 @@ template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int32 > #endif } - template < typename E > - static void RegMultiplyThrow( const unsigned __int64& a, unsigned __int32 b, unsigned __int64* pRet ) SAFEINT_CPP_THROW + template + 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(); + if (!IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet)) E::SafeIntOnOverflow(); #else unsigned __int32 aHigh, aLow; @@ -2151,25 +2447,23 @@ template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int32 > // => (aHigh * b * 2^32) + (aLow * b) aHigh = (unsigned __int32)(a >> 32); - aLow = (unsigned __int32)a; + aLow = (unsigned __int32)a; *pRet = 0; - if(aHigh != 0) + if (aHigh != 0) { *pRet = (unsigned __int64)aHigh * (unsigned __int64)b; unsigned __int64 tmp; - if((unsigned __int32)(*pRet >> 32) != 0) - E::SafeIntOnOverflow(); + if ((unsigned __int32)(*pRet >> 32) != 0) E::SafeIntOnOverflow(); *pRet <<= 32; tmp = (unsigned __int64)aLow * (unsigned __int64)b; *pRet += tmp; - if(*pRet < tmp) - E::SafeIntOnOverflow(); + if (*pRet < tmp) E::SafeIntOnOverflow(); return; } @@ -2180,72 +2474,71 @@ template<> class LargeIntRegMultiply< unsigned __int64, unsigned __int32 > } }; -template<> class LargeIntRegMultiply< unsigned __int64, signed __int32 > +template<> +class LargeIntRegMultiply { public: // Intrinsic not needed - static bool RegMultiply( const unsigned __int64& a, signed __int32 b, unsigned __int64* pRet ) SAFEINT_NOTHROW + static bool RegMultiply(const unsigned __int64& a, signed __int32 b, unsigned __int64* pRet) SAFEINT_NOTHROW { - if( b < 0 && a != 0 ) - return false; + if (b < 0 && a != 0) return false; #if SAFEINT_USE_INTRINSICS - return IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ); + return IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet); #else - return LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply(a, (unsigned __int32)b, pRet); + return LargeIntRegMultiply::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 + template + static void RegMultiplyThrow(const unsigned __int64& a, signed __int32 b, unsigned __int64* pRet) SAFEINT_CPP_THROW { - if( b < 0 && a != 0 ) - E::SafeIntOnOverflow(); + if (b < 0 && a != 0) E::SafeIntOnOverflow(); #if SAFEINT_USE_INTRINSICS - if( !IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ) ) - E::SafeIntOnOverflow(); + if (!IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet)) E::SafeIntOnOverflow(); #else - LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( a, (unsigned __int32)b, pRet ); + LargeIntRegMultiply::template RegMultiplyThrow( + a, (unsigned __int32)b, pRet); #endif } }; -template<> class LargeIntRegMultiply< unsigned __int64, signed __int64 > +template<> +class LargeIntRegMultiply { public: - static bool RegMultiply( const unsigned __int64& a, signed __int64 b, unsigned __int64* pRet ) SAFEINT_NOTHROW + static bool RegMultiply(const unsigned __int64& a, signed __int64 b, unsigned __int64* pRet) SAFEINT_NOTHROW { - if( b < 0 && a != 0 ) - return false; + if (b < 0 && a != 0) return false; #if SAFEINT_USE_INTRINSICS - return IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ); + return IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet); #else - return LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply(a, (unsigned __int64)b, pRet); + return LargeIntRegMultiply::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 + template + static void RegMultiplyThrow(const unsigned __int64& a, signed __int64 b, unsigned __int64* pRet) SAFEINT_CPP_THROW { - if( b < 0 && a != 0 ) - E::SafeIntOnOverflow(); + if (b < 0 && a != 0) E::SafeIntOnOverflow(); #if SAFEINT_USE_INTRINSICS - if( !IntrinsicMultiplyUint64( a, (unsigned __int64)b, pRet ) ) - E::SafeIntOnOverflow(); + if (!IntrinsicMultiplyUint64(a, (unsigned __int64)b, pRet)) E::SafeIntOnOverflow(); #else - LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::template RegMultiplyThrow< E >( a, (unsigned __int64)b, pRet ); + LargeIntRegMultiply::template RegMultiplyThrow( + a, (unsigned __int64)b, pRet); #endif } }; -template<> class LargeIntRegMultiply< signed __int32, unsigned __int64 > +template<> +class LargeIntRegMultiply { public: // Devolves into ordinary 64-bit calculation - static bool RegMultiply( signed __int32 a, const unsigned __int64& b, signed __int32* pRet ) SAFEINT_NOTHROW + static bool RegMultiply(signed __int32 a, const unsigned __int64& b, signed __int32* pRet) SAFEINT_NOTHROW { unsigned __int32 bHigh, bLow; bool fIsNegative = false; @@ -2258,25 +2551,23 @@ template<> class LargeIntRegMultiply< signed __int32, unsigned __int64 > // If the first part is != 0, fail bHigh = (unsigned __int32)(b >> 32); - bLow = (unsigned __int32)b; + bLow = (unsigned __int32)b; *pRet = 0; - if(bHigh != 0 && a != 0) - return false; + if (bHigh != 0 && a != 0) return false; - if( a < 0 ) + if (a < 0) { - - a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); + a = (signed __int32)AbsValueHelper::method>::Abs(a); fIsNegative = true; } unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow; - if( !fIsNegative ) + if (!fIsNegative) { - if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt ) + if (tmp <= (unsigned __int64)IntTraits::maxInt) { *pRet = (signed __int32)tmp; return true; @@ -2284,9 +2575,9 @@ template<> class LargeIntRegMultiply< signed __int32, unsigned __int64 > } else { - if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt+1 ) + if (tmp <= (unsigned __int64)IntTraits::maxInt + 1) { - *pRet = SignedNegation< signed __int32 >::Value( tmp ); + *pRet = SignedNegation::Value(tmp); return true; } } @@ -2294,8 +2585,8 @@ template<> class LargeIntRegMultiply< signed __int32, unsigned __int64 > return false; } - template < typename E > - static void RegMultiplyThrow( signed __int32 a, const unsigned __int64& b, signed __int32* pRet ) SAFEINT_CPP_THROW + template + static void RegMultiplyThrow(signed __int32 a, const unsigned __int64& b, signed __int32* pRet) SAFEINT_CPP_THROW { unsigned __int32 bHigh, bLow; bool fIsNegative = false; @@ -2305,24 +2596,23 @@ template<> class LargeIntRegMultiply< signed __int32, unsigned __int64 > // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) bHigh = (unsigned __int32)(b >> 32); - bLow = (unsigned __int32)b; + bLow = (unsigned __int32)b; *pRet = 0; - if(bHigh != 0 && a != 0) - E::SafeIntOnOverflow(); + if (bHigh != 0 && a != 0) E::SafeIntOnOverflow(); - if( a < 0 ) + if (a < 0) { - a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); + a = (signed __int32)AbsValueHelper::method>::Abs(a); fIsNegative = true; } unsigned __int64 tmp = (unsigned __int32)a * (unsigned __int64)bLow; - if( !fIsNegative ) + if (!fIsNegative) { - if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt ) + if (tmp <= (unsigned __int64)IntTraits::maxInt) { *pRet = (signed __int32)tmp; return; @@ -2330,9 +2620,9 @@ template<> class LargeIntRegMultiply< signed __int32, unsigned __int64 > } else { - if( tmp <= (unsigned __int64)IntTraits< signed __int32 >::maxInt+1 ) + if (tmp <= (unsigned __int64)IntTraits::maxInt + 1) { - *pRet = SignedNegation< signed __int32 >::Value( tmp ); + *pRet = SignedNegation::Value(tmp); return; } } @@ -2341,11 +2631,12 @@ template<> class LargeIntRegMultiply< signed __int32, unsigned __int64 > } }; -template<> class LargeIntRegMultiply< unsigned __int32, unsigned __int64 > +template<> +class LargeIntRegMultiply { public: // Becomes ordinary 64-bit multiplication, intrinsic not needed - static bool RegMultiply( unsigned __int32 a, const unsigned __int64& b, unsigned __int32* pRet ) SAFEINT_NOTHROW + 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 @@ -2353,60 +2644,61 @@ template<> class LargeIntRegMultiply< unsigned __int32, unsigned __int64 > // 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; + if ((unsigned __int32)(b >> 32) != 0 && a != 0) return false; unsigned __int64 tmp = b * (unsigned __int64)a; - if( (unsigned __int32)(tmp >> 32) != 0 ) // overflow + 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 + template + 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(); + if ((unsigned __int32)(b >> 32) != 0 && a != 0) E::SafeIntOnOverflow(); unsigned __int64 tmp = b * (unsigned __int64)a; - if( (unsigned __int32)(tmp >> 32) != 0 ) // overflow + if ((unsigned __int32)(tmp >> 32) != 0) // overflow E::SafeIntOnOverflow(); *pRet = (unsigned __int32)tmp; } }; -template<> class LargeIntRegMultiply< unsigned __int32, signed __int64 > +template<> +class LargeIntRegMultiply { public: - static bool RegMultiply( unsigned __int32 a, const signed __int64& b, unsigned __int32* pRet ) SAFEINT_NOTHROW + 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 ); + if (b < 0 && a != 0) return false; + return LargeIntRegMultiply::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 + template + static void RegMultiplyThrow(unsigned __int32 a, const signed __int64& b, unsigned __int32* pRet) SAFEINT_CPP_THROW { - if( b < 0 && a != 0 ) - E::SafeIntOnOverflow(); + if (b < 0 && a != 0) E::SafeIntOnOverflow(); - LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::template RegMultiplyThrow< E >( a, (unsigned __int64)b, pRet ); + LargeIntRegMultiply::template RegMultiplyThrow( + a, (unsigned __int64)b, pRet); } }; -template<> class LargeIntRegMultiply< signed __int64, signed __int64 > +template<> +class LargeIntRegMultiply { public: - static bool RegMultiply( const signed __int64& a, const signed __int64& b, signed __int64* pRet ) SAFEINT_NOTHROW + 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 ); + return IntrinsicMultiplyInt64(a, b, pRet); #else bool aNegative = false; bool bNegative = false; @@ -2415,34 +2707,35 @@ template<> class LargeIntRegMultiply< signed __int64, signed __int64 > __int64 a1 = a; __int64 b1 = b; - if( a1 < 0 ) + if (a1 < 0) { aNegative = true; - a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); } - if( b1 < 0 ) + if (b1 < 0) { bNegative = true; - b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); + b1 = (signed __int64)AbsValueHelper::method>::Abs(b1); } - if( LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( (unsigned __int64)a1, (unsigned __int64)b1, &tmp ) ) + if (LargeIntRegMultiply::RegMultiply( + (unsigned __int64)a1, (unsigned __int64)b1, &tmp)) { // The unsigned multiplication didn't overflow - if( aNegative ^ bNegative ) + if (aNegative ^ bNegative) { // Result must be negative - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + if (tmp <= (unsigned __int64)IntTraits::minInt) { - *pRet = SignedNegation< signed __int64 >::Value( tmp ); + *pRet = SignedNegation::Value(tmp); return true; } } else { // Result must be positive - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + if (tmp <= (unsigned __int64)IntTraits::maxInt) { *pRet = (signed __int64)tmp; return true; @@ -2454,12 +2747,13 @@ template<> class LargeIntRegMultiply< signed __int64, signed __int64 > #endif } - template < typename E > - static void RegMultiplyThrow( const signed __int64& a, const signed __int64& b, signed __int64* pRet ) SAFEINT_CPP_THROW + template + 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(); + if (!IntrinsicMultiplyInt64(a, b, pRet)) E::SafeIntOnOverflow(); #else bool aNegative = false; bool bNegative = false; @@ -2468,34 +2762,35 @@ template<> class LargeIntRegMultiply< signed __int64, signed __int64 > __int64 a1 = a; __int64 b1 = b; - if( a1 < 0 ) + if (a1 < 0) { aNegative = true; - a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); } - if( b1 < 0 ) + if (b1 < 0) { bNegative = true; - b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); + b1 = (signed __int64)AbsValueHelper::method>::Abs(b1); } - LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::template RegMultiplyThrow< E >( (unsigned __int64)a1, (unsigned __int64)b1, &tmp ); + LargeIntRegMultiply::template RegMultiplyThrow( + (unsigned __int64)a1, (unsigned __int64)b1, &tmp); // The unsigned multiplication didn't overflow or we'd be in the exception handler - if( aNegative ^ bNegative ) + if (aNegative ^ bNegative) { // Result must be negative - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + if (tmp <= (unsigned __int64)IntTraits::minInt) { - *pRet = SignedNegation< signed __int64 >::Value( tmp ); + *pRet = SignedNegation::Value(tmp); return; } } else { // Result must be positive - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + if (tmp <= (unsigned __int64)IntTraits::maxInt) { *pRet = (signed __int64)tmp; return; @@ -2507,40 +2802,41 @@ template<> class LargeIntRegMultiply< signed __int64, signed __int64 > } }; -template<> class LargeIntRegMultiply< signed __int64, unsigned __int32 > +template<> +class LargeIntRegMultiply { public: - static bool RegMultiply( const signed __int64& a, unsigned __int32 b, signed __int64* pRet ) SAFEINT_NOTHROW + 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 ); + return IntrinsicMultiplyInt64(a, (signed __int64)b, pRet); #else bool aNegative = false; unsigned __int64 tmp; __int64 a1 = a; - if( a1 < 0 ) + if (a1 < 0) { aNegative = true; - a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); } - if( LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply( (unsigned __int64)a1, b, &tmp ) ) + if (LargeIntRegMultiply::RegMultiply((unsigned __int64)a1, b, &tmp)) { // The unsigned multiplication didn't overflow - if( aNegative ) + if (aNegative) { // Result must be negative - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + if (tmp <= (unsigned __int64)IntTraits::minInt) { - *pRet = SignedNegation< signed __int64 >::Value( tmp ); + *pRet = SignedNegation::Value(tmp); return true; } } else { // Result must be positive - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + if (tmp <= (unsigned __int64)IntTraits::maxInt) { *pRet = (signed __int64)tmp; return true; @@ -2552,39 +2848,39 @@ template<> class LargeIntRegMultiply< signed __int64, unsigned __int32 > #endif } - template < typename E > - static void RegMultiplyThrow( const signed __int64& a, unsigned __int32 b, signed __int64* pRet ) SAFEINT_CPP_THROW + template + 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(); + if (!IntrinsicMultiplyInt64(a, (signed __int64)b, pRet)) E::SafeIntOnOverflow(); #else bool aNegative = false; unsigned __int64 tmp; __int64 a1 = a; - if( a1 < 0 ) + if (a1 < 0) { aNegative = true; - a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); } - LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( (unsigned __int64)a1, b, &tmp ); + LargeIntRegMultiply::template RegMultiplyThrow( + (unsigned __int64)a1, b, &tmp); // The unsigned multiplication didn't overflow - if( aNegative ) + if (aNegative) { // Result must be negative - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + if (tmp <= (unsigned __int64)IntTraits::minInt) { - *pRet = SignedNegation< signed __int64 >::Value( tmp ); + *pRet = SignedNegation::Value(tmp); return; } } else { // Result must be positive - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + if (tmp <= (unsigned __int64)IntTraits::maxInt) { *pRet = (signed __int64)tmp; return; @@ -2596,13 +2892,14 @@ template<> class LargeIntRegMultiply< signed __int64, unsigned __int32 > } }; -template<> class LargeIntRegMultiply< signed __int64, signed __int32 > +template<> +class LargeIntRegMultiply { public: - static bool RegMultiply( const signed __int64& a, signed __int32 b, signed __int64* pRet ) SAFEINT_NOTHROW + 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 ); + return IntrinsicMultiplyInt64(a, (signed __int64)b, pRet); #else bool aNegative = false; bool bNegative = false; @@ -2611,34 +2908,35 @@ template<> class LargeIntRegMultiply< signed __int64, signed __int32 > __int64 a1 = a; __int64 b1 = b; - if( a1 < 0 ) + if (a1 < 0) { aNegative = true; - a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); } - if( b1 < 0 ) + if (b1 < 0) { bNegative = true; - b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); + b1 = (signed __int64)AbsValueHelper::method>::Abs(b1); } - if( LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply( (unsigned __int64)a1, (unsigned __int32)b1, &tmp ) ) + if (LargeIntRegMultiply::RegMultiply( + (unsigned __int64)a1, (unsigned __int32)b1, &tmp)) { // The unsigned multiplication didn't overflow - if( aNegative ^ bNegative ) + if (aNegative ^ bNegative) { // Result must be negative - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + if (tmp <= (unsigned __int64)IntTraits::minInt) { - *pRet = SignedNegation< signed __int64 >::Value( tmp ); + *pRet = SignedNegation::Value(tmp); return true; } } else { // Result must be positive - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + if (tmp <= (unsigned __int64)IntTraits::maxInt) { *pRet = (signed __int64)tmp; return true; @@ -2650,46 +2948,46 @@ template<> class LargeIntRegMultiply< signed __int64, signed __int32 > #endif } - template < typename E > - static void RegMultiplyThrow( signed __int64 a, signed __int32 b, signed __int64* pRet ) SAFEINT_CPP_THROW + template + 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(); + if (!IntrinsicMultiplyInt64(a, (signed __int64)b, pRet)) E::SafeIntOnOverflow(); #else bool aNegative = false; bool bNegative = false; unsigned __int64 tmp; - if( a < 0 ) + if (a < 0) { aNegative = true; - a = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a); + a = (signed __int64)AbsValueHelper::method>::Abs(a); } - if( b < 0 ) + if (b < 0) { bNegative = true; - b = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(b); + b = (signed __int32)AbsValueHelper::method>::Abs(b); } - LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( (unsigned __int64)a, (unsigned __int32)b, &tmp ); + LargeIntRegMultiply::template RegMultiplyThrow( + (unsigned __int64)a, (unsigned __int32)b, &tmp); // The unsigned multiplication didn't overflow - if( aNegative ^ bNegative ) + if (aNegative ^ bNegative) { // Result must be negative - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + if (tmp <= (unsigned __int64)IntTraits::minInt) { - *pRet = SignedNegation< signed __int64 >::Value( tmp ); + *pRet = SignedNegation::Value(tmp); return; } } else { // Result must be positive - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + if (tmp <= (unsigned __int64)IntTraits::maxInt) { *pRet = (signed __int64)tmp; return; @@ -2701,18 +2999,18 @@ template<> class LargeIntRegMultiply< signed __int64, signed __int32 > } }; -template<> class LargeIntRegMultiply< signed __int32, signed __int64 > +template<> +class LargeIntRegMultiply { public: - static bool RegMultiply( signed __int32 a, const signed __int64& b, signed __int32* pRet ) SAFEINT_NOTHROW + 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 (IntrinsicMultiplyInt64(a, b, &tmp)) { - if( tmp > IntTraits< signed __int32 >::maxInt || - tmp < IntTraits< signed __int32 >::minInt ) + if (tmp > IntTraits::maxInt || tmp < IntTraits::minInt) { return false; } @@ -2728,34 +3026,35 @@ template<> class LargeIntRegMultiply< signed __int32, signed __int64 > unsigned __int32 tmp; __int64 b1 = b; - if( a < 0 ) + if (a < 0) { aNegative = true; - a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); + a = (signed __int32)AbsValueHelper::method>::Abs(a); } - if( b1 < 0 ) + if (b1 < 0) { bNegative = true; - b1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b1); + b1 = (signed __int64)AbsValueHelper::method>::Abs(b1); } - if( LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::RegMultiply( (unsigned __int32)a, (unsigned __int64)b1, &tmp ) ) + if (LargeIntRegMultiply::RegMultiply( + (unsigned __int32)a, (unsigned __int64)b1, &tmp)) { // The unsigned multiplication didn't overflow - if( aNegative ^ bNegative ) + if (aNegative ^ bNegative) { // Result must be negative - if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::minInt ) + if (tmp <= (unsigned __int32)IntTraits::minInt) { - *pRet = SignedNegation< signed __int32 >::Value( tmp ); + *pRet = SignedNegation::Value(tmp); return true; } } else { // Result must be positive - if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::maxInt ) + if (tmp <= (unsigned __int32)IntTraits::maxInt) { *pRet = (signed __int32)tmp; return true; @@ -2767,16 +3066,15 @@ template<> class LargeIntRegMultiply< signed __int32, signed __int64 > #endif } - template < typename E > - static void RegMultiplyThrow( signed __int32 a, const signed __int64& b, signed __int32* pRet ) SAFEINT_CPP_THROW + template + 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 (IntrinsicMultiplyInt64(a, b, &tmp)) { - if( tmp > IntTraits< signed __int32 >::maxInt || - tmp < IntTraits< signed __int32 >::minInt ) + if (tmp > IntTraits::maxInt || tmp < IntTraits::minInt) { E::SafeIntOnOverflow(); } @@ -2792,34 +3090,35 @@ template<> class LargeIntRegMultiply< signed __int32, signed __int64 > unsigned __int32 tmp; signed __int64 b2 = b; - if( a < 0 ) + if (a < 0) { aNegative = true; - a = (signed __int32)AbsValueHelper< signed __int32, GetAbsMethod< signed __int32 >::method >::Abs(a); + a = (signed __int32)AbsValueHelper::method>::Abs(a); } - if( b < 0 ) + if (b < 0) { bNegative = true; - b2 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(b2); + b2 = (signed __int64)AbsValueHelper::method>::Abs(b2); } - LargeIntRegMultiply< unsigned __int32, unsigned __int64 >::template RegMultiplyThrow< E >( (unsigned __int32)a, (unsigned __int64)b2, &tmp ); + LargeIntRegMultiply::template RegMultiplyThrow( + (unsigned __int32)a, (unsigned __int64)b2, &tmp); // The unsigned multiplication didn't overflow - if( aNegative ^ bNegative ) + if (aNegative ^ bNegative) { // Result must be negative - if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::minInt ) + if (tmp <= (unsigned __int32)IntTraits::minInt) { - *pRet = SignedNegation< signed __int32 >::Value( tmp ); + *pRet = SignedNegation::Value(tmp); return; } } else { // Result must be positive - if( tmp <= (unsigned __int32)IntTraits< signed __int32 >::maxInt ) + if (tmp <= (unsigned __int32)IntTraits::maxInt) { *pRet = (signed __int32)tmp; return; @@ -2831,39 +3130,41 @@ template<> class LargeIntRegMultiply< signed __int32, signed __int64 > } }; -template<> class LargeIntRegMultiply< signed __int64, unsigned __int64 > +template<> +class LargeIntRegMultiply { 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 + 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 ) + if (a1 < 0) { aNegative = true; - a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); } - if( LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( (unsigned __int64)a1, (unsigned __int64)b, &tmp ) ) + if (LargeIntRegMultiply::RegMultiply( + (unsigned __int64)a1, (unsigned __int64)b, &tmp)) { // The unsigned multiplication didn't overflow - if( aNegative ) + if (aNegative) { // Result must be negative - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + if (tmp <= (unsigned __int64)IntTraits::minInt) { - *pRet = SignedNegation< signed __int64 >::Value( tmp ); + *pRet = SignedNegation::Value(tmp); return true; } } else { // Result must be positive - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + if (tmp <= (unsigned __int64)IntTraits::maxInt) { *pRet = (signed __int64)tmp; return true; @@ -2874,35 +3175,38 @@ template<> class LargeIntRegMultiply< signed __int64, unsigned __int64 > return false; } - template < typename E > - static void RegMultiplyThrow( const signed __int64& a, const unsigned __int64& b, signed __int64* pRet ) SAFEINT_CPP_THROW + template + 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 ) + if (a1 < 0) { aNegative = true; - a1 = (signed __int64)AbsValueHelper< signed __int64, GetAbsMethod< signed __int64 >::method >::Abs(a1); + a1 = (signed __int64)AbsValueHelper::method>::Abs(a1); } - if( LargeIntRegMultiply< unsigned __int64, unsigned __int64 >::RegMultiply( (unsigned __int64)a1, (unsigned __int64)b, &tmp ) ) + if (LargeIntRegMultiply::RegMultiply( + (unsigned __int64)a1, (unsigned __int64)b, &tmp)) { // The unsigned multiplication didn't overflow - if( aNegative ) + if (aNegative) { // Result must be negative - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::minInt ) + if (tmp <= (unsigned __int64)IntTraits::minInt) { - *pRet = SignedNegation< signed __int64 >::Value( tmp ); + *pRet = SignedNegation::Value(tmp); return; } } else { // Result must be positive - if( tmp <= (unsigned __int64)IntTraits< signed __int64 >::maxInt ) + if (tmp <= (unsigned __int64)IntTraits::maxInt) { *pRet = (signed __int64)tmp; return; @@ -2919,63 +3223,70 @@ template<> class LargeIntRegMultiply< signed __int64, unsigned __int64 > // 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 > +template +class MultiplicationHelper { public: // T, U are unsigned __int64 - static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW { - C_ASSERT( IntTraits::isUint64 && IntTraits::isUint64 ); + 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) ); + return LargeIntRegMultiply::RegMultiply( + t1, u1, reinterpret_cast(&ret)); } - template < typename E > + template static void MultiplyThrow(const unsigned __int64& t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW { - C_ASSERT( IntTraits::isUint64 && IntTraits::isUint64 ); + 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) ); + LargeIntRegMultiply::template RegMultiplyThrow( + t1, u1, reinterpret_cast(&ret)); } }; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Uint > +template +class MultiplicationHelper { 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 + static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW { - C_ASSERT( IntTraits::isUint64 ); + C_ASSERT(IntTraits::isUint64); unsigned __int64 t1 = t; - return LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::RegMultiply( t1, (unsigned __int32)u, reinterpret_cast(&ret) ); + return LargeIntRegMultiply::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 + template + static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW { - C_ASSERT( IntTraits::isUint64 ); + C_ASSERT(IntTraits::isUint64); unsigned __int64 t1 = t; - LargeIntRegMultiply< unsigned __int64, unsigned __int32 >::template RegMultiplyThrow< E >( t1, (unsigned __int32)u, reinterpret_cast(&ret) ); + LargeIntRegMultiply::template RegMultiplyThrow( + t1, (unsigned __int32)u, reinterpret_cast(&ret)); } }; // converse of the previous function -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_UintUint64 > +template +class MultiplicationHelper { 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 ); + 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) ) + if (LargeIntRegMultiply::RegMultiply(t, u1, &tmp) && + SafeCastHelper::method>::Cast(tmp, ret)) { return true; } @@ -2983,75 +3294,83 @@ template < typename T, typename U > class MultiplicationHelper< T, U, Multiplica return false; } - template < typename E > + template static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW { - C_ASSERT( IntTraits::isUint64 ); + 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); + LargeIntRegMultiply::template RegMultiplyThrow(t, u1, &tmp); + SafeCastHelper::method>::template CastThrow(tmp, + ret); } }; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Int > +template +class MultiplicationHelper { 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 ); + C_ASSERT(IntTraits::isUint64); unsigned __int64 t1 = t; - return LargeIntRegMultiply< unsigned __int64, signed __int32 >::RegMultiply(t1, (signed __int32)u, reinterpret_cast< unsigned __int64* >(&ret)); + return LargeIntRegMultiply::RegMultiply( + t1, (signed __int32)u, reinterpret_cast(&ret)); } - template < typename E > + template static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW { - C_ASSERT( IntTraits::isUint64 ); + 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)); + LargeIntRegMultiply::template RegMultiplyThrow( + t1, (signed __int32)u, reinterpret_cast(&ret)); } }; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Int64 > +template +class MultiplicationHelper { 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 ); + 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)); + __int64 u1 = u; + return LargeIntRegMultiply::RegMultiply( + t1, u1, reinterpret_cast(&ret)); } - template < typename E > + template static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW { - C_ASSERT( IntTraits::isUint64 && IntTraits::isInt64 ); + 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)); + __int64 u1 = u; + LargeIntRegMultiply::template RegMultiplyThrow( + t1, u1, reinterpret_cast(&ret)); } }; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_UintInt64 > +template +class MultiplicationHelper { 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; + 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) ) + if (LargeIntRegMultiply::RegMultiply((unsigned __int32)t, u1, &tmp) && + SafeCastHelper::method>::Cast(tmp, ret)) { return true; } @@ -3059,95 +3378,103 @@ template < typename T, typename U > class MultiplicationHelper< T, U, Multiplica return false; } - template < typename E > + template static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW { - C_ASSERT( IntTraits::isInt64 ); - __int64 u1 = u; + 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); + LargeIntRegMultiply::template RegMultiplyThrow((unsigned __int32)t, u1, &tmp); + SafeCastHelper::method>::template CastThrow(tmp, + ret); } }; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Uint > +template +class MultiplicationHelper { public: // T is __int64 // U is unsigned up to 32-bit - static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + 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) ); + 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 + template + 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) ); + C_ASSERT(IntTraits::isInt64); + __int64 t1 = t; + LargeIntRegMultiply<__int64, unsigned __int32>::template RegMultiplyThrow( + t1, (unsigned __int32)u, reinterpret_cast<__int64*>(&ret)); } }; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Int64 > +template +class MultiplicationHelper { public: // T, U are __int64 - static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + 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) ); + 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 + template + 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)); + C_ASSERT(IntTraits::isInt64 && IntTraits::isInt64); + __int64 t1 = t; + __int64 u1 = u; + LargeIntRegMultiply<__int64, __int64>::template RegMultiplyThrow(t1, u1, reinterpret_cast<__int64*>(&ret)); } }; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Int > +template +class MultiplicationHelper { public: // T is __int64 // U is signed up to 32-bit - static bool Multiply( const T& t, U u, T& ret ) SAFEINT_NOTHROW + 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)); + 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 + template + 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)); + C_ASSERT(IntTraits::isInt64); + __int64 t1 = t; + LargeIntRegMultiply<__int64, __int32>::template RegMultiplyThrow( + t1, (__int32)u, reinterpret_cast<__int64*>(&ret)); } }; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_IntUint64 > +template +class MultiplicationHelper { 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 ); + 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 ) ) + if (LargeIntRegMultiply<__int32, unsigned __int64>::RegMultiply((__int32)t, u1, &tmp) && + SafeCastHelper::method>::Cast(tmp, ret)) { return true; } @@ -3155,54 +3482,57 @@ template < typename T, typename U > class MultiplicationHelper< T, U, Multiplica return false; } - template < typename E > + template static void MultiplyThrow(T t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW { - C_ASSERT( IntTraits::isUint64 ); + 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 ); + LargeIntRegMultiply<__int32, unsigned __int64>::template RegMultiplyThrow((__int32)t, u1, &tmp); + SafeCastHelper::method>::template CastThrow(tmp, ret); } }; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Uint64> +template +class MultiplicationHelper { public: // T is __int64 // U is unsigned __int64 - static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW { - C_ASSERT( IntTraits::isInt64 && IntTraits::isUint64 ); - __int64 t1 = t; + 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) ); + 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 + template + static void MultiplyThrow(const __int64& t, const unsigned __int64& u, T& ret) SAFEINT_CPP_THROW { - C_ASSERT( IntTraits::isInt64 && IntTraits::isUint64 ); - __int64 t1 = t; + 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) ); + LargeIntRegMultiply<__int64, unsigned __int64>::template RegMultiplyThrow( + t1, u1, reinterpret_cast<__int64*>(&ret)); } }; -template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_IntInt64> +template +class MultiplicationHelper { public: // T is signed, up to 32-bit // U is __int64 - static bool Multiply( T t, const U& u, T& ret ) SAFEINT_NOTHROW + static bool Multiply(T t, const U& u, T& ret) SAFEINT_NOTHROW { - C_ASSERT( IntTraits::isInt64 ); + 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 ) ) + if (LargeIntRegMultiply<__int32, __int64>::RegMultiply((__int32)t, u1, &tmp) && + SafeCastHelper::method>::Cast(tmp, ret)) { return true; } @@ -3210,15 +3540,15 @@ template < typename T, typename U > class MultiplicationHelper< T, U, Multiplica return false; } - template < typename E > + template static void MultiplyThrow(T t, const U& u, T& ret) SAFEINT_CPP_THROW { - C_ASSERT( IntTraits::isInt64 ); + 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 ); + LargeIntRegMultiply<__int32, __int64>::template RegMultiplyThrow((__int32)t, u1, &tmp); + SafeCastHelper::method>::template CastThrow(tmp, ret); } }; @@ -3232,82 +3562,85 @@ enum DivisionState DivisionState_SignedSigned }; -template < typename T, typename U > class DivisionMethod +template +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) + method = + (SafeIntCompare::isBothUnsigned + ? DivisionState_OK + : (!IntTraits::isSigned && IntTraits::isSigned) + ? DivisionState_UnsignedSigned + : (IntTraits::isSigned && IntTraits::isUint32 && IntTraits::isLT64Bit) + ? DivisionState_SignedUnsigned32 + : (IntTraits::isSigned && IntTraits::isUint64) + ? DivisionState_SignedUnsigned64 + : (IntTraits::isSigned && !IntTraits::isSigned) ? DivisionState_SignedUnsigned + : DivisionState_SignedSigned) }; }; -template < typename T, typename U, int state > class DivisionHelper; +template +class DivisionHelper; -template < typename T, typename U > class DivisionHelper< T, U, DivisionState_OK > +template +class DivisionHelper { public: - static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW { - if( u == 0 ) - return SafeIntDivideByZero; + if (u == 0) return SafeIntDivideByZero; - if( t == 0 ) + if (t == 0) { result = 0; return SafeIntNoError; } - result = (T)( t/u ); + result = (T)(t / u); return SafeIntNoError; } - template < typename E > - static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + template + static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW { - if( u == 0 ) - E::SafeIntOnDivZero(); + if (u == 0) E::SafeIntOnDivZero(); - if( t == 0 ) + if (t == 0) { result = 0; return; } - result = (T)( t/u ); + result = (T)(t / u); } }; -template < typename T, typename U > class DivisionHelper< T, U, DivisionState_UnsignedSigned> +template +class DivisionHelper { public: - static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW { + if (u == 0) return SafeIntDivideByZero; - if( u == 0 ) - return SafeIntDivideByZero; - - if( t == 0 ) + if (t == 0) { result = 0; return SafeIntNoError; } - if( u > 0 ) + if (u > 0) { - result = (T)( t/u ); + 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 ) + if (AbsValueHelper::method>::Abs(u) > t) { result = 0; return SafeIntNoError; @@ -3316,28 +3649,26 @@ template < typename T, typename U > class DivisionHelper< T, U, DivisionState_Un return SafeIntArithmeticOverflow; } - template < typename E > - static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + template + static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW { + if (u == 0) E::SafeIntOnDivZero(); - if( u == 0 ) - E::SafeIntOnDivZero(); - - if( t == 0 ) + if (t == 0) { result = 0; return; } - if( u > 0 ) + if (u > 0) { - result = (T)( t/u ); + 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 ) + if (AbsValueHelper::method>::Abs(u) > t) { result = 0; return; @@ -3347,15 +3678,15 @@ template < typename T, typename U > class DivisionHelper< T, U, DivisionState_Un } }; -template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned32 > +template +class DivisionHelper { public: - static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW { - if( u == 0 ) - return SafeIntDivideByZero; + if (u == 0) return SafeIntDivideByZero; - if( t == 0 ) + if (t == 0) { result = 0; return SafeIntNoError; @@ -3365,23 +3696,23 @@ template < typename T, typename U > class DivisionHelper< T, U, DivisionState_Si // 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 ); + if (t > 0) + result = (T)(t / u); else - result = (T)( (__int64)t/(__int64)u ); + 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 + template + static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW { - if( u == 0 ) + if (u == 0) { E::SafeIntOnDivZero(); } - if( t == 0 ) + if (t == 0) { result = 0; return; @@ -3391,41 +3722,42 @@ template < typename T, typename U > class DivisionHelper< T, U, DivisionState_Si // 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 ); + if (t > 0) + result = (T)(t / u); else - result = (T)( (__int64)t/(__int64)u ); + result = (T)((__int64)t / (__int64)u); } }; -template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned64 > +template +class DivisionHelper { public: - static SafeIntError Divide( const T& t, const unsigned __int64& u, T& result ) SAFEINT_NOTHROW + static SafeIntError Divide(const T& t, const unsigned __int64& u, T& result) SAFEINT_NOTHROW { - C_ASSERT( IntTraits< U >::isUint64 ); + C_ASSERT(IntTraits::isUint64); - if( u == 0 ) + if (u == 0) { return SafeIntDivideByZero; } - if( t == 0 ) + if (t == 0) { result = 0; return SafeIntNoError; } - if( u <= (unsigned __int64)IntTraits< T >::maxInt ) + if (u <= (unsigned __int64)IntTraits::maxInt) { // Else u can safely be cast to T - if( CompileConst< sizeof( T ) < sizeof( __int64 )>::Value() ) - result = (T)( (int)t/(int)u ); + if (CompileConst::Value()) + result = (T)((int)t / (int)u); else - result = (T)((__int64)t/(__int64)u); + result = (T)((__int64)t / (__int64)u); } else // Corner case - if( t == IntTraits< T >::minInt && u == (unsigned __int64)IntTraits< T >::minInt ) + if (t == IntTraits::minInt && u == (unsigned __int64)IntTraits::minInt) { // Min int divided by it's own magnitude is -1 result = -1; @@ -3437,32 +3769,32 @@ template < typename T, typename U > class DivisionHelper< T, U, DivisionState_Si return SafeIntNoError; } - template < typename E > - static void DivideThrow( const T& t, const unsigned __int64& u, T& result ) SAFEINT_CPP_THROW + template + static void DivideThrow(const T& t, const unsigned __int64& u, T& result) SAFEINT_CPP_THROW { - C_ASSERT( IntTraits< U >::isUint64 ); + C_ASSERT(IntTraits::isUint64); - if( u == 0 ) + if (u == 0) { E::SafeIntOnDivZero(); } - if( t == 0 ) + if (t == 0) { result = 0; return; } - if( u <= (unsigned __int64)IntTraits< T >::maxInt ) + if (u <= (unsigned __int64)IntTraits::maxInt) { // Else u can safely be cast to T - if( CompileConst< sizeof( T ) < sizeof( __int64 ) >::Value() ) - result = (T)( (int)t/(int)u ); + if (CompileConst::Value()) + result = (T)((int)t / (int)u); else - result = (T)((__int64)t/(__int64)u); + result = (T)((__int64)t / (__int64)u); } else // Corner case - if( t == IntTraits< T >::minInt && u == (unsigned __int64)IntTraits< T >::minInt ) + if (t == IntTraits::minInt && u == (unsigned __int64)IntTraits::minInt) { // Min int divided by it's own magnitude is -1 result = -1; @@ -3474,89 +3806,89 @@ template < typename T, typename U > class DivisionHelper< T, U, DivisionState_Si } }; -template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned> +template +class DivisionHelper { 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 + static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW { - if( u == 0 ) + if (u == 0) { return SafeIntDivideByZero; } - if( t == 0 ) + if (t == 0) { result = 0; return SafeIntNoError; } - result = (T)( t/u ); + result = (T)(t / u); return SafeIntNoError; } - template < typename E > - static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + template + static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW { - if( u == 0 ) + if (u == 0) { E::SafeIntOnDivZero(); } - if( t == 0 ) + if (t == 0) { result = 0; return; } - result = (T)( t/u ); + result = (T)(t / u); } }; -template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedSigned> +template +class DivisionHelper { public: - static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + static SafeIntError Divide(const T& t, const U& u, T& result) SAFEINT_NOTHROW { - if( u == 0 ) + if (u == 0) { return SafeIntDivideByZero; } - if( t == 0 ) + if (t == 0) { result = 0; return SafeIntNoError; } // Must test for corner case - if( t == IntTraits< T >::minInt && u == (U)-1 ) - return SafeIntArithmeticOverflow; + if (t == IntTraits::minInt && u == (U)-1) return SafeIntArithmeticOverflow; - result = (T)( t/u ); + result = (T)(t / u); return SafeIntNoError; } - template < typename E > - static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + template + static void DivideThrow(const T& t, const U& u, T& result) SAFEINT_CPP_THROW { - if(u == 0) + if (u == 0) { E::SafeIntOnDivZero(); } - if( t == 0 ) + if (t == 0) { result = 0; return; } // Must test for corner case - if( t == IntTraits< T >::minInt && u == (U)-1 ) - E::SafeIntOnOverflow(); + if (t == IntTraits::minInt && u == (U)-1) E::SafeIntOnOverflow(); - result = (T)( t/u ); + result = (T)(t / u); } }; @@ -3580,54 +3912,88 @@ enum AdditionState AdditionState_Error }; -template< typename T, typename U > +template 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) + // unsigned-unsigned + method = + (IntRegion::IntZone_UintLT32_UintLT32 + ? AdditionState_CastIntCheckMax + : (IntRegion::IntZone_Uint32_UintLT64) + ? AdditionState_CastUintCheckOverflow + : (IntRegion::IntZone_UintLT32_Uint32) + ? AdditionState_CastUintCheckOverflowMax + : (IntRegion::IntZone_Uint64_Uint) + ? AdditionState_CastUint64CheckOverflow + : (IntRegion::IntZone_UintLT64_Uint64) + ? AdditionState_CastUint64CheckOverflowMax + : + // unsigned-signed + (IntRegion::IntZone_UintLT32_IntLT32) + ? AdditionState_CastIntCheckSafeIntMinMax + : (IntRegion::IntZone_Uint32_IntLT64 || + IntRegion::IntZone_UintLT32_Int32) + ? AdditionState_CastInt64CheckSafeIntMinMax + : (IntRegion::IntZone_Uint64_Int || + IntRegion::IntZone_Uint64_Int64) + ? AdditionState_CastUint64CheckSafeIntMinMax + : (IntRegion::IntZone_UintLT64_Int64) + ? AdditionState_CastUint64CheckSafeIntMinMax2 + : + // signed-signed + (IntRegion::IntZone_IntLT32_IntLT32) + ? AdditionState_CastIntCheckSafeIntMinMax + : (IntRegion::IntZone_Int32_IntLT64 || + IntRegion::IntZone_IntLT32_Int32) + ? AdditionState_CastInt64CheckSafeIntMinMax + : (IntRegion::IntZone_Int64_Int || + IntRegion::IntZone_Int64_Int64) + ? AdditionState_CastInt64CheckOverflow + : (IntRegion::IntZone_IntLT64_Int64) + ? AdditionState_CastInt64CheckOverflowSafeIntMinMax + : + // signed-unsigned + (IntRegion:: + IntZone_IntLT32_UintLT32) + ? AdditionState_CastIntCheckMax + : (IntRegion:: + IntZone_Int32_UintLT32 || + IntRegion:: + IntZone_IntLT64_Uint32) + ? AdditionState_CastInt64CheckMax + : (IntRegion:: + IntZone_Int64_UintLT64) + ? AdditionState_CastInt64CheckOverflowMax + : (IntRegion:: + 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 +class AdditionHelper; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastIntCheckMax > +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW { - //16-bit or less unsigned addition + // 16-bit or less unsigned addition __int32 tmp = lhs + rhs; - if( tmp <= (__int32)IntTraits< T >::maxInt ) + if (tmp <= (__int32)IntTraits::maxInt) { result = (T)tmp; return true; @@ -3636,13 +4002,13 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C return false; } - template < typename E > - static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW { - //16-bit or less unsigned addition + // 16-bit or less unsigned addition __int32 tmp = lhs + rhs; - if( tmp <= (__int32)IntTraits< T >::maxInt ) + if (tmp <= (__int32)IntTraits::maxInt) { result = (T)tmp; return; @@ -3652,16 +4018,17 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUintCheckOverflow > +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 ) + // we added didn't get smaller + if (tmp >= lhs) { result = (T)tmp; return true; @@ -3669,14 +4036,14 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C return false; } - template < typename E > - static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) + // we added didn't get smaller + if (tmp >= lhs) { result = (T)tmp; return; @@ -3685,16 +4052,17 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUintCheckOverflowMax> +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 ) + if (tmp >= lhs && tmp <= IntTraits::maxInt) { result = (T)tmp; return true; @@ -3702,14 +4070,14 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C return false; } - template < typename E > - static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW { - //32-bit or less - both are unsigned + // 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 ) + if (tmp >= lhs && tmp <= IntTraits::maxInt) { result = (T)tmp; return; @@ -3718,16 +4086,17 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckOverflow> +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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) + if (tmp >= lhs) { result = (T)tmp; return true; @@ -3736,14 +4105,14 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C return false; } - template < typename E > - static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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) + if (tmp >= lhs) { result = (T)tmp; return; @@ -3753,16 +4122,17 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckOverflowMax > +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW { - //lhs unsigned __int64, rhs unsigned + // 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 ) + if (tmp >= lhs && tmp <= IntTraits::maxInt) { result = (T)tmp; return true; @@ -3771,14 +4141,14 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C return false; } - template < typename E > - static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW { - //lhs unsigned __int64, rhs unsigned + // 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 ) + if (tmp >= lhs && tmp <= IntTraits::maxInt) { result = (T)tmp; return; @@ -3788,15 +4158,16 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastIntCheckSafeIntMinMax > +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 ) + if (tmp <= (__int32)IntTraits::maxInt && tmp >= (__int32)IntTraits::minInt) { result = (T)tmp; return true; @@ -3805,13 +4176,13 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C return false; } - template < typename E > - static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) + if (tmp <= (__int32)IntTraits::maxInt && tmp >= (__int32)IntTraits::minInt) { result = (T)tmp; return; @@ -3821,15 +4192,16 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckSafeIntMinMax > +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 ) + if (tmp <= (__int64)IntTraits::maxInt && tmp >= (__int64)IntTraits::minInt) { result = (T)tmp; return true; @@ -3838,13 +4210,13 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C return false; } - template < typename E > - static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) + if (tmp <= (__int64)IntTraits::maxInt && tmp >= (__int64)IntTraits::minInt) { result = (T)tmp; return; @@ -3854,15 +4226,16 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckMax > +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 ) + if (tmp <= IntTraits::maxInt) { result = (T)tmp; return true; @@ -3871,13 +4244,13 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C return false; } - template < typename E > - static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) + if (tmp <= IntTraits::maxInt) { result = (T)tmp; return; @@ -3887,20 +4260,21 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckSafeIntMinMax > +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 ) + if (rhs < 0) { // So we're effectively subtracting - tmp = AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + tmp = AbsValueHelper::method>::Abs(rhs); - if( tmp <= lhs ) + if (tmp <= lhs) { result = lhs - tmp; return true; @@ -3912,7 +4286,7 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; // We added and it did not become smaller - if( tmp >= lhs ) + if (tmp >= lhs) { result = (T)tmp; return true; @@ -3922,18 +4296,18 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C return false; } - template < typename E > - static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) + if (rhs < 0) { // So we're effectively subtracting - tmp = AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + tmp = AbsValueHelper::method>::Abs(rhs); - if( tmp <= lhs ) + if (tmp <= lhs) { result = lhs - tmp; return; @@ -3945,7 +4319,7 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; // We added and it did not become smaller - if( tmp >= lhs ) + if (tmp >= lhs) { result = (T)tmp; return; @@ -3956,17 +4330,18 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckSafeIntMinMax2> +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 (rhs < 0) { - if( lhs >= ~(unsigned __int64)( rhs ) + 1 )//negation is safe, since rhs is 64-bit + if (lhs >= ~(unsigned __int64)(rhs) + 1) // negation is safe, since rhs is 64-bit { - result = (T)( lhs + rhs ); + result = (T)(lhs + rhs); return true; } } @@ -3977,7 +4352,7 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C // 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 ) + if (tmp <= IntTraits::maxInt) { result = (T)tmp; return true; @@ -3986,15 +4361,15 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C return false; } - template < typename E > - static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 (rhs < 0) { - if( lhs >= ~(unsigned __int64)( rhs ) + 1) //negation is safe, since rhs is 64-bit + if (lhs >= ~(unsigned __int64)(rhs) + 1) // negation is safe, since rhs is 64-bit { - result = (T)( lhs + rhs ); + result = (T)(lhs + rhs); return; } } @@ -4005,7 +4380,7 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C // 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 ) + if (tmp <= IntTraits::maxInt) { result = (T)tmp; return; @@ -4015,65 +4390,63 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflow> +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 ) + if (lhs >= 0) { // mixed sign cannot overflow - if( rhs >= 0 && tmp < lhs ) - return false; + if (rhs >= 0 && tmp < lhs) return false; } else { // lhs negative - if( rhs < 0 && tmp > lhs ) - return false; + 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 + template + 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 ) + if (lhs >= 0) { // mixed sign cannot overflow - if( rhs >= 0 && tmp < lhs ) - E::SafeIntOnOverflow(); + if (rhs >= 0 && tmp < lhs) E::SafeIntOnOverflow(); } else { // lhs negative - if( rhs < 0 && tmp > lhs ) - E::SafeIntOnOverflow(); + if (rhs < 0 && tmp > lhs) E::SafeIntOnOverflow(); } result = (T)tmp; } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflowSafeIntMinMax> +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW { - //rhs is signed __int64, lhs signed + // 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 ) + if (AdditionHelper<__int64, __int64, AdditionState_CastInt64CheckOverflow>::Addition( + (__int64)lhs, (__int64)rhs, tmp) && + tmp <= IntTraits::maxInt && tmp >= IntTraits::minInt) { result = (T)tmp; return true; @@ -4082,16 +4455,16 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C return false; } - template < typename E > - static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + static void AdditionThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW { - //rhs is signed __int64, lhs signed + // rhs is signed __int64, lhs signed __int64 tmp; - AdditionHelper< __int64, __int64, AdditionState_CastInt64CheckOverflow >::AdditionThrow< E >( (__int64)lhs, (__int64)rhs, tmp ); + AdditionHelper<__int64, __int64, AdditionState_CastInt64CheckOverflow>::AdditionThrow( + (__int64)lhs, (__int64)rhs, tmp); - if( tmp <= IntTraits< T >::maxInt && - tmp >= IntTraits< T >::minInt ) + if (tmp <= IntTraits::maxInt && tmp >= IntTraits::minInt) { result = (T)tmp; return; @@ -4101,15 +4474,16 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflowMax> +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + static bool Addition(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW { - //lhs is signed __int64, rhs unsigned < 64-bit + // lhs is signed __int64, rhs unsigned < 64-bit unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)rhs; - if( (__int64)tmp >= lhs ) + if ((__int64)tmp >= lhs) { result = (T)(__int64)tmp; return true; @@ -4118,15 +4492,15 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C return false; } - template < typename E > - static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) + if ((__int64)tmp >= lhs) { result = (T)(__int64)tmp; return; @@ -4136,18 +4510,19 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_C } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_ManualCheckInt64Uint64 > +template +class AdditionHelper { public: - static bool Addition( const __int64& lhs, const unsigned __int64& rhs, __int64& result ) SAFEINT_NOTHROW + static bool Addition(const __int64& lhs, const unsigned __int64& rhs, __int64& result) SAFEINT_NOTHROW { - C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); + C_ASSERT(IntTraits::isInt64 && IntTraits::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 ) + if ((__int64)tmp >= lhs) { result = (__int64)tmp; return true; @@ -4156,14 +4531,14 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_M return false; } - template < typename E > - static void AdditionThrow( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_CPP_THROW + template + static void AdditionThrow(const __int64& lhs, const unsigned __int64& rhs, T& result) SAFEINT_CPP_THROW { - C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); + C_ASSERT(IntTraits::isInt64 && IntTraits::isUint64); // rhs is unsigned __int64, lhs __int64 unsigned __int64 tmp = (unsigned __int64)lhs + rhs; - if( (__int64)tmp >= lhs ) + if ((__int64)tmp >= lhs) { result = (__int64)tmp; return; @@ -4173,40 +4548,43 @@ template < typename T, typename U > class AdditionHelper < T, U, AdditionState_M } }; -template < typename T, typename U > class AdditionHelper < T, U, AdditionState_ManualCheck> +template +class AdditionHelper { public: - static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 ) + 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 ) ) + if ((__int32)tmp >= lhs && + SafeCastHelper::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 + template + 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 ) + 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 ) + if ((__int32)tmp >= lhs) { - SafeCastHelper< T, __int32, GetCastMethod< T, __int32 >::method >::template CastThrow< E >( (__int32)tmp, result ); + SafeCastHelper::method>::template CastThrow((__int32)tmp, + result); return; } } @@ -4242,103 +4620,133 @@ enum SubtractionState SubtractionState_Error }; -template < typename T, typename U > class SubtractionMethod +template +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-unsigned + method = + ((IntRegion::IntZone_UintLT32_UintLT32 || (IntRegion::IntZone_Uint32_UintLT64) || + (IntRegion::IntZone_UintLT32_Uint32) || (IntRegion::IntZone_Uint64_Uint) || + (IntRegion::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) + (IntRegion::IntZone_UintLT32_IntLT32) + ? SubtractionState_CastIntCheckSafeIntMinMax + : (IntRegion::IntZone_Uint32_IntLT64 || IntRegion::IntZone_UintLT32_Int32) + ? SubtractionState_CastInt64CheckSafeIntMinMax + : (IntRegion::IntZone_Uint64_Int || IntRegion::IntZone_Uint64_Int64) + ? SubtractionState_Uint64Int + : (IntRegion::IntZone_UintLT64_Int64) ? SubtractionState_UintInt64 : + // signed-signed + (IntRegion::IntZone_IntLT32_IntLT32) + ? SubtractionState_CastIntCheckSafeIntMinMax + : (IntRegion::IntZone_Int32_IntLT64 || + IntRegion::IntZone_IntLT32_Int32) + ? SubtractionState_CastInt64CheckSafeIntMinMax + : (IntRegion::IntZone_Int64_Int || + IntRegion::IntZone_Int64_Int64) + ? SubtractionState_Int64Int + : (IntRegion::IntZone_IntLT64_Int64) + ? SubtractionState_IntInt64 + : + // signed-unsigned + (IntRegion::IntZone_IntLT32_UintLT32) + ? SubtractionState_CastIntCheckMin + : (IntRegion::IntZone_Int32_UintLT32 || + IntRegion::IntZone_IntLT64_Uint32) + ? SubtractionState_CastInt64CheckMin + : (IntRegion::IntZone_Int64_UintLT64) + ? SubtractionState_Int64Uint + : (IntRegion::IntZone_Int_Uint64) + ? SubtractionState_IntUint64 + : (IntRegion:: + IntZone_Int64_Uint64) + ? SubtractionState_Int64Uint64 + : SubtractionState_Error) }; }; // this is for the case of U - SafeInt< T, E > -template < typename T, typename U > class SubtractionMethod2 +template +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-unsigned + method = + ((IntRegion::IntZone_UintLT32_UintLT32 || (IntRegion::IntZone_Uint32_UintLT64) || + (IntRegion::IntZone_UintLT32_Uint32) || (IntRegion::IntZone_Uint64_Uint) || + (IntRegion::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) + (IntRegion::IntZone_UintLT32_IntLT32) + ? SubtractionState_CastIntCheckSafeIntMinMax2 + : (IntRegion::IntZone_Uint32_IntLT64 || IntRegion::IntZone_UintLT32_Int32) + ? SubtractionState_CastInt64CheckSafeIntMinMax2 + : (IntRegion::IntZone_Uint64_Int || IntRegion::IntZone_Uint64_Int64) + ? SubtractionState_Uint64Int2 + : (IntRegion::IntZone_UintLT64_Int64) ? SubtractionState_UintInt642 : + // signed-signed + (IntRegion::IntZone_IntLT32_IntLT32) + ? SubtractionState_CastIntCheckSafeIntMinMax2 + : (IntRegion::IntZone_Int32_IntLT64 || + IntRegion::IntZone_IntLT32_Int32) + ? SubtractionState_CastInt64CheckSafeIntMinMax2 + : (IntRegion::IntZone_Int64_Int || + IntRegion::IntZone_Int64_Int64) + ? SubtractionState_Int64Int2 + : (IntRegion::IntZone_IntLT64_Int64) + ? SubtractionState_IntInt642 + : + // signed-unsigned + (IntRegion::IntZone_IntLT32_UintLT32) + ? SubtractionState_CastIntCheckSafeIntMinMax2 + : (IntRegion::IntZone_Int32_UintLT32 || + IntRegion::IntZone_IntLT64_Uint32) + ? SubtractionState_CastInt64CheckSafeIntMinMax2 + : (IntRegion::IntZone_Int64_UintLT64) + ? SubtractionState_Int64Uint2 + : (IntRegion::IntZone_Int_Uint64) + ? SubtractionState_IntUint642 + : (IntRegion:: + IntZone_Int64_Uint64) + ? SubtractionState_Int64Uint642 + : SubtractionState_Error) }; }; -template < typename T, typename U, int method > class SubtractionHelper; +template +class SubtractionHelper; -template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_BothUnsigned > +template +class SubtractionHelper { public: - static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + static bool Subtract(const T& lhs, const U& rhs, T& result) SAFEINT_NOTHROW { // both are unsigned - easy case - if( rhs <= lhs ) + if (rhs <= lhs) { - result = (T)( lhs - rhs ); + 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 + template + static void SubtractThrow(const T& lhs, const U& rhs, T& result) SAFEINT_CPP_THROW { // both are unsigned - easy case - if( rhs <= lhs ) + if (rhs <= lhs) { - result = (T)( lhs - rhs ); + result = (T)(lhs - rhs); return; } @@ -4346,30 +4754,31 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt } }; -template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_BothUnsigned2 > +template +class SubtractionHelper { public: - static bool Subtract( const T& lhs, const U& rhs, U& result ) SAFEINT_NOTHROW + 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 ) + if (rhs <= lhs) { T tmp = (T)(lhs - rhs); - return SafeCastHelper< U, T, GetCastMethod::method>::Cast( tmp, result); + return SafeCastHelper::method>::Cast(tmp, result); } return false; } - template < typename E > - static void SubtractThrow( const T& lhs, const U& rhs, U& result ) SAFEINT_CPP_THROW + template + static void SubtractThrow(const T& lhs, const U& rhs, U& result) SAFEINT_CPP_THROW { // both are unsigned - easy case - if( rhs <= lhs ) + if (rhs <= lhs) { T tmp = (T)(lhs - rhs); - SafeCastHelper< U, T, GetCastMethod::method >::template CastThrow( tmp, result); + SafeCastHelper::method>::template CastThrow(tmp, result); return; } @@ -4377,16 +4786,17 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt } }; -template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastIntCheckSafeIntMinMax > +template +class SubtractionHelper { public: - static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 ) ) + if (SafeCastHelper::method>::Cast(tmp, result)) { result = (T)tmp; return true; @@ -4395,50 +4805,52 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt return false; } - template < typename E > - static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ); + SafeCastHelper::method>::template CastThrow(tmp, result); } }; -template class SubtractionHelper< U, T, SubtractionState_CastIntCheckSafeIntMinMax2 > +template +class SubtractionHelper { public: - static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + 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 ); + return SafeCastHelper::method>::Cast(tmp, result); } - template < typename E > - static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ); + SafeCastHelper::method>::template CastThrow(tmp, result); } }; -template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastIntCheckMin > +template +class SubtractionHelper { public: - static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 ) + if (tmp >= (__int32)IntTraits::minInt) { result = (T)tmp; return true; @@ -4447,14 +4859,14 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt return false; } - template < typename E > - static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) + if (tmp >= (__int32)IntTraits::minInt) { result = (T)tmp; return; @@ -4464,62 +4876,65 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt } }; -template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastInt64CheckSafeIntMinMax > +template +class SubtractionHelper { public: - static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 ); + return SafeCastHelper::method>::Cast(tmp, result); } - template < typename E > - static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ); + SafeCastHelper::method>::template CastThrow(tmp, result); } }; -template class SubtractionHelper< U, T, SubtractionState_CastInt64CheckSafeIntMinMax2 > +template +class SubtractionHelper { public: - static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + 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 ); + return SafeCastHelper::method>::Cast(tmp, result); } - template < typename E > - static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ); + SafeCastHelper::method>::template CastThrow(tmp, result); } }; -template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastInt64CheckMin > +template +class SubtractionHelper { public: - static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 ) + if (tmp >= (__int64)IntTraits::minInt) { result = (T)tmp; return true; @@ -4528,14 +4943,14 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt return false; } - template < typename E > - static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) + if (tmp >= (__int64)IntTraits::minInt) { result = (T)tmp; return; @@ -4545,18 +4960,19 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt } }; -template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Uint64Int > +template +class SubtractionHelper { public: - static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 (rhs >= 0) { - if( (unsigned __int64)rhs <= lhs ) + if ((unsigned __int64)rhs <= lhs) { - result = (T)( lhs - (unsigned __int64)rhs ); + result = (T)(lhs - (unsigned __int64)rhs); return true; } } @@ -4564,25 +4980,24 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt { T tmp = lhs; // we're now effectively adding - result = lhs + AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + result = lhs + AbsValueHelper::method>::Abs(rhs); - if(result >= tmp) - return true; + if (result >= tmp) return true; } return false; } - template < typename E > - static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 (rhs >= 0) { - if( (unsigned __int64)rhs <= lhs ) + if ((unsigned __int64)rhs <= lhs) { - result = (T)( lhs - (unsigned __int64)rhs ); + result = (T)(lhs - (unsigned __int64)rhs); return; } } @@ -4590,37 +5005,37 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt { T tmp = lhs; // we're now effectively adding - result = lhs + AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + result = lhs + AbsValueHelper::method>::Abs(rhs); - if(result >= tmp) - return; + if (result >= tmp) return; } E::SafeIntOnOverflow(); } }; -template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Uint64Int2 > +template +class SubtractionHelper { public: - static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW { // U is unsigned __int64, T is signed - if( rhs < 0 ) + if (rhs < 0) { // treat this as addition unsigned __int64 tmp; - tmp = lhs + (unsigned __int64)AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( rhs ); + tmp = lhs + (unsigned __int64)AbsValueHelper::method>::Abs(rhs); // must check for addition overflow and max - if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + if (tmp >= lhs && tmp <= IntTraits::maxInt) { result = (T)tmp; return true; } } - else if( (unsigned __int64)rhs > lhs ) // now both are positive, so comparison always works + 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 @@ -4633,7 +5048,7 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt // result is positive unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; - if( tmp <= IntTraits< T >::maxInt ) + if (tmp <= IntTraits::maxInt) { result = (T)tmp; return true; @@ -4643,25 +5058,25 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt return false; } - template < typename E > - static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + template + static void SubtractThrow(const U& lhs, const T& rhs, T& result) SAFEINT_CPP_THROW { // U is unsigned __int64, T is signed - if( rhs < 0 ) + if (rhs < 0) { // treat this as addition unsigned __int64 tmp; - tmp = lhs + (unsigned __int64)AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( rhs ); + tmp = lhs + (unsigned __int64)AbsValueHelper::method>::Abs(rhs); // must check for addition overflow and max - if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + if (tmp >= lhs && tmp <= IntTraits::maxInt) { result = (T)tmp; return; } } - else if( (unsigned __int64)rhs > lhs ) // now both are positive, so comparison always works + 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 @@ -4674,7 +5089,7 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt // result is positive unsigned __int64 tmp = (unsigned __int64)lhs - (unsigned __int64)rhs; - if( tmp <= IntTraits< T >::maxInt ) + if (tmp <= IntTraits::maxInt) { result = (T)tmp; return; @@ -4685,18 +5100,19 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt } }; -template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_UintInt64 > +template +class SubtractionHelper { public: - static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 (rhs >= 0) { - if( (unsigned __int64)rhs <= lhs ) + if ((unsigned __int64)rhs <= lhs) { - result = (T)( lhs - (T)rhs ); + result = (T)(lhs - (T)rhs); return true; } } @@ -4705,10 +5121,10 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt // 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 + unsigned __int64 tmp = lhs + ~(unsigned __int64)(rhs) + 1; // negation safe // but we could exceed MaxInt - if(tmp <= IntTraits< T >::maxInt) + if (tmp <= IntTraits::maxInt) { result = (T)tmp; return true; @@ -4718,16 +5134,16 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt return false; } - template < typename E > - static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 (rhs >= 0) { - if( (unsigned __int64)rhs <= lhs ) + if ((unsigned __int64)rhs <= lhs) { - result = (T)( lhs - (T)rhs ); + result = (T)(lhs - (T)rhs); return; } } @@ -4736,10 +5152,10 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt // 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 + unsigned __int64 tmp = lhs + ~(unsigned __int64)(rhs) + 1; // negation safe // but we could exceed MaxInt - if(tmp <= IntTraits< T >::maxInt) + if (tmp <= IntTraits::maxInt) { result = (T)tmp; return; @@ -4750,25 +5166,26 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt } }; -template class SubtractionHelper< U, T, SubtractionState_UintInt642 > +template +class SubtractionHelper { public: - static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + static bool Subtract(const U& lhs, const T& rhs, T& result) SAFEINT_NOTHROW { // U unsigned 32-bit or less, T __int64 - if( rhs >= 0 ) + if (rhs >= 0) { // overflow not possible - result = (T)( (__int64)lhs - rhs ); + 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 ); + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)(-rhs); - if( tmp <= (unsigned __int64)IntTraits< T >::maxInt ) + if (tmp <= (unsigned __int64)IntTraits::maxInt) { result = (T)tmp; return true; @@ -4778,23 +5195,23 @@ template class SubtractionHelper< U, T, SubtractionStat return false; } - template < typename E > - static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) + if (rhs >= 0) { // overflow not possible - result = (T)( (__int64)lhs - rhs ); + 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 ); + unsigned __int64 tmp = (unsigned __int64)lhs + (unsigned __int64)(-rhs); - if( tmp <= (unsigned __int64)IntTraits< T >::maxInt ) + if (tmp <= (unsigned __int64)IntTraits::maxInt) { result = (T)tmp; return; @@ -4805,10 +5222,11 @@ template class SubtractionHelper< U, T, SubtractionStat } }; -template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Int > +template +class SubtractionHelper { public: - static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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: @@ -4823,8 +5241,8 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt // 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 + if ((lhs >= 0 && rhs < 0 && tmp < lhs) || // condition 2 + (rhs >= 0 && tmp > lhs)) // condition 3 { return false; } @@ -4833,8 +5251,8 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt return true; } - template < typename E > - static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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: @@ -4849,8 +5267,8 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt // 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 + if ((lhs >= 0 && rhs < 0 && tmp < lhs) || // condition 2 + (rhs >= 0 && tmp > lhs)) // condition 3 { E::SafeIntOnOverflow(); } @@ -4859,10 +5277,11 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt } }; -template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Int2 > +template +class SubtractionHelper { public: - static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + 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; @@ -4874,12 +5293,11 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt // 3) lhs negative, rhs positive - check result <= lhs // 4) lhs negative, rhs negative - overflow not possible in tmp - if( lhs >= 0 ) + 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 ) ) + if ((IntTraits::isLT64Bit && tmp > IntTraits::maxInt) || (rhs < 0 && tmp < lhs)) { return false; } @@ -4887,8 +5305,7 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt else { // lhs negative - if( ( IntTraits< T >::isLT64Bit && tmp < IntTraits< T >::minInt) || - ( rhs >=0 && tmp > lhs ) ) + if ((IntTraits::isLT64Bit && tmp < IntTraits::minInt) || (rhs >= 0 && tmp > lhs)) { return false; } @@ -4898,8 +5315,8 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt return true; } - template < typename E > - static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + template + 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; @@ -4911,12 +5328,12 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt // 3) lhs negative, rhs positive - check result <= lhs // 4) lhs negative, rhs negative - overflow not possible in tmp - if( lhs >= 0 ) + 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 ) ) + if ((CompileConst::isLT64Bit>::Value() && tmp > IntTraits::maxInt) || + (rhs < 0 && tmp < lhs)) { E::SafeIntOnOverflow(); } @@ -4924,8 +5341,8 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt else { // lhs negative - if( ( CompileConst< IntTraits< T >::isLT64Bit >::Value() && tmp < IntTraits< T >::minInt) || - ( rhs >=0 && tmp > lhs ) ) + if ((CompileConst::isLT64Bit>::Value() && tmp < IntTraits::minInt) || + (rhs >= 0 && tmp > lhs)) { E::SafeIntOnOverflow(); } @@ -4935,10 +5352,11 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt } }; -template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_IntInt64 > +template +class SubtractionHelper { public: - static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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: @@ -4950,12 +5368,12 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); - if( lhs >= 0 ) + if (lhs >= 0) { // first case - if( rhs >= 0 ) + if (rhs >= 0) { - if( tmp >= IntTraits< T >::minInt ) + if (tmp >= IntTraits::minInt) { result = (T)tmp; return true; @@ -4964,7 +5382,7 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt else { // second case - if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + if (tmp >= lhs && tmp <= IntTraits::maxInt) { result = (T)tmp; return true; @@ -4975,9 +5393,9 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt { // lhs < 0 // third case - if( rhs >= 0 ) + if (rhs >= 0) { - if( tmp <= lhs && tmp >= IntTraits< T >::minInt ) + if (tmp <= lhs && tmp >= IntTraits::minInt) { result = (T)tmp; return true; @@ -4986,7 +5404,7 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt else { // fourth case - if( tmp <= IntTraits< T >::maxInt ) + if (tmp <= IntTraits::maxInt) { result = (T)tmp; return true; @@ -4997,8 +5415,8 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt return false; } - template < typename E > - static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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: @@ -5010,12 +5428,12 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt __int64 tmp = (__int64)((unsigned __int64)lhs - (unsigned __int64)rhs); - if( lhs >= 0 ) + if (lhs >= 0) { // first case - if( rhs >= 0 ) + if (rhs >= 0) { - if( tmp >= IntTraits< T >::minInt ) + if (tmp >= IntTraits::minInt) { result = (T)tmp; return; @@ -5024,7 +5442,7 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt else { // second case - if( tmp >= lhs && tmp <= IntTraits< T >::maxInt ) + if (tmp >= lhs && tmp <= IntTraits::maxInt) { result = (T)tmp; return; @@ -5035,9 +5453,9 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt { // lhs < 0 // third case - if( rhs >= 0 ) + if (rhs >= 0) { - if( tmp <= lhs && tmp >= IntTraits< T >::minInt ) + if (tmp <= lhs && tmp >= IntTraits::minInt) { result = (T)tmp; return; @@ -5046,7 +5464,7 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt else { // fourth case - if( tmp <= IntTraits< T >::maxInt ) + if (tmp <= IntTraits::maxInt) { result = (T)tmp; return; @@ -5058,52 +5476,52 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt } }; -template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_IntInt642 > +template +class SubtractionHelper { public: - static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + 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 ) ) + if ((lhs >= 0 && rhs < 0 && tmp < lhs) || (rhs > 0 && tmp > lhs)) { return false; - //else OK + // else OK } result = (T)tmp; return true; } - template < typename E > - static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) ) + if ((lhs >= 0 && rhs < 0 && tmp < lhs) || (rhs > 0 && tmp > lhs)) { E::SafeIntOnOverflow(); - //else OK + // else OK } result = (T)tmp; } }; -template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Uint > +template +class SubtractionHelper { public: - static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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 ) + if ((__int64)tmp <= lhs) { result = (T)(__int64)tmp; return true; @@ -5112,14 +5530,14 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt return false; } - template < typename E > - static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) + if ((__int64)tmp <= lhs) { result = (T)tmp; return; @@ -5129,16 +5547,17 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt } }; -template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Uint2 > +template +class SubtractionHelper { public: // lhs is __int64, rhs is unsigned 32-bit or smaller - static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + 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 ) + if ((__int64)tmp <= IntTraits::maxInt && (__int64)tmp >= IntTraits::minInt) { result = (T)(__int64)tmp; return true; @@ -5147,13 +5566,13 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt return false; } - template < typename E > - static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) + if ((__int64)tmp <= IntTraits::maxInt && (__int64)tmp >= IntTraits::minInt) { result = (T)(__int64)tmp; return; @@ -5163,31 +5582,32 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt } }; -template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_IntUint64 > +template +class SubtractionHelper { public: - static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + 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; + const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits::maxInt + 1; - if( lhs < 0 ) + if (lhs < 0) { - if( rhs <= AbsMinIntT - AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( lhs ) ) + if (rhs <= AbsMinIntT - AbsValueHelper::method>::Abs(lhs)) { - result = (T)( lhs - rhs ); + result = (T)(lhs - rhs); return true; } } else { - if( rhs <= AbsMinIntT + (unsigned __int64)lhs ) + if (rhs <= AbsMinIntT + (unsigned __int64)lhs) { - result = (T)( lhs - rhs ); + result = (T)(lhs - rhs); return true; } } @@ -5195,29 +5615,29 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt return false; } - template < typename E > - static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + template + 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; + const unsigned __int64 AbsMinIntT = (unsigned __int64)IntTraits::maxInt + 1; - if( lhs < 0 ) + if (lhs < 0) { - if( rhs <= AbsMinIntT - AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( lhs ) ) + if (rhs <= AbsMinIntT - AbsValueHelper::method>::Abs(lhs)) { - result = (T)( lhs - rhs ); + result = (T)(lhs - rhs); return; } } else { - if( rhs <= AbsMinIntT + (unsigned __int64)lhs ) + if (rhs <= AbsMinIntT + (unsigned __int64)lhs) { - result = (T)( lhs - rhs ); + result = (T)(lhs - rhs); return; } } @@ -5226,13 +5646,14 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt } }; -template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_IntUint642 > +template +class SubtractionHelper { public: - static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + 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 ) + if (lhs >= 0 && (T)lhs >= rhs) { result = (T)((U)lhs - (U)rhs); return true; @@ -5241,11 +5662,11 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt return false; } - template < typename E > - static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) + if (lhs >= 0 && (T)lhs >= rhs) { result = (T)((U)lhs - (U)rhs); return; @@ -5253,20 +5674,20 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt E::SafeIntOnOverflow(); } - }; -template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Uint64 > +template +class SubtractionHelper { public: - static bool Subtract( const __int64& lhs, const unsigned __int64& rhs, __int64& result ) SAFEINT_NOTHROW + static bool Subtract(const __int64& lhs, const unsigned __int64& rhs, __int64& result) SAFEINT_NOTHROW { - C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); + C_ASSERT(IntTraits::isInt64 && IntTraits::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 ) + if ((__int64)tmp <= lhs) { result = (__int64)tmp; return true; @@ -5274,15 +5695,15 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt return false; } - template < typename E > - static void SubtractThrow( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_CPP_THROW + template + static void SubtractThrow(const __int64& lhs, const unsigned __int64& rhs, T& result) SAFEINT_CPP_THROW { - C_ASSERT( IntTraits< T >::isInt64 && IntTraits< U >::isUint64 ); + C_ASSERT(IntTraits::isInt64 && IntTraits::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 ) + if ((__int64)tmp <= lhs) { result = (__int64)tmp; return; @@ -5290,18 +5711,18 @@ template < typename T, typename U > class SubtractionHelper< T, U, SubtractionSt E::SafeIntOnOverflow(); } - }; -template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Uint642 > +template +class SubtractionHelper { 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 + 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 ) + C_ASSERT(IntTraits::isUint64 && IntTraits::isInt64); + if (lhs >= 0 && (unsigned __int64)lhs >= rhs) { result = (unsigned __int64)lhs - rhs; return true; @@ -5310,11 +5731,11 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt return false; } - template < typename E > - static void SubtractThrow( const __int64& lhs, const unsigned __int64& rhs, T& result ) SAFEINT_CPP_THROW + template + 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 ) + C_ASSERT(IntTraits::isUint64 && IntTraits::isInt64); + if (lhs >= 0 && (unsigned __int64)lhs >= rhs) { result = (unsigned __int64)lhs - rhs; return; @@ -5322,7 +5743,6 @@ template < typename U, typename T > class SubtractionHelper< U, T, SubtractionSt E::SafeIntOnOverflow(); } - }; enum BinaryState @@ -5333,7 +5753,8 @@ enum BinaryState BinaryState_Int32 }; -template < typename T, typename U > class BinaryMethod +template +class BinaryMethod { public: enum @@ -5342,12 +5763,10 @@ template < typename T, typename U > class BinaryMethod // 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 + method = (sizeof(T) <= sizeof(U) || SafeIntCompare::isBothUnsigned || !IntTraits::isSigned) + ? BinaryState_OK + : IntTraits::isInt8 ? BinaryState_Int8 + : IntTraits::isInt16 ? BinaryState_Int16 : BinaryState_Int32 }; }; @@ -5357,126 +5776,141 @@ template < typename T, typename U > class BinaryMethod #define BinaryAssert(x) SAFEINT_ASSERT(x) #endif -template < typename T, typename U, int method > class BinaryAndHelper; +template +class BinaryAndHelper; -template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_OK > +template +class BinaryAndHelper { public: - static T And( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs & rhs ); } + static T And(T lhs, U rhs) SAFEINT_NOTHROW { return (T)(lhs & rhs); } }; -template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int8 > +template +class BinaryAndHelper { public: - static T And( T lhs, U rhs ) SAFEINT_NOTHROW + 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 ); + BinaryAssert((lhs & rhs) == (lhs & (unsigned __int8)rhs)); + return (T)(lhs & (unsigned __int8)rhs); } }; -template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int16 > +template +class BinaryAndHelper { public: - static T And( T lhs, U rhs ) SAFEINT_NOTHROW + 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 ); + // 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 > +template +class BinaryAndHelper { public: - static T And( T lhs, U rhs ) SAFEINT_NOTHROW + 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 ); + // 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 +class BinaryOrHelper; -template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_OK > +template +class BinaryOrHelper { public: - static T Or( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs | rhs ); } + static T Or(T lhs, U rhs) SAFEINT_NOTHROW { return (T)(lhs | rhs); } }; -template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int8 > +template +class BinaryOrHelper { public: - static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + 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 ); + // 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 > +template +class BinaryOrHelper { public: - static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + 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 ); + // 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 > +template +class BinaryOrHelper { public: - static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + 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 ); + // cast forces sign extension to be zeros + BinaryAssert((lhs | rhs) == (lhs | (unsigned __int32)rhs)); + return (T)(lhs | (unsigned __int32)rhs); } }; -template class BinaryXorHelper; +template +class BinaryXorHelper; -template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_OK > +template +class BinaryXorHelper { public: - static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs ^ rhs ); } + static T Xor(T lhs, U rhs) SAFEINT_NOTHROW { return (T)(lhs ^ rhs); } }; -template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int8 > +template +class BinaryXorHelper { public: - static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + 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 ); + BinaryAssert((lhs ^ rhs) == (lhs ^ (unsigned __int8)rhs)); + return (T)(lhs ^ (unsigned __int8)rhs); } }; -template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int16 > +template +class BinaryXorHelper { public: - static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + 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 ); + BinaryAssert((lhs ^ rhs) == (lhs ^ (unsigned __int16)rhs)); + return (T)(lhs ^ (unsigned __int16)rhs); } }; -template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int32 > +template +class BinaryXorHelper { public: - static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + 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 ); + BinaryAssert((lhs ^ rhs) == (lhs ^ (unsigned __int32)rhs)); + return (T)(lhs ^ (unsigned __int32)rhs); } }; @@ -5485,121 +5919,122 @@ template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int // 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 +template +inline bool SafeCast(const T From, U& To) SAFEINT_NOTHROW { - return SafeCastHelper< U, T, GetCastMethod< U, T >::method >::Cast( From, To ); + return SafeCastHelper::method>::Cast(From, To); } -template < typename T, typename U > -inline bool SafeEquals( const T t, const U u ) SAFEINT_NOTHROW +template +inline bool SafeEquals(const T t, const U u) SAFEINT_NOTHROW { - return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( t, u ); + return EqualityTest::method>::IsEquals(t, u); } -template < typename T, typename U > -inline bool SafeNotEquals( const T t, const U u ) SAFEINT_NOTHROW +template +inline bool SafeNotEquals(const T t, const U u) SAFEINT_NOTHROW { - return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( t, u ); + return !EqualityTest::method>::IsEquals(t, u); } -template < typename T, typename U > -inline bool SafeGreaterThan( const T t, const U u ) SAFEINT_NOTHROW +template +inline bool SafeGreaterThan(const T t, const U u) SAFEINT_NOTHROW { - return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( t, u ); + return GreaterThanTest::method>::GreaterThan(t, u); } -template < typename T, typename U > -inline bool SafeGreaterThanEquals( const T t, const U u ) SAFEINT_NOTHROW +template +inline bool SafeGreaterThanEquals(const T t, const U u) SAFEINT_NOTHROW { - return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( u, t ); + return !GreaterThanTest::method>::GreaterThan(u, t); } -template < typename T, typename U > -inline bool SafeLessThan( const T t, const U u ) SAFEINT_NOTHROW +template +inline bool SafeLessThan(const T t, const U u) SAFEINT_NOTHROW { - return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( u, t ); + return GreaterThanTest::method>::GreaterThan(u, t); } -template < typename T, typename U > -inline bool SafeLessThanEquals( const T t, const U u ) SAFEINT_NOTHROW +template +inline bool SafeLessThanEquals(const T t, const U u) SAFEINT_NOTHROW { - return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( t, u ); + return !GreaterThanTest::method>::GreaterThan(t, u); } -template < typename T, typename U > -inline bool SafeModulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW +template +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 ); + return (ModulusHelper::method>::Modulus(t, u, result) == SafeIntNoError); } -template < typename T, typename U > -inline bool SafeMultiply( T t, U u, T& result ) SAFEINT_NOTHROW +template +inline bool SafeMultiply(T t, U u, T& result) SAFEINT_NOTHROW { - return MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::Multiply( t, u, result ); + return MultiplicationHelper::method>::Multiply(t, u, result); } -template < typename T, typename U > -inline bool SafeDivide( T t, U u, T& result ) SAFEINT_NOTHROW +template +inline bool SafeDivide(T t, U u, T& result) SAFEINT_NOTHROW { - return ( DivisionHelper< T, U, DivisionMethod< T, U >::method >::Divide( t, u, result ) == SafeIntNoError ); + return (DivisionHelper::method>::Divide(t, u, result) == SafeIntNoError); } -template < typename T, typename U > -inline bool SafeAdd( T t, U u, T& result ) SAFEINT_NOTHROW +template +inline bool SafeAdd(T t, U u, T& result) SAFEINT_NOTHROW { - return AdditionHelper< T, U, AdditionMethod< T, U >::method >::Addition( t, u, result ); + return AdditionHelper::method>::Addition(t, u, result); } -template < typename T, typename U > -inline bool SafeSubtract( T t, U u, T& result ) SAFEINT_NOTHROW +template +inline bool SafeSubtract(T t, U u, T& result) SAFEINT_NOTHROW { - return SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::Subtract( t, u, result ); + return SubtractionHelper::method>::Subtract(t, u, result); } /***************** end external functions ************************************/ // Main SafeInt class // Assumes exceptions can be thrown -template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeInt +template +class SafeInt { public: SafeInt() SAFEINT_NOTHROW { - C_ASSERT( NumericType< T >::isInt ); + C_ASSERT(NumericType::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 + SafeInt(const T& i) SAFEINT_NOTHROW { - C_ASSERT( NumericType< T >::isInt ); - //always safe + C_ASSERT(NumericType::isInt); + // always safe m_int = i; } // provide explicit boolean converter - SafeInt( bool b ) SAFEINT_NOTHROW + SafeInt(bool b) SAFEINT_NOTHROW { - C_ASSERT( NumericType< T >::isInt ); - m_int = (T)( b ? 1 : 0 ); + C_ASSERT(NumericType::isInt); + m_int = (T)(b ? 1 : 0); } - template < typename U > - SafeInt(const SafeInt< U, E >& u) SAFEINT_CPP_THROW + template + SafeInt(const SafeInt& u) SAFEINT_CPP_THROW { - C_ASSERT( NumericType< T >::isInt ); - *this = SafeInt< T, E >( (U)u ); + C_ASSERT(NumericType::isInt); + *this = SafeInt((U)u); } - template < typename U > - SafeInt( const U& i ) SAFEINT_CPP_THROW + template + SafeInt(const U& i) SAFEINT_CPP_THROW { - C_ASSERT( NumericType< T >::isInt ); + C_ASSERT(NumericType::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 ); + SafeCastHelper::method>::template CastThrow(i, m_int); } // The destructor is intentionally commented out - no destructor @@ -5607,36 +6042,35 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI // 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 + template + SafeInt& 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 ); + *this = SafeInt(rhs); return *this; } - SafeInt< T, E >& operator =( const T& rhs ) SAFEINT_NOTHROW + SafeInt& 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 + template + SafeInt& operator=(const SafeInt& rhs) SAFEINT_CPP_THROW { - SafeCastHelper< T, U, GetCastMethod< T, U >::method >::template CastThrow< E >( rhs.Ref(), m_int ); + SafeCastHelper::method>::template CastThrow(rhs.Ref(), m_int); return *this; } - SafeInt< T, E >& operator =( const SafeInt< T, E >& rhs ) SAFEINT_NOTHROW + SafeInt& operator=(const SafeInt& rhs) SAFEINT_NOTHROW { m_int = rhs.m_int; return *this; @@ -5644,57 +6078,56 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI // Casting operators - operator bool() const SAFEINT_NOTHROW - { - return !!m_int; - } + 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 ); + SafeCastHelper::method>::template CastThrow(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 ); + SafeCastHelper::method>::template CastThrow(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 ); + SafeCastHelper::method>::template CastThrow(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 ); + SafeCastHelper<__int16, T, GetCastMethod<__int16, T>::method>::template CastThrow(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 ); + SafeCastHelper::method>::template CastThrow(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 ); + SafeCastHelper<__int32, T, GetCastMethod<__int32, T>::method>::template CastThrow(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 ); + SafeCastHelper::method>::template CastThrow(m_int, + val); return val; } @@ -5703,28 +6136,29 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI operator long() const SAFEINT_CPP_THROW { long val; - SafeCastHelper< long, T, GetCastMethod< long, T >::method >::template CastThrow< E >( m_int, val ); - return val; + SafeCastHelper::method>::template CastThrow(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 ); + SafeCastHelper::method>::template CastThrow(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 ); + SafeCastHelper<__int64, T, GetCastMethod<__int64, T>::method>::template CastThrow(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 ); + SafeCastHelper::method>::template CastThrow(m_int, + val); return val; } @@ -5732,7 +6166,7 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI 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 ); + SafeCastHelper::method>::template CastThrow(m_int, val); return val; } #endif @@ -5744,7 +6178,7 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI 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 ); + SafeCastHelper::method>::template CastThrow(m_int, val); return val; } #endif @@ -5753,20 +6187,20 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI operator float() const SAFEINT_CPP_THROW { float val; - SafeCastHelper< float, T, GetCastMethod< float, T >::method >::template CastThrow< E >( m_int, val ); + SafeCastHelper::method>::template CastThrow(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 ); + SafeCastHelper::method>::template CastThrow(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 ); + SafeCastHelper::method>::template CastThrow(m_int, val); return val; } @@ -5783,20 +6217,20 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI // 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; } + 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; } + 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; } + const SafeInt& operator+() const SAFEINT_NOTHROW { return *this; } - //unary - + // unary - - SafeInt< T, E > operator -() const SAFEINT_CPP_THROW + SafeInt 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 @@ -5804,9 +6238,9 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI } // prefix increment operator - SafeInt< T, E >& operator ++() SAFEINT_CPP_THROW + SafeInt& operator++() SAFEINT_CPP_THROW { - if( m_int != IntTraits< T >::maxInt ) + if (m_int != IntTraits::maxInt) { ++m_int; return *this; @@ -5815,9 +6249,9 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI } // prefix decrement operator - SafeInt< T, E >& operator --() SAFEINT_CPP_THROW + SafeInt& operator--() SAFEINT_CPP_THROW { - if( m_int != IntTraits< T >::minInt ) + if (m_int != IntTraits::minInt) { --m_int; return *this; @@ -5829,11 +6263,11 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI // characteristics // postfix increment operator - SafeInt< T, E > operator ++( int ) SAFEINT_CPP_THROW // dummy arg to comply with spec + SafeInt operator++(int) SAFEINT_CPP_THROW // dummy arg to comply with spec { - if( m_int != IntTraits< T >::maxInt ) + if (m_int != IntTraits::maxInt) { - SafeInt< T, E > tmp( m_int ); + SafeInt tmp(m_int); m_int++; return tmp; @@ -5842,11 +6276,11 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI } // postfix decrement operator - SafeInt< T, E > operator --( int ) SAFEINT_CPP_THROW // dummy arg to comply with spec + SafeInt operator--(int) SAFEINT_CPP_THROW // dummy arg to comply with spec { - if( m_int != IntTraits< T >::minInt ) + if (m_int != IntTraits::minInt) { - SafeInt< T, E > tmp( m_int ); + SafeInt tmp(m_int); m_int--; return tmp; } @@ -5856,7 +6290,7 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI // 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 ); } + SafeInt operator~() const SAFEINT_NOTHROW { return SafeInt((T)~m_int); } // Binary operators // @@ -5900,181 +6334,184 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI // 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 + template + SafeInt 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 ); + ModulusHelper::method>::template ModulusThrow(m_int, rhs, result); + return SafeInt(result); } - SafeInt< T, E > operator %( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + SafeInt operator%(SafeInt 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 ); + ModulusHelper::method>::template ModulusThrow(m_int, rhs, result); + return SafeInt(result); } // Modulus assignment - template < typename U > - SafeInt< T, E >& operator %=( U rhs ) SAFEINT_CPP_THROW + template + SafeInt& operator%=(U rhs) SAFEINT_CPP_THROW { - ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, rhs, m_int ); + ModulusHelper::method>::template ModulusThrow(m_int, rhs, m_int); return *this; } - template < typename U > - SafeInt< T, E >& operator %=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + template + SafeInt& operator%=(SafeInt rhs) SAFEINT_CPP_THROW { - ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, (U)rhs, m_int ); + ModulusHelper::method>::template ModulusThrow(m_int, (U)rhs, m_int); return *this; } // Multiplication - template < typename U > - SafeInt< T, E > operator *( U rhs ) const SAFEINT_CPP_THROW + template + SafeInt 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 ); + T ret(0); + MultiplicationHelper::method>::template MultiplyThrow(m_int, rhs, ret); + return SafeInt(ret); } - SafeInt< T, E > operator *( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + SafeInt operator*(SafeInt 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 ); + T ret(0); + MultiplicationHelper::method>::template MultiplyThrow(m_int, (T)rhs, ret); + return SafeInt(ret); } // Multiplication assignment - SafeInt< T, E >& operator *=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + SafeInt& operator*=(SafeInt rhs) SAFEINT_CPP_THROW { - MultiplicationHelper< T, T, MultiplicationMethod< T, T >::method >::template MultiplyThrow< E >( m_int, (T)rhs, m_int ); + MultiplicationHelper::method>::template MultiplyThrow(m_int, (T)rhs, m_int); return *this; } - template < typename U > - SafeInt< T, E >& operator *=( U rhs ) SAFEINT_CPP_THROW + template + SafeInt& operator*=(U rhs) SAFEINT_CPP_THROW { - MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs, m_int ); + MultiplicationHelper::method>::template MultiplyThrow(m_int, rhs, m_int); return *this; } - template < typename U > - SafeInt< T, E >& operator *=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + template + SafeInt& operator*=(SafeInt rhs) SAFEINT_CPP_THROW { - MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs.Ref(), m_int ); + MultiplicationHelper::method>::template MultiplyThrow( + m_int, rhs.Ref(), m_int); return *this; } // Division - template < typename U > - SafeInt< T, E > operator /( U rhs ) const SAFEINT_CPP_THROW + template + SafeInt 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 ); + T ret(0); + DivisionHelper::method>::template DivideThrow(m_int, rhs, ret); + return SafeInt(ret); } - SafeInt< T, E > operator /( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + SafeInt operator/(SafeInt 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 ); + T ret(0); + DivisionHelper::method>::template DivideThrow(m_int, (T)rhs, ret); + return SafeInt(ret); } // Division assignment - SafeInt< T, E >& operator /=( SafeInt< T, E > i ) SAFEINT_CPP_THROW + SafeInt& operator/=(SafeInt i) SAFEINT_CPP_THROW { - DivisionHelper< T, T, DivisionMethod< T, T >::method >::template DivideThrow< E >( m_int, (T)i, m_int ); + DivisionHelper::method>::template DivideThrow(m_int, (T)i, m_int); return *this; } - template < typename U > SafeInt< T, E >& operator /=( U i ) SAFEINT_CPP_THROW + template + SafeInt& operator/=(U i) SAFEINT_CPP_THROW { - DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, i, m_int ); + DivisionHelper::method>::template DivideThrow(m_int, i, m_int); return *this; } - template < typename U > SafeInt< T, E >& operator /=( SafeInt< U, E > i ) + template + SafeInt& operator/=(SafeInt i) { - DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, (U)i, m_int ); + DivisionHelper::method>::template DivideThrow(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 + SafeInt operator+(SafeInt 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 ); + T ret(0); + AdditionHelper::method>::template AdditionThrow(m_int, (T)rhs, ret); + return SafeInt(ret); } - template < typename U > - SafeInt< T, E > operator +( U rhs ) const SAFEINT_CPP_THROW + template + SafeInt 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 ); + T ret(0); + AdditionHelper::method>::template AdditionThrow(m_int, rhs, ret); + return SafeInt(ret); } - //addition assignment - SafeInt< T, E >& operator +=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + // addition assignment + SafeInt& operator+=(SafeInt rhs) SAFEINT_CPP_THROW { - AdditionHelper< T, T, AdditionMethod< T, T >::method >::template AdditionThrow< E >( m_int, (T)rhs, m_int ); + AdditionHelper::method>::template AdditionThrow(m_int, (T)rhs, m_int); return *this; } - template < typename U > - SafeInt< T, E >& operator +=( U rhs ) SAFEINT_CPP_THROW + template + SafeInt& operator+=(U rhs) SAFEINT_CPP_THROW { - AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, rhs, m_int ); + AdditionHelper::method>::template AdditionThrow(m_int, rhs, m_int); return *this; } - template < typename U > - SafeInt< T, E >& operator +=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + template + SafeInt& operator+=(SafeInt rhs) SAFEINT_CPP_THROW { - AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, (U)rhs, m_int ); + AdditionHelper::method>::template AdditionThrow(m_int, (U)rhs, m_int); return *this; } // Subtraction - template < typename U > - SafeInt< T, E > operator -( U rhs ) const SAFEINT_CPP_THROW + template + SafeInt 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 ); + T ret(0); + SubtractionHelper::method>::template SubtractThrow(m_int, rhs, ret); + return SafeInt(ret); } - SafeInt< T, E > operator -(SafeInt< T, E > rhs) const SAFEINT_CPP_THROW + SafeInt operator-(SafeInt 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 ); + T ret(0); + SubtractionHelper::method>::template SubtractThrow(m_int, (T)rhs, ret); + return SafeInt(ret); } // Subtraction assignment - SafeInt< T, E >& operator -=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + SafeInt& operator-=(SafeInt rhs) SAFEINT_CPP_THROW { - SubtractionHelper< T, T, SubtractionMethod< T, T >::method >::template SubtractThrow< E >( m_int, (T)rhs, m_int ); + SubtractionHelper::method>::template SubtractThrow(m_int, (T)rhs, m_int); return *this; } - template < typename U > - SafeInt< T, E >& operator -=( U rhs ) SAFEINT_CPP_THROW + template + SafeInt& operator-=(U rhs) SAFEINT_CPP_THROW { - SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, rhs, m_int ); + SubtractionHelper::method>::template SubtractThrow(m_int, rhs, m_int); return *this; } - template < typename U > - SafeInt< T, E >& operator -=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + template + SafeInt& operator-=(SafeInt rhs) SAFEINT_CPP_THROW { - SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, (U)rhs, m_int ); + SubtractionHelper::method>::template SubtractThrow(m_int, (U)rhs, m_int); return *this; } @@ -6086,86 +6523,86 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI // Left shift // Also, shifting > bitcount is undefined - trap in debug #ifdef SAFEINT_DISABLE_SHIFT_ASSERT - #define ShiftAssert(x) +#define ShiftAssert(x) #else - #define ShiftAssert(x) SAFEINT_ASSERT(x) +#define ShiftAssert(x) SAFEINT_ASSERT(x) #endif - template < typename U > - SafeInt< T, E > operator <<( U bits ) const SAFEINT_NOTHROW + template + SafeInt operator<<(U bits) const SAFEINT_NOTHROW { - ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); - ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + ShiftAssert(!IntTraits::isSigned || bits >= 0); + ShiftAssert(bits < (int)IntTraits::bitCount); - return SafeInt< T, E >( (T)( m_int << bits ) ); + return SafeInt((T)(m_int << bits)); } - template < typename U > - SafeInt< T, E > operator <<( SafeInt< U, E > bits ) const SAFEINT_NOTHROW + template + SafeInt operator<<(SafeInt bits) const SAFEINT_NOTHROW { - ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); - ShiftAssert( (U)bits < (int)IntTraits< T >::bitCount ); + ShiftAssert(!IntTraits::isSigned || (U)bits >= 0); + ShiftAssert((U)bits < (int)IntTraits::bitCount); - return SafeInt< T, E >( (T)( m_int << (U)bits ) ); + return SafeInt((T)(m_int << (U)bits)); } // Left shift assignment - template < typename U > - SafeInt< T, E >& operator <<=( U bits ) SAFEINT_NOTHROW + template + SafeInt& operator<<=(U bits) SAFEINT_NOTHROW { - ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); - ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + ShiftAssert(!IntTraits::isSigned || bits >= 0); + ShiftAssert(bits < (int)IntTraits::bitCount); m_int <<= bits; return *this; } - template < typename U > - SafeInt< T, E >& operator <<=( SafeInt< U, E > bits ) SAFEINT_NOTHROW + template + SafeInt& operator<<=(SafeInt bits) SAFEINT_NOTHROW { - ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); - ShiftAssert( (U)bits < (int)IntTraits< T >::bitCount ); + ShiftAssert(!IntTraits::isSigned || (U)bits >= 0); + ShiftAssert((U)bits < (int)IntTraits::bitCount); m_int <<= (U)bits; return *this; } // Right shift - template < typename U > - SafeInt< T, E > operator >>( U bits ) const SAFEINT_NOTHROW + template + SafeInt operator>>(U bits) const SAFEINT_NOTHROW { - ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); - ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + ShiftAssert(!IntTraits::isSigned || bits >= 0); + ShiftAssert(bits < (int)IntTraits::bitCount); - return SafeInt< T, E >( (T)( m_int >> bits ) ); + return SafeInt((T)(m_int >> bits)); } - template < typename U > - SafeInt< T, E > operator >>( SafeInt< U, E > bits ) const SAFEINT_NOTHROW + template + SafeInt operator>>(SafeInt bits) const SAFEINT_NOTHROW { - ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); - ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + ShiftAssert(!IntTraits::isSigned || (U)bits >= 0); + ShiftAssert(bits < (int)IntTraits::bitCount); - return SafeInt< T, E >( (T)(m_int >> (U)bits) ); + return SafeInt((T)(m_int >> (U)bits)); } // Right shift assignment - template < typename U > - SafeInt< T, E >& operator >>=( U bits ) SAFEINT_NOTHROW + template + SafeInt& operator>>=(U bits) SAFEINT_NOTHROW { - ShiftAssert( !IntTraits< U >::isSigned || bits >= 0 ); - ShiftAssert( bits < (int)IntTraits< T >::bitCount ); + ShiftAssert(!IntTraits::isSigned || bits >= 0); + ShiftAssert(bits < (int)IntTraits::bitCount); m_int >>= bits; return *this; } - template < typename U > - SafeInt< T, E >& operator >>=( SafeInt< U, E > bits ) SAFEINT_NOTHROW + template + SafeInt& operator>>=(SafeInt bits) SAFEINT_NOTHROW { - ShiftAssert( !IntTraits< U >::isSigned || (U)bits >= 0 ); - ShiftAssert( (U)bits < (int)IntTraits< T >::bitCount ); + ShiftAssert(!IntTraits::isSigned || (U)bits >= 0); + ShiftAssert((U)bits < (int)IntTraits::bitCount); m_int >>= (U)bits; return *this; @@ -6176,13 +6613,10 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI // 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 ); - } + SafeInt operator&(SafeInt rhs) const SAFEINT_NOTHROW { return SafeInt(m_int & (T)rhs); } - template < typename U > - SafeInt< T, E > operator &( U rhs ) const SAFEINT_NOTHROW + template + SafeInt operator&(U rhs) const SAFEINT_NOTHROW { // we want to avoid setting bits by surprise // consider the case of lhs = int, value = 0xffffffff @@ -6196,129 +6630,117 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI // 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 ) ); + return SafeInt(BinaryAndHelper::method>::And(m_int, rhs)); } // Bitwise & assignment - SafeInt< T, E >& operator &=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + SafeInt& operator&=(SafeInt rhs) SAFEINT_NOTHROW { m_int &= (T)rhs; return *this; } - template < typename U > - SafeInt< T, E >& operator &=( U rhs ) SAFEINT_NOTHROW + template + SafeInt& operator&=(U rhs) SAFEINT_NOTHROW { - m_int = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, rhs ); + m_int = BinaryAndHelper::method>::And(m_int, rhs); return *this; } - template < typename U > - SafeInt< T, E >& operator &=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + template + SafeInt& operator&=(SafeInt rhs) SAFEINT_NOTHROW { - m_int = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, (U)rhs ); + m_int = BinaryAndHelper::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 ) ); - } + SafeInt operator^(SafeInt rhs) const SAFEINT_NOTHROW { return SafeInt((T)(m_int ^ (T)rhs)); } - template < typename U > - SafeInt< T, E > operator ^( U rhs ) const SAFEINT_NOTHROW + template + SafeInt 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 ) ); + return SafeInt(BinaryXorHelper::method>::Xor(m_int, rhs)); } // XOR assignment - SafeInt< T, E >& operator ^=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + SafeInt& operator^=(SafeInt rhs) SAFEINT_NOTHROW { m_int ^= (T)rhs; return *this; } - template < typename U > - SafeInt< T, E >& operator ^=( U rhs ) SAFEINT_NOTHROW + template + SafeInt& operator^=(U rhs) SAFEINT_NOTHROW { - m_int = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, rhs ); + m_int = BinaryXorHelper::method>::Xor(m_int, rhs); return *this; } - template < typename U > - SafeInt< T, E >& operator ^=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + template + SafeInt& operator^=(SafeInt rhs) SAFEINT_NOTHROW { - m_int = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, (U)rhs ); + m_int = BinaryXorHelper::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 ) ); - } + SafeInt operator|(SafeInt rhs) const SAFEINT_NOTHROW { return SafeInt((T)(m_int | (T)rhs)); } - template < typename U > - SafeInt< T, E > operator |( U rhs ) const SAFEINT_NOTHROW + template + SafeInt operator|(U rhs) const SAFEINT_NOTHROW { - return SafeInt< T, E >( BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, rhs ) ); + return SafeInt(BinaryOrHelper::method>::Or(m_int, rhs)); } // bitwise OR assignment - SafeInt< T, E >& operator |=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + SafeInt& operator|=(SafeInt rhs) SAFEINT_NOTHROW { m_int |= (T)rhs; return *this; } - template < typename U > - SafeInt< T, E >& operator |=( U rhs ) SAFEINT_NOTHROW + template + SafeInt& operator|=(U rhs) SAFEINT_NOTHROW { - m_int = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, rhs ); + m_int = BinaryOrHelper::method>::Or(m_int, rhs); return *this; } - template < typename U > - SafeInt< T, E >& operator |=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + template + SafeInt& operator|=(SafeInt rhs) SAFEINT_NOTHROW { - m_int = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, (U)rhs ); + m_int = BinaryOrHelper::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 + SafeInt Min(SafeInt test, const T floor = IntTraits::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 + SafeInt Max(SafeInt test, const T upper = IntTraits::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 + void Swap(SafeInt& with) SAFEINT_NOTHROW { - T temp( m_int ); + 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 SafeAtoI(const char* input) SAFEINT_CPP_THROW { return SafeTtoI(input); } - static SafeInt< T, E > SafeWtoI( const wchar_t* input ) - { - return SafeTtoI( input ); - } + static SafeInt SafeWtoI(const wchar_t* input) { return SafeTtoI(input); } enum alignBits { @@ -6332,80 +6754,73 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI align256 = 8 }; - template < alignBits bits > - const SafeInt< T, E >& Align() SAFEINT_CPP_THROW + template + const SafeInt& Align() SAFEINT_CPP_THROW { // Zero is always aligned - if( m_int == 0 ) - return *this; + 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 ) ); + ShiftAssert(((IntTraits::isSigned && bits < (int)IntTraits::bitCount - 1) || + (!IntTraits::isSigned && bits < (int)IntTraits::bitCount)) && + bits >= 0 && (!IntTraits::isSigned || m_int > 0)); - const T AlignValue = ( (T)1 << bits ) - 1; + const T AlignValue = ((T)1 << bits) - 1; - m_int = (T)( ( m_int + AlignValue ) & ~AlignValue ); + m_int = (T)((m_int + AlignValue) & ~AlignValue); - if( m_int <= 0 ) - E::SafeIntOnOverflow(); + 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: + const SafeInt& Align2() { return Align(); } + const SafeInt& Align4() { return Align(); } + const SafeInt& Align8() { return Align(); } + const SafeInt& Align16() { return Align(); } + const SafeInt& Align32() { return Align(); } + const SafeInt& Align64() { return Align(); } +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 + template + static SafeInt SafeTtoI(U* input) SAFEINT_CPP_THROW { - U* tmp = input; - SafeInt< T, E > s; + U* tmp = input; + SafeInt s; bool negative = false; // Bad input, or empty string - if( input == nullptr || input[0] == 0 ) - E::SafeIntOnOverflow(); + if (input == nullptr || input[0] == 0) E::SafeIntOnOverflow(); - switch( *tmp ) + switch (*tmp) { - case '-': - tmp++; - negative = true; - break; - case '+': - tmp++; - break; + case '-': + tmp++; + negative = true; + break; + case '+': tmp++; break; } - while( *tmp != 0 ) + while (*tmp != 0) { - if( *tmp < '0' || *tmp > '9' ) - break; + if (*tmp < '0' || *tmp > '9') break; - if( (T)s != 0 ) - s *= (T)10; + if ((T)s != 0) s *= (T)10; - if( !negative ) - s += (T)( *tmp - '0' ); + if (!negative) + s += (T)(*tmp - '0'); else - s -= (T)( *tmp - '0' ); + s -= (T)(*tmp - '0'); tmp++; } @@ -6418,165 +6833,166 @@ template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeI // Helper function used to subtract pointers. // Used to squelch warnings -template +template SafeInt SafePtrDiff(const P* p1, const P* p2) SAFEINT_CPP_THROW { - return SafeInt( p1 - p2 ); + 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 +// Less than +template +bool operator<(U lhs, SafeInt rhs) SAFEINT_NOTHROW { - return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, lhs ); + return GreaterThanTest::method>::GreaterThan((T)rhs, lhs); } -template < typename T, typename U, typename E > -bool operator <( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +template +bool operator<(SafeInt lhs, U rhs) SAFEINT_NOTHROW { - return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( rhs, (T)lhs ); + return GreaterThanTest::method>::GreaterThan(rhs, (T)lhs); } -template < typename T, typename U, typename E > -bool operator <( SafeInt< U, E > lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +template +bool operator<(SafeInt lhs, SafeInt rhs) SAFEINT_NOTHROW { - return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, (U)lhs ); + return GreaterThanTest::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 +template +bool operator>(U lhs, SafeInt rhs) SAFEINT_NOTHROW { - return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( lhs, (T)rhs ); + return GreaterThanTest::method>::GreaterThan(lhs, (T)rhs); } -template < typename T, typename U, typename E > -bool operator >( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +template +bool operator>(SafeInt lhs, U rhs) SAFEINT_NOTHROW { - return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, rhs ); + return GreaterThanTest::method>::GreaterThan((T)lhs, rhs); } -template < typename T, typename U, typename E > -bool operator >( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +template +bool operator>(SafeInt lhs, SafeInt rhs) SAFEINT_NOTHROW { - return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, (U)rhs ); + return GreaterThanTest::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 +template +bool operator>=(U lhs, SafeInt rhs) SAFEINT_NOTHROW { - return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, lhs ); + return !GreaterThanTest::method>::GreaterThan((T)rhs, lhs); } -template < typename T, typename U, typename E > -bool operator >=( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +template +bool operator>=(SafeInt lhs, U rhs) SAFEINT_NOTHROW { - return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( rhs, (T)lhs ); + return !GreaterThanTest::method>::GreaterThan(rhs, (T)lhs); } -template < typename T, typename U, typename E > -bool operator >=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +template +bool operator>=(SafeInt lhs, SafeInt rhs) SAFEINT_NOTHROW { - return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( (U)rhs, (T)lhs ); + return !GreaterThanTest::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 +template +bool operator<=(U lhs, SafeInt rhs) SAFEINT_NOTHROW { - return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( lhs, (T)rhs ); + return !GreaterThanTest::method>::GreaterThan(lhs, (T)rhs); } -template < typename T, typename U, typename E > -bool operator <=( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +template +bool operator<=(SafeInt lhs, U rhs) SAFEINT_NOTHROW { - return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, rhs ); + return !GreaterThanTest::method>::GreaterThan((T)lhs, rhs); } -template < typename T, typename U, typename E > -bool operator <=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +template +bool operator<=(SafeInt lhs, SafeInt rhs) SAFEINT_NOTHROW { - return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, (U)rhs ); + return !GreaterThanTest::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 +template +bool operator==(bool lhs, SafeInt rhs) SAFEINT_NOTHROW { - return lhs == ( (T)rhs == 0 ? false : true ); + return lhs == ((T)rhs == 0 ? false : true); } -template < typename T, typename E > -bool operator ==( SafeInt< T, E > lhs, bool rhs ) SAFEINT_NOTHROW +template +bool operator==(SafeInt lhs, bool rhs) SAFEINT_NOTHROW { - return rhs == ( (T)lhs == 0 ? false : true ); + return rhs == ((T)lhs == 0 ? false : true); } -template < typename T, typename U, typename E > -bool operator ==( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +template +bool operator==(U lhs, SafeInt rhs) SAFEINT_NOTHROW { - return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals((T)rhs, lhs); + return EqualityTest::method>::IsEquals((T)rhs, lhs); } -template < typename T, typename U, typename E > -bool operator ==( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +template +bool operator==(SafeInt lhs, U rhs) SAFEINT_NOTHROW { - return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, rhs ); + return EqualityTest::method>::IsEquals((T)lhs, rhs); } -template < typename T, typename U, typename E > -bool operator ==( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +template +bool operator==(SafeInt lhs, SafeInt rhs) SAFEINT_NOTHROW { - return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, (U)rhs ); + return EqualityTest::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 +// not equals +template +bool operator!=(U lhs, SafeInt rhs) SAFEINT_NOTHROW { - return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)rhs, lhs ); + return !EqualityTest::method>::IsEquals((T)rhs, lhs); } -template < typename T, typename U, typename E > -bool operator !=( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +template +bool operator!=(SafeInt lhs, U rhs) SAFEINT_NOTHROW { - return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, rhs ); + return !EqualityTest::method>::IsEquals((T)lhs, rhs); } -template < typename T, typename U, typename E > -bool operator !=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +template +bool operator!=(SafeInt lhs, SafeInt rhs) SAFEINT_NOTHROW { - return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( lhs, rhs ); + return !EqualityTest::method>::IsEquals(lhs, rhs); } - -template < typename T, typename E > -bool operator !=( bool lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +template +bool operator!=(bool lhs, SafeInt rhs) SAFEINT_NOTHROW { - return ( (T)rhs == 0 ? false : true ) != lhs; + return ((T)rhs == 0 ? false : true) != lhs; } -template < typename T, typename E > -bool operator !=( SafeInt< T, E > lhs, bool rhs ) SAFEINT_NOTHROW +template +bool operator!=(SafeInt lhs, bool rhs) SAFEINT_NOTHROW { - return ( (T)lhs == 0 ? false : true ) != rhs; + return ((T)lhs == 0 ? false : true) != rhs; } +template +class ModulusSimpleCaseHelper; -template < typename T, typename U, typename E, int method > class ModulusSimpleCaseHelper; - -template < typename T, typename E, int method > class ModulusSignedCaseHelper; +template +class ModulusSignedCaseHelper; -template < typename T, typename E > class ModulusSignedCaseHelper < T, E, true > +template +class ModulusSignedCaseHelper { public: - static bool SignedCase( SafeInt< T, E > rhs, SafeInt< T, E >& result ) SAFEINT_NOTHROW + static bool SignedCase(SafeInt rhs, SafeInt& result) SAFEINT_NOTHROW { - if( (T)rhs == (T)-1 ) + if ((T)rhs == (T)-1) { result = 0; return true; @@ -6585,27 +7001,24 @@ template < typename T, typename E > class ModulusSignedCaseHelper < T, E, true > } }; -template < typename T, typename E > class ModulusSignedCaseHelper < T, E, false > +template +class ModulusSignedCaseHelper { public: - static bool SignedCase( SafeInt< T, E > /*rhs*/, SafeInt< T, E >& /*result*/ ) SAFEINT_NOTHROW - { - return false; - } + static bool SignedCase(SafeInt /*rhs*/, SafeInt& /*result*/) SAFEINT_NOTHROW { return false; } }; -template < typename T, typename U, typename E > -class ModulusSimpleCaseHelper < T, U, E, true > +template +class ModulusSimpleCaseHelper { public: - static bool ModulusSimpleCase( U lhs, SafeInt< T, E > rhs, SafeInt< T, E >& result ) SAFEINT_CPP_THROW + static bool ModulusSimpleCase(U lhs, SafeInt rhs, SafeInt& result) SAFEINT_CPP_THROW { - if( rhs != 0 ) + if (rhs != 0) { - if( ModulusSignedCaseHelper< T, E, IntTraits< T >::isSigned >::SignedCase( rhs, result ) ) - return true; + if (ModulusSignedCaseHelper::isSigned>::SignedCase(rhs, result)) return true; - result = SafeInt< T, E >( (T)( lhs % (T)rhs ) ); + result = SafeInt((T)(lhs % (T)rhs)); return true; } @@ -6613,19 +7026,19 @@ class ModulusSimpleCaseHelper < T, U, E, true > } }; -template< typename T, typename U, typename E > -class ModulusSimpleCaseHelper < T, U, E, false > +template +class ModulusSimpleCaseHelper { public: - static bool ModulusSimpleCase( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt< T, E >& /*result*/ ) SAFEINT_NOTHROW + static bool ModulusSimpleCase(U /*lhs*/, SafeInt /*rhs*/, SafeInt& /*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 +template +SafeInt operator%(U lhs, SafeInt rhs) SAFEINT_CPP_THROW { // Value of return depends on sign of lhs // This one may not be safe - bounds check in constructor @@ -6633,53 +7046,58 @@ SafeInt< T, E > operator %( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW // Fast-track the simple case // same size and same sign - SafeInt< T, E > result; + SafeInt result; - if( ModulusSimpleCaseHelper< T, U, E, - sizeof(T) == sizeof(U) && (bool)IntTraits< T >::isSigned == (bool)IntTraits< U >::isSigned >::ModulusSimpleCase( lhs, rhs, result ) ) + if (ModulusSimpleCaseHelper < T, + U, + E, + sizeof(T) == sizeof(U) && + (bool)IntTraits::isSigned == (bool)IntTraits::isSigned > ::ModulusSimpleCase(lhs, rhs, result)) return result; - return SafeInt< T, E >( ( SafeInt< U, E >( lhs ) % (T)rhs ) ); + return SafeInt((SafeInt(lhs) % (T)rhs)); } // Multiplication -template < typename T, typename U, typename E > -SafeInt< T, E > operator *( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +template +SafeInt operator*(U lhs, SafeInt 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); + T ret(0); + MultiplicationHelper::method>::template MultiplyThrow((T)rhs, lhs, ret); + return SafeInt(ret); } -template < typename T, typename U, typename E, int method > class DivisionNegativeCornerCaseHelper; +template +class DivisionNegativeCornerCaseHelper; -template < typename T, typename U, typename E > class DivisionNegativeCornerCaseHelper< T, U, E, true > +template +class DivisionNegativeCornerCaseHelper { public: - static bool NegativeCornerCase( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + static bool NegativeCornerCase(U lhs, SafeInt 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 ); + if (CompileConst::Value()) + tmp = lhs / (U)(~(unsigned __int32)(T)rhs + 1); else - tmp = lhs/(U)( ~(unsigned __int64)(T)rhs + 1 ); + tmp = lhs / (U)(~(unsigned __int64)(T)rhs + 1); - if( tmp <= (U)IntTraits< T >::maxInt ) + if (tmp <= (U)IntTraits::maxInt) { - result = SafeInt< T, E >( (T)(~(unsigned __int64)tmp + 1) ); + result = SafeInt((T)(~(unsigned __int64)tmp + 1)); return true; } // Corner case - T maxT = IntTraits< T >::maxInt; - if( tmp == (U)maxT + 1 ) + T maxT = IntTraits::maxInt; + if (tmp == (U)maxT + 1) { - T minT = IntTraits< T >::minInt; - result = SafeInt< T, E >( minT ); + T minT = IntTraits::minInt; + result = SafeInt(minT); return true; } @@ -6687,35 +7105,41 @@ template < typename T, typename U, typename E > class DivisionNegativeCornerCase } }; -template < typename T, typename U, typename E > class DivisionNegativeCornerCaseHelper< T, U, E, false > +template +class DivisionNegativeCornerCaseHelper { public: - static bool NegativeCornerCase( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + static bool NegativeCornerCase(U /*lhs*/, SafeInt /*rhs*/, SafeInt& /*result*/) SAFEINT_NOTHROW { return false; } }; -template < typename T, typename U, typename E, int method > class DivisionCornerCaseHelper; +template +class DivisionCornerCaseHelper; -template < typename T, typename U, typename E > class DivisionCornerCaseHelper < T, U, E, true > +template +class DivisionCornerCaseHelper { public: - static bool DivisionCornerCase1( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + static bool DivisionCornerCase1(U lhs, SafeInt rhs, SafeInt& result) SAFEINT_CPP_THROW { - if( (T)rhs > 0 ) + if ((T)rhs > 0) { - result = SafeInt< T, E >( lhs/(T)rhs ); + result = SafeInt(lhs / (T)rhs); return true; } // Now rhs is either negative, or zero - if( (T)rhs != 0 ) + if ((T)rhs != 0) { - if( DivisionNegativeCornerCaseHelper< T, U, E, sizeof( U ) >= 4 && sizeof( T ) <= sizeof( U ) >::NegativeCornerCase( lhs, rhs, result ) ) + 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); + result = SafeInt(lhs / (T)rhs); return true; } @@ -6723,23 +7147,26 @@ template < typename T, typename U, typename E > class DivisionCornerCaseHelper < } }; -template < typename T, typename U, typename E > class DivisionCornerCaseHelper < T, U, E, false > +template +class DivisionCornerCaseHelper { public: - static bool DivisionCornerCase1( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + static bool DivisionCornerCase1(U /*lhs*/, SafeInt /*rhs*/, SafeInt& /*result*/) SAFEINT_NOTHROW { return false; } }; -template < typename T, typename U, typename E, int method > class DivisionCornerCaseHelper2; +template +class DivisionCornerCaseHelper2; -template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 < T, U, E, true > +template +class DivisionCornerCaseHelper2 { public: - static bool DivisionCornerCase2( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + static bool DivisionCornerCase2(U lhs, SafeInt rhs, SafeInt& result) SAFEINT_CPP_THROW { - if( lhs == IntTraits< U >::minInt && (T)rhs == -1 ) + if (lhs == IntTraits::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 @@ -6748,12 +7175,12 @@ template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 #if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER #pragma warning(push) -//cast truncates constant value -#pragma warning(disable:4310) +// cast truncates constant value +#pragma warning(disable : 4310) #endif - if( CompileConst::Value() ) - result = SafeInt< T, E >( (T)( -(T)IntTraits< U >::minInt ) ); + if (CompileConst::Value()) + result = SafeInt((T)(-(T)IntTraits::minInt)); else E::SafeIntOnOverflow(); @@ -6768,131 +7195,134 @@ template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 } }; -template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 < T, U, E, false > +template +class DivisionCornerCaseHelper2 { public: - static bool DivisionCornerCase2( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + static bool DivisionCornerCase2(U /*lhs*/, SafeInt /*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 +template +SafeInt operator/(U lhs, SafeInt 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 ) ) + SafeInt result; + if (DivisionCornerCaseHelper::method == (int)DivisionState_UnsignedSigned>:: + DivisionCornerCase1(lhs, rhs, result)) return result; - if( DivisionCornerCaseHelper2< T, U, E, SafeIntCompare< T, U >::isBothSigned >::DivisionCornerCase2( lhs, rhs, result ) ) + if (DivisionCornerCaseHelper2::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 ); + DivisionHelper::method>::template DivideThrow(lhs, (T)rhs, ret); + return SafeInt(ret); } // Addition -template < typename T, typename U, typename E > -SafeInt< T, E > operator +( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +template +SafeInt operator+(U lhs, SafeInt 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 ); + T ret(0); + AdditionHelper::method>::template AdditionThrow((T)rhs, lhs, ret); + return SafeInt(ret); } // Subtraction -template < typename T, typename U, typename E > -SafeInt< T, E > operator -( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +template +SafeInt operator-(U lhs, SafeInt rhs) SAFEINT_CPP_THROW { - T ret( 0 ); - SubtractionHelper< U, T, SubtractionMethod2< U, T >::method >::template SubtractThrow< E >( lhs, rhs.Ref(), ret ); + T ret(0); + SubtractionHelper::method>::template SubtractThrow(lhs, rhs.Ref(), ret); - return SafeInt< T, E >( ret ); + return SafeInt(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 +template +T& operator+=(T& lhs, SafeInt rhs) SAFEINT_CPP_THROW { - T ret( 0 ); - AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( lhs, (U)rhs, ret ); + T ret(0); + AdditionHelper::method>::template AdditionThrow(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 +template +T& operator-=(T& lhs, SafeInt rhs) SAFEINT_CPP_THROW { - T ret( 0 ); - SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( lhs, (U)rhs, ret ); + T ret(0); + SubtractionHelper::method>::template SubtractThrow(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 +template +T& operator*=(T& lhs, SafeInt rhs) SAFEINT_CPP_THROW { - T ret( 0 ); - MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( lhs, (U)rhs, ret ); + T ret(0); + MultiplicationHelper::method>::template MultiplyThrow(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 +template +T& operator/=(T& lhs, SafeInt rhs) SAFEINT_CPP_THROW { - T ret( 0 ); - DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( lhs, (U)rhs, ret ); + T ret(0); + DivisionHelper::method>::template DivideThrow(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 +template +T& operator%=(T& lhs, SafeInt rhs) SAFEINT_CPP_THROW { - T ret( 0 ); - ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( lhs, (U)rhs, ret ); + T ret(0); + ModulusHelper::method>::template ModulusThrow(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 +template +T& operator&=(T& lhs, SafeInt rhs) SAFEINT_NOTHROW { - lhs = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( lhs, (U)rhs ); + lhs = BinaryAndHelper::method>::And(lhs, (U)rhs); return lhs; } -template < typename T, typename U, typename E > -T& operator ^=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +template +T& operator^=(T& lhs, SafeInt rhs) SAFEINT_NOTHROW { - lhs = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( lhs, (U)rhs ); + lhs = BinaryXorHelper::method>::Xor(lhs, (U)rhs); return lhs; } -template < typename T, typename U, typename E > -T& operator |=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +template +T& operator|=(T& lhs, SafeInt rhs) SAFEINT_NOTHROW { - lhs = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( lhs, (U)rhs ); + lhs = BinaryOrHelper::method>::Or(lhs, (U)rhs); return lhs; } -template < typename T, typename U, typename E > -T& operator <<=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +template +T& operator<<=(T& lhs, SafeInt rhs) SAFEINT_NOTHROW { - lhs = (T)( SafeInt< T, E >( lhs ) << (U)rhs ); + lhs = (T)(SafeInt(lhs) << (U)rhs); return lhs; } -template < typename T, typename U, typename E > -T& operator >>=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +template +T& operator>>=(T& lhs, SafeInt rhs) SAFEINT_NOTHROW { - lhs = (T)( SafeInt< T, E >( lhs ) >> (U)rhs ); + lhs = (T)(SafeInt(lhs) >> (U)rhs); return lhs; } @@ -6900,90 +7330,90 @@ T& operator >>=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW // 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 +template +T*& operator+=(T*& lhs, SafeInt 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 ); + SafeInt ptr_val = reinterpret_cast(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 ) ) ) ); + lhs = reinterpret_cast((size_t)(ptr_val + (ptrdiff_t)(SafeInt(rhs) * sizeof(T)))); return lhs; } -template < typename T, typename U, typename E > -T*& operator -=( T*& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +template +T*& operator-=(T*& lhs, SafeInt 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 ); + SafeInt ptr_val = reinterpret_cast(lhs); // See above for comments - lhs = reinterpret_cast< T* >( (size_t)( ptr_val - (ptrdiff_t)( SafeInt< ptrdiff_t, E >( rhs ) * sizeof( T ) ) ) ); + lhs = reinterpret_cast((size_t)(ptr_val - (ptrdiff_t)(SafeInt(rhs) * sizeof(T)))); return lhs; } -template < typename T, typename U, typename E > -T*& operator *=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +template +T*& operator*=(T*& lhs, SafeInt) SAFEINT_NOTHROW { // This operator explicitly not supported - C_ASSERT( sizeof(T) == 0 ); + C_ASSERT(sizeof(T) == 0); return (lhs = NULL); } -template < typename T, typename U, typename E > -T*& operator /=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +template +T*& operator/=(T*& lhs, SafeInt) SAFEINT_NOTHROW { // This operator explicitly not supported - C_ASSERT( sizeof(T) == 0 ); + C_ASSERT(sizeof(T) == 0); return (lhs = NULL); } -template < typename T, typename U, typename E > -T*& operator %=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +template +T*& operator%=(T*& lhs, SafeInt) SAFEINT_NOTHROW { // This operator explicitly not supported - C_ASSERT( sizeof(T) == 0 ); + C_ASSERT(sizeof(T) == 0); return (lhs = NULL); } -template < typename T, typename U, typename E > -T*& operator &=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +template +T*& operator&=(T*& lhs, SafeInt) SAFEINT_NOTHROW { // This operator explicitly not supported - C_ASSERT( sizeof(T) == 0 ); + C_ASSERT(sizeof(T) == 0); return (lhs = NULL); } -template < typename T, typename U, typename E > -T*& operator ^=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +template +T*& operator^=(T*& lhs, SafeInt) SAFEINT_NOTHROW { // This operator explicitly not supported - C_ASSERT( sizeof(T) == 0 ); + C_ASSERT(sizeof(T) == 0); return (lhs = NULL); } -template < typename T, typename U, typename E > -T*& operator |=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +template +T*& operator|=(T*& lhs, SafeInt) SAFEINT_NOTHROW { // This operator explicitly not supported - C_ASSERT( sizeof(T) == 0 ); + C_ASSERT(sizeof(T) == 0); return (lhs = NULL); } -template < typename T, typename U, typename E > -T*& operator <<=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +template +T*& operator<<=(T*& lhs, SafeInt) SAFEINT_NOTHROW { // This operator explicitly not supported - C_ASSERT( sizeof(T) == 0 ); + C_ASSERT(sizeof(T) == 0); return (lhs = NULL); } -template < typename T, typename U, typename E > -T*& operator >>=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +template +T*& operator>>=(T*& lhs, SafeInt) SAFEINT_NOTHROW { // This operator explicitly not supported - C_ASSERT( sizeof(T) == 0 ); + C_ASSERT(sizeof(T) == 0); return (lhs = NULL); } @@ -6991,23 +7421,23 @@ T*& operator >>=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW // 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 +template +SafeInt operator<<(U lhs, SafeInt bits) SAFEINT_NOTHROW { - ShiftAssert( !IntTraits< T >::isSigned || (T)bits >= 0 ); - ShiftAssert( (T)bits < (int)IntTraits< U >::bitCount ); + ShiftAssert(!IntTraits::isSigned || (T)bits >= 0); + ShiftAssert((T)bits < (int)IntTraits::bitCount); - return SafeInt< U, E >( (U)( lhs << (T)bits ) ); + return SafeInt((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 +template +SafeInt operator>>(U lhs, SafeInt bits) SAFEINT_NOTHROW { - ShiftAssert( !IntTraits< T >::isSigned || (T)bits >= 0 ); - ShiftAssert( (T)bits < (int)IntTraits< U >::bitCount ); + ShiftAssert(!IntTraits::isSigned || (T)bits >= 0); + ShiftAssert((T)bits < (int)IntTraits::bitCount); - return SafeInt< U, E >( (U)( lhs >> (T)bits ) ); + return SafeInt((U)(lhs >> (T)bits)); } // Bitwise operators @@ -7015,24 +7445,24 @@ SafeInt< U, E > operator >>( U lhs, SafeInt< T, E > bits ) SAFEINT_NOTHROW // 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 +template +SafeInt operator&(U lhs, SafeInt rhs)SAFEINT_NOTHROW { - return SafeInt< T, E >( BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( (T)rhs, lhs ) ); + return SafeInt(BinaryAndHelper::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 +template +SafeInt operator^(U lhs, SafeInt rhs) SAFEINT_NOTHROW { - return SafeInt< T, E >(BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( (T)rhs, lhs ) ); + return SafeInt(BinaryXorHelper::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 +template +SafeInt operator|(U lhs, SafeInt rhs) SAFEINT_NOTHROW { - return SafeInt< T, E >( BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( (T)rhs, lhs ) ); + return SafeInt(BinaryOrHelper::method>::Or((T)rhs, lhs)); } #if SAFEINT_COMPILER == GCC_COMPILER @@ -7043,6 +7473,10 @@ SafeInt< T, E > operator |( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW #pragma clang diagnostic pop #endif -} // utilities -} // safeint3 +#ifdef C_ASSERT_DEFINED_SAFEINT +#undef C_ASSERT +#undef C_ASSERT_DEFINED_SAFEINT +#endif // C_ASSERT_DEFINED_SAFEINT +} // namespace safeint3 +} // namespace msl diff --git a/Release/include/cpprest/details/basic_types.h b/Release/include/cpprest/details/basic_types.h index eadba01684..d2ceb87189 100644 --- a/Release/include/cpprest/details/basic_types.h +++ b/Release/include/cpprest/details/basic_types.h @@ -1,27 +1,29 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Platform-dependent type definitions -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Platform-dependent type definitions + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#include +#include "cpprest/details/cpprest_compat.h" #include #include #include -#include "cpprest/details/cpprest_compat.h" +#include #ifndef _WIN32 -# define __STDC_LIMIT_MACROS -# include +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#include #else #include #endif @@ -30,7 +32,6 @@ namespace utility { - #ifdef _WIN32 #define _UTF16_STRINGS #endif @@ -47,9 +48,9 @@ typedef uint32_t HRESULT; // Needed for PPLX // // On Windows, all strings are wide // -typedef wchar_t char_t ; +typedef wchar_t char_t; typedef std::wstring string_t; -#define _XPLATSTR(x) L ## x +#define _XPLATSTR(x) L##x typedef std::wostringstream ostringstream_t; typedef std::wofstream ofstream_t; typedef std::wostream ostream_t; @@ -86,7 +87,7 @@ typedef std::stringstream stringstream_t; #define U(x) _XPLATSTR(x) #endif // !_TURN_OFF_PLATFORM_STRING -}// namespace utility +} // namespace utility typedef char utf8char; typedef std::string utf8string; @@ -114,7 +115,6 @@ 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) diff --git a/Release/include/cpprest/details/cpprest_compat.h b/Release/include/cpprest/details/cpprest_compat.h index 7b4f3c1842..6dae41d8d9 100644 --- a/Release/include/cpprest/details/cpprest_compat.h +++ b/Release/include/cpprest/details/cpprest_compat.h @@ -1,41 +1,43 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* 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: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * 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: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#if defined(_WIN32) // Settings specific to Windows +#if defined(_WIN32) #if _MSC_VER >= 1900 #define CPPREST_NOEXCEPT noexcept +#define CPPREST_CONSTEXPR constexpr #else #define CPPREST_NOEXCEPT -#endif - -#define CASABLANCA_UNREFERENCED_PARAMETER(x) (x) +#define CPPREST_CONSTEXPR const +#endif // _MSC_VER >= 1900 #include -#else // End settings specific to Windows - -// Settings common to all but Windows +#else // ^^^ _WIN32 ^^^ // vvv !_WIN32 vvv -#define __declspec(x) __attribute__ ((x)) +#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 __assume(x) \ + do \ + { \ + if (!(x)) __builtin_unreachable(); \ + } while (false) #define CPPREST_NOEXCEPT noexcept +#define CPPREST_CONSTEXPR constexpr #include #define _ASSERTE(x) assert(x) @@ -43,12 +45,13 @@ // No SAL on non Windows platforms #include "cpprest/details/nosal.h" -#if not defined __cdecl -#if defined cdecl -#define __cdecl __attribute__ ((cdecl)) -#else +#if !defined(__cdecl) +#if defined(cdecl) +#define __cdecl __attribute__((cdecl)) +#else // ^^^ defined cdecl ^^^ // vvv !defined cdecl vvv #define __cdecl -#endif +#endif // defined cdecl +#endif // not defined __cdecl #if defined(__ANDROID__) // This is needed to disable the use of __thread inside the boost library. @@ -58,30 +61,26 @@ // the .so from loading. #if not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION #define BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION -#endif -#endif +#endif // not defined BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION +#endif // defined(__ANDROID__) #ifdef __clang__ #include -#endif - -#endif // defined(__APPLE__) - -#endif - +#endif // __clang__ +#endif // _WIN32 #ifdef _NO_ASYNCRTIMP #define _ASYNCRTIMP -#else +#else // ^^^ _NO_ASYNCRTIMP ^^^ // vvv !_NO_ASYNCRTIMP vvv #ifdef _ASYNCRT_EXPORT #define _ASYNCRTIMP __declspec(dllexport) -#else +#else // ^^^ _ASYNCRT_EXPORT ^^^ // vvv !_ASYNCRT_EXPORT vvv #define _ASYNCRTIMP __declspec(dllimport) -#endif -#endif +#endif // _ASYNCRT_EXPORT +#endif // _NO_ASYNCRTIMP #ifdef CASABLANCA_DEPRECATION_NO_WARNINGS #define CASABLANCA_DEPRECATED(x) #else #define CASABLANCA_DEPRECATED(x) __declspec(deprecated(x)) -#endif +#endif // CASABLANCA_DEPRECATION_NO_WARNINGS diff --git a/Release/include/cpprest/details/fileio.h b/Release/include/cpprest/details/fileio.h index f60947daf1..ee88c15a5d 100644 --- a/Release/include/cpprest/details/fileio.h +++ b/Release/include/cpprest/details/fileio.h @@ -1,92 +1,95 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* 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: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * 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: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once #ifdef _WIN32 #include #endif -#include "pplx/pplxtasks.h" #include "cpprest/details/basic_types.h" +#include "pplx/pplxtasks.h" -namespace Concurrency { namespace streams +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 +/// +/// 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) { - _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 + // Positional data - size_t m_rdpos; - size_t m_wrpos; - bool m_atend; + size_t m_rdpos; + size_t m_wrpos; + bool m_atend; - // Input buffer + // Input buffer - size_t m_buffer_size; // The intended size of the buffer to read into. - char *m_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 + 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; + std::ios_base::openmode m_mode; - pplx::extensibility::recursive_lock_t m_lock; - }; + 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) {} - /// - /// 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() {} - }; +protected: + virtual ~_filestream_callback() {} +}; -} -}} +} // namespace details +} // namespace streams +} // namespace Concurrency extern "C" { @@ -102,7 +105,10 @@ extern "C" /// 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); + _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 /// @@ -117,78 +123,98 @@ _ASYNCRTIMP bool __cdecl _open_fsb_str(_In_ concurrency::streams::details::_file /// 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); + _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); - + /// + /// 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); + /// + /// 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); + /// + /// 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); + /// + /// 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); + /// + /// 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 _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 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); + /// + /// 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/Release/include/cpprest/details/http_helpers.h b/Release/include/cpprest/details/http_helpers.h index 2d2c1b6091..9bed095b0e 100644 --- a/Release/include/cpprest/details/http_helpers.h +++ b/Release/include/cpprest/details/http_helpers.h @@ -1,108 +1,48 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* 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: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * 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: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once #include "cpprest/details/basic_types.h" - #include "cpprest/http_msg.h" -namespace web { namespace http +namespace web +{ +namespace http { namespace details { - - 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); - } - - namespace compression - { - enum class compression_algorithm : int - { - deflate = 15, - gzip = 31, - invalid = 9999 - }; - - using data_buffer = std::vector; - - class stream_decompressor - { - public: - - static compression_algorithm to_compression_algorithm(const utility::string_t& alg) - { - if (_XPLATSTR("gzip") == alg) - { - return compression_algorithm::gzip; - } - else if (_XPLATSTR("deflate") == alg) - { - return compression_algorithm::deflate; - } - - return compression_algorithm::invalid; - } - - _ASYNCRTIMP static bool __cdecl is_supported(); - - _ASYNCRTIMP stream_decompressor(compression_algorithm alg); - - _ASYNCRTIMP data_buffer decompress(const data_buffer& input); - - _ASYNCRTIMP data_buffer decompress(const uint8_t* input, size_t input_size); - - _ASYNCRTIMP bool has_error() const; - - private: - class stream_decompressor_impl; - std::shared_ptr m_pimpl; - }; - - class stream_compressor - { - public: - - _ASYNCRTIMP static bool __cdecl is_supported(); - - _ASYNCRTIMP stream_compressor(compression_algorithm alg); - - _ASYNCRTIMP data_buffer compress(const data_buffer& input, bool finish); - - _ASYNCRTIMP data_buffer compress(const uint8_t* input, size_t input_size, bool finish); - - _ASYNCRTIMP bool has_error() const; - - private: - class stream_compressor_impl; - std::shared_ptr m_pimpl; - }; - - } -}}} +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); +} // namespace chunked_encoding + +} // namespace details +} // namespace http +} // namespace web diff --git a/Release/include/cpprest/details/http_server.h b/Release/include/cpprest/details/http_server.h index 0a94b0361b..37a82ff3dc 100644 --- a/Release/include/cpprest/details/http_server.h +++ b/Release/include/cpprest/details/http_server.h @@ -1,13 +1,13 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* HTTP Library: interface to implement HTTP server to service http_listeners. -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * HTTP Library: interface to implement HTTP server to service http_listeners. + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once @@ -17,23 +17,24 @@ #include "cpprest/http_listener.h" -namespace web { namespace http +namespace web +{ +namespace http +{ +namespace experimental { -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() { }; + virtual ~http_server() {}; /// /// Start listening for incoming requests. @@ -43,12 +44,14 @@ class http_server /// /// Registers an http listener. /// - virtual pplx::task register_listener(_In_ web::http::experimental::listener::details::http_listener_impl *pListener) = 0; + 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; + virtual pplx::task unregister_listener( + _In_ web::http::experimental::listener::details::http_listener_impl* pListener) = 0; /// /// Stop processing and listening for incoming requests. @@ -65,4 +68,5 @@ class http_server } // namespace details } // namespace experimental -}} // namespace web::http +} // namespace http +} // namespace web diff --git a/Release/include/cpprest/details/http_server_api.h b/Release/include/cpprest/details/http_server_api.h index e44e104f02..db1825726d 100644 --- a/Release/include/cpprest/details/http_server_api.h +++ b/Release/include/cpprest/details/http_server_api.h @@ -1,13 +1,13 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* HTTP Library: exposes the entry points to the http server transport apis. -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * HTTP Library: exposes the entry points to the http server transport apis. + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once @@ -15,16 +15,17 @@ #error "Error: http server APIs are not supported in XP" #endif //_WIN32_WINNT < _WIN32_WINNT_VISTA -#include - #include "cpprest/http_listener.h" +#include -namespace web { namespace http +namespace web +{ +namespace http +{ +namespace experimental { -namespace experimental { namespace details { - class http_server; /// @@ -37,7 +38,6 @@ class http_server; class http_server_api { public: - /// /// Returns whether or not any listeners are registered. /// @@ -56,20 +56,21 @@ class http_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); + 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); + 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(); + static http_server* __cdecl server_api(); private: - /// Used to lock access to the server api registration static pplx::extensibility::critical_section_t s_lock; @@ -86,4 +87,7 @@ class http_server_api http_server_api(); }; -}}}} // namespaces +} // namespace details +} // namespace experimental +} // namespace http +} // namespace web diff --git a/Release/include/cpprest/details/nosal.h b/Release/include/cpprest/details/nosal.h index 7aeeec03de..91a2dd8614 100644 --- a/Release/include/cpprest/details/nosal.h +++ b/Release/include/cpprest/details/nosal.h @@ -1,12 +1,12 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -***/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + ***/ #pragma once // selected MS SAL annotations @@ -74,4 +74,4 @@ #ifdef _Inout_updates_bytes_ #undef _Inout_updates_bytes_ #endif -#define _Inout_updates_bytes_(x) \ No newline at end of file +#define _Inout_updates_bytes_(x) diff --git a/Release/include/cpprest/details/resource.h b/Release/include/cpprest/details/resource.h index 7ca31da740..2d61283934 100644 --- a/Release/include/cpprest/details/resource.h +++ b/Release/include/cpprest/details/resource.h @@ -3,12 +3,12 @@ // 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 +#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/Release/include/cpprest/details/web_utilities.h b/Release/include/cpprest/details/web_utilities.h index ba6416546e..853d7614b1 100644 --- a/Release/include/cpprest/details/web_utilities.h +++ b/Release/include/cpprest/details/web_utilities.h @@ -1,66 +1,59 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* utility classes used by the different web:: clients -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * utility classes used by the different web:: clients + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once #include "cpprest/asyncrt_utils.h" +#include "cpprest/uri.h" namespace web { - -namespace http { namespace client { namespace details { -class winhttp_client; -class winrt_client; -class asio_context; -}}} -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; + _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) +#ifdef _WIN32 +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA +#ifdef __cplusplus_winrt class winrt_encryption { public: - winrt_encryption() {} - _ASYNCRTIMP winrt_encryption(const std::wstring &data); + winrt_encryption() = default; + _ASYNCRTIMP winrt_encryption(const std::wstring& data); _ASYNCRTIMP plaintext_string decrypt() const; + private: ::pplx::task m_buffer; }; -#else +#else // ^^^ __cplusplus_winrt ^^^ // vvv !__cplusplus_winrt vvv class win32_encryption { public: - win32_encryption() {} - _ASYNCRTIMP win32_encryption(const std::wstring &data); + win32_encryption() = default; + _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 -} +#endif // __cplusplus_winrt +#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA +#endif // _WIN32 +} // namespace details /// /// Represents a set of user credentials (user name and password) to be used @@ -79,25 +72,26 @@ class credentials /// /// 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) - {} + 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; } + 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 + 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(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return utility::string_t(*m_password.decrypt()); #else return m_password; @@ -113,7 +107,7 @@ class credentials details::plaintext_string _internal_decrypt() const { // Encryption APIs not supported on XP -#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) +#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return m_password.decrypt(); #else return details::plaintext_string(new ::utility::string_t(m_password)); @@ -123,7 +117,7 @@ class credentials private: ::utility::string_t m_username; -#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) +#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA #if defined(__cplusplus_winrt) details::winrt_encryption m_password; #else @@ -140,26 +134,38 @@ class credentials /// class web_proxy { - enum web_proxy_mode_internal{ use_default_, use_auto_discovery_, disabled_, user_provided_ }; + 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_}; + 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_) {} + web_proxy() : m_address(), 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)) {} + web_proxy(web_proxy_mode mode) : m_address(), 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_) {} + 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. @@ -177,8 +183,9 @@ class web_proxy /// 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_ ) + void set_credentials(web::credentials cred) + { + if (m_mode == disabled_) { throw std::invalid_argument("Cannot attach credentials to a disabled proxy"); } @@ -215,4 +222,4 @@ class web_proxy web::credentials m_credentials; }; -} +} // namespace web diff --git a/Release/include/cpprest/details/x509_cert_utilities.h b/Release/include/cpprest/details/x509_cert_utilities.h deleted file mode 100644 index cc40d9ccfb..0000000000 --- a/Release/include/cpprest/details/x509_cert_utilities.h +++ /dev/null @@ -1,49 +0,0 @@ -/*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* 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: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ - -#pragma once - -#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 -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-local-typedef" -#endif -#include -#if defined(__clang__) -#pragma clang diagnostic pop -#endif -#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 to 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); - -}}}} - -#endif \ No newline at end of file diff --git a/Release/include/cpprest/filestream.h b/Release/include/cpprest/filestream.h index b8b982556a..1e4a0f278e 100644 --- a/Release/include/cpprest/filestream.h +++ b/Release/include/cpprest/filestream.h @@ -1,22 +1,22 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Asynchronous File streams -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Asynchronous File streams + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#ifndef _CASA_FILE_STREAMS_H -#define _CASA_FILE_STREAMS_H +#ifndef CASA_FILE_STREAMS_H +#define CASA_FILE_STREAMS_H -#include "cpprest/details/fileio.h" #include "cpprest/astreambuf.h" +#include "cpprest/details/fileio.h" #include "cpprest/streams.h" #include @@ -25,1096 +25,1070 @@ #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 +{ +} namespace concurrency = Concurrency; #endif #endif -namespace Concurrency { namespace streams +namespace Concurrency +{ +namespace streams +{ +// Forward declarations +template +class file_buffer; + +namespace details +{ +// This operation queue is NOT thread safe +class async_operation_queue { - // Forward declarations - template class file_buffer; + pplx::task m_lastOperation; -namespace details { - // This operation queue is NOT thread safe - class async_operation_queue +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()) { - pplx::task m_lastOperation; - public: - async_operation_queue() + decltype(op()) res; // res is task , which always has default constructor + if (m_lastOperation.is_done()) { - m_lastOperation = pplx::task_from_result(); + res = op(); // Exceptions are expected to be thrown directly without catching + if (res.is_done()) return res; } - - // 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()) + else { - 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. + res = m_lastOperation.then([=] { + return op(); // It will cause task unwrapping }); - return res; } + 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; - void wait() const + virtual ~basic_file_buffer() + { + if (this->can_read()) { - m_lastOperation.wait(); + this->_close_read().wait(); } - }; + if (this->can_write()) + { + this->_close_write().wait(); + } + } +protected: /// - /// 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. + /// can_seek is used to determine whether a stream buffer supports seeking. /// - 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(); - } - } + virtual bool can_seek() const { return this->is_open(); } - protected: + /// + /// has_size is used to determine whether a stream buffer supports size(). + /// + virtual bool has_size() const { return this->is_open(); } - /// - /// can_seek is used to determine whether a stream buffer supports seeking. - /// - virtual bool can_seek() const { return this->is_open(); } + virtual utility::size64_t size() const + { + if (!this->is_open()) return 0; + return _get_size(m_info, sizeof(_CharType)); + } - /// - /// has_size is used to determine whether a stream buffer supports size(). - /// - virtual bool has_size() const { return this->is_open(); } + /// + /// 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; + } - virtual utility::size64_t size() const - { - if (!this->is_open()) - return 0; - return _get_size(m_info, sizeof(_CharType)); - } + /// + /// 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; - /// - /// 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 (size == 0 && m_info->m_buffer != nullptr) { - if ( direction == std::ios_base::in ) - return m_info->m_buffer_size; - else - return 0; + delete m_info->m_buffer; + m_info->m_buffer = nullptr; } + } - /// - /// 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; + /// + /// 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); - if ( size == 0 && m_info->m_buffer != nullptr ) - { - delete m_info->m_buffer; - m_info->m_buffer = nullptr; - } - } + return _in_avail_unprot(); + } - /// - /// 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); + size_t _in_avail_unprot() const + { + if (!this->is_open()) return 0; - return _in_avail_unprot(); - } + 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; - size_t _in_avail_unprot() const - { - if ( !this->is_open() ) 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; - 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; + return buffill - bufpos; + } - 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; + _file_info* _close_stream() + { + // indicate that we are no longer open + auto fileInfo = m_info; + m_info = nullptr; + return fileInfo; + } - return buffill - bufpos; - } + static pplx::task _close_file(_In_ _file_info* fileInfo) + { + pplx::task_completion_event result_tce; + auto callback = new _filestream_callback_close(result_tce); - _file_info * _close_stream() + if (!_close_fsb_nolock(&fileInfo, callback)) { - // indicate that we are no longer open - auto fileInfo = m_info; - m_info = nullptr; - return fileInfo; + delete callback; + return pplx::task_from_result(); } + return pplx::create_task(result_tce); + } - static pplx::task _close_file(_In_ _file_info * fileInfo) - { - pplx::task_completion_event result_tce; - auto callback = new _filestream_callback_close(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 ( !_close_fsb_nolock(&fileInfo, callback) ) + if (this->can_write()) { - delete callback; return pplx::task_from_result(); } - return pplx::create_task(result_tce); - } + else + { + // Neither heads are open. Close the underlying device + // to indicate that we are no longer open + auto fileInfo = _close_stream(); - // Workaround GCC compiler bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58972 - void _invoke_parent_close_read() + return _close_file(fileInfo); + } + }); + } + + pplx::task _close_write() + { + streambuf_state_manager<_CharType>::_close_write(); + if (this->can_read()) { - streambuf_state_manager<_CharType>::_close_read(); + // Read head is still open. Just flush the write data + return flush_internal(); } - - pplx::task _close_read() + else { - return m_readOps.enqueue_operation([this] - { - _invoke_parent_close_read(); + // Neither heads are open. Close the underlying device - if (this->can_write()) + // 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 { - return pplx::task_from_result(); + flushTask.wait(); } - else + catch (...) { - // 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(); + // 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); + return this->_close_file(fileInfo); }); } + } - /// - /// 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; - } + /// + /// 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); - /// - /// Submits a block already allocated by the stream buffer. - /// - /// Count of characters to be commited. - void _commit(size_t) + // Potentially we should consider deprecating this API, it is TERRIBLY inefficient. + std::shared_ptr<_CharType> sharedCh; + try { + sharedCh = std::make_shared<_CharType>(ch); } - - /// - /// 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) + catch (const std::bad_alloc&) { - ptr = nullptr; - count = 0; - return false; + delete callback; + throw; } - /// - /// 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) + size_t written = _putn_fsb(m_info, callback, sharedCh.get(), 1, sizeof(_CharType)); + if (written == sizeof(_CharType)) { - (void)(count); + delete callback; + return pplx::task_from_result(ch); } - /// - /// 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 != size_t(-1) ) - { - delete callback; - written = written/sizeof(_CharType); - return pplx::task_from_result(written); - } - return pplx::create_task(result_tce); - } + return pplx::create_task(result_tce).then([sharedCh](size_t) { return static_cast(*sharedCh); }); + } - // 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); - } - } + /// + /// 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; } - /// - /// 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); + /// + /// Submits a block already allocated by the stream buffer. + /// + /// Count of characters to be committed. + void _commit(size_t) {} - // Check again once the lock is held. + /// + /// 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; + } - 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); - } - } + /// + /// 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); } - auto result_tce = pplx::task_completion_event(); - auto callback = new _filestream_callback_bumpc(m_info, result_tce); + /// + /// 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 ch = _getn_fsb(m_info, callback, &callback->m_ch, 1, sizeof(_CharType)); + size_t written = _putn_fsb(m_info, callback, ptr, count, 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); - }); + if (written != 0 && written != size_t(-1)) + { + delete callback; + written = written / sizeof(_CharType); + return pplx::task_from_result(written); } + 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() + // Temporarily needed until the deprecated putn is removed. + virtual pplx::task _putn(const _CharType* ptr, size_t count, bool copy) + { + if (copy) { - 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; + auto sharedData = std::make_shared>(ptr, ptr + count); + return _putn(ptr, count).then([sharedData](size_t size) { return size; }); } - - pplx::task _getcImpl() + else { - if ( _in_avail_unprot() > 0 ) + 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 ) + if (_in_avail_unprot() > 0) { auto bufoff = m_info->m_rdpos - m_info->m_bufoff; - _CharType ch = m_info->m_buffer[bufoff*sizeof(_CharType)]; + _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_getc(m_info, result_tce); + 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) ) + 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(); - /// - /// 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(); - }); - } + if (_in_avail_unprot() == 0) return traits::requires_async(); - /// - /// 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(); + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); + + if (_in_avail_unprot() == 0) return traits::requires_async(); - 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); - if ( _in_avail_unprot() == 0 ) return traits::requires_async(); + // Check again once the lock is held. - auto bufoff = m_info->m_rdpos - m_info->m_bufoff; - _CharType ch = m_info->m_buffer[bufoff*sizeof(_CharType)]; - return (int_type)ch; + 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); + } } - /// - /// 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(); - }); - } + 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)); - /// - /// 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() + if (ch == sizeof(_CharType)) { - 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(); - }); + 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 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); + /// + /// 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(); }); + } - if ( _in_avail_unprot() >= count ) - { - pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); + /// + /// 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(); - // Check again once the lock is held. + if (_in_avail_unprot() == 0) return traits::requires_async(); - 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)); + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); - m_info->m_rdpos += count; - return pplx::task_from_result(count); - } - } + 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); - auto result_tce = pplx::task_completion_event(); - auto callback = new _filestream_callback_read(m_info, result_tce); + if (_in_avail_unprot() >= count) + { + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); - size_t read = _getn_fsb(m_info, callback, ptr, count, sizeof(_CharType)); + // Check again once the lock is held. - if ( read != 0 && read != size_t(-1) ) + if (_in_avail_unprot() >= count) { - 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)); + 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); } - 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; + auto result_tce = pplx::task_completion_event(); + auto callback = new _filestream_callback_read(m_info, result_tce); - if ( count == 0 || in_avail() == 0 ) return 0; + size_t read = _getn_fsb(m_info, callback, ptr, count, sizeof(_CharType)); - pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); + if (read != 0 && read != size_t(-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); + }); + } - size_t available = _in_avail_unprot(); - size_t copy = (count < available) ? count : available; + /// + /// 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; - 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)); + if (count == 0 || in_avail() == 0) return 0; - m_info->m_rdpos += copy; - m_info->m_atend = (copy < count); - return copy; - } + pplx::extensibility::scoped_recursive_lock_t lck(m_info->m_lock); - /// - /// 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; - } + 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 + /// + /// 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) { - return const_cast(this)->seekoff(0, std::ios_base::cur, mode); + m_readOps.wait(); + return (pos_type)_seekrdpos_fsb(m_info, size_t(pos), sizeof(_CharType)); } - - /// - /// 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) + else if ((m_info->m_mode & std::ios::ios_base::app) == 0) { - 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(); + 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) + /// + /// 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) { - if ( mode == std::ios_base::in ) + m_readOps.wait(); + size_t current_pos = static_cast(-1); + switch (way) { - 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::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)); + 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)); + current_pos = _seekrdtoend_fsb(m_info, int64_t(offset), sizeof(_CharType)); + if (current_pos == static_cast(-1)) + { + return -1; + } + return (pos_type)current_pos; default: // Fail on invalid input (_S_ios_seekdir_end) assert(false); - } } - else if ( (m_info->m_mode & std::ios::ios_base::app) == 0 ) + } + else if ((m_info->m_mode & std::ios::ios_base::app) == 0) + { + switch (way) { - switch ( way ) - { - case std::ios_base::beg: - return (pos_type)_seekwrpos_fsb(m_info, size_t(offset), sizeof(_CharType)); + 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)); + 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(); } + 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;}); - } + /// + /// 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; +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); + 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); + 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) { } + 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, + 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 + int _Prot = (int)std::ios_base::_Openprot #else - int _Prot = 0 // unsupported on Linux, for now + 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); - } + ) + { + 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) + 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) { - 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 + virtual void on_opened(_In_ _file_info* info) { - public: - _filestream_callback_open(const pplx::task_completion_event>> &op) : m_op(op) { } + m_op.set(std::shared_ptr>(new basic_file_buffer<_CharType>(info))); + delete this; + } - 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; + } - virtual void on_error(const std::exception_ptr & e) - { - m_op.set_exception(e); - delete this; - } + private: + pplx::task_completion_event>> m_op; + }; - 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) {} - class _filestream_callback_close : public details::_filestream_callback + virtual void on_closed() { - public: - _filestream_callback_close(const pplx::task_completion_event &op) : m_op(op) { } - - virtual void on_closed() - { - m_op.set(); - delete this; - } + m_op.set(); + delete this; + } - virtual void on_error(const std::exception_ptr & e) - { - m_op.set_exception(e); - 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; - }; + private: + pplx::task_completion_event m_op; + }; - template - class _filestream_callback_write : public details::_filestream_callback + 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) { - 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_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; - } + 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; - }; + private: + _file_info* m_info; + pplx::task_completion_event m_op; + }; - class _filestream_callback_write_b : public details::_filestream_callback + 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) { - 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; - } + virtual void on_completed(size_t) + { + m_op.set(); + delete this; + } - private: - _file_info *m_info; - pplx::task_completion_event m_op; - }; + 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 + 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) { - 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_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; - } + 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; - }; + private: + _file_info* m_info; + pplx::task_completion_event m_op; + }; - class _filestream_callback_bumpc : public details::_filestream_callback + 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) { - 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) + virtual void on_completed(size_t result) + { + if (result == sizeof(_CharType)) { - if ( result == sizeof(_CharType) ) - { - m_info->m_rdpos += 1; - m_op.set(m_ch); - } - else - { - m_op.set(traits::eof()); - } - delete this; + m_info->m_rdpos += 1; + m_op.set(m_ch); } - - virtual void on_error(const std::exception_ptr & e) + else { - m_op.set_exception(e); - delete this; + 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; + int_type m_ch; - private: - _file_info *m_info; - pplx::task_completion_event m_op; - }; + private: + _file_info* m_info; + pplx::task_completion_event m_op; + }; - class _filestream_callback_getc : public details::_filestream_callback + 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) { - 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) + virtual void on_completed(size_t result) + { + if (result == sizeof(_CharType)) { - if ( result == sizeof(_CharType) ) - { - m_op.set(m_ch); - } - else - { - m_op.set(traits::eof()); - } - delete this; + m_op.set(m_ch); } - - int_type m_ch; - - virtual void on_error(const std::exception_ptr & e) + else { - m_op.set_exception(e); - delete this; + m_op.set(traits::eof()); } + delete this; + } - private: - _file_info *m_info; - pplx::task_completion_event m_op; - }; + int_type m_ch; - _file_info *m_info; - async_operation_queue m_readOps; + 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; }; - } // namespace details + _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) /// - /// Stream buffer for file streams. + /// Open a new stream buffer representing the given file. /// - /// - /// 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, + /// 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 + int prot = _SH_DENYRD #else - int prot = 0 // unsupported on Linux + 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()); - }); - } + ) + { + 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. + /// Open a new stream buffer representing the given file. /// - /// - /// The data type of the basic element of the file_stream. - /// - template - class file_stream + /// 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) { - public: - + 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, + /// + /// 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 + int prot = (int)std::ios_base::_Openprot #else - int prot = 0 + 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); - }); - } + ) + { + 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, + /// + /// Open a new output 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 + int prot = (int)std::ios_base::_Openprot #else - int prot = 0 + 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); - }); - } + ) + { + 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 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); - }); - } + /// + /// Open a new output 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 +typedef file_stream fstream; +} // namespace streams +} // namespace Concurrency #endif - - - - - - - - - - - - - - diff --git a/Release/include/cpprest/http_client.h b/Release/include/cpprest/http_client.h index a936a23ede..82fa3222b6 100644 --- a/Release/include/cpprest/http_client.h +++ b/Release/include/cpprest/http_client.h @@ -1,45 +1,60 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* HTTP Library: Client-side APIs. -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * HTTP Library: Client-side APIs. + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#ifndef _CASA_HTTP_CLIENT_H -#define _CASA_HTTP_CLIENT_H +#ifndef CASA_HTTP_CLIENT_H +#define CASA_HTTP_CLIENT_H -#if defined (__cplusplus_winrt) +#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;}}} +#include +namespace web +{ +namespace http +{ +namespace client +{ +typedef IXMLHTTPRequest2* native_handle; +} +} // namespace http +} // namespace web #else -namespace web { namespace http{namespace client{ -typedef void* native_handle;}}} +namespace web +{ +namespace http +{ +namespace client +{ +typedef void* native_handle; +} +} // namespace http +} // namespace web #endif // __cplusplus_winrt -#include -#include - -#include "pplx/pplxtasks.h" +#include "cpprest/asyncrt_utils.h" +#include "cpprest/details/basic_types.h" +#include "cpprest/details/web_utilities.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" +#include "pplx/pplxtasks.h" +#include +#include -#if !defined(CPPREST_TARGET_XP) +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA #include "cpprest/oauth1.h" #endif @@ -65,7 +80,6 @@ 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. @@ -79,32 +93,29 @@ using web::web_proxy; class http_client_config { public: - http_client_config() : - m_guarantee_order(false), - m_timeout(std::chrono::seconds(30)), - m_chunksize(0), - m_request_compressed(false) + http_client_config() + : m_guarantee_order(false) + , m_timeout(std::chrono::seconds(30)) + , m_chunksize(0) + , m_request_compressed(false) #if !defined(__cplusplus_winrt) , m_validate_certificates(true) #endif #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) , m_tlsext_sni_enabled(true) #endif -#if defined(_WIN32) && !defined(__cplusplus_winrt) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) , m_buffer_request(false) #endif { } -#if !defined(CPPREST_TARGET_XP) +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA /// /// Get OAuth 1.0 configuration. /// /// Shared pointer to OAuth 1.0 configuration. - const std::shared_ptr oauth1() const - { - return m_oauth1; - } + const std::shared_ptr oauth1() const { return m_oauth1; } /// /// Set OAuth 1.0 configuration. @@ -120,10 +131,7 @@ class http_client_config /// Get OAuth 2.0 configuration. /// /// Shared pointer to OAuth 2.0 configuration. - const std::shared_ptr oauth2() const - { - return m_oauth2; - } + const std::shared_ptr oauth2() const { return m_oauth2; } /// /// Set OAuth 2.0 configuration. @@ -138,71 +146,51 @@ class http_client_config /// Get the web proxy object /// /// A reference to the web proxy object. - const web_proxy& proxy() const - { - return m_proxy; - } + 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); - } + 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; - } + 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; - } + 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; - } + 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; - } + 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 std::chrono::duration_cast(m_timeout); - } + utility::seconds timeout() const { return std::chrono::duration_cast(m_timeout); } /// /// Get the timeout /// /// The timeout (in whatever duration) used for each send and receive operation on the client. - template + template T timeout() const { return std::chrono::duration_cast(m_timeout); @@ -210,9 +198,10 @@ class http_client_config /// /// Set the timeout /// - /// The timeout (duration from microseconds range and up) used for each send and receive operation on the client. - template - void set_timeout(const T &timeout) + /// The timeout (duration from microseconds range and up) used for each send and receive + /// operation on the client. + template + void set_timeout(const T& timeout) { m_timeout = std::chrono::duration_cast(timeout); } @@ -220,83 +209,65 @@ class http_client_config /// /// 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; - } + /// 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; - } + /// 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; - } + bool is_default_chunksize() const { return m_chunksize == 0; } /// - /// Checks if requesting a compressed response is turned on, the default is off. + /// Checks if requesting a compressed response using Content-Encoding is turned on, the default is off. /// - /// True if compressed response is enabled, false otherwise - bool request_compressed_response() const - { - return m_request_compressed; - } + /// True if a content-encoded compressed response is allowed, false otherwise + bool request_compressed_response() const { return m_request_compressed; } /// - /// Request that the server responds with a compressed body. - /// If true, in cases where the server does not support compression, this will have no effect. + /// Request that the server respond with a compressed body using Content-Encoding; to use Transfer-Encoding, do not + /// set this, and specify a vector of pointers + /// to the set_decompress_factories method of the object for the request. + /// If true and the server does not support compression, this will have no effect. /// The response body is internally decompressed before the consumer receives the data. /// - /// True to turn on response body compression, false otherwise. - /// Please note there is a performance cost due to copying the request data. Currently only supported on Windows and OSX. - void set_request_compressed_response(bool request_compressed) - { - m_request_compressed = request_compressed; - } + /// True to turn on content-encoded response body compression, false + /// otherwise. Please note there is a performance cost due to copying the request data. Currently + /// only supported on Windows and OSX. + void set_request_compressed_response(bool request_compressed) { m_request_compressed = request_compressed; } #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; - } + 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; - } + /// 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 -#if defined(_WIN32) && !defined(__cplusplus_winrt) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) /// /// 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; - } + bool buffer_request() const { return m_buffer_request; } /// /// Sets the request buffering property. @@ -305,10 +276,7 @@ class http_client_config /// /// 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; - } + void set_buffer_request(bool buffer_request) { m_buffer_request = buffer_request; } #endif /// @@ -319,7 +287,7 @@ class http_client_config /// Windows Desktop, WinHTTP - HINTERNET (session) /// /// A user callback allowing for customization of the session - void set_nativesessionhandle_options(const std::function &callback) + void set_nativesessionhandle_options(const std::function& callback) { m_set_user_nativesessionhandle_options = callback; } @@ -331,8 +299,7 @@ class http_client_config /// A internal implementation handle. void _invoke_nativesessionhandle_options(native_handle handle) const { - if (m_set_user_nativesessionhandle_options) - m_set_user_nativesessionhandle_options(handle); + if (m_set_user_nativesessionhandle_options) m_set_user_nativesessionhandle_options(handle); } /// @@ -347,9 +314,9 @@ class http_client_config /// http - boost::asio::ip::tcp::socket * /// /// A user callback allowing for customization of the request - void set_nativehandle_options(const std::function &callback) + void set_nativehandle_options(const std::function& callback) { - m_set_user_nativehandle_options = callback; + m_set_user_nativehandle_options = callback; } /// @@ -358,18 +325,18 @@ class http_client_config /// A internal implementation handle. void invoke_nativehandle_options(native_handle handle) const { - if (m_set_user_nativehandle_options) - m_set_user_nativehandle_options(handle); + if (m_set_user_nativehandle_options) m_set_user_nativehandle_options(handle); } #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) /// /// Sets a callback to enable custom setting of the ssl context, at construction time. /// - /// A user callback allowing for customization of the ssl context at construction time. + /// A user callback allowing for customization of the ssl context at construction + /// time. void set_ssl_context_callback(const std::function& callback) { - m_ssl_context_callback = callback; + m_ssl_context_callback = callback; } /// @@ -384,24 +351,19 @@ class http_client_config /// Gets the TLS extension server name indication (SNI) status. /// /// True if TLS server name indication is enabled, false otherwise. - bool is_tlsext_sni_enabled() const - { - return m_tlsext_sni_enabled; - } + bool is_tlsext_sni_enabled() const { return m_tlsext_sni_enabled; } /// /// Sets the TLS extension server name indication (SNI) status. /// - /// False to disable the TLS (ClientHello) extension for server name indication, true otherwise. - /// Note: This setting is enabled by default as it is required in most virtual hosting scenarios. - void set_tlsext_sni_enabled(bool tlsext_sni_enabled) - { - m_tlsext_sni_enabled = tlsext_sni_enabled; - } + /// False to disable the TLS (ClientHello) extension for server name indication, + /// true otherwise. Note: This setting is enabled by default as it is required in most virtual + /// hosting scenarios. + void set_tlsext_sni_enabled(bool tlsext_sni_enabled) { m_tlsext_sni_enabled = tlsext_sni_enabled; } #endif private: -#if !defined(CPPREST_TARGET_XP) +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA std::shared_ptr m_oauth1; #endif @@ -421,13 +383,13 @@ class http_client_config #endif std::function m_set_user_nativehandle_options; - std::function m_set_user_nativesessionhandle_options; + std::function m_set_user_nativesessionhandle_options; #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) std::function m_ssl_context_callback; bool m_tlsext_sni_enabled; #endif -#if defined(_WIN32) && !defined(__cplusplus_winrt) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) bool m_buffer_request; #endif }; @@ -443,15 +405,17 @@ class http_client /// /// 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); + /// 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); + /// 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. @@ -477,14 +441,14 @@ class http_client /// Adds an HTTP pipeline stage to the client. /// /// A function object representing the pipeline stage. - _ASYNCRTIMP void add_handler(const std::function __cdecl(http_request, std::shared_ptr)> &handler); - + _ASYNCRTIMP void add_handler(const std::function __cdecl( + http_request, std::shared_ptr)>& handler); /// /// Adds an HTTP pipeline stage to the client. /// /// A shared pointer to a pipeline stage. - _ASYNCRTIMP void add_handler(const std::shared_ptr &stage); + _ASYNCRTIMP void add_handler(const std::shared_ptr& stage); /// /// Asynchronously sends an HTTP request. @@ -492,7 +456,8 @@ class http_client /// 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()); + _ASYNCRTIMP pplx::task request( + http_request request, const pplx::cancellation_token& token = pplx::cancellation_token::none()); /// /// Asynchronously sends an HTTP request. @@ -500,7 +465,8 @@ class http_client /// 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()) + pplx::task request(const method& mtd, + const pplx::cancellation_token& token = pplx::cancellation_token::none()) { http_request msg(mtd); return request(msg, token); @@ -510,13 +476,12 @@ class http_client /// 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. + /// 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()) + 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); @@ -527,15 +492,15 @@ class http_client /// 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()) + /// 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); @@ -548,17 +513,16 @@ class http_client /// 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()) + /// 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)); @@ -571,17 +535,16 @@ class http_client /// 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()) + /// 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)); @@ -594,17 +557,17 @@ class http_client /// 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. + /// 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()) + 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)); @@ -617,15 +580,14 @@ class http_client /// 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) + /// 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); } @@ -635,15 +597,14 @@ class http_client /// 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) + /// 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)); @@ -656,35 +617,34 @@ class http_client /// 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) + /// 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); + return request( + mtd, path_query_fragment, body_data, ::utility::conversions::to_utf16string("text/plain"), token); } -#if !defined (__cplusplus_winrt) +#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()) + /// 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); @@ -696,15 +656,14 @@ class http_client /// 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) + /// 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); } @@ -714,20 +673,18 @@ class http_client /// 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. + /// 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()) + 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); @@ -739,34 +696,34 @@ class http_client /// 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) + /// 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: - std::shared_ptr<::web::http::client::http_pipeline> m_pipeline; }; -namespace details { -#if defined(_WIN32) -extern const utility::char_t * get_with_body_err_msg; +namespace details +{ +#if defined(_WIN32) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) +extern const utility::char_t* get_with_body_err_msg; #endif -} +} // namespace details -}}} +} // namespace client +} // namespace http +} // namespace web #endif diff --git a/Release/include/cpprest/http_compression.h b/Release/include/cpprest/http_compression.h new file mode 100644 index 0000000000..b0059a6419 --- /dev/null +++ b/Release/include/cpprest/http_compression.h @@ -0,0 +1,326 @@ +/*** + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * HTTP Library: Compression and decompression interfaces + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ +#pragma once + +namespace web +{ +namespace http +{ +namespace compression +{ +/// +/// Hint as to whether a compress or decompress call is meant to be the last for a particular HTTP request or reply +/// +enum operation_hint +{ + is_last, // Used for the expected last compress() call, or for an expected single decompress() call + has_more // Used when further compress() calls will be made, or when multiple decompress() calls may be required +}; + +/// +/// Result structure for asynchronous compression and decompression operations +/// +struct operation_result +{ + size_t input_bytes_processed; // From the input buffer + size_t output_bytes_produced; // To the output buffer + bool done; // For compress, set when 'last' is true and there was enough space to complete compression; + // for decompress, set if the end of the decompression stream has been reached +}; + +/// +/// Compression interface for use with HTTP requests +/// +class compress_provider +{ +public: + virtual const utility::string_t& algorithm() const = 0; + virtual size_t compress(const uint8_t* input, + size_t input_size, + uint8_t* output, + size_t output_size, + operation_hint hint, + size_t& input_bytes_processed, + bool& done) = 0; + virtual pplx::task compress( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint) = 0; + virtual void reset() = 0; + virtual ~compress_provider() = default; +}; + +/// +/// Decompression interface for use with HTTP requests +/// +class decompress_provider +{ +public: + virtual const utility::string_t& algorithm() const = 0; + virtual size_t decompress(const uint8_t* input, + size_t input_size, + uint8_t* output, + size_t output_size, + operation_hint hint, + size_t& input_bytes_processed, + bool& done) = 0; + virtual pplx::task decompress( + const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size, operation_hint hint) = 0; + virtual void reset() = 0; + virtual ~decompress_provider() = default; +}; + +/// +/// Factory interface for compressors for use with received HTTP requests +/// +class compress_factory +{ +public: + virtual const utility::string_t& algorithm() const = 0; + virtual std::unique_ptr make_compressor() const = 0; + virtual ~compress_factory() = default; +}; + +/// +/// Factory interface for decompressors for use with HTTP requests +/// +class decompress_factory +{ +public: + virtual const utility::string_t& algorithm() const = 0; + virtual uint16_t weight() const = 0; + virtual std::unique_ptr make_decompressor() const = 0; + virtual ~decompress_factory() = default; +}; + +/// +/// Built-in compression support +/// +namespace builtin +{ +/// +/// Test whether cpprestsdk was built with built-in compression support +/// True if cpprestsdk was built with built-in compression support, and false if not. +/// +_ASYNCRTIMP bool supported(); + +/// +// String constants for each built-in compression algorithm, for convenient use with the factory functions +/// +namespace algorithm +{ +#if defined(_MSC_VER) && _MSC_VER < 1900 +const utility::char_t* const GZIP = _XPLATSTR("gzip"); +const utility::char_t* const DEFLATE = _XPLATSTR("deflate"); +const utility::char_t* const BROTLI = _XPLATSTR("br"); +#else // ^^^ VS2013 and before ^^^ // vvv VS2015+, and everything else vvv +constexpr const utility::char_t* const GZIP = _XPLATSTR("gzip"); +constexpr const utility::char_t* const DEFLATE = _XPLATSTR("deflate"); +constexpr const utility::char_t* const BROTLI = _XPLATSTR("br"); +#endif + +/// +/// Test whether cpprestsdk was built with built-in compression support and +/// the supplied string matches a supported built-in algorithm +/// The name of the algorithm to test for built-in support. +/// True if cpprestsdk was built with built-in compression support and +/// the supplied string matches a supported built-in algorithm, and false if not. +/// +_ASYNCRTIMP bool supported(const utility::string_t& algorithm); +} // namespace algorithm + +/// +/// Factory function to instantiate a built-in compression provider with default parameters by compression algorithm +/// name. +/// +/// The name of the algorithm for which to instantiate a provider. +/// +/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists. +/// +_ASYNCRTIMP std::unique_ptr make_compressor(const utility::string_t& algorithm); + +/// +/// Factory function to instantiate a built-in decompression provider with default parameters by compression algorithm +/// name. +/// +/// The name of the algorithm for which to instantiate a provider. +/// +/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists. +/// +_ASYNCRTIMP std::unique_ptr make_decompressor(const utility::string_t& algorithm); + +/// +/// Factory function to obtain a pointer to a built-in compression provider factory by compression algorithm name. +/// +/// The name of the algorithm for which to find a factory. +/// +/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists. +/// +_ASYNCRTIMP std::shared_ptr get_compress_factory(const utility::string_t& algorithm); + +/// +/// Factory function to obtain a pointer to a built-in decompression provider factory by compression algorithm name. +/// +/// The name of the algorithm for which to find a factory. +/// +/// A caller-owned pointer to a provider of the requested-type, or to nullptr if no such built-in type exists. +/// +_ASYNCRTIMP std::shared_ptr get_decompress_factory(const utility::string_t& algorithm); + +/// +// Factory function to instantiate a built-in gzip compression provider with caller-selected parameters. +/// +/// +/// A caller-owned pointer to a gzip compression provider, or to nullptr if the library was built without built-in +/// compression support. +/// +_ASYNCRTIMP std::unique_ptr make_gzip_compressor(int compressionLevel, + int method, + int strategy, + int memLevel); + +/// +// Factory function to instantiate a built-in deflate compression provider with caller-selected parameters. +/// +/// +/// A caller-owned pointer to a deflate compression provider, or to nullptr if the library was built without built-in +/// compression support.. +/// +_ASYNCRTIMP std::unique_ptr make_deflate_compressor(int compressionLevel, + int method, + int strategy, + int memLevel); + +/// +// Factory function to instantiate a built-in Brotli compression provider with caller-selected parameters. +/// +/// +/// A caller-owned pointer to a Brotli compression provider, or to nullptr if the library was built without built-in +/// compression support. +/// +_ASYNCRTIMP std::unique_ptr make_brotli_compressor( + uint32_t window, uint32_t quality, uint32_t mode, uint32_t block, uint32_t nomodel, uint32_t hint); +} // namespace builtin + +/// +/// Factory function to instantiate a compression provider factory by compression algorithm name. +/// +/// The name of the algorithm supported by the factory. Must match that returned by the +/// web::http::compression::compress_provider type instantiated by the factory's make_compressor function. +/// The supplied string is copied, and thus need not remain valid once the call returns. +/// A factory function to be used to instantiate a compressor matching the factory's +/// reported algorithm. +/// +/// A pointer to a generic provider factory implementation configured with the supplied parameters. +/// +/// +/// This method may be used to conveniently instantiate a factory object for a caller-selected compress_provider. +/// That provider may be of the caller's own design, or it may be one of the built-in types. As such, this method may +/// be helpful when a caller wishes to build vectors containing a mix of custom and built-in providers. +/// +_ASYNCRTIMP std::shared_ptr make_compress_factory( + const utility::string_t& algorithm, std::function()> make_compressor); + +/// +/// Factory function to instantiate a decompression provider factory by compression algorithm name. +/// +/// The name of the algorithm supported by the factory. Must match that returned by the +/// web::http::compression::decompress_provider type instantiated by the factory's make_decompressor function. +/// The supplied string is copied, and thus need not remain valid once the call returns. +/// A numeric weight for the compression algorithm, times 1000, for use as a "quality value" when +/// requesting that the server send a compressed response. Valid values are between 0 and 1000, inclusive, where higher +/// values indicate more preferred algorithms, and 0 indicates that the algorithm is not allowed; values greater than +/// 1000 are treated as 1000. +/// A factory function to be used to instantiate a decompressor matching the factory's +/// reported algorithm. +/// +/// A pointer to a generic provider factory implementation configured with the supplied parameters. +/// +/// +/// This method may be used to conveniently instantiate a factory object for a caller-selected +/// decompress_provider. That provider may be of the caller's own design, or it may be one of the built-in +/// types. As such, this method may be helpful when a caller wishes to change the weights of built-in provider types, +/// to use custom providers without explicitly implementing a decompress_factory, or to build vectors containing +/// a mix of custom and built-in providers. +/// +_ASYNCRTIMP std::shared_ptr make_decompress_factory( + const utility::string_t& algorithm, + uint16_t weight, + std::function()> make_decompressor); + +namespace details +{ +/// +/// Header type enum for use with compressor and decompressor header parsing and building functions +/// +enum header_types +{ + transfer_encoding, + content_encoding, + te, + accept_encoding +}; + +/// +/// Factory function to instantiate an appropriate compression provider, if any. +/// +/// A TE or Accept-Encoding header to interpret. +/// Specifies the type of header whose contents are in the encoding parameter; valid values are +/// header_type::te and header_type::accept_encoding. +/// A compressor object of the caller's preferred (possibly custom) type, which is used if +/// possible. +/// A collection of factory objects for use in construction of an appropriate compressor, if +/// any. If empty or not supplied, the set of supported built-in compressors is used. +/// +/// A pointer to a compressor object that is acceptable per the supplied header, or to nullptr if no matching +/// algorithm is found. +/// +_ASYNCRTIMP std::unique_ptr get_compressor_from_header( + const utility::string_t& encoding, + header_types type, + const std::vector>& factories = std::vector>()); + +/// +/// Factory function to instantiate an appropriate decompression provider, if any. +/// +/// A Transfer-Encoding or Content-Encoding header to interpret. +/// Specifies the type of header whose contents are in the encoding parameter; valid values are +/// header_type::transfer_encoding and header_type::content_encoding. +/// A collection of factory objects for use in construction of an appropriate decompressor, +/// if any. If empty or not supplied, the set of supported built-in compressors is used. +/// +/// A pointer to a decompressor object that is acceptable per the supplied header, or to nullptr if no matching +/// algorithm is found. +/// +_ASYNCRTIMP std::unique_ptr get_decompressor_from_header( + const utility::string_t& encoding, + header_types type, + const std::vector>& factories = + std::vector>()); + +/// +/// Helper function to compose a TE or Accept-Encoding header with supported, and possibly ranked, compression +/// algorithms. +/// +/// Specifies the type of header to be built; valid values are header_type::te and +/// header_type::accept_encoding. +/// A collection of factory objects for use in header construction. If empty or not +/// supplied, the set of supported built-in compressors is used. +/// +/// A well-formed header, without the header name, specifying the acceptable ranked compression types. +/// +_ASYNCRTIMP utility::string_t build_supported_header(header_types type, + const std::vector>& factories = + std::vector>()); +} // namespace details +} // namespace compression +} // namespace http +} // namespace web diff --git a/Release/include/cpprest/http_headers.h b/Release/include/cpprest/http_headers.h index 2458872410..4b4f9eaceb 100644 --- a/Release/include/cpprest/http_headers.h +++ b/Release/include/cpprest/http_headers.h @@ -1,24 +1,26 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once +#include "cpprest/asyncrt_utils.h" #include #include #include -#include #include -#include "cpprest/asyncrt_utils.h" - -namespace web { namespace http { +#include +namespace web +{ +namespace http +{ /// /// Binds an individual reference to a string value. /// @@ -29,7 +31,7 @@ namespace web { namespace http { /// 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 +bool bind(const key_type& text, _t& ref) // const { utility::istringstream_t iss(text); iss >> ref; @@ -49,14 +51,45 @@ bool bind(const key_type &text, _t &ref) // const /// The string value. /// The value to bind to. /// true if the binding succeeds, false otherwise. -template +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 +bool bind(const key_type& text, utility::string_t& ref) // const { ref = text; return true; } +namespace details +{ +template +bool bind_impl(const key_type& text, _t& ref) +{ + utility::istringstream_t iss(text); + iss.imbue(std::locale::classic()); + iss >> ref; + if (iss.fail() || !iss.eof()) + { + return false; + } + + return true; +} + +template +bool bind_impl(const key_type& text, utf16string& ref) +{ + ref = utility::conversions::to_utf16string(text); + return true; +} + +template +bool bind_impl(const key_type& text, std::string& ref) +{ + ref = utility::conversions::to_utf8string(text); + return true; +} +} // namespace details + /// /// Represents HTTP headers, acts like a map. /// @@ -66,32 +99,32 @@ class http_headers /// 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 + 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 + return utility::details::str_iless(str1, str2); } }; +private: + typedef std::map inner_container; + +public: /// /// 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; + typedef inner_container::key_type key_type; + typedef inner_container::key_compare key_compare; + typedef inner_container::allocator_type allocator_type; + typedef inner_container::size_type size_type; + typedef inner_container::difference_type difference_type; + typedef inner_container::pointer pointer; + typedef inner_container::const_pointer const_pointer; + typedef inner_container::reference reference; + typedef inner_container::const_reference const_reference; + typedef inner_container::iterator iterator; + typedef inner_container::const_iterator const_iterator; + typedef inner_container::reverse_iterator reverse_iterator; + typedef inner_container::const_reverse_iterator const_reverse_iterator; /// /// Constructs an empty set of HTTP headers. @@ -102,15 +135,15 @@ class http_headers /// Copy constructor. /// /// An http_headers object to copy from. - http_headers(const http_headers &other) : m_headers(other.m_headers) {} + 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) + http_headers& operator=(const http_headers& other) { - if(this != &other) + if (this != &other) { m_headers = other.m_headers; } @@ -121,15 +154,15 @@ class http_headers /// Move constructor. /// /// An http_headers object to move. - http_headers(http_headers &&other) : m_headers(std::move(other.m_headers)) {} + 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) + http_headers& operator=(http_headers&& other) { - if(this != &other) + if (this != &other) { m_headers = std::move(other.m_headers); } @@ -145,13 +178,15 @@ class http_headers template void add(const key_type& name, const _t1& value) { - if (has(name)) + auto printedValue = utility::conversions::details::print_string(value); + auto& mapVal = m_headers[name]; + if (mapVal.empty()) { - m_headers[name].append(_XPLATSTR(", ")).append(utility::conversions::details::print_string(value)); + mapVal = std::move(printedValue); } else { - m_headers[name] = utility::conversions::details::print_string(value); + mapVal.append(_XPLATSTR(", ")).append(std::move(printedValue)); } } @@ -159,10 +194,7 @@ class http_headers /// Removes a header field. /// /// The name of the header field. - void remove(const key_type& name) - { - m_headers.erase(name); - } + void remove(const key_type& name) { m_headers.erase(name); } /// /// Removes all elements from the headers. @@ -191,7 +223,7 @@ class http_headers /// /// 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]; } + 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 @@ -199,8 +231,8 @@ class http_headers /// /// 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); } + 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. @@ -209,23 +241,15 @@ class http_headers /// 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 + 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 + if (iter == m_headers.end()) { return false; } + + return web::http::details::bind_impl(iter->second, value) || iter->second.empty(); } /// @@ -291,34 +315,8 @@ class http_headers _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, utf16string &ref) const - { - ref = utility::conversions::to_utf16string(text); - return true; - } - bool bind_impl(const key_type &text, std::string &ref) const - { - ref = utility::conversions::to_utf8string(text); - return true; - } - // Headers are stored in a map with case insensitive key. - std::map m_headers; + inner_container m_headers; }; - -}} \ No newline at end of file +} // namespace http +} // namespace web diff --git a/Release/include/cpprest/http_listener.h b/Release/include/cpprest/http_listener.h index c8059e02ba..a5457c0135 100644 --- a/Release/include/cpprest/http_listener.h +++ b/Release/include/cpprest/http_listener.h @@ -1,27 +1,27 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* HTTP Library: HTTP listener (server-side) APIs -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * HTTP Library: HTTP listener (server-side) APIs + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#ifndef _CASA_HTTP_LISTENER_H -#define _CASA_HTTP_LISTENER_H - -#include -#include +#ifndef CASA_HTTP_LISTENER_H +#define CASA_HTTP_LISTENER_H #include "cpprest/http_msg.h" +#include +#include #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO) #include #endif -#if !defined(_WIN32) || (_WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO) +#if !defined(_WIN32) || (_WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt)) || \ + defined(CPPREST_FORCE_HTTP_LISTENER_ASIO) namespace web { @@ -33,53 +33,50 @@ 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)) - , m_backlog(0) - {} + http_listener_config() : m_timeout(utility::seconds(120)), m_backlog(0) {} /// /// Copy constructor. /// /// http_listener_config to copy. - http_listener_config(const http_listener_config &other) + http_listener_config(const http_listener_config& other) : m_timeout(other.m_timeout) , m_backlog(other.m_backlog) #if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO) , m_ssl_context_callback(other.m_ssl_context_callback) #endif - {} + { + } /// /// Move constructor. /// /// http_listener_config to move from. - http_listener_config(http_listener_config &&other) + http_listener_config(http_listener_config&& other) : m_timeout(std::move(other.m_timeout)) , m_backlog(std::move(other.m_backlog)) #if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO) , m_ssl_context_callback(std::move(other.m_ssl_context_callback)) #endif - {} + { + } /// /// Assignment operator. /// /// http_listener_config instance. - http_listener_config & operator=(const http_listener_config &rhs) + http_listener_config& operator=(const http_listener_config& rhs) { - if(this != &rhs) + if (this != &rhs) { m_timeout = rhs.m_timeout; m_backlog = rhs.m_backlog; @@ -94,9 +91,9 @@ class http_listener_config /// Assignment operator. /// /// http_listener_config instance. - http_listener_config & operator=(http_listener_config &&rhs) + http_listener_config& operator=(http_listener_config&& rhs) { - if(this != &rhs) + if (this != &rhs) { m_timeout = std::move(rhs.m_timeout); m_backlog = std::move(rhs.m_backlog); @@ -111,39 +108,27 @@ class http_listener_config /// Get the timeout /// /// The timeout (in seconds). - utility::seconds timeout() const - { - return m_timeout; - } + 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); - } + void set_timeout(utility::seconds timeout) { m_timeout = std::move(timeout); } /// /// Get the listen backlog /// - /// The maximum length of the queue of pending connections, or zero for the implementation default. - /// The implementation may not honour this value. - int backlog() const - { - return m_backlog; - } + /// The maximum length of the queue of pending connections, or zero for the implementation + /// default. The implementation may not honour this value. + int backlog() const { return m_backlog; } /// /// Set the listen backlog /// - /// The maximum length of the queue of pending connections, or zero for the implementation default. - /// The implementation may not honour this value. - void set_backlog(int backlog) - { - m_backlog = backlog; - } + /// The maximum length of the queue of pending connections, or zero for the implementation + /// default. The implementation may not honour this value. + void set_backlog(int backlog) { m_backlog = backlog; } #if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO) /// @@ -158,15 +143,15 @@ class http_listener_config /// /// Set the callback of ssl context /// - /// The function to configure a ssl context which will setup https connections. - void set_ssl_context_callback(const std::function &ssl_context_callback) + /// The function to configure a ssl context which will setup https + /// connections. + void set_ssl_context_callback(const std::function& ssl_context_callback) { m_ssl_context_callback = ssl_context_callback; } #endif private: - utility::seconds m_timeout; int m_backlog; #if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO) @@ -176,19 +161,13 @@ class http_listener_config 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()) - { - } + 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); @@ -202,16 +181,15 @@ class http_listener_impl /// 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::uri& uri() const { return m_uri; } - const http_listener_config & configuration() const { return m_config; } + 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); @@ -235,7 +213,6 @@ class http_listener_impl class http_listener { public: - /// /// Create a listener from a URI. /// @@ -261,57 +238,58 @@ class http_listener /// /// 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()) - { - } + 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(); + ~http_listener() + { + if (m_impl) + { + // As a safe guard close the listener if not already done. + // Users are required to call close, but this is just a safeguard. + try + { + m_impl->close().wait(); + } + catch (...) + { + } + } + } /// /// 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(); - } + 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. + /// 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(); - } + 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; - } + 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) + void support(const http::method& method, const std::function& handler) { m_impl->m_supported_methods[method] = handler; } @@ -320,30 +298,27 @@ class http_listener /// Get the URI of the listener. /// /// The URI this listener is for. - const http::uri & uri() const { return m_impl->uri(); } + 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(); } + 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)) - { - } + 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) + http_listener& operator=(http_listener&& other) { - if(this != &other) + if (this != &other) { m_impl = std::move(other.m_impl); } @@ -351,15 +326,17 @@ class http_listener } private: - // No copying of listeners. - http_listener(const http_listener &other); - http_listener &operator=(const http_listener &other); + http_listener(const http_listener& other); + http_listener& operator=(const http_listener& other); std::unique_ptr m_impl; }; -}}}} +} // namespace listener +} // namespace experimental +} // namespace http +} // namespace web #endif #endif diff --git a/Release/include/cpprest/http_msg.h b/Release/include/cpprest/http_msg.h index b85e98ff42..50f05ef213 100644 --- a/Release/include/cpprest/http_msg.h +++ b/Release/include/cpprest/http_msg.h @@ -1,37 +1,36 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* HTTP Library: Request and reply message definitions. -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * HTTP Library: Request and reply message definitions. + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once +#include "cpprest/asyncrt_utils.h" +#include "cpprest/containerstream.h" +#include "cpprest/details/cpprest_compat.h" +#include "cpprest/http_compression.h" +#include "cpprest/http_headers.h" +#include "cpprest/json.h" +#include "cpprest/streams.h" +#include "cpprest/uri.h" +#include "pplx/pplxtasks.h" #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" +#include 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. @@ -40,7 +39,7 @@ using web::uri_builder; namespace client { - class http_client; +class http_client; } /// @@ -52,7 +51,10 @@ struct http_version uint8_t minor; inline bool operator==(const http_version& other) const { return major == other.major && minor == other.minor; } - inline bool operator<(const http_version& other) const { return major < other.major || (major == other.major && minor < other.minor); } + inline bool operator<(const http_version& other) const + { + return major < other.major || (major == other.major && minor < other.minor); + } inline bool operator!=(const http_version& other) const { return !(*this == other); } inline bool operator>=(const http_version& other) const { return !(*this < other); } @@ -95,7 +97,7 @@ class methods { public: #define _METHODS -#define DAT(a,b) _ASYNCRTIMP const static method a; +#define DAT(a, b) _ASYNCRTIMP const static method a; #include "cpprest/details/http_constants.dat" #undef _METHODS #undef DAT @@ -110,14 +112,14 @@ class status_codes { public: #define _PHRASES -#define DAT(a,b,c) const static status_code a=b; +#define DAT(a, b, c) const static status_code a = b; #include "cpprest/details/http_constants.dat" #undef _PHRASES #undef DAT }; -namespace details { - +namespace details +{ /// /// Constants for MIME types. /// @@ -125,7 +127,7 @@ class mime_types { public: #define _MIME_TYPES -#define DAT(a,b) _ASYNCRTIMP const static utility::string_t a; +#define DAT(a, b) _ASYNCRTIMP const static utility::string_t a; #include "cpprest/details/http_constants.dat" #undef _MIME_TYPES #undef DAT @@ -138,26 +140,27 @@ class charset_types { public: #define _CHARSET_TYPES -#define DAT(a,b) _ASYNCRTIMP const static utility::string_t a; +#define DAT(a, b) _ASYNCRTIMP const static utility::string_t a; #include "cpprest/details/http_constants.dat" #undef _CHARSET_TYPES #undef DAT }; -} +} // namespace details /// 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 - }; -} +/// +/// 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 +}; +} // namespace message_direction typedef utility::string_t reason_phrase; typedef std::function progress_handler; @@ -175,7 +178,7 @@ class header_names { public: #define _HEADER_NAMES -#define DAT(a,b) _ASYNCRTIMP const static utility::string_t a; +#define DAT(a, b) _ASYNCRTIMP const static utility::string_t a; #include "cpprest/details/http_constants.dat" #undef _HEADER_NAMES #undef DAT @@ -187,13 +190,11 @@ class header_names 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)) {} + http_exception(const utility::string_t& whatArg) : m_msg(utility::conversions::to_utf8string(whatArg)) {} #ifdef _WIN32 /// @@ -208,8 +209,7 @@ class http_exception : public std::exception /// 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)) + http_exception(int errorCode) : m_errorCode(utility::details::create_error_code(errorCode)) { m_msg = m_errorCode.message(); } @@ -219,10 +219,11 @@ class http_exception : public std::exception /// /// 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)) - {} + 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 /// @@ -230,10 +231,10 @@ class http_exception : public std::exception /// /// 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)) - {} + http_exception(int errorCode, std::string whatArg) + : m_errorCode(utility::details::create_error_code(errorCode)), m_msg(std::move(whatArg)) + { + } #endif /// @@ -242,28 +243,44 @@ class http_exception : public std::exception /// /// Error code value. /// Error category for the code. - http_exception(int errorCode, const std::error_category &cat) : m_errorCode(std::error_code(errorCode, cat)) + 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. + /// Creates an http_exception with from a error code with a category, and a string message. /// - /// A null terminated character string. - const char* what() const CPPREST_NOEXCEPT + /// Error code value. + /// Error message string. + http_exception(std::error_code errorCode, const utility::string_t& whatArg) + : m_errorCode(std::move(errorCode)), m_msg(utility::conversions::to_utf8string(whatArg)) { - return m_msg.c_str(); } +#ifdef _WIN32 /// - /// Retrieves the underlying error code causing this exception. + /// Creates an http_exception with from a error code with a category, and a string message. /// - /// A std::error_code. - const std::error_code & error_code() const + /// Error code value. + /// Error message string. + http_exception(std::error_code errorCode, std::string whatArg) + : m_errorCode(std::move(errorCode)), m_msg(std::move(whatArg)) { - return m_errorCode; } +#endif + + /// + /// 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; @@ -272,7 +289,6 @@ class http_exception : public std::exception namespace details { - /// /// Base class for HTTP messages. /// This class is to store common functionality so it isn't duplicated on @@ -281,19 +297,24 @@ namespace details 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; } + http::http_version http_version() const { return m_http_version; } + + 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); + _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, @@ -302,7 +323,8 @@ class http_msg_base /// 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); + 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); @@ -316,40 +338,87 @@ class http_msg_base /// /// Completes this message /// - virtual _ASYNCRTIMP void _complete(utility::size64_t bodySize, const std::exception_ptr &exceptionPtr = std::exception_ptr()); + 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; } + 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; } + 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; } + 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 concurrency::streams::ostream& outstream() const { return m_outStream; } + + /// + /// Sets the compressor for the message body + /// + void set_compressor(std::unique_ptr compressor) + { + m_compressor = std::move(compressor); + } + + /// + /// Gets the compressor for the message body, if any + /// + std::unique_ptr& compressor() { return m_compressor; } + + /// + /// Sets the collection of factory classes for decompressors for use with the message body + /// + void set_decompress_factories(const std::vector>& factories) + { + m_decompressors = factories; + } + + /// + /// Gets the collection of factory classes for decompressors to be used to decompress the message body, if any + /// + const std::vector>& decompress_factories() + { + return m_decompressors; + } - const pplx::task_completion_event & _get_data_available() const { return m_data_available; } + 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 remaining input stream length + /// + /// + /// std::numeric_limits::max() if the stream's remaining length cannot be determined + /// length if the stream's remaining length (which may be 0) can be determined + /// + /// + /// This routine should only be called after a msg (request/response) has been + /// completely constructed. + /// + _ASYNCRTIMP size_t _get_stream_length(); + /// /// Determine the content length /// /// - /// size_t::max if there is content with unknown length (transfer_encoding:chunked) + /// std::numeric_limits::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 /// @@ -359,14 +428,34 @@ class http_msg_base /// _ASYNCRTIMP size_t _get_content_length(); + /// + /// Determine the content length, and, if necessary, manage compression in the Transfer-Encoding header + /// + /// + /// std::numeric_limits::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 is like _get_content_length, except that it adds a compression algorithm to + /// the Trasfer-Length header if compression is configured. It throws if a Transfer-Encoding + /// header exists and does not match the one it generated. + /// + _ASYNCRTIMP size_t _get_content_length_and_set_compression(); + + void _set_http_version(const http::http_version& http_version) { m_http_version = http_version; } + protected: + std::unique_ptr m_compressor; + std::unique_ptr m_decompressor; + std::vector> m_decompressors; /// /// 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 + /// Even in the presence 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 @@ -381,11 +470,14 @@ class http_msg_base /// concurrency::streams::ostream m_outStream; + http::http_version m_http_version; 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; + + size_t _get_content_length(bool honor_compression); }; /// @@ -397,6 +489,7 @@ class _http_server_context public: _http_server_context() {} virtual ~_http_server_context() {} + private: }; @@ -406,7 +499,7 @@ class _http_server_context class _http_response final : public http::details::http_msg_base { public: - _http_response() : m_status_code((std::numeric_limits::max)()) { } + _http_response() : m_status_code((std::numeric_limits::max)()) {} _http_response(http::status_code code) : m_status_code(code) {} @@ -414,15 +507,18 @@ class _http_response final : public http::details::http_msg_base void set_status_code(http::status_code code) { m_status_code = code; } - const http::reason_phrase & reason_phrase() const { return m_reason_phrase; } + const http::reason_phrase& reason_phrase() const { return m_reason_phrase; } - void set_reason_phrase(const http::reason_phrase &reason) { m_reason_phrase = reason; } + 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(); } + _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_server_context(std::unique_ptr server_context) + { + m_server_context = std::move(server_context); + } private: std::unique_ptr<_http_server_context> m_server_context; @@ -433,27 +529,24 @@ class _http_response final : public http::details::http_msg_base } // 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()) { } + 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)) { } + http_response(http::status_code code) : _m_impl(std::make_shared(code)) {} /// /// Gets the status code of the response message. @@ -475,14 +568,14 @@ class http_response /// 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(); } + 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); } + void set_reason_phrase(const http::reason_phrase& reason) const { _m_impl->set_reason_phrase(reason); } /// /// Gets the headers of the response message. @@ -491,13 +584,13 @@ class http_response /// /// Use the to fill in desired headers. /// - http_headers &headers() { return _m_impl->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(); } + const http_headers& headers() const { return _m_impl->headers(); } /// /// Generates a string representation of the message, including the body when possible. @@ -520,31 +613,39 @@ class http_response 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); }); + 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. + /// 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 text. /// 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); }); + 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. + /// 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 text. /// 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); }); + return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { + return impl->extract_utf16string(ignore_content_type); + }); } /// @@ -556,7 +657,9 @@ class http_response 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); }); + return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { + return impl->_extract_json(ignore_content_type); + }); } /// @@ -566,7 +669,9 @@ class http_response 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(); }); + return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) { + return impl->_extract_vector(); + }); } /// @@ -574,14 +679,14 @@ class http_response /// 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. + /// 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")) + 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); + _m_impl->set_body( + concurrency::streams::bytestream::open_istream(std::move(body_text)), length, content_type); } /// @@ -589,13 +694,13 @@ class http_response /// 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. + /// 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")) + 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); + _m_impl->set_body( + concurrency::streams::bytestream::open_istream(body_text), body_text.size(), content_type); } /// @@ -607,7 +712,8 @@ class http_response /// /// 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")) + 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) { @@ -616,10 +722,9 @@ class http_response 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")))); + _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")))); } /// @@ -630,11 +735,13 @@ class http_response /// /// This will overwrite any previously set body data. /// - void set_body(const json::value &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")); + set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)), + length, + _XPLATSTR("application/json")); } /// @@ -645,7 +752,7 @@ class http_response /// /// This will overwrite any previously set body data. /// - void set_body(std::vector &&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); @@ -659,7 +766,7 @@ class http_response /// /// This will overwrite any previously set body data. /// - void set_body(const std::vector &body_data) + void set_body(const std::vector& body_data) { set_body(concurrency::streams::bytestream::open_istream(body_data), body_data.size()); } @@ -671,10 +778,11 @@ class http_response /// 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. + /// This cannot be used in conjunction with any external 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")) + 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); } @@ -687,10 +795,12 @@ class http_response /// 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. + /// This cannot be used in conjunction with any external 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")) + 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); } @@ -705,10 +815,7 @@ class http_response /// 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(); - } + concurrency::streams::istream body() const { return _m_impl->instream(); } /// /// Signals the user (client) when all the data for this response message has been received. @@ -717,36 +824,40 @@ class http_response 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; }); + 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)); } + 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 { +namespace details +{ /// /// Internal representation of an HTTP request message. /// class _http_request final : 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; } + http::method& method() { return m_method; } - uri &request_uri() { return m_uri; } + uri& request_uri() { return m_uri; } _ASYNCRTIMP uri absolute_uri() const; @@ -754,62 +865,54 @@ class _http_request final : public http::details::http_msg_base, public std::ena _ASYNCRTIMP void set_request_uri(const uri&); - http::http_version http_version() const { return m_http_version; } - const utility::string_t& remote_address() const { return m_remote_address; } - const pplx::cancellation_token &cancellation_token() const { return m_cancellationToken; } + const pplx::cancellation_token& cancellation_token() const { return m_cancellationToken; } - void set_cancellation_token(const pplx::cancellation_token &token) - { - m_cancellationToken = token; - } + 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); + _ASYNCRTIMP pplx::task reply(const http_response& response); - pplx::task get_response() - { - return pplx::task(m_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_response_stream(const concurrency::streams::ostream& stream) { m_response_stream = stream; } - void set_progress_handler(const progress_handler &handler) + 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; } + const concurrency::streams::ostream& _response_stream() const { return m_response_stream; } - http::details::_http_server_context * _get_server_context() const { return m_server_context.get(); } + const std::shared_ptr& _progress_handler() const { return m_progress_handler; } - void _set_server_context(std::unique_ptr server_context) { m_server_context = std::move(server_context); } + http::details::_http_server_context* _get_server_context() const { return m_server_context.get(); } - void _set_listener_path(const utility::string_t &path) { m_listener_path = path; } + void _set_server_context(std::unique_ptr server_context) + { + m_server_context = std::move(server_context); + } - void _set_base_uri(const http::uri &base_uri) { m_base_uri = base_uri; } + void _set_listener_path(const utility::string_t& path) { m_listener_path = path; } - void _set_http_version(const http::http_version &http_version) { m_http_version = http_version; } + void _set_base_uri(const http::uri& base_uri) { m_base_uri = base_uri; } - void _set_remote_address(const utility::string_t &remote_address) { m_remote_address = remote_address; } + void _set_remote_address(const utility::string_t& remote_address) { m_remote_address = remote_address; } 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. + // 0 = No reply sent + // 1 = Usual reply sent + // 2 = Reply aborted by another thread; e.g. server shutdown pplx::details::atomic_long m_initiated_response; std::unique_ptr m_server_context; @@ -826,13 +929,10 @@ class _http_request final : public http::details::http_msg_base, public std::ena pplx::task_completion_event m_response; - http::http_version m_http_version; - utility::string_t m_remote_address; }; - -} // namespace details +} // namespace details /// /// Represents an HTTP request. @@ -843,15 +943,13 @@ class http_request /// /// Constructs a new HTTP request with the 'GET' method. /// - http_request() - : _m_impl(std::make_shared(methods::GET)) {} + 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))) {} + http_request(http::method mtd) : _m_impl(std::make_shared(std::move(mtd))) {} /// /// Destructor frees any held resources. @@ -862,19 +960,19 @@ class 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(); } + const http::method& method() const { return _m_impl->method(); } /// /// Set 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; } + 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(); } + const uri& request_uri() const { return _m_impl->request_uri(); } /// /// Set the underling URI of the request message. @@ -909,7 +1007,7 @@ class http_request /// /// Use the http_headers::add to fill in desired headers. /// - http_headers &headers() { return _m_impl->headers(); } + http_headers& headers() { return _m_impl->headers(); } /// /// Gets a const reference to the headers of the response message. @@ -918,7 +1016,7 @@ class http_request /// /// Use the http_headers::add to fill in desired headers. /// - const http_headers &headers() const { return _m_impl->headers(); } + const http_headers& headers() const { return _m_impl->headers(); } /// /// Returns the HTTP protocol version of this request message. @@ -944,31 +1042,39 @@ class http_request 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); }); + 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. + /// 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); }); + 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. + /// 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); }); + return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { + return impl->extract_utf16string(ignore_content_type); + }); } /// @@ -980,7 +1086,9 @@ class http_request 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); }); + return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { + return impl->_extract_json(ignore_content_type); + }); } /// @@ -990,7 +1098,9 @@ class http_request 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(); }); + return pplx::create_task(_m_impl->_get_data_available()).then([impl](utility::size64_t) { + return impl->_extract_vector(); + }); } /// @@ -998,14 +1108,14 @@ class http_request /// 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. + /// 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")) + 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); + _m_impl->set_body( + concurrency::streams::bytestream::open_istream(std::move(body_text)), length, content_type); } /// @@ -1013,13 +1123,13 @@ class http_request /// 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. + /// 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")) + 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); + _m_impl->set_body( + concurrency::streams::bytestream::open_istream(body_text), body_text.size(), content_type); } /// @@ -1032,19 +1142,19 @@ class http_request /// /// 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")) + 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) + 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")))); + _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")))); } /// @@ -1055,11 +1165,13 @@ class http_request /// /// This will overwrite any previously set body data. /// - void set_body(const json::value &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")); + _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_text)), + length, + _XPLATSTR("application/json")); } /// @@ -1070,10 +1182,12 @@ class http_request /// /// This will overwrite any previously set body data. /// - void set_body(std::vector &&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")); + _m_impl->set_body(concurrency::streams::bytestream::open_istream(std::move(body_data)), + length, + _XPLATSTR("application/octet-stream")); } /// @@ -1084,7 +1198,7 @@ class http_request /// /// This will overwrite any previously set body data. /// - void set_body(const std::vector &body_data) + void set_body(const std::vector& body_data) { set_body(concurrency::streams::bytestream::open_istream(body_data), body_data.size()); } @@ -1099,7 +1213,8 @@ class http_request /// 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")) + 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); } @@ -1115,7 +1230,9 @@ class http_request /// 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")) + 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); } @@ -1130,10 +1247,7 @@ class http_request /// 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(); - } + 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 @@ -1144,11 +1258,96 @@ class http_request /// 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) + void set_response_stream(const concurrency::streams::ostream& stream) { return _m_impl->set_response_stream(stream); } + /// + /// Sets a compressor that will be used to compress the body of the HTTP message as it is sent. + /// + /// A pointer to an instantiated compressor of the desired type. + /// + /// This cannot be used in conjunction with any external means of compression. The Transfer-Encoding + /// header will be managed internally, and must not be set by the client. + /// + void set_compressor(std::unique_ptr compressor) + { + return _m_impl->set_compressor(std::move(compressor)); + } + + /// + /// Sets a compressor that will be used to compress the body of the HTTP message as it is sent. + /// + /// The built-in compression algorithm to use. + /// + /// True if a built-in compressor was instantiated, otherwise false. + /// + /// + /// This cannot be used in conjunction with any external means of compression. The Transfer-Encoding + /// header will be managed internally, and must not be set by the client. + /// + bool set_compressor(utility::string_t algorithm) + { + _m_impl->set_compressor(http::compression::builtin::make_compressor(algorithm)); + return (bool)_m_impl->compressor(); + } + + /// + /// Gets the compressor to be used to compress the message body, if any. + /// + /// + /// The compressor itself. + /// + std::unique_ptr& compressor() { return _m_impl->compressor(); } + + /// + /// Sets the default collection of built-in factory classes for decompressors that may be used to + /// decompress the body of the HTTP message as it is received, effectively enabling decompression. + /// + /// The collection of factory classes for allowable decompressors. The + /// supplied vector itself need not remain valid after the call returns. + /// + /// This default collection is implied if request_compressed_response() is set in the associated + /// client::http_client_config and neither overload of this method has been called. + /// + /// This cannot be used in conjunction with any external means of decompression. The TE and Accept-Encoding + /// headers must not be set by the client, as they will be managed internally as appropriate. + /// + _ASYNCRTIMP void set_decompress_factories(); + + /// + /// Sets a collection of factory classes for decompressors that may be used to decompress the + /// body of the HTTP message as it is received, effectively enabling decompression. + /// + /// + /// If set, this collection takes the place of the built-in compression providers. It may contain + /// custom factory classes and/or factory classes for built-in providers, and may be used to adjust + /// the weights of the built-in providers, which default to 500 (i.e. "q=0.500"). + /// + /// This cannot be used in conjunction with any external means of decompression. The TE and Accept-Encoding + /// headers must not be set by the client, as they will be managed internally as appropriate. + /// + void set_decompress_factories(const std::vector>& factories) + { + return _m_impl->set_decompress_factories(factories); + } + + /// + /// Gets the collection of factory classes for decompressors to be used to decompress the message body, if any. + /// + /// + /// The collection of factory classes itself. + /// + /// + /// This cannot be used in conjunction with any external means of decompression. The TE + /// header must not be set by the client, as it will be managed internally. + /// + const std::vector>& decompress_factories() const + { + return _m_impl->decompress_factories(); + } + /// /// Defines a callback function that will be invoked for every chunk of data uploaded or downloaded /// as part of the request. @@ -1177,27 +1376,21 @@ class http_request /// 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); - } + 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); } + 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)); - } + pplx::task reply(http::status_code status) const { return reply(http_response(status)); } /// /// Responds to this HTTP request. @@ -1205,7 +1398,7 @@ class 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 + pplx::task reply(http::status_code status, const json::value& body_data) const { http_response response(status); response.set_body(body_data); @@ -1223,7 +1416,9 @@ class http_request // 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 + 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); @@ -1242,7 +1437,9 @@ class http_request // 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 + 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); @@ -1261,7 +1458,9 @@ class http_request // 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 + 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); @@ -1275,7 +1474,9 @@ class http_request /// 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 + 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); @@ -1290,7 +1491,10 @@ class http_request /// 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 + 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); @@ -1311,10 +1515,7 @@ class http_request /// 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(); - } + pplx::task get_response() const { return _m_impl->get_response(); } /// /// Generates a string representation of the message, including the body when possible. @@ -1336,43 +1537,44 @@ class http_request /// /// Gets the server context associated with this HTTP message. /// - http::details::_http_server_context * _get_server_context() const { return _m_impl->_get_server_context(); } + 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) + static http_request _create_request(std::unique_ptr server_context) { - _m_impl->set_cancellation_token(token); + return http_request(std::move(server_context)); } - - const pplx::cancellation_token & _cancellation_token() const + void _set_server_context(std::unique_ptr server_context) { - return _m_impl->cancellation_token(); + _m_impl->_set_server_context(std::move(server_context)); } - void _set_base_uri(const http::uri &base_uri) - { - _m_impl->_set_base_uri(base_uri); - } + 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))) {} + http_request(std::unique_ptr server_context) + : _m_impl(std::make_shared(std::move(server_context))) + { + } std::shared_ptr _m_impl; }; -namespace client { +namespace client +{ class http_pipeline; } @@ -1384,17 +1586,16 @@ class http_pipeline; /// 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. +/// 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: - http_pipeline_stage() = default; - http_pipeline_stage & operator=(const http_pipeline_stage &) = delete; - http_pipeline_stage(const http_pipeline_stage &) = delete; + http_pipeline_stage& operator=(const http_pipeline_stage&) = delete; + http_pipeline_stage(const http_pipeline_stage&) = delete; virtual ~http_pipeline_stage() = default; @@ -1406,36 +1607,26 @@ class http_pipeline_stage : public std::enable_shared_from_this propagate(http_request request) = 0; protected: - /// /// 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; - } + 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. CASABLANCA_DEPRECATED("This api is redundant. Use 'shared_from_this()' directly instead.") - std::shared_ptr current_stage() - { - return this->shared_from_this(); - } + std::shared_ptr current_stage() { return this->shared_from_this(); } private: friend class ::web::http::client::http_pipeline; - void set_next_stage(const std::shared_ptr &next) - { - m_next_stage = next; - } + void set_next_stage(const std::shared_ptr& next) { m_next_stage = next; } std::shared_ptr m_next_stage; - }; -}} +} // namespace http +} // namespace web diff --git a/Release/include/cpprest/interopstream.h b/Release/include/cpprest/interopstream.h index 5cf575aa0e..e3287c1beb 100644 --- a/Release/include/cpprest/interopstream.h +++ b/Release/include/cpprest/interopstream.h @@ -1,524 +1,554 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* 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: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * 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: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#include "pplx/pplxtasks.h" #include "cpprest/astreambuf.h" #include "cpprest/streams.h" +#include "pplx/pplxtasks.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 { - +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; /// - /// 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. + /// Private constructor /// - template - class basic_stdio_buffer : public streambuf_state_manager<_CharType> + basic_stdio_buffer(_In_ std::basic_streambuf<_CharType>* streambuf, std::ios_base::openmode mode) + : streambuf_state_manager<_CharType>(mode), m_buffer(streambuf) { - 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; } +public: + /// + /// Destructor + /// + virtual ~basic_stdio_buffer() + { + this->_close_read(); + this->_close_write(); + } - virtual pplx::task _sync() { return pplx::task_from_result(m_buffer->pubsync() == 0); } +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 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)); } + virtual size_t in_avail() const { return (size_t)m_buffer->in_avail(); } - 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 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 _getn(_Out_writes_ (size) _CharType *ptr, _In_ size_t size) { return pplx::task_from_result((size_t)m_buffer->sgetn(ptr, size)); } + virtual pplx::task _sync() { return pplx::task_from_result(m_buffer->pubsync() == 0); } - virtual int_type _sbumpc() { return m_buffer->sbumpc(); } - virtual int_type _sgetc() { return m_buffer->sgetc(); } + 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)); + } - 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()); } + 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 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 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 _CharType* _alloc(size_t) { return nullptr; } - virtual void _commit(size_t) {} + virtual int_type _sbumpc() { return m_buffer->sbumpc(); } + virtual int_type _sgetc() { return m_buffer->sgetc(); } - virtual bool acquire(_CharType*&, size_t&) { return false; } - virtual void release(_CharType *, size_t) { } + 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()); } - template friend class concurrency::streams::stdio_ostream; - template friend class concurrency::streams::stdio_istream; + 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); + } - std::basic_streambuf<_CharType>* m_buffer; - }; + virtual _CharType* _alloc(size_t) { return nullptr; } + virtual void _commit(size_t) {} - } // namespace details + 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: /// - /// 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. + /// Constructor /// - /// - /// The data type of the basic element of the stdio_ostream. + /// + /// The data type of the basic element of the source output stream. /// - /// - /// 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 + /// 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)))) { - 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; } - }; + /// + /// Copy constructor + /// + /// The source object + stdio_ostream(const stdio_ostream& other) : basic_ostream(other) {} /// - /// 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. + /// 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 stdio_istream. + /// + /// The data type of the basic element of the source 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 + /// 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)))) { - 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 { + /// + /// Copy constructor + /// + /// The source object + stdio_istream(const stdio_istream& other) : basic_istream(other) {} /// - /// 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:: + /// Assignment operator /// - template - class basic_async_streambuf : public std::basic_streambuf + /// The source object + /// A reference to the input stream object that contains the result of the assignment. + stdio_istream& operator=(const stdio_istream& other) { - 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. - // + 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) + /// + /// Writes one byte to the stream buffer. + /// + int_type overflow(int_type ch) + { + try { - try - { return m_buffer.putc(CharType(ch)).get(); } - catch(...) - { - return traits::eof(); - } + catch (...) + { + return traits::eof(); } + } - /// - /// Gets one byte from the stream buffer without moving the read position. - /// - int_type underflow() + /// + /// Gets one byte from the stream buffer without moving the read position. + /// + int_type underflow() + { + try { - try - { return m_buffer.getc().get(); } - catch(...) - { - return traits::eof(); - } + catch (...) + { + return traits::eof(); } + } - /// - /// Gets one byte from the stream buffer and move the read position one character. - /// - int_type uflow() + /// + /// Gets one byte from the stream buffer and move the read position one character. + /// + int_type uflow() + { + try { - try - { return m_buffer.bumpc().get(); } - catch(...) - { - return traits::eof(); - } + 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; + /// + /// 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 - { + try + { while (read_so_far < cnt) { - size_t rd = m_buffer.getn(ptr+read_so_far, cnt-read_so_far).get(); + size_t rd = m_buffer.getn(ptr + read_so_far, cnt - read_so_far).get(); read_so_far += rd; - if ( rd == 0 ) - break; + if (rd == 0) break; } return read_so_far; } - catch(...) - { - return 0; - } + 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) + /// + /// Writes a given number of characters from the provided block into the stream buffer. + /// + std::streamsize xsputn(const CharType* ptr, std::streamsize count) + { + try { - try - { - return m_buffer.putn_nocopy(ptr, static_cast(count)).get(); - } - catch(...) - { - return 0; - } + return m_buffer.putn_nocopy(ptr, static_cast(count)).get(); } - - /// - /// Synchronizes with the underlying medium. - /// - int sync() // must be int as per std::basic_streambuf + catch (...) { - 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) + /// + /// Synchronizes with the underlying medium. + /// + int sync() // must be int as per std::basic_streambuf + { + try { - 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)); - } + m_buffer.sync().wait(); } - - /// - /// 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) + catch (...) { - try - { - return m_buffer.seekpos(pos, mode); - } - catch(...) - { - return (pos_type(-1)); - } } - - private: - concurrency::streams::streambuf m_buffer; - }; - - } // namespace details + return 0; + } /// - /// A concrete STL ostream which relies on an asynchronous stream for its I/O. + /// Seeks to the given offset relative to the beginning, end, or current position. /// - /// - /// The data type of the basic element of the stream. - /// - template - class async_ostream : public std::basic_ostream + pos_type seekoff(off_type offset, + std::ios_base::seekdir dir, + std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { - 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()) + 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); } - - /// - /// 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) + catch (...) { + return (pos_type(-1)); } - - private: - details::basic_async_streambuf m_strbuf; - }; + } /// - /// A concrete STL istream which relies on an asynchronous stream for its I/O. + /// Seeks to the given offset relative to the beginning of the stream. /// - /// - /// The data type of the basic element of the stream. - /// - template - class async_istream : public std::basic_istream + pos_type seekpos(pos_type pos, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { - 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()) + try { + return m_buffer.seekpos(pos, mode); } - - /// - /// 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) + 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()) + { + } - private: - details::basic_async_streambuf m_strbuf; - }; + /// + /// 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()) + { + } /// - /// A concrete STL istream which relies on an asynchronous stream buffer for its I/O. + /// Constructor /// - /// - /// The data type of the basic element of the stream. + /// + /// The data type of the basic element of the source streambuf. /// - template - class async_iostream : public std::basic_iostream + /// The asynchronous stream buffer to use for I/O + template + async_istream(const streams::streambuf& strbuf) + : std::basic_istream(&m_strbuf), m_strbuf(strbuf) { - 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; +}; + +/// +/// 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; - }; +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: /// - /// Static class containing factory functions for WinRT streams implemented on top of Casablanca async streams. + /// Creates a WinRT IInputStream reference from an asynchronous stream buffer. /// - /// 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); - }; + /// 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 +} // namespace streams +} // namespace Concurrency #if defined(_WIN32) #pragma warning(pop) -#endif \ No newline at end of file +#endif diff --git a/Release/include/cpprest/json.h b/Release/include/cpprest/json.h index 07c5450243..4095be50ea 100644 --- a/Release/include/cpprest/json.h +++ b/Release/include/cpprest/json.h @@ -1,1979 +1,1786 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* HTTP Library: JSON parser and writer -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * HTTP Library: JSON parser and writer + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#ifndef _CASA_JSON_H -#define _CASA_JSON_H +#ifndef CASA_JSON_H +#define CASA_JSON_H +#include "cpprest/asyncrt_utils.h" +#include "cpprest/details/basic_types.h" +#include #include -#include #include -#include +#include #include -#include -#include "cpprest/details/basic_types.h" -#include "cpprest/asyncrt_utils.h" +#include namespace web { /// Library for parsing and serializing JSON values to and from C++ types. namespace json { - // Various forward declarations. - namespace details +// 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 + +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 { - class _Value; - class _Number; - class _Null; - class _Boolean; - class _String; - class _Object; - class _Array; - template class JSON_Parser; - } + /// 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; - namespace details + /// + /// 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 if the values are equal. + _ASYNCRTIMP bool operator==(const value& other) const; + + /// + /// Compares two JSON values for inequality. + /// + /// The JSON value to compare with. + /// True if 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; + + /// + /// Tests for the presence of a number field + /// + /// The name of the field + /// True if the field exists, false otherwise. + _ASYNCRTIMP bool has_number_field(const utility::string_t& key) const; + + /// + /// Tests for the presence of an integer field + /// + /// The name of the field + /// True if the field exists, false otherwise. + _ASYNCRTIMP bool has_integer_field(const utility::string_t& key) const; + + /// + /// Tests for the presence of a double field + /// + /// The name of the field + /// True if the field exists, false otherwise. + _ASYNCRTIMP bool has_double_field(const utility::string_t& key) const; + + /// + /// Tests for the presence of a boolean field + /// + /// The name of the field + /// True if the field exists, false otherwise. + _ASYNCRTIMP bool has_boolean_field(const utility::string_t& key) const; + + /// + /// Tests for the presence of a string field + /// + /// The name of the field + /// True if the field exists, false otherwise. + _ASYNCRTIMP bool has_string_field(const utility::string_t& key) const; + + /// + /// Tests for the presence of an array field + /// + /// The name of the field + /// True if the field exists, false otherwise. + _ASYNCRTIMP bool has_array_field(const utility::string_t& key) const; + + /// + /// Tests for the presence of an object field + /// + /// The name of the field + /// True if the field exists, false otherwise. + _ASYNCRTIMP bool has_object_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) { - extern bool g_keep_json_object_unsorted; + // 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; + /// - /// Preserve the order of the name/value pairs when parsing a JSON object. - /// The default is false, which can yield better performance. + /// Accesses an element of a JSON array. /// - /// 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); + /// 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 -#ifdef _DEBUG -#define ENABLE_JSON_VALUE_VISUALIZER -#endif + /// + /// 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 - - class number; - class array; - class object; - /// - /// A JSON value represented as a C++ class. + /// Serializes the content of the value into a string instance in UTF8 format /// - class value + /// 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 { - 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: + std::unique_ptr m_value; +#ifdef ENABLE_JSON_VALUE_VISUALIZER + value_type m_kind; #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; +/// +/// 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; -#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 +public: + json_exception(const char* const message) : _message(message) {} +#ifdef _UTF16_STRINGS + json_exception(const wchar_t* const message) : _message(utility::conversions::utf16_to_utf8(message)) {} +#endif // _UTF16_STRINGS + json_exception(std::string&& message) : _message(std::move(message)) {} - /// - /// 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); - } + // Must be narrow string because it derives from std::exception + const char* what() const CPPREST_NOEXCEPT { return _message.c_str(); } +}; - /// - /// 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; - - /// - /// Tests for the presence of a number field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_number_field(const utility::string_t &key) const; - - /// - /// Tests for the presence of an integer field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_integer_field(const utility::string_t &key) const; - - /// - /// Tests for the presence of a double field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_double_field(const utility::string_t &key) const; - - /// - /// Tests for the presence of a boolean field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_boolean_field(const utility::string_t &key) const; - - /// - /// Tests for the presence of a string field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_string_field(const utility::string_t &key) const; - - /// - /// Tests for the presence of an array field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_array_field(const utility::string_t &key) const; - - /// - /// Tests for the presence of an object field - /// - /// The name of the field - /// True if the field exists, false otherwise. - _ASYNCRTIMP bool has_object_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); +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"; } -#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)); + 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"; } -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; +const json_error_category_impl& json_error_category(); +} // namespace details -#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; +/// +/// A JSON array represented as a C++ class. +/// +class array +{ + typedef std::vector storage_type; -#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 - {} +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; - std::unique_ptr m_value; -#ifdef ENABLE_JSON_VALUE_VISUALIZER - value_type m_kind; -#endif - }; +private: + array() : m_elements() {} + array(size_type size) : m_elements(size) {} + array(storage_type elements) : m_elements(std::move(elements)) {} +public: /// - /// A single exception type to represent errors in parsing, converting, and accessing - /// elements of JSON values. + /// Gets the beginning iterator element of the array /// - 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)) { } + /// An iterator to the beginning of the JSON array. + iterator begin() { return m_elements.begin(); } - // Must be narrow string because it derives from std::exception - const char* what() const CPPREST_NOEXCEPT - { - return _message.c_str(); - } - }; + /// + /// 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(); } - 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"; - } + /// + /// Gets the end iterator element of the array + /// + /// An iterator to the end of the JSON array. + iterator end() { return m_elements.end(); } - 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"; - } - } - }; + /// + /// 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(); } - const json_error_category_impl& json_error_category(); - } + /// + /// 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(); } /// - /// A JSON array represented as a C++ class. + /// Gets the beginning const reverse iterator element of the array /// - 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(); - } + /// An const_reverse_iterator to the beginning of the JSON array. + const_reverse_iterator rbegin() const { return m_elements.rbegin(); } - /// - /// 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 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 iterator element of the array - /// - /// An iterator to the end of the JSON array. - iterator end() - { - return m_elements.end(); - } + /// + /// 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 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 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 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 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 - /// - /// An const_reverse_iterator to the beginning of the JSON array. - const_reverse_iterator rbegin() const - { - return m_elements.rbegin(); - } + /// + /// 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 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. + /// + /// A const_reverse_iterator to the end of the JSON array. + const_reverse_iterator crend() const { return m_elements.crend(); } - /// - /// 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(); - } + /// + /// 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); } - /// - /// Gets the beginning const iterator element of the array. - /// - /// A const_iterator to the beginning of the JSON array. - const_iterator cbegin() const + /// + /// 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()) { - return m_elements.cbegin(); + throw json_exception("index out of bounds"); } + m_elements.erase(m_elements.begin() + index); + } - /// - /// 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(); - } + /// + /// 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("index out of bounds"); - /// - /// 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(); - } + return m_elements[index]; + } - /// - /// 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(); - } + /// + /// 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("index out of bounds"); - /// - /// 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); - } + return m_elements[index]; + } - /// - /// 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. + /// + /// 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); - /// - /// 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]; + } - return m_elements[index]; - } + /// + /// Gets the number of elements of the array. + /// + /// The number of elements. + size_type size() const { return m_elements.size(); } - /// - /// 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")); +private: + storage_type m_elements; - return m_elements[index]; - } + friend class details::_Array; + template + friend class json::details::JSON_Parser; +}; - /// - /// 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); +/// +/// A JSON object represented as a C++ class. +/// +class object +{ + typedef std::vector> storage_type; - return m_elements[index]; - } +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; - /// - /// Gets the number of elements of the array. - /// - /// The number of elements. - size_type size() const +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) { - return m_elements.size(); + sort(m_elements.begin(), m_elements.end(), compare_pairs); } + } - private: - storage_type m_elements; +public: + /// + /// Gets the beginning iterator element of the object + /// + /// An iterator to the beginning of the JSON object. + iterator begin() { return m_elements.begin(); } - friend class details::_Array; - template friend class json::details::JSON_Parser; - }; + /// + /// 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(); } /// - /// A JSON object represented as a C++ class. + /// Gets the end iterator element of the object /// - 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); - } - } + /// An iterator to the end of the JSON object. + iterator end() { return m_elements.end(); } - 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 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 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 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 end iterator element of the object - /// - /// An iterator to the end of the JSON object. - iterator end() - { - return m_elements.end(); - } + /// + /// 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 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 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 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 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 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 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 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 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 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 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 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 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(); } - /// - /// 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(); - } + /// + /// 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); } - /// - /// 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 + /// + /// 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()) { - return m_elements.crbegin(); + throw web::json::json_exception("Key not found"); } - /// - /// 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(); - } + m_elements.erase(iter); + } - /// - /// 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) + /// + /// 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()) { - return m_elements.erase(position); + throw web::json::json_exception("Key not found"); } - /// - /// 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); - } + 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. - 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 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()) { - auto iter = find_by_key(key); - if (iter == m_elements.end()) - { - throw web::json::json_exception(_XPLATSTR("Key not found")); - } - - return iter->second; + throw web::json::json_exception("Key not found"); } - /// - /// 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; + } - 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); - /// - /// 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) + if (iter == m_elements.end() || key != iter->first) { - auto iter = find_insert_location(key); + return m_elements.insert(iter, std::pair(key, value()))->second; + } - if (iter == m_elements.end() || key != iter->first) - { - return m_elements.insert(iter, std::pair(key, value()))->second; - } + return iter->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 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(); } - /// - /// 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 if empty. + bool empty() const { return m_elements.empty(); } - /// - /// Checks if there are any elements in the JSON object. - /// - /// True iff empty. - bool empty() const - { - return m_elements.empty(); - } - private: +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; + } - 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) + storage_type::iterator find_insert_location(const utility::string_t& key) + { + if (m_keep_order) { - return p1.first < key; + return std::find_if(m_elements.begin(), + m_elements.end(), + [&key](const std::pair& p) { return p.first == key; }); } - - storage_type::iterator find_insert_location(const utility::string_t &key) + else { - 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); - } + 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 + storage_type::const_iterator find_by_key(const utility::string_t& key) const + { + if (m_keep_order) { - 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; - } + return std::find_if(m_elements.begin(), + m_elements.end(), + [&key](const std::pair& p) { return p.first == key; }); } - - storage_type::iterator find_by_key(const utility::string_t& key) + else { - auto iter = find_insert_location(key); + 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; + storage_type m_elements; + bool m_keep_order; + friend class details::_Object; - template friend class json::details::JSON_Parser; - }; + 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: /// - /// A JSON number represented as a C++ class. + /// Does the number fit into int32? /// - 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; - } - } + /// true if the number fits into int32, false otherwise + _ASYNCRTIMP bool is_int32() const; - /// - /// 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; - } - } + /// + /// Does the number fit into unsigned int32? + /// + /// true if the number fits into unsigned int32, false otherwise + _ASYNCRTIMP bool is_uint32() const; - /// - /// 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); - } + /// + /// Does the number fit into int64? + /// + /// true if the number fits into int64, false otherwise + _ASYNCRTIMP bool is_int64() const; - /// - /// Converts the JSON number to unsigned int32. - /// - /// An usigned int32 representation of the number - uint32_t to_uint32() 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) { - if (m_type == double_type) - return static_cast(m_value); - else - return static_cast(m_intval); + case signed_type: return m_intval >= 0; + case unsigned_type: return true; + case double_type: + default: return false; } + } - /// - /// Converts the JSON number to int64. - /// - /// An int64 representation of the number - int64_t to_int64() const + /// + /// Converts the JSON number to a C++ double. + /// + /// A double representation of the number + double to_double() const + { + switch (m_type) { - if (m_type == double_type) - return static_cast(m_value); - else - return static_cast(m_intval); + 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 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); - } + /// + /// 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); + } - /// - /// 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; - } + /// + /// Converts the JSON number to unsigned int32. + /// + /// An unsigned 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); + } - /// - /// 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; + /// + /// 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); + } - 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); - // Absence of this return statement provokes a warning from Intel - // compiler, but its presence results in a warning from MSVC, so - // we have to resort to conditional compilation to keep both happy. -#ifdef __INTEL_COMPILER - return false; -#endif - } + /// + /// 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); + } - private: - union - { - int64_t m_intval; - uint64_t m_uintval; - double m_value; - }; + /// + /// 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; } - enum type + /// + /// Compares two JSON numbers for equality. + /// + /// The JSON number to compare with. + /// True if the numbers are equal. + bool operator==(const number& other) const + { + if (m_type != other.m_type) return false; + + switch (m_type) { - signed_type=0, unsigned_type, double_type - } 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); + // Absence of this return statement provokes a warning from Intel + // compiler, but its presence results in a warning from MSVC, so + // we have to resort to conditional compilation to keep both happy. +#ifdef __INTEL_COMPILER + return false; +#endif + } - friend class details::_Number; +private: + union { + int64_t m_intval; + uint64_t m_uintval; + double m_value; }; - namespace details + enum type { - class _Value - { - public: - virtual std::unique_ptr<_Value> _copy_value() = 0; + signed_type = 0, + unsigned_type, + double_type + } m_type; - 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")); } + friend class details::_Number; +}; + +namespace details +{ +class _Value +{ +public: + virtual std::unique_ptr<_Value> _copy_value() = 0; - 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 bool has_field(const utility::string_t&) const { return false; } + virtual value get_field(const utility::string_t&) const { throw json_exception("not an object"); } + virtual value get_element(array::size_type) const { throw json_exception("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")); } + virtual value& index(const utility::string_t&) { throw json_exception("not an object"); } + virtual value& index(array::size_type) { throw json_exception("not an array"); } - // Common function used for serialization to strings and streams. - virtual void serialize_impl(std::string& str) const - { - format(str); - } + virtual const value& cnst_index(const utility::string_t&) const { throw json_exception("not an object"); } + virtual const value& cnst_index(array::size_type) const { throw json_exception("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); - } + 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 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 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 bool is_integer() const { throw json_exception("not a number"); } + virtual bool is_double() const { throw json_exception("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 const json::number& as_number() { throw json_exception("not a number"); } + virtual double as_double() const { throw json_exception("not a number"); } + virtual int as_integer() const { throw json_exception("not a number"); } + virtual bool as_bool() const { throw json_exception("not a boolean"); } + virtual json::array& as_array() { throw json_exception("not an array"); } + virtual const json::array& as_array() const { throw json_exception("not an array"); } + virtual json::object& as_object() { throw json_exception("not an object"); } + virtual const json::object& as_object() const { throw json_exception("not an object"); } + virtual const utility::string_t& as_string() const { throw json_exception("not a string"); } - virtual size_t size() const { return 0; } + virtual size_t size() const { return 0; } - virtual ~_Value() {} + virtual ~_Value() {} - protected: - _Value() {} +protected: + _Value() {} - virtual void format(std::basic_string& stream) const - { - stream.append("null"); - } + 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"); - } + virtual void format(std::basic_string& stream) const { stream.append(L"null"); } #endif - private: +private: + friend class web::json::value; +}; - 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 _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) {} - 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 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 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 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 double as_double() const { return m_number.to_double(); } - virtual int as_integer() const - { - return m_number.to_int32(); - } + virtual int as_integer() const { return m_number.to_int32(); } - virtual const number& as_number() { return m_number; } + virtual const number& as_number() { return m_number; } - protected: - virtual void format(std::basic_string& stream) const ; +protected: + virtual void format(std::basic_string& stream) const; #ifdef _WIN32 - virtual void format(std::basic_string& stream) const; + virtual void format(std::basic_string& stream) const; #endif - private: - template friend class json::details::JSON_Parser; +private: + template + friend class json::details::JSON_Parser; - json::number m_number; - }; + json::number m_number; +}; - class _Boolean : public _Value - { - public: - _Boolean(bool value) : m_value(value) { } +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 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 json::value::value_type type() const { return json::value::Boolean; } - virtual bool as_bool() const { return m_value; } + virtual bool as_bool() const { return m_value; } - protected: - virtual void format(std::basic_string& stream) const - { - stream.append(m_value ? "true" : "false"); - } +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"); - } + 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: +private: + template + friend class json::details::JSON_Parser; + bool m_value; +}; - _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) - { } +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) - { } + _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 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 json::value::value_type type() const { return json::value::String; } - virtual const utility::string_t & as_string() const; + virtual const utility::string_t& as_string() const; - virtual void serialize_impl(std::string& str) const - { - serialize_impl_char_type(str); - } + 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); - } + virtual void serialize_impl(std::wstring& str) const { serialize_impl_char_type(str); } #endif - protected: - virtual void format(std::basic_string& str) const; +protected: + virtual void format(std::basic_string& str) const; #ifdef _WIN32 - virtual void format(std::basic_string& str) const; + virtual void format(std::basic_string& str) const; #endif - private: - friend class _Object; - friend class _Array; +private: + friend class _Object; + friend class _Array; - size_t get_reserve_size() const - { - return m_string.size() + 2; - } + 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); - } + 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; + std::string as_utf8_string() const; + utf16string as_utf16_string() const; - utility::string_t m_string; + 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); - }; + // There are significant performance gains that can be made by knowing 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); +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); +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); +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) { } +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 std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Object>(*this); } - virtual json::object& as_object() { return m_object; } + virtual json::object& as_object() { return m_object; } - virtual const json::object& as_object() const { 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 json::value::value_type type() const { return json::value::Object; } - virtual bool has_field(const utility::string_t &) const; + virtual bool has_field(const utility::string_t&) const; - virtual json::value &index(const utility::string_t &key); + 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; + 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)); - } + 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); - } + 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); - } + 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(); } + size_t size() const { return m_object.size(); } - protected: - virtual void format(std::basic_string& str) const - { - format_impl(str); - } +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); - } + virtual void format(std::basic_string& str) const { format_impl(str); } #endif - private: - json::object m_object; +private: + json::object m_object; - template friend class json::details::JSON_Parser; + template + friend class json::details::JSON_Parser; - template - void format_impl(std::basic_string& str) const + 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) { - 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('}'); + 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 + 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; // Multiply by each object/array element + if (valueSize == 0) { - // 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) + if (iter->second.type() == json::value::String) + { + valueSize = static_cast<_String*>(iter->second.m_value.get())->get_reserve_size(); + } + else { - 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; + valueSize = 5; // true, false, or null } - return reserveSize; } - }; + 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)) { } +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 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::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::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]; - } + 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; + 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(); + 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; - } + for (; iterT != iterTe && iterO != iterOe; ++iterT, ++iterO) + { + if (*iterT != *iterO) return false; + } - return true; - } + 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); - } + 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); - } + 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(); } + size_t size() const { return m_array.size(); } - protected: - virtual void format(std::basic_string& str) const - { - format_impl(str); - } +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); - } + virtual void format(std::basic_string& str) const { format_impl(str); } #endif - private: - json::array m_array; - - template friend class json::details::JSON_Parser; +private: + json::array m_array; - 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(']'); - } + template + friend class json::details::JSON_Parser; - size_t get_reserve_size() const + 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) { - // 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; + iter->format(str); + str.push_back(','); } - }; - } // 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(); + lastElement->format(str); + } + str.push_back(']'); } - /// - /// 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 + size_t get_reserve_size() const { - return m_value->has_field(key); - } + // 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 - /// - /// 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); - } + if (valueSize == 0) valueSize = 5; // true, false, or null - /// - /// 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); + reserveSize += valueSize; + } + return reserveSize; } - - /// - /// 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); -}} +}; +} // 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); +} // namespace json +} // namespace web #endif diff --git a/Release/include/cpprest/oauth1.h b/Release/include/cpprest/oauth1.h index 24a10c7d79..dbc45bdd71 100644 --- a/Release/include/cpprest/oauth1.h +++ b/Release/include/cpprest/oauth1.h @@ -1,22 +1,22 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* HTTP Library: Oauth 1.0 -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * HTTP Library: Oauth 1.0 + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#ifndef _CASA_OAUTH1_H -#define _CASA_OAUTH1_H +#ifndef CASA_OAUTH1_H +#define CASA_OAUTH1_H -#include "cpprest/http_msg.h" #include "cpprest/details/web_utilities.h" +#include "cpprest/http_msg.h" namespace web { @@ -24,16 +24,15 @@ namespace http { namespace client { - // Forward declaration to avoid circular include dependency. - class http_client_config; -} +// Forward declaration to avoid circular include dependency. +class http_client_config; +} // namespace client /// oAuth 1.0 library. namespace oauth1 { namespace details { - class oauth1_handler; // State currently used by oauth1_config to authenticate request. @@ -43,14 +42,16 @@ class oauth1_handler; 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)) - {} + 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); } @@ -83,12 +84,11 @@ class oauth1_strings #undef DAT }; -} // namespace web::http::oauth1::details +} // namespace details /// oAuth functionality is currently in beta. namespace experimental { - /// /// Constant strings for OAuth 1.0 signature methods. /// @@ -97,7 +97,7 @@ class oauth1_methods { public: #define _OAUTH1_METHODS -#define DAT(a,b) _ASYNCRTIMP static const oauth1_method a; +#define DAT(a, b) _ASYNCRTIMP static const oauth1_method a; #include "cpprest/details/http_constants.dat" #undef _OAUTH1_METHODS #undef DAT @@ -123,7 +123,6 @@ class oauth1_exception : public std::exception class oauth1_token { public: - /// /// Constructs an initially empty invalid access token. /// @@ -134,10 +133,10 @@ class oauth1_token /// /// 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)) - {} + 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. @@ -156,13 +155,13 @@ class oauth1_token /// Set access token. /// /// Access token string to set. - void set_access_token(utility::string_t &&access_token) { m_token = std::move(access_token); } + 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; } + void set_access_token(const utility::string_t& access_token) { m_token = access_token; } /// /// Get token secret. @@ -174,26 +173,29 @@ class oauth1_token /// Set token secret. /// /// Token secret string to set. - void set_secret(utility::string_t &&secret) { m_secret = std::move(secret); } + 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; } + 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; } + 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) + void set_additional_parameter(utility::string_t&& paramName, utility::string_t&& paramValue) { m_additional_parameters[std::move(paramName)] = std::move(paramValue); } @@ -203,7 +205,7 @@ class oauth1_token /// /// Parameter name. /// Parameter value. - void set_additional_parameter(const utility::string_t ¶mName, const utility::string_t ¶mValue) + void set_additional_parameter(const utility::string_t& paramName, const utility::string_t& paramValue) { m_additional_parameters[paramName] = paramValue; } @@ -227,20 +229,25 @@ class oauth1_token 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) - {} + 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. @@ -261,8 +268,8 @@ class oauth1_config /// 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. + /// 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); /// @@ -283,7 +290,7 @@ class oauth1_config /// 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) + pplx::task refresh_token(const utility::string_t& key) { return _request_token(_generate_auth_state(key, m_token.additional_parameters().at(key)), false); } @@ -400,7 +407,10 @@ class oauth1_config /// 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()); } + 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 @@ -418,14 +428,12 @@ class oauth1_config // 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(); - } + 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)); + return details::oauth1_state( + _generate_timestamp(), _generate_nonce(), std::move(extra_key), std::move(extra_value)); } details::oauth1_state _generate_auth_state() @@ -444,20 +452,26 @@ class oauth1_config /// /// 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; } + 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); } + 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) + void set_parameters(const std::map& parameters) { m_parameters_to_sign.clear(); m_parameters_to_sign = parameters; @@ -472,39 +486,29 @@ class oauth1_config /// Get the web proxy object /// /// A reference to the web proxy object. - const web_proxy& proxy() const - { - return m_proxy; - } + const web_proxy& proxy() const { return m_proxy; } /// /// Set the web proxy object that will be used by token_from_code and token_from_refresh /// /// A reference to the web proxy object. - void set_proxy(const web_proxy& proxy) - { - m_proxy = proxy; - } + void set_proxy(const web_proxy& proxy) { m_proxy = proxy; } private: friend class web::http::client::http_client_config; friend class web::http::oauth1::details::oauth1_handler; - oauth1_config() : - m_is_authorization_completed(false) - {} + oauth1_config() : m_is_authorization_completed(false) {} - utility::string_t _generate_nonce() - { - return m_nonce_generator.generate(); - } + utility::string_t _generate_nonce() { return m_nonce_generator.generate(); } static utility::string_t _generate_timestamp() { return utility::conversions::details::to_string_t(utility::datetime::utc_timestamp()); } - _ASYNCRTIMP static std::vector __cdecl _hmac_sha1(const utility::string_t& key, const utility::string_t& data); + _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); @@ -517,12 +521,9 @@ class oauth1_config 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()); - } + void _authenticate_request(http_request& req) { _authenticate_request(req, _generate_auth_state()); } - _ASYNCRTIMP void _authenticate_request(http_request &req, details::oauth1_state 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); @@ -537,7 +538,7 @@ class oauth1_config utility::string_t m_realm; oauth1_method m_method; - std::map m_parameters_to_sign; + std::map m_parameters_to_sign; web::web_proxy m_proxy; @@ -545,17 +546,14 @@ class oauth1_config bool m_is_authorization_completed; }; -} // namespace web::http::oauth1::experimental +} // namespace experimental namespace details { - class oauth1_handler : public http_pipeline_stage { public: - oauth1_handler(std::shared_ptr cfg) : - m_config(std::move(cfg)) - {} + oauth1_handler(std::shared_ptr cfg) : m_config(std::move(cfg)) {} virtual pplx::task propagate(http_request request) override { @@ -570,6 +568,9 @@ class oauth1_handler : public http_pipeline_stage std::shared_ptr m_config; }; -}}}} +} // namespace details +} // namespace oauth1 +} // namespace http +} // namespace web #endif diff --git a/Release/include/cpprest/oauth2.h b/Release/include/cpprest/oauth2.h index 7afccdc3e1..693ebbe34c 100644 --- a/Release/include/cpprest/oauth2.h +++ b/Release/include/cpprest/oauth2.h @@ -1,22 +1,22 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* HTTP Library: Oauth 2.0 -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * HTTP Library: Oauth 2.0 + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#ifndef _CASA_OAUTH2_H -#define _CASA_OAUTH2_H +#ifndef CASA_OAUTH2_H +#define CASA_OAUTH2_H -#include "cpprest/http_msg.h" #include "cpprest/details/web_utilities.h" +#include "cpprest/http_msg.h" namespace web { @@ -24,16 +24,15 @@ namespace http { namespace client { - // Forward declaration to avoid circular include dependency. - class http_client_config; -} +// Forward declaration to avoid circular include dependency. +class http_client_config; +} // namespace client /// oAuth 2.0 library. namespace oauth2 { namespace details { - class oauth2_handler; // Constant strings for OAuth 2.0. @@ -48,12 +47,11 @@ class oauth2_strings #undef DAT }; -} // namespace web::http::oauth2::details +} // namespace details /// oAuth functionality is currently in beta. namespace experimental { - /// /// Exception type for OAuth 2.0 errors. /// @@ -74,16 +72,18 @@ class oauth2_exception : public std::exception class oauth2_token { public: - /// /// Value for undefined expiration time in expires_in(). /// - enum { undefined_expiration = -1 }; + 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) - {} + 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. @@ -201,23 +201,26 @@ class oauth2_token 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(), - utility::string_t user_agent=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_user_agent(std::move(user_agent)), - m_implicit_grant(false), - m_bearer_auth(true), - m_http_basic_auth(true), - m_access_token_key(details::oauth2_strings::access_token) - {} + 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(), + utility::string_t user_agent = 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_user_agent(std::move(user_agent)) + , 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. @@ -242,8 +245,8 @@ class oauth2_config /// 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. + /// 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); /// @@ -276,7 +279,8 @@ class oauth2_config { 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); + ub.append_query( + details::oauth2_strings::refresh_token, uri::encode_data_string(token().refresh_token()), false); return _request_token(ub); } @@ -430,37 +434,31 @@ class oauth2_config /// Get access token key. /// /// Access token key string. - const utility::string_t& access_token_key() const { return m_access_token_key; } + 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); } - + /// /// Get the web proxy object /// /// A reference to the web proxy object. - const web_proxy& proxy() const - { - return m_proxy; - } + const web_proxy& proxy() const { return m_proxy; } /// /// Set the web proxy object that will be used by token_from_code and token_from_refresh /// /// A reference to the web proxy object. - void set_proxy(const web_proxy& proxy) - { - m_proxy = proxy; - } - + void set_proxy(const web_proxy& proxy) { m_proxy = proxy; } + /// /// Get user agent to be used in oauth2 flows. /// /// User agent string. - const utility::string_t& user_agent() const { return m_user_agent; } + const utility::string_t& user_agent() const { return m_user_agent; } /// /// Set user agent to be used in oauth2 flows. /// If none is provided a default user agent is provided. @@ -471,17 +469,13 @@ class oauth2_config 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) - {} + 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 + void _authenticate_request(http_request& req) const { if (bearer_auth()) { @@ -516,17 +510,14 @@ class oauth2_config utility::nonce_generator m_state_generator; }; -} // namespace web::http::oauth2::experimental +} // namespace experimental namespace details { - class oauth2_handler : public http_pipeline_stage { public: - oauth2_handler(std::shared_ptr cfg) : - m_config(std::move(cfg)) - {} + oauth2_handler(std::shared_ptr cfg) : m_config(std::move(cfg)) {} virtual pplx::task propagate(http_request request) override { @@ -541,6 +532,9 @@ class oauth2_handler : public http_pipeline_stage std::shared_ptr m_config; }; -}}}} +} // namespace details +} // namespace oauth2 +} // namespace http +} // namespace web #endif diff --git a/Release/include/cpprest/producerconsumerstream.h b/Release/include/cpprest/producerconsumerstream.h index 4e10872f45..bbdb2c1c85 100644 --- a/Release/include/cpprest/producerconsumerstream.h +++ b/Release/include/cpprest/producerconsumerstream.h @@ -1,697 +1,655 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* 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: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * 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: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#ifndef _CASA_PRODUCER_CONSUMER_STREAMS_H -#define _CASA_PRODUCER_CONSUMER_STREAMS_H +#ifndef CASA_PRODUCER_CONSUMER_STREAMS_H +#define CASA_PRODUCER_CONSUMER_STREAMS_H -#include -#include +#include "cpprest/astreambuf.h" +#include "pplx/pplxtasks.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; -namespace Concurrency { namespace streams { + /// + /// 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) + { + } - namespace details { + /// + /// 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(); - /// - /// 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) - { - } + _ASSERTE(m_requests.empty()); + m_blocks.clear(); + } - /// - /// 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; } - /// - /// 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; - } + /// + /// has_size is used to determine whether a stream buffer supports size(). + /// + virtual bool has_size() const { return false; } - /// - /// 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; - } + /// + /// 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; } - /// - /// 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(); - } + /// + /// 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; } - // 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(); } + /// + /// 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; } - /// - /// 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(); - } + /// + /// 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(); + } - /// - /// 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); + /// + /// 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; + } - // 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. + // 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((bool)m_allocBlock); - m_allocBlock->update_write_head(count); - m_blocks.push_back(m_allocBlock); - m_allocBlock = nullptr; + _ASSERTE(!m_allocBlock); + m_allocBlock = std::make_shared<_block>(count); + return m_allocBlock->wbegin(); + } - update_write_head(count); - } + /// + /// 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); - /// - /// 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; + // 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. - if (!this->can_read()) return false; + _ASSERTE((bool)m_allocBlock); + m_allocBlock->update_write_head(count); + m_blocks.push_back(m_allocBlock); + m_allocBlock = nullptr; - pplx::extensibility::scoped_critical_section_t l(m_lock); + update_write_head(count); + } - 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(); + /// + /// 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; - count = block->rd_chars_left(); - ptr = block->rbegin(); + if (!this->can_read()) return false; - _ASSERTE(ptr != nullptr); - return true; - } - } + pplx::extensibility::scoped_critical_section_t l(m_lock); - /// - /// 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; + 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(); - pplx::extensibility::scoped_critical_section_t l(m_lock); - auto block = m_blocks.front(); + count = block->rd_chars_left(); + ptr = block->rbegin(); - _ASSERTE(block->rd_chars_left() >= count); - block->m_read += count; + _ASSERTE(ptr != nullptr); + return true; + } + } - update_read_head(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_opt_(count) _CharType* ptr, _In_ size_t count) + { + if (ptr == nullptr) return; - protected: + pplx::extensibility::scoped_critical_section_t l(m_lock); + auto block = m_blocks.front(); - virtual pplx::task _sync() - { - pplx::extensibility::scoped_critical_section_t l(m_lock); + _ASSERTE(block->rd_chars_left() >= count); + block->m_read += count; - m_synced = in_avail(); + update_read_head(count); + } - fulfill_outstanding(); +protected: + virtual pplx::task _sync() + { + pplx::extensibility::scoped_critical_section_t l(m_lock); - return pplx::task_from_result(true); - } + m_synced = in_avail(); - 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)); - } + fulfill_outstanding(); + return pplx::task_from_result(true); + } - 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 pplx::task _putc(_CharType ch) + { + return pplx::task_from_result((this->write(&ch, 1) == 1) ? static_cast(ch) : traits::eof()); + } - 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 pplx::task _putn(const _CharType* ptr, size_t count) + { + return pplx::task_from_result(this->write(ptr, count)); + } - 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 _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 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 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 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 _bumpc() + { + pplx::task_completion_event tce; + enqueue_request(_request(1, [this, tce]() { tce.set(this->read_byte(true)); })); + return pplx::create_task(tce); + } - 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); - } + virtual int_type _sbumpc() + { + pplx::extensibility::scoped_critical_section_t l(m_lock); + return can_satisfy(1) ? this->read_byte(true) : traits::requires_async(); + } - 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 _getc() + { + pplx::task_completion_event tce; + enqueue_request(_request(1, [this, tce]() { tce.set(this->read_byte(false)); })); + return pplx::create_task(tce); + } - 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); - } + 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 _ungetc() - { - return pplx::task_from_result(traits::eof()); - } + 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); + } - private: + virtual pplx::task _ungetc() { return pplx::task_from_result(traits::eof()); } - /// - /// 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; +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); + { + pplx::extensibility::scoped_critical_section_t l(this->m_lock); - // This runs on the thread that called close. - this->fulfill_outstanding(); - } + // This runs on the thread that called close. + this->fulfill_outstanding(); + } - return pplx::task_from_result(); - } + 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(); - } + /// + /// 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; + /// + /// 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; + // 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); + 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)); - } + // 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); + // 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; - } + 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(); + /// + /// 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; + // 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(); + // We have enough data to satisfy this request + req.complete(); - // Remove it from the request queue - m_requests.pop(); - } - } + // 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 + /// + /// 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]) {} - if (advance) - { - m_read += countRead; - } + ~_block() { delete[] m_data; } - return countRead; - } + // Read head + size_t m_read; - // 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)); + // Write head + size_t m_pos; - const _CharType * srcEnd = src + countWritten; + // Allocation size (of m_data) + size_t m_size; -#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 + // The data store + _CharType* m_data; - update_write_head(countWritten); - return countWritten; - } + // Pointer to the read head + _CharType* rbegin() { return m_data + m_read; } - void update_write_head(size_t count) - { - m_pos += count; - } + // Pointer to the write head + _CharType* wbegin() { return m_data + m_pos; } - size_t rd_chars_left() const { return m_pos-m_read; } - size_t wr_chars_left() const { return m_size-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)); - private: + _CharType* beg = rbegin(); + _CharType* end = rbegin() + countRead; - // Copy is not supported - _block(const _block&); - _block& operator=(const _block&); - }; +#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0 + // 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 - /// - /// Represents a request on the stream buffer - typically reads - /// - class _request + if (advance) { - public: + m_read += countRead; + } - typedef std::function func_type; - _request(size_t count, const func_type& func) - : m_func(func), m_count(count) - { - } + return countRead; + } - void complete() - { - m_func(); - } + // 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)); - size_t size() const - { - return m_count; - } + const _CharType* srcEnd = src + countWritten; - private: +#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0 + // 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 - func_type m_func; - size_t m_count; - }; + update_write_head(countWritten); + return countWritten; + } - 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); - } - } + void update_write_head(size_t count) { m_pos += count; } - /// - /// Determine if the request can be satisfied. - /// - bool can_satisfy(size_t count) - { - return (m_synced > 0) || (this->in_avail() >= count) || !this->can_write(); - } + size_t rd_chars_left() const { return m_pos - m_read; } + size_t wr_chars_left() const { return m_size - m_pos; } - /// - /// 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(); - } + private: + // Copy is not supported + _block(const _block&); + _block& operator=(const _block&); + }; - /// - /// 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)); + /// + /// 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) {} - size_t read = 0; + void complete() { m_func(); } - 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); + size_t size() const { return m_count; } - read += read_from_block; + private: + func_type m_func; + size_t m_count; + }; - _ASSERTE(count >= read); - if (read == count) break; - } + void enqueue_request(_request req) + { + pplx::extensibility::scoped_critical_section_t l(m_lock); - if (advance) - { - update_read_head(read); - } + 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); + } + } - return read; - } + /// + /// Determine if the request can be satisfied. + /// + bool can_satisfy(size_t count) { return (m_synced > 0) || (this->in_avail() >= count) || !this->can_write(); } - /// - /// 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(); - } - } + /// + /// 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(); + } - // The in/out mode for the buffer - std::ios_base::openmode m_mode; + /// + /// 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)); - // Default block size - msl::safeint3::SafeInt m_alloc_size; + size_t read = 0; - // Block used for alloc/commit - std::shared_ptr<_block> m_allocBlock; + 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); - // Total available data - size_t m_total; + read += read_from_block; - size_t m_total_read; - size_t m_total_written; + _ASSERTE(count >= read); + if (read == count) break; + } - // 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; + if (advance) + { + update_read_head(read); + } - // 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; + return read; + } - // Memory blocks - std::deque> m_blocks; + /// + /// 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; - // Queue of requests - std::queue<_request> m_requests; - }; + if (m_synced > 0) m_synced = (m_synced > count) ? (m_synced - count) : 0; - } // namespace details + // 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; /// - /// 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. + /// Create a producer_consumer_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> + /// The internal default block size. + producer_consumer_buffer(size_t alloc_size = 512) + : streambuf<_CharType>(std::make_shared>(alloc_size)) { - 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 +} // namespace streams +} // namespace Concurrency -#endif \ No newline at end of file +#endif diff --git a/Release/include/cpprest/rawptrstream.h b/Release/include/cpprest/rawptrstream.h index 3fdbb99cb3..1f15ecbe77 100644 --- a/Release/include/cpprest/rawptrstream.h +++ b/Release/include/cpprest/rawptrstream.h @@ -1,630 +1,598 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* 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: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * 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: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#ifndef _CASA_RAWPTR_STREAMS_H -#define _CASA_RAWPTR_STREAMS_H +#ifndef CASA_RAWPTR_STREAMS_H +#define CASA_RAWPTR_STREAMS_H -#include -#include +#include "cpprest/astreambuf.h" +#include "cpprest/streams.h" +#include "pplx/pplxtasks.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; -namespace Concurrency { namespace streams { + /// + /// 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) + { + } - // Forward declarations - template class rawptr_buffer; + /// + /// Destructor + /// + virtual ~basic_rawptr_buffer() + { + this->_close_read(); + this->_close_write(); + } - namespace details { +protected: + /// + /// can_seek is used to determine whether a stream buffer supports seeking. + /// + virtual bool can_seek() const { return this->is_open(); } /// - /// 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. + /// has_size is used to determine whether a stream buffer supports size(). /// - 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) - { - } + virtual bool has_size() const { return this->is_open(); } - /// - /// Destructor - /// - virtual ~basic_rawptr_buffer() - { - this->_close_read(); - this->_close_write(); - } + /// + /// 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); } - protected: + /// + /// 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; } - /// - /// can_seek is used to determine whether a stream buffer supports seeking. - /// - virtual bool can_seek() const { return this->is_open(); } + /// + /// 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; } - /// - /// has_size is used to determine whether a stream buffer supports size(). - /// - virtual bool has_size() const { return this->is_open(); } + /// + /// 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); - /// - /// 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); - } + msl::safeint3::SafeInt readhead(m_current_position); + msl::safeint3::SafeInt writeend(m_size); + return (size_t)(writeend - readhead); + } - /// - /// 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 + /// + /// 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) { - return 0; + this->_close_read().get(); // Safe to call get() here. } - /// - /// 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) + if (mode & std::ios_base::out) { - return; + this->_close_write().get(); // Safe to call get() here. } - /// - /// 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 + if (!this->can_read() && !this->can_write()) { - // 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); + m_data = nullptr; } - /// - /// 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. - } + // Exceptions will be propagated out of _close_read or _close_write + return pplx::task_from_result(); + } - if (mode & std::ios_base::out) - { - this->_close_write().get(); // Safe to call get() here. - } + virtual pplx::task _sync() { return pplx::task_from_result(true); } - if ( !this->can_read() && !this->can_write() ) - { - m_data = nullptr; - } + 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); + } - // Exceptions will be propagated out of _close_read or _close_write - return pplx::task_from_result(); - } + 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)); + } - virtual pplx::task _sync() - { - return pplx::task_from_result(true); - } + /// + /// 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; - 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); - } + msl::safeint3::SafeInt readhead(m_current_position); + msl::safeint3::SafeInt writeend(m_size); + size_t space_left = (size_t)(writeend - readhead); - 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)); - } + if (space_left < count) return nullptr; - /// - /// 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; + // Let the caller copy the data + return (_CharType*)(m_data + m_current_position); + } - msl::safeint3::SafeInt readhead(m_current_position); - msl::safeint3::SafeInt writeend(m_size); - size_t space_left = (size_t)(writeend - readhead); + /// + /// 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); + } - if (space_left < count) return nullptr; + /// + /// 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; - // Let the caller copy the data - return (_CharType*)(m_data+m_current_position); - } + if (!this->can_read()) return false; - /// - /// Submits a block already allocated by the stream buffer. - /// - /// The number of characters to be committed. - void _commit(size_t actual) + count = in_avail(); + + if (count > 0) { - // Update the write position and satisfy any pending reads - update_current_position(m_current_position+actual); + ptr = (_CharType*)(m_data + m_current_position); + return true; } - - /// - /// 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) + else { - count = 0; ptr = nullptr; - if (!this->can_read()) return false; - - count = in_avail(); + // 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; + } + } - if (count > 0) - { - ptr = (_CharType*)(m_data+m_current_position); - return true; - } - else - { - ptr = nullptr; + /// + /// 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); + } - // 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; - } - } + virtual pplx::task _getn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) + { + return pplx::task_from_result(this->read(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_opt_ (count) _CharType *ptr, _In_ size_t count) - { - if (ptr != nullptr) - update_current_position(m_current_position + count); - } + size_t _sgetn(_Out_writes_(count) _CharType* ptr, _In_ size_t count) { return this->read(ptr, count); } - virtual pplx::task _getn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) - { - return pplx::task_from_result(this->read(ptr, count)); - } + virtual size_t _scopy(_Out_writes_(count) _CharType* ptr, _In_ size_t count) + { + return this->read(ptr, count, false); + } - size_t _sgetn(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) - { - return this->read(ptr, count); - } + virtual pplx::task _bumpc() { return pplx::task_from_result(this->read_byte(true)); } - virtual size_t _scopy(_Out_writes_ (count) _CharType *ptr, _In_ size_t count) - { - return this->read(ptr, count, false); - } + virtual int_type _sbumpc() { return this->read_byte(true); } - virtual pplx::task _bumpc() - { - return pplx::task_from_result(this->read_byte(true)); - } + virtual pplx::task _getc() { return pplx::task_from_result(this->read_byte(false)); } - virtual int_type _sbumpc() - { - return this->read_byte(true); - } + int_type _sgetc() { return this->read_byte(false); } - virtual pplx::task _getc() - { - return pplx::task_from_result(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()); - int_type _sgetc() - { - return this->read_byte(false); - } + this->read_byte(true); + return pplx::task_from_result(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()); + 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(); + } - this->read_byte(true); - return pplx::task_from_result(this->read_byte(false)); - } + /// + /// 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()); - 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(); - } + 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(); + } - /// - /// 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); - /// - /// 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) + if (position >= beg) { - pos_type beg(0); - pos_type end(m_size); + auto pos = static_cast(position); - if (position >= beg) + // Read head + if ((mode & std::ios_base::in) && this->can_read()) { - auto pos = static_cast(position); - - // Read head - if ((mode & std::ios_base::in) && this->can_read()) + if (position <= end) { - 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 + // 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); } } - 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 ) + // Write head + if ((mode & std::ios_base::out) && this->can_write()) { - case std::ios_base::beg: - return seekpos(beg + offset, mode); + // Update write head and satisfy read requests if any + update_current_position(pos); - 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()); + return static_cast(m_current_position); } } - 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); - } + return static_cast(traits::eof()); + } - /// - /// 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); - } + /// + /// 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); - static void validate_mode(std::ios_base::openmode mode) + switch (way) { - // 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"); - } + case std::ios_base::beg: return seekpos(beg + offset, mode); - /// - /// 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); - } + case std::ios_base::cur: return seekpos(cur + offset, mode); - /// - /// 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(); + case std::ios_base::end: return seekpos(end + offset, mode); + + default: return static_cast(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; +private: + template + friend class ::concurrency::streams::rawptr_buffer; - msl::safeint3::SafeInt request_size(count); - msl::safeint3::SafeInt read_size = request_size.Min(in_avail()); + /// + /// 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); + } - size_t newPos = m_current_position + read_size; + /// + /// 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); + } - auto readBegin = m_data + m_current_position; - auto readEnd = m_data + newPos; + 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"); + } -#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 + /// + /// 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); + } - if (advance) - { - update_current_position(newPos); - } + /// + /// 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(); + } - return (size_t) read_size; - } + /// + /// 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; - /// - /// 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 request_size(count); + msl::safeint3::SafeInt read_size = request_size.Min(in_avail()); - msl::safeint3::SafeInt newSize = msl::safeint3::SafeInt(count) +m_current_position; + size_t newPos = m_current_position + read_size; - if ( newSize > m_size ) - throw std::runtime_error("Writing past the end of the buffer"); + auto readBegin = m_data + m_current_position; + auto readEnd = m_data + newPos; - // 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)); +#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0 + // Avoid warning C4996: Use checked iterators under SECURE_SCL + std::copy(readBegin, readEnd, stdext::checked_array_iterator<_CharType*>(ptr, count)); #else - std::copy(ptr, ptr + count, m_data+m_current_position); + std::copy(readBegin, readEnd, ptr); #endif // _WIN32 - // Update write head and satisfy pending reads if any - update_current_position(newSize); - - return count; + if (advance) + { + update_current_position(newPos); } - /// - /// Updates the current read or write position - /// - void update_current_position(size_t newPos) - { - // The new write head - m_current_position = newPos; + return (size_t)read_size; + } - _ASSERTE(m_current_position <= m_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; - // The actual memory block - _CharType* m_data; + if (newSize > m_size) throw std::runtime_error("Writing past the end of the buffer"); - // The size of the memory block - size_t m_size; + // Copy the data +#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL != 0 + // 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 - // Read/write head - size_t m_current_position; - }; + // Update write head and satisfy pending reads if any + update_current_position(newSize); - } // namespace details + return count; + } /// - /// 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. + /// Updates the current read or write position /// - /// - /// The data type of the basic element of the rawptr_buffer. - /// - template - class rawptr_buffer : public streambuf<_CharType> + void update_current_position(size_t newPos) { - 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))) - { - } + // 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(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))) - { - } + /// + /// 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))) + { + } - /// - /// Default constructor. - /// - rawptr_buffer() - { - } - }; + /// + /// 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; /// - /// 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. + /// Create a rawptr-stream given a pointer to a read-only memory block and the size of the block. /// - /// - /// The data type of the basic element of the rawptr_stream. - /// - template - class rawptr_stream + /// 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) { - 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)); - } + 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 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)); - } - }; + /// + /// 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 +} // namespace streams +} // namespace Concurrency #endif diff --git a/Release/include/cpprest/streams.h b/Release/include/cpprest/streams.h index 75deb8fc59..c2968b9d49 100644 --- a/Release/include/cpprest/streams.h +++ b/Release/include/cpprest/streams.h @@ -1,962 +1,946 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* 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: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * 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: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#ifndef _CASA_STREAMS_H -#define _CASA_STREAMS_H +#ifndef CASA_STREAMS_H +#define CASA_STREAMS_H #include "cpprest/astreambuf.h" #include -namespace Concurrency { namespace streams +namespace Concurrency { - template class basic_ostream; - template class basic_istream; +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) { } +namespace details +{ +template +class basic_ostream_helper +{ +public: + basic_ostream_helper(streams::streambuf buffer) : m_buffer(buffer) {} - ~basic_ostream_helper() { } + ~basic_ostream_helper() {} - private: - template friend class streams::basic_ostream; +private: + template + friend class streams::basic_ostream; - concurrency::streams::streambuf m_buffer; - }; + concurrency::streams::streambuf m_buffer; +}; - template - class basic_istream_helper - { - public: - basic_istream_helper(streams::streambuf buffer) : m_buffer(buffer) { } +template +class basic_istream_helper +{ +public: + basic_istream_helper(streams::streambuf buffer) : m_buffer(buffer) {} - ~basic_istream_helper() { } + ~basic_istream_helper() {} - private: - template friend class streams::basic_istream; +private: + template + friend class streams::basic_istream; - concurrency::streams::streambuf m_buffer; - }; + 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 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()); - } +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 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"; +} // namespace details - 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 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; } /// - /// Base interface for all asynchronous output streams. + /// Constructor /// - template - class basic_ostream - { - public: - typedef 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); - } + /// 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, 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(); - } + /// + /// 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); - } + /// + /// 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"); + /// + /// 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; + 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(); }); - } + 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"))); + /// + /// 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); + if (count == 0) return pplx::task_from_result((size_t)0); - auto buffer = helper()->m_buffer; - auto data = buffer.alloc(count); + 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; - if ( data != nullptr ) + const bool acquired = source.acquire(data, available); + if (available >= count) { - 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); + 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 { - 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) { - // 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); + source.release(data, 0); } - } - } - /// - /// 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; + std::shared_ptr buf(new CharType[count], [](CharType* buf) { delete[] buf; }); - 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; }); + 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 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 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; - /// - /// 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 + if (str.empty()) { - 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); + return pplx::task_from_result(0); } - - /// - /// Flush any buffered output data. - /// - pplx::task flush() const + else { - pplx::task result; - if ( !_verify_and_return_task(details::_out_stream_msg, result) ) return result; - return helper()->m_buffer.sync(); + auto sharedStr = std::make_shared>(str); + return helper()->m_buffer.putn_nocopy(sharedStr->c_str(), sharedStr->size()).then([sharedStr](size_t size) { + return size; + }); } + } - /// - /// 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); - } + /// + /// 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)); + } - /// - /// 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); - } + /// + /// 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); + } - /// - /// 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); - } + /// + /// 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(); + } - /// - /// 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; - } + /// + /// 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); + } - protected: + /// + /// 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); + } - void set_helper(std::shared_ptr> helper) - { - m_helper = helper; - } + /// + /// 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); + } - private: + /// + /// 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(); } - 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; - } + /// + /// 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(); } - void _verify_and_throw(const char *msg) const + /// + /// 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)) { - auto buffer = helper()->m_buffer; - if ( !(buffer.exception() == nullptr) ) - std::rethrow_exception(buffer.exception()); - if ( !buffer.can_write() ) - throw std::runtime_error(msg); + tsk = pplx::task_from_exception(buffer.exception()); + return false; } - - std::shared_ptr> helper() const + if (!buffer.can_write()) { - if ( !m_helper ) - throw std::logic_error("uninitialized stream object"); - return m_helper; + tsk = pplx::task_from_exception(std::make_exception_ptr(std::runtime_error(msg))); + return false; } + return true; + } - std::shared_ptr> m_helper; - }; + 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); + } - template - struct _type_parser_integral_traits + std::shared_ptr> helper() const { - typedef std::false_type _is_integral; - typedef std::false_type _is_unsigned; - }; + 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;}; +#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(char, INT8_MIN, INT8_MAX) +_INT_TRAIT(signed char, INT8_MIN, INT8_MAX) +_INT_TRAIT(short, INT16_MIN, INT16_MAX) #if defined(_NATIVE_WCHAR_T_DEFINED) - _INT_TRAIT(wchar_t,WCHAR_MIN, WCHAR_MAX) +_INT_TRAIT(wchar_t, WCHAR_MIN, WCHAR_MAX) #endif - _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) +_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) +#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 +template +class _type_parser_base +{ +public: + typedef char_traits traits; + typedef typename 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 asynchronous 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) { - public: - typedef char_traits traits; - typedef typename traits::int_type int_type; + typedef typename _type_parser_integral_traits::_is_integral ii; + typedef typename _type_parser_integral_traits::_is_unsigned ui; - _type_parser_base() { } + static_assert(ii::value || !ui::value, "type is not supported for extraction from a stream"); - protected: - // Aid in parsing input: skipping whitespace characters. - static pplx::task _skip_whitespace(streams::streambuf buffer); + return _parse(buffer, ii {}, ui {}); + } - // 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); - }; +private: + static pplx::task _parse(streams::streambuf buffer, std::false_type, std::false_type) + { + _parse_floating_point(buffer); + } + + 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 traits::int_type int_type; + typedef typename traits::pos_type pos_type; + typedef typename traits::off_type off_type; + + /// + /// Default constructor + /// + basic_istream() {} /// - /// Class used to handle asychronous parsing for basic_istream::extract. To support new - /// types create a new template specialization and implement the parse function. + /// Constructor /// - template - class type_parser + /// + /// The data type of the basic element of the stream. + /// + /// A stream buffer. + template + basic_istream(streams::streambuf buffer) + : m_helper(std::make_shared>(std::move(buffer))) { - 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); - } + _verify_and_throw(details::_in_streambuf_msg); + } - 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 - } + /// + /// Copy constructor + /// + /// The source object + basic_istream(const basic_istream& other) : m_helper(other.m_helper) {} - 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"); - }); - } + /// + /// 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; + } - 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"); - }); - } - }; + /// + /// 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(); + } /// - /// Base interface for all asynchronous input streams. + /// Close the stream with exception, preventing further read operations. /// - template - class basic_istream - { - public: - - typedef 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_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); - } + /// 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(); + } - /// - /// 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; - } + /// + /// 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; } - /// - /// 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(); - } + /// + /// 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(); + } - /// - /// 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(); - } + /// + /// 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"); - /// - /// 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; - } + pplx::task result; + if (!_verify_and_return_task(details::_in_stream_msg, result)) return result; - /// - /// 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 + auto copy = std::make_shared(); + return helper()->m_buffer.getn((CharType*)copy.get(), sizeof(T)).then([copy](pplx::task) -> 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) { - pplx::task result; - if ( !_verify_and_return_task(details::_in_stream_msg, result) ) return result; - return helper()->m_buffer.bumpc(); + 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); } - - /// - /// 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 + else { - 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; + size_t available = 0; - auto copy = std::make_shared(); - return helper()->m_buffer.getn((CharType*)copy.get(), sizeof(T)).then([copy](pplx::task) -> T + const bool acquired = buffer.acquire(data, available); + if (available >= count) { - return std::move(*copy); - }); + 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); + } } + } - /// - /// 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"))); + /// + /// 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 = 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(); + }); + }; - // Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations. - auto buffer = helper()->m_buffer; + auto update = [=](int_type ch) mutable { + if (ch == traits::eof()) return false; + if (ch == delim) return false; - auto data = target.alloc(count); + _locals->outbuf[_locals->write_pos] = static_cast(ch); + _locals->write_pos += 1; - if ( data != nullptr ) + if (_locals->is_full()) { - 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); + // Flushing synchronously because performance is terrible if we + // schedule an empty task. This isn't on a user's thread. + flush().get(); } - else + + return true; + }; + + auto loop = pplx::details::_do_while([=]() mutable -> pplx::task { + while (buffer.in_avail() > 0) { - size_t available = 0; + int_type ch = buffer.sbumpc(); - const bool acquired = buffer.acquire(data, available); - if (available >= count) + if (ch == req_async) { - 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); + break; } - else + + if (!update(ch)) { - // 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); + return pplx::task_from_result(false); } } - } - - /// - /// 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 = traits::requires_async(); - - std::shared_ptr<_read_helper> _locals = std::make_shared<_read_helper>(); + return buffer.bumpc().then(update); + }); - 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(); - }); - }; + return loop.then([=](bool) mutable { return flush().then([=] { return _locals->total; }); }); + } - auto update = [=](int_type ch) mutable - { - if (ch == traits::eof()) return false; - if (ch == delim) return false; + /// + /// 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"))); - _locals->outbuf[_locals->write_pos] = static_cast(ch); - _locals->write_pos += 1; + // Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations. + concurrency::streams::streambuf buffer = helper()->m_buffer; - 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(); - } + int_type req_async = traits::requires_async(); - return true; - }; + std::shared_ptr<_read_helper> _locals = std::make_shared<_read_helper>(); - 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; }); + 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(); }); - } + }; - /// - /// 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"))); + auto update = [=](int_type ch) mutable { + if (ch == traits::eof()) return false; + if (ch == '\n') return false; + if (ch == '\r') + { + _locals->saw_CR = true; + return true; + } - // Capture 'buffer' rather than 'helper' here due to VC++ 2010 limitations. - concurrency::streams::streambuf buffer = helper()->m_buffer; + _locals->outbuf[_locals->write_pos] = static_cast(ch); + _locals->write_pos += 1; - int_type req_async = traits::requires_async(); + 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(); + } - std::shared_ptr<_read_helper> _locals = std::make_shared<_read_helper>(); + return true; + }; - auto flush = [=]() mutable + auto update_after_cr = [=](int_type ch) mutable -> pplx::task { + if (ch == traits::eof()) return pplx::task_from_result(false); + if (ch == '\n') { - return target.putn_nocopy(_locals->outbuf, _locals->write_pos).then([=](size_t wrote) mutable - { - _locals->total += wrote; - _locals->write_pos = 0; - return target.sync(); - }); - }; + return buffer.bumpc().then([](int_type) { return false; }); + } + return pplx::task_from_result(false); + }; - auto update = [=](int_type ch) mutable - { - if (ch == 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 loop = pplx::details::_do_while([=]() mutable -> pplx::task { + while (buffer.in_avail() > 0) + { + int_type ch; - auto update_after_cr = [=] (int_type ch) mutable -> pplx::task + if (_locals->saw_CR) { - if (ch == traits::eof()) return pplx::task_from_result(false); - if (ch == '\n') - { - return buffer.bumpc().then([](int_type) { return false; }); - } + ch = buffer.sgetc(); + if (ch == '\n') buffer.sbumpc(); return pplx::task_from_result(false); - }; + } - auto loop = pplx::details::_do_while([=]() mutable -> pplx::task - { - while ( buffer.in_avail() > 0 ) - { - 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; }); - }); - } + ch = buffer.sbumpc(); - /// - /// 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"))); + if (ch == req_async) break; - 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>(); + if (!update(ch)) + { + return pplx::task_from_result(false); + } + } - auto copy_to_target = [l_locals, target, l_buffer, l_buf_size]() mutable -> pplx::task + if (_locals->saw_CR) { - // 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 buffer.getc().then(update_after_cr); + } + return buffer.bumpc().then(update); + }); - 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); + return loop.then([=](bool) mutable { return flush().then([=] { return _locals->total; }); }); + } - // 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 - { + /// + /// 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) @@ -965,229 +949,204 @@ namespace Concurrency { namespace streams return target.sync().then([]() { return true; }); }); - }); - }; + }); + }; - auto loop = pplx::details::_do_while(copy_to_target); + auto loop = pplx::details::_do_while(copy_to_target); - return loop.then([=](bool) mutable -> size_t - { - return l_locals->total; - }); - } + 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 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); - } + /// + /// 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); - } + /// + /// 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; - } + /// + /// 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(); } - /// - /// 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); - } + /// + /// 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); } - private: + /// + /// Test whether the stream has been initialized or not. + /// + operator bool() const { return is_valid(); } - 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; - } + /// + /// 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); + } - void _verify_and_throw(const char *msg) const +private: + template + bool _verify_and_return_task(const char* msg, pplx::task& tsk) const + { + auto buffer = helper()->m_buffer; + if (!(buffer.exception() == nullptr)) { - auto buffer = helper()->m_buffer; - if ( !(buffer.exception() == nullptr) ) - std::rethrow_exception(buffer.exception()); - if ( !buffer.can_read() ) - throw std::runtime_error(msg); + tsk = pplx::task_from_exception(buffer.exception()); + return false; } - - std::shared_ptr> helper() const + if (!buffer.can_read()) { - if ( !m_helper ) - throw std::logic_error("uninitialized stream object"); - return m_helper; + tsk = pplx::task_from_exception(std::make_exception_ptr(std::runtime_error(msg))); + return false; } + return true; + } - static const size_t buf_size = 16*1024; + 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); + } - struct _read_helper - { - size_t total; - CharType outbuf[buf_size]; - size_t write_pos; - bool saw_CR; + std::shared_ptr> helper() const + { + if (!m_helper) throw std::logic_error("uninitialized stream object"); + return m_helper; + } - bool is_full() const - { - return write_pos == buf_size; - } + static const size_t buf_size = 16 * 1024; - _read_helper() : total(0), write_pos(0), saw_CR(false) - { - } - }; + struct _read_helper + { + size_t total; + CharType outbuf[buf_size]; + size_t write_pos; + bool saw_CR; - std::shared_ptr> m_helper; + bool is_full() const { return write_pos == buf_size; } + + _read_helper() : total(0), write_pos(0), saw_CR(false) {} }; - typedef basic_ostream ostream; - typedef basic_istream istream; + std::shared_ptr> m_helper; +}; + +typedef basic_ostream ostream; +typedef basic_istream istream; - typedef basic_ostream wostream; - typedef basic_istream wistream; +typedef basic_ostream wostream; +typedef basic_istream wistream; template pplx::task _type_parser_base::_skip_whitespace(streams::streambuf buffer) { int_type req_async = traits::requires_async(); - auto update = [=] (int_type ch) mutable + auto update = [=](int_type ch) mutable { + if (isspace(ch)) { - if (isspace(ch)) + if (buffer.sbumpc() == req_async) { - 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; + // 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; - }; + return false; + }; - auto loop = pplx::details::_do_while([=]() mutable -> pplx::task + auto loop = pplx::details::_do_while([=]() mutable -> pplx::task { + while (buffer.in_avail() > 0) { - while (buffer.in_avail() > 0) - { - int_type ch = buffer.sgetc(); + int_type ch = buffer.sgetc(); - if (ch == req_async) - break; + if (ch == req_async) break; - if (!update(ch)) - { - return pplx::task_from_result(false); - } + if (!update(ch)) + { + return pplx::task_from_result(false); } - return buffer.getc().then(update); - }); + } + return buffer.getc().then(update); + }); - return loop.then([=](pplx::task op) - { - op.wait(); - }); + return loop.then([=](pplx::task op) { op.wait(); }); } template template -pplx::task _type_parser_base::_parse_input( - concurrency::streams::streambuf buffer, - AcceptFunctor accept_character, - ExtractFunctor extract) +pplx::task _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 - { + auto update = [=](pplx::task op) -> pplx::task { int_type ch = op.get(); if (ch == traits::eof()) return pplx::task_from_result(false); - bool accptd = accept_character(state, ch); - if (!accptd) - return pplx::task_from_result(false); + bool accepted = accept_character(state, ch); + if (!accepted) 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 - { + auto peek_char = [=]() -> pplx::task { concurrency::streams::streambuf buf = buffer; // If task results are immediately available, there's little need to use ".then()," @@ -1197,8 +1156,7 @@ pplx::task _type_parser_base::_parse_input( while (get_op.is_done()) { auto condition = update(get_op); - if (!condition.is_done() || !condition.get()) - return condition; + if (!condition.is_done() || !condition.get()) return condition; get_op = buf.getc(); } @@ -1206,38 +1164,37 @@ pplx::task _type_parser_base::_parse_input( return get_op.then(update); }; - auto finish = - [=](pplx::task op) -> pplx::task - { - op.wait(); - pplx::task result = extract(state); - return result; - }; + 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); - }); + 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 +class type_parser> : public _type_parser_base { typedef _type_parser_base base; + public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { - return base::template _parse_input, std::string>(buffer, _accept_char, _extract_result); + return 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 == traits::eof() || isspace(ch)) return false; + if (ch == traits::eof() || isspace(ch)) return false; state->push_back(CharType(ch)); return true; } @@ -1248,9 +1205,10 @@ class type_parser> : public _type_parser_ba }; template -class type_parser : public _type_parser_base +class type_parser : public _type_parser_base { typedef _type_parser_base base; + public: typedef typename base::traits traits; typedef typename base::int_type int_type; @@ -1259,6 +1217,7 @@ class type_parser : public _type_parser_base { return base::template _parse_input<_int64_state, int64_t>(buffer, _accept_char, _extract_result); } + private: struct _int64_state { @@ -1266,31 +1225,30 @@ class type_parser : public _type_parser_base int64_t result; bool correct; - char minus; // 0 -- no sign, 1 -- plus, 2 -- minus + char minus; // 0 -- no sign, 1 -- plus, 2 -- minus }; static bool _accept_char(std::shared_ptr<_int64_state> state, int_type ch) { - if ( ch == traits::eof()) return false; - if ( state->minus == 0 ) + if (ch == traits::eof()) return false; + if (state->minus == 0) { // OK to find a sign. - if ( !::isdigit(ch) && ch != int_type('+') && ch != int_type('-') ) - return false; + if (!::isdigit(ch) && ch != int_type('+') && ch != int_type('-')) return false; } else { - if ( !::isdigit(ch) ) return false; + if (!::isdigit(ch)) return false; } // At least one digit was found. state->correct = true; - if ( ch == int_type('+') ) + if (ch == int_type('+')) { state->minus = 1; } - else if ( ch == int_type('-') ) + else if (ch == int_type('-')) { state->minus = 2; } @@ -1302,9 +1260,9 @@ class type_parser : public _type_parser_base bool positive = state->result >= 0; state->result *= 10; - state->result += int64_t(ch-int_type('0')); + state->result += int64_t(ch - int_type('0')); - if ( (state->result >= 0) != positive ) + if ((state->result >= 0) != positive) { state->correct = false; return false; @@ -1315,43 +1273,61 @@ class type_parser : public _type_parser_base 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"); + 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 +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() {} + _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 + 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 + char exponent_minus; // 0 -- no sign, 1 -- plus, 2 -- minus bool complete; std::string p_exception_string; }; -template +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(); + std::string result; + if (exponent) + { + result.assign("Invalid character 'X' in exponent"); + } + else + { + result.assign("Invalid character 'X'"); + } + + result[19] = static_cast(ch); + return result; } -template +template static bool _accept_char(std::shared_ptr<_double_state> state, int_type ch) { - if ( state->minus == 0 ) + if (state->minus == 0) { - if ( !::isdigit(ch) && ch != int_type('.') && ch != int_type('+') && ch != int_type('-') ) + if (!::isdigit(ch) && ch != int_type('.') && ch != int_type('+') && ch != int_type('-')) { if (!state->complete) state->p_exception_string = create_exception_message(ch, false); @@ -1377,7 +1353,7 @@ static bool _accept_char(std::shared_ptr<_double_state> state, in switch (ch) { - case int_type('+') : + case int_type('+'): state->complete = false; if (state->exponent) { @@ -1393,7 +1369,7 @@ static bool _accept_char(std::shared_ptr<_double_state> state, in state->minus = 1; } break; - case int_type('-') : + case int_type('-'): state->complete = false; if (state->exponent) { @@ -1410,17 +1386,16 @@ static bool _accept_char(std::shared_ptr<_double_state> state, in state->minus = 2; } break; - case int_type('.') : + case int_type('.'): state->complete = false; - if (state->after_comma > 0) - return false; + if (state->after_comma > 0) return false; state->after_comma = 1; break; - case int_type('E') : case int_type('e') : + case int_type('E'): + case int_type('e'): state->complete = false; - if (state->exponent) - return false; + if (state->exponent) return false; state->exponent_number = 0; state->exponent = true; break; @@ -1428,51 +1403,45 @@ static bool _accept_char(std::shared_ptr<_double_state> state, in state->complete = true; if (!state->exponent) { - if (state->minus == 0) - state->minus = 1; + if (state->minus == 0) state->minus = 1; state->result *= 10; - state->result += int64_t(ch-int_type('0')); + state->result += int64_t(ch - int_type('0')); - if (state->after_comma > 0) - state->after_comma++; + 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')); + state->exponent_number += int64_t(ch - int_type('0')); } } return true; } -template +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->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"); + 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->exponent_minus == 2) state->exponent_number = 0 - state->exponent_number; - if (state->after_comma > 0) - state->exponent_number -= state->after_comma-1; + 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 +#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") +#pragma pop_macro("max") } else { @@ -1480,8 +1449,7 @@ static pplx::task _extract_result(std::shared_ptr<_double_stateexponent_number); - if (!is_zero && - result > -std::numeric_limits::denorm_min() && + if (!is_zero && result > -std::numeric_limits::denorm_min() && result < std::numeric_limits::denorm_min()) throw std::underflow_error("The value is too small"); } @@ -1490,47 +1458,53 @@ static pplx::task _extract_result(std::shared_ptr<_double_state -class type_parser : public _type_parser_base +class type_parser : public _type_parser_base { typedef _type_parser_base base; + public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { - return base::template _parse_input<_double_state, double>(buffer, _accept_char, _extract_result); + return base::template _parse_input<_double_state, double>( + buffer, _accept_char, _extract_result); } + protected: }; template -class type_parser : public _type_parser_base +class type_parser : public _type_parser_base { typedef _type_parser_base base; + public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { - return base::template _parse_input<_double_state, float>(buffer, _accept_char, _extract_result); + return base::template _parse_input<_double_state, float>( + buffer, _accept_char, _extract_result); } + protected: }; - template -class type_parser : public _type_parser_base +class type_parser : public _type_parser_base { typedef _type_parser_base base; + public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { - return base::template _parse_input<_uint64_state,uint64_t>(buffer, _accept_char, _extract_result); + return base::template _parse_input<_uint64_state, uint64_t>(buffer, _accept_char, _extract_result); } private: @@ -1543,43 +1517,45 @@ class type_parser : public _type_parser_base static bool _accept_char(std::shared_ptr<_uint64_state> state, int_type ch) { - if ( !::isdigit(ch) ) return false; + 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')); + 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"); + 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 +class type_parser : public _type_parser_base { typedef _type_parser_base base; + public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { - return base::template _parse_input<_bool_state,bool>(buffer, _accept_char, _extract_result); + return 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' } + _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; }; @@ -1587,44 +1563,62 @@ class type_parser : public _type_parser_base { 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; + 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; } @@ -1641,119 +1635,113 @@ class type_parser : public _type_parser_base }; template -class type_parser : public _type_parser_base +class type_parser : public _type_parser_base { typedef _type_parser_base base; + public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { - return base::_skip_whitespace(buffer).then( - [=](pplx::task op) -> pplx::task - { - op.wait(); - return type_parser::_get_char(buffer); - }); + return 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 op) -> signed char - { - int_type val = op.get(); - if (val == traits::eof()) - throw std::runtime_error("reached end-of-stream while constructing a value"); - return static_cast(val); - }); + return buf.bumpc().then([=](pplx::task op) -> signed char { + int_type val = op.get(); + if (val == 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 +class type_parser : public _type_parser_base { typedef _type_parser_base base; + public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { - return base::_skip_whitespace(buffer).then( - [=](pplx::task op) -> pplx::task - { - op.wait(); - return type_parser::_get_char(buffer); - }); + return 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 op) -> unsigned char - { - int_type val = op.get(); - if (val == traits::eof()) - throw std::runtime_error("reached end-of-stream while constructing a value"); - return static_cast(val); - }); + return buf.bumpc().then([=](pplx::task op) -> unsigned char { + int_type val = op.get(); + if (val == 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 +class type_parser : public _type_parser_base { typedef _type_parser_base base; + public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { - return base::_skip_whitespace(buffer).then( - [=](pplx::task op) -> pplx::task - { - op.wait(); - return _get_char(buffer); - }); + return 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 op) -> char - { - int_type val = op.get(); - if (val == traits::eof()) - throw std::runtime_error("reached end-of-stream while constructing a value"); - return char(val); - }); + return buf.bumpc().then([=](pplx::task op) -> char { + int_type val = op.get(); + if (val == 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 +class type_parser>> + : public _type_parser_base { typedef _type_parser_base base; + public: typedef typename base::traits traits; typedef typename base::int_type int_type; static pplx::task parse(streams::streambuf buffer) { - return _parse_input,std::basic_string>(buffer, _accept_char, _extract_result); + return base::template _parse_input, std::basic_string>( + buffer, _accept_char, _extract_result); } private: - static bool _accept_char(const std::shared_ptr> &state, int_type ch) + static bool _accept_char(const std::shared_ptr>& state, int_type ch) { - if ( ch == concurrency::streams::char_traits::eof() || isspace(ch)) return false; + if (ch == concurrency::streams::char_traits::eof() || isspace(ch)) return false; state->push_back(char(ch)); return true; } @@ -1764,7 +1752,7 @@ class type_parser -#include -#include - #include "cpprest/base_uri.h" +#include namespace web { +/// +/// Builder for constructing URIs incrementally. +/// +class uri_builder +{ +public: + /// + /// Creates a builder with an initially empty URI. + /// + uri_builder() = default; + + /// + /// 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; } + /// - /// Builder for constructing URIs incrementally. + /// Get the path component of the URI as an encoded string. /// - class uri_builder + /// 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) { - public: - - /// - /// Creates a builder with an initially empty URI. - /// - uri_builder() = default; - - /// - /// 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) + if (do_encoding) { - m_uri.m_scheme = scheme; - return *this; + m_uri.m_user_info = uri::encode_uri(user_info, uri::components::user_info); } - - /// - /// 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) + else { - m_uri.m_user_info = do_encoding ? uri::encode_uri(user_info, uri::components::user_info) : user_info; - return *this; + m_uri.m_user_info = user_info; } - /// - /// 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) + 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) + { + if (do_encoding) { - m_uri.m_host = do_encoding ? uri::encode_uri(host, uri::components::host) : host; - return *this; + m_uri.m_host = uri::encode_uri(host, uri::components::host); } - - /// - /// 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) + else { - m_uri.m_port = port; - return *this; + m_uri.m_host = host; } - /// - /// 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. - _ASYNCRTIMP uri_builder & set_port(const utility::string_t &port); - - /// - /// 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) + 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. + _ASYNCRTIMP uri_builder& set_port(const utility::string_t& port); + + /// + /// 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) + { + if (do_encoding) + { + m_uri.m_path = uri::encode_uri(path, uri::components::path); + } + else { - m_uri.m_path = do_encoding ? uri::encode_uri(path, uri::components::path) : path; - return *this; + m_uri.m_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) + /// + /// 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) + { + if (do_encoding) { - m_uri.m_query = do_encoding ? uri::encode_uri(query, uri::components::query) : query; - return *this; + m_uri.m_query = uri::encode_uri(query, uri::components::query); } - - /// - /// 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) + else { - m_uri.m_fragment = do_encoding ? uri::encode_uri(fragment, uri::components::fragment) : fragment; - return *this; + m_uri.m_query = query; } - /// - /// Clears all components of the underlying URI in this uri_builder. - /// - void clear() + 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) + { + if (do_encoding) { - m_uri = details::uri_components(); + m_uri.m_fragment = uri::encode_uri(fragment, uri::components::fragment); } - - /// - /// 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(const utility::string_t &name, const T &value, bool do_encoding = true) + else { - if (do_encoding) - append_query_encode_impl(name, utility::conversions::details::print_utf8string(value)); - else - append_query_no_encode_impl(name, utility::conversions::details::print_string(value)); - return *this; + m_uri.m_fragment = fragment; } - /// - /// 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() const; - - /// - /// 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() const; - - /// - /// Validate the generated URI from all existing components of this uri_builder. - /// - /// Whether the URI is valid. - _ASYNCRTIMP bool is_valid(); - - private: - _ASYNCRTIMP void append_query_encode_impl(const utility::string_t &name, const utf8string &value); - _ASYNCRTIMP void append_query_no_encode_impl(const utility::string_t &name, const utility::string_t &value); - - details::uri_components m_uri; - }; + 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 the raw contents of the path argument to the path of this uri_builder with no separator de-duplication. + /// + /// + /// The path argument is appended after adding a '/' separator without regards to the contents of path. If an empty + /// string is provided, this function will immediately return without changes to the stored path value. For example: + /// if the current contents are "/abc" and path="/xyz", the result will be "/abc//xyz". + /// + /// 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_raw(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(const utility::string_t& name, const T& value, bool do_encoding = true) + { + if (do_encoding) + append_query_encode_impl(name, utility::conversions::details::print_utf8string(value)); + else + append_query_no_encode_impl(name, utility::conversions::details::print_string(value)); + return *this; + } + + /// + /// 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() const; + + /// + /// 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() const; + + /// + /// Validate the generated URI from all existing components of this uri_builder. + /// + /// Whether the URI is valid. + _ASYNCRTIMP bool is_valid(); + +private: + _ASYNCRTIMP void append_query_encode_impl(const utility::string_t& name, const utf8string& value); + _ASYNCRTIMP void append_query_no_encode_impl(const utility::string_t& name, const utility::string_t& value); + + details::uri_components m_uri; +}; } // namespace web diff --git a/Release/include/cpprest/version.h b/Release/include/cpprest/version.h index f3d9602e67..a6a55e231b 100644 --- a/Release/include/cpprest/version.h +++ b/Release/include/cpprest/version.h @@ -1,11 +1,10 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -*/ -#define CPPREST_VERSION_REVISION 2 + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + */ #define CPPREST_VERSION_MINOR 10 #define CPPREST_VERSION_MAJOR 2 +#define CPPREST_VERSION_REVISION 14 -#define CPPREST_VERSION (CPPREST_VERSION_MAJOR*100000+CPPREST_VERSION_MINOR*100+CPPREST_VERSION_REVISION) - +#define CPPREST_VERSION (CPPREST_VERSION_MAJOR * 100000 + CPPREST_VERSION_MINOR * 100 + CPPREST_VERSION_REVISION) diff --git a/Release/include/cpprest/ws_client.h b/Release/include/cpprest/ws_client.h index 9a324cde51..af17bd6060 100644 --- a/Release/include/cpprest/ws_client.h +++ b/Release/include/cpprest/ws_client.h @@ -1,37 +1,46 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Websocket client side implementation -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Websocket client side implementation + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#ifndef _CASA_WS_CLIENT_H -#define _CASA_WS_CLIENT_H +#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/asyncrt_utils.h" #include "cpprest/details/web_utilities.h" #include "cpprest/http_headers.h" -#include "cpprest/asyncrt_utils.h" +#include "cpprest/uri.h" #include "cpprest/ws_msg.h" +#include "pplx/pplxtasks.h" +#include +#include +#include +#include + +#if !defined(_WIN32) || !defined(__cplusplus_winrt) +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#endif +#include "boost/asio/ssl.hpp" +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#endif namespace web { - // For backwards compatibility for when in the experimental namespace. // At next major release this should be deleted. namespace experimental = web; @@ -45,14 +54,13 @@ 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 + unsupported = 1003, // or data_mismatch abnormal_close = 1006, inconsistent_datatype = 1007, policy_violation = 1008, @@ -68,93 +76,64 @@ enum class websocket_close_status class websocket_client_config { public: - /// /// Creates a websocket client configuration with default settings. /// - websocket_client_config() : - m_sni_enabled(true), - m_validate_certificates(true) - { - } + websocket_client_config() : m_sni_enabled(true), m_validate_certificates(true) {} /// /// Get the web proxy object /// /// A reference to the web proxy object. - const web_proxy& proxy() const - { - return m_proxy; - } + 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; - } + 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; - } + 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; - } + 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; - } + 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; - } + 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; - } + void set_server_name(const utf8string& name) { m_sni_hostname = name; } /// - /// Gets the server host name to usefor TLS Server Name Indication (SNI). + /// Gets the server host name to use for TLS Server Name Indication (SNI). /// /// Host name as a string. - const utf8string & server_name() const - { - return m_sni_hostname; - } + const utf8string& server_name() const { return m_sni_hostname; } /// /// Sets the User Agent to be used for the connection /// /// The User Agent to use, as a string. - _ASYNCRTIMP void set_user_agent(const utf8string &user_agent); + _ASYNCRTIMP void set_user_agent(const utf8string& user_agent); /// /// Gets the headers of the HTTP request message used in the WebSocket protocol handshake. @@ -163,20 +142,20 @@ class websocket_client_config /// /// Use the to fill in desired headers. /// - web::http::http_headers &headers() { return m_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; } + 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); + _ASYNCRTIMP void add_subprotocol(const ::utility::string_t& name); /// /// Gets list of the specified subprotocols. @@ -185,25 +164,40 @@ class websocket_client_config /// 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; - + /// /// Gets the server certificate validation property. /// /// True if certificates are to be verified, false otherwise. - bool validate_certificates() const + 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; } + +#if !defined(_WIN32) || !defined(__cplusplus_winrt) + /// + /// Sets a callback to enable custom setting of the ssl context, at construction time. + /// + /// A user callback allowing for customization of the ssl context at construction + /// time. + void set_ssl_context_callback(const std::function& callback) { - return m_validate_certificates; + m_ssl_context_callback = callback; } - + /// - /// Sets the server certificate validation property. + /// Gets the user's callback to allow for customization of the ssl context. /// - /// 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) + const std::function& get_ssl_context_callback() const { - m_validate_certificates = validate_certs; + return m_ssl_context_callback; } +#endif private: web::web_proxy m_proxy; @@ -212,6 +206,9 @@ class websocket_client_config bool m_sni_enabled; utf8string m_sni_hostname; bool m_validate_certificates; +#if !defined(_WIN32) || !defined(__cplusplus_winrt) + std::function m_ssl_context_callback; +#endif }; /// @@ -220,13 +217,11 @@ class websocket_client_config 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)) {} + websocket_exception(const utility::string_t& whatArg) : m_msg(utility::conversions::to_utf8string(whatArg)) {} #ifdef _WIN32 /// @@ -241,8 +236,7 @@ class websocket_exception : public std::exception /// 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)) + websocket_exception(int errorCode) : m_errorCode(utility::details::create_error_code(errorCode)) { m_msg = m_errorCode.message(); } @@ -252,10 +246,11 @@ class websocket_exception : public std::exception /// /// 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)) - {} + 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 /// @@ -264,19 +259,19 @@ class websocket_exception : public std::exception /// 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)) - {} + : 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)) - {} + websocket_exception(std::error_code code, std::string whatArg) + : m_errorCode(std::move(code)), m_msg(std::move(whatArg)) + { + } #endif /// @@ -285,7 +280,7 @@ class websocket_exception : public std::exception /// /// Error code value. /// Error category for the code. - websocket_exception(int errorCode, const std::error_category &cat) : m_errorCode(std::error_code(errorCode, cat)) + websocket_exception(int errorCode, const std::error_category& cat) : m_errorCode(std::error_code(errorCode, cat)) { m_msg = m_errorCode.message(); } @@ -295,28 +290,22 @@ class websocket_exception : public std::exception /// 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)) - {} + 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(); - } + 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; - } + const std::error_code& error_code() const CPPREST_NOEXCEPT { return m_errorCode; } private: std::error_code m_errorCode; @@ -325,43 +314,33 @@ class websocket_exception : public std::exception 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)) {} - websocket_client_callback_impl(websocket_client_config config) : - m_config(std::move(config)) {} - - virtual ~websocket_client_callback_impl() CPPREST_NOEXCEPT{} + virtual ~websocket_client_callback_impl() CPPREST_NOEXCEPT {} virtual pplx::task connect() = 0; - virtual pplx::task send(websocket_outgoing_message &msg) = 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 pplx::task close(websocket_close_status close_status, const utility::string_t& close_reason = {}) = 0; - virtual void set_close_handler(const std::function& handler) = 0; + virtual void set_close_handler( + const std::function& + handler) = 0; - const web::uri& uri() const - { - return m_uri; - } + const web::uri& uri() const { return m_uri; } - void set_uri(const web::uri &uri) - { - m_uri = uri; - } + void set_uri(const web::uri& uri) { m_uri = uri; } - const websocket_client_config& config() const - { - return m_config; - } + const websocket_client_config& config() const { return m_config; } static void verify_uri(const web::uri& uri) { @@ -393,7 +372,6 @@ class websocket_client_callback_impl // 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); @@ -401,9 +379,9 @@ class websocket_client_task_impl _ASYNCRTIMP pplx::task receive(); - _ASYNCRTIMP void close_pending_tasks_with_error(const websocket_exception &exc); + _ASYNCRTIMP void close_pending_tasks_with_error(const websocket_exception& exc); - const std::shared_ptr & callback_client() const { return m_callback_client; }; + const std::shared_ptr& callback_client() const { return m_callback_client; }; private: void set_handler(); @@ -423,7 +401,7 @@ class websocket_client_task_impl std::shared_ptr m_callback_client; }; -} +} // namespace details /// /// Websocket client class, used to maintain a connection to a remote host for an extended session. @@ -434,31 +412,31 @@ class websocket_client /// /// Creates a new websocket_client. /// - websocket_client() : - m_client(std::make_shared(websocket_client_config())) - {} + 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))) - {} + /// 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) + /// 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) - { + return m_client->callback_client()->connect().then([client](pplx::task result) { try { result.get(); @@ -475,36 +453,31 @@ class websocket_client /// 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); - } + 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(); - } + /// 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. + /// 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(); - } + 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. + /// 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("")) + /// 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 = {}) { return m_client->callback_client()->close(close_status, close_reason); } @@ -513,27 +486,22 @@ class websocket_client /// Gets the websocket client URI. /// /// URI connected to. - const web::uri& uri() const - { - return m_client->callback_client()->uri(); - } + 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(); - } + 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. +/// 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 { @@ -546,7 +514,8 @@ class websocket_callback_client /// /// Creates a new websocket_callback_client. /// - /// The client configuration object containing the possible configuration options to initialize the websocket_client. + /// The client configuration object containing the possible configuration options to + /// initialize the websocket_client. _ASYNCRTIMP websocket_callback_client(websocket_client_config client_config); /// @@ -554,8 +523,9 @@ class websocket_callback_client /// 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) + /// 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); @@ -566,10 +536,7 @@ class websocket_callback_client /// 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); - } + pplx::task send(websocket_outgoing_message msg) { return m_client->send(msg); } /// /// Set the received handler for notification of client websocket messages. @@ -584,21 +551,21 @@ class websocket_callback_client } /// - /// Closes a websocket client connection, sends a close frame to the server and waits for a close message from the server. + /// 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(); - } + 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. + /// 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("")) + /// 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 = {}) { return m_client->close(close_status, close_reason); } @@ -611,7 +578,9 @@ class websocket_callback_client /// 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) + void set_close_handler(const std::function& handler) { m_client->set_close_handler(handler); } @@ -620,26 +589,22 @@ class websocket_callback_client /// Gets the websocket client URI. /// /// URI connected to. - const web::uri& uri() const - { - return m_client->uri(); - } + 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(); - } + const websocket_client_config& config() const { return m_client->config(); } private: std::shared_ptr m_client; }; -}}} +} // namespace client +} // namespace websockets +} // namespace web #endif -#endif \ No newline at end of file +#endif diff --git a/Release/include/cpprest/ws_msg.h b/Release/include/cpprest/ws_msg.h index 326541c7a1..9b13a80849 100644 --- a/Release/include/cpprest/ws_msg.h +++ b/Release/include/cpprest/ws_msg.h @@ -1,27 +1,26 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Websocket incoming and outgoing message definitions. -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * 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/asyncrt_utils.h" #include "cpprest/containerstream.h" +#include "cpprest/streams.h" #include "cpprest/uri.h" -#include "cpprest/asyncrt_utils.h" +#include "pplx/pplxtasks.h" +#include +#include namespace web { @@ -29,15 +28,14 @@ namespace websockets { namespace client { - namespace details { - class winrt_callback_client; - class wspp_callback_client; +class winrt_callback_client; +class wspp_callback_client; #if defined(__cplusplus_winrt) - ref class ReceiveContext; +ref class ReceiveContext; #endif -} +} // namespace details /// /// The different types of websocket message. @@ -60,15 +58,24 @@ enum class websocket_message_type class websocket_outgoing_message { public: - #if !defined(__cplusplus_winrt) /// - /// Sets a the outgoing message to be an unsolicited pong message. + /// Sets the outgoing message to be a ping message. /// This is useful when the client side wants to check whether the server is alive. /// - void set_pong_message() + /// UTF-8 String containing the optional ping message. + void set_ping_message(const std::string& data = {}) { - this->set_message_pong(); + this->set_message_ping(concurrency::streams::container_buffer(data)); + } + + /// + /// Sets the outgoing message to be an unsolicited pong message. + /// + /// UTF-8 String containing the optional pong message. + void set_pong_message(const std::string& data = {}) + { + this->set_message_pong(concurrency::streams::container_buffer(data)); } #endif @@ -76,7 +83,7 @@ class websocket_outgoing_message /// Sets a UTF-8 message as the message body. /// /// UTF-8 String containing body of the message. - void set_utf8_message(std::string &&data) + void set_utf8_message(std::string&& data) { this->set_message(concurrency::streams::container_buffer(std::move(data))); } @@ -85,7 +92,7 @@ class websocket_outgoing_message /// 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) + void set_utf8_message(const std::string& data) { this->set_message(concurrency::streams::container_buffer(data)); } @@ -95,7 +102,7 @@ class websocket_outgoing_message /// /// 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) + void set_utf8_message(const concurrency::streams::istream& istream) { this->set_message(istream, SIZE_MAX, websocket_message_type::text_message); } @@ -105,7 +112,7 @@ class websocket_outgoing_message /// /// 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) + void set_utf8_message(const concurrency::streams::istream& istream, size_t len) { this->set_message(istream, len, websocket_message_type::text_message); } @@ -115,7 +122,7 @@ class websocket_outgoing_message /// /// 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) + void set_binary_message(const concurrency::streams::istream& istream, size_t len) { this->set_message(istream, len, websocket_message_type::binary_message); } @@ -125,7 +132,7 @@ class websocket_outgoing_message /// /// 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) + void set_binary_message(const concurrency::streams::istream& istream) { this->set_message(istream, SIZE_MAX, websocket_message_type::binary_message); } @@ -139,36 +146,35 @@ class websocket_outgoing_message websocket_message_type m_msg_type; size_t m_length; - void signal_body_sent() const - { - m_body_sent.set(); - } + 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); - } + 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; } + const pplx::task_completion_event& body_sent() const { return m_body_sent; } #if !defined(__cplusplus_winrt) - void set_message_pong() - { - concurrency::streams::container_buffer buffer(""); - m_msg_type = websocket_message_type::pong; - m_length = static_cast(buffer.size()); - m_body = buffer; - } + void set_message_ping(const concurrency::streams::container_buffer& buffer) + { + m_msg_type = websocket_message_type::ping; + m_length = static_cast(buffer.size()); + m_body = buffer; + } + void set_message_pong(const concurrency::streams::container_buffer& buffer) + { + m_msg_type = websocket_message_type::pong; + m_length = static_cast(buffer.size()); + m_body = buffer; + } #endif - void set_message(const concurrency::streams::container_buffer &buffer) + 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) + void set_message(const concurrency::streams::istream& istream, size_t len, websocket_message_type msg_type) { m_msg_type = msg_type; m_length = len; @@ -182,7 +188,6 @@ class websocket_outgoing_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. @@ -200,8 +205,8 @@ class websocket_incoming_message /// concurrency::streams::istream body() const { - auto to_uint8_t_stream = [](const concurrency::streams::streambuf &buf) -> concurrency::streams::istream - { + auto to_uint8_t_stream = + [](const concurrency::streams::streambuf& buf) -> concurrency::streams::istream { return buf.create_istream(); }; return to_uint8_t_stream(m_body); @@ -210,28 +215,19 @@ class websocket_incoming_message /// /// Returns the length of the received message. /// - size_t length() const - { - return static_cast(m_body.size()); - } + 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; - } + 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; - } + websocket_message_type message_type() const { return m_msg_type; } private: friend class details::winrt_callback_client; @@ -246,6 +242,8 @@ class websocket_incoming_message websocket_message_type m_msg_type; }; -}}} +} // namespace client +} // namespace websockets +} // namespace web #endif diff --git a/Release/include/pplx/pplx.h b/Release/include/pplx/pplx.h index 5a7c970319..d9ba9c619a 100644 --- a/Release/include/pplx/pplx.h +++ b/Release/include/pplx/pplx.h @@ -1,15 +1,15 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Parallel Patterns Library -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Parallel Patterns Library + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once @@ -56,10 +56,10 @@ // conditional expression is constant #if defined(_MSC_VER) #pragma warning(push) -#pragma warning(disable: 4127) +#pragma warning(disable : 4127) #endif -#pragma pack(push,_CRT_PACKING) +#pragma pack(push, _CRT_PACKING) /// /// The pplx namespace provides classes and functions that give you access to the Concurrency Runtime, @@ -68,7 +68,6 @@ /**/ namespace pplx { - /// /// Sets the ambient scheduler to be used by the PPL constructs. /// @@ -81,137 +80,124 @@ _PPLXIMP std::shared_ptr _pplx_cdecl get_ambient_sche 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; - }; +// +// 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() {} +}; - struct _TaskProcHandle - { - _TaskProcHandle() - { - } +template +struct _AutoDeleter +{ + _AutoDeleter(_T* _PPtr) : _Ptr(_PPtr) {} + ~_AutoDeleter() { delete _Ptr; } + _T* _Ptr; +}; - virtual ~_TaskProcHandle() {} - virtual void invoke() const = 0; +struct _TaskProcHandle +{ + _TaskProcHandle() {} - static void _pplx_cdecl _RunChoreBridge(void * _Parameter) - { - auto _PTaskHandle = static_cast<_TaskProcHandle *>(_Parameter); - _AutoDeleter<_TaskProcHandle> _AutoDeleter(_PTaskHandle); - _PTaskHandle->invoke(); - } - }; + virtual ~_TaskProcHandle() {} + virtual void invoke() const = 0; - enum _TaskInliningMode + static void _pplx_cdecl _RunChoreBridge(void* _Parameter) { - // 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: + auto _PTaskHandle = static_cast<_TaskProcHandle*>(_Parameter); + _AutoDeleter<_TaskProcHandle> _AutoDeleter(_PTaskHandle); + _PTaskHandle->invoke(); + } +}; - typedef _TaskProcHandle _TaskProcHandle_t; +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) - { - } + _TaskCollectionImpl(scheduler_ptr _PScheduler) : _M_pScheduler(_PScheduler) {} - void _ScheduleTask(_TaskProcHandle_t* _PTaskHandle, _TaskInliningMode _InliningMode) + void _ScheduleTask(_TaskProcHandle_t* _PTaskHandle, _TaskInliningMode _InliningMode) + { + if (_InliningMode == _ForceInline) { - if (_InliningMode == _ForceInline) - { - _TaskProcHandle_t::_RunChoreBridge(_PTaskHandle); - } - else - { - _M_pScheduler->schedule(_TaskProcHandle_t::_RunChoreBridge, _PTaskHandle); - } + _TaskProcHandle_t::_RunChoreBridge(_PTaskHandle); } - - void _Cancel() + else { - // No cancellation support + _M_pScheduler->schedule(_TaskProcHandle_t::_RunChoreBridge, _PTaskHandle); } + } - void _RunAndWait() - { - // No inlining support yet - _Wait(); - } + void _Cancel() + { + // No cancellation support + } - void _Wait() - { - _M_Completed.wait(); - } + void _RunAndWait() + { + // No inlining support yet + _Wait(); + } - void _Complete() - { - _M_Completed.set(); - } + void _Wait() { _M_Completed.wait(); } - scheduler_ptr _GetScheduler() const - { - return _M_pScheduler; - } + void _Complete() { _M_Completed.set(); } - // Fire and forget - static void _RunTask(TaskProc_t _Proc, void * _Parameter, _TaskInliningMode _InliningMode) + scheduler_ptr _GetScheduler() const { return _M_pScheduler; } + + // Fire and forget + static void _RunTask(TaskProc_t _Proc, void* _Parameter, _TaskInliningMode _InliningMode) + { + if (_InliningMode == _ForceInline) { - if (_InliningMode == _ForceInline) - { - _Proc(_Parameter); - } - else - { - // Schedule the work on the ambient scheduler - get_ambient_scheduler()->schedule(_Proc, _Parameter); - } + _Proc(_Parameter); } - - static bool _pplx_cdecl _Is_cancellation_requested() + else { - // We do not yet have the ability to determine the current task. So return false always - return false; + // Schedule the work on the ambient scheduler + get_ambient_scheduler()->schedule(_Proc, _Parameter); } - 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 {}; + 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; +typedef _TaskCollectionImpl _TaskCollection_t; +typedef _TaskInliningMode _TaskInliningMode_t; +typedef _Task_generator_oversubscriber _Task_generator_oversubscriber_t; } // namespace details diff --git a/Release/include/pplx/pplxcancellation_token.h b/Release/include/pplx/pplxcancellation_token.h index 634e4fafb3..b7ad1a07a2 100644 --- a/Release/include/pplx/pplxcancellation_token.h +++ b/Release/include/pplx/pplxcancellation_token.h @@ -1,13 +1,13 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Parallel Patterns Library : cancellation_token -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Parallel Patterns Library : cancellation_token + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once @@ -22,18 +22,17 @@ #error This file must not be included for Visual Studio 12 or later #endif +#include "pplx/pplxinterface.h" #include #include -#include "pplx/pplxinterface.h" -#pragma pack(push,_CRT_PACKING) +#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 @@ -55,26 +54,17 @@ class task_canceled : public std::exception /// A descriptive message of the error. /// /**/ - explicit task_canceled(_In_z_ const char * _Message) throw() - : _message(_Message) - { - } + explicit task_canceled(_In_z_ const char* _Message) throw() : _message(_Message) {} /// /// Constructs a task_canceled object. /// /**/ - task_canceled() throw() - : exception() - { - } + task_canceled() throw() : exception() {} - ~task_canceled() throw () {} + ~task_canceled() throw() {} - const char* what() const CPPREST_NOEXCEPT - { - return _message.c_str(); - } + const char* what() const CPPREST_NOEXCEPT { return _message.c_str(); } }; /// @@ -82,7 +72,8 @@ class task_canceled : public std::exception /// 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. +/// The various methods which throw this exception will generally document under what circumstances they will throw +/// it. /// /**/ class invalid_operation : public std::exception @@ -98,334 +89,287 @@ class invalid_operation : public std::exception /// A descriptive message of the error. /// /**/ - invalid_operation(_In_z_ const char * _Message) throw() - : _message(_Message) - { - } + invalid_operation(_In_z_ const char* _Message) throw() : _message(_Message) {} /// /// Constructs an invalid_operation object. /// /**/ - invalid_operation() throw() - : exception() - { - } - - ~invalid_operation() throw () {} + invalid_operation() throw() : exception() {} - const char* what() const CPPREST_NOEXCEPT - { - return _message.c_str(); - } + ~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); } - // Base class for all reference counted objects - class _RefCounter + // Acquires a reference + // Returns the new reference count. + long _Reference() { - 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(); - } + long _Refcount = atomic_increment(_M_refCount); - return _Refcount; - } + // 0 - 1 transition is illegal + _ASSERTE(_Refcount > 1); + return _Refcount; + } - protected: + // Releases the reference + // Returns the new reference count + long _Release() + { + long _Refcount = atomic_decrement(_M_refCount); + _ASSERTE(_Refcount >= 0); - // Allow derived classes to provide their own deleter - virtual void _Destroy() + if (_Refcount == 0) { - delete this; + _Destroy(); } - // Only allow instantiation through derived class - _RefCounter(long _InitialCount = 1) : _M_refCount(_InitialCount) - { - _ASSERTE(_M_refCount > 0); - } + return _Refcount; + } - // Reference count - atomic_long _M_refCount; - }; +protected: + // Allow derived classes to provide their own deleter + virtual void _Destroy() { delete this; } - class _CancellationTokenState; + // Only allow instantiation through derived class + _RefCounter(long _InitialCount = 1) : _M_refCount(_InitialCount) { _ASSERTE(_M_refCount > 0); } - class _CancellationTokenRegistration : public _RefCounter - { - private: + // Reference count + atomic_long _M_refCount; +}; - 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; +class _CancellationTokenState; - public: +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; - _CancellationTokenRegistration(long _InitialRefs = 1) : - _RefCounter(_InitialRefs), - _M_state(_STATE_CALLED), - _M_pTokenState(NULL) - { - } +public: + _CancellationTokenRegistration(long _InitialRefs = 1) + : _RefCounter(_InitialRefs), _M_state(_STATE_CALLED), _M_pTokenState(NULL) + { + } - _CancellationTokenState *_GetToken() const - { - return _M_pTokenState; - } + _CancellationTokenState* _GetToken() const { return _M_pTokenState; } - protected: +protected: + virtual ~_CancellationTokenRegistration() { _ASSERTE(_M_state != _STATE_CLEAR); } - virtual ~_CancellationTokenRegistration() - { - _ASSERTE(_M_state != _STATE_CLEAR); - } + virtual void _Exec() = 0; - virtual void _Exec() = 0; +private: + friend class _CancellationTokenState; - private: + void _Invoke() + { + long tid = ::pplx::details::platform::GetCurrentThreadId(); + _ASSERTE((tid & 0x3) == 0); // If this ever fires, we need a different encoding for this. - friend class _CancellationTokenState; + long result = atomic_compare_exchange(_M_state, tid, _STATE_CLEAR); - void _Invoke() + if (result == _STATE_CLEAR) { - long tid = ::pplx::details::platform::GetCurrentThreadId(); - _ASSERTE((tid & 0x3) == 0); // If this ever fires, we need a different encoding for this. + _Exec(); - long result = atomic_compare_exchange(_M_state, tid, _STATE_CLEAR); + result = atomic_compare_exchange(_M_state, _STATE_CALLED, tid); - if (result == _STATE_CLEAR) + if (result == _STATE_SYNCHRONIZE) { - _Exec(); - - result = atomic_compare_exchange(_M_state, _STATE_CALLED, tid); - - if (result == _STATE_SYNCHRONIZE) - { - _M_pSyncBlock->set(); - } + _M_pSyncBlock->set(); } - _Release(); } + _Release(); + } - atomic_long _M_state; - extensibility::event_t *_M_pSyncBlock; - _CancellationTokenState *_M_pTokenState; - }; + atomic_long _M_state; + extensibility::event_t* _M_pSyncBlock; + _CancellationTokenState* _M_pTokenState; +}; - template - class _CancellationTokenCallback : public _CancellationTokenRegistration - { - public: +template +class _CancellationTokenCallback : public _CancellationTokenRegistration +{ +public: + _CancellationTokenCallback(const _Function& _Func) : _M_function(_Func) {} - _CancellationTokenCallback(const _Function& _Func) : - _M_function(_Func) - { - } +protected: + virtual void _Exec() { _M_function(); } - protected: +private: + _Function _M_function; +}; - virtual void _Exec() - { - _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) + { + } - private: +protected: + virtual void _Exec() { m_proc(m_pData); } - _Function _M_function; - }; +private: + TaskProc_t m_proc; + void* m_pData; +}; - class CancellationTokenRegistration_TaskProc : public _CancellationTokenRegistration +// 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) {} - CancellationTokenRegistration_TaskProc(TaskProc_t proc, _In_ void *pData, int initialRefs) : - _CancellationTokenRegistration(initialRefs), m_proc(proc), m_pData(pData) + ~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 } - protected: - - virtual void _Exec() + void swap(TokenRegistrationContainer& list) { - m_proc(m_pData); + std::swap(list._M_begin, _M_begin); + std::swap(list._M_last, _M_last); } - private: - - TaskProc_t m_proc; - void *m_pData; - - }; + bool empty() { return _M_begin == nullptr; } - // The base implementation of a cancellation token. - class _CancellationTokenState : public _RefCounter - { - protected: - class TokenRegistrationContainer + template + void for_each(T lambda) { - private: - typedef struct _Node { - _CancellationTokenRegistration* _M_token; - _Node *_M_next; - } Node; - - public: - TokenRegistrationContainer() : _M_begin(nullptr), _M_last(nullptr) + Node* node = _M_begin; + + while (node != nullptr) { + lambda(node->_M_token); + node = node->_M_next; } + } - ~TokenRegistrationContainer() + void push_back(_CancellationTokenRegistration* token) + { + Node* node = reinterpret_cast(::malloc(sizeof(Node))); + if (node == nullptr) { -#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 + throw ::std::bad_alloc(); } - void swap(TokenRegistrationContainer& list) + node->_M_token = token; + node->_M_next = nullptr; + + if (_M_begin == nullptr) { - std::swap(list._M_begin, _M_begin); - std::swap(list._M_last, _M_last); + _M_begin = node; } - - bool empty() + else { - return _M_begin == nullptr; + _M_last->_M_next = node; } - template - void for_each(T lambda) - { - Node* node = _M_begin; + _M_last = node; + } - while (node != nullptr) - { - lambda(node->_M_token); - node = node->_M_next; - } - } + void remove(_CancellationTokenRegistration* token) + { + Node* node = _M_begin; + Node* prev = nullptr; - void push_back(_CancellationTokenRegistration* token) + while (node != nullptr) { - Node* node = reinterpret_cast(::malloc(sizeof(Node))); - if (node == nullptr) + if (node->_M_token == token) { - throw ::std::bad_alloc(); - } + if (prev == nullptr) + { + _M_begin = node->_M_next; + } + else + { + prev->_M_next = node->_M_next; + } - node->_M_token = token; - node->_M_next = nullptr; + if (node->_M_next == nullptr) + { + _M_last = prev; + } - if (_M_begin == nullptr) - { - _M_begin = node; - } - else - { - _M_last->_M_next = node; + ::free(node); + break; } - _M_last = node; + prev = node; + node = node->_M_next; } + } - void remove(_CancellationTokenRegistration* token) - { - Node* node = _M_begin; - Node* prev = nullptr; + private: + Node* _M_begin; + Node* _M_last; + }; - 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; - } - } +public: + static _CancellationTokenState* _NewTokenState() { return new _CancellationTokenState(); } - private: - Node *_M_begin; - Node *_M_last; - }; + static _CancellationTokenState* _None() { return reinterpret_cast<_CancellationTokenState*>(2); } - public: + static bool _IsValid(_In_opt_ _CancellationTokenState* _PToken) { return (_PToken != NULL && _PToken != _None()); } - static _CancellationTokenState * _NewTokenState() - { - return new _CancellationTokenState(); - } - - static _CancellationTokenState *_None() - { - return reinterpret_cast<_CancellationTokenState *>(2); - } + _CancellationTokenState() : _M_stateFlag(0) {} - 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); } - ~_CancellationTokenState() + 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; { @@ -433,198 +377,157 @@ namespace details _M_registrations.swap(rundownList); } - rundownList.for_each([](_CancellationTokenRegistration * pRegistration) - { - pRegistration->_M_state = _CancellationTokenRegistration::_STATE_SYNCHRONIZE; - pRegistration->_Release(); - }); - } + rundownList.for_each([](_CancellationTokenRegistration* pRegistration) { pRegistration->_Invoke(); }); - bool _IsCanceled() const - { - return (_M_stateFlag != 0); + _M_stateFlag = 2; + _M_cancelComplete.set(); } + } - 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); - } + _CancellationTokenRegistration* _RegisterCallback(TaskProc_t _PCallback, _In_ void* _PData, int _InitialRefs = 1) + { + _CancellationTokenRegistration* pRegistration = + new CancellationTokenRegistration_TaskProc(_PCallback, _PData, _InitialRefs); + _RegisterCallback(pRegistration); + return pRegistration; + } - rundownList.for_each([](_CancellationTokenRegistration * pRegistration) - { - pRegistration->_Invoke(); - }); + 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); - _M_stateFlag = 2; - _M_cancelComplete.set(); + if (!_IsCanceled()) + { + invoke = false; + _M_registrations.push_back(_PRegistration); } } - _CancellationTokenRegistration *_RegisterCallback(TaskProc_t _PCallback, _In_ void *_PData, int _InitialRefs = 1) + if (invoke) { - _CancellationTokenRegistration *pRegistration = new CancellationTokenRegistration_TaskProc(_PCallback, _PData, _InitialRefs); - _RegisterCallback(pRegistration); - return pRegistration; + _PRegistration->_Invoke(); } + } - void _RegisterCallback(_In_ _CancellationTokenRegistration *_PRegistration) - { - _PRegistration->_M_state = _CancellationTokenRegistration::_STATE_CLEAR; - _PRegistration->_Reference(); - _PRegistration->_M_pTokenState = this; + void _DeregisterCallback(_In_ _CancellationTokenRegistration* _PRegistration) + { + bool synchronize = false; - bool invoke = true; + { + extensibility::scoped_critical_section_t _Lock(_M_listLock); - if (!_IsCanceled()) + // + // 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 canceling thread to guarantee + // that the cancellation is finished by the time we return from this method. + // + if (!_M_registrations.empty()) { - extensibility::scoped_critical_section_t _Lock(_M_listLock); - - if (!_IsCanceled()) - { - invoke = false; - _M_registrations.push_back(_PRegistration); - } + _M_registrations.remove(_PRegistration); + _PRegistration->_M_state = _CancellationTokenRegistration::_STATE_SYNCHRONIZE; + _PRegistration->_Release(); } - - if (invoke) + else { - _PRegistration->_Invoke(); + synchronize = true; } } - void _DeregisterCallback(_In_ _CancellationTokenRegistration *_PRegistration) + // + // 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) { - bool synchronize = false; + long result = atomic_compare_exchange(_PRegistration->_M_state, + _CancellationTokenRegistration::_STATE_DEFER_DELETE, + _CancellationTokenRegistration::_STATE_CLEAR); + switch (result) { - 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 + case _CancellationTokenRegistration::_STATE_CLEAR: + case _CancellationTokenRegistration::_STATE_CALLED: break; + case _CancellationTokenRegistration::_STATE_DEFER_DELETE: + case _CancellationTokenRegistration::_STATE_SYNCHRONIZE: _ASSERTE(false); break; + default: { - synchronize = true; - } - } + 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; + } - // - // 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 - ); + extensibility::event_t ev; + _PRegistration->_M_pSyncBlock = &ev; - 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); - } + long result_1 = + atomic_exchange(_PRegistration->_M_state, _CancellationTokenRegistration::_STATE_SYNCHRONIZE); - break; + 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; +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?? + // 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; + // Lock to protect the registrations list + extensibility::critical_section_t _M_listLock; - // The protected list of registrations - TokenRegistrationContainer _M_registrations; - }; + // 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. +/// 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() : - _M_pRegistration(NULL) - { - } + ~cancellation_token_registration() { _Clear(); } - ~cancellation_token_registration() - { - _Clear(); - } + cancellation_token_registration(const cancellation_token_registration& _Src) { _Assign(_Src._M_pRegistration); } - 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(cancellation_token_registration&& _Src) { _Move(_Src._M_pRegistration); } cancellation_token_registration& operator=(const cancellation_token_registration& _Src) { @@ -651,17 +554,13 @@ class cancellation_token_registration return _M_pRegistration == _Rhs._M_pRegistration; } - bool operator!=(const cancellation_token_registration& _Rhs) const - { - return !(operator==(_Rhs)); - } + 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) + + cancellation_token_registration(_In_ details::_CancellationTokenRegistration* _PRegistration) + : _M_pRegistration(_PRegistration) { } @@ -674,7 +573,7 @@ class cancellation_token_registration _M_pRegistration = NULL; } - void _Assign(_In_ details::_CancellationTokenRegistration *_PRegistration) + void _Assign(_In_ details::_CancellationTokenRegistration* _PRegistration) { if (_PRegistration != NULL) { @@ -683,26 +582,25 @@ class cancellation_token_registration _M_pRegistration = _PRegistration; } - void _Move(_In_ details::_CancellationTokenRegistration *&_PRegistration) + void _Move(_In_ details::_CancellationTokenRegistration*& _PRegistration) { _M_pRegistration = _PRegistration; _PRegistration = NULL; } - details::_CancellationTokenRegistration *_M_pRegistration; + 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. +/// 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; + typedef details::_CancellationTokenState* _ImplType; /// /// Returns a cancellation token which can never be subject to cancellation. @@ -710,20 +608,11 @@ class cancellation_token /// /// A cancellation token that cannot be canceled. /// - static cancellation_token none() - { - return cancellation_token(); - } + static cancellation_token none() { return cancellation_token(); } - cancellation_token(const cancellation_token& _Src) - { - _Assign(_Src._M_Impl); - } + cancellation_token(const cancellation_token& _Src) { _Assign(_Src._M_Impl); } - cancellation_token(cancellation_token&& _Src) - { - _Move(_Src._M_Impl); - } + cancellation_token(cancellation_token&& _Src) { _Move(_Src._M_Impl); } cancellation_token& operator=(const cancellation_token& _Src) { @@ -745,20 +634,11 @@ class cancellation_token return *this; } - bool operator==(const cancellation_token& _Src) const - { - return _M_Impl == _Src._M_Impl; - } + bool operator==(const cancellation_token& _Src) const { return _M_Impl == _Src._M_Impl; } - bool operator!=(const cancellation_token& _Src) const - { - return !(operator==(_Src)); - } + bool operator!=(const cancellation_token& _Src) const { return !(operator==(_Src)); } - ~cancellation_token() - { - _Clear(); - } + ~cancellation_token() { _Clear(); } /// /// Returns an indication of whether this token can be canceled or not. @@ -766,10 +646,7 @@ class cancellation_token /// /// An indication of whether this token can be canceled or not. /// - bool is_cancelable() const - { - return (_M_Impl != NULL); - } + bool is_cancelable() const { return (_M_Impl != NULL); } /// /// Returns true if the token has been canceled. @@ -777,14 +654,12 @@ class cancellation_token /// /// The value true if the token has been canceled; otherwise, the value false. /// - bool is_canceled() const - { - return (_M_Impl != NULL && _M_Impl->_IsCanceled()); - } + 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. + /// 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. @@ -793,10 +668,11 @@ class cancellation_token /// 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. + /// 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 @@ -807,43 +683,37 @@ class cancellation_token throw invalid_operation(); } #if defined(_MSC_VER) -#pragma warning(suppress: 28197) +#pragma warning(suppress : 28197) #endif - details::_CancellationTokenCallback<_Function> *_PCallback = new details::_CancellationTokenCallback<_Function>(_Func); + 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. + /// 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. + /// 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 _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); - } + static cancellation_token _FromImpl(_ImplType _Impl) { return cancellation_token(_Impl); } private: - friend class cancellation_token_source; _ImplType _M_Impl; @@ -866,19 +736,15 @@ class cancellation_token _M_Impl = _Impl; } - void _Move(_ImplType &_Impl) + void _Move(_ImplType& _Impl) { _M_Impl = _Impl; _Impl = NULL; } - cancellation_token() : - _M_Impl(NULL) - { - } + cancellation_token() : _M_Impl(NULL) {} - cancellation_token(_ImplType _Impl) : - _M_Impl(_Impl) + cancellation_token(_ImplType _Impl) : _M_Impl(_Impl) { if (_M_Impl == ::pplx::details::_CancellationTokenState::_None()) { @@ -898,26 +764,17 @@ class cancellation_token class cancellation_token_source { public: - - typedef ::pplx::details::_CancellationTokenState * _ImplType; + typedef ::pplx::details::_CancellationTokenState* _ImplType; /// - /// Constructs a new cancellation_token_source. The source can be used to flag cancellation of some cancelable operation. + /// 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() { _M_Impl = new ::pplx::details::_CancellationTokenState; } - cancellation_token_source(const cancellation_token_source& _Src) - { - _Assign(_Src._M_Impl); - } + 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(cancellation_token_source&& _Src) { _Move(_Src._M_Impl); } cancellation_token_source& operator=(const cancellation_token_source& _Src) { @@ -939,15 +796,9 @@ class cancellation_token_source 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 _M_Impl == _Src._M_Impl; } - bool operator!=(const cancellation_token_source& _Src) const - { - return !(operator==(_Src)); - } + bool operator!=(const cancellation_token_source& _Src) const { return !(operator==(_Src)); } ~cancellation_token_source() { @@ -964,31 +815,29 @@ class cancellation_token_source /// /// A cancellation token associated with this source. /// - cancellation_token get_token() const - { - return cancellation_token(_M_Impl); - } + 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 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. + /// 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) + static cancellation_token_source create_linked_source(cancellation_token& _Src) { cancellation_token_source newSource; - _Src.register_callback( [newSource](){ newSource.cancel(); } ); + _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. + /// 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. @@ -997,8 +846,9 @@ class cancellation_token_source /// 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. + /// 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) @@ -1006,32 +856,22 @@ class cancellation_token_source cancellation_token_source newSource; for (_Iter _It = _Begin; _It != _End; ++_It) { - _It->register_callback( [newSource](){ newSource.cancel(); } ); + _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. + /// 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(); - } + void cancel() const { _M_Impl->_Cancel(); } - _ImplType _GetImpl() const - { - return _M_Impl; - } + _ImplType _GetImpl() const { return _M_Impl; } - static cancellation_token_source _FromImpl(_ImplType _Impl) - { - return cancellation_token_source(_Impl); - } + static cancellation_token_source _FromImpl(_ImplType _Impl) { return cancellation_token_source(_Impl); } private: - _ImplType _M_Impl; void _Clear() @@ -1052,14 +892,13 @@ class cancellation_token_source _M_Impl = _Impl; } - void _Move(_ImplType &_Impl) + void _Move(_ImplType& _Impl) { _M_Impl = _Impl; _Impl = NULL; } - cancellation_token_source(_ImplType _Impl) : - _M_Impl(_Impl) + cancellation_token_source(_ImplType _Impl) : _M_Impl(_Impl) { if (_M_Impl == ::pplx::details::_CancellationTokenState::_None()) { diff --git a/Release/include/pplx/pplxconv.h b/Release/include/pplx/pplxconv.h index 9023064aed..723d42fc83 100644 --- a/Release/include/pplx/pplxconv.h +++ b/Release/include/pplx/pplxconv.h @@ -1,15 +1,15 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Utilities to convert between PPL tasks and PPLX tasks -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Utilities to convert between PPL tasks and PPLX tasks + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once @@ -22,18 +22,25 @@ #if defined(_MSC_VER) && (_MSC_VER >= 1700) && (_MSC_VER < 1800) && !CPPREST_FORCE_PPLX -#include #include "pplx/pplxtasks.h" +#include namespace pplx { namespace _Ppl_conv_helpers { template -auto _Set_value(_Tc _Tcp, const _F& _Func) -> decltype(_Tcp.set(_Func())) { return _Tcp.set(_Func()); } +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(); } +auto _Set_value(_Tc _Tcp, const _F& _Func, ...) -> decltype(_Tcp.set()) +{ + _Func(); + return _Tcp.set(); +} template _OtherTaskType _Convert_task(_TaskType _Task) @@ -42,9 +49,9 @@ _OtherTaskType _Convert_task(_TaskType _Task) _Task.then([_Tc](_TaskType _Task2) { try { - _Ppl_conv_helpers::_Set_value(_Tc, [=]{ return _Task2.get(); }); + _Ppl_conv_helpers::_Set_value(_Tc, [=] { return _Task2.get(); }); } - catch(...) + catch (...) { _Tc.set_exception(std::current_exception()); } @@ -52,18 +59,22 @@ _OtherTaskType _Convert_task(_TaskType _Task) _OtherTaskType _T_other(_Tc); return _T_other; } -} +} // namespace _Ppl_conv_helpers 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); + 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); + return _Ppl_conv_helpers::_Convert_task, + pplx::task<_TaskType>, + pplx::task_completion_event<_TaskType>>(_Task); } } // namespace pplx diff --git a/Release/include/pplx/pplxinterface.h b/Release/include/pplx/pplxinterface.h index 5df5777182..4e5ca5bfc6 100644 --- a/Release/include/pplx/pplxinterface.h +++ b/Release/include/pplx/pplxinterface.h @@ -1,15 +1,15 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* PPL interfaces -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * PPL interfaces + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once @@ -38,20 +38,19 @@ 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. +/// 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 *); +typedef void(_pplx_cdecl* TaskProc_t)(void*); /// /// Scheduler Interface /// struct __declspec(novtable) scheduler_interface { - virtual void schedule( TaskProc_t, _In_ void* ) = 0; + virtual void schedule(TaskProc_t, _In_ void*) = 0; }; /// @@ -72,25 +71,17 @@ struct scheduler_ptr /// /// Creates a scheduler pointer from raw pointer to scheduler /// - explicit scheduler_ptr(_In_opt_ scheduler_interface * pScheduler) : m_scheduler(pScheduler) - { - } + explicit scheduler_ptr(_In_opt_ scheduler_interface* pScheduler) : m_scheduler(pScheduler) {} /// /// Behave like a pointer /// - scheduler_interface *operator->() const - { - return get(); - } + scheduler_interface* operator->() const { return get(); } /// /// Returns the raw pointer to the scheduler /// - scheduler_interface * get() const - { - return m_scheduler; - } + scheduler_interface* get() const { return m_scheduler; } /// /// Test whether the scheduler pointer is non-null @@ -98,15 +89,13 @@ struct scheduler_ptr operator bool() const { return get() != nullptr; } private: - std::shared_ptr m_sharedScheduler; - scheduler_interface * m_scheduler; + 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. +/// 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. /// /// /// @@ -118,8 +107,8 @@ struct scheduler_ptr 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. + /// 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, @@ -131,7 +120,8 @@ enum task_group_status completed, /// - /// The task_group or structured_task_group object was canceled. One or more tasks may not have executed. + /// The task_group or structured_task_group object was canceled. One or more tasks may not have + /// executed. /// /**/ canceled @@ -189,54 +179,49 @@ 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_increment(long volatile& _Target) { return _InterlockedIncrement(&_Target); } -inline long atomic_add(long volatile & _Target, long value) -{ - return _InterlockedExchangeAdd(&_Target, value) + value; -} +inline long atomic_add(long volatile& _Target, long value) { return _InterlockedExchangeAdd(&_Target, value) + value; } -inline size_t atomic_increment(size_t volatile & _Target) +inline size_t atomic_increment(size_t volatile& _Target) { #if (defined(_M_IX86) || defined(_M_ARM)) - return static_cast(_InterlockedIncrement(reinterpret_cast(&_Target))); + return static_cast(_InterlockedIncrement(reinterpret_cast(&_Target))); #else - return static_cast(_InterlockedIncrement64(reinterpret_cast<__int64 volatile *>(&_Target))); + return static_cast(_InterlockedIncrement64(reinterpret_cast<__int64 volatile*>(&_Target))); #endif } -inline long atomic_decrement(long volatile & _Target) -{ - return _InterlockedDecrement(&_Target); -} +inline long atomic_decrement(long volatile& _Target) { return _InterlockedDecrement(&_Target); } -inline size_t atomic_decrement(size_t volatile & _Target) +inline size_t atomic_decrement(size_t volatile& _Target) { #if (defined(_M_IX86) || defined(_M_ARM)) - return static_cast(_InterlockedDecrement(reinterpret_cast(&_Target))); + return static_cast(_InterlockedDecrement(reinterpret_cast(&_Target))); #else - return static_cast(_InterlockedDecrement64(reinterpret_cast<__int64 volatile *>(&_Target))); + return static_cast(_InterlockedDecrement64(reinterpret_cast<__int64 volatile*>(&_Target))); #endif } -inline long atomic_compare_exchange(long volatile & _Target, long _Exchange, long _Comparand) +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) +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))); + 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))); + return static_cast(_InterlockedCompareExchange64(reinterpret_cast<__int64 volatile*>(_Target), + static_cast<__int64>(_Exchange), + static_cast<__int64>(_Comparand))); #endif } #endif // _USE_REAL_ATOMICS -}} // namespace pplx +} // namespace details +} // namespace pplx #endif // _PPLXINTERFACE_H diff --git a/Release/include/pplx/pplxlinux.h b/Release/include/pplx/pplxlinux.h index 6aa1ba352e..5aca316e45 100644 --- a/Release/include/pplx/pplxlinux.h +++ b/Release/include/pplx/pplxlinux.h @@ -1,246 +1,219 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Linux specific pplx implementations -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Linux specific pplx implementations + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #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" +#include "pthread.h" +#include #if defined(__APPLE__) -#include -#include #include +#include +#include #else -#include +#include #include +#include #endif #include "pplx/pplxinterface.h" - namespace pplx { #if defined(__APPLE__) - namespace cpprest_synchronization = ::boost; +namespace cpprest_synchronization = ::boost; #else - namespace cpprest_synchronization = ::std; +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) +/// +/// 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(); + +/// +/// Captures the callstack +/// +__declspec(noinline) inline static size_t CaptureCallstack(void**, size_t, size_t) { return 0; } +} // namespace platform + +/// +/// 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() { - return 0; + cpprest_synchronization::lock_guard lock(_lock); + _signaled = true; + _condition.notify_all(); } -} - - /// - /// 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; + void reset() + { + cpprest_synchronization::lock_guard lock(_lock); + _signaled = false; + } - event_impl() - : _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; } - - void set() + else { - cpprest_synchronization::lock_guard lock(_lock); - _signaled = true; - _condition.notify_all(); + 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; } + } - void reset() - { - cpprest_synchronization::lock_guard lock(_lock); - _signaled = false; - } + unsigned int wait() { return wait(event_impl::timeout_infinite); } +}; - 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; - } - } +/// +/// Reader writer lock +/// +class reader_writer_lock_impl +{ +private: + pthread_rwlock_t _M_reader_writer_lock; - unsigned int wait() +public: + class scoped_lock_read + { + public: + explicit scoped_lock_read(reader_writer_lock_impl& _Reader_writer_lock) + : _M_reader_writer_lock(_Reader_writer_lock) { - return wait(event_impl::timeout_infinite); + _M_reader_writer_lock.lock_read(); } - }; - /// - /// Reader writer lock - /// - class reader_writer_lock_impl - { - private: + ~scoped_lock_read() { _M_reader_writer_lock.unlock(); } - pthread_rwlock_t _M_reader_writer_lock; + 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 + }; - public: + reader_writer_lock_impl() { pthread_rwlock_init(&_M_reader_writer_lock, nullptr); } - 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); } - ~reader_writer_lock_impl() - { - pthread_rwlock_destroy(&_M_reader_writer_lock); - } + void lock() { pthread_rwlock_wrlock(&_M_reader_writer_lock); } - void lock() - { - pthread_rwlock_wrlock(&_M_reader_writer_lock); - } + void lock_read() { pthread_rwlock_rdlock(&_M_reader_writer_lock); } - void lock_read() - { - pthread_rwlock_rdlock(&_M_reader_writer_lock); - } + void unlock() { pthread_rwlock_unlock(&_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 mutex - /// - class recursive_lock_impl + ~recursive_lock_impl() { - public: + _ASSERTE(_M_owner == -1); + _ASSERTE(_M_recursionCount == 0); + } - recursive_lock_impl() - : _M_owner(-1), _M_recursionCount(0) - { - } + void lock() + { + auto id = ::pplx::details::platform::GetCurrentThreadId(); - ~recursive_lock_impl() + if (_M_owner == id) { - _ASSERTE(_M_owner == -1); - _ASSERTE(_M_recursionCount == 0); + _M_recursionCount++; } - - void lock() + else { - auto id = ::pplx::details::platform::GetCurrentThreadId(); - - if ( _M_owner == id ) - { - _M_recursionCount++; - } - else - { - _M_cs.lock(); - _M_owner = id; - _M_recursionCount = 1; - } + _M_cs.lock(); + _M_owner = id; + _M_recursionCount = 1; } + } - void unlock() - { - _ASSERTE(_M_owner == ::pplx::details::platform::GetCurrentThreadId()); - _ASSERTE(_M_recursionCount >= 1); + void unlock() + { + _ASSERTE(_M_owner == ::pplx::details::platform::GetCurrentThreadId()); + _ASSERTE(_M_recursionCount >= 1); - _M_recursionCount--; + _M_recursionCount--; - if ( _M_recursionCount == 0 ) - { - _M_owner = -1; - _M_cs.unlock(); - } + if (_M_recursionCount == 0) + { + _M_owner = -1; + _M_cs.unlock(); } + } - private: - cpprest_synchronization::mutex _M_cs; - volatile long _M_owner; - long _M_recursionCount; - }; +private: + cpprest_synchronization::mutex _M_cs; + std::atomic _M_owner; + long _M_recursionCount; +}; #if defined(__APPLE__) - class apple_scheduler : public pplx::scheduler_interface +class apple_scheduler : public pplx::scheduler_interface #else - class linux_scheduler : public pplx::scheduler_interface +class linux_scheduler : public pplx::scheduler_interface #endif - { - public: - _PPLXIMP virtual void schedule( TaskProc_t proc, _In_ void* param); - }; +{ +public: + _PPLXIMP virtual void schedule(TaskProc_t proc, _In_ void* param); +#if defined(__APPLE__) + virtual ~apple_scheduler() {} +#else + virtual ~linux_scheduler() {} +#endif +}; } // namespace details @@ -257,60 +230,58 @@ class scoped_lock _M_critical_section.lock(); } - ~scoped_lock() - { - _M_critical_section.unlock(); - } + ~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 + 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::event_impl event_t; - typedef cpprest_synchronization::mutex critical_section_t; - typedef scoped_lock scoped_critical_section_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::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; -} +typedef ::pplx::details::recursive_lock_impl recursive_lock_t; +typedef scoped_lock scoped_recursive_lock_t; +} // namespace extensibility /// /// Default scheduler type /// #if defined(__APPLE__) - typedef details::apple_scheduler default_scheduler_t; +typedef details::apple_scheduler default_scheduler_t; #else - typedef details::linux_scheduler default_scheduler_t; +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 +/// +/// 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 +} // namespace details + +// 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); } +__attribute__((always_inline)) inline void* _ReturnAddress() { return __builtin_return_address(0); } } // namespace pplx diff --git a/Release/include/pplx/pplxtasks.h b/Release/include/pplx/pplxtasks.h index e784306d40..6868fc1619 100644 --- a/Release/include/pplx/pplxtasks.h +++ b/Release/include/pplx/pplxtasks.h @@ -1,7336 +1,7600 @@ -/*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Parallel Patterns Library - PPLx Tasks -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ - -#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) -#include -#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 makes only movable functions be able to be passed to std::function -/// - template - struct _NonCopyableFunctorWrapper - { - template, - typename std::decay<_Tx>::type>::value>::type> - explicit _NonCopyableFunctorWrapper(_Tx&& f) - : _M_functor{std::make_shared<_Ty>(std::forward<_Tx>(f))} - {} - - template - auto operator()(_Args&&... args) -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...)) - { - return _M_functor->operator()(std::forward<_Args>(args)...); - } - - template - auto operator()(_Args&&... args) const -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...)) - { - return _M_functor->operator()(std::forward<_Args>(args)...); - } - - std::shared_ptr<_Ty> _M_functor; - }; - - template - struct _CopyableFunctor - { - typedef _Ty _Type; - }; - - template - struct _CopyableFunctor<_Ty, typename std::enable_if< - std::is_move_constructible<_Ty>::value && !std::is_copy_constructible<_Ty>::value>::type> - { - typedef _NonCopyableFunctorWrapper<_Ty> _Type; - }; -} -/// -/// 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(_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>(std::forward<_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(_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>(std::forward<_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(_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>(std::forward<_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(_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>(std::forward<_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; - typename details::_CopyableFunctor::type >::_Type _M_function; - - template - _ContinuationTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _AncestorImpl, - const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type & _ContinuationImpl, - _ForwardedFunction&& _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(std::forward<_ForwardedFunction>(_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(_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>(std::forward<_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(_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(), std::forward<_Function>(_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(_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(std::forward<_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(_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(std::forward<_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 - { - 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(_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(std::forward<_Function>(_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 -auto operator&&(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) -> decltype(when_all(&_Lhs, &_Lhs)) -{ - 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 -auto operator&&(const task> & _Lhs, const task<_ReturnType> & _Rhs) -> decltype(details::_WhenAllVectorAndValue(_Lhs, _Rhs, true)) -{ - 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 -auto operator&&(const task<_ReturnType> & _Lhs, const task> & _Rhs) -> decltype(details::_WhenAllVectorAndValue(_Rhs, _Lhs, false)) -{ - 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 -auto operator&&(const task> & _Lhs, const task> & _Rhs) -> decltype(when_all(&_Lhs, &_Lhs)) -{ - 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 -auto operator||(const task<_ReturnType> & _Lhs, const task> & _Rhs) -> decltype(_Rhs || _Lhs) -{ - 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. -/// -/// -/**/ -template, typename _Pair = std::pair> -_Ty operator||(const task & _Lhs_arg, const task & _Rhs_arg) -{ - const _Ty& _Lhs = _Lhs_arg; - const _Ty& _Rhs = _Rhs_arg; - auto _PParam = new details::_RunAnyParam<_Pair>(); - - 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([=](_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](_Ty _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); -} - -template -inline task<_Ty> task_from_result(const task_options& _TaskOptions = task_options()) -{ - task_completion_event<_Ty> _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 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 - +/*** + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Parallel Patterns Library - PPLx Tasks + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ +#pragma once + +#ifndef PPLXTASKS_H +#define PPLXTASKS_H + +#include "cpprest/details/cpprest_compat.h" + +#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX +#include +namespace pplx = Concurrency; + +namespace Concurrency +{ +/// +/// Sets the ambient scheduler to be used by the PPL constructs. +/// +_ASYNCRTIMP void __cdecl set_cpprestsdk_ambient_scheduler(const std::shared_ptr& _Scheduler); + +/// +/// Gets the ambient scheduler to be used by the PPL constructs +/// +_ASYNCRTIMP const std::shared_ptr& __cdecl get_cpprestsdk_ambient_scheduler(); + +} // namespace 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; +} // namespace extensibility +} // namespace Concurrency +#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 +#include + +#if defined(_MSC_VER) +#include +#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("PPLXTASKS_WITH_WINRT", "1") +#else /* defined(__cplusplus_winrt) */ +#pragma detect_mismatch("PPLXTASKS_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); +} +} // namespace std +#endif /* _MSC_VER < 1700 */ +#ifndef PPLX_TASK_ASYNC_LOGGING +#if _MSC_VER >= 1800 && defined(__cplusplus_winrt) +#define PPLX_TASK_ASYNC_LOGGING 1 // Only enable async logging under dev12 winrt +#else +#define PPLX_TASK_ASYNC_LOGGING 0 +#endif +#endif /* !PPLX_TASK_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 PPLX_TASK_SAVE_FRAME_COUNT +#ifdef _DEBUG +#define PPLX_TASK_SAVE_FRAME_COUNT 10 +#else +#define PPLX_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 PPLX_TASK_SAVE_FRAME_COUNT > 1 +#if defined(__cplusplus_winrt) && !defined(_DEBUG) +#pragma message( \ + "WARNING: Redefining PPLX_TASK_SAVE_FRAME_COUNT under Release build for non-desktop applications is not supported; only one frame will be captured!") +#define PPLX_CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) +#else +#define PPLX_CAPTURE_CALLSTACK() \ + ::pplx::details::_TaskCreationCallstack::_CaptureMultiFramesCallstack(PPLX_TASK_SAVE_FRAME_COUNT) +#endif +#else +#define PPLX_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 cancelable 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< + typename _TaskTypeTraits::_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 apartment 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 apartment + 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 apartment if necessary + } + + 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 specify 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); +} // namespace details +/// +/// 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 PPLX_TASK_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 cancellation 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 marshaled 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 canceled, either work chore or the cancel thread should already have set task's + // state properly -- canceled 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 exception 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 funneling 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 + std::atomic<_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 PPLX_TASK_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 canceling 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; + std::atomic _M_fHasValue; + std::atomic _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 canceled 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 PPLX_CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for + // set_exception. + return _Cancel(std::make_exception_ptr(_Except), PPLX_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 PPLX_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 PPLX_CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for + // set_exception. + return _Cancel(_ExceptionPtr, PPLX_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), PPLX_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 PPLX_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 PPLX_CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for + // set_exception. + return _M_unitEvent._Cancel(_ExceptionPtr, PPLX_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 makes only movable functions be able to be passed to std::function +/// +template +struct _NonCopyableFunctorWrapper +{ + template, typename std::decay<_Tx>::type>::value>::type> + explicit _NonCopyableFunctorWrapper(_Tx&& f) : _M_functor {std::make_shared<_Ty>(std::forward<_Tx>(f))} + { + } + + template + auto operator()(_Args&&... args) -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...)) + { + return _M_functor->operator()(std::forward<_Args>(args)...); + } + + template + auto operator()(_Args&&... args) const -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...)) + { + return _M_functor->operator()(std::forward<_Args>(args)...); + } + + std::shared_ptr<_Ty> _M_functor; +}; + +template +struct _CopyableFunctor +{ + typedef _Ty _Type; +}; + +template +struct _CopyableFunctor< + _Ty, + typename std::enable_if::value && !std::is_copy_constructible<_Ty>::value>::type> +{ + typedef _NonCopyableFunctorWrapper<_Ty> _Type; +}; +} // namespace details +/// +/// 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 lambda that returns a task<result_type> reach their terminal + /// state when the inner task reaches its terminal state, and not when the lambda 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 lambda that returns a task<result_type> reach their terminal + /// state when the inner task reaches its terminal state, and not when the lambda 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 PPLX_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 PPLX_CAPTURE_CALLSTACK() evaluate to the + // the call site of the task constructor. + _SetTaskCreationCallstack(PPLX_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 lambda that returns a task<result_type> reach their terminal + /// state when the inner task reaches its terminal state, and not when the lambda 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 PPLX_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 PPLX_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 + : PPLX_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 lambda that returns a task<result_type> reach their terminal + /// state when the inner task reaches its terminal state, and not when the lambda 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 lambda that returns a task<result_type> reach their terminal + /// state when the inner task reaches its terminal state, and not when the lambda 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 PPLX_CAPTURE_CALLSTACK gives us the expected result + auto then(_Function&& _Func) const -> + typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + task_options _TaskOptions; + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(std::forward<_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 PPLX_CAPTURE_CALLSTACK gives us the expected result + auto then(_Function&& _Func, task_options _TaskOptions) const -> + typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(std::forward<_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 PPLX_CAPTURE_CALLSTACK gives us the expected result + auto then(_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(PPLX_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(std::forward<_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 implementation. + /// + 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 implementation 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(_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>(std::forward<_Function>(_Func), + _PTokenState, + task_continuation_context::use_default(), + _Scheduler, + PPLX_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); + (void)_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 under /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; + typename details::_CopyableFunctor::type>::_Type _M_function; + + template + _ContinuationTaskHandle( + const typename details::_Task_ptr<_ReturnType>::_Type& _AncestorImpl, + const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type& _ContinuationImpl, + _ForwardedFunction&& _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(std::forward<_ForwardedFunction>(_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); + (void)_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 under /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< + typename details::_ValueTypeOrRefType<_ReturnType>::_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(_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>(std::forward<_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(_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(), + std::forward<_Function>(_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 lambda that returns a task<result_type> reach their terminal + /// state when the inner task reaches its terminal state, and not when the lambda 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 lambda that returns a task<result_type> reach their terminal + /// state when the inner task reaches its terminal state, and not when the lambda 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 PPLX_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 PPLX_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 + : PPLX_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 lambda that returns a task<result_type> reach their terminal + /// state when the inner task reaches its terminal state, and not when the lambda 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 lambda that returns a task<result_type> reach their terminal + /// state when the inner task reaches its terminal state, and not when the lambda 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 PPLX_CAPTURE_CALLSTACK gives us the expected result + auto then(_Function&& _Func, task_options _TaskOptions = task_options()) const -> + typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK()); + return _M_unitTask._ThenImpl(std::forward<_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 PPLX_CAPTURE_CALLSTACK gives us the expected result + auto then(_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(PPLX_CAPTURE_CALLSTACK()); + return _M_unitTask._ThenImpl(std::forward<_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 { 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 implementation. + /// + void _SetImpl(const details::_Task_ptr::_Type& _Impl) { _M_unitTask._SetImpl(_Impl); } + + /// + /// Set the implementation of the task to be the supplied implementation 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(_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(std::forward<_Function>(_Func), + _PTokenState, + task_continuation_context::use_default(), + _Scheduler, + PPLX_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 supported 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(PPLX_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 +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 canceled. This way "pending canceled" looks to the outside as + // "canceled" 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 +ref class _AsyncProgressBase abstract : _AsyncInfoBase<_Attributes, _ResultType> +{ +}; + +template +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 asynchronous 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 lambda +/// 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 lambda 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, PPLX_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 successfully 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 encountered, 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 successfully 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 encountered, will be thrown if you call get() or wait() on +/// that task. +/// +/// +/**/ +template +auto operator&&(const task<_ReturnType>& _Lhs, const task<_ReturnType>& _Rhs) -> decltype(when_all(&_Lhs, &_Lhs)) +{ + task<_ReturnType> _PTasks[2] = {_Lhs, _Rhs}; + return when_all(_PTasks, _PTasks + 2); +} + +/// +/// Creates a task that will complete successfully 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 encountered, will be thrown if you call get() or wait() on +/// that task. +/// +/// +/**/ +template +auto operator&&(const task>& _Lhs, const task<_ReturnType>& _Rhs) + -> decltype(details::_WhenAllVectorAndValue(_Lhs, _Rhs, true)) +{ + return details::_WhenAllVectorAndValue(_Lhs, _Rhs, true); +} + +/// +/// Creates a task that will complete successfully 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 encountered, will be thrown if you call get() or wait() on +/// that task. +/// +/// +/**/ +template +auto operator&&(const task<_ReturnType>& _Lhs, const task>& _Rhs) + -> decltype(details::_WhenAllVectorAndValue(_Rhs, _Lhs, false)) +{ + return details::_WhenAllVectorAndValue(_Rhs, _Lhs, false); +} + +/// +/// Creates a task that will complete successfully 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 encountered, will be thrown if you call get() or wait() on +/// that task. +/// +/// +/**/ +template +auto operator&&(const task>& _Lhs, const task>& _Rhs) + -> decltype(when_all(&_Lhs, &_Lhs)) +{ + 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 successfully 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 successfully 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 successfully 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 +auto operator||(const task<_ReturnType>& _Lhs, const task>& _Rhs) -> decltype(_Rhs || _Lhs) +{ + 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 successfully 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, typename _Pair = std::pair> +_Ty operator||(const task& _Lhs_arg, const task& _Rhs_arg) +{ + const _Ty& _Lhs = _Lhs_arg; + const _Ty& _Rhs = _Rhs_arg; + auto _PParam = new details::_RunAnyParam<_Pair>(); + + 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( + [=](_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](_Ty _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); +} + +template +inline task<_Ty> task_from_result(const task_options& _TaskOptions = task_options()) +{ + task_completion_event<_Ty> _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 pplx + +#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/Release/include/pplx/pplxwin.h b/Release/include/pplx/pplxwin.h index faa49a59df..95a23b3158 100644 --- a/Release/include/pplx/pplxwin.h +++ b/Release/include/pplx/pplxwin.h @@ -1,15 +1,15 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Windows specific pplx implementations -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Windows specific pplx implementations + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once @@ -20,199 +20,184 @@ namespace pplx { - namespace details { - namespace platform - { - /// - /// Returns a unique identifier for the execution thread where this routine in invoked - /// - _PPLXIMP long __cdecl GetCurrentThreadId(); +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(); +/// +/// 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); +/// +/// 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(); +/// +// Internal API which retrieves the next async id. +/// +_PPLXIMP unsigned int __cdecl GetNextAsyncId(); #endif - } - - /// - /// Manual reset event - /// - class event_impl - { - public: +} // namespace platform - static const unsigned int timeout_infinite = 0xFFFFFFFF; - - _PPLXIMP event_impl(); - - _PPLXIMP ~event_impl(); +/// +/// Manual reset event +/// +class event_impl +{ +public: + static const unsigned int timeout_infinite = 0xFFFFFFFF; - _PPLXIMP void set(); + _PPLXIMP event_impl(); - _PPLXIMP void reset(); + _PPLXIMP ~event_impl(); - _PPLXIMP unsigned int wait(unsigned int timeout); + _PPLXIMP void set(); - unsigned int wait() - { - return wait(event_impl::timeout_infinite); - } + _PPLXIMP void reset(); - private: - // Windows events - void * _M_impl; + _PPLXIMP unsigned int wait(unsigned int timeout); - event_impl(const event_impl&); // no copy constructor - event_impl const & operator=(const event_impl&); // no assignment operator - }; + unsigned int wait() { return wait(event_impl::timeout_infinite); } - /// - /// Mutex - lock for mutual exclusion - /// - class critical_section_impl - { - public: +private: + // Windows events + void* _M_impl; - _PPLXIMP critical_section_impl(); + event_impl(const event_impl&); // no copy constructor + event_impl const& operator=(const event_impl&); // no assignment operator +}; - _PPLXIMP ~critical_section_impl(); +/// +/// Mutex - lock for mutual exclusion +/// +class critical_section_impl +{ +public: + _PPLXIMP critical_section_impl(); - _PPLXIMP void lock(); + _PPLXIMP ~critical_section_impl(); - _PPLXIMP void unlock(); + _PPLXIMP void lock(); - private: + _PPLXIMP void unlock(); - typedef void * _PPLX_BUFFER; +private: + typedef void* _PPLX_BUFFER; - // Windows critical section - _PPLX_BUFFER _M_impl[8]; + // 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 - }; + 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 +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA +/// +/// Reader writer lock +/// +class reader_writer_lock_impl +{ +public: + class scoped_lock_read { public: - - class scoped_lock_read + explicit scoped_lock_read(reader_writer_lock_impl& _Reader_writer_lock) + : _M_reader_writer_lock(_Reader_writer_lock) { - 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(); - } + _M_reader_writer_lock.lock_read(); + } - ~scoped_lock_read() - { - _M_reader_writer_lock.unlock(); - } + ~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 + }; - 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 reader_writer_lock_impl(); + _PPLXIMP void lock(); - _PPLXIMP void lock(); + _PPLXIMP void lock_read(); - _PPLXIMP void lock_read(); + _PPLXIMP void unlock(); - _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 - private: +/// +/// Recursive mutex +/// +class recursive_lock_impl +{ +public: + recursive_lock_impl() : _M_owner(-1), _M_recursionCount(0) {} - // 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 + ~recursive_lock_impl() { - public: + _ASSERTE(_M_owner == -1); + _ASSERTE(_M_recursionCount == 0); + } - recursive_lock_impl() - : _M_owner(-1), _M_recursionCount(0) - { - } + void lock() + { + auto id = ::pplx::details::platform::GetCurrentThreadId(); - ~recursive_lock_impl() + if (_M_owner == id) { - _ASSERTE(_M_owner == -1); - _ASSERTE(_M_recursionCount == 0); + _M_recursionCount++; } - - void lock() + else { - auto id = ::pplx::details::platform::GetCurrentThreadId(); - - if ( _M_owner == id ) - { - _M_recursionCount++; - } - else - { - _M_cs.lock(); - _M_owner = id; - _M_recursionCount = 1; - } + _M_cs.lock(); + _M_owner = id; + _M_recursionCount = 1; } + } - void unlock() - { - _ASSERTE(_M_owner == ::pplx::details::platform::GetCurrentThreadId()); - _ASSERTE(_M_recursionCount >= 1); + void unlock() + { + _ASSERTE(_M_owner == ::pplx::details::platform::GetCurrentThreadId()); + _ASSERTE(_M_recursionCount >= 1); - _M_recursionCount--; + _M_recursionCount--; - if ( _M_recursionCount == 0 ) - { - _M_owner = -1; - _M_cs.unlock(); - } + 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; - }; +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); - }; +class windows_scheduler : public pplx::scheduler_interface +{ +public: + _PPLXIMP virtual void schedule(TaskProc_t proc, _In_ void* param); +}; } // namespace details @@ -229,36 +214,32 @@ class scoped_lock _M_critical_section.lock(); } - ~scoped_lock() - { - _M_critical_section.unlock(); - } + ~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 + 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; +typedef ::pplx::details::event_impl event_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::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; -} +typedef ::pplx::details::recursive_lock_impl recursive_lock_t; +typedef scoped_lock scoped_recursive_lock_t; +} // namespace extensibility /// /// Default scheduler type @@ -267,19 +248,21 @@ typedef details::windows_scheduler default_scheduler_t; namespace details { - /// - /// Terminate the process due to unhandled exception - /// +/// +/// 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 +#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 +#endif diff --git a/Release/include/pplx/threadpool.h b/Release/include/pplx/threadpool.h index 919eb75552..b297ff6bc8 100644 --- a/Release/include/pplx/threadpool.h +++ b/Release/include/pplx/threadpool.h @@ -1,14 +1,14 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* -* Simple Linux implementation of a static thread pool. -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -***/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * + * Simple Linux implementation of a static thread pool. + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + ***/ #pragma once #if defined(__clang__) @@ -23,15 +23,15 @@ #endif #if defined(__ANDROID__) +#include "pplx/pplx.h" #include #include -#include "pplx/pplx.h" #endif #include "cpprest/details/cpprest_compat.h" -namespace crossplat { - +namespace crossplat +{ #if defined(__ANDROID__) // IDEA: Break this section into a separate android/jni header extern std::atomic JVM; @@ -39,10 +39,7 @@ JNIEnv* get_jvm_env(); struct java_local_ref_deleter { - void operator()(jobject lref) const - { - crossplat::get_jvm_env()->DeleteLocalRef(lref); - } + void operator()(jobject lref) const { crossplat::get_jvm_env()->DeleteLocalRef(lref); } }; template @@ -57,6 +54,17 @@ class threadpool virtual ~threadpool() = default; + /// + /// Initializes the cpprestsdk threadpool with a custom number of threads + /// + /// + /// This function allows an application (in their main function) to initialize the cpprestsdk + /// threadpool with a custom threadcount. Libraries should avoid calling this function to avoid + /// a diamond problem with multiple consumers attempting to customize the pool. + /// + /// Thrown if the threadpool has already been initialized + static void initialize_with_threads(size_t num_threads); + template CASABLANCA_DEPRECATED("Use `.service().post(task)` directly.") void schedule(T task) @@ -72,5 +80,4 @@ class threadpool boost::asio::io_service m_service; }; -} - +} // namespace crossplat diff --git a/Release/libs/websocketpp b/Release/libs/websocketpp new file mode 160000 index 0000000000..c6d7e295bf --- /dev/null +++ b/Release/libs/websocketpp @@ -0,0 +1 @@ +Subproject commit c6d7e295bf5a0ab9b5f896720cc1a0e0fdc397a7 diff --git a/Release/libs/websocketpp/.gitattributes b/Release/libs/websocketpp/.gitattributes deleted file mode 100644 index a9e4fc784b..0000000000 --- a/Release/libs/websocketpp/.gitattributes +++ /dev/null @@ -1,18 +0,0 @@ -# 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/Release/libs/websocketpp/.gitignore b/Release/libs/websocketpp/.gitignore deleted file mode 100644 index 558a1b3d7f..0000000000 --- a/Release/libs/websocketpp/.gitignore +++ /dev/null @@ -1,90 +0,0 @@ -# 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/Release/libs/websocketpp/.travis.yml b/Release/libs/websocketpp/.travis.yml deleted file mode 100644 index 027ac560ed..0000000000 --- a/Release/libs/websocketpp/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -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/Release/libs/websocketpp/CMakeLists.txt b/Release/libs/websocketpp/CMakeLists.txt deleted file mode 100644 index f8df9de08a..0000000000 --- a/Release/libs/websocketpp/CMakeLists.txt +++ /dev/null @@ -1,247 +0,0 @@ - -############ 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