diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index e506522..3ff3e70 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -1,13 +1,13 @@ name: github-actions on: [push, pull_request] jobs: - windows-2019: - runs-on: windows-2019 + windows-2022: + runs-on: windows-2022 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Boost install run: | - (New-Object System.Net.WebClient).DownloadFile("https://archives.boost.io/release/1.87.0/binaries/boost_1_87_0-msvc-14.2-64.exe", "${{ runner.temp }}\boost.exe") + (New-Object System.Net.WebClient).DownloadFile("https://archives.boost.io/release/1.88.0/binaries/boost_1_88_0-msvc-14.2-64.exe", "${{ runner.temp }}\boost.exe") Start-Process -Wait -FilePath "${{ runner.temp }}\boost.exe" "/SILENT","/SP-","/SUPPRESSMSGBOXES","/DIR=${{ runner.temp }}\boost-install" - name: AVRO install run: | @@ -19,7 +19,7 @@ jobs: cd ${{ runner.temp }} mkdir avro-cpp-build cd avro-cpp-build - cmake -G"Visual Studio 16 2019" -A x64 -T host=x64 -Wno-dev -Wno-deprecated -DBoost_INCLUDE_DIR=${{ runner.temp }}\boost-install -DCMAKE_INSTALL_PREFIX=${{ runner.temp }}/avro-cpp-install ${{ runner.temp }}/avro-cpp-1.11.3 + cmake -G"Visual Studio 17 2022" -A x64 -T host=x64 -Wno-dev -Wno-deprecated -DBoost_INCLUDE_DIR=${{ runner.temp }}\boost-install -DCMAKE_INSTALL_PREFIX=${{ runner.temp }}/avro-cpp-install ${{ runner.temp }}/avro-cpp-1.11.3 cmake --build . --config Release --target avrocpp_s -j2 cmake --install . - name: CMake build and install @@ -27,15 +27,15 @@ jobs: cd ${{ github.workspace }}/.. mkdir build cd build - cmake -G"Visual Studio 16 2019" -A x64 -T host=x64 -Wno-dev -Wno-deprecated -DBoost_INCLUDE_DIR=${{ runner.temp }}\boost-install -DAVRO_ROOT=${{ runner.temp }}/avro-cpp-install -DAVRO_USE_STATIC_LIBS=TRUE -DWITH_ETP_SSL=FALSE ${{ github.workspace }} + cmake -G"Visual Studio 17 2022" -A x64 -T host=x64 -Wno-dev -Wno-deprecated -DBoost_INCLUDE_DIR=${{ runner.temp }}\boost-install -DAVRO_ROOT=${{ runner.temp }}/avro-cpp-install -DAVRO_USE_STATIC_LIBS=TRUE -DWITH_ETP_SSL=FALSE ${{ github.workspace }} cmake --build . --config Release -j2 - windows-2019-with-fesapi: - runs-on: windows-2019 + windows-2022-with-fesapi: + runs-on: windows-2022 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Boost install run: | - (New-Object System.Net.WebClient).DownloadFile("https://archives.boost.io/release/1.87.0/binaries/boost_1_87_0-msvc-14.2-64.exe", "${{ runner.temp }}\boost.exe") + (New-Object System.Net.WebClient).DownloadFile("https://archives.boost.io/release/1.88.0/binaries/boost_1_88_0-msvc-14.2-64.exe", "${{ runner.temp }}\boost.exe") Start-Process -Wait -FilePath "${{ runner.temp }}\boost.exe" "/SILENT","/SP-","/SUPPRESSMSGBOXES","/DIR=${{ runner.temp }}\boost-install" - name: AVRO install run: | @@ -47,24 +47,24 @@ jobs: cd ${{ runner.temp }} mkdir avro-cpp-build cd avro-cpp-build - cmake -G"Visual Studio 16 2019" -A x64 -T host=x64 -Wno-dev -Wno-deprecated -DBoost_INCLUDE_DIR=${{ runner.temp }}\boost-install -DCMAKE_INSTALL_PREFIX=${{ runner.temp }}/avro-cpp-install ${{ runner.temp }}/avro-cpp-1.11.3 + cmake -G"Visual Studio 17 2022" -A x64 -T host=x64 -Wno-dev -Wno-deprecated -DBoost_INCLUDE_DIR=${{ runner.temp }}\boost-install -DCMAKE_INSTALL_PREFIX=${{ runner.temp }}/avro-cpp-install ${{ runner.temp }}/avro-cpp-1.11.3 cmake --build . --config Release --target avrocpp_s -j2 cmake --install . - name: FESAPI install run: | - (New-Object System.Net.WebClient).DownloadFile("https://github.com/F2I-Consulting/fesapi/releases/download/v2.12.1.0/fesapi2_12_1_0-cpp-vs2019-x64.zip", "${{ runner.temp }}\fesapi.zip") + (New-Object System.Net.WebClient).DownloadFile("https://github.com/F2I-Consulting/fesapi/releases/download/v2.14.0.0/fesapi2_14_0_0-cpp-vs2019-x64.zip", "${{ runner.temp }}\fesapi.zip") 7z x ${{ runner.temp }}\fesapi.zip -o${{ runner.temp }} - name: CMake build and install run: | cd ${{ github.workspace }}/.. mkdir build cd build - cmake -G"Visual Studio 16 2019" -A x64 -T host=x64 -Wno-dev -Wno-deprecated -DBoost_INCLUDE_DIR=${{ runner.temp }}\boost-install -DAVRO_ROOT=${{ runner.temp }}/avro-cpp-install -DAVRO_USE_STATIC_LIBS=TRUE -DWITH_FESAPI=TRUE -DFESAPI_ROOT=${{ runner.temp }}/fesapi2_12_1_0-cpp-vs2019-x64 -DWITH_ETP_SSL=FALSE ${{ github.workspace }} + cmake -G"Visual Studio 17 2022" -A x64 -T host=x64 -Wno-dev -Wno-deprecated -DBoost_INCLUDE_DIR=${{ runner.temp }}\boost-install -DAVRO_ROOT=${{ runner.temp }}/avro-cpp-install -DAVRO_USE_STATIC_LIBS=TRUE -DWITH_FESAPI=TRUE -DFESAPI_ROOT=${{ runner.temp }}/fesapi2_14_0_0-cpp-vs2019-x64 -DWITH_ETP_SSL=FALSE ${{ github.workspace }} cmake --build . --config Release -j2 ubuntu-22: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: APT install run: | sudo apt update @@ -91,8 +91,8 @@ jobs: ubuntu-22-java11: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '11' @@ -137,8 +137,8 @@ jobs: CC: ${{ matrix.cc }} CXX: ${{ matrix.cxx }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '11' @@ -148,13 +148,16 @@ jobs: sudo apt install -y ${{ matrix.xcc_pkg }} libhdf5-dev libminizip-dev libboost-all-dev - name: FESAPI install run: | - git clone --branch v2.12.1.0 --single-branch https://github.com/F2I-Consulting/fesapi.git ${{ runner.temp }}/fesapi-src + git clone --branch v2.14.0.0 --single-branch https://github.com/F2I-Consulting/fesapi.git ${{ runner.temp }}/fesapi-src cd ${{ runner.temp }} mkdir fesapi-build cd fesapi-build cmake -DMINIZIP_INCLUDE_DIR=/usr/include/minizip -DMINIZIP_LIBRARY_RELEASE=/usr/lib/x86_64-linux-gnu/libminizip.so.1.0.0 -DCMAKE_BUILD_TYPE=Release -DWITH_JAVA_WRAPPING=TRUE -DCMAKE_INSTALL_PREFIX=${{ runner.temp }}/fesapi-install -DCMAKE_C_COMPILER=${{ matrix.cc }} -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} ${{ runner.temp }}/fesapi-src cmake --build . -j2 cmake --install . + mkdir -p ${{ runner.temp }}/fesapi-install/include/fesapi + cd ${{ runner.temp }}/fesapi-src/src + find . -name "*.h" -exec cp --parents \{\} ${{ runner.temp }}/fesapi-install/include/fesapi/ \; - name: AVRO INSTALL run: | curl https://archive.apache.org/dist/avro/avro-1.11.3/cpp/avro-cpp-1.11.3.tar.gz -o ${{ runner.temp }}/avro-cpp-1.11.3.tar.gz @@ -172,31 +175,31 @@ jobs: cd ${{ github.workspace }}/.. mkdir build cd build - cmake -DAVRO_ROOT=${{ runner.temp }}/avro-cpp-install -DAVRO_USE_STATIC_LIBS=TRUE -DWITH_FESAPI=TRUE -DFESAPI_ROOT=${{ runner.temp }}/fesapi-install -DFESAPI_JAR=${{ runner.temp }}/fesapi-install/lib/fesapiJava-2.12.1.0.jar -DWITH_JAVA_WRAPPING=TRUE ${{ github.workspace }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} + cmake -DAVRO_ROOT=${{ runner.temp }}/avro-cpp-install -DAVRO_USE_STATIC_LIBS=TRUE -DWITH_FESAPI=TRUE -DFESAPI_ROOT=${{ runner.temp }}/fesapi-install -DFESAPI_JAR=${{ runner.temp }}/fesapi-install/lib/fesapiJava-2.14.0.0.jar -DWITH_JAVA_WRAPPING=TRUE ${{ github.workspace }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} cmake --build . --config Release -j2 build_wheels_windows: name: Build wheels on windows-latest runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Stub `setup.py` check # It will be generated during CMake run # https://github.com/pypa/cibuildwheel/issues/1139 run: touch python/setup.py - name: Build wheels - uses: pypa/cibuildwheel@v2.21.3 + uses: pypa/cibuildwheel@v3.2.1 env: CIBW_BUILD: cp38-win_amd64 cp39-win_amd64 cp310-win_amd64 cp311-win_amd64 cp312-win_amd64 cp313-win_amd64 CIBW_ARCHS: auto64 CIBW_BEFORE_ALL: > %VCPKG_INSTALLATION_ROOT%\vcpkg install boost-uuid minizip hdf5[zlib] && cd ${{ runner.temp }} && - powershell -Command "(New-Object System.Net.WebClient).DownloadFile('https://github.com/F2I-Consulting/fesapi/archive/refs/tags/v2.12.1.0.tar.gz', '${{ runner.temp }}\fesapi-2.12.1.0.tar.gz')" && - 7z x ${{ runner.temp }}\fesapi-2.12.1.0.tar.gz -o${{ runner.temp }} && - 7z x ${{ runner.temp }}\fesapi-2.12.1.0.tar -o${{ runner.temp }} && + powershell -Command "(New-Object System.Net.WebClient).DownloadFile('https://github.com/F2I-Consulting/fesapi/archive/refs/tags/v2.14.0.0.tar.gz', '${{ runner.temp }}\fesapi-2.14.0.0.tar.gz')" && + 7z x ${{ runner.temp }}\fesapi-2.14.0.0.tar.gz -o${{ runner.temp }} && + 7z x ${{ runner.temp }}\fesapi-2.14.0.0.tar -o${{ runner.temp }} && mkdir fesapi-build && cd fesapi-build && - cmake -DCMAKE_TOOLCHAIN_FILE=%VCPKG_INSTALLATION_ROOT%\scripts\buildsystems\vcpkg.cmake -G"Visual Studio 17 2022" -A x64 -T host=x64 -Wno-dev -Wno-deprecated -DCMAKE_INSTALL_PREFIX=${{ runner.temp }}/fesapi-install ${{ runner.temp }}\fesapi-2.12.1.0 && + cmake -DCMAKE_TOOLCHAIN_FILE=%VCPKG_INSTALLATION_ROOT%\scripts\buildsystems\vcpkg.cmake -G"Visual Studio 17 2022" -A x64 -T host=x64 -Wno-dev -Wno-deprecated -DCMAKE_INSTALL_PREFIX=${{ runner.temp }}/fesapi-install ${{ runner.temp }}\fesapi-2.14.0.0 && cmake --build . --config Release -j2 && cmake --build . --config Release --target INSTALL && %VCPKG_INSTALLATION_ROOT%\vcpkg install openssl boost-beast avro-cpp && @@ -221,13 +224,14 @@ jobs: name: Build wheels on ubuntu-latest runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Stub `setup.py` check # It will be generated during CMake run # https://github.com/pypa/cibuildwheel/issues/1139 run: touch python/setup.py - name: Build wheels - uses: pypa/cibuildwheel@v2.21.3 + # Above cibuildwheel@v2.22.0, GNU 14 is most likely used instead of GNU 12 or 13 which makes AVRO 1.11.3 not compiling + uses: pypa/cibuildwheel@v2.22.0 env: CIBW_BUILD: cp38-manylinux_* cp39-manylinux_* cp310-manylinux_* cp311-manylinux_* cp312-manylinux_* cp313-manylinux_* CIBW_ARCHS: auto64 @@ -239,11 +243,11 @@ jobs: yum install -y epel-release && yum --enablerepo=epel install -y minizip1.2-devel hdf5-devel cmake3 && cd / && - wget https://github.com/F2I-Consulting/fesapi/archive/refs/tags/v2.12.1.0.zip && - unzip v2.12.1.0.zip && + wget https://github.com/F2I-Consulting/fesapi/archive/refs/tags/v2.14.0.0.zip && + unzip v2.14.0.0.zip && mkdir fesapi-build && cd fesapi-build && - cmake3 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:STRING=/fesapi-install /fesapi-2.12.1.0 && + cmake3 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:STRING=/fesapi-install /fesapi-2.14.0.0 && cmake3 --build . -j2 --config Release && cmake3 --install . && cd / && @@ -277,13 +281,13 @@ jobs: name: Build wheels on macos-14 runs-on: macos-14 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Stub `setup.py` check # It will be generated during CMake run # https://github.com/pypa/cibuildwheel/issues/1139 run: touch python/setup.py - name: Build wheels - uses: pypa/cibuildwheel@v2.21.3 + uses: pypa/cibuildwheel@v3.2.1 env: CIBW_BUILD: cp38-macosx_* cp39-macosx_* cp310-macosx_* cp311-macosx_* cp312-macosx_* cp313-macosx_* CIBW_ARCHS: auto64 @@ -312,11 +316,11 @@ jobs: cmake --build . -j2 --config Release && cmake --install . && cd ${{ github.workspace }}/.. && - wget --no-verbose https://github.com/F2I-Consulting/fesapi/archive/refs/tags/v2.12.1.0.zip && - unzip v2.12.1.0.zip && + wget --no-verbose https://github.com/F2I-Consulting/fesapi/archive/refs/tags/v2.14.0.0.zip && + unzip v2.14.0.0.zip && mkdir fesapi-build && cd fesapi-build && - cmake -DCMAKE_BUILD_TYPE=Release -DBOOST_ROOT=${{ github.workspace }}/../boost-install -DMINIZIP_ROOT=${{ github.workspace }}/../minizip-install -DHDF5_ROOT=${{ github.workspace }}/../hdf5-install -DHDF5_USE_STATIC_LIBRARIES=TRUE -DCMAKE_INSTALL_PREFIX:STRING=${{ github.workspace }}/../fesapi-install ${{ github.workspace }}/../fesapi-2.12.1.0 && + cmake -DCMAKE_BUILD_TYPE=Release -DBOOST_ROOT=${{ github.workspace }}/../boost-install -DMINIZIP_ROOT=${{ github.workspace }}/../minizip-install -DHDF5_ROOT=${{ github.workspace }}/../hdf5-install -DHDF5_USE_STATIC_LIBRARIES=TRUE -DCMAKE_INSTALL_PREFIX:STRING=${{ github.workspace }}/../fesapi-install ${{ github.workspace }}/../fesapi-2.14.0.0 && cmake --build . -j2 --config Release && cmake --install . && cd ${{ github.workspace }}/.. && @@ -329,6 +333,8 @@ jobs: cd ${{ github.workspace }}/.. && wget --no-verbose https://archive.apache.org/dist/avro/avro-1.11.3/cpp/avro-cpp-1.11.3.tar.gz && tar xf avro-cpp-1.11.3.tar.gz && + sed -i '' 's/cmake_minimum_required (VERSION 3.1)/cmake_minimum_required (VERSION 3.5)/' avro-cpp-1.11.3/CMakeLists.txt && + sed -i '' 's/if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.0)/if (APPLE)/' avro-cpp-1.11.3/CMakeLists.txt && sed -i '' 's/install (TARGETS avrocpp avrocpp_s/install (TARGETS avrocpp_s/' avro-cpp-1.11.3/CMakeLists.txt && sed -i '' 's/install (TARGETS avrogencpp RUNTIME DESTINATION bin)//' avro-cpp-1.11.3/CMakeLists.txt && mkdir avro-build && diff --git a/CMakeLists.txt b/CMakeLists.txt index f7ba54a..d2b79bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,8 +15,8 @@ set (FETPAPI_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) # version mechanism set (Fetpapi_VERSION_MAJOR 0) -set (Fetpapi_VERSION_MINOR 3) -set (Fetpapi_VERSION_PATCH 1) +set (Fetpapi_VERSION_MINOR 4) +set (Fetpapi_VERSION_PATCH 0) set (Fetpapi_VERSION_TWEAK 0) set (Fetpapi_VERSION ${Fetpapi_VERSION_MAJOR}.${Fetpapi_VERSION_MINOR}.${Fetpapi_VERSION_PATCH}.${Fetpapi_VERSION_TWEAK}) @@ -200,7 +200,6 @@ set (ALL_SOURCES_AND_HEADERS ${FETPAPI_HEADERS} ${FETPAPI_PROTOCOL_SOURCES} ${FETPAPI_PROTOCOL_HEADERS} - ${FETPAPI_TOOLS_HEADERS} ${FETPAPI_FESAPI_SOURCES} ${FETPAPI_FESAPI_HEADERS} ${FETPAPI_SSL_SOURCES} @@ -224,8 +223,6 @@ target_include_directories(${PROJECT_NAME} INTERFACE # organizing sources and headers in the Visual Studio Project if (WIN32) - source_group ("tools" FILES ${FETPAPI_TOOLS_HEADERS}) - set (ETP_PREFIX "etp") source_group ("${ETP_PREFIX}" FILES ${FETPAPI_SOURCES} ${FETPAPI_HEADERS}) source_group ("${ETP_PREFIX}\\ProtocolHandlers" FILES ${FETPAPI_PROTOCOL_SOURCES} ${FETPAPI_PROTOCOL_HEADERS}) @@ -307,12 +304,6 @@ INSTALL ( COMPONENT fetpapi_headers ) -INSTALL ( - FILES ${FETPAPI_TOOLS_HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/fetpapi/tools - COMPONENT fetpapi_headers -) - if (WITH_ETP_SSL) INSTALL ( FILES ${FETPAPI_SSL_HEADERS} diff --git a/README.md b/README.md index e75b4d1..4142656 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Download (build and install if necessary) third party libraries: - BOOST : All versions from version 1.66 should be ok but you may experience some [min/max build issues](https://github.com/boostorg/beast/issues/1980) using version 1.72 or 1.73. - AVRO : https://avro.apache.org/releases.html#Download (starting from version 1.9.0 [except 1.11.1](https://issues.apache.org/jira/browse/AVRO-3601), build it with the above boost library.) -- (OPTIONALLY) OpenSSL : version 1.1 is known to work. +- (OPTIONALLY) OpenSSL : version 3.4 is known to work. - (OPTIONALLY) [FESAPI](https://github.com/F2I-Consulting/fesapi/releases) : All versions from version 2.7.0.0 should be ok but a minimal version of 2.11.0.0 is recommended to automatically recognize FESAPI CMake Variables using CMake find Module and build silently the EtpClient example. # Configure the build diff --git a/cmake/FetpapiClientUsingFesapi.java b/cmake/FetpapiClientUsingFesapi.java index a5effb6..0a05e70 100644 --- a/cmake/FetpapiClientUsingFesapi.java +++ b/cmake/FetpapiClientUsingFesapi.java @@ -16,7 +16,7 @@ Licensed to the Apache Software Foundation (ASF) under one specific language governing permissions and limitations under the License. -----------------------------------------------------------------------*/ -package com.f2i_consulting.fetpapi.client; +package com.f2i_consulting.example; import java.util.Optional; import java.util.UUID; diff --git a/cmake/fetpapiCsWithFesapi.csproj.template b/cmake/fetpapiCsWithFesapi.csproj.template index 327f39f..8025923 100644 --- a/cmake/fetpapiCsWithFesapi.csproj.template +++ b/cmake/fetpapiCsWithFesapi.csproj.template @@ -10,7 +10,11 @@ Properties F2iConsulting.Fetpapi ${CS_LIBRARY_NAME} + + + v4.8 512 diff --git a/cmake/fetpapiCsWithoutFesapi.csproj.template b/cmake/fetpapiCsWithoutFesapi.csproj.template index 3b01749..d98085a 100644 --- a/cmake/fetpapiCsWithoutFesapi.csproj.template +++ b/cmake/fetpapiCsWithoutFesapi.csproj.template @@ -10,7 +10,11 @@ Properties F2iConsulting.Fetpapi ${CS_LIBRARY_NAME} + + + v4.8 512 diff --git a/cmake/pyproject.toml.in b/cmake/pyproject.toml.in index c8fd895..ffe9606 100644 --- a/cmake/pyproject.toml.in +++ b/cmake/pyproject.toml.in @@ -16,7 +16,6 @@ readme = "README.md" classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', - 'License :: OSI Approved :: Apache Software License', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX :: Linux', 'Operating System :: MacOS', @@ -29,9 +28,10 @@ classifiers=[ 'Programming Language :: Python :: 3.12', 'Topic :: Software Development', 'Topic :: Software Development :: Libraries', - 'Topic :: Software Development :: Libraries :: Application Frameworks ', + 'Topic :: Software Development :: Libraries :: Application Frameworks', 'Topic :: File Formats', ] +license = {text = "Apache-2.0"} keywords = [ "energistics", "resqml", @@ -43,7 +43,7 @@ keywords = [ ] requires-python = ">=3.8" dependencies = [ - 'fesapi==2.12.1', + 'fesapi==2.14.0', ] [project.urls] diff --git a/cmake/swigEtp1_2Include.i.in b/cmake/swigEtp1_2Include.i.in index 6b01bb5..8f99e3a 100644 --- a/cmake/swigEtp1_2Include.i.in +++ b/cmake/swigEtp1_2Include.i.in @@ -32,6 +32,7 @@ under the License. #ifdef WITH_FESAPI %include "${FESAPI_INCLUDE_DIR}/fesapi/nsDefinitions.h" %import(module="fesapi") "${FESAPI_INCLUDE_DIR}/fesapi/common/DataObjectReference.h" +%import(module="fesapi") "${FESAPI_INCLUDE_DIR}/fesapi/common/DataObjectRepository.h" %import(module="fesapi") "${FESAPI_INCLUDE_DIR}/fesapi/common/HdfProxyFactory.h" %import(module="fesapi") "${FESAPI_INCLUDE_DIR}/fesapi/common/AbstractObject.h" #endif @@ -298,7 +299,7 @@ typedef long long time_t; %include "std_array.i" %typemap(javaimports) SWIGTYPE %{ - import com.f2i_consulting.fetpapi.*; +import com.f2i_consulting.fetpapi.*; %} namespace Energistics { @@ -376,22 +377,22 @@ namespace Energistics { std::string get_string(); void set_string(const std::string& v); - Energistics::Etp::v12::Datatypes::ArrayOfBoolean get_ArrayOfBoolean(); + Energistics::Etp::v12::Datatypes::ArrayOfBoolean const& get_ArrayOfBoolean() const; void set_ArrayOfBoolean(const Energistics::Etp::v12::Datatypes::ArrayOfBoolean& v); - Energistics::Etp::v12::Datatypes::ArrayOfInt get_ArrayOfInt(); + Energistics::Etp::v12::Datatypes::ArrayOfInt const& get_ArrayOfInt() const; void set_ArrayOfInt(const Energistics::Etp::v12::Datatypes::ArrayOfInt& v); - Energistics::Etp::v12::Datatypes::ArrayOfLong get_ArrayOfLong(); + Energistics::Etp::v12::Datatypes::ArrayOfLong const& get_ArrayOfLong() const; void set_ArrayOfLong(const Energistics::Etp::v12::Datatypes::ArrayOfLong& v); - Energistics::Etp::v12::Datatypes::ArrayOfFloat get_ArrayOfFloat(); + Energistics::Etp::v12::Datatypes::ArrayOfFloat const& get_ArrayOfFloat() const; void set_ArrayOfFloat(const Energistics::Etp::v12::Datatypes::ArrayOfFloat& v); - Energistics::Etp::v12::Datatypes::ArrayOfDouble get_ArrayOfDouble(); + Energistics::Etp::v12::Datatypes::ArrayOfDouble const& get_ArrayOfDouble() const; void set_ArrayOfDouble(const Energistics::Etp::v12::Datatypes::ArrayOfDouble& v); - Energistics::Etp::v12::Datatypes::ArrayOfString get_ArrayOfString(); + Energistics::Etp::v12::Datatypes::ArrayOfString const& get_ArrayOfString() const; void set_ArrayOfString(const Energistics::Etp::v12::Datatypes::ArrayOfString& v); std::string get_bytes(); @@ -411,25 +412,25 @@ namespace Energistics { public: size_t idx() const; - Energistics::Etp::v12::Datatypes::ArrayOfBoolean get_ArrayOfBoolean(); + Energistics::Etp::v12::Datatypes::ArrayOfBoolean const& get_ArrayOfBoolean() const; void set_ArrayOfBoolean(const Energistics::Etp::v12::Datatypes::ArrayOfBoolean& v); - Energistics::Etp::v12::Datatypes::ArrayOfInt get_ArrayOfInt(); + Energistics::Etp::v12::Datatypes::ArrayOfInt const& get_ArrayOfInt() const; void set_ArrayOfInt(const Energistics::Etp::v12::Datatypes::ArrayOfInt& v); - Energistics::Etp::v12::Datatypes::ArrayOfLong get_ArrayOfLong(); + Energistics::Etp::v12::Datatypes::ArrayOfLong const& get_ArrayOfLong() const; void set_ArrayOfLong(const Energistics::Etp::v12::Datatypes::ArrayOfLong& v); - Energistics::Etp::v12::Datatypes::ArrayOfFloat get_ArrayOfFloat(); + Energistics::Etp::v12::Datatypes::ArrayOfFloat const& get_ArrayOfFloat() const; void set_ArrayOfFloat(const Energistics::Etp::v12::Datatypes::ArrayOfFloat& v); - Energistics::Etp::v12::Datatypes::ArrayOfDouble get_ArrayOfDouble(); + Energistics::Etp::v12::Datatypes::ArrayOfDouble const& get_ArrayOfDouble() const; void set_ArrayOfDouble(const Energistics::Etp::v12::Datatypes::ArrayOfDouble& v); - Energistics::Etp::v12::Datatypes::ArrayOfString get_ArrayOfString(); + Energistics::Etp::v12::Datatypes::ArrayOfString const& get_ArrayOfString() const; void set_ArrayOfString(const Energistics::Etp::v12::Datatypes::ArrayOfString& v); - std::string get_bytes(); + std::string const& get_bytes() const; void set_bytes(const std::string& v); }; @@ -1267,10 +1268,9 @@ namespace Energistics { } %fragment("data_array_handler_reference_function", "header", fragment="data_array_handler_reference_init") { - - static PyObject *data_array_handler_reference() { - static PyObject *data_array_handler_reference_string = SWIG_Python_str_FromChar("__data_array_handler_reference"); - return data_array_handler_reference_string; + static PyObject *data_array_handler_reference() { + static PyObject *data_array_handler_reference_string = SWIG_Python_str_FromChar("__data_array_handler_reference"); + return data_array_handler_reference_string; } } @@ -1639,17 +1639,7 @@ namespace ETP_NS void setDataspaceProtocolHandlers(std::shared_ptr dataspaceHandlers); void setDataspaceOSDUProtocolHandlers(std::shared_ptr dataspaceOSDUHandlers); - template int64_t sendWithSpecificHandler(const T & mb, std::shared_ptr specificHandler, int64_t correlationId = 0, int32_t messageFlags = 0) - { - int64_t msgId = encode(mb, correlationId, messageFlags); // put the message to write in the queue - - if (sendingQueue.size() == 1) { - do_write(); - } - specificProtocolHandlers[msgId] = specificHandler; - - return msgId; - } + template int64_t sendWithSpecificHandler(const T & mb, std::shared_ptr specificHandler, int64_t correlationId = 0, int32_t messageFlags = 0) {} %template(sendWithSpecificHandler) sendWithSpecificHandler; %template(sendWithSpecificHandler) sendWithSpecificHandler; %template(sendWithSpecificHandler) sendWithSpecificHandler; @@ -1724,15 +1714,7 @@ namespace ETP_NS %template(sendWithSpecificHandler) sendWithSpecificHandler; %template(sendWithSpecificHandler) sendWithSpecificHandler; - template int64_t send(const T & mb, int64_t correlationId = 0, int32_t messageFlags = 0) - { - if (protocolHandlers.size() > mb.protocolId) { - return sendWithSpecificHandler(mb, protocolHandlers[mb.protocolId], correlationId, messageFlags); - } - else { - throw std::logic_error("The agent has no registered handler at all for the protocol " + std::to_string(mb.protocolId)); - } - } + template int64_t send(const T & mb, int64_t correlationId = 0, int32_t messageFlags = 0) {} %template(send) send; %template(send) send; %template(send) send; @@ -1807,12 +1789,7 @@ namespace ETP_NS %template(send) send; %template(send) send; - template void sendAndBlock(const T & mb, int64_t correlationId = 0, int32_t messageFlags = 0) - { - int64_t msgId = send(mb, correlationId, messageFlags); - while (isMessageStillProcessing(msgId)) {} - } - + template int64_t sendAndBlock(const T & mb, int64_t correlationId = 0, int32_t messageFlags = 0) {} %template(sendAndBlock) sendAndBlock; %template(sendAndBlock) sendAndBlock; %template(sendAndBlock) sendAndBlock; @@ -1966,37 +1943,38 @@ namespace ETP_NS std::vector getDataspaceInfo(const std::map& dataspaceUris); /** - * Copy by reference some dataspaces into another one. + * A customer sends to a store to lock or unlock one or more dataspaces. * This function should be used with caution if Dataspace OSDU Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. + * An OSDU locked dataspace will have its custom data "locked" to true. It will also have its custom datm "read-only" to true for all users. + * As a reminder, a custom data "read-only" can be true with a custom data "locked" to false in case the dataspace is not locked but in read only for the particular current ETP user. * - * @param sourceDataspaceUris ETP general map : One each for each source dataspace to be copied. They are identified by their URI. - * @param targetDataspaceUri The URI of the ETP dataspace where the sourceDataspaces have to be copied by reference. - * @param return The map keys corresponding to the dataspaces which have been successfully copied into the target dataspace. + * @param dataspaceUris ETP general map where the values must be the URIs for the dataspaces the customer wants to lock or unlock. + * @param lock true for locking the dataspaces, false to unlock the dataspaces */ - std::vector copyDataspacesContent(const std::map& sourceDataspaceUris, const std::string& targetDataspaceUri); + std::vector lockDataspaces(const std::map& dataspaceUris, bool lock); /** - * A customer sends to a store to lock or unlock one or more dataspaces. + * Copy by reference some dataspaces into another one. * This function should be used with caution if Dataspace OSDU Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * - * @param dataspaceUris ETP general map where the values must be the URIs for the dataspaces the customer wants to lock or unlock. - * @param lock true for locking the dataspaces, false to unlock the dataspaces - * @param return The map keys corresponding to the dataspaces which have been successfully locked or unlocked. + * @param sourceDataspaceUris ETP general map : One each for each source dataspace to be copied. They are identified by their URI. + * @param targetDataspaceUri The URI of the ETP dataspace where the sourceDataspaces have to be copied by reference. + * @param return The map keys corresponding to the dataspaces which have been successfully copied into the target dataspace. */ - std::vector lockDataspaces(const std::map& dataspaceUris, bool lock); + std::vector copyDataspacesContent(const std::map& sourceDataspaceUris, const std::string& targetDataspaceUri); /** * Copy by reference some dataobjects into another dataspace. * This function should be used with caution if Dataspace OSDU Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * - * @param sourceUris ETP general map : One each for each source dataobject to be copied. They are identified by their URI. - * @param targetDataspaceUri The URI of the ETP dataspace where the source dataobjects have to be copied by reference. - * @param return The map keys corresponding to the dataobjects which have been successfully copied into the target dataspace. + * @param sourceDataobjectUris ETP general map : One for each source dataobject to be copied. They are identified by their URI. + * @param targetDataspaceUri The URI of the ETP dataspace where the source dataobjects have to be copied by reference. + * @param return The map keys corresponding to the dataobjects which have been successfully copied into the target dataspace. */ - std::vector copyToDataspace(const std::map& sourceUris, const std::string& targetDataspaceUri); + std::vector copyToDataspace(const std::map& sourceDataobjectUris, const std::string& targetDataspaceUri); /**************** *** DISCOVERY *** @@ -2074,6 +2052,26 @@ namespace ETP_NS */ std::vector deleteDataObjects(const std::map& uris); + /********************* + ***** STORE OSDU ***** + **********************/ + + /** + * A customer sends to a store to copy by value a dataobject in the same dataspace with potentially some of its sources based on their datatypes. + * This function should be used with caution if Store OSDU Handlers have been overidden. + * It actually sends a message and blocks the current thread until a response has been received from the store. + * + * @param sourceDataobjectUri The URI of the dataobject to be copied. + * @param sourcesDepth The "depth" or how many "levels" (or "jumps") in the data model (graph) from the starting point (specified by the URI) that you want to copy + * Depth MUST always be greater than zero. + * @param dataObjectTypes Optionally, specify the types of data objects that you want to copy. + * The default is an empty array, which means ALL data types negotiated for the current ETP session. + * They ARE case sensitive. EXAMPLES: "witsml20.Well", "witsml20.Wellbore", "prodml21.WellTest", "resqml20.obj_TectonicBoundaryFeature", "eml21.DataAssuranceRecord" + * To indicate that all data objects within a data schema version are supported, you can use a star (*) as a wildcard, EXAMPLE: "witsml20.*", "prodml21.*", "resqml20.*" + * @param return The received dataobjects in a map where the key makes the link between the asked uris and the received dataobjects. + */ + std::vector copyDataObjectsByValue(const std::string& sourceDataobjectUri, int32_t sourcesDepth = 0, const std::vector& dataObjectTypes = {}); + /**************** ** TRANSACTION ** ****************/ @@ -2131,19 +2129,14 @@ namespace ETP_NS std::string getDataspaceUri(const std::string& uri); /** - * @param session Provide this parameter if you want to send a protocol exception in case of non validation. - */ - Energistics::Etp::v12::Datatypes::ErrorInfo validateUri(const std::string & uri, ETP_NS::AbstractSession* session = nullptr); - - /** - * @param session Provide this parameter if you want to send a protocol exception in case of non validation. + * Validate an ETP URI */ - Energistics::Etp::v12::Datatypes::ErrorInfo validateDataObjectUri(const std::string & uri, ETP_NS::AbstractSession* session = nullptr); + bool validateUri(const std::string& uri); /** - * Build a protocol exception message which only contains a single error message (not a messsage map). + * Validate an ETP dataobject URI */ - Energistics::Etp::v12::Protocol::Core::ProtocolException buildSingleMessageProtocolException(int32_t m_code, const std::string & m_message); + bool validateDataObjectUri(const std::string& uri); } /******************* CLIENT ***************************/ @@ -2156,7 +2149,7 @@ namespace ETP_NS * Run the websocket and then the ETP session. * Everything related to this session (including the completion handlers) will operate on the same unique thread in a single event loop. */ - bool run(); + void run(); }; class InitializationParameters @@ -2175,7 +2168,7 @@ namespace ETP_NS InitializationParameters(const std::string& instanceUuid, const std::string & host, unsigned short port, const std::string & urlPath = ""); virtual ~InitializationParameters(); - void setMaxWebSocketMessagePayloadSize(int64_t value); + void setMaxWebSocketMessagePayloadSize(uint64_t value); uint64_t getMaxWebSocketMessagePayloadSize() const; void setPreferredMaxFrameSize(uint64_t value); @@ -2224,7 +2217,7 @@ namespace ETP_NS #ifdef WITH_FESAPI %typemap(javaimports) FesapiHdfProxyFactory %{ - import com.f2i_consulting.fesapi.common.HdfProxyFactory; +import com.f2i_consulting.fesapi.common.HdfProxyFactory; %} %typemap(csimports) FesapiHdfProxyFactory %{ using F2iConsulting.Fesapi.common; @@ -2281,7 +2274,9 @@ namespace ETP_NS } #ifdef WITH_FESAPI -%pragma(java) moduleimports="import com.f2i_consulting.fesapi.common.AbstractObject;" -%pragma(java) jniclassimports="import com.f2i_consulting.fesapi.common.AbstractObject;" +%pragma(java) moduleimports="import com.f2i_consulting.fesapi.common.AbstractObject; +import com.f2i_consulting.fesapi.common.DataObjectRepository;" +%pragma(java) jniclassimports="import com.f2i_consulting.fesapi.common.AbstractObject; +import com.f2i_consulting.fesapi.common.DataObjectRepository;" %pragma(csharp) moduleimports="using F2iConsulting.Fesapi.common;" #endif \ No newline at end of file diff --git a/cs/CMakeLists.txt b/cs/CMakeLists.txt index 042fde8..eff2b9c 100644 --- a/cs/CMakeLists.txt +++ b/cs/CMakeLists.txt @@ -9,21 +9,6 @@ message("Generating SWIG C# files...") # Cleaning execute_process(COMMAND powershell "Remove-Item ${CMAKE_SOURCE_DIR}/cs/src/* -recurse -exclude .gitignore") -# The name of the library is different on Windows because it includes the version -if (WIN32) - if (SWIG_LINKED_TO_RELEASE) - set (ASSEMBLY_NAME ${PROJECT_NAME}${CMAKE_RELEASE_POSTFIX}.${Fetpapi_VERSION}) - else (SWIG_LINKED_TO_RELEASE) - set (ASSEMBLY_NAME ${PROJECT_NAME}${CMAKE_DEBUG_POSTFIX}.${Fetpapi_VERSION}) - endif (SWIG_LINKED_TO_RELEASE) -else (WIN32) - if (SWIG_LINKED_TO_RELEASE) - set (ASSEMBLY_NAME ${PROJECT_NAME}${CMAKE_RELEASE_POSTFIX}) - else (SWIG_LINKED_TO_RELEASE) - set (ASSEMBLY_NAME ${PROJECT_NAME}${CMAKE_DEBUG_POSTFIX}) - endif (SWIG_LINKED_TO_RELEASE) -endif (WIN32) - # SWIG execution set (EXECUTE_COMMAND "${SWIG_EXECUTABLE}") list (APPEND EXECUTE_COMMAND -v) @@ -36,7 +21,7 @@ if (WITH_ETP_SSL) list (APPEND EXECUTE_COMMAND -DWITH_ETP_SSL) endif (WITH_ETP_SSL) list (APPEND EXECUTE_COMMAND -dllimport) -list (APPEND EXECUTE_COMMAND ${ASSEMBLY_NAME}.dll) +list (APPEND EXECUTE_COMMAND ${PROJECT_NAME}${CMAKE_RELEASE_POSTFIX}-${Fetpapi_VERSION_MAJOR}.${Fetpapi_VERSION_MINOR}.dll) list (APPEND EXECUTE_COMMAND -namespace) list (APPEND EXECUTE_COMMAND F2iConsulting.Fetpapi) list (APPEND EXECUTE_COMMAND -o) diff --git a/example/withFesapi/etpClient.cpp b/example/withFesapi/etpClient.cpp index f7d1ffd..dc92b35 100644 --- a/example/withFesapi/etpClient.cpp +++ b/example/withFesapi/etpClient.cpp @@ -21,7 +21,11 @@ under the License. #include #include +#include +#include +#include #include +#include #include #include "etp/ClientSessionLaunchers.h" @@ -47,6 +51,7 @@ void printHelp() std::cout << "\tBlockingImport" << std::endl << "\t\tList all dataobjects from the project/study named dataspace (or the first dataspace) and get the first one in a blocking way" << std::endl << std::endl; std::cout << "\tBlockingExport" << std::endl << "\t\tPut a dummy horizon feature into a dummy dataspace which is also created" << std::endl << std::endl; std::cout << "\tPing" << std::endl << "\t\tPing the server" << std::endl << std::endl; + std::cout << "\tPutDummyHorizon" << std::endl << "\t\tPut a dummy horizon to the store" << std::endl << std::endl; std::cout << "\tList" << std::endl << "\t\tList the objects which have been got from ETP to the in-memory Dataobject repository" << std::endl << std::endl; std::cout << "\tPutXmlAndHdfAtOnce" << std::endl << "\t\tPut a dummy point set representation to the store sending XML and HDF5 points at once." << std::endl << std::endl; std::cout << "\tGetDataspaces" << std::endl << "\t\tGet all store dataspaces" << std::endl << std::endl; @@ -68,6 +73,7 @@ void printHelp() std::cout << "\tDeleteDataObject URI" << std::endl << "\t\tDelete a dataobject" << std::endl << std::endl; std::cout << "\tDeleteDataspace URI" << std::endl << "\t\tDelete a dataspace" << std::endl << std::endl; std::cout << "\tGetDeletedResources dataspaceURI" << std::endl << "\t\tGet all deleted resources" << std::endl << std::endl; + std::cout << "\tGetAllRepsAndProps dataspace" << std::endl << "\t\tGet all the representations and all their properties from a dataspace" << std::endl << std::endl; std::cout << "\tquit" << std::endl << "\t\tQuit the session." << std::endl << std::endl; } @@ -80,11 +86,10 @@ void askUser(std::shared_ptr session, COMMON_NS::DataOb while (command != "quit") { if (session->isEtpSessionClosed()) { - command = "quit"; - } - else { - std::getline(std::cin, command); + std::cout << "The ETP session has been lost. You should quit if FETPAPI cannot reconnect by itself" << std::endl; } + + std::getline(std::cin, command); auto commandTokens = tokenize(command, ' '); if (commandTokens.empty()) { @@ -191,8 +196,8 @@ void askUser(std::shared_ptr session, COMMON_NS::DataOb const auto resources = session->getResources(mb.context, mb.scope); for (auto& resource : resources) { std::cout << resource.uri << std::endl; - if (resource.has_sourceCount()) std::cout << "Source count: " << resource.sourceCount.get() << std::endl; - if (resource.has_targetCount()) std::cout << "Target count: " << resource.targetCount.get() << std::endl; + if (resource.has_sourceCount()) std::cout << "Source count: " << resource.sourceCount.value() << std::endl; + if (resource.has_targetCount()) std::cout << "Target count: " << resource.targetCount.value() << std::endl; } continue; } @@ -239,9 +244,9 @@ void askUser(std::shared_ptr session, COMMON_NS::DataOb std::cerr << " The UUID " << uuid << " from URI " << commandTokens[1] << " does not correspond to a representation which is on client side. Please get first this dataobject from the store before to call GetXYZPoints on it." << std::endl; continue; } - auto xyzPointCount = rep->getXyzPointCountOfPatch(0); + auto xyzPointCount = rep->getXyzPointCountOfAllPatches(); std::unique_ptr xyzPoints(new double[xyzPointCount * 3]); - rep->getXyzPointsOfPatch(0, xyzPoints.get()); + rep->getXyzPointsOfAllPatches(xyzPoints.get()); for (auto xyzPointIndex = 0; xyzPointIndex < xyzPointCount && xyzPointIndex < 20; ++xyzPointIndex) { std::cout << "XYZ Point Index " << xyzPointIndex << " : " << xyzPoints[xyzPointIndex * 3] << "," << xyzPoints[xyzPointIndex * 3 + 1] << "," << xyzPoints[xyzPointIndex * 3 + 2] << std::endl; } @@ -270,6 +275,72 @@ void askUser(std::shared_ptr session, COMMON_NS::DataOb std::cout << dataspace.uri << std::endl; } } + else if (commandTokens[0] == "GetAllRepsAndProps") { + if (commandTokens.size() == 1) { + std::cerr << "Please provide some ETP URIs of a dataspace" << std::endl; + continue; + } + std::map dataspaceUris; + dataspaceUris["0"] = commandTokens[1]; + + Energistics::Etp::v12::Datatypes::Object::ContextInfo ctxInfo; + ctxInfo.uri = dataspaceUris["0"]; + ctxInfo.depth = 0; + ctxInfo.navigableEdges = Energistics::Etp::v12::Datatypes::Object::RelationshipKind::Both; + ctxInfo.includeSecondaryTargets = false; + ctxInfo.includeSecondarySources = false; + const auto resources = session->getResources(ctxInfo, Energistics::Etp::v12::Datatypes::Object::ContextScopeKind::targets); + std::cout << "************ GET ALL " << resources.size() << " DATAOBJECTS ************" << std::endl; + if (!resources.empty()) { + std::map< std::string, std::string > query; + size_t index = 0; + for (auto& resource : resources) { + query[std::to_string(index++)] = resource.uri; + } + const auto dataobjects = session->getDataObjects(query); + for (const auto& dataobjectEntry : dataobjects) { + repo.addOrReplaceGsoapProxy(dataobjectEntry.second.data, ETP_NS::EtpHelpers::getDataObjectType(dataobjectEntry.second.resource.uri), ETP_NS::EtpHelpers::getDataspaceUri(dataobjectEntry.second.resource.uri)); + } + // Parse reps + auto global_start = std::chrono::high_resolution_clock::now(); + for (auto* rep : repo.getDataObjects()) { + std::cout << "Representation " << rep->getTitle() << std::endl; + std::unique_ptr ijkGridPoints(new double[rep->getXyzPointCountOfAllPatches() * 3]); + auto t_start = std::chrono::high_resolution_clock::now(); + try { + rep->getXyzPointsOfAllPatches(ijkGridPoints.get()); + std::cout << "XYZ POINTS IN " << std::chrono::duration(std::chrono::high_resolution_clock::now() - t_start).count() << " ms" << std::endl; + } + catch (...) { + std::cerr << "Error reading XYZ points." << std::endl; + } + + auto allProps = rep->getValuesPropertySet(); + size_t propIndex = 1; + for (auto* prop : allProps) { + size_t valuesCount = prop->getValuesCountOfPatch(0); + if (dynamic_cast(prop) != nullptr) { + std::cout << "Continuous Prop " << propIndex++ << "/" << allProps.size() << " : " << prop->getTitle() << std::endl; + std::unique_ptr propValues(new double[valuesCount]); + t_start = std::chrono::high_resolution_clock::now(); + prop->getDoubleValuesOfPatch(0, propValues.get()); + std::cout << "Continuous Prop IN " << std::chrono::duration(std::chrono::high_resolution_clock::now() - t_start).count() << " ms" << std::endl; + } + else { + std::cout << "Non Continuous Prop " << propIndex++ << "/" << allProps.size() << " : " << prop->getTitle() << std::endl; + std::unique_ptr propValues(new int[valuesCount]); + t_start = std::chrono::high_resolution_clock::now(); + prop->getInt32ValuesOfPatch(0, propValues.get()); + std::cout << "Non Continuous Prop IN " << std::chrono::duration(std::chrono::high_resolution_clock::now() - t_start).count() << " ms" << std::endl; + } + } + } + std::cout << "GLOBALLY DONE IN " << std::chrono::duration(std::chrono::high_resolution_clock::now() - global_start).count() << " ms" << std::endl; + } + else { + std::cout << "There is no dataobject in this dataspace" << std::endl; + } + } else if (commandTokens[0] == "PutDataspace") { if (commandTokens.size() == 1) { std::cerr << "Please provide some ETP URI of a dataspace" << std::endl; @@ -418,6 +489,106 @@ void askUser(std::shared_ptr session, COMMON_NS::DataOb std::cout << "PING at " << ping.currentDateTime << std::endl; std::cout << "Please Set Verbosity to 1 if you don't see anything" << std::endl; } + else if (commandTokens[0] == "PutDummyHorizon") { + Energistics::Etp::v12::Datatypes::Object::Dataspace dataspace; + dataspace.path = "demo/PutHorizon3"; + dataspace.uri = "eml:///dataspace('" + dataspace.path + "')"; + Energistics::Etp::v12::Datatypes::DataValue dataValue; + Energistics::Etp::v12::Datatypes::ArrayOfString aos; + + aos.values.push_back("data.default.viewers@osdu.example.com"); + //aos.values.push_back("data.default.viewers@opendes.contoso.com"); + dataValue.item.set_ArrayOfString(aos); + dataspace.customData["viewers"] = dataValue; + + aos.values[0] = "data.default.owners@osdu.example.com"; + //aos.values[0] = "data.default.owners@opendes.contoso.com"; + dataValue.item.set_ArrayOfString(aos); + dataspace.customData["owners"] = dataValue; + + aos.values[0] = "osdu-public-usa-dataset"; + //aos.values[0] = "opendes-ReservoirDDMS-Legal-Tag"; + dataValue.item.set_ArrayOfString(aos); + dataspace.customData["legaltags"] = dataValue; + + aos.values[0] = "US"; + dataValue.item.set_ArrayOfString(aos); + dataspace.customData["otherRelevantDataCountries"] = dataValue; + + std::map deleteDs; + deleteDs["0"] = dataspace.uri; + session->deleteDataspaces(deleteDs); + + std::map dataspaces; + dataspaces["0"] = dataspace; + auto successKeys = session->putDataspaces(dataspaces); + if (successKeys.size() == 1) std::cout << "Dataspace has been put" << std::endl; + else std::cout << "Error when putting dataspace" << std::endl; + + COMMON_NS::DataObjectRepository tmpRepo; + tmpRepo.setDefaultStandard(COMMON_NS::DataObjectRepository::EnergisticsStandard::RESQML2_0_1); + tmpRepo.setDefaultStandard(COMMON_NS::DataObjectRepository::EnergisticsStandard::EML2_0); + auto* local_3d_crs = tmpRepo.createLocalDepth3dCrs("b2129512-b8f9-4721-8a70-1abac53ef406", "Default CRS", + 0.0, 0.0, 0.0, 0.0, + gsoap_resqml2_0_1::eml20__LengthUom::m, 5215, + gsoap_resqml2_0_1::eml20__LengthUom::m, "Unknown", + false); + tmpRepo.setDefaultCrs(local_3d_crs); + tmpRepo.setHdfProxyFactory(new ETP_NS::FesapiHdfProxyFactory(session.get())); + auto* hdf_proxy = tmpRepo.createHdfProxy("f8160b8f-0517-4c55-ab6e-ed8bcdc87111", "Hdf Proxy", + ".", "fake.h5", + COMMON_NS::DataObjectRepository::openingMode::OVERWRITE); + hdf_proxy->setUriSource(dataspace.uri); + tmpRepo.setDefaultHdfProxy(hdf_proxy); + auto* horizon_feature = tmpRepo.createHorizon("c0f12836-41f4-44a8-a3fd-95ac78f6232d", "My horizon feature"); + auto* horizon_interpretation = tmpRepo.createHorizonInterpretation(horizon_feature, "dc217b29-8ceb-4b77-bdcc-6bcfd9cd3baf", "My horizon interpretation"); + auto* horizon_grid_2d_representation = tmpRepo.createGrid2dRepresentation(horizon_interpretation, "7721fb3c-39ba-4d59-ba0b-f9451706a94c", "My horizon representation"); + + std::vector< std::string > dataspacesToLock; + dataspacesToLock.push_back(dataspace.uri); + + auto transaction_start = std::chrono::high_resolution_clock::now(); + + session->startTransaction(dataspacesToLock); + + const size_t ni = 10000; + const size_t nj = 1000; + std::unique_ptr resqml_points(new double[ni * nj]); + for (double i = 0; i < ni * nj; ++i) { + resqml_points[(int)i] = i * 100; + } + horizon_grid_2d_representation->setGeometryAsArray2dOfExplicitZ(resqml_points.get(), ni, nj, hdf_proxy, + 0.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 25.0, + 0.0, 1.0, 0.0, 50.0); + + for (size_t propIndex = 0; propIndex < 1; ++propIndex) { + auto t_start = std::chrono::high_resolution_clock::now(); + auto* prop = tmpRepo.createContinuousProperty(horizon_grid_2d_representation, "", "", 1, gsoap_eml2_3::eml23__IndexableElement::nodes, gsoap_resqml2_0_1::resqml20__ResqmlUom::m, + gsoap_resqml2_0_1::resqml20__ResqmlPropertyKind::length); + std::unique_ptr prop_values(new double[ni * nj]); + prop->pushBackDoubleHdf5Array2dOfValues(prop_values.get(), ni, nj, hdf_proxy); + std::cout << " Pushed prop " << propIndex << " in " << std::chrono::duration(std::chrono::high_resolution_clock::now() - t_start).count() << " ms" << std::endl; + std::cout << " Global time " << std::chrono::duration(std::chrono::high_resolution_clock::now() - transaction_start).count() << " s" << std::endl; + } + + tmpRepo.setUriSource(dataspace.uri); + std::map dataobjects; + auto allUuids = tmpRepo.getUuids(); + int index = 0; + for (auto& uuid : allUuids) + dataobjects[std::to_string(index++)] = ETP_NS::FesapiHelpers::buildEtpDataObjectFromEnergisticsObject(tmpRepo, uuid); + successKeys = session->putDataObjects(dataobjects); + for (std::string& str : successKeys) + std::cout << "successKey : " << str << std::endl; + + std::cout << "commit : " << session->commitTransaction() << std::endl; + + if (session != nullptr && !session->isWebSocketSessionClosed()) + horizon_grid_2d_representation->getZValues(resqml_points.get()); + + tmpRepo.clear(); + } else if (commandTokens[0] == "GetDataspaces") { const auto dataspaces = session->getDataspaces(); for (auto& dataspace : dataspaces) { @@ -509,8 +680,9 @@ void askUser(std::shared_ptr session, COMMON_NS::DataOb Energistics::Etp::v12::Datatypes::AnyArray data; Energistics::Etp::v12::Datatypes::ArrayOfInt arrayOfInt; arrayOfInt.values = { 0,1,2,3,4,5,6,7,8,9 }; - data.item.set_ArrayOfInt(arrayOfInt); + data.item.set_ArrayOfInt(std::move(arrayOfInt)); pda.dataArrays["0"].array.data = data; + std::cout << "Start sending the array" << std::endl; session->send(pda, 0, 0x02); } @@ -552,7 +724,9 @@ int main(int argc, char **argv) std::string authorization; std::getline(std::cin, authorization); - bool successfulConnection = false; + std::cout << "Give the data partition id you want to direct your requests (or hit enter if no data partition)" << std::endl; + std::string dataPartition; + std::getline(std::cin, dataPartition); COMMON_NS::DataObjectRepository repo; repo.setDefaultStandard(COMMON_NS::DataObjectRepository::EnergisticsStandard::RESQML2_0_1); @@ -562,16 +736,16 @@ int main(int argc, char **argv) ETP_NS::InitializationParameters initializationParams = argc == 2 ? ETP_NS::InitializationParameters(gen(), argv[1]) // URL based : ETP_NS::InitializationParameters(gen(), argv[1], std::stoi(argv[2]), argc < 4 ? "/" : argv[3]); // IP Port and target based - std::map< std::string, std::string > additionalHeaderField = { {"data-partition-id", "osdu"} }; // Example for OSDU RDDMS + std::map< std::string, std::string > additionalHeaderField = { {"data-partition-id", dataPartition} }; // Example for OSDU RDDMS initializationParams.setAdditionalHandshakeHeaderFields(additionalHeaderField); std::cout << "Creating a client session..." << std::endl; auto clientSession = ETP_NS::ClientSessionLaunchers::createClientSession(&initializationParams, authorization); + clientSession->setVerbose(false); repo.setHdfProxyFactory(new ETP_NS::FesapiHdfProxyFactory(clientSession.get())); std::thread sessionThread(&ETP_NS::ClientSession::run, clientSession); - sessionThread.detach(); // Wait for the ETP session to be opened auto t_start = std::chrono::high_resolution_clock::now(); @@ -585,6 +759,8 @@ int main(int argc, char **argv) clientSession->setTimeOut(60000); askUser(clientSession, repo); + sessionThread.join(); + #ifdef _WIN32 _CrtDumpMemoryLeaks(); #endif diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt index 61f9996..cd343a5 100644 --- a/java/CMakeLists.txt +++ b/java/CMakeLists.txt @@ -20,9 +20,9 @@ file (REMOVE_RECURSE ${CMAKE_SOURCE_DIR}/java/src/com/f2i_consulting/fetpapi/etp # The assembly name is used to load the debug vs release FETPAPI library in the Java example if (WIN32) if (SWIG_LINKED_TO_RELEASE) - set (ASSEMBLY_NAME ${PROJECT_NAME}${CMAKE_RELEASE_POSTFIX}.${Fetpapi_VERSION}) + set (ASSEMBLY_NAME ${PROJECT_NAME}${CMAKE_RELEASE_POSTFIX}-${Fetpapi_VERSION_MAJOR}.${Fetpapi_VERSION_MINOR}) else (SWIG_LINKED_TO_RELEASE) - set (ASSEMBLY_NAME ${PROJECT_NAME}${CMAKE_DEBUG_POSTFIX}.${Fetpapi_VERSION}) + set (ASSEMBLY_NAME ${PROJECT_NAME}${CMAKE_DEBUG_POSTFIX}-${Fetpapi_VERSION_MAJOR}.${Fetpapi_VERSION_MINOR}) endif (SWIG_LINKED_TO_RELEASE) else (WIN32) if (SWIG_LINKED_TO_RELEASE) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 1455314..8e461ac 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -58,7 +58,7 @@ if (WITH_FESAPI) set (FESAPI_LIBRARY_RELEASE_WLE ",'${FESAPI_LIBRARY_RELEASE_WLE}'") endif () if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - set (EXTRA_COMPILE_ARGS "extra-compile-args=['/DSWIG_TYPE_TABLE=FESTAPI', '/utf-8']") + set (EXTRA_COMPILE_ARGS "extra-compile-args=['/DSWIG_TYPE_TABLE=FESTAPI', '/std:c++17', '/utf-8']") else () set (EXTRA_COMPILE_ARGS "extra-compile-args=['-DSWIG_TYPE_TABLE=FESTAPI', '-std=c++17']") endif() diff --git a/python/example/PutHorizon.ipynb b/python/example/PutHorizon.ipynb index 7961b9d..96ebd41 100644 --- a/python/example/PutHorizon.ipynb +++ b/python/example/PutHorizon.ipynb @@ -140,7 +140,7 @@ "id": "abbc43cb", "metadata": {}, "source": [ - "Let's create a dataspace" + "Let's create a dataspace with the OSDU ACL mandatory information" ] }, { @@ -152,7 +152,31 @@ "source": [ "dataspace = fetpapi.Dataspace()\n", "dataspace.path = \"demo/PutHorizon\"\n", - "dataspace.uri = \"eml:///dataspace('\" + dataspace.path + \"')\"" + "dataspace.uri = \"eml:///dataspace('\" + dataspace.path + \"')\"\n", + "dataspace.customData = fetpapi.MapStringDataValue()\n", + "dataValue = fetpapi.DataValue()\n", + "aos = fetpapi.ArrayOfString()\n", + "dataValue.item = fetpapi.DataValueitem_t()\n", + "tmp = fetpapi.StringVector()\n", + "tmp.push_back(\"data.default.viewers@osdu.example.com\")\n", + "aos.values = tmp\n", + "dataValue.item.set_ArrayOfString(aos)\n", + "dataspace.customData[\"viewers\"] = dataValue\n", + "tmp.clear()\n", + "tmp.push_back(\"data.default.owners@osdu.example.com\")\n", + "aos.values = tmp\n", + "dataValue.item.set_ArrayOfString(aos)\n", + "dataspace.customData[\"owners\"] = dataValue\n", + "tmp.clear()\n", + "tmp.push_back(\"osdu-public-usa-dataset\")\n", + "aos.values = tmp\n", + "dataValue.item.set_ArrayOfString(aos)\n", + "dataspace.customData[\"legaltags\"] = dataValue\n", + "tmp.clear()\n", + "tmp.push_back(\"US\")\n", + "aos.values = tmp\n", + "dataValue.item.set_ArrayOfString(aos)\n", + "dataspace.customData[\"otherRelevantDataCountries\"] = dataValue" ] }, { @@ -242,7 +266,7 @@ "resqml_points = fesapi.DoubleArray(6)\n", "for i in range(6):\n", " resqml_points.setitem(i, i*100)\n", - "horizon_grid_2d_representation.setGeometryAsArray2dOfExplicitZ(resqml_points, 2, 3, hdf_proxy,\n", + "horizon_grid_2d_representation.setGeometryAsArray2dOfExplicitZ(resqml_points.cast(), 2, 3, hdf_proxy,\n", " 0.0, 0.0, 0.0,\n", " 1.0, 0.0, 0.0, 25.0,\n", " 0.0, 1.0, 0.0, 50.0)" diff --git a/python/example/etp_client_example.py b/python/example/etp_client_example.py index 46fa2e3..3091caa 100644 --- a/python/example/etp_client_example.py +++ b/python/example/etp_client_example.py @@ -79,28 +79,53 @@ def start_etp_server(client_session): nb_xyz_points = ijk_grid.getXyzPointCountOfAllPatches() print("XYZ points count :", nb_xyz_points) xyz_points = fesapi.DoubleArray(nb_xyz_points * 3) - ijk_grid.getXyzPointsOfAllPatches(xyz_points) + ijk_grid.getXyzPointsOfAllPatches(xyz_points.cast()) ijk_grid.loadSplitInformation() origin_index = ijk_grid.getXyzPointIndexFromCellCorner(0, 0, 0, 0) print("Cell 0,0,0 corner 0 is at index ", origin_index) print("Cell 0,0,0 corner 0 is ", xyz_points.getitem(origin_index * 3), " ", xyz_points.getitem(origin_index * 3 + 1), " ", xyz_points.getitem(origin_index * 3 + 2)) ijk_grid.unloadSplitInformation() + + if ijk_grid.getValuesPropertyCount() > 0: + prop = ijk_grid.getValuesProperty(0) + print("Prop at index 0 : " + prop.getTitle()) + print(type(prop)) + + if isinstance(prop, fesapi.Resqml2_ContinuousProperty): + prop_values = fesapi.DoubleArray(ijk_grid.getICellCount() * ijk_grid.getJCellCount() * ijk_grid.getKCellCount()) + prop.getDoubleValuesOfPatch(0, prop_values.cast()) + print("Cell 0,0,0 has prop value ", prop_values.getitem(0)) + print("Cell 1,0,0 has prop value ", prop_values.getitem(1)) + print("Cell 2,0,0 has prop value ", prop_values.getitem(2)) + print("Cell 3,0,0 has prop value ", prop_values.getitem(3)) + print("Cell 4,0,0 has prop value ", prop_values.getitem(4)) + print("Cell 5,0,0 has prop value ", prop_values.getitem(5)) + else: + print(f"This property is a {type(prop)}") + else: + print("This IJK grid has no property") else: print("This dataspace has no IJK Grid") print("Read data of the first 2d grid"); if repo.getHorizonGrid2dRepresentationCount() > 0: grid2d = repo.getHorizonGrid2dRepresentation(0) - print("2d Grid : " + grid2d.getTitle()) + print("2d grid : " + grid2d.getTitle()) print(f"iCount : {grid2d.getNodeCountAlongIAxis()} jCount : {grid2d.getNodeCountAlongJAxis()}") nb_z_points = grid2d.getNodeCountAlongIAxis() * grid2d.getNodeCountAlongJAxis() print(f"XYZ points count : {nb_z_points}") z_points = fesapi.DoubleArray(nb_z_points) - grid2d.getZValues(z_points) + grid2d.getZValues(z_points.cast()) print("Z value at index 0 : ", z_points.getitem(0)) print("Z value at index 1 : ", z_points.getitem(1)) + + if grid2d.getValuesPropertyCount() > 0: + prop = grid2d.getValuesProperty(0) + print("Prop at index 0 : " + prop.getTitle()) + else: + print("This 2d grid has no property") else: print("This dataspace has no 2d Grid") diff --git a/python/example/fetpapi.ipynb b/python/example/fetpapi.ipynb index 85bf62a..48055dd 100644 --- a/python/example/fetpapi.ipynb +++ b/python/example/fetpapi.ipynb @@ -131,6 +131,8 @@ " sleep(0.25)\t\n", "if client_session.isEtpSessionClosed():\n", " print(\"The ETP session could not be established in 5 seconds.\")\n", + " if not etp_server_url.endswith(\"/\"):\n", + " print(\"You may try adding an ending slash to your ETP server URL.\")\n", "else:\n", " print(\"Now connected to ETP Server\")" ] diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 574b0b3..2657944 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,3 @@ set(FESAPI_TOOLS_HEADERS ${CMAKE_CURRENT_LIST_DIR}/nsDefinitions.h ) include(${CMAKE_CURRENT_LIST_DIR}/etp/CMakeLists.txt) -include(${CMAKE_CURRENT_LIST_DIR}/tools/CMakeLists.txt) diff --git a/src/etp/AbstractClientSessionCRTP.h b/src/etp/AbstractClientSessionCRTP.h index acce0a3..3a6e676 100644 --- a/src/etp/AbstractClientSessionCRTP.h +++ b/src/etp/AbstractClientSessionCRTP.h @@ -30,14 +30,15 @@ namespace ETP_NS virtual ~AbstractClientSessionCRTP() = default; - void on_connect(boost::system::error_code ec) { + void on_ssl_handshake(boost::system::error_code ec) { if (ec) { - std::cerr << "on_connect : " << ec.message() << std::endl; + std::cerr << "ERROR at Websocket connection : " << ec.message() << std::endl; + return; } #if BOOST_VERSION < 107000 // Perform the websocket handshake - derived().ws().async_handshake_ex(responseType, + derived().ws()->async_handshake_ex(responseType, etpServerHost + ":" + etpServerPort, etpServerTarget, [&](websocket::request_type& m) { @@ -52,11 +53,11 @@ namespace ETP_NS } }, std::bind( - &AbstractClientSessionCRTP::on_handshake, + &ClientSession::on_handshake, std::static_pointer_cast(shared_from_this()), std::placeholders::_1)); #else - derived().ws().set_option(websocket::stream_base::decorator( + derived().ws()->set_option(websocket::stream_base::decorator( [&](websocket::request_type& m) { m.insert(boost::beast::http::field::sec_websocket_protocol, "etp12.energistics.org"); @@ -71,10 +72,10 @@ namespace ETP_NS }) ); // Perform the websocket handshake - derived().ws().async_handshake(responseType, + derived().ws()->async_handshake(responseType, etpServerHost + ":" + etpServerPort, etpServerTarget, std::bind( - &AbstractClientSessionCRTP::on_handshake, + &ClientSession::on_handshake, std::static_pointer_cast(shared_from_this()), std::placeholders::_1)); #endif @@ -85,7 +86,7 @@ namespace ETP_NS * The ETP session had to be closed before. */ FETPAPI_DLL_IMPORT_OR_EXPORT void do_close() { - derived().ws().async_close(websocket::close_code::normal, + derived().ws()->async_close(websocket::close_code::normal, std::bind( &AbstractSession::on_close, shared_from_this(), @@ -100,7 +101,7 @@ namespace ETP_NS } // Read a message into our buffer - derived().ws().async_read( + derived().ws()->async_read( receivedBuffer, std::bind( &AbstractSession::on_read, @@ -109,28 +110,9 @@ namespace ETP_NS std::placeholders::_2)); } - void on_handshake(boost::system::error_code ec) - { - if (ec) { - std::cerr << "on_handshake : " << ec.message() << std::endl; - std::cerr << "Sometimes some ETP server require a trailing slash at the end of their URL. Did you also check your optional \"data-partition-id\" additional Header Field?" << std::endl; - return; - } - - if (!responseType.count(boost::beast::http::field::sec_websocket_protocol) || - responseType[boost::beast::http::field::sec_websocket_protocol] != "etp12.energistics.org") - std::cerr << "The client MUST specify the Sec-Websocket-Protocol header value of etp12.energistics.org, and the server MUST reply with the same" << std::endl; - - successfulConnection = true; - webSocketSessionClosed = false; - - send(requestSession, 0, 0x02); - do_read(); - } - - void setMaxWebSocketMessagePayloadSize(int64_t value) final { + void setMaxWebSocketMessagePayloadSize(uint64_t value) final { maxWebSocketMessagePayloadSize = value; - derived().ws().read_message_max(value); + derived().ws()->read_message_max(value); } protected: @@ -140,12 +122,12 @@ namespace ETP_NS Derived& derived() { return static_cast(*this); } void do_write() { - const std::lock_guard specificProtocolHandlersLock(specificProtocolHandlersMutex); if (sendingQueue.empty()) { fesapi_log("The sending queue is empty."); return; } + const std::lock_guard specificProtocolHandlersLock(specificProtocolHandlersMutex); bool previousSentMessageCompleted = specificProtocolHandlers.find(std::get<0>(sendingQueue.front())) == specificProtocolHandlers.end(); if (!previousSentMessageCompleted) { @@ -154,16 +136,26 @@ namespace ETP_NS else { fesapi_log("Sending Message id :", std::to_string(std::get<0>(sendingQueue.front()))); - derived().ws().async_write( + derived().ws()->async_write( boost::asio::buffer(std::get<1>(sendingQueue.front())), - std::bind( - &AbstractSession::on_write, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2)); - - // Register the handler to respond to the sent message - specificProtocolHandlers[std::get<0>(sendingQueue.front())] = std::get<2>(sendingQueue.front()); + [this, self{ this->shared_from_this() }](boost::system::error_code ec, std::size_t) + ->void + { + if (ec) { + std::cerr << "on_write : " << ec.message() << std::endl; + } + else { + // Register the handler to respond to the sent message + const std::lock_guard specificProtocolHandlersLock(specificProtocolHandlersMutex); + specificProtocolHandlers[std::get<0>(sendingQueue.front())] = std::get<2>(sendingQueue.front()); + } + + // Remove the sent message from the queue + const std::lock_guard sendingQueueLock(sendingQueueMutex); + sendingQueue.pop(); + + do_write(); + }); } } }; diff --git a/src/etp/AbstractSession.cpp b/src/etp/AbstractSession.cpp index 20737ee..9409afd 100644 --- a/src/etp/AbstractSession.cpp +++ b/src/etp/AbstractSession.cpp @@ -42,18 +42,46 @@ void AbstractSession::on_read(boost::system::error_code ec, std::size_t bytes_tr boost::ignore_unused(bytes_transferred); if (ec) { + // If read completes with an error, it indicates there is an issue with the connection, so async_close would also complete with an error. + // Therefore, there is no need to call async_close; the destructor of websocket::stream will forcefully close the underlying socket. + flushReceivingBuffer(); if (ec == websocket::error::closed) { // This indicates that the web socket (and consequently etp) session was closed - fesapi_log("The other endpoint closed the web socket (and consequently etp) connection."); - webSocketSessionClosed = true; - flushReceivingBuffer(); + if (etpSessionClosed) { + fesapi_log("The other endpoint closed the web socket (and consequently etp) connection in a graceful way."); + } + else { + std::cerr << "The other endpoint closed the web socket(and consequently etp) connection in a graceful way." << std::endl; + } + } +#if BOOST_VERSION > 106900 + else if (ec == boost::beast::error::timeout) { + // This indicates that the web socket (and consequently etp) session was closed + std::cerr << "Beast timeout has been reached" << std::endl; } +#endif else { - // This indicates an unexpected error - fesapi_log("on_read : error code number", ec.value(), "->", ec.message()); + if (etpSessionClosed) { + // This error may be a common one to ignore in case of SSL short read : https://github.com/boostorg/beast/issues/824 + fesapi_log("It looks that the other endpoint has closed the websocket session in a non graceful way."); + } + else { + // This indicates an unexpected error + std::cerr << "on_read : error code number " << ec.value() << std::endl; + std::cerr << "on_read : error message " << ec.message() << std::endl; + std::cerr << "on_read : error category " << ec.category().name() << std::endl; + } } + const std::lock_guard specificProtocolHandlersLock(specificProtocolHandlersMutex); + specificProtocolHandlers.clear(); + const std::lock_guard sendingQueueLock(sendingQueueMutex); + std::queue< std::tuple, std::shared_ptr> > empty; + std::swap(sendingQueue, empty); + webSocketSessionClosed = true; + etpSessionClosed = true; + return; } @@ -128,7 +156,7 @@ void AbstractSession::on_read(boost::system::error_code ec, std::size_t bytes_tr normalProtocolHandlerIt->second->decodeMessageBody(receivedMh, d); } else { - std::cerr << "Received a message with id " << receivedMh.messageId << " for which non protocol handlers is associated. Protocol " << receivedMhProtocol << std::endl; + std::cerr << "Received a message with id " << receivedMh.messageId << " for which no protocol handler is associated. Protocol " << receivedMhProtocol << std::endl; send(ETP_NS::EtpHelpers::buildSingleMessageProtocolException(4, "The agent does not support the protocol " + std::to_string(receivedMhProtocol) + " identified in a message header."), receivedMh.messageId, 0x02); } } @@ -141,7 +169,7 @@ void AbstractSession::on_read(boost::system::error_code ec, std::size_t bytes_tr send(ETP_NS::EtpHelpers::buildSingleMessageProtocolException(19, "The agent is unable to de-serialize the body of the message id " + std::to_string(receivedMh.messageId) + " : " + std::string(e.what())), 0, 0x02); } - if (specificProtocolHandlers.empty() && isCloseRequested) + if (specificProtocolHandlers.empty() && isCloseRequested_) { etpSessionClosed = true; send(Energistics::Etp::v12::Protocol::Core::CloseSession(), 0, 0x02); @@ -255,7 +283,7 @@ std::vector AbstractSession::lockDataspaces(const std::map AbstractSession::copyToDataspace(const std::map& sourceUris, const std::string& targetDataspaceUri) +std::vector AbstractSession::copyToDataspace(const std::map& sourceDataobjectUris, const std::string& targetDataspaceUri) { std::shared_ptr handlers = getDataspaceOSDUProtocolHandlers(); if (handlers == nullptr) { @@ -263,7 +291,7 @@ std::vector AbstractSession::copyToDataspace(const std::map result = handlers->getSuccessKeys(); @@ -420,7 +448,7 @@ std::vector AbstractSession::deleteDataObjects(const std::map AbstractSession::copyDataObjectsByValue(const std::string& uri, int32_t sourcesDepth, const std::vector& dataObjectTypes) +std::vector AbstractSession::copyDataObjectsByValue(const std::string& sourceDataobjectUri, int32_t sourcesDepth, const std::vector& dataObjectTypes) { std::shared_ptr handlers = getStoreOSDUProtocolHandlers(); if (handlers == nullptr) { @@ -428,7 +456,7 @@ std::vector AbstractSession::copyDataObjectsByValue(const std::stri } Energistics::Etp::v12::Protocol::StoreOSDU::CopyDataObjectsByValue msg; - msg.uri = uri; + msg.uri = sourceDataobjectUri; msg.sourcesDepth = sourcesDepth; msg.dataObjectTypes = dataObjectTypes; sendAndBlock(msg, 0, 0x02); diff --git a/src/etp/AbstractSession.h b/src/etp/AbstractSession.h index 0b1dd39..95fa00a 100644 --- a/src/etp/AbstractSession.h +++ b/src/etp/AbstractSession.h @@ -57,10 +57,7 @@ namespace ETP_NS * If the ETP session is not set up, it returns the nil UUID. */ const boost::uuids::uuid& getIdentifier() { - if (isEtpSessionClosed()) { - identifier = boost::uuids::nil_uuid(); - } - + std::lock_guard lock(identifierMutex); return identifier; } @@ -78,11 +75,6 @@ namespace ETP_NS return _timeOut; } - /** - * The list of subscriptions recorded by customers on this session. - */ - std::unordered_map subscriptions; - /** * Set the Core protocol handlers */ @@ -169,7 +161,7 @@ namespace ETP_NS * @param messageFlags The message flags to be sent within the header * @return The ID of the message that has been put in the sending queue. */ - template void sendAndBlock(const T & mb, int64_t correlationId = 0, int32_t messageFlags = 0) + template int64_t sendAndBlock(const T & mb, int64_t correlationId = 0, int32_t messageFlags = 0) { int64_t msgId = send(mb, correlationId, messageFlags); // The correlationId of the first message MUST be set to 0 and the correlationId of all successive @@ -184,6 +176,8 @@ namespace ETP_NS throw std::runtime_error("Time out waiting for a response of message id " + std::to_string(msgId)); } } + + return msgId; } /** @@ -197,12 +191,22 @@ namespace ETP_NS */ template int64_t sendWithSpecificHandler(const T & mb, std::shared_ptr specificHandler, int64_t correlationId = 0, int32_t messageFlags = 0) { + if (mb.protocolId != 0 || mb.messageTypeId != 1) { + // Wait for a reconnection + while (isEtpSessionClosed() && !isCloseRequested()) {} + // Check if reconnection is successful + if (isEtpSessionClosed() && !isCloseRequested()) { + throw std::runtime_error("The ETP session could not be opened in order to send the message."); + } + } + // Encode the message into AVRO format auto queueItem = encode(mb, correlationId, messageFlags); const std::lock_guard sendingQueueLock(sendingQueueMutex); // Set the handlers which are going to be called for the response to this sent message std::get<2>(queueItem) = specificHandler; + // Push the message into the queue sendingQueue.push(queueItem); fesapi_log("*************************************************"); @@ -223,6 +227,47 @@ namespace ETP_NS return std::get<0>(queueItem); } + /** + * Send a message to the server and register a specific handler for the response and block the thread until the answer of the server has been processed by the handlers + * Please look at setTimeOut if you want to set the default timeout value which is 10 000 ms. + * + * @param mb The ETP message body to send + * @param correlationId The ID of the message which this message is answering to. + * @param messageFlags The message flags to be sent within the header + * @return The ID of the message that has been put in the sending queue. + */ + template int64_t sendWithSpecificHandlerAndBlock(const T& mb, std::shared_ptr specificHandler, int64_t correlationId = 0, int32_t messageFlags = 0) + { + const int64_t msgId = sendWithSpecificHandler(mb, specificHandler, correlationId, messageFlags); + // The correlationId of the first message MUST be set to 0 and the correlationId of all successive + // messages in the same multipart request or notification MUST be set to the messageId of the first + // message of the multipart request or notification. + // If the request message is itself multipart, the correlationId of each message of the multipart + // response MUST be set to the messageId of the FIRST message in the multipart request. + + const auto t_start = std::chrono::high_resolution_clock::now(); + while (isMessageStillProcessing(correlationId == 0 ? msgId : correlationId)) { + if (std::chrono::duration(std::chrono::high_resolution_clock::now() - t_start).count() > _timeOut) { + throw std::runtime_error("Time out waiting for a response of message id " + std::to_string(msgId)); + } + } + + // If the message has not been answered correctly + if (isEtpSessionClosed() && !isCloseRequested()) { + // Wait for a reconnection + while (isEtpSessionClosed() && !isCloseRequested()) {} + // Check if reconnection is successfull + if (isEtpSessionClosed()) { + throw std::runtime_error("The ETP session could not be opened in order to send again the message."); + } + else { + return sendWithSpecificHandlerAndBlock(mb, specificHandler, correlationId, messageFlags); + } + } + + return msgId; + } + /** * Close the web socket session (without sending any ETP message) */ @@ -238,18 +283,6 @@ namespace ETP_NS */ FETPAPI_DLL_IMPORT_OR_EXPORT void on_read(boost::system::error_code ec, std::size_t bytes_transferred); - void on_write(boost::system::error_code ec, std::size_t) { - if(ec) { - std::cerr << "on_write : " << ec.message() << std::endl; - } - - // Remove the sent message from the queue - const std::lock_guard sendingQueueLock(sendingQueueMutex); - sendingQueue.pop(); - - do_write(); - } - void on_close(boost::system::error_code ec) { if(ec) { std::cerr << "on_close : " << ec.message() << std::endl; @@ -262,6 +295,11 @@ namespace ETP_NS webSocketSessionClosed = true; } + /** + * Check if the the closing of the session has been requested by this session or not. + */ + FETPAPI_DLL_IMPORT_OR_EXPORT bool isCloseRequested() const { return isCloseRequested_; } + /** * Check if the websocket session (starting after the HTTP handshake/upgrade) is not opened yet or has been closed. */ @@ -275,10 +313,9 @@ namespace ETP_NS const std::lock_guard specificProtocolHandlersLock(specificProtocolHandlersMutex); return (!sendingQueue.empty() && std::get<0>(sendingQueue.front()) <= msgId) || specificProtocolHandlers.count(msgId) > 0; } - //FETPAPI_DLL_IMPORT_OR_EXPORT bool isMessageStillProcessing(int64_t msgId) const { return specificProtocolHandlers.count(msgId) > 0; } - virtual void setMaxWebSocketMessagePayloadSize(int64_t value) = 0; - int64_t getMaxWebSocketMessagePayloadSize() const { return maxWebSocketMessagePayloadSize; } + virtual void setMaxWebSocketMessagePayloadSize(uint64_t value) = 0; + uint64_t getMaxWebSocketMessagePayloadSize() const { return maxWebSocketMessagePayloadSize; } /**************** ***** CORE ****** @@ -290,7 +327,7 @@ namespace ETP_NS * This method does not block. */ FETPAPI_DLL_IMPORT_OR_EXPORT void close() { - isCloseRequested = true; + isCloseRequested_ = true; sendingQueueMutex.lock(); specificProtocolHandlersMutex.lock(); if (specificProtocolHandlers.empty() && sendingQueue.empty()) { @@ -313,7 +350,7 @@ namespace ETP_NS FETPAPI_DLL_IMPORT_OR_EXPORT void closeAndBlock() { close(); auto t_start = std::chrono::high_resolution_clock::now(); - while (!isEtpSessionClosed()) { + while (!webSocketSessionClosed) { if (std::chrono::duration(std::chrono::high_resolution_clock::now() - t_start).count() > _timeOut) { throw std::runtime_error("Time out waiting for closing"); } @@ -325,8 +362,6 @@ namespace ETP_NS */ FETPAPI_DLL_IMPORT_OR_EXPORT bool isEtpSessionClosed() const { return webSocketSessionClosed || etpSessionClosed; } - void setEtpSessionClosed(bool etpSessionClosed_) { etpSessionClosed = etpSessionClosed_; } - /**************** *** DATASPACE *** ****************/ @@ -334,7 +369,7 @@ namespace ETP_NS /** * A customer sends to a store to discover all dataspaces available on the store. * This function should be used with caution if Dataspace Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * * @param storeLastWriteFilter An optional filter to limit the dataspaces returned by date/time last saved to the store. * The store returns a list of dataspaces whose last changed date/time is greater than the specified date/time. @@ -346,7 +381,7 @@ namespace ETP_NS /** * A customer sends to a store to create one or more dataspaces. * This function should be used with caution if Dataspace Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * * @param dataspaces ETP general map : One each for each dataspace the customer wants to add or update. * @param return The map keys corresponding to the dataspaces which have been put successfully into the store. @@ -356,7 +391,7 @@ namespace ETP_NS /** * A customer sends to a store to delete one or more dataspaces. * This function should be used with caution if Dataspace Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * * @param dataspaceUris ETP general map where the values must be the URIs for the dataspaces the customer wants to delete. * @param return The map keys corresponding to the dataspaces which have been deleted successfully. @@ -370,28 +405,19 @@ namespace ETP_NS /** * A customer sends to a store to discover information of particular dataspaces. * This function should be used with caution if Dataspace OSDU Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * * @param dataspaceUris ETP general map : One each for each dataspace, identified by their URI, the customer wants to get info about. * @param return The dataspaces the store could return. */ FETPAPI_DLL_IMPORT_OR_EXPORT std::vector getDataspaceInfo(const std::map& dataspaceUris); - /** - * Copy by reference some dataspaces into another one. - * This function should be used with caution if Dataspace OSDU Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. - * - * @param sourceDataspaceUris ETP general map : One each for each source dataspace to be copied. They are identified by their URI. - * @param targetDataspaceUri The URI of the ETP dataspace where the sourceDataspaces have to be copied by reference. - * @param return The map keys corresponding to the dataspaces which have been successfully copied into the target dataspace. - */ - FETPAPI_DLL_IMPORT_OR_EXPORT std::vector copyDataspacesContent(const std::map& sourceDataspaceUris, const std::string& targetDataspaceUri); - /** * A customer sends to a store to lock or unlock one or more dataspaces. * This function should be used with caution if Dataspace OSDU Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. + * An OSDU locked dataspace will have its custom data "locked" to true. It will also have its custom datm "read-only" to true for all users. + * As a reminder, a custom data "read-only" can be true with a custom data "locked" to false in case the dataspace is not locked but in read only for the particular current ETP user. * * @param dataspaceUris ETP general map where the values must be the URIs for the dataspaces the customer wants to lock or unlock. * @param lock true for locking the dataspaces, false to unlock the dataspaces @@ -399,16 +425,27 @@ namespace ETP_NS */ FETPAPI_DLL_IMPORT_OR_EXPORT std::vector lockDataspaces(const std::map& dataspaceUris, bool lock); + /** + * Copy by reference some dataspaces into another one. + * This function should be used with caution if Dataspace OSDU Handlers have been overidden. + * It actually sends a message and blocks the current thread until a response has been received from the store. + * + * @param sourceDataspaceUris ETP general map : One each for each source dataspace to be copied. They are identified by their URI. + * @param targetDataspaceUri The URI of the ETP dataspace where the sourceDataspaces have to be copied by reference. + * @param return The map keys corresponding to the dataspaces which have been successfully copied into the target dataspace. + */ + FETPAPI_DLL_IMPORT_OR_EXPORT std::vector copyDataspacesContent(const std::map& sourceDataspaceUris, const std::string& targetDataspaceUri); + /** * Copy by reference some dataobjects into another dataspace. * This function should be used with caution if Dataspace OSDU Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * - * @param sourceUris ETP general map : One each for each source dataobject to be copied. They are identified by their URI. - * @param targetDataspaceUri The URI of the ETP dataspace where the source dataobjects have to be copied by reference. - * @param return The map keys corresponding to the dataobjects which have been successfully copied into the target dataspace. + * @param sourceDataobjectUris ETP general map : One for each source dataobject to be copied. They are identified by their URI. + * @param targetDataspaceUri The URI of the ETP dataspace where the source dataobjects have to be copied by reference. + * @param return The map keys corresponding to the dataobjects which have been successfully copied into the target dataspace. */ - FETPAPI_DLL_IMPORT_OR_EXPORT std::vector copyToDataspace(const std::map& sourceUris, const std::string& targetDataspaceUri); + FETPAPI_DLL_IMPORT_OR_EXPORT std::vector copyToDataspace(const std::map& sourceDataobjectUris, const std::string& targetDataspaceUri); /**************** *** DISCOVERY *** @@ -417,7 +454,7 @@ namespace ETP_NS /** * A Customer sends this message to a store to discover data objects in the store. * This function should be used with caution if Discovery Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * * @param context Includes the URI of the dataspace or data object to begin the discovery, what specific types of data objects are of interest, * and how many "levels" of relationships in the model to discover, among others. @@ -438,7 +475,7 @@ namespace ETP_NS /** * A customer sends to a store to discover data objects that have been deleted (which are sometimes called "tombstones"). * This function should be used with caution if Discovery Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * * @param dataspaceUri The URI of the dataspace where the objects were deleted. * @param deleteTimeFilter An optional filter to filter the discovery on a date when the data object was deleted in a particular store. @@ -459,7 +496,7 @@ namespace ETP_NS /** * A customer sends to a store to get one or more data objects, each identified by a URI. * This function should be used with caution if Store Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * * @param uris ETP general map where the values MUST be the URIs of a data object to be retrieved. * @param return The received dataobjects in a map where the key makes the link between the asked uris and the received dataobjects. @@ -469,7 +506,7 @@ namespace ETP_NS /** * A customer sends to a store to add or update one or more data objects. * This function should be used with caution if Store Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * * @param uris ETP general map where the values MUST be the data for each data object in the request, including each one's URI. * @param return The map keys corresponding to the dataObjects which have been put successfully. @@ -479,7 +516,7 @@ namespace ETP_NS /** * A customer sends to a store to delete one or more data objects from the store. * This function should be used with caution if Store Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * * @param uris ETP general map where the values MUST be the URIs of a data object to be deleted. * @param return The map keys corresponding to the dataObjects which have been deleted successfully. @@ -491,16 +528,20 @@ namespace ETP_NS **********************/ /** - * A customer sends to a store to copy by value a dataobject with potentially some of its sources based on their datatypes. + * A customer sends to a store to copy by value a dataobject in the same dataspace with potentially some of its sources based on their datatypes. * This function should be used with caution if Store OSDU Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * - * @param uri The URI of the dataobject to be copied. - * @param sourcesDepth The number of level if sources of the dataobject to be copied as well. - * @param sourcesDepth The number of level if sources of the dataobject to be copied as well. - * @param return The received dataobjects in a map where the key makes the link between the asked uris and the received dataobjects. + * @param sourceDataobjectUri The URI of the dataobject to be copied. + * @param sourcesDepth The "depth" or how many "levels" (or "jumps") in the data model (graph) from the starting point (specified by the URI) that you want to copy + * Depth MUST always be greater than zero. + * @param dataObjectTypes Optionally, specify the types of data objects that you want to copy. + * The default is an empty array, which means ALL data types negotiated for the current ETP session. + * They ARE case sensitive. EXAMPLES: "witsml20.Well", "witsml20.Wellbore", "prodml21.WellTest", "resqml20.obj_TectonicBoundaryFeature", "eml21.DataAssuranceRecord" + * To indicate that all data objects within a data schema version are supported, you can use a star (*) as a wildcard, EXAMPLE: "witsml20.*", "prodml21.*", "resqml20.*" + * @param return The received dataobjects in a map where the key makes the link between the asked uris and the received dataobjects. */ - FETPAPI_DLL_IMPORT_OR_EXPORT std::vector copyDataObjectsByValue(const std::string& uri, int32_t sourcesDepth = 0, const std::vector& dataObjectTypes = {}); + FETPAPI_DLL_IMPORT_OR_EXPORT std::vector copyDataObjectsByValue(const std::string& sourceDataobjectUri, int32_t sourcesDepth = 0, const std::vector& dataObjectTypes = {}); /**************** ** TRANSACTION ** @@ -509,7 +550,7 @@ namespace ETP_NS /** * A customer sends to a store to begin a transaction. * This function should be used with caution if Transaction Handlers have been overidden. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * * @param dataspaceUris Indicates the dataspaces involved in the transaction. An empty STRING means the default dataspace. An empty LIST means all dataspaces. * @param readOnly Indicates that the request in the transaction is read-only (i.e., "get" messages). @@ -521,7 +562,7 @@ namespace ETP_NS * A customer sends to a store to commit and end a transaction. This message implies that the customer * has received from or sent to the store all the data required for some purpose. The customer asserts that * the data sent in the scope of this transaction is a consistent unit of work. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * @return Failure message or empty string if success */ FETPAPI_DLL_IMPORT_OR_EXPORT std::string rollbackTransaction(); @@ -530,7 +571,7 @@ namespace ETP_NS * A customer sends to a store to cancel a transaction. The store MUST disregard any requests or data sent * with that transaction. The current transaction (the one being canceled) MUST NOT change the state of * the store. - * It actually sends a message and block the current thread until a response has been received from the store. + * It actually sends a message and blocks the current thread until a response has been received from the store. * @return Failure message or empty string if success */ FETPAPI_DLL_IMPORT_OR_EXPORT std::string commitTransaction(); @@ -570,7 +611,7 @@ namespace ETP_NS /// which should be determined by the limits imposed by the WebSocket library used by each endpoint. /// See https://www.boost.org/doc/libs/1_75_0/libs/beast/doc/html/beast/using_websocket/messages.html /// and https://www.boost.org/doc/libs/1_75_0/libs/beast/doc/html/beast/ref/boost__beast__websocket__stream/read_message_max/overload1.html - int64_t maxWebSocketMessagePayloadSize{ 16000000 }; + uint64_t maxWebSocketMessagePayloadSize{ 16000000 }; /// Indicates if the websocket session is opened or not. It becomes false after the websocket handshake std::atomic webSocketSessionClosed{ true }; /// Indicates if the ETP1.2 session is opened or not. It becomes false after the requestSession and openSession message @@ -585,9 +626,11 @@ namespace ETP_NS /// The next available message id. std::atomic messageId; /// The identifier of the session - boost::uuids::uuid identifier; + boost::uuids::uuid identifier{ boost::uuids::nil_uuid() }; + std::mutex identifierMutex; /// Indicates that the endpoint request to close the websocket session - bool isCloseRequested{ false }; + bool isCloseRequested_{ false }; + size_t reconnectionTryCount_ = 0; AbstractSession() = default; @@ -597,6 +640,11 @@ namespace ETP_NS receivedBuffer.consume(receivedBuffer.size()); } + void setEtpSessionClosed(bool etpSessionClosed_) { + etpSessionClosed = etpSessionClosed_; + reconnectionTryCount_ = 0; + } + /** * Write the current buffer on the web socket */ @@ -624,7 +672,7 @@ namespace ETP_NS avro::encode(*e, mh); avro::encode(*e, mb); e->flush(); - const int64_t byteCount = e->byteCount(); + const uint64_t byteCount = e->byteCount(); if (byteCount < maxWebSocketMessagePayloadSize) { return std::make_tuple(mh.messageId, *avro::snapshot(*out).get(), nullptr); @@ -713,5 +761,7 @@ namespace ETP_NS protocolHandlers[protocolId] = coreHandlers; } + + friend void CoreHandlers::decodeMessageBody(const Energistics::Etp::v12::Datatypes::MessageHeader& mh, avro::DecoderPtr d); }; } diff --git a/src/etp/ClientSession.h b/src/etp/ClientSession.h index f11bf40..86403ba 100644 --- a/src/etp/ClientSession.h +++ b/src/etp/ClientSession.h @@ -48,9 +48,7 @@ namespace ETP_NS * Since this is a loop, you may want to operate this method on a dedicated thread not to block your program. * This method returns only when the session is closed. */ - bool run() { - successfulConnection = false; - + void run() { // Look up the domain name before to run the session // It is important to do this before to run the io context. Otherwise running the io context would return immediately if nothing has to be done. resolver.async_resolve( @@ -66,17 +64,39 @@ namespace ETP_NS // Run will return only when there will no more be any uncomplete operations (such as a reading operation for example) getIoContext().run(); - return successfulConnection; + // Try to reconnect up to 10 times + if (!isCloseRequested_ && reconnectionTryCount_ < 10) { + ++reconnectionTryCount_; + std::cerr << "Session has been disconnected, trying to reconnect... " << reconnectionTryCount_ << "/10" << std::endl; + getIoContext().restart(); + run(); + } + + if (!isCloseRequested_ && reconnectionTryCount_ >= 10) { + std::cerr << "Could not reconnect after 10 retries... Give up and close" << reconnectionTryCount_ << "/10" << std::endl; + isCloseRequested_ = true; + } } - virtual void on_resolve(boost::system::error_code ec, tcp::resolver::results_type results) = 0; + void on_resolve(boost::system::error_code ec, tcp::resolver::results_type results) { + if (ec) { + std::cerr << "on_resolve : " << ec.message() << std::endl; + } + + asyncConnect(results); + } + + virtual void asyncConnect(const tcp::resolver::results_type& results) = 0; + virtual bool isTls() const = 0; void on_handshake(boost::system::error_code ec) { if (ec) { - std::cerr << "on_handshake : " << ec.message() << std::endl; - std::cerr << "Sometimes some ETP server require a trailing slash at the end of their URL. Did you also check your optional \"data-partition-id\" additional Header Field?" << std::endl; + std::cerr << "on WS handshake, error code number : " << ec.value() << std::endl; + std::cerr << "on WS handshake, error message : " << ec.message() << std::endl; + std::cerr << "on WS handshake, error category : " << ec.category().name() << std::endl; + std::cerr << "Sometimes some ETP server require a trailing slash at the end of their URL. Did you also check your optional \"data-partition-id\" additional Header Field? Has your token expired?" << std::endl; return; } @@ -84,7 +104,7 @@ namespace ETP_NS responseType[boost::beast::http::field::sec_websocket_protocol] != "etp12.energistics.org") std::cerr << "The client MUST specify the Sec-Websocket-Protocol header value of etp12.energistics.org, and the server MUST reply with the same" << std::endl; - successfulConnection = true; + fesapi_log("Now connected to Websocket"); webSocketSessionClosed = false; send(requestSession, 0, 0x02); @@ -104,7 +124,6 @@ namespace ETP_NS std::map additionalHandshakeHeaderFields_; websocket::response_type responseType; // In order to check handshake sec_websocket_protocol Energistics::Etp::v12::Protocol::Core::RequestSession requestSession; - bool successfulConnection = false; /** * @param initializationParams The initialization parameters of the session including IP host, port, requestedProtocols, supportedDataObjects @@ -114,7 +133,7 @@ namespace ETP_NS */ ClientSession( InitializationParameters const* initializationParams, const std::string& target, const std::string& etpServerAuth, const std::string& proxyAuth = "") : - ioc(4), + ioc(), resolver(ioc), etpServerHost(initializationParams->getEtpServerHost()), etpServerPort(std::to_string(initializationParams->getEtpServerPort())), diff --git a/src/etp/ClientSessionLaunchers.cpp b/src/etp/ClientSessionLaunchers.cpp index 43f672f..8b777b1 100644 --- a/src/etp/ClientSessionLaunchers.cpp +++ b/src/etp/ClientSessionLaunchers.cpp @@ -67,12 +67,15 @@ std::shared_ptr ETP_NS::ClientSessionLaunchers::createCli #ifdef WITH_ETP_SSL if (initializationParams->getEtpServerPort() == 443 || initializationParams->isTlsForced()) { // The SSL context is required, and holds certificates - boost::asio::ssl::context ctx{ boost::asio::ssl::context::sslv23_client }; + // From official ETP documentation : If the ETP server is supporting TLS, it MUST support v1.2 or greater + boost::asio::ssl::context ctx{ boost::asio::ssl::context::tlsv12_client }; ctx.set_default_verify_paths(); ctx.set_options( boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3 + | boost::asio::ssl::context::no_tlsv1 + | boost::asio::ssl::context::no_tlsv1_1 | boost::asio::ssl::context::single_dh_use ); @@ -95,7 +98,7 @@ std::shared_ptr ETP_NS::ClientSessionLaunchers::createCli std::size_t preferredMaxFrameSize = getNegotiatedMaxWebSocketFramePayloadSize(restClientSession->getResponse().body(), initializationParams->getPreferredMaxFrameSize()); - result = std::make_shared(ctx, initializationParams, "/" + initializationParams->getEtpServerUrlPath(), + result = std::make_shared(std::move(ctx), initializationParams, "/" + initializationParams->getEtpServerUrlPath(), authorization, proxyAuthorization, initializationParams->getAdditionalHandshakeHeaderFields(), preferredMaxFrameSize); } diff --git a/src/etp/EtpHelpers.cpp b/src/etp/EtpHelpers.cpp index a194952..366eea7 100644 --- a/src/etp/EtpHelpers.cpp +++ b/src/etp/EtpHelpers.cpp @@ -18,15 +18,16 @@ under the License. -----------------------------------------------------------------------*/ #include "EtpHelpers.h" -#if (defined(_WIN32) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)))) #include -#endif -#include "AbstractSession.h" #include "EtpException.h" std::string ETP_NS::EtpHelpers::getDataObjectType(const std::string& uri) { + if (!ETP_NS::EtpHelpers::validateDataObjectUri(uri)) { + throw ETP_NS::EtpException(9, "The dataobject ETP URI \"" + uri + "\" is invalid."); + } + const std::size_t lastSlash = uri.find_last_of("/"); const std::size_t lastOpenParenthesis = uri.find_last_of("("); return lastSlash != std::string::npos && lastOpenParenthesis != std::string::npos @@ -36,9 +37,13 @@ std::string ETP_NS::EtpHelpers::getDataObjectType(const std::string& uri) std::string ETP_NS::EtpHelpers::getDataspaceUri(const std::string& uri) { + if (!ETP_NS::EtpHelpers::validateUri(uri)) { + throw ETP_NS::EtpException(9, "The ETP URI \"" + uri + "\" is invalid."); + } + const size_t dataspacePos = uri.find("dataspace('"); if (dataspacePos == std::string::npos) { - return ""; + return "eml:///"; } const size_t closingParenthesisPos = uri.find(')', dataspacePos); @@ -48,72 +53,23 @@ std::string ETP_NS::EtpHelpers::getDataspaceUri(const std::string& uri) return uri.substr(0, closingParenthesisPos+1); } -Energistics::Etp::v12::Datatypes::ErrorInfo ETP_NS::EtpHelpers::validateUri(const std::string & uri, ETP_NS::AbstractSession* session) +bool ETP_NS::EtpHelpers::validateUri(const std::string & uri) { - Energistics::Etp::v12::Datatypes::ErrorInfo errorInfo; - errorInfo.code = -1; // Regular expressions are not handled before GCC 4.9 // https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions -#if (defined(_WIN32) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)))) - const bool result = + return std::regex_match(uri, std::regex("^eml:///(dataspace[(]'.*'[)])?", std::regex::ECMAScript)) || std::regex_match(uri, std::regex("^eml:///(dataspace[(]'.*'[)]/)?(resqml20|eml20)\.obj_[a-zA-Z0-9]+[(][a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}(,.*)?[)]", std::regex::ECMAScript)) || - std::regex_match(uri, std::regex("^eml:///(dataspace[(]'.*'[)]/)?(witsml|resqml|prodml|eml)([0-9]{2})\[a-zA-Z0-9]+[(][a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}(,.*)?[)]", std::regex::ECMAScript)); - if (!result) { - std::cerr << "The URI \"" + uri + "\" is invalid." << std::endl; - } -#else - const bool result = uri.find("eml:///") == 0; -#endif - - if (!result) { - errorInfo.code = 9; - errorInfo.message = "The URI " + uri + " is invalid."; - - if (session != nullptr) { - Energistics::Etp::v12::Protocol::Core::ProtocolException error; - error.error.emplace(errorInfo); - session->send(error); - } - } - - return errorInfo; - + std::regex_match(uri, std::regex("^eml:///(dataspace[(]'.*'[)]/)?(witsml|resqml|prodml|eml)([0-9]{2})\.[a-zA-Z0-9]+[(][a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}(,.*)?[)]", std::regex::ECMAScript)); } -Energistics::Etp::v12::Datatypes::ErrorInfo ETP_NS::EtpHelpers::validateDataObjectUri(const std::string & uri, AbstractSession* session) +bool ETP_NS::EtpHelpers::validateDataObjectUri(const std::string & uri) { - Energistics::Etp::v12::Datatypes::ErrorInfo errorInfo; - errorInfo.code = -1; // Regular expressions are not handled before GCC 4.9 // https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions -#if (defined(_WIN32) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)))) - const bool result = (uri.find("resqml20") != std::string::npos || uri.find("eml20") != std::string::npos) + return (uri.find("resqml20") != std::string::npos || uri.find("eml20") != std::string::npos) ? std::regex_match(uri, std::regex("^eml:///(dataspace[(]'.*'[)]/)?(resqml20|eml20)\.obj_[a-zA-Z0-9]+[(][a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}(,.*)?[)]", std::regex::ECMAScript)) : std::regex_match(uri, std::regex("^eml:///(dataspace[(]'.*'[)]/)?(witsml|resqml|prodml|eml)([0-9]{2})\.[a-zA-Z0-9]+[(][a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}(,.*)?[)]", std::regex::ECMAScript)); - if (!result) { - std::cerr << "The data object URI \"" + uri + "\" is invalid." << std::endl; - } -#else - const bool result = uri.find("eml:///") == 0 && - (uri.find("resqml20.obj_") != std::string::npos || uri.find("eml20.obj_") != std::string::npos || - uri.find("witsml20.") != std::string::npos || uri.find("eml21.") != std::string::npos || - uri.find("prodml21.") != std::string::npos || uri.find("eml22.") != std::string::npos || - uri.find("witsml21.") != std::string::npos); -#endif - - if (!result) { - errorInfo.code = 9; - errorInfo.message = "The data object URI " + uri + " is invalid."; - - if (session != nullptr) { - Energistics::Etp::v12::Protocol::Core::ProtocolException error; - error.error.emplace(errorInfo); - session->send(error); - } - } - - return errorInfo; } Energistics::Etp::v12::Protocol::Core::ProtocolException ETP_NS::EtpHelpers::buildSingleMessageProtocolException(int32_t m_code, const std::string & m_message) @@ -131,11 +87,11 @@ std::pair ETP_NS::EtpHelpers::getUuidAndVersionFromUri { std::pair result; - Energistics::Etp::v12::Datatypes::ErrorInfo error = ETP_NS::EtpHelpers::validateDataObjectUri(uri); - if (error.code > -1) { - throw ETP_NS::EtpException(error.code, error.message); + if (!ETP_NS::EtpHelpers::validateDataObjectUri(uri)) { + throw ETP_NS::EtpException(9, "Invalid URI"); } + // old code to be updated if (uri[6] != '/') { throw ETP_NS::EtpException(2, "The URI " + uri + " uses some dataspaces. This agent does not support dataspace."); } diff --git a/src/etp/EtpHelpers.h b/src/etp/EtpHelpers.h index 80b509e..aa30a4a 100644 --- a/src/etp/EtpHelpers.h +++ b/src/etp/EtpHelpers.h @@ -40,7 +40,6 @@ namespace COMMON_NS namespace ETP_NS { - class AbstractSession; namespace EtpHelpers { @@ -61,14 +60,14 @@ namespace ETP_NS FETPAPI_DLL_IMPORT_OR_EXPORT std::string getDataspaceUri(const std::string& uri); /** - * @param session Provide this parameter if you want to send a protocol exception in case of non validation. + * Validate an ETP URI */ - FETPAPI_DLL_IMPORT_OR_EXPORT Energistics::Etp::v12::Datatypes::ErrorInfo validateUri(const std::string & uri, AbstractSession* session = nullptr); + FETPAPI_DLL_IMPORT_OR_EXPORT bool validateUri(const std::string& uri); /** - * @param session Provide this parameter if you want to send a protocol exception in case of non validation. + * Validate an ETP dataobject URI */ - FETPAPI_DLL_IMPORT_OR_EXPORT Energistics::Etp::v12::Datatypes::ErrorInfo validateDataObjectUri(const std::string & uri, AbstractSession* session = nullptr); + FETPAPI_DLL_IMPORT_OR_EXPORT bool validateDataObjectUri(const std::string& uri); /** * Build a protocol exception message which only contains a single error message (not a messsage map). diff --git a/src/etp/EtpMessages.h b/src/etp/EtpMessages.h index f60b982..ae35941 100644 --- a/src/etp/EtpMessages.h +++ b/src/etp/EtpMessages.h @@ -1,18 +1,20 @@ #ifndef ETP_MESSAGES__ #define ETP_MESSAGES__ +#include #include +#include #include -#include -#include +#include #include -#include -#include -#include -#include +#include +#include +#include + #include #include #include +#include namespace Energistics { namespace Etp { @@ -347,9 +349,9 @@ namespace Energistics { namespace Protocol { namespace Dataspace { struct GetDataspaces { - boost::optional storeLastWriteFilter; - bool has_storeLastWriteFilter() const { return storeLastWriteFilter.is_initialized(); } - int64_t get_storeLastWriteFilter() const { return storeLastWriteFilter.get(); } + std::optional storeLastWriteFilter; + bool has_storeLastWriteFilter() const { return storeLastWriteFilter.has_value(); } + int64_t get_storeLastWriteFilter() const { return storeLastWriteFilter.value(); } static constexpr int messageTypeId=1; static constexpr uint16_t protocolId = static_cast::type>(Energistics::Etp::v12::Datatypes::Protocol::Dataspace); }; @@ -400,9 +402,9 @@ namespace Energistics { namespace Discovery { struct GetDeletedResources { std::string dataspaceUri; - boost::optional deleteTimeFilter; - bool has_deleteTimeFilter() const { return deleteTimeFilter.is_initialized(); } - int64_t get_deleteTimeFilter() const { return deleteTimeFilter.get(); } + std::optional deleteTimeFilter; + bool has_deleteTimeFilter() const { return deleteTimeFilter.has_value(); } + int64_t get_deleteTimeFilter() const { return deleteTimeFilter.value(); } std::vector dataObjectTypes; static constexpr int messageTypeId=5; static constexpr uint16_t protocolId = static_cast::type>(Energistics::Etp::v12::Datatypes::Protocol::Discovery); @@ -737,7 +739,7 @@ namespace Energistics { namespace v12 { namespace Datatypes { struct ArrayOfNullableBoolean { - std::vector> values; + std::vector> values; }; } } @@ -758,7 +760,7 @@ namespace Energistics { namespace v12 { namespace Datatypes { struct ArrayOfNullableInt { - std::vector> values; + std::vector> values; }; } } @@ -779,7 +781,7 @@ namespace Energistics { namespace v12 { namespace Datatypes { struct ArrayOfNullableLong { - std::vector> values; + std::vector> values; }; } } @@ -847,122 +849,80 @@ namespace Energistics { namespace Datatypes { struct AnyArrayitem_t { private: - size_t idx_=0; - boost::any value_; + std::variant< + Energistics::Etp::v12::Datatypes::ArrayOfBoolean, + Energistics::Etp::v12::Datatypes::ArrayOfInt, + Energistics::Etp::v12::Datatypes::ArrayOfLong, + Energistics::Etp::v12::Datatypes::ArrayOfFloat, + Energistics::Etp::v12::Datatypes::ArrayOfDouble, + Energistics::Etp::v12::Datatypes::ArrayOfString, + std::string + > value_; public: - size_t idx() const { return idx_; } + size_t idx() const { return value_.index(); } Energistics::Etp::v12::Datatypes::ArrayOfBoolean const & get_ArrayOfBoolean() const { - if (idx_ != 0) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfBoolean const & >(value_); - } - Energistics::Etp::v12::Datatypes::ArrayOfBoolean& get_ArrayOfBoolean() { - if (idx_ != 0) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfBoolean& >(value_); + return std::get(value_); } void set_ArrayOfBoolean(const Energistics::Etp::v12::Datatypes::ArrayOfBoolean& v) { - idx_ = 0; - value_ = v; + value_.emplace(v); } - Energistics::Etp::v12::Datatypes::ArrayOfInt const & get_ArrayOfInt() const { - if (idx_ != 1) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfInt const & >(value_); + void set_ArrayOfBoolean(const Energistics::Etp::v12::Datatypes::ArrayOfBoolean&& v) { + value_.emplace(std::move(v)); } - Energistics::Etp::v12::Datatypes::ArrayOfInt& get_ArrayOfInt() { - if (idx_ != 1) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfInt& >(value_); + Energistics::Etp::v12::Datatypes::ArrayOfInt const & get_ArrayOfInt() const { + return std::get(value_); } void set_ArrayOfInt(const Energistics::Etp::v12::Datatypes::ArrayOfInt& v) { - idx_ = 1; - value_ = v; + value_.emplace(v); } - Energistics::Etp::v12::Datatypes::ArrayOfLong const & get_ArrayOfLong() const { - if (idx_ != 2) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfLong const & >(value_); + void set_ArrayOfInt(const Energistics::Etp::v12::Datatypes::ArrayOfInt&& v) { + value_.emplace(std::move(v)); } - Energistics::Etp::v12::Datatypes::ArrayOfLong& get_ArrayOfLong() { - if (idx_ != 2) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfLong& >(value_); + Energistics::Etp::v12::Datatypes::ArrayOfLong const & get_ArrayOfLong() const { + return std::get(value_); } void set_ArrayOfLong(const Energistics::Etp::v12::Datatypes::ArrayOfLong& v) { - idx_ = 2; - value_ = v; + value_.emplace(v); } - Energistics::Etp::v12::Datatypes::ArrayOfFloat const & get_ArrayOfFloat() const { - if (idx_ != 3) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfFloat const & >(value_); + void set_ArrayOfLong(const Energistics::Etp::v12::Datatypes::ArrayOfLong&& v) { + value_.emplace(std::move(v)); } - Energistics::Etp::v12::Datatypes::ArrayOfFloat& get_ArrayOfFloat() { - if (idx_ != 3) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfFloat& >(value_); + Energistics::Etp::v12::Datatypes::ArrayOfFloat const & get_ArrayOfFloat() const { + return std::get(value_); } void set_ArrayOfFloat(const Energistics::Etp::v12::Datatypes::ArrayOfFloat& v) { - idx_ = 3; - value_ = v; + value_.emplace(v); } - Energistics::Etp::v12::Datatypes::ArrayOfDouble const & get_ArrayOfDouble() const { - if (idx_ != 4) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfDouble const & >(value_); + void set_ArrayOfFloat(const Energistics::Etp::v12::Datatypes::ArrayOfFloat&& v) { + value_.emplace(std::move(v)); } - Energistics::Etp::v12::Datatypes::ArrayOfDouble& get_ArrayOfDouble() { - if (idx_ != 4) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfDouble& >(value_); + Energistics::Etp::v12::Datatypes::ArrayOfDouble const & get_ArrayOfDouble() const { + return std::get(value_); } void set_ArrayOfDouble(const Energistics::Etp::v12::Datatypes::ArrayOfDouble& v) { - idx_ = 4; - value_ = v; + value_.emplace(v); } - Energistics::Etp::v12::Datatypes::ArrayOfString const & get_ArrayOfString() const { - if (idx_ != 5) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfString const & >(value_); + void set_ArrayOfDouble(const Energistics::Etp::v12::Datatypes::ArrayOfDouble&& v) { + value_.emplace(std::move(v)); } - Energistics::Etp::v12::Datatypes::ArrayOfString& get_ArrayOfString() { - if (idx_ != 5) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfString& >(value_); + Energistics::Etp::v12::Datatypes::ArrayOfString const & get_ArrayOfString() const { + return std::get(value_); } void set_ArrayOfString(const Energistics::Etp::v12::Datatypes::ArrayOfString& v) { - idx_ = 5; - value_ = v; + value_.emplace(v); } - std::string const & get_bytes() const { - if (idx_ != 6) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< std::string const & >(value_); + void set_ArrayOfString(const Energistics::Etp::v12::Datatypes::ArrayOfString&& v) { + value_.emplace(std::move(v)); } - std::string& get_bytes() { - if (idx_ != 6) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< std::string& >(value_); + std::string const & get_bytes() const { + return std::get(value_); } void set_bytes(const std::string& v) { - idx_ = 6; - value_ = v; + value_.emplace(v); + } + void set_bytes(const std::string&& v) { + value_.emplace(std::move(v)); } }; } @@ -1015,42 +975,42 @@ namespace avro { { Energistics::Etp::v12::Datatypes::ArrayOfInt vv; avro::decode(d, vv); - v.set_ArrayOfInt(vv); + v.set_ArrayOfInt(std::move(vv)); } break; case 2: { Energistics::Etp::v12::Datatypes::ArrayOfLong vv; avro::decode(d, vv); - v.set_ArrayOfLong(vv); + v.set_ArrayOfLong(std::move(vv)); } break; case 3: { Energistics::Etp::v12::Datatypes::ArrayOfFloat vv; avro::decode(d, vv); - v.set_ArrayOfFloat(vv); + v.set_ArrayOfFloat(std::move(vv)); } break; case 4: { Energistics::Etp::v12::Datatypes::ArrayOfDouble vv; avro::decode(d, vv); - v.set_ArrayOfDouble(vv); + v.set_ArrayOfDouble(std::move(vv)); } break; case 5: { Energistics::Etp::v12::Datatypes::ArrayOfString vv; avro::decode(d, vv); - v.set_ArrayOfString(vv); + v.set_ArrayOfString(std::move(vv)); } break; case 6: { std::string vv; avro::decode(d, vv); - v.set_bytes(vv); + v.set_bytes(std::move(vv)); } break; } @@ -1188,299 +1148,140 @@ namespace Energistics { namespace Datatypes { struct DataValueitem_t { private: - size_t idx_=0; - boost::any value_; + std::variant< + std::nullptr_t, + bool, + int32_t, + int64_t, + float, + double, + std::string, + Energistics::Etp::v12::Datatypes::ArrayOfBoolean, + Energistics::Etp::v12::Datatypes::ArrayOfNullableBoolean, + Energistics::Etp::v12::Datatypes::ArrayOfInt, + Energistics::Etp::v12::Datatypes::ArrayOfNullableInt, + Energistics::Etp::v12::Datatypes::ArrayOfLong, + Energistics::Etp::v12::Datatypes::ArrayOfNullableLong, + Energistics::Etp::v12::Datatypes::ArrayOfFloat, + Energistics::Etp::v12::Datatypes::ArrayOfDouble, + Energistics::Etp::v12::Datatypes::ArrayOfString, + Energistics::Etp::v12::Datatypes::ArrayOfBytes, + std::vector, + Energistics::Etp::v12::Datatypes::AnySparseArray + > value_; public: - size_t idx() const { return idx_; } - bool is_null() const { return idx_==0; } - void set_null() { idx_=0; value_ = boost::any(); } - bool const & get_boolean() const { - if (idx_ != 1) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< bool const & >(value_); - } - bool& get_boolean() { - if (idx_ != 1) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< bool& >(value_); + size_t idx() const { return value_.index(); } + bool is_null() const { return idx() == 0; } + void set_null() { value_ = nullptr; } + bool get_boolean() const { + return std::get(value_); } void set_boolean(const bool& v) { - idx_ = 1; value_ = v; } - int32_t const & get_int() const { - if (idx_ != 2) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< int32_t const & >(value_); - } - int32_t& get_int() { - if (idx_ != 2) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< int32_t& >(value_); + int32_t get_int() const { + return std::get(value_); } void set_int(const int32_t& v) { - idx_ = 2; value_ = v; } - int64_t const & get_long() const { - if (idx_ != 3) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< int64_t const & >(value_); - } - int64_t& get_long() { - if (idx_ != 3) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< int64_t& >(value_); + int64_t get_long() const { + return std::get(value_); } void set_long(const int64_t& v) { - idx_ = 3; value_ = v; } - float const & get_float() const { - if (idx_ != 4) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< float const & >(value_); - } - float& get_float() { - if (idx_ != 4) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< float& >(value_); + float get_float() const { + return std::get(value_); } void set_float(const float& v) { - idx_ = 4; value_ = v; } - double const & get_double() const { - if (idx_ != 5) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< double const & >(value_); - } - double& get_double() { - if (idx_ != 5) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< double& >(value_); + double get_double() const { + return std::get(value_); } void set_double(const double& v) { - idx_ = 5; value_ = v; } std::string const & get_string() const { - if (idx_ != 6) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< std::string const & >(value_); - } - std::string& get_string() { - if (idx_ != 6) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< std::string& >(value_); + return std::get(value_); } void set_string(const std::string& v) { - idx_ = 6; value_ = v; } Energistics::Etp::v12::Datatypes::ArrayOfBoolean const & get_ArrayOfBoolean() const { - if (idx_ != 7) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfBoolean const & >(value_); - } - Energistics::Etp::v12::Datatypes::ArrayOfBoolean& get_ArrayOfBoolean() { - if (idx_ != 7) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfBoolean& >(value_); + return std::get(value_); } void set_ArrayOfBoolean(const Energistics::Etp::v12::Datatypes::ArrayOfBoolean& v) { - idx_ = 7; value_ = v; } Energistics::Etp::v12::Datatypes::ArrayOfNullableBoolean const & get_ArrayOfNullableBoolean() const { - if (idx_ != 8) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfNullableBoolean const & >(value_); - } - Energistics::Etp::v12::Datatypes::ArrayOfNullableBoolean& get_ArrayOfNullableBoolean() { - if (idx_ != 8) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfNullableBoolean& >(value_); + return std::get(value_); } void set_ArrayOfNullableBoolean(const Energistics::Etp::v12::Datatypes::ArrayOfNullableBoolean& v) { - idx_ = 8; value_ = v; } Energistics::Etp::v12::Datatypes::ArrayOfInt const & get_ArrayOfInt() const { - if (idx_ != 9) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfInt const & >(value_); - } - Energistics::Etp::v12::Datatypes::ArrayOfInt& get_ArrayOfInt() { - if (idx_ != 9) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfInt& >(value_); + return std::get(value_); } void set_ArrayOfInt(const Energistics::Etp::v12::Datatypes::ArrayOfInt& v) { - idx_ = 9; value_ = v; } Energistics::Etp::v12::Datatypes::ArrayOfNullableInt const & get_ArrayOfNullableInt() const { - if (idx_ != 10) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfNullableInt const & >(value_); - } - Energistics::Etp::v12::Datatypes::ArrayOfNullableInt& get_ArrayOfNullableInt() { - if (idx_ != 10) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfNullableInt& >(value_); + return std::get(value_); } void set_ArrayOfNullableInt(const Energistics::Etp::v12::Datatypes::ArrayOfNullableInt& v) { - idx_ = 10; value_ = v; } Energistics::Etp::v12::Datatypes::ArrayOfLong const & get_ArrayOfLong() const { - if (idx_ != 11) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfLong const & >(value_); - } - Energistics::Etp::v12::Datatypes::ArrayOfLong& get_ArrayOfLong() { - if (idx_ != 11) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfLong& >(value_); + return std::get(value_); } void set_ArrayOfLong(const Energistics::Etp::v12::Datatypes::ArrayOfLong& v) { - idx_ = 11; value_ = v; } Energistics::Etp::v12::Datatypes::ArrayOfNullableLong const & get_ArrayOfNullableLong() const { - if (idx_ != 12) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfNullableLong const & >(value_); - } - Energistics::Etp::v12::Datatypes::ArrayOfNullableLong& get_ArrayOfNullableLong() { - if (idx_ != 12) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfNullableLong& >(value_); + return std::get(value_); } void set_ArrayOfNullableLong(const Energistics::Etp::v12::Datatypes::ArrayOfNullableLong& v) { - idx_ = 12; value_ = v; } Energistics::Etp::v12::Datatypes::ArrayOfFloat const & get_ArrayOfFloat() const { - if (idx_ != 13) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfFloat const & >(value_); - } - Energistics::Etp::v12::Datatypes::ArrayOfFloat& get_ArrayOfFloat() { - if (idx_ != 13) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfFloat& >(value_); + return std::get(value_); } void set_ArrayOfFloat(const Energistics::Etp::v12::Datatypes::ArrayOfFloat& v) { - idx_ = 13; value_ = v; } Energistics::Etp::v12::Datatypes::ArrayOfDouble const & get_ArrayOfDouble() const { - if (idx_ != 14) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfDouble const & >(value_); - } - Energistics::Etp::v12::Datatypes::ArrayOfDouble& get_ArrayOfDouble() { - if (idx_ != 14) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfDouble& >(value_); + return std::get(value_); } void set_ArrayOfDouble(const Energistics::Etp::v12::Datatypes::ArrayOfDouble& v) { - idx_ = 14; value_ = v; } Energistics::Etp::v12::Datatypes::ArrayOfString const & get_ArrayOfString() const { - if (idx_ != 15) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfString const & >(value_); - } - Energistics::Etp::v12::Datatypes::ArrayOfString& get_ArrayOfString() { - if (idx_ != 15) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfString& >(value_); + return std::get(value_); } void set_ArrayOfString(const Energistics::Etp::v12::Datatypes::ArrayOfString& v) { - idx_ = 15; value_ = v; } Energistics::Etp::v12::Datatypes::ArrayOfBytes const & get_ArrayOfBytes() const { - if (idx_ != 16) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfBytes const & >(value_); - } - Energistics::Etp::v12::Datatypes::ArrayOfBytes& get_ArrayOfBytes() { - if (idx_ != 16) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ArrayOfBytes& >(value_); + return std::get(value_); } void set_ArrayOfBytes(const Energistics::Etp::v12::Datatypes::ArrayOfBytes& v) { - idx_ = 16; value_ = v; } - std::string const & get_bytes() const { - if (idx_ != 17) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< std::string const & >(value_); - } - std::string& get_bytes() { - if (idx_ != 17) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< std::string& >(value_); + std::string get_bytes() const { + const auto& bytes = std::get>(value_); + return std::string(reinterpret_cast(bytes.data()), bytes.size()); + } void set_bytes(const std::string& v) { - idx_ = 17; value_ = v; } Energistics::Etp::v12::Datatypes::AnySparseArray const & get_AnySparseArray() const { - if (idx_ != 18) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::AnySparseArray const & >(value_); - } - Energistics::Etp::v12::Datatypes::AnySparseArray& get_AnySparseArray() { - if (idx_ != 18) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::AnySparseArray& >(value_); + return std::get(value_); } void set_AnySparseArray(const Energistics::Etp::v12::Datatypes::AnySparseArray& v) { - idx_ = 18; value_ = v; } }; @@ -1492,7 +1293,6 @@ namespace avro { template<> struct codec_traits { static void encode(Encoder& e, Energistics::Etp::v12::Datatypes::DataValueitem_t v) { - e.encodeUnionIndex(v.idx()); switch (v.idx()) { case 0: @@ -1841,9 +1641,9 @@ namespace Energistics { namespace Protocol { namespace Core { struct ProtocolException { - boost::optional error; - bool has_error() const { return error.is_initialized(); } - Energistics::Etp::v12::Datatypes::ErrorInfo get_error() const { return error.get(); } + std::optional error; + bool has_error() const { return error.has_value(); } + Energistics::Etp::v12::Datatypes::ErrorInfo get_error() const { return error.value(); } std::map errors; static constexpr int messageTypeId=1000; static constexpr uint16_t protocolId = static_cast::type>(Energistics::Etp::v12::Datatypes::Protocol::Core); @@ -2770,59 +2570,33 @@ namespace Energistics { namespace Datatypes { struct IndexValueitem_t { private: - size_t idx_=0; - boost::any value_; + std::variant< + std::nullptr_t, + int64_t, + double, + Energistics::Etp::v12::Datatypes::ChannelData::PassIndexedDepth + > value_; public: - size_t idx() const { return idx_; } - bool is_null() const { return idx_==0; } - void set_null() { idx_=0; value_ = boost::any(); } - int64_t const & get_long() const { - if (idx_ != 1) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< int64_t const & >(value_); - } - int64_t& get_long() { - if (idx_ != 1) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< int64_t& >(value_); + size_t idx() const { return value_.index(); } + bool is_null() const { return idx() == 0; } + void set_null() { value_ = nullptr; } + int64_t get_long() const { + return std::get(value_); } void set_long(const int64_t& v) { - idx_ = 1; value_ = v; } - double const & get_double() const { - if (idx_ != 2) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< double const & >(value_); - } - double& get_double() { - if (idx_ != 2) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< double& >(value_); + double get_double() const { + return std::get(value_); } void set_double(const double& v) { - idx_ = 2; value_ = v; } Energistics::Etp::v12::Datatypes::ChannelData::PassIndexedDepth const & get_PassIndexedDepth() const { - if (idx_ != 3) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ChannelData::PassIndexedDepth const & >(value_); - } - Energistics::Etp::v12::Datatypes::ChannelData::PassIndexedDepth& get_PassIndexedDepth() { - if (idx_ != 3) { - throw avro::Exception("Invalid type for union."); - } - return boost::any_cast< Energistics::Etp::v12::Datatypes::ChannelData::PassIndexedDepth& >(value_); + return std::get(value_); } void set_PassIndexedDepth(const Energistics::Etp::v12::Datatypes::ChannelData::PassIndexedDepth& v) { - idx_ = 3; value_ = v; } }; @@ -2918,9 +2692,9 @@ namespace Energistics { int64_t channelId = 0; Energistics::Etp::v12::Datatypes::IndexValue startIndex; bool dataChanges=false; - boost::optional requestLatestIndexCount; - bool has_requestLatestIndexCount() const { return requestLatestIndexCount.is_initialized(); } - int32_t get_requestLatestIndexCount() const { return requestLatestIndexCount.get(); } + std::optional requestLatestIndexCount; + bool has_requestLatestIndexCount() const { return requestLatestIndexCount.has_value(); } + int32_t get_requestLatestIndexCount() const { return requestLatestIndexCount.value(); } }; } } @@ -4432,12 +4206,12 @@ namespace Energistics { Energistics::Etp::v12::Datatypes::Object::ContextInfo context; Energistics::Etp::v12::Datatypes::Object::ContextScopeKind scope = Energistics::Etp::v12::Datatypes::Object::ContextScopeKind::targets; bool countObjects=false; - boost::optional storeLastWriteFilter; - bool has_storeLastWriteFilter() const { return storeLastWriteFilter.is_initialized(); } - int64_t get_storeLastWriteFilter() const { return storeLastWriteFilter.get(); } - boost::optional activeStatusFilter; - bool has_activeStatusFilter() const { return activeStatusFilter.is_initialized(); } - Energistics::Etp::v12::Datatypes::Object::ActiveStatusKind get_activeStatusFilter() const { return activeStatusFilter.get(); } + std::optional storeLastWriteFilter; + bool has_storeLastWriteFilter() const { return storeLastWriteFilter.has_value(); } + int64_t get_storeLastWriteFilter() const { return storeLastWriteFilter.value(); } + std::optional activeStatusFilter; + bool has_activeStatusFilter() const { return activeStatusFilter.has_value(); } + Energistics::Etp::v12::Datatypes::Object::ActiveStatusKind get_activeStatusFilter() const { return activeStatusFilter.value(); } bool includeEdges=false; static constexpr int messageTypeId=1; static constexpr uint16_t protocolId = static_cast::type>(Energistics::Etp::v12::Datatypes::Protocol::Discovery); @@ -4475,12 +4249,12 @@ namespace Energistics { struct FindResources { Energistics::Etp::v12::Datatypes::Object::ContextInfo context; Energistics::Etp::v12::Datatypes::Object::ContextScopeKind scope = Energistics::Etp::v12::Datatypes::Object::ContextScopeKind::targets; - boost::optional storeLastWriteFilter; - bool has_storeLastWriteFilter() const { return storeLastWriteFilter.is_initialized(); } - int64_t get_storeLastWriteFilter() const { return storeLastWriteFilter.get(); } - boost::optional activeStatusFilter; - bool has_activeStatusFilter() const { return activeStatusFilter.is_initialized(); } - Energistics::Etp::v12::Datatypes::Object::ActiveStatusKind get_activeStatusFilter() const { return activeStatusFilter.get(); } + std::optional storeLastWriteFilter; + bool has_storeLastWriteFilter() const { return storeLastWriteFilter.has_value(); } + int64_t get_storeLastWriteFilter() const { return storeLastWriteFilter.value(); } + std::optional activeStatusFilter; + bool has_activeStatusFilter() const { return activeStatusFilter.has_value(); } + Energistics::Etp::v12::Datatypes::Object::ActiveStatusKind get_activeStatusFilter() const { return activeStatusFilter.value(); } static constexpr int messageTypeId=1; static constexpr uint16_t protocolId = static_cast::type>(Energistics::Etp::v12::Datatypes::Protocol::DiscoveryQuery); }; @@ -4513,12 +4287,12 @@ namespace Energistics { struct FindDataObjects { Energistics::Etp::v12::Datatypes::Object::ContextInfo context; Energistics::Etp::v12::Datatypes::Object::ContextScopeKind scope = Energistics::Etp::v12::Datatypes::Object::ContextScopeKind::targets; - boost::optional storeLastWriteFilter; - bool has_storeLastWriteFilter() const { return storeLastWriteFilter.is_initialized(); } - int64_t get_storeLastWriteFilter() const { return storeLastWriteFilter.get(); } - boost::optional activeStatusFilter; - bool has_activeStatusFilter() const { return activeStatusFilter.is_initialized(); } - Energistics::Etp::v12::Datatypes::Object::ActiveStatusKind get_activeStatusFilter() const { return activeStatusFilter.get(); } + std::optional storeLastWriteFilter; + bool has_storeLastWriteFilter() const { return storeLastWriteFilter.has_value(); } + int64_t get_storeLastWriteFilter() const { return storeLastWriteFilter.value(); } + std::optional activeStatusFilter; + bool has_activeStatusFilter() const { return activeStatusFilter.has_value(); } + Energistics::Etp::v12::Datatypes::Object::ActiveStatusKind get_activeStatusFilter() const { return activeStatusFilter.value(); } std::string format; static constexpr int messageTypeId=1; static constexpr uint16_t protocolId = static_cast::type>(Energistics::Etp::v12::Datatypes::Protocol::StoreQuery); @@ -4612,12 +4386,12 @@ namespace Energistics { std::string uri; std::vector alternateUris; std::string name; - boost::optional sourceCount; - bool has_sourceCount() const { return sourceCount.is_initialized(); } - int32_t get_sourceCount() const { return sourceCount.get(); } - boost::optional targetCount; - bool has_targetCount() const { return targetCount.is_initialized(); } - int32_t get_targetCount() const { return targetCount.get(); } + std::optional sourceCount; + bool has_sourceCount() const { return sourceCount.has_value(); } + int32_t get_sourceCount() const { return sourceCount.value(); } + std::optional targetCount; + bool has_targetCount() const { return targetCount.has_value(); } + int32_t get_targetCount() const { return targetCount.value(); } int64_t lastChanged = 0; int64_t storeLastWrite = 0; int64_t storeCreated = 0; @@ -4752,9 +4526,9 @@ namespace Energistics { struct DataObject { Energistics::Etp::v12::Datatypes::Object::Resource resource; std::string format; - boost::optional blobId; - bool has_blobId() const { return blobId.is_initialized(); } - Energistics::Etp::v12::Datatypes::Uuid get_blobId() const { return blobId.get(); } + std::optional blobId; + bool has_blobId() const { return blobId.has_value(); } + Energistics::Etp::v12::Datatypes::Uuid get_blobId() const { return blobId.value(); } std::string data; }; } @@ -5008,9 +4782,9 @@ namespace Energistics { namespace Object { struct SupportedType { std::string dataObjectType; - boost::optional objectCount; - bool has_objectCount() const { return objectCount.is_initialized(); } - int32_t get_objectCount() const { return objectCount.get(); } + std::optional objectCount; + bool has_objectCount() const { return objectCount.has_value(); } + int32_t get_objectCount() const { return objectCount.value(); } Energistics::Etp::v12::Datatypes::Object::RelationshipKind relationshipKind = Energistics::Etp::v12::Datatypes::Object::RelationshipKind::Primary; }; } @@ -5032,203 +4806,44 @@ namespace avro { } }; } -namespace avro { - template<> struct codec_traits> { - static void encode(Encoder& e, boost::optional v) { - if (v) { - e.encodeUnionIndex(1); - avro::encode(e, v.get()); - } - else { - e.encodeUnionIndex(0); - e.encodeNull(); - } - } - static void decode(Decoder& d, boost::optional& v) { - size_t n = d.decodeUnionIndex(); - if (n >= 2) { throw avro::Exception("Union index too big"); } - switch (n) { - case 0: - { - d.decodeNull(); - v = boost::none; - } - break; - case 1: - { - bool vv; - avro::decode(d, vv); - v.emplace(vv); - } - break; - } - } - }; -} namespace avro { - template<> struct codec_traits> { - static void encode(Encoder& e, boost::optional v) { - if (v) { + /** + * codec_traits for Avro optional assumming that the schema is ["null", T]. + */ + template + struct codec_traits> { + /** + * Encodes a given value. + */ + static void encode(Encoder& e, const std::optional& b) { + if (b) { e.encodeUnionIndex(1); - avro::encode(e, v.get()); + avro::encode(e, b.value()); } else { e.encodeUnionIndex(0); e.encodeNull(); } } - static void decode(Decoder& d, boost::optional& v) { - size_t n = d.decodeUnionIndex(); - if (n >= 2) { throw avro::Exception("Union index too big"); } - switch (n) { - case 0: - { - d.decodeNull(); - v = boost::none; - } - break; - case 1: - { - int32_t vv; - avro::decode(d, vv); - v.emplace(vv); - } - break; - } - } - }; -} -namespace avro { - template<> struct codec_traits> { - static void encode(Encoder& e, boost::optional v) { - if (v) { - e.encodeUnionIndex(1); - avro::encode(e, v.get()); - } - else { - e.encodeUnionIndex(0); - e.encodeNull(); - } - } - static void decode(Decoder& d, boost::optional& v) { + /** + * Decodes into a given value. + */ + static void decode(Decoder& d, std::optional& s) { size_t n = d.decodeUnionIndex(); - if (n >= 2) { throw avro::Exception("Union index too big"); } + if (n >= 2) { throw avro::Exception("Union index too big for optional (expected 0 or 1, got " + std::to_string(n) + ")"); } switch (n) { case 0: { d.decodeNull(); - v = boost::none; + s.reset(); } break; case 1: { - int64_t vv; - avro::decode(d, vv); - v.emplace(vv); - } - break; - } - } - }; -} - -namespace avro { - template<> struct codec_traits> { - static void encode(Encoder& e, boost::optional v) { - if (v) { - e.encodeUnionIndex(1); - avro::encode(e, v.get()); - } - else { - e.encodeUnionIndex(0); - e.encodeNull(); - } - } - static void decode(Decoder& d, boost::optional& v) { - size_t n = d.decodeUnionIndex(); - if (n >= 2) { throw avro::Exception("Union index too big"); } - switch (n) { - case 0: - { - d.decodeNull(); - v = boost::none; - } - break; - case 1: - { - Energistics::Etp::v12::Datatypes::Uuid vv; - avro::decode(d, vv); - v.emplace(vv); - } - break; - } - } - }; -} - -namespace avro { - template<> struct codec_traits> { - static void encode(Encoder& e, boost::optional v) { - if (v) { - e.encodeUnionIndex(1); - avro::encode(e, v.get()); - } - else { - e.encodeUnionIndex(0); - e.encodeNull(); - } - } - static void decode(Decoder& d, boost::optional& v) { - size_t n = d.decodeUnionIndex(); - if (n >= 2) { throw avro::Exception("Union index too big"); } - switch (n) { - case 0: - { - d.decodeNull(); - v = boost::none; - } - break; - case 1: - { - Energistics::Etp::v12::Datatypes::ErrorInfo vv; - avro::decode(d, vv); - v.emplace(vv); - } - break; - } - } - }; -} - -namespace avro { - template<> struct codec_traits> { - static void encode(Encoder& e, boost::optional v) { - if (v) { - e.encodeUnionIndex(1); - avro::encode(e, v.get()); - } - else { - e.encodeUnionIndex(0); - e.encodeNull(); - } - } - static void decode(Decoder& d, boost::optional& v) { - size_t n = d.decodeUnionIndex(); - if (n >= 2) { throw avro::Exception("Union index too big"); } - switch (n) { - case 0: - { - d.decodeNull(); - v = boost::none; - } - break; - case 1: - { - Energistics::Etp::v12::Datatypes::Object::ActiveStatusKind vv; - avro::decode(d, vv); - v.emplace(vv); + s.emplace(); + avro::decode(d, *s); } break; } diff --git a/src/etp/HttpClientSession.h b/src/etp/HttpClientSession.h index b3c4369..33661f9 100644 --- a/src/etp/HttpClientSession.h +++ b/src/etp/HttpClientSession.h @@ -95,11 +95,15 @@ namespace ETP_NS return; } + // Reality check: IPv6 is unlikely to be available yet + std::vector endpoints(results.begin(), results.end()); + std::stable_partition(endpoints.begin(), endpoints.end(), [](auto entry) {return entry.protocol() == tcp::v4(); }); + // Make the connection on the IP address we get from a lookup boost::asio::async_connect( socket_, - results.begin(), - results.end(), + endpoints.begin(), + endpoints.end(), std::bind( &HttpClientSession::on_connect, shared_from_this(), diff --git a/src/etp/InitializationParameters.h b/src/etp/InitializationParameters.h index 52db6f5..a6b367c 100644 --- a/src/etp/InitializationParameters.h +++ b/src/etp/InitializationParameters.h @@ -93,7 +93,7 @@ namespace ETP_NS * where port is optional and is defaulted to 80 if scheme is "ws" or if no scheme is provided. * In "wss" schema cases, port is defaulted to 443. * @param proxyUrl The proxy URL. It must follow the syntax http://: or simply :. - * Leave it empty if your connection to eptServerUrl is direct and does not pass throughr any proxy. + * Leave it empty if your connection to eptServerUrl is direct and does not pass through any proxy. */ FETPAPI_DLL_IMPORT_OR_EXPORT InitializationParameters(const std::string& instanceUuid, const std::string& etpServerUrl, const std::string& proxyUrl = "") diff --git a/src/etp/PlainClientSession.cpp b/src/etp/PlainClientSession.cpp index b834eba..84cec5d 100644 --- a/src/etp/PlainClientSession.cpp +++ b/src/etp/PlainClientSession.cpp @@ -24,15 +24,7 @@ using namespace ETP_NS; PlainClientSession::PlainClientSession( InitializationParameters const* initializationParams, const std::string & target, const std::string & authorization, const std::string& proxyAuthorization, const std::map& additionalHandshakeHeaderFields, std::size_t frameSize) : - AbstractClientSessionCRTP(initializationParams, target, authorization, proxyAuthorization), - ws_(ioc) + AbstractClientSessionCRTP(initializationParams, target, authorization, proxyAuthorization), frameSize_(frameSize) { - ws_.binary(true); -#if BOOST_VERSION < 107000 - ws_.write_buffer_size(frameSize); -#else - ws_.write_buffer_bytes(frameSize); -#endif - additionalHandshakeHeaderFields_ = additionalHandshakeHeaderFields; } diff --git a/src/etp/PlainClientSession.h b/src/etp/PlainClientSession.h index ac0e305..65db0f6 100644 --- a/src/etp/PlainClientSession.h +++ b/src/etp/PlainClientSession.h @@ -35,28 +35,61 @@ namespace ETP_NS virtual ~PlainClientSession() = default; // Called by the base class - FETPAPI_DLL_IMPORT_OR_EXPORT websocket::stream& ws() { return ws_; } +#if BOOST_VERSION < 107000 + FETPAPI_DLL_IMPORT_OR_EXPORT std::unique_ptr>& ws() { return ws_; } +#else + FETPAPI_DLL_IMPORT_OR_EXPORT std::unique_ptr>& ws() { return ws_; } +#endif bool isTls() const final{ return false; } - void on_resolve(boost::system::error_code ec, tcp::resolver::results_type results) + void asyncConnect(const tcp::resolver::results_type& results) { - if (ec) { - std::cerr << "on_resolve : " << ec.message() << std::endl; - } + // Reality check: IPv6 is unlikely to be available yet + std::vector endpoints = std::vector(results.begin(), results.end()); + std::stable_partition(endpoints.begin(), endpoints.end(), [](auto entry) {return entry.protocol() == tcp::v4(); }); + +#if BOOST_VERSION < 107000 + ws_.reset(new websocket::stream(ioc)); + ws_->write_buffer_size(frameSize_); + ws_->binary(true); // Make the connection on the IP address we get from a lookup boost::asio::async_connect( - ws_.next_layer(), - results.begin(), - results.end(), + ws_->next_layer(), + endpoints.begin(), + endpoints.end(), std::bind( - &AbstractClientSessionCRTP::on_connect, + &AbstractClientSessionCRTP::on_ssl_handshake, std::static_pointer_cast(shared_from_this()), std::placeholders::_1)); +#else + ws_.reset(new websocket::stream(ioc)); + ws_->write_buffer_bytes(frameSize_); + ws_->binary(true); + + // Make the connection on the IP address we get from a lookup + boost::beast::get_lowest_layer(*ws_).async_connect( + endpoints, + boost::beast::bind_front_handler( + &PlainClientSession::on_connect, + std::static_pointer_cast(shared_from_this()))); +#endif + } + +#if BOOST_VERSION > 106900 + void on_connect(boost::beast::error_code ec, tcp::resolver::results_type::endpoint_type) + { + on_ssl_handshake(ec); } +#endif private: - websocket::stream ws_; +#if BOOST_VERSION < 107000 + std::unique_ptr> ws_; +#else + std::unique_ptr> ws_; +#endif + std::size_t frameSize_; }; } diff --git a/src/etp/ProtocolHandlers/CoreHandlers.cpp b/src/etp/ProtocolHandlers/CoreHandlers.cpp index 2cfdf44..4e950c7 100644 --- a/src/etp/ProtocolHandlers/CoreHandlers.cpp +++ b/src/etp/ProtocolHandlers/CoreHandlers.cpp @@ -46,7 +46,7 @@ void CoreHandlers::decodeMessageBody(const Energistics::Etp::v12::Datatypes::Mes auto search = os.endpointCapabilities.find("MaxWebSocketMessagePayloadSize"); if (search != os.endpointCapabilities.end() && search->second.item.idx() == 3) { - const int64_t maxWebSocketMessagePayloadSize = search->second.item.get_long(); + const uint64_t maxWebSocketMessagePayloadSize = search->second.item.get_long(); if (maxWebSocketMessagePayloadSize > 0 && maxWebSocketMessagePayloadSize < session->getMaxWebSocketMessagePayloadSize()) { session->setMaxWebSocketMessagePayloadSize(maxWebSocketMessagePayloadSize); @@ -54,6 +54,10 @@ void CoreHandlers::decodeMessageBody(const Energistics::Etp::v12::Datatypes::Mes } session->setEtpSessionClosed(false); + { + std::lock_guard lock(session->identifierMutex); + std::copy(os.sessionId.array.begin(), os.sessionId.array.end(), session->identifier.begin()); + } on_OpenSession(os, mh.correlationId); } else if (mh.messageType == Energistics::Etp::v12::Protocol::Core::CloseSession::messageTypeId) { @@ -104,7 +108,7 @@ void CoreHandlers::on_RequestSession(const Energistics::Etp::v12::Protocol::Core void CoreHandlers::on_OpenSession(const Energistics::Etp::v12::Protocol::Core::OpenSession &, int64_t) { - session->fesapi_log("The session has been opened with the default core protocol handlers."); + session->fesapi_log("The session", session->getIdentifier(), "has been opened with the default core protocol handlers."); } void CoreHandlers::on_CloseSession(const Energistics::Etp::v12::Protocol::Core::CloseSession &, int64_t) @@ -117,7 +121,7 @@ void CoreHandlers::on_ProtocolException(const Energistics::Etp::v12::Protocol::C { std::cerr << "EXCEPTION received for message_id " << correlationId << std::endl; if (pe.error) { - std::cerr << "Single error code " << pe.error.get().code << " : " << pe.error.get().message << std::endl; + std::cerr << "Single error code " << pe.error.value().code << " : " << pe.error.value().message << std::endl; } else { std::cerr << "One or more error code : " << std::endl; diff --git a/src/etp/ProtocolHandlers/DataArrayHandlers.h b/src/etp/ProtocolHandlers/DataArrayHandlers.h index 21398d9..ed7001e 100644 --- a/src/etp/ProtocolHandlers/DataArrayHandlers.h +++ b/src/etp/ProtocolHandlers/DataArrayHandlers.h @@ -31,73 +31,73 @@ namespace ETP_NS void decodeMessageBody(const Energistics::Etp::v12::Datatypes::MessageHeader & mh, avro::DecoderPtr d); /** - * @param msg The ETP message boday which has been received and which is to be processed. + * @param msg The ETP message body which has been received and which is to be processed. * @param correlationId It is the correlation ID to use if a response is needed to this message. It corresponds to the message ID of the received ETP message. */ virtual void on_GetDataArrays(const Energistics::Etp::v12::Protocol::DataArray::GetDataArrays& msg, int64_t correlationId); /** - * @param msg The ETP message boday which has been received and which is to be processed. + * @param msg The ETP message body which has been received and which is to be processed. * @param correlationId It is the correlation ID to use if a response is needed to this message. It corresponds to the message ID of the received ETP message. */ virtual void on_GetDataArraysResponse(const Energistics::Etp::v12::Protocol::DataArray::GetDataArraysResponse& msg, int64_t correlationId); /** - * @param msg The ETP message boday which has been received and which is to be processed. + * @param msg The ETP message body which has been received and which is to be processed. * @param correlationId It is the correlation ID to use if a response is needed to this message. It corresponds to the message ID of the received ETP message. */ virtual void on_PutDataArrays(const Energistics::Etp::v12::Protocol::DataArray::PutDataArrays& msg, int64_t correlationId); /** - * @param msg The ETP message boday which has been received and which is to be processed. + * @param msg The ETP message body which has been received and which is to be processed. * @param correlationId It is the correlation ID to use if a response is needed to this message. It corresponds to the message ID of the received ETP message. */ virtual void on_PutDataArraysResponse(const Energistics::Etp::v12::Protocol::DataArray::PutDataArraysResponse& msg, int64_t correlationId); /** - * @param msg The ETP message boday which has been received and which is to be processed. + * @param msg The ETP message body which has been received and which is to be processed. * @param correlationId It is the correlation ID to use if a response is needed to this message. It corresponds to the message ID of the received ETP message. */ virtual void on_GetDataSubarrays(const Energistics::Etp::v12::Protocol::DataArray::GetDataSubarrays& msg, int64_t correlationId); /** - * @param msg The ETP message boday which has been received and which is to be processed. + * @param msg The ETP message body which has been received and which is to be processed. * @param correlationId It is the correlation ID to use if a response is needed to this message. It corresponds to the message ID of the received ETP message. */ virtual void on_GetDataSubarraysResponse(const Energistics::Etp::v12::Protocol::DataArray::GetDataSubarraysResponse& msg, int64_t correlationId); /** - * @param msg The ETP message boday which has been received and which is to be processed. + * @param msg The ETP message body which has been received and which is to be processed. * @param correlationId It is the correlation ID to use if a response is needed to this message. It corresponds to the message ID of the received ETP message. */ virtual void on_PutDataSubarrays(const Energistics::Etp::v12::Protocol::DataArray::PutDataSubarrays& msg, int64_t correlationId); /** - * @param msg The ETP message boday which has been received and which is to be processed. + * @param msg The ETP message body which has been received and which is to be processed. * @param correlationId It is the correlation ID to use if a response is needed to this message. It corresponds to the message ID of the received ETP message. */ virtual void on_PutDataSubarraysResponse(const Energistics::Etp::v12::Protocol::DataArray::PutDataSubarraysResponse& msg, int64_t correlationId); /** - * @param msg The ETP message boday which has been received and which is to be processed. + * @param msg The ETP message body which has been received and which is to be processed. * @param correlationId It is the correlation ID to use if a response is needed to this message. It corresponds to the message ID of the received ETP message. */ virtual void on_GetDataArrayMetadata(const Energistics::Etp::v12::Protocol::DataArray::GetDataArrayMetadata& msg, int64_t correlationId); /** - * @param msg The ETP message boday which has been received and which is to be processed. + * @param msg The ETP message body which has been received and which is to be processed. * @param correlationId It is the correlation ID to use if a response is needed to this message. It corresponds to the message ID of the received ETP message. */ virtual void on_GetDataArrayMetadataResponse(const Energistics::Etp::v12::Protocol::DataArray::GetDataArrayMetadataResponse& msg, int64_t correlationId); /** - * @param msg The ETP message boday which has been received and which is to be processed. + * @param msg The ETP message body which has been received and which is to be processed. * @param correlationId It is the correlation ID to use if a response is needed to this message. It corresponds to the message ID of the received ETP message. */ virtual void on_PutUninitializedDataArrays(const Energistics::Etp::v12::Protocol::DataArray::PutUninitializedDataArrays& msg, int64_t correlationId); /** - * @param msg The ETP message boday which has been received and which is to be processed. + * @param msg The ETP message body which has been received and which is to be processed. * @param correlationId It is the correlation ID to use if a response is needed to this message. It corresponds to the message ID of the received ETP message. */ virtual void on_PutUninitializedDataArraysResponse(const Energistics::Etp::v12::Protocol::DataArray::PutUninitializedDataArraysResponse& msg, int64_t correlationId); diff --git a/src/etp/ProtocolHandlers/GetFullDataArrayHandlers.h b/src/etp/ProtocolHandlers/GetFullDataArrayHandlers.h index fc36b0d..9d0103f 100644 --- a/src/etp/ProtocolHandlers/GetFullDataArrayHandlers.h +++ b/src/etp/ProtocolHandlers/GetFullDataArrayHandlers.h @@ -84,45 +84,45 @@ namespace ETP_NS if (msg.dataArrays.size() == 1) { auto dataArray = msg.dataArrays.begin()->second; if (dataArray.data.item.idx() == 0) { - Energistics::Etp::v12::Datatypes::ArrayOfBoolean& avroArray = dataArray.data.item.get_ArrayOfBoolean(); + const Energistics::Etp::v12::Datatypes::ArrayOfBoolean& avroArray = dataArray.data.item.get_ArrayOfBoolean(); for (size_t i = 0; i < avroArray.values.size(); ++i) { values[i] = avroArray.values[i]; } } else if (dataArray.data.item.idx() == 1) { - Energistics::Etp::v12::Datatypes::ArrayOfInt& avroArray = dataArray.data.item.get_ArrayOfInt(); + const Energistics::Etp::v12::Datatypes::ArrayOfInt& avroArray = dataArray.data.item.get_ArrayOfInt(); for (size_t i = 0; i < avroArray.values.size(); ++i) { values[i] = avroArray.values[i]; } } else if (dataArray.data.item.idx() == 2) { - Energistics::Etp::v12::Datatypes::ArrayOfLong& avroArray = dataArray.data.item.get_ArrayOfLong(); + const Energistics::Etp::v12::Datatypes::ArrayOfLong& avroArray = dataArray.data.item.get_ArrayOfLong(); for (size_t i = 0; i < avroArray.values.size(); ++i) { values[i] = avroArray.values[i]; } } else if (dataArray.data.item.idx() == 3) { - Energistics::Etp::v12::Datatypes::ArrayOfFloat& avroArray = dataArray.data.item.get_ArrayOfFloat(); + const Energistics::Etp::v12::Datatypes::ArrayOfFloat& avroArray = dataArray.data.item.get_ArrayOfFloat(); for (size_t i = 0; i < avroArray.values.size(); ++i) { values[i] = avroArray.values[i]; } } else if (dataArray.data.item.idx() == 4) { - Energistics::Etp::v12::Datatypes::ArrayOfDouble& avroArray = dataArray.data.item.get_ArrayOfDouble(); + const Energistics::Etp::v12::Datatypes::ArrayOfDouble& avroArray = dataArray.data.item.get_ArrayOfDouble(); for (size_t i = 0; i < avroArray.values.size(); ++i) { values[i] = avroArray.values[i]; } } /* else if (dataArray.data.item.idx() == 5) { - Energistics::Etp::v12::Datatypes::ArrayOfString& avroArray = dataArray.data.item.get_ArrayOfString(); + const Energistics::Etp::v12::Datatypes::ArrayOfString& avroArray = dataArray.data.item.get_ArrayOfString(); for (auto i = 0; i < avroArray.values.size(); ++i) { values[i] = avroArray.values[i]; } } */ else if (dataArray.data.item.idx() == 6) { - std::string& avroValues = dataArray.data.item.get_bytes(); + const std::string& avroValues = dataArray.data.item.get_bytes(); for (size_t i = 0; i < avroValues.size(); ++i) { values[i] = avroValues[i]; } @@ -162,45 +162,45 @@ namespace ETP_NS // Copy from the ETP subarray to the receiving array if (dataArray.data.item.idx() == 0) { - Energistics::Etp::v12::Datatypes::ArrayOfBoolean& avroArray = dataArray.data.item.get_ArrayOfBoolean(); + const Energistics::Etp::v12::Datatypes::ArrayOfBoolean& avroArray = dataArray.data.item.get_ArrayOfBoolean(); for (auto i = 0; i < iterator->second.counts.back(); ++i) { values[i + arrayOffset] = avroArray.values[i + subarrayOffset]; } } else if (dataArray.data.item.idx() == 1) { - Energistics::Etp::v12::Datatypes::ArrayOfInt& avroArray = dataArray.data.item.get_ArrayOfInt(); + const Energistics::Etp::v12::Datatypes::ArrayOfInt& avroArray = dataArray.data.item.get_ArrayOfInt(); for (auto i = 0; i < iterator->second.counts.back(); ++i) { values[i + arrayOffset] = avroArray.values[i + subarrayOffset]; } } else if (dataArray.data.item.idx() == 2) { - Energistics::Etp::v12::Datatypes::ArrayOfLong& avroArray = dataArray.data.item.get_ArrayOfLong(); + const Energistics::Etp::v12::Datatypes::ArrayOfLong& avroArray = dataArray.data.item.get_ArrayOfLong(); for (auto i = 0; i < iterator->second.counts.back(); ++i) { values[i + arrayOffset] = avroArray.values[i + subarrayOffset]; } } else if (dataArray.data.item.idx() == 3) { - Energistics::Etp::v12::Datatypes::ArrayOfFloat& avroArray = dataArray.data.item.get_ArrayOfFloat(); + const Energistics::Etp::v12::Datatypes::ArrayOfFloat& avroArray = dataArray.data.item.get_ArrayOfFloat(); for (auto i = 0; i < iterator->second.counts.back(); ++i) { values[i + arrayOffset] = avroArray.values[i + subarrayOffset]; } } else if (dataArray.data.item.idx() == 4) { - Energistics::Etp::v12::Datatypes::ArrayOfDouble& avroArray = dataArray.data.item.get_ArrayOfDouble(); + const Energistics::Etp::v12::Datatypes::ArrayOfDouble& avroArray = dataArray.data.item.get_ArrayOfDouble(); for (auto i = 0; i < iterator->second.counts.back(); ++i) { values[i + arrayOffset] = avroArray.values[i + subarrayOffset]; } } /* else if (dataArray.data.item.idx() == 5) { - Energistics::Etp::v12::Datatypes::ArrayOfString& avroArray = dataArray.data.item.get_ArrayOfString(); + const Energistics::Etp::v12::Datatypes::ArrayOfString& avroArray = dataArray.data.item.get_ArrayOfString(); for (auto i = 0; i < avroArray.values.size(); ++i) { values[i] = avroArray.values[i]; } } */ else if (dataArray.data.item.idx() == 6) { - std::string& avroValues = dataArray.data.item.get_bytes(); + const std::string& avroValues = dataArray.data.item.get_bytes(); for (auto i = 0; i < iterator->second.counts.back(); ++i) { values[i + arrayOffset] = avroValues[i + subarrayOffset]; } diff --git a/src/etp/ProtocolHandlers/ProtocolHandlers.cpp b/src/etp/ProtocolHandlers/ProtocolHandlers.cpp index 91edb4f..66fe4bf 100644 --- a/src/etp/ProtocolHandlers/ProtocolHandlers.cpp +++ b/src/etp/ProtocolHandlers/ProtocolHandlers.cpp @@ -21,8 +21,6 @@ under the License. #include "../AbstractSession.h" #include "../EtpHelpers.h" -#include "../../tools/date.h" - using namespace ETP_NS; void ProtocolHandlers::printDataObject(const Energistics::Etp::v12::Datatypes::Object::DataObject & dataObject) @@ -37,16 +35,15 @@ void ProtocolHandlers::printDataObject(const Energistics::Etp::v12::Datatypes::O } std::cout << "name : " << dataObject.resource.name << std::endl; if (dataObject.resource.sourceCount) { - std::cout << "source count : " << dataObject.resource.sourceCount.get() << std::endl; + std::cout << "source count : " << dataObject.resource.sourceCount.value() << std::endl; } if (dataObject.resource.targetCount) { - std::cout << "target count : " << dataObject.resource.targetCount.get() << std::endl; + std::cout << "target count : " << dataObject.resource.targetCount.value() << std::endl; } - std::cout << "lastChanged : "; + std::cout << "lastChanged timestamp : "; if (dataObject.resource.lastChanged >= 0) { - auto duration = std::chrono::microseconds(dataObject.resource.lastChanged); - std::cout << date::format("%FT%TZ", date::floor(duration)); + std::cout << dataObject.resource.lastChanged; } else { std::cout << "unknown"; diff --git a/src/etp/ProtocolHandlers/StoreNotificationHandlers.cpp b/src/etp/ProtocolHandlers/StoreNotificationHandlers.cpp index bc78868..4bf4055 100644 --- a/src/etp/ProtocolHandlers/StoreNotificationHandlers.cpp +++ b/src/etp/ProtocolHandlers/StoreNotificationHandlers.cpp @@ -21,8 +21,6 @@ under the License. #include "../AbstractSession.h" #include "../EtpHelpers.h" -#include "../../tools/date.h" - using namespace ETP_NS; void StoreNotificationHandlers::decodeMessageBody(const Energistics::Etp::v12::Datatypes::MessageHeader & mh, avro::DecoderPtr d) @@ -99,37 +97,24 @@ void StoreNotificationHandlers::on_SubscribeNotificationsResponse(const Energist session->fesapi_log("Received SubscribeNotificationsResponse"); } -void StoreNotificationHandlers::on_UnsubscribeNotifications(const Energistics::Etp::v12::Protocol::StoreNotification::UnsubscribeNotifications & msg, int64_t messageId, int64_t) +void StoreNotificationHandlers::on_UnsubscribeNotifications(const Energistics::Etp::v12::Protocol::StoreNotification::UnsubscribeNotifications&, int64_t, int64_t) { session->fesapi_log("on_UnsubscribeNotifications"); - int64_t toRemove = (std::numeric_limits::max)(); - for (const auto& pair : session->subscriptions) { - if (pair.second.requestUuid.array == msg.requestUuid.array) { - toRemove = pair.first; - break; - } - } - - if (toRemove != (std::numeric_limits::max)()) { - session->subscriptions.erase(toRemove); - } - else { - session->send(ETP_NS::EtpHelpers::buildSingleMessageProtocolException(5, "The subscription request UUID is unknown by the store."), messageId, 0x02); - } + session->send(ETP_NS::EtpHelpers::buildSingleMessageProtocolException(7, "The StoreHandlers::on_UnsubscribeNotifications method has not been overriden by the agent."), 0x02); } -void StoreNotificationHandlers::on_UnsolicitedStoreNotifications(const Energistics::Etp::v12::Protocol::StoreNotification::UnsolicitedStoreNotifications &, int64_t) +void StoreNotificationHandlers::on_UnsolicitedStoreNotifications(const Energistics::Etp::v12::Protocol::StoreNotification::UnsolicitedStoreNotifications&, int64_t) { session->fesapi_log("Received UnsolicitedStoreNotifications"); } -void StoreNotificationHandlers::on_SubscriptionEnded(const Energistics::Etp::v12::Protocol::StoreNotification::SubscriptionEnded &, int64_t) +void StoreNotificationHandlers::on_SubscriptionEnded(const Energistics::Etp::v12::Protocol::StoreNotification::SubscriptionEnded&, int64_t) { session->fesapi_log("Received SubscriptionEnded "); } -void StoreNotificationHandlers::on_ObjectChanged(const Energistics::Etp::v12::Protocol::StoreNotification::ObjectChanged & msg, int64_t) +void StoreNotificationHandlers::on_ObjectChanged(const Energistics::Etp::v12::Protocol::StoreNotification::ObjectChanged& msg, int64_t) { switch (msg.change.changeKind) { case Energistics::Etp::v12::Datatypes::Object::ObjectChangeKind::authorized: session->fesapi_log("authorized"); break; @@ -141,8 +126,7 @@ void StoreNotificationHandlers::on_ObjectChanged(const Energistics::Etp::v12::Pr case Energistics::Etp::v12::Datatypes::Object::ObjectChangeKind::unjoinedSubscription: session->fesapi_log("unjoinedSubscription"); break; } - auto duration = std::chrono::microseconds(msg.change.changeTime); - session->fesapi_log("on", date::format("%FT%TZ", date::floor(duration))); + session->fesapi_log("on timestamp ", msg.change.changeTime); printDataObject(msg.change.dataObject); } diff --git a/src/etp/fesapi/FesapiHdfProxy.cpp b/src/etp/fesapi/FesapiHdfProxy.cpp index 31ac384..31343c6 100644 --- a/src/etp/fesapi/FesapiHdfProxy.cpp +++ b/src/etp/fesapi/FesapiHdfProxy.cpp @@ -53,21 +53,11 @@ Energistics::Etp::v12::Datatypes::DataArrayTypes::DataArrayMetadata FesapiHdfPro // We don't care about the template parameter in this particular case auto handlers = std::make_shared>(session_, nullptr); - const int64_t msgId = session_->sendWithSpecificHandler( + session_->sendWithSpecificHandlerAndBlock( buildGetDataArrayMetadataMessage(datasetName), handlers, 0, 0x02); - // Blocking loop - auto t_start = std::chrono::high_resolution_clock::now(); - // Use timeOut value for session. - auto timeOut = session_->getTimeOut(); - while (session_->isMessageStillProcessing(msgId)) { - if (std::chrono::duration(std::chrono::high_resolution_clock::now() - t_start).count() > timeOut) { - throw std::runtime_error("Time out waiting for a response of GetDataArrayMetadata message id " + std::to_string(msgId)); - } - } - return handlers->getDataArrayMetadata(); } @@ -128,7 +118,7 @@ namespace { static_cast(values), static_cast(values) + totalCount); - data.item.set_ArrayOfDouble(avroArray); + data.item.set_ArrayOfDouble(std::move(avroArray)); } else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::FLOAT) { Energistics::Etp::v12::Datatypes::ArrayOfFloat avroArray; @@ -137,7 +127,7 @@ namespace { static_cast(values), static_cast(values) + totalCount); - data.item.set_ArrayOfFloat(avroArray); + data.item.set_ArrayOfFloat(std::move(avroArray)); } else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT64 || datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT64) { @@ -147,7 +137,7 @@ namespace { static_cast(values), static_cast(values) + totalCount); - data.item.set_ArrayOfLong(avroArray); + data.item.set_ArrayOfLong(std::move(avroArray)); } else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT32 || datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT32) { @@ -157,7 +147,7 @@ namespace { static_cast(values), static_cast(values) + totalCount); - data.item.set_ArrayOfInt(avroArray); + data.item.set_ArrayOfInt(std::move(avroArray)); } else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT16 || datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT16) { @@ -167,7 +157,7 @@ namespace { avroArray.values.push_back(static_cast(values)[i]); } - data.item.set_ArrayOfInt(avroArray); + data.item.set_ArrayOfInt(std::move(avroArray)); } else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT8 || datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT8) { @@ -177,7 +167,7 @@ namespace { avroArray.push_back(static_cast(values)[i]); } - data.item.set_bytes(avroArray); + data.item.set_bytes(std::move(avroArray)); } else { throw logic_error( @@ -336,18 +326,48 @@ void FesapiHdfProxy::writeArrayNdSlab( const uint64_t* numValuesInEachDimension, const uint64_t* offsetInEachDimension, unsigned int numDimensions) +{ + std::setstillProcessingMsgIds = async_writeArrayNdSlab(groupName, datasetName, + datatype, values, numValuesInEachDimension, + offsetInEachDimension, numDimensions); + + auto t_start = std::chrono::high_resolution_clock::now(); + while (!stillProcessingMsgIds.empty()) { + std::vector idsToErase; + for (int64_t msgId : stillProcessingMsgIds) { + if (!session_->isMessageStillProcessing(msgId)) { + idsToErase.push_back(msgId); + } + } + for (int64_t msgId : idsToErase) { + stillProcessingMsgIds.erase(msgId); + } + if (std::chrono::duration(std::chrono::high_resolution_clock::now() - t_start).count() > session_->getTimeOut()) { + throw std::runtime_error("Time out waiting for a writeArrayNdSlab response"); + } + } +} + +std::set FesapiHdfProxy::async_writeArrayNdSlab( + const string& groupName, + const string& datasetName, + COMMON_NS::AbstractObject::numericalDatatypeEnum datatype, + const void* values, + const uint64_t* numValuesInEachDimension, + const uint64_t* offsetInEachDimension, + unsigned int numDimensions) { if (!isOpened()) open(); // URI AND PATH - std::string uri{ buildEtp12Uri() }; + const std::string uri{ buildEtp12Uri() }; - std::string pathInResource{ (groupName.back() == '/' ? + const std::string pathInResource{ (groupName.back() == '/' ? groupName : groupName + '/') + datasetName }; // Create Total Count - size_t totalCount = std::accumulate(numValuesInEachDimension, numValuesInEachDimension + numDimensions, 1, std::multiplies()); + const size_t totalCount = std::accumulate(numValuesInEachDimension, numValuesInEachDimension + numDimensions, 1, std::multiplies()); // Determine Value Size (bytes) and Any Array Type size_t valueSize{ 1 }; @@ -380,6 +400,7 @@ void FesapiHdfProxy::writeArrayNdSlab( "You need to give a COMMON_NS::AbstractObject::numericalDatatypeEnum as the datatype"); } + std::set sentMessageIds; if (totalCount * valueSize <= maxArraySize_) { std::vector counts; std::vector starts; @@ -399,7 +420,7 @@ void FesapiHdfProxy::writeArrayNdSlab( pdsa.dataSubarrays["0"].data = convertVoidArrayIntoAvroAnyArray(datatype, values, totalCount); // Send putDataSubarrays Message - session_->sendAndBlock(pdsa, 0, 0x02); + sentMessageIds.insert(session_->send(pdsa, 0, 0x02)); } else { std::unique_ptr counts(new uint64_t[numDimensions]); @@ -415,10 +436,11 @@ void FesapiHdfProxy::writeArrayNdSlab( if (numValuesInEachDimension[dimIdx] > 1) { uint64_t previousCount = counts[dimIdx]; counts[dimIdx] /= 2; - - writeArrayNdSlab(groupName, datasetName, + + std::set intermediateResult = async_writeArrayNdSlab(groupName, datasetName, datatype, values, counts.get(), starts.get(), numDimensions); + sentMessageIds.insert(intermediateResult.begin(), intermediateResult.end()); writtenTotalCount = std::accumulate(counts.get(), counts.get() + numDimensions, 1, std::multiplies()); @@ -434,10 +456,13 @@ void FesapiHdfProxy::writeArrayNdSlab( + std::to_string(maxArraySize_) + " bytes."); } - writeArrayNdSlab(groupName, datasetName, datatype, - (int8_t*)values + (writtenTotalCount * valueSize), counts.get(), + std::set intermediateResult = async_writeArrayNdSlab(groupName, datasetName, datatype, + (const int8_t*)values + (writtenTotalCount * valueSize), counts.get(), starts.get(), numDimensions); + sentMessageIds.insert(intermediateResult.begin(), intermediateResult.end()); } + + return sentMessageIds; } void FesapiHdfProxy::readArrayNdOfDoubleValues( diff --git a/src/etp/fesapi/FesapiHdfProxy.h b/src/etp/fesapi/FesapiHdfProxy.h index da7a74b..59f1a7d 100644 --- a/src/etp/fesapi/FesapiHdfProxy.h +++ b/src/etp/fesapi/FesapiHdfProxy.h @@ -18,13 +18,14 @@ under the License. -----------------------------------------------------------------------*/ #pragma once +#include +#include + #include #include "../AbstractSession.h" #include "../ProtocolHandlers/GetFullDataArrayHandlers.h" -#include - namespace ETP_NS { class FETPAPI_DLL_IMPORT_OR_EXPORT FesapiHdfProxy : public EML2_NS::AbstractHdfProxy @@ -36,7 +37,7 @@ namespace ETP_NS */ FesapiHdfProxy(AbstractSession* session, COMMON_NS::DataObjectRepository * repo, const std::string & guid, const std::string & title, const std::string & packageDirAbsolutePath, const std::string & externalFilePath, COMMON_NS::DataObjectRepository::openingMode hdfPermissionAccess) : EML2_NS::AbstractHdfProxy(packageDirAbsolutePath, externalFilePath, hdfPermissionAccess), session_(session), compressionLevel(0) { - xmlNs_ = repo->getDefaultEmlVersion() == COMMON_NS::DataObjectRepository::EnergisticsStandard::EML2_0 ? "eml20" : "eml23"; + xmlNs_ = repo->getDefaultEmlVersion() == COMMON_NS::DataObjectRepository::EnergisticsStandard::EML2_3 ? "eml23" : "eml20"; initGsoapProxy(repo, guid, title, 20); } @@ -184,6 +185,26 @@ namespace ETP_NS const uint64_t* offsetValuesInEachDimension, unsigned int numDimensions) final; + /** + * Find the array associated with @p groupName and @p name and write to it asynchronously. + * @param groupName The name of the group associated with the array. + * @param name The name of the array (potentially with multi dimensions). + * @param datatype The specific datatype of the values to write. + * @param values 1d array of specific datatype ordered firstly by fastest direction. + * @param numValuesInEachDimension Number of values in each dimension of the array to write. They are ordered from fastest index to slowest index. + * @param offsetValuesInEachDimension Offset values in each dimension of the array to write. They are ordered from fastest index to slowest index. + * @param numDimensions The number of the dimensions of the array to write. + * @return All message ids which have been sent to the ETP server + */ + std::set async_writeArrayNdSlab( + const std::string& groupName, + const std::string& name, + COMMON_NS::AbstractObject::numericalDatatypeEnum datatype, + const void* values, + const uint64_t* numValuesInEachDimension, + const uint64_t* offsetValuesInEachDimension, + unsigned int numDimensions); + /** * Write some string attributes into a group */ @@ -486,18 +507,9 @@ namespace ETP_NS template void readArrayNdOfValues(const std::string & datasetName, T* values) { - if (!isOpened()) { - throw std::runtime_error("The ETP session does not look to be opened. Please reconnect."); - } - // First get metadata about the data array - std::vector dimensions; - const Energistics::Etp::v12::Datatypes::DataArrayTypes::DataArrayMetadata daMetadata = getDataArrayMetadata(datasetName); - size_t valueCount = 1; - for (int64_t dim : daMetadata.dimensions) { - valueCount *= dim; - } + const size_t valueCount = std::accumulate(daMetadata.dimensions.begin(), daMetadata.dimensions.end(), 1, std::multiplies()); size_t valueSize = 1; switch (daMetadata.transportArrayType) { @@ -509,7 +521,7 @@ namespace ETP_NS case Energistics::Etp::v12::Datatypes::AnyArrayType::arrayOfDouble: valueSize = 8; break; default: throw std::logic_error("Array of strings are not implemented yet"); } - size_t wholeSize = valueCount * valueSize; + const size_t wholeSize = valueCount * valueSize; // maxAllowedDataArraySize is the maximum serialized size of the array (including avro extra longs for array blocks) const size_t maxAllowedDataArraySize = session_->getMaxWebSocketMessagePayloadSize() @@ -520,13 +532,10 @@ namespace ETP_NS auto specializedHandler = std::make_shared>(session_, values); if (wholeSize + (valueCount + 1) * 8 <= maxAllowedDataArraySize) { // There can be valueCount array block and there is the length of the last array block // Get all values at once - const int64_t msgId = session_->sendWithSpecificHandler( + session_->sendWithSpecificHandlerAndBlock( buildGetDataArraysMessage(datasetName), specializedHandler, 0, 0x02); - - // Blocking loop - while (session_->isMessageStillProcessing(msgId)) {} } else { // Get all values using several data subarrays allowing more granular streaming @@ -586,13 +595,10 @@ namespace ETP_NS } // Send message - const int64_t msgId = session_->sendWithSpecificHandler( + session_->sendWithSpecificHandlerAndBlock( msg, specializedHandler, 0, 0x02); - - // Blocking loop - while (session_->isMessageStillProcessing(msgId)) {} } } }; diff --git a/src/etp/ssl/CMakeLists.txt b/src/etp/ssl/CMakeLists.txt index 8e63450..bd7310a 100644 --- a/src/etp/ssl/CMakeLists.txt +++ b/src/etp/ssl/CMakeLists.txt @@ -1,4 +1,6 @@ set(FETPAPI_SSL_SOURCES ${CMAKE_CURRENT_LIST_DIR}/SslClientSession.cpp ) set(FETPAPI_SSL_HEADERS ${CMAKE_CURRENT_LIST_DIR}/HttpsClientSession.h - ${CMAKE_CURRENT_LIST_DIR}/ssl_stream.h ${CMAKE_CURRENT_LIST_DIR}/SslClientSession.h ) +if ((Boost_VERSION_MAJOR EQUAL 1) AND (Boost_VERSION_MINOR LESS 68)) + list(APPEND FETPAPI_SSL_HEADERS ${CMAKE_CURRENT_LIST_DIR}/ssl_stream.h) +endif () diff --git a/src/etp/ssl/HttpsClientSession.h b/src/etp/ssl/HttpsClientSession.h index 067ad1b..615a672 100644 --- a/src/etp/ssl/HttpsClientSession.h +++ b/src/etp/ssl/HttpsClientSession.h @@ -70,18 +70,13 @@ namespace ETP_NS uint16_t proxyPort = 80, const std::string& proxyAuthorization = "") { - size_t hostSizeWithNullTermChar = etpServerHost.size() + 1; - char* copyHost = new char[hostSizeWithNullTermChar]; - std::memcpy(copyHost, etpServerHost.c_str(), hostSizeWithNullTermChar); // Copy host because it must be non const in SSL_set_tlsext_host_name // Set SNI Hostname (many hosts need this to handshake successfully) - if (!SSL_set_tlsext_host_name(stream_.native_handle(), copyHost)) + if (!SSL_set_tlsext_host_name(stream_.native_handle(), etpServerHost.data())) { boost::system::error_code ec{ static_cast(::ERR_get_error()), boost::asio::error::get_ssl_category() }; - std::cerr << ec.message() << "\n"; - delete[] copyHost; + std::cerr << "HTTPS on connect (SNI): " << ec.message() << "\n"; return; } - delete[] copyHost; // Set up an HTTP GET request message req_.version(version); @@ -123,15 +118,19 @@ namespace ETP_NS tcp::resolver::results_type results) { if (ec) { - std::cerr << "resolve : " << ec.message() << std::endl; + std::cerr << "HTTP over SSL resolve : " << ec.message() << std::endl; return; } + // Reality check: IPv6 is unlikely to be available yet + std::vector endpoints(results.begin(), results.end()); + std::stable_partition(endpoints.begin(), endpoints.end(), [](auto entry) {return entry.protocol() == tcp::v4();}); + // Make the connection on the IP address we get from a lookup boost::asio::async_connect( stream_.next_layer(), - results.begin(), - results.end(), + endpoints.begin(), + endpoints.end(), std::bind( &HttpsClientSession::on_connect, shared_from_this(), @@ -142,7 +141,7 @@ namespace ETP_NS on_connect(boost::system::error_code ec) { if (ec) { - std::cerr << "connect : " << ec.message() << std::endl; + std::cerr << "HTTP over SSL connect : " << ec.message() << std::endl; return; } @@ -174,7 +173,7 @@ namespace ETP_NS boost::ignore_unused(bytes_transferred); if (ec) { - std::cerr << "Proxy handshake write : " << ec.message() << std::endl; + std::cerr << "HTTP over SSL Proxy handshake write : " << ec.message() << std::endl; return; } @@ -208,7 +207,7 @@ namespace ETP_NS { boost::ignore_unused(bytes_transferred); if (ec) { - std::cerr << "read : " << ec.message() << std::endl; + std::cerr << "HTTP over SSL read : " << ec.message() << std::endl; return; } @@ -225,7 +224,7 @@ namespace ETP_NS on_handshake(boost::system::error_code ec) { if (ec) { - std::cerr << "handshake : " << ec.message() << std::endl; + std::cerr << "HTTP over SSL handshake : " << ec.message() << std::endl; return; } @@ -246,7 +245,7 @@ namespace ETP_NS boost::ignore_unused(bytes_transferred); if (ec) { - std::cerr << "write : " << ec.message() << std::endl; + std::cerr << "HTTP over SSL write : " << ec.message() << std::endl; return; } @@ -267,21 +266,16 @@ namespace ETP_NS boost::ignore_unused(bytes_transferred); if (ec) { - std::cerr << "read : " << ec.message() << std::endl; + std::cerr << "HTTP over SSL read : " << ec.message() << std::endl; return; } - // Force close - boost::system::error_code closeEc; - stream_.next_layer().close(closeEc); - /* // Gracefully close the stream_ stream_.async_shutdown( std::bind( &HttpsClientSession::on_shutdown, shared_from_this(), std::placeholders::_1)); - */ } void diff --git a/src/etp/ssl/SslClientSession.cpp b/src/etp/ssl/SslClientSession.cpp index b6b2cab..c9acbf2 100644 --- a/src/etp/ssl/SslClientSession.cpp +++ b/src/etp/ssl/SslClientSession.cpp @@ -20,18 +20,10 @@ under the License. using namespace ETP_NS; -SslClientSession::SslClientSession(boost::asio::ssl::context& ctx, +SslClientSession::SslClientSession(boost::asio::ssl::context&& ctx, InitializationParameters const* initializationParams, const std::string& target, const std::string& authorization, const std::string& proxyAuthorization, const std::map& additionalHandshakeHeaderFields, std::size_t frameSize) - : AbstractClientSessionCRTP(initializationParams, target, authorization, proxyAuthorization), - ws_(ioc, ctx) + : AbstractClientSessionCRTP(initializationParams, target, authorization, proxyAuthorization), sslContext_(std::move(ctx)), frameSize_(frameSize) { - ws_.binary(true); -#if BOOST_VERSION < 107000 - ws_.write_buffer_size(frameSize); -#else - ws_.write_buffer_bytes(frameSize); -#endif - additionalHandshakeHeaderFields_ = additionalHandshakeHeaderFields; } diff --git a/src/etp/ssl/SslClientSession.h b/src/etp/ssl/SslClientSession.h index 48cf2b6..4c78997 100644 --- a/src/etp/ssl/SslClientSession.h +++ b/src/etp/ssl/SslClientSession.h @@ -25,10 +25,13 @@ under the License. #include "ssl_stream.h" #elif BOOST_VERSION < 107000 #include -#else +#elif BOOST_VERSION < 108600 #include #include #include +#else +#include +#include #endif namespace http = boost::beast::http; // from @@ -37,47 +40,79 @@ namespace ETP_NS { class SslClientSession : public AbstractClientSessionCRTP { - private: - websocket::stream> ws_; - http::request proxyHandshake; - http::response proxyHandshakeResponse; - // use own response parser - // NOTE: 200 response to a CONNECT request from a tunneling proxy do not carry a body - http::response_parser http_proxy_handshake_parser; - public: /* * @param frameSize Sets the size of the write buffer used by the implementation to send frames : https://www.boost.org/doc/libs/1_75_0/libs/beast/doc/html/beast/ref/boost__beast__websocket__stream/write_buffer_bytes/overload1.html. */ - FETPAPI_DLL_IMPORT_OR_EXPORT SslClientSession(boost::asio::ssl::context& ctx, + FETPAPI_DLL_IMPORT_OR_EXPORT SslClientSession(boost::asio::ssl::context&& ctx, InitializationParameters const* initializationParams, const std::string& target, const std::string& authorization, const std::string& proxyAuthorization = "", const std::map& additionalHandshakeHeaderFields = {}, std::size_t frameSize = 4096); virtual ~SslClientSession() {} // Called by the base class - FETPAPI_DLL_IMPORT_OR_EXPORT websocket::stream>& ws() { return ws_; } +#if BOOST_VERSION < 107000 + FETPAPI_DLL_IMPORT_OR_EXPORT std::unique_ptr>>& ws() { return ws_; } +#elif BOOST_VERSION < 108600 + FETPAPI_DLL_IMPORT_OR_EXPORT std::unique_ptr>>& ws() { return ws_; } +#else + FETPAPI_DLL_IMPORT_OR_EXPORT std::unique_ptr< websocket::stream>>& ws() { return ws_; } +#endif bool isTls() const final { return true; } - void on_resolve(boost::system::error_code ec, tcp::resolver::results_type results) + void asyncConnect(const tcp::resolver::results_type& results) { - if (ec) { - std::cerr << "on_resolve : " << ec.message() << std::endl; +#if BOOST_VERSION < 107000 + ws_.reset(new websocket::stream>(ioc, sslContext_)); +#elif BOOST_VERSION < 108600 + ws_.reset(new websocket::stream>(ioc, sslContext_)); +#else + ws_.reset(new websocket::stream>(ioc, sslContext_)); +#endif + + ws_->binary(true); +#if BOOST_VERSION < 107000 + ws_->write_buffer_size(frameSize_); +#else + ws_->write_buffer_bytes(frameSize_); +#endif + + // Set SNI Hostname (many hosts need this to handshake successfully) + if (!SSL_set_tlsext_host_name(ws_->next_layer().native_handle(), etpServerHost.data())) + { + boost::system::error_code ecSNI{ static_cast(::ERR_get_error()), boost::asio::error::get_ssl_category() }; + std::cerr << "Websocket on connect (SNI): " << ecSNI.message() << std::endl; } + // Reality check: IPv6 is unlikely to be available yet + std::vector endpoints = std::vector(results.begin(), results.end()); + std::stable_partition(endpoints.begin(), endpoints.end(), [](auto entry) {return entry.protocol() == tcp::v4(); }); + // Make the connection on the IP address we get from a lookup +#if BOOST_VERSION < 107000 boost::asio::async_connect( - ws_.next_layer().next_layer(), - results.begin(), - results.end(), + ws_->next_layer().next_layer(), + endpoints.begin(), + endpoints.end(), std::bind( &SslClientSession::on_ssl_connect, std::static_pointer_cast(shared_from_this()), std::placeholders::_1)); +#else + boost::beast::get_lowest_layer(*ws_).async_connect( + endpoints, + boost::beast::bind_front_handler( + &SslClientSession::on_ssl_connect, + std::static_pointer_cast(shared_from_this()))); +#endif } +#if BOOST_VERSION < 107000 void on_ssl_connect(boost::system::error_code ec) { +#else + void on_ssl_connect(boost::beast::error_code ec, tcp::resolver::results_type::endpoint_type) { +#endif if (ec) { std::cerr << "on_ssl_connect : " << ec.message() << std::endl; } @@ -95,7 +130,7 @@ namespace ETP_NS } // Send the handshake to the proxy - http::async_write(ws_.next_layer().next_layer(), proxyHandshake, + http::async_write(ws_->next_layer().next_layer(), proxyHandshake, std::bind( &SslClientSession::on_proxy_handshake_write, std::static_pointer_cast(shared_from_this()), @@ -104,10 +139,10 @@ namespace ETP_NS } else { // Perform the SSL handshake - ws_.next_layer().async_handshake( + ws_->next_layer().async_handshake( boost::asio::ssl::stream_base::client, std::bind( - &AbstractClientSessionCRTP::on_connect, + &AbstractClientSessionCRTP::on_ssl_handshake, std::static_pointer_cast(shared_from_this()), std::placeholders::_1)); } @@ -140,7 +175,7 @@ namespace ETP_NS http_proxy_handshake_parser.skip(true); // see https://stackoverflow.com/a/49837467/10904212 // Receive the HTTP response - http::async_read(ws_.next_layer().next_layer(), receivedBuffer, http_proxy_handshake_parser, + http::async_read(ws_->next_layer().next_layer(), receivedBuffer, http_proxy_handshake_parser, std::bind( &SslClientSession::on_proxy_handshake_read, std::static_pointer_cast(shared_from_this()), @@ -160,12 +195,28 @@ namespace ETP_NS } // Perform the SSL handshake - ws_.next_layer().async_handshake( + ws_->next_layer().async_handshake( boost::asio::ssl::stream_base::client, std::bind( - &AbstractClientSessionCRTP::on_connect, + &AbstractClientSessionCRTP::on_ssl_handshake, std::static_pointer_cast(shared_from_this()), std::placeholders::_1)); } + + private: + boost::asio::ssl::context sslContext_; +#if BOOST_VERSION < 107000 + std::unique_ptr>> ws_; +#elif BOOST_VERSION < 108600 + std::unique_ptr>> ws_; +#else + std::unique_ptr>> ws_; +#endif + http::request proxyHandshake; + http::response proxyHandshakeResponse; + // use own response parser + // NOTE: 200 response to a CONNECT request from a tunneling proxy do not carry a body + http::response_parser http_proxy_handshake_parser; + std::size_t frameSize_; }; } diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt deleted file mode 100644 index b46449c..0000000 --- a/src/tools/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -set(FETPAPI_TOOLS_HEADERS ${CMAKE_CURRENT_LIST_DIR}/date.h ) diff --git a/src/tools/date.h b/src/tools/date.h deleted file mode 100644 index beb627e..0000000 --- a/src/tools/date.h +++ /dev/null @@ -1,8245 +0,0 @@ -#ifndef DATE_H -#define DATE_H - -// The MIT License (MIT) -// -// Copyright (c) 2015, 2016, 2017 Howard Hinnant -// Copyright (c) 2016 Adrian Colomitchi -// Copyright (c) 2017 Florian Dang -// Copyright (c) 2017 Paul Thompson -// Copyright (c) 2018, 2019 Tomasz Kamiński -// Copyright (c) 2019 Jiangang Zhuang -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that would involve another several millennia of evolution). -// We did not mean to shout. - -#ifndef HAS_STRING_VIEW -# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define HAS_STRING_VIEW 1 -# else -# define HAS_STRING_VIEW 0 -# endif -#endif // HAS_STRING_VIEW - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if HAS_STRING_VIEW -# include -#endif -#include -#include - -#ifdef __GNUC__ -# pragma GCC diagnostic push -# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 7) -# pragma GCC diagnostic ignored "-Wpedantic" -# endif -# if __GNUC__ < 5 - // GCC 4.9 Bug 61489 Wrong warning with -Wmissing-field-initializers -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -# endif -#endif - -#ifdef _MSC_VER -# pragma warning(push) -// warning C4127: conditional expression is constant -# pragma warning(disable : 4127) -#endif - -namespace date -{ - -//---------------+ -// Configuration | -//---------------+ - -#ifndef ONLY_C_LOCALE -# define ONLY_C_LOCALE 0 -#endif - -#if defined(_MSC_VER) && (!defined(__clang__) || (_MSC_VER < 1910)) -// MSVC -# ifndef _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING -# define _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING -# endif -# if _MSC_VER < 1910 -// before VS2017 -# define CONSTDATA const -# define CONSTCD11 -# define CONSTCD14 -# define NOEXCEPT _NOEXCEPT -# else -// VS2017 and later -# define CONSTDATA constexpr const -# define CONSTCD11 constexpr -# define CONSTCD14 constexpr -# define NOEXCEPT noexcept -# endif - -#elif defined(__SUNPRO_CC) && __SUNPRO_CC <= 0x5150 -// Oracle Developer Studio 12.6 and earlier -# define CONSTDATA constexpr const -# define CONSTCD11 constexpr -# define CONSTCD14 -# define NOEXCEPT noexcept - -#elif __cplusplus >= 201402 -// C++14 -# define CONSTDATA constexpr const -# define CONSTCD11 constexpr -# define CONSTCD14 constexpr -# define NOEXCEPT noexcept -#else -// C++11 -# define CONSTDATA constexpr const -# define CONSTCD11 constexpr -# define CONSTCD14 -# define NOEXCEPT noexcept -#endif - -#ifndef HAS_UNCAUGHT_EXCEPTIONS -# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define HAS_UNCAUGHT_EXCEPTIONS 1 -# else -# define HAS_UNCAUGHT_EXCEPTIONS 0 -# endif -#endif // HAS_UNCAUGHT_EXCEPTIONS - -#ifndef HAS_VOID_T -# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define HAS_VOID_T 1 -# else -# define HAS_VOID_T 0 -# endif -#endif // HAS_VOID_T - -// Protect from Oracle sun macro -#ifdef sun -# undef sun -#endif - -// Work around for a NVCC compiler bug which causes it to fail -// to compile std::ratio_{multiply,divide} when used directly -// in the std::chrono::duration template instantiations below -namespace detail { -template -using ratio_multiply = decltype(std::ratio_multiply{}); - -template -using ratio_divide = decltype(std::ratio_divide{}); -} // namespace detail - -//-----------+ -// Interface | -//-----------+ - -// durations - -using days = std::chrono::duration - , std::chrono::hours::period>>; - -using weeks = std::chrono::duration - , days::period>>; - -using years = std::chrono::duration - , days::period>>; - -using months = std::chrono::duration - >>; - -// time_point - -template - using sys_time = std::chrono::time_point; - -using sys_days = sys_time; -using sys_seconds = sys_time; - -struct local_t {}; - -template - using local_time = std::chrono::time_point; - -using local_seconds = local_time; -using local_days = local_time; - -// types - -struct last_spec -{ - explicit last_spec() = default; -}; - -class day; -class month; -class year; - -class weekday; -class weekday_indexed; -class weekday_last; - -class month_day; -class month_day_last; -class month_weekday; -class month_weekday_last; - -class year_month; - -class year_month_day; -class year_month_day_last; -class year_month_weekday; -class year_month_weekday_last; - -// date composition operators - -CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; -CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; - -CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; -CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; -CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; - -CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; - -CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; - -CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; - -CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; - -CONSTCD11 - year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; - -// Detailed interface - -// day - -class day -{ - unsigned char d_; - -public: - day() = default; - explicit CONSTCD11 day(unsigned d) NOEXCEPT; - - CONSTCD14 day& operator++() NOEXCEPT; - CONSTCD14 day operator++(int) NOEXCEPT; - CONSTCD14 day& operator--() NOEXCEPT; - CONSTCD14 day operator--(int) NOEXCEPT; - - CONSTCD14 day& operator+=(const days& d) NOEXCEPT; - CONSTCD14 day& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; - -CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; -CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; -CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; -CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d); - -// month - -class month -{ - unsigned char m_; - -public: - month() = default; - explicit CONSTCD11 month(unsigned m) NOEXCEPT; - - CONSTCD14 month& operator++() NOEXCEPT; - CONSTCD14 month operator++(int) NOEXCEPT; - CONSTCD14 month& operator--() NOEXCEPT; - CONSTCD14 month operator--(int) NOEXCEPT; - - CONSTCD14 month& operator+=(const months& m) NOEXCEPT; - CONSTCD14 month& operator-=(const months& m) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; - -CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; -CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; -CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; -CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m); - -// year - -class year -{ - short y_; - -public: - year() = default; - explicit CONSTCD11 year(int y) NOEXCEPT; - - CONSTCD14 year& operator++() NOEXCEPT; - CONSTCD14 year operator++(int) NOEXCEPT; - CONSTCD14 year& operator--() NOEXCEPT; - CONSTCD14 year operator--(int) NOEXCEPT; - - CONSTCD14 year& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 year operator-() const NOEXCEPT; - CONSTCD11 year operator+() const NOEXCEPT; - - CONSTCD11 bool is_leap() const NOEXCEPT; - - CONSTCD11 explicit operator int() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - static CONSTCD11 year min() NOEXCEPT { return year{-32767}; } - static CONSTCD11 year max() NOEXCEPT { return year{32767}; } -}; - -CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; - -CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; -CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; -CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; -CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y); - -// weekday - -class weekday -{ - unsigned char wd_; -public: - weekday() = default; - explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; - CONSTCD14 weekday(const sys_days& dp) NOEXCEPT; - CONSTCD14 explicit weekday(const local_days& dp) NOEXCEPT; - - CONSTCD14 weekday& operator++() NOEXCEPT; - CONSTCD14 weekday operator++(int) NOEXCEPT; - CONSTCD14 weekday& operator--() NOEXCEPT; - CONSTCD14 weekday operator--(int) NOEXCEPT; - - CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; - CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; - - CONSTCD11 unsigned c_encoding() const NOEXCEPT; - CONSTCD11 unsigned iso_encoding() const NOEXCEPT; - - CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; - CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; - -private: - static CONSTCD14 unsigned char weekday_from_days(int z) NOEXCEPT; - - friend CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; - friend CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; - friend CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; - template - friend std::basic_ostream& - operator<<(std::basic_ostream& os, const weekday& wd); - friend class weekday_indexed; -}; - -CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; - -CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; -CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd); - -// weekday_indexed - -class weekday_indexed -{ - unsigned char wd_ : 4; - unsigned char index_ : 4; - -public: - weekday_indexed() = default; - CONSTCD11 weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT; - - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi); - -// weekday_last - -class weekday_last -{ - date::weekday wd_; - -public: - explicit CONSTCD11 weekday_last(const date::weekday& wd) NOEXCEPT; - - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl); - -namespace detail -{ - -struct unspecified_month_disambiguator {}; - -} // namespace detail - -// year_month - -class year_month -{ - date::year y_; - date::month m_; - -public: - year_month() = default; - CONSTCD11 year_month(const date::year& y, const date::month& m) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - - template - CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; - template - CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; - CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; - CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; - -template -CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; -template -CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; -template -CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; - -CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; -CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; -CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym); - -// month_day - -class month_day -{ - date::month m_; - date::day d_; - -public: - month_day() = default; - CONSTCD11 month_day(const date::month& m, const date::day& d) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::day day() const NOEXCEPT; - - CONSTCD14 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md); - -// month_day_last - -class month_day_last -{ - date::month m_; - -public: - CONSTCD11 explicit month_day_last(const date::month& m) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl); - -// month_weekday - -class month_weekday -{ - date::month m_; - date::weekday_indexed wdi_; -public: - CONSTCD11 month_weekday(const date::month& m, - const date::weekday_indexed& wdi) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd); - -// month_weekday_last - -class month_weekday_last -{ - date::month m_; - date::weekday_last wdl_; - -public: - CONSTCD11 month_weekday_last(const date::month& m, - const date::weekday_last& wd) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); - -// class year_month_day - -class year_month_day -{ - date::year y_; - date::month m_; - date::day d_; - -public: - year_month_day() = default; - CONSTCD11 year_month_day(const date::year& y, const date::month& m, - const date::day& d) NOEXCEPT; - CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; - - CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; - CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; - - template - CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; - template - CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; - -template -CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; -template -CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; -template -CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; -CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; -CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; -CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd); - -// year_month_day_last - -class year_month_day_last -{ - date::year y_; - date::month_day_last mdl_; - -public: - CONSTCD11 year_month_day_last(const date::year& y, - const date::month_day_last& mdl) NOEXCEPT; - - template - CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; - template - CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::month_day_last month_day_last() const NOEXCEPT; - CONSTCD14 date::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; - -template -CONSTCD14 -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -template -CONSTCD14 -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; - -template -CONSTCD14 -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); - -// year_month_weekday - -class year_month_weekday -{ - date::year y_; - date::month m_; - date::weekday_indexed wdi_; - -public: - year_month_weekday() = default; - CONSTCD11 year_month_weekday(const date::year& y, const date::month& m, - const date::weekday_indexed& wdi) NOEXCEPT; - CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; - CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; - - template - CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; - template - CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); - -// year_month_weekday_last - -class year_month_weekday_last -{ - date::year y_; - date::month m_; - date::weekday_last wdl_; - -public: - CONSTCD11 year_month_weekday_last(const date::year& y, const date::month& m, - const date::weekday_last& wdl) NOEXCEPT; - - template - CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; - template - CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - -private: - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -CONSTCD11 -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 date::day operator "" _d(unsigned long long d) NOEXCEPT; -CONSTCD11 date::year operator "" _y(unsigned long long y) NOEXCEPT; - -} // inline namespace literals -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -// CONSTDATA date::month January{1}; -// CONSTDATA date::month February{2}; -// CONSTDATA date::month March{3}; -// CONSTDATA date::month April{4}; -// CONSTDATA date::month May{5}; -// CONSTDATA date::month June{6}; -// CONSTDATA date::month July{7}; -// CONSTDATA date::month August{8}; -// CONSTDATA date::month September{9}; -// CONSTDATA date::month October{10}; -// CONSTDATA date::month November{11}; -// CONSTDATA date::month December{12}; -// -// CONSTDATA date::weekday Sunday{0u}; -// CONSTDATA date::weekday Monday{1u}; -// CONSTDATA date::weekday Tuesday{2u}; -// CONSTDATA date::weekday Wednesday{3u}; -// CONSTDATA date::weekday Thursday{4u}; -// CONSTDATA date::weekday Friday{5u}; -// CONSTDATA date::weekday Saturday{6u}; - -#if HAS_VOID_T - -template > -struct is_clock - : std::false_type -{}; - -template -struct is_clock> - : std::true_type -{}; - -template inline constexpr bool is_clock_v = is_clock::value; - -#endif // HAS_VOID_T - -//----------------+ -// Implementation | -//----------------+ - -// utilities -namespace detail { - -template> -class save_istream -{ -protected: - std::basic_ios& is_; - CharT fill_; - std::ios::fmtflags flags_; - std::streamsize precision_; - std::streamsize width_; - std::basic_ostream* tie_; - std::locale loc_; - -public: - ~save_istream() - { - is_.fill(fill_); - is_.flags(flags_); - is_.precision(precision_); - is_.width(width_); - is_.imbue(loc_); - is_.tie(tie_); - } - - save_istream(const save_istream&) = delete; - save_istream& operator=(const save_istream&) = delete; - - explicit save_istream(std::basic_ios& is) - : is_(is) - , fill_(is.fill()) - , flags_(is.flags()) - , precision_(is.precision()) - , width_(is.width(0)) - , tie_(is.tie(nullptr)) - , loc_(is.getloc()) - { - if (tie_ != nullptr) - tie_->flush(); - } -}; - -template> -class save_ostream - : private save_istream -{ -public: - ~save_ostream() - { - if ((this->flags_ & std::ios::unitbuf) && -#if HAS_UNCAUGHT_EXCEPTIONS - std::uncaught_exceptions() == 0 && -#else - !std::uncaught_exception() && -#endif - this->is_.good()) - this->is_.rdbuf()->pubsync(); - } - - save_ostream(const save_ostream&) = delete; - save_ostream& operator=(const save_ostream&) = delete; - - explicit save_ostream(std::basic_ios& os) - : save_istream(os) - { - } -}; - -template -struct choose_trunc_type -{ - static const int digits = std::numeric_limits::digits; - using type = typename std::conditional - < - digits < 32, - std::int32_t, - typename std::conditional - < - digits < 64, - std::int64_t, -#ifdef __SIZEOF_INT128__ - __int128 -#else - std::int64_t -#endif - >::type - >::type; -}; - -template -CONSTCD11 -inline -typename std::enable_if -< - !std::chrono::treat_as_floating_point::value, - T ->::type -trunc(T t) NOEXCEPT -{ - return t; -} - -template -CONSTCD14 -inline -typename std::enable_if -< - std::chrono::treat_as_floating_point::value, - T ->::type -trunc(T t) NOEXCEPT -{ - using std::numeric_limits; - using I = typename choose_trunc_type::type; - CONSTDATA auto digits = numeric_limits::digits; - static_assert(digits < numeric_limits::digits, ""); - CONSTDATA auto max = I{1} << (digits-1); - CONSTDATA auto min = -max; - const auto negative = t < T{0}; - if (min <= t && t <= max && t != 0 && t == t) - { - t = static_cast(static_cast(t)); - if (t == 0 && negative) - t = -t; - } - return t; -} - -template -struct static_gcd -{ - static const std::intmax_t value = static_gcd::value; -}; - -template -struct static_gcd -{ - static const std::intmax_t value = Xp; -}; - -template <> -struct static_gcd<0, 0> -{ - static const std::intmax_t value = 1; -}; - -template -struct no_overflow -{ -private: - static const std::intmax_t gcd_n1_n2 = static_gcd::value; - static const std::intmax_t gcd_d1_d2 = static_gcd::value; - static const std::intmax_t n1 = R1::num / gcd_n1_n2; - static const std::intmax_t d1 = R1::den / gcd_d1_d2; - static const std::intmax_t n2 = R2::num / gcd_n1_n2; - static const std::intmax_t d2 = R2::den / gcd_d1_d2; -#ifdef __cpp_constexpr - static const std::intmax_t max = std::numeric_limits::max(); -#else - static const std::intmax_t max = LLONG_MAX; -#endif - - template - struct mul // overflow == false - { - static const std::intmax_t value = Xp * Yp; - }; - - template - struct mul - { - static const std::intmax_t value = 1; - }; - -public: - static const bool value = (n1 <= max / d2) && (n2 <= max / d1); - typedef std::ratio::value, - mul::value> type; -}; - -} // detail - -// trunc towards zero -template -CONSTCD11 -inline -typename std::enable_if -< - detail::no_overflow::value, - To ->::type -trunc(const std::chrono::duration& d) -{ - return To{detail::trunc(std::chrono::duration_cast(d).count())}; -} - -template -CONSTCD11 -inline -typename std::enable_if -< - !detail::no_overflow::value, - To ->::type -trunc(const std::chrono::duration& d) -{ - using std::chrono::duration_cast; - using std::chrono::duration; - using rep = typename std::common_type::type; - return To{detail::trunc(duration_cast(duration_cast>(d)).count())}; -} - -#ifndef HAS_CHRONO_ROUNDING -# if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 190023918 || (_MSC_FULL_VER >= 190000000 && defined (__clang__))) -# define HAS_CHRONO_ROUNDING 1 -# elif defined(__cpp_lib_chrono) && __cplusplus > 201402 && __cpp_lib_chrono >= 201510 -# define HAS_CHRONO_ROUNDING 1 -# elif defined(_LIBCPP_VERSION) && __cplusplus > 201402 && _LIBCPP_VERSION >= 3800 -# define HAS_CHRONO_ROUNDING 1 -# else -# define HAS_CHRONO_ROUNDING 0 -# endif -#endif // HAS_CHRONO_ROUNDING - -#if HAS_CHRONO_ROUNDING == 0 - -// round down -template -CONSTCD14 -inline -typename std::enable_if -< - detail::no_overflow::value, - To ->::type -floor(const std::chrono::duration& d) -{ - auto t = trunc(d); - if (t > d) - return t - To{1}; - return t; -} - -template -CONSTCD14 -inline -typename std::enable_if -< - !detail::no_overflow::value, - To ->::type -floor(const std::chrono::duration& d) -{ - using rep = typename std::common_type::type; - return floor(floor>(d)); -} - -// round to nearest, to even on tie -template -CONSTCD14 -inline -To -round(const std::chrono::duration& d) -{ - auto t0 = floor(d); - auto t1 = t0 + To{1}; - if (t1 == To{0} && t0 < To{0}) - t1 = -t1; - auto diff0 = d - t0; - auto diff1 = t1 - d; - if (diff0 == diff1) - { - if (t0 - trunc(t0/2)*2 == To{0}) - return t0; - return t1; - } - if (diff0 < diff1) - return t0; - return t1; -} - -// round up -template -CONSTCD14 -inline -To -ceil(const std::chrono::duration& d) -{ - auto t = trunc(d); - if (t < d) - return t + To{1}; - return t; -} - -template ::is_signed - >::type> -CONSTCD11 -std::chrono::duration -abs(std::chrono::duration d) -{ - return d >= d.zero() ? d : static_cast(-d); -} - -// round down -template -CONSTCD11 -inline -std::chrono::time_point -floor(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{date::floor(tp.time_since_epoch())}; -} - -// round to nearest, to even on tie -template -CONSTCD11 -inline -std::chrono::time_point -round(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{round(tp.time_since_epoch())}; -} - -// round up -template -CONSTCD11 -inline -std::chrono::time_point -ceil(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{ceil(tp.time_since_epoch())}; -} - -#else // HAS_CHRONO_ROUNDING == 1 - -using std::chrono::floor; -using std::chrono::ceil; -using std::chrono::round; -using std::chrono::abs; - -#endif // HAS_CHRONO_ROUNDING - -namespace detail -{ - -template -CONSTCD14 -inline -typename std::enable_if -< - !std::chrono::treat_as_floating_point::value, - To ->::type -round_i(const std::chrono::duration& d) -{ - return round(d); -} - -template -CONSTCD14 -inline -typename std::enable_if -< - std::chrono::treat_as_floating_point::value, - To ->::type -round_i(const std::chrono::duration& d) -{ - return d; -} - -template -CONSTCD11 -inline -std::chrono::time_point -round_i(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{round_i(tp.time_since_epoch())}; -} - -} // detail - -// trunc towards zero -template -CONSTCD11 -inline -std::chrono::time_point -trunc(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{trunc(tp.time_since_epoch())}; -} - -// day - -CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} -CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} -CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} -CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} -CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} -CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} -CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 31;} - -CONSTCD11 -inline -bool -operator==(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const day& x, const day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const day& x, const day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const day& x, const day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const day& x, const day& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -days -operator-(const day& x, const day& y) NOEXCEPT -{ - return days{static_cast(static_cast(x) - - static_cast(y))}; -} - -CONSTCD11 -inline -day -operator+(const day& x, const days& y) NOEXCEPT -{ - return day{static_cast(x) + static_cast(y.count())}; -} - -CONSTCD11 -inline -day -operator+(const days& x, const day& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -day -operator-(const day& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const day& d) -{ - detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(d); - return os; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d) -{ - detail::low_level_fmt(os, d); - if (!d.ok()) - os << " is not a valid day"; - return os; -} - -// month - -CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} -CONSTCD14 inline month& month::operator++() NOEXCEPT {*this += months{1}; return *this;} -CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline month& month::operator--() NOEXCEPT {*this -= months{1}; return *this;} -CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -month& -month::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -month& -month::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} - -CONSTCD11 -inline -bool -operator==(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const month& x, const month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const month& x, const month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month& x, const month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month& x, const month& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD14 -inline -months -operator-(const month& x, const month& y) NOEXCEPT -{ - auto const d = static_cast(x) - static_cast(y); - return months(d <= 11 ? d : d + 12); -} - -CONSTCD14 -inline -month -operator+(const month& x, const months& y) NOEXCEPT -{ - auto const mu = static_cast(static_cast(x)) + y.count() - 1; - auto const yr = (mu >= 0 ? mu : mu-11) / 12; - return month{static_cast(mu - yr * 12 + 1)}; -} - -CONSTCD14 -inline -month -operator+(const months& x, const month& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -month -operator-(const month& x, const months& y) NOEXCEPT -{ - return x + -y; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month& m) -{ - if (m.ok()) - { - CharT fmt[] = {'%', 'b', 0}; - os << format(os.getloc(), fmt, m); - } - else - os << static_cast(m); - return os; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m) -{ - detail::low_level_fmt(os, m); - if (!m.ok()) - os << " is not a valid month"; - return os; -} - -// year - -CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} -CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} -CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} -CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} -CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} -CONSTCD11 inline year year::operator-() const NOEXCEPT {return year{-y_};} -CONSTCD11 inline year year::operator+() const NOEXCEPT {return *this;} - -CONSTCD11 -inline -bool -year::is_leap() const NOEXCEPT -{ - return y_ % 4 == 0 && (y_ % 100 != 0 || y_ % 400 == 0); -} - -CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} - -CONSTCD11 -inline -bool -year::ok() const NOEXCEPT -{ - return y_ != std::numeric_limits::min(); -} - -CONSTCD11 -inline -bool -operator==(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const year& x, const year& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const year& x, const year& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year& x, const year& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year& x, const year& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -years -operator-(const year& x, const year& y) NOEXCEPT -{ - return years{static_cast(x) - static_cast(y)}; -} - -CONSTCD11 -inline -year -operator+(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) + y.count()}; -} - -CONSTCD11 -inline -year -operator+(const years& x, const year& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -year -operator-(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) - y.count()}; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const year& y) -{ - detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::internal); - os.width(4 + (y < year{0})); - os.imbue(std::locale::classic()); - os << static_cast(y); - return os; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y) -{ - detail::low_level_fmt(os, y); - if (!y.ok()) - os << " is not a valid year"; - return os; -} - -// weekday - -CONSTCD14 -inline -unsigned char -weekday::weekday_from_days(int z) NOEXCEPT -{ - auto u = static_cast(z); - return static_cast(z >= -4 ? (u+4) % 7 : u % 7); -} - -CONSTCD11 -inline -weekday::weekday(unsigned wd) NOEXCEPT - : wd_(static_cast(wd != 7 ? wd : 0)) - {} - -CONSTCD14 -inline -weekday::weekday(const sys_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD14 -inline -weekday::weekday(const local_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {*this += days{1}; return *this;} -CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {*this -= days{1}; return *this;} -CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -weekday& -weekday::operator+=(const days& d) NOEXCEPT -{ - *this = *this + d; - return *this; -} - -CONSTCD14 -inline -weekday& -weekday::operator-=(const days& d) NOEXCEPT -{ - *this = *this - d; - return *this; -} - -CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} - -CONSTCD11 -inline -unsigned weekday::c_encoding() const NOEXCEPT -{ - return unsigned{wd_}; -} - -CONSTCD11 -inline -unsigned weekday::iso_encoding() const NOEXCEPT -{ - return unsigned{((wd_ == 0u) ? 7u : wd_)}; -} - -CONSTCD11 -inline -bool -operator==(const weekday& x, const weekday& y) NOEXCEPT -{ - return x.wd_ == y.wd_; -} - -CONSTCD11 -inline -bool -operator!=(const weekday& x, const weekday& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD14 -inline -days -operator-(const weekday& x, const weekday& y) NOEXCEPT -{ - auto const wdu = x.wd_ - y.wd_; - auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; - return days{wdu - wk * 7}; -} - -CONSTCD14 -inline -weekday -operator+(const weekday& x, const days& y) NOEXCEPT -{ - auto const wdu = static_cast(static_cast(x.wd_)) + y.count(); - auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; - return weekday{static_cast(wdu - wk * 7)}; -} - -CONSTCD14 -inline -weekday -operator+(const days& x, const weekday& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -weekday -operator-(const weekday& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const weekday& wd) -{ - if (wd.ok()) - { - CharT fmt[] = {'%', 'a', 0}; - os << format(fmt, wd); - } - else - os << wd.c_encoding(); - return os; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd) -{ - detail::low_level_fmt(os, wd); - if (!wd.ok()) - os << " is not a valid weekday"; - return os; -} - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 -inline -date::day -operator "" _d(unsigned long long d) NOEXCEPT -{ - return date::day{static_cast(d)}; -} - -CONSTCD11 -inline -date::year -operator "" _y(unsigned long long y) NOEXCEPT -{ - return date::year(static_cast(y)); -} -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -CONSTDATA date::last_spec last{}; - -CONSTDATA date::month jan{1}; -CONSTDATA date::month feb{2}; -CONSTDATA date::month mar{3}; -CONSTDATA date::month apr{4}; -CONSTDATA date::month may{5}; -CONSTDATA date::month jun{6}; -CONSTDATA date::month jul{7}; -CONSTDATA date::month aug{8}; -CONSTDATA date::month sep{9}; -CONSTDATA date::month oct{10}; -CONSTDATA date::month nov{11}; -CONSTDATA date::month dec{12}; - -CONSTDATA date::weekday sun{0u}; -CONSTDATA date::weekday mon{1u}; -CONSTDATA date::weekday tue{2u}; -CONSTDATA date::weekday wed{3u}; -CONSTDATA date::weekday thu{4u}; -CONSTDATA date::weekday fri{5u}; -CONSTDATA date::weekday sat{6u}; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -} // inline namespace literals -#endif - -CONSTDATA date::month January{1}; -CONSTDATA date::month February{2}; -CONSTDATA date::month March{3}; -CONSTDATA date::month April{4}; -CONSTDATA date::month May{5}; -CONSTDATA date::month June{6}; -CONSTDATA date::month July{7}; -CONSTDATA date::month August{8}; -CONSTDATA date::month September{9}; -CONSTDATA date::month October{10}; -CONSTDATA date::month November{11}; -CONSTDATA date::month December{12}; - -CONSTDATA date::weekday Monday{1}; -CONSTDATA date::weekday Tuesday{2}; -CONSTDATA date::weekday Wednesday{3}; -CONSTDATA date::weekday Thursday{4}; -CONSTDATA date::weekday Friday{5}; -CONSTDATA date::weekday Saturday{6}; -CONSTDATA date::weekday Sunday{7}; - -// weekday_indexed - -CONSTCD11 -inline -weekday -weekday_indexed::weekday() const NOEXCEPT -{ - return date::weekday{static_cast(wd_)}; -} - -CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} - -CONSTCD11 -inline -bool -weekday_indexed::ok() const NOEXCEPT -{ - return weekday().ok() && 1 <= index_ && index_ <= 5; -} - -#ifdef __GNUC__ -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wconversion" -#endif // __GNUC__ - -CONSTCD11 -inline -weekday_indexed::weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT - : wd_(static_cast(static_cast(wd.wd_))) - , index_(static_cast(index)) - {} - -#ifdef __GNUC__ -# pragma GCC diagnostic pop -#endif // __GNUC__ - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const weekday_indexed& wdi) -{ - return low_level_fmt(os, wdi.weekday()) << '[' << wdi.index() << ']'; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi) -{ - detail::low_level_fmt(os, wdi); - if (!wdi.ok()) - os << " is not a valid weekday_indexed"; - return os; -} - -CONSTCD11 -inline -weekday_indexed -weekday::operator[](unsigned index) const NOEXCEPT -{ - return {*this, index}; -} - -CONSTCD11 -inline -bool -operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return x.weekday() == y.weekday() && x.index() == y.index(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return !(x == y); -} - -// weekday_last - -CONSTCD11 inline date::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} -CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} -CONSTCD11 inline weekday_last::weekday_last(const date::weekday& wd) NOEXCEPT : wd_(wd) {} - -CONSTCD11 -inline -bool -operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return x.weekday() == y.weekday(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const weekday_last& wdl) -{ - return low_level_fmt(os, wdl.weekday()) << "[last]"; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl) -{ - detail::low_level_fmt(os, wdl); - if (!wdl.ok()) - os << " is not a valid weekday_last"; - return os; -} - -CONSTCD11 -inline -weekday_last -weekday::operator[](last_spec) const NOEXCEPT -{ - return weekday_last{*this}; -} - -// year_month - -CONSTCD11 -inline -year_month::year_month(const date::year& y, const date::month& m) NOEXCEPT - : y_(y) - , m_(m) - {} - -CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} - -template -CONSTCD14 -inline -year_month& -year_month::operator+=(const months& dm) NOEXCEPT -{ - *this = *this + dm; - return *this; -} - -template -CONSTCD14 -inline -year_month& -year_month::operator-=(const months& dm) NOEXCEPT -{ - *this = *this - dm; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator+=(const years& dy) NOEXCEPT -{ - *this = *this + dy; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator-=(const years& dy) NOEXCEPT -{ - *this = *this - dy; - return *this; -} - -CONSTCD11 -inline -bool -operator==(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month())); -} - -CONSTCD11 -inline -bool -operator>(const year_month& x, const year_month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x < y); -} - -template -CONSTCD14 -inline -year_month -operator+(const year_month& ym, const months& dm) NOEXCEPT -{ - auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); - auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; - dmi = dmi - dy * 12 + 1; - return (ym.year() + years(dy)) / month(static_cast(dmi)); -} - -template -CONSTCD14 -inline -year_month -operator+(const months& dm, const year_month& ym) NOEXCEPT -{ - return ym + dm; -} - -template -CONSTCD14 -inline -year_month -operator-(const year_month& ym, const months& dm) NOEXCEPT -{ - return ym + -dm; -} - -CONSTCD11 -inline -months -operator-(const year_month& x, const year_month& y) NOEXCEPT -{ - return (x.year() - y.year()) + - months(static_cast(x.month()) - static_cast(y.month())); -} - -CONSTCD11 -inline -year_month -operator+(const year_month& ym, const years& dy) NOEXCEPT -{ - return (ym.year() + dy) / ym.month(); -} - -CONSTCD11 -inline -year_month -operator+(const years& dy, const year_month& ym) NOEXCEPT -{ - return ym + dy; -} - -CONSTCD11 -inline -year_month -operator-(const year_month& ym, const years& dy) NOEXCEPT -{ - return ym + -dy; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const year_month& ym) -{ - low_level_fmt(os, ym.year()) << '/'; - return low_level_fmt(os, ym.month()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym) -{ - detail::low_level_fmt(os, ym); - if (!ym.ok()) - os << " is not a valid year_month"; - return os; -} - -// month_day - -CONSTCD11 -inline -month_day::month_day(const date::month& m, const date::day& d) NOEXCEPT - : m_(m) - , d_(d) - {} - -CONSTCD11 inline date::month month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline date::day month_day::day() const NOEXCEPT {return d_;} - -CONSTCD14 -inline -bool -month_day::ok() const NOEXCEPT -{ - CONSTDATA date::day d[] = - { - date::day(31), date::day(29), date::day(31), - date::day(30), date::day(31), date::day(30), - date::day(31), date::day(31), date::day(30), - date::day(31), date::day(30), date::day(31) - }; - return m_.ok() && date::day{1} <= d_ && d_ <= d[static_cast(m_)-1]; -} - -CONSTCD11 -inline -bool -operator==(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())); -} - -CONSTCD11 -inline -bool -operator>(const month_day& x, const month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x < y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month_day& md) -{ - low_level_fmt(os, md.month()) << '/'; - return low_level_fmt(os, md.day()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md) -{ - detail::low_level_fmt(os, md); - if (!md.ok()) - os << " is not a valid month_day"; - return os; -} - -// month_day_last - -CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} -CONSTCD11 inline month_day_last::month_day_last(const date::month& m) NOEXCEPT : m_(m) {} - -CONSTCD11 -inline -bool -operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() < y.month(); -} - -CONSTCD11 -inline -bool -operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month_day_last& mdl) -{ - return low_level_fmt(os, mdl.month()) << "/last"; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl) -{ - detail::low_level_fmt(os, mdl); - if (!mdl.ok()) - os << " is not a valid month_day_last"; - return os; -} - -// month_weekday - -CONSTCD11 -inline -month_weekday::month_weekday(const date::month& m, - const date::weekday_indexed& wdi) NOEXCEPT - : m_(m) - , wdi_(wdi) - {} - -CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_indexed -month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD11 -inline -bool -month_weekday::ok() const NOEXCEPT -{ - return m_.ok() && wdi_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month_weekday& mwd) -{ - low_level_fmt(os, mwd.month()) << '/'; - return low_level_fmt(os, mwd.weekday_indexed()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd) -{ - detail::low_level_fmt(os, mwd); - if (!mwd.ok()) - os << " is not a valid month_weekday"; - return os; -} - -// month_weekday_last - -CONSTCD11 -inline -month_weekday_last::month_weekday_last(const date::month& m, - const date::weekday_last& wdl) NOEXCEPT - : m_(m) - , wdl_(wdl) - {} - -CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_last -month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD11 -inline -bool -month_weekday_last::ok() const NOEXCEPT -{ - return m_.ok() && wdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month_weekday_last& mwdl) -{ - low_level_fmt(os, mwdl.month()) << '/'; - return low_level_fmt(os, mwdl.weekday_last()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) -{ - detail::low_level_fmt(os, mwdl); - if (!mwdl.ok()) - os << " is not a valid month_weekday_last"; - return os; -} - -// year_month_day_last - -CONSTCD11 -inline -year_month_day_last::year_month_day_last(const date::year& y, - const date::month_day_last& mdl) NOEXCEPT - : y_(y) - , mdl_(mdl) - {} - -template -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -template -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} - -CONSTCD11 -inline -month_day_last -year_month_day_last::month_day_last() const NOEXCEPT -{ - return mdl_; -} - -CONSTCD14 -inline -day -year_month_day_last::day() const NOEXCEPT -{ - CONSTDATA date::day d[] = - { - date::day(31), date::day(28), date::day(31), - date::day(30), date::day(31), date::day(30), - date::day(31), date::day(31), date::day(30), - date::day(31), date::day(30), date::day(31) - }; - return (month() != February || !y_.is_leap()) && mdl_.ok() ? - d[static_cast(month()) - 1] : date::day{29}; -} - -CONSTCD14 -inline -year_month_day_last::operator sys_days() const NOEXCEPT -{ - return sys_days(year()/month()/day()); -} - -CONSTCD14 -inline -year_month_day_last::operator local_days() const NOEXCEPT -{ - return local_days(year()/month()/day()); -} - -CONSTCD11 -inline -bool -year_month_day_last::ok() const NOEXCEPT -{ - return y_.ok() && mdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month_day_last() == y.month_day_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month_day_last() < y.month_day_last())); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const year_month_day_last& ymdl) -{ - low_level_fmt(os, ymdl.year()) << '/'; - return low_level_fmt(os, ymdl.month_day_last()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) -{ - detail::low_level_fmt(os, ymdl); - if (!ymdl.ok()) - os << " is not a valid year_month_day_last"; - return os; -} - -template -CONSTCD14 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return (ymdl.year() / ymdl.month() + dm) / last; -} - -template -CONSTCD14 -inline -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dm; -} - -template -CONSTCD14 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return ymdl + (-dm); -} - -CONSTCD11 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return {ymdl.year()+dy, ymdl.month_day_last()}; -} - -CONSTCD11 -inline -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dy; -} - -CONSTCD11 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return ymdl + (-dy); -} - -// year_month_day - -CONSTCD11 -inline -year_month_day::year_month_day(const date::year& y, const date::month& m, - const date::day& d) NOEXCEPT - : y_(y) - , m_(m) - , d_(d) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT - : y_(ymdl.year()) - , m_(ymdl.month()) - , d_(ymdl.day()) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(sys_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(local_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} - -template -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -template -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD14 -inline -days -year_month_day::to_days() const NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const y = static_cast(y_) - (m_ <= February); - auto const m = static_cast(m_); - auto const d = static_cast(d_); - auto const era = (y >= 0 ? y : y-399) / 400; - auto const yoe = static_cast(y - era * 400); // [0, 399] - auto const doy = (153*(m > 2 ? m-3 : m+9) + 2)/5 + d-1; // [0, 365] - auto const doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096] - return days{era * 146097 + static_cast(doe) - 719468}; -} - -CONSTCD14 -inline -year_month_day::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_day::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_day::ok() const NOEXCEPT -{ - if (!(y_.ok() && m_.ok())) - return false; - return date::day{1} <= d_ && d_ <= (y_ / m_ / last).day(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())))); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd) -{ - detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.imbue(std::locale::classic()); - os << static_cast(ymd.year()) << '-'; - os.width(2); - os << static_cast(ymd.month()) << '-'; - os.width(2); - os << static_cast(ymd.day()); - if (!ymd.ok()) - os << " is not a valid year_month_day"; - return os; -} - -CONSTCD14 -inline -year_month_day -year_month_day::from_days(days dp) NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const z = dp.count() + 719468; - auto const era = (z >= 0 ? z : z - 146096) / 146097; - auto const doe = static_cast(z - era * 146097); // [0, 146096] - auto const yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399] - auto const y = static_cast(yoe) + era * 400; - auto const doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365] - auto const mp = (5*doy + 2)/153; // [0, 11] - auto const d = doy - (153*mp+2)/5 + 1; // [1, 31] - auto const m = mp < 10 ? mp+3 : mp-9; // [1, 12] - return year_month_day{date::year{y + (m <= 2)}, date::month(m), date::day(d)}; -} - -template -CONSTCD14 -inline -year_month_day -operator+(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return (ymd.year() / ymd.month() + dm) / ymd.day(); -} - -template -CONSTCD14 -inline -year_month_day -operator+(const months& dm, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dm; -} - -template -CONSTCD14 -inline -year_month_day -operator-(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return ymd + (-dm); -} - -CONSTCD11 -inline -year_month_day -operator+(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return (ymd.year() + dy) / ymd.month() / ymd.day(); -} - -CONSTCD11 -inline -year_month_day -operator+(const years& dy, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dy; -} - -CONSTCD11 -inline -year_month_day -operator-(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return ymd + (-dy); -} - -// year_month_weekday - -CONSTCD11 -inline -year_month_weekday::year_month_weekday(const date::year& y, const date::month& m, - const date::weekday_indexed& wdi) - NOEXCEPT - : y_(y) - , m_(m) - , wdi_(wdi) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -template -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -template -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday::weekday() const NOEXCEPT -{ - return wdi_.weekday(); -} - -CONSTCD11 -inline -unsigned -year_month_weekday::index() const NOEXCEPT -{ - return wdi_.index(); -} - -CONSTCD11 -inline -weekday_indexed -year_month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD14 -inline -year_month_weekday::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_weekday::ok() const NOEXCEPT -{ - if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) - return false; - if (wdi_.index() <= 4) - return true; - auto d2 = wdi_.weekday() - date::weekday(static_cast(y_/m_/1)) + - days((wdi_.index()-1)*7 + 1); - return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); -} - -CONSTCD14 -inline -year_month_weekday -year_month_weekday::from_days(days d) NOEXCEPT -{ - sys_days dp{d}; - auto const wd = date::weekday(dp); - auto const ymd = year_month_day(dp); - return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; -} - -CONSTCD14 -inline -days -year_month_weekday::to_days() const NOEXCEPT -{ - auto d = sys_days(y_/m_/1); - return (d + (wdi_.weekday() - date::weekday(d) + days{(wdi_.index()-1)*7}) - ).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) -{ - detail::low_level_fmt(os, ymwdi.year()) << '/'; - detail::low_level_fmt(os, ymwdi.month()) << '/'; - detail::low_level_fmt(os, ymwdi.weekday_indexed()); - if (!ymwdi.ok()) - os << " is not a valid year_month_weekday"; - return os; -} - -template -CONSTCD14 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); -} - -template -CONSTCD14 -inline -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dm; -} - -template -CONSTCD14 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return ymwd + (-dm); -} - -CONSTCD11 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dy; -} - -CONSTCD11 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return ymwd + (-dy); -} - -// year_month_weekday_last - -CONSTCD11 -inline -year_month_weekday_last::year_month_weekday_last(const date::year& y, - const date::month& m, - const date::weekday_last& wdl) NOEXCEPT - : y_(y) - , m_(m) - , wdl_(wdl) - {} - -template -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -template -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday_last::weekday() const NOEXCEPT -{ - return wdl_.weekday(); -} - -CONSTCD11 -inline -weekday_last -year_month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD14 -inline -year_month_weekday_last::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday_last::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD11 -inline -bool -year_month_weekday_last::ok() const NOEXCEPT -{ - return y_.ok() && m_.ok() && wdl_.ok(); -} - -CONSTCD14 -inline -days -year_month_weekday_last::to_days() const NOEXCEPT -{ - auto const d = sys_days(y_/m_/last); - return (d - (date::weekday{d} - wdl_.weekday())).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) -{ - detail::low_level_fmt(os, ymwdl.year()) << '/'; - detail::low_level_fmt(os, ymwdl.month()) << '/'; - detail::low_level_fmt(os, ymwdl.weekday_last()); - if (!ymwdl.ok()) - os << " is not a valid year_month_weekday_last"; - return os; -} - -template -CONSTCD14 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); -} - -template -CONSTCD14 -inline -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dm; -} - -template -CONSTCD14 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return ymwdl + (-dm); -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dy; -} - -CONSTCD11 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return ymwdl + (-dy); -} - -// year_month from operator/() - -CONSTCD11 -inline -year_month -operator/(const year& y, const month& m) NOEXCEPT -{ - return {y, m}; -} - -CONSTCD11 -inline -year_month -operator/(const year& y, int m) NOEXCEPT -{ - return y / month(static_cast(m)); -} - -// month_day from operator/() - -CONSTCD11 -inline -month_day -operator/(const month& m, const day& d) NOEXCEPT -{ - return {m, d}; -} - -CONSTCD11 -inline -month_day -operator/(const day& d, const month& m) NOEXCEPT -{ - return m / d; -} - -CONSTCD11 -inline -month_day -operator/(const month& m, int d) NOEXCEPT -{ - return m / day(static_cast(d)); -} - -CONSTCD11 -inline -month_day -operator/(int m, const day& d) NOEXCEPT -{ - return month(static_cast(m)) / d; -} - -CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} - -// month_day_last from operator/() - -CONSTCD11 -inline -month_day_last -operator/(const month& m, last_spec) NOEXCEPT -{ - return month_day_last{m}; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, const month& m) NOEXCEPT -{ - return m/last; -} - -CONSTCD11 -inline -month_day_last -operator/(int m, last_spec) NOEXCEPT -{ - return month(static_cast(m))/last; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, int m) NOEXCEPT -{ - return m/last; -} - -// month_weekday from operator/() - -CONSTCD11 -inline -month_weekday -operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT -{ - return {m, wdi}; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT -{ - return m / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(int m, const weekday_indexed& wdi) NOEXCEPT -{ - return month(static_cast(m)) / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, int m) NOEXCEPT -{ - return m / wdi; -} - -// month_weekday_last from operator/() - -CONSTCD11 -inline -month_weekday_last -operator/(const month& m, const weekday_last& wdl) NOEXCEPT -{ - return {m, wdl}; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, const month& m) NOEXCEPT -{ - return m / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(int m, const weekday_last& wdl) NOEXCEPT -{ - return month(static_cast(m)) / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, int m) NOEXCEPT -{ - return m / wdl; -} - -// year_month_day from operator/() - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, const day& d) NOEXCEPT -{ - return {ym.year(), ym.month(), d}; -} - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, int d) NOEXCEPT -{ - return ym / day(static_cast(d)); -} - -CONSTCD11 -inline -year_month_day -operator/(const year& y, const month_day& md) NOEXCEPT -{ - return y / md.month() / md.day(); -} - -CONSTCD11 -inline -year_month_day -operator/(int y, const month_day& md) NOEXCEPT -{ - return year(y) / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, const year& y) NOEXCEPT -{ - return y / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, int y) NOEXCEPT -{ - return year(y) / md; -} - -// year_month_day_last from operator/() - -CONSTCD11 -inline -year_month_day_last -operator/(const year_month& ym, last_spec) NOEXCEPT -{ - return {ym.year(), month_day_last{ym.month()}}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const year& y, const month_day_last& mdl) NOEXCEPT -{ - return {y, mdl}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(int y, const month_day_last& mdl) NOEXCEPT -{ - return year(y) / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, const year& y) NOEXCEPT -{ - return y / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, int y) NOEXCEPT -{ - return year(y) / mdl; -} - -// year_month_weekday from operator/() - -CONSTCD11 -inline -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT -{ - return {ym.year(), ym.month(), wdi}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT -{ - return {y, mwd.month(), mwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT -{ - return year(y) / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT -{ - return y / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT -{ - return year(y) / mwd; -} - -// year_month_weekday_last from operator/() - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT -{ - return {ym.year(), ym.month(), wdl}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT -{ - return {y, mwdl.month(), mwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT -{ - return year(y) / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT -{ - return y / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT -{ - return year(y) / mwdl; -} - -template -struct fields; - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const fields& fds, const std::string* abbrev = nullptr, - const std::chrono::seconds* offset_sec = nullptr); - -template -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - fields& fds, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr); - -// hh_mm_ss - -namespace detail -{ - -struct undocumented {explicit undocumented() = default;}; - -// width::value is the number of fractional decimal digits in 1/n -// width<0>::value and width<1>::value are defined to be 0 -// If 1/n takes more than 18 fractional decimal digits, -// the result is truncated to 19. -// Example: width<2>::value == 1 -// Example: width<3>::value == 19 -// Example: width<4>::value == 2 -// Example: width<10>::value == 1 -// Example: width<1000>::value == 3 -template -struct width -{ - static_assert(d > 0, "width called with zero denominator"); - static CONSTDATA unsigned value = 1 + width::value; -}; - -template -struct width -{ - static CONSTDATA unsigned value = 0; -}; - -template -struct static_pow10 -{ -private: - static CONSTDATA std::uint64_t h = static_pow10::value; -public: - static CONSTDATA std::uint64_t value = h * h * (exp % 2 ? 10 : 1); -}; - -template <> -struct static_pow10<0> -{ - static CONSTDATA std::uint64_t value = 1; -}; - -template -class decimal_format_seconds -{ - using CT = typename std::common_type::type; - using rep = typename CT::rep; - static unsigned CONSTDATA trial_width = - detail::width::value; -public: - static unsigned CONSTDATA width = trial_width < 19 ? trial_width : 6u; - using precision = std::chrono::duration::value>>; - -private: - std::chrono::seconds s_; - precision sub_s_; - -public: - CONSTCD11 decimal_format_seconds() - : s_() - , sub_s_() - {} - - CONSTCD11 explicit decimal_format_seconds(const Duration& d) NOEXCEPT - : s_(std::chrono::duration_cast(d)) - , sub_s_(std::chrono::duration_cast(d - s_)) - {} - - CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;} - CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} - CONSTCD11 precision subseconds() const NOEXCEPT {return sub_s_;} - - CONSTCD14 precision to_duration() const NOEXCEPT - { - return s_ + sub_s_; - } - - CONSTCD11 bool in_conventional_range() const NOEXCEPT - { - return sub_s_ < std::chrono::seconds{1} && s_ < std::chrono::minutes{1}; - } - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, const decimal_format_seconds& x) - { - return x.print(os, std::chrono::treat_as_floating_point{}); - } - - template - std::basic_ostream& - print(std::basic_ostream& os, std::true_type) const - { - date::detail::save_ostream _(os); - std::chrono::duration d = s_ + sub_s_; - if (d < std::chrono::seconds{10}) - os << '0'; - os.precision(width+6); - os << std::fixed << d.count(); - return os; - } - - template - std::basic_ostream& - print(std::basic_ostream& os, std::false_type) const - { - date::detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << s_.count(); - if (width > 0) - { -#if !ONLY_C_LOCALE - os << std::use_facet>(os.getloc()).decimal_point(); -#else - os << '.'; -#endif - date::detail::save_ostream _s(os); - os.imbue(std::locale::classic()); - os.width(width); - os << sub_s_.count(); - } - return os; - } -}; - -template -inline -CONSTCD11 -typename std::enable_if - < - std::numeric_limits::is_signed, - std::chrono::duration - >::type -abs(std::chrono::duration d) -{ - return d >= d.zero() ? +d : -d; -} - -template -inline -CONSTCD11 -typename std::enable_if - < - !std::numeric_limits::is_signed, - std::chrono::duration - >::type -abs(std::chrono::duration d) -{ - return d; -} - -} // namespace detail - -template -class hh_mm_ss -{ - using dfs = detail::decimal_format_seconds::type>; - - std::chrono::hours h_; - std::chrono::minutes m_; - dfs s_; - bool neg_; - -public: - static unsigned CONSTDATA fractional_width = dfs::width; - using precision = typename dfs::precision; - - CONSTCD11 hh_mm_ss() NOEXCEPT - : hh_mm_ss(Duration::zero()) - {} - - CONSTCD11 explicit hh_mm_ss(Duration d) NOEXCEPT - : h_(std::chrono::duration_cast(detail::abs(d))) - , m_(std::chrono::duration_cast(detail::abs(d)) - h_) - , s_(detail::abs(d) - h_ - m_) - , neg_(d < Duration::zero()) - {} - - CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} - CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} - CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_.seconds();} - CONSTCD14 std::chrono::seconds& - seconds(detail::undocumented) NOEXCEPT {return s_.seconds();} - CONSTCD11 precision subseconds() const NOEXCEPT {return s_.subseconds();} - CONSTCD11 bool is_negative() const NOEXCEPT {return neg_;} - - CONSTCD11 explicit operator precision() const NOEXCEPT {return to_duration();} - CONSTCD11 precision to_duration() const NOEXCEPT - {return (s_.to_duration() + m_ + h_) * (1-2*neg_);} - - CONSTCD11 bool in_conventional_range() const NOEXCEPT - { - return !neg_ && h_ < days{1} && m_ < std::chrono::hours{1} && - s_.in_conventional_range(); - } - -private: - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, hh_mm_ss const& tod) - { - if (tod.is_negative()) - os << '-'; - if (tod.h_ < std::chrono::hours{10}) - os << '0'; - os << tod.h_.count() << ':'; - if (tod.m_ < std::chrono::minutes{10}) - os << '0'; - os << tod.m_.count() << ':' << tod.s_; - return os; - } - - template - friend - std::basic_ostream& - date::to_stream(std::basic_ostream& os, const CharT* fmt, - const fields& fds, const std::string* abbrev, - const std::chrono::seconds* offset_sec); - - template - friend - std::basic_istream& - date::from_stream(std::basic_istream& is, const CharT* fmt, - fields& fds, - std::basic_string* abbrev, std::chrono::minutes* offset); -}; - -inline -CONSTCD14 -bool -is_am(std::chrono::hours const& h) NOEXCEPT -{ - using std::chrono::hours; - return hours{0} <= h && h < hours{12}; -} - -inline -CONSTCD14 -bool -is_pm(std::chrono::hours const& h) NOEXCEPT -{ - using std::chrono::hours; - return hours{12} <= h && h < hours{24}; -} - -inline -CONSTCD14 -std::chrono::hours -make12(std::chrono::hours h) NOEXCEPT -{ - using std::chrono::hours; - if (h < hours{12}) - { - if (h == hours{0}) - h = hours{12}; - } - else - { - if (h != hours{12}) - h = h - hours{12}; - } - return h; -} - -inline -CONSTCD14 -std::chrono::hours -make24(std::chrono::hours h, bool is_pm) NOEXCEPT -{ - using std::chrono::hours; - if (is_pm) - { - if (h != hours{12}) - h = h + hours{12}; - } - else if (h == hours{12}) - h = hours{0}; - return h; -} - -template -using time_of_day = hh_mm_ss; - -template -CONSTCD11 -inline -hh_mm_ss> -make_time(const std::chrono::duration& d) -{ - return hh_mm_ss>(d); -} - -template -inline -typename std::enable_if -< - !std::is_convertible::value, - std::basic_ostream& ->::type -operator<<(std::basic_ostream& os, const sys_time& tp) -{ - auto const dp = date::floor(tp); - return os << year_month_day(dp) << ' ' << make_time(tp-dp); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const sys_days& dp) -{ - return os << year_month_day(dp); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_time& ut) -{ - return (date::operator<<(os, sys_time{ut.time_since_epoch()})); -} - -namespace detail -{ - -template -class string_literal; - -template -inline -CONSTCD14 -string_literal::type, - N1 + N2 - 1> -operator+(const string_literal& x, const string_literal& y) NOEXCEPT; - -template -class string_literal -{ - CharT p_[N]; - - CONSTCD11 string_literal() NOEXCEPT - : p_{} - {} - -public: - using const_iterator = const CharT*; - - string_literal(string_literal const&) = default; - string_literal& operator=(string_literal const&) = delete; - - template ::type> - CONSTCD11 string_literal(CharT c) NOEXCEPT - : p_{c} - { - } - - template ::type> - CONSTCD11 string_literal(CharT c1, CharT c2) NOEXCEPT - : p_{c1, c2} - { - } - - template ::type> - CONSTCD11 string_literal(CharT c1, CharT c2, CharT c3) NOEXCEPT - : p_{c1, c2, c3} - { - } - - CONSTCD14 string_literal(const CharT(&a)[N]) NOEXCEPT - : p_{} - { - for (std::size_t i = 0; i < N; ++i) - p_[i] = a[i]; - } - - template ::type> - CONSTCD14 string_literal(const char(&a)[N]) NOEXCEPT - : p_{} - { - for (std::size_t i = 0; i < N; ++i) - p_[i] = a[i]; - } - - template ::value>::type> - CONSTCD14 string_literal(string_literal const& a) NOEXCEPT - : p_{} - { - for (std::size_t i = 0; i < N; ++i) - p_[i] = a[i]; - } - - CONSTCD11 const CharT* data() const NOEXCEPT {return p_;} - CONSTCD11 std::size_t size() const NOEXCEPT {return N-1;} - - CONSTCD11 const_iterator begin() const NOEXCEPT {return p_;} - CONSTCD11 const_iterator end() const NOEXCEPT {return p_ + N-1;} - - CONSTCD11 CharT const& operator[](std::size_t n) const NOEXCEPT - { - return p_[n]; - } - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, const string_literal& s) - { - return os << s.p_; - } - - template - friend - CONSTCD14 - string_literal::type, - N1 + N2 - 1> - operator+(const string_literal& x, const string_literal& y) NOEXCEPT; -}; - -template -CONSTCD11 -inline -string_literal -operator+(const string_literal& x, const string_literal& y) NOEXCEPT -{ - return string_literal(x[0], y[0]); -} - -template -CONSTCD11 -inline -string_literal -operator+(const string_literal& x, const string_literal& y) NOEXCEPT -{ - return string_literal(x[0], x[1], y[0]); -} - -template -CONSTCD14 -inline -string_literal::type, - N1 + N2 - 1> -operator+(const string_literal& x, const string_literal& y) NOEXCEPT -{ - using CT = typename std::conditional::type; - - string_literal r; - std::size_t i = 0; - for (; i < N1-1; ++i) - r.p_[i] = CT(x.p_[i]); - for (std::size_t j = 0; j < N2; ++j, ++i) - r.p_[i] = CT(y.p_[j]); - - return r; -} - - -template -inline -std::basic_string -operator+(std::basic_string x, const string_literal& y) -{ - x.append(y.data(), y.size()); - return x; -} - -#if __cplusplus >= 201402 && (!defined(__EDG_VERSION__) || __EDG_VERSION__ > 411) \ - && (!defined(__SUNPRO_CC) || __SUNPRO_CC > 0x5150) - -template ::value || - std::is_same::value || - std::is_same::value || - std::is_same::value>> -CONSTCD14 -inline -string_literal -msl(CharT c) NOEXCEPT -{ - return string_literal{c}; -} - -CONSTCD14 -inline -std::size_t -to_string_len(std::intmax_t i) -{ - std::size_t r = 0; - do - { - i /= 10; - ++r; - } while (i > 0); - return r; -} - -template -CONSTCD14 -inline -std::enable_if_t -< - N < 10, - string_literal -> -msl() NOEXCEPT -{ - return msl(char(N % 10 + '0')); -} - -template -CONSTCD14 -inline -std::enable_if_t -< - 10 <= N, - string_literal -> -msl() NOEXCEPT -{ - return msl() + msl(char(N % 10 + '0')); -} - -template -CONSTCD14 -inline -std::enable_if_t -< - std::ratio::type::den != 1, - string_literal::type::num) + - to_string_len(std::ratio::type::den) + 4> -> -msl(std::ratio) NOEXCEPT -{ - using R = typename std::ratio::type; - return msl(CharT{'['}) + msl() + msl(CharT{'/'}) + - msl() + msl(CharT{']'}); -} - -template -CONSTCD14 -inline -std::enable_if_t -< - std::ratio::type::den == 1, - string_literal::type::num) + 3> -> -msl(std::ratio) NOEXCEPT -{ - using R = typename std::ratio::type; - return msl(CharT{'['}) + msl() + msl(CharT{']'}); -} - - -#else // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411) - -inline -std::string -to_string(std::uint64_t x) -{ - return std::to_string(x); -} - -template -inline -std::basic_string -to_string(std::uint64_t x) -{ - auto y = std::to_string(x); - return std::basic_string(y.begin(), y.end()); -} - -template -inline -typename std::enable_if -< - std::ratio::type::den != 1, - std::basic_string ->::type -msl(std::ratio) -{ - using R = typename std::ratio::type; - return std::basic_string(1, '[') + to_string(R::num) + CharT{'/'} + - to_string(R::den) + CharT{']'}; -} - -template -inline -typename std::enable_if -< - std::ratio::type::den == 1, - std::basic_string ->::type -msl(std::ratio) -{ - using R = typename std::ratio::type; - return std::basic_string(1, '[') + to_string(R::num) + CharT{']'}; -} - -#endif // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411) - -template -CONSTCD11 -inline -string_literal -msl(std::atto) NOEXCEPT -{ - return string_literal{'a'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::femto) NOEXCEPT -{ - return string_literal{'f'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::pico) NOEXCEPT -{ - return string_literal{'p'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::nano) NOEXCEPT -{ - return string_literal{'n'}; -} - -template -CONSTCD11 -inline -typename std::enable_if -< - std::is_same::value, - string_literal ->::type -msl(std::micro) NOEXCEPT -{ - return string_literal{'\xC2', '\xB5'}; -} - -template -CONSTCD11 -inline -typename std::enable_if -< - !std::is_same::value, - string_literal ->::type -msl(std::micro) NOEXCEPT -{ - return string_literal{CharT{static_cast('\xB5')}}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::milli) NOEXCEPT -{ - return string_literal{'m'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::centi) NOEXCEPT -{ - return string_literal{'c'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::deca) NOEXCEPT -{ - return string_literal{'d', 'a'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::deci) NOEXCEPT -{ - return string_literal{'d'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::hecto) NOEXCEPT -{ - return string_literal{'h'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::kilo) NOEXCEPT -{ - return string_literal{'k'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::mega) NOEXCEPT -{ - return string_literal{'M'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::giga) NOEXCEPT -{ - return string_literal{'G'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::tera) NOEXCEPT -{ - return string_literal{'T'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::peta) NOEXCEPT -{ - return string_literal{'P'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::exa) NOEXCEPT -{ - return string_literal{'E'}; -} - -template -CONSTCD11 -inline -auto -get_units(Period p) - -> decltype(msl(p) + string_literal{'s'}) -{ - return msl(p) + string_literal{'s'}; -} - -template -CONSTCD11 -inline -string_literal -get_units(std::ratio<1>) -{ - return string_literal{'s'}; -} - -template -CONSTCD11 -inline -string_literal -get_units(std::ratio<3600>) -{ - return string_literal{'h'}; -} - -template -CONSTCD11 -inline -string_literal -get_units(std::ratio<60>) -{ - return string_literal{'m', 'i', 'n'}; -} - -template -CONSTCD11 -inline -string_literal -get_units(std::ratio<86400>) -{ - return string_literal{'d'}; -} - -template > -struct make_string; - -template <> -struct make_string -{ - template - static - std::string - from(Rep n) - { - return std::to_string(n); - } -}; - -template -struct make_string -{ - template - static - std::basic_string - from(Rep n) - { - auto s = std::to_string(n); - return std::basic_string(s.begin(), s.end()); - } -}; - -template <> -struct make_string -{ - template - static - std::wstring - from(Rep n) - { - return std::to_wstring(n); - } -}; - -template -struct make_string -{ - template - static - std::basic_string - from(Rep n) - { - auto s = std::to_wstring(n); - return std::basic_string(s.begin(), s.end()); - } -}; - -} // namespace detail - -// to_stream - -CONSTDATA year nanyear{-32768}; - -template -struct fields -{ - year_month_day ymd{nanyear/0/0}; - weekday wd{8u}; - hh_mm_ss tod{}; - bool has_tod = false; - -#if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ <= 409) - fields() : ymd{nanyear/0/0}, wd{8u}, tod{}, has_tod{false} {} -#else - fields() = default; -#endif - - fields(year_month_day ymd_) : ymd(ymd_) {} - fields(weekday wd_) : wd(wd_) {} - fields(hh_mm_ss tod_) : tod(tod_), has_tod(true) {} - - fields(year_month_day ymd_, weekday wd_) : ymd(ymd_), wd(wd_) {} - fields(year_month_day ymd_, hh_mm_ss tod_) : ymd(ymd_), tod(tod_), - has_tod(true) {} - - fields(weekday wd_, hh_mm_ss tod_) : wd(wd_), tod(tod_), has_tod(true) {} - - fields(year_month_day ymd_, weekday wd_, hh_mm_ss tod_) - : ymd(ymd_) - , wd(wd_) - , tod(tod_) - , has_tod(true) - {} -}; - -namespace detail -{ - -template -unsigned -extract_weekday(std::basic_ostream& os, const fields& fds) -{ - if (!fds.ymd.ok() && !fds.wd.ok()) - { - // fds does not contain a valid weekday - os.setstate(std::ios::failbit); - return 8; - } - weekday wd; - if (fds.ymd.ok()) - { - wd = weekday{sys_days(fds.ymd)}; - if (fds.wd.ok() && wd != fds.wd) - { - // fds.ymd and fds.wd are inconsistent - os.setstate(std::ios::failbit); - return 8; - } - } - else - wd = fds.wd; - return static_cast((wd - Sunday).count()); -} - -template -unsigned -extract_month(std::basic_ostream& os, const fields& fds) -{ - if (!fds.ymd.month().ok()) - { - // fds does not contain a valid month - os.setstate(std::ios::failbit); - return 0; - } - return static_cast(fds.ymd.month()); -} - -} // namespace detail - -#if ONLY_C_LOCALE - -namespace detail -{ - -inline -std::pair -weekday_names() -{ - static const std::string nm[] = - { - "Sunday", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - "Sun", - "Mon", - "Tue", - "Wed", - "Thu", - "Fri", - "Sat" - }; - return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); -} - -inline -std::pair -month_names() -{ - static const std::string nm[] = - { - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec" - }; - return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); -} - -inline -std::pair -ampm_names() -{ - static const std::string nm[] = - { - "AM", - "PM" - }; - return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); -} - -template -FwdIter -scan_keyword(std::basic_istream& is, FwdIter kb, FwdIter ke) -{ - size_t nkw = static_cast(std::distance(kb, ke)); - const unsigned char doesnt_match = '\0'; - const unsigned char might_match = '\1'; - const unsigned char does_match = '\2'; - unsigned char statbuf[100]; - unsigned char* status = statbuf; - std::unique_ptr stat_hold(0, free); - if (nkw > sizeof(statbuf)) - { - status = (unsigned char*)std::malloc(nkw); - if (status == nullptr) - throw std::bad_alloc(); - stat_hold.reset(status); - } - size_t n_might_match = nkw; // At this point, any keyword might match - size_t n_does_match = 0; // but none of them definitely do - // Initialize all statuses to might_match, except for "" keywords are does_match - unsigned char* st = status; - for (auto ky = kb; ky != ke; ++ky, ++st) - { - if (!ky->empty()) - *st = might_match; - else - { - *st = does_match; - --n_might_match; - ++n_does_match; - } - } - // While there might be a match, test keywords against the next CharT - for (size_t indx = 0; is && n_might_match > 0; ++indx) - { - // Peek at the next CharT but don't consume it - auto ic = is.peek(); - if (ic == EOF) - { - is.setstate(std::ios::eofbit); - break; - } - auto c = static_cast(toupper(static_cast(ic))); - bool consume = false; - // For each keyword which might match, see if the indx character is c - // If a match if found, consume c - // If a match is found, and that is the last character in the keyword, - // then that keyword matches. - // If the keyword doesn't match this character, then change the keyword - // to doesn't match - st = status; - for (auto ky = kb; ky != ke; ++ky, ++st) - { - if (*st == might_match) - { - if (c == static_cast(toupper(static_cast((*ky)[indx])))) - { - consume = true; - if (ky->size() == indx+1) - { - *st = does_match; - --n_might_match; - ++n_does_match; - } - } - else - { - *st = doesnt_match; - --n_might_match; - } - } - } - // consume if we matched a character - if (consume) - { - (void)is.get(); - // If we consumed a character and there might be a matched keyword that - // was marked matched on a previous iteration, then such keywords - // are now marked as not matching. - if (n_might_match + n_does_match > 1) - { - st = status; - for (auto ky = kb; ky != ke; ++ky, ++st) - { - if (*st == does_match && ky->size() != indx+1) - { - *st = doesnt_match; - --n_does_match; - } - } - } - } - } - // We've exited the loop because we hit eof and/or we have no more "might matches". - // Return the first matching result - for (st = status; kb != ke; ++kb, ++st) - if (*st == does_match) - break; - if (kb == ke) - is.setstate(std::ios::failbit); - return kb; -} - -} // namespace detail - -#endif // ONLY_C_LOCALE - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const fields& fds, const std::string* abbrev, - const std::chrono::seconds* offset_sec) -{ -#if ONLY_C_LOCALE - using detail::weekday_names; - using detail::month_names; - using detail::ampm_names; -#endif - using detail::save_ostream; - using detail::get_units; - using detail::extract_weekday; - using detail::extract_month; - using std::ios; - using std::chrono::duration_cast; - using std::chrono::seconds; - using std::chrono::minutes; - using std::chrono::hours; - date::detail::save_ostream ss(os); - os.fill(' '); - os.flags(std::ios::skipws | std::ios::dec); - os.width(0); - tm tm{}; - bool insert_negative = fds.has_tod && fds.tod.to_duration() < Duration::zero(); -#if !ONLY_C_LOCALE - auto& facet = std::use_facet>(os.getloc()); -#endif - const CharT* command = nullptr; - CharT modified = CharT{}; - for (; *fmt; ++fmt) - { - switch (*fmt) - { - case 'a': - case 'A': - if (command) - { - if (modified == CharT{}) - { - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; -#if !ONLY_C_LOCALE - const CharT f[] = {'%', *fmt}; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); -#else // ONLY_C_LOCALE - os << weekday_names().first[tm.tm_wday+7*(*fmt == 'a')]; -#endif // ONLY_C_LOCALE - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'b': - case 'B': - case 'h': - if (command) - { - if (modified == CharT{}) - { - tm.tm_mon = static_cast(extract_month(os, fds)) - 1; -#if !ONLY_C_LOCALE - const CharT f[] = {'%', *fmt}; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); -#else // ONLY_C_LOCALE - os << month_names().first[tm.tm_mon+12*(*fmt != 'B')]; -#endif // ONLY_C_LOCALE - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'c': - case 'x': - if (command) - { - if (modified == CharT{'O'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - if (*fmt == 'c' && !fds.has_tod) - os.setstate(std::ios::failbit); -#if !ONLY_C_LOCALE - tm = std::tm{}; - auto const& ymd = fds.ymd; - auto ld = local_days(ymd); - if (*fmt == 'c') - { - tm.tm_sec = static_cast(fds.tod.seconds().count()); - tm.tm_min = static_cast(fds.tod.minutes().count()); - tm.tm_hour = static_cast(fds.tod.hours().count()); - } - tm.tm_mday = static_cast(static_cast(ymd.day())); - tm.tm_mon = static_cast(extract_month(os, fds) - 1); - tm.tm_year = static_cast(ymd.year()) - 1900; - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; - tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); - CharT f[3] = {'%'}; - auto fe = std::begin(f) + 1; - if (modified == CharT{'E'}) - *fe++ = modified; - *fe++ = *fmt; - facet.put(os, os, os.fill(), &tm, std::begin(f), fe); -#else // ONLY_C_LOCALE - if (*fmt == 'c') - { - auto wd = static_cast(extract_weekday(os, fds)); - os << weekday_names().first[static_cast(wd)+7] - << ' '; - os << month_names().first[extract_month(os, fds)-1+12] << ' '; - auto d = static_cast(static_cast(fds.ymd.day())); - if (d < 10) - os << ' '; - os << d << ' ' - << make_time(duration_cast(fds.tod.to_duration())) - << ' ' << fds.ymd.year(); - - } - else // *fmt == 'x' - { - auto const& ymd = fds.ymd; - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(ymd.month()) << CharT{'/'}; - os.width(2); - os << static_cast(ymd.day()) << CharT{'/'}; - os.width(2); - os << static_cast(ymd.year()) % 100; - } -#endif // ONLY_C_LOCALE - } - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'C': - if (command) - { - if (modified == CharT{'O'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.year().ok()) - os.setstate(std::ios::failbit); - auto y = static_cast(fds.ymd.year()); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - if (y >= 0) - { - os.width(2); - os << y/100; - } - else - { - os << CharT{'-'}; - os.width(2); - os << -(y-99)/100; - } - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'E'}) - { - tm.tm_year = y - 1900; - CharT f[3] = {'%', 'E', 'C'}; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'd': - case 'e': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.day().ok()) - os.setstate(std::ios::failbit); - auto d = static_cast(static_cast(fds.ymd.day())); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - save_ostream _(os); - if (*fmt == CharT{'d'}) - os.fill('0'); - else - os.fill(' '); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << d; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - tm.tm_mday = d; - CharT f[3] = {'%', 'O', *fmt}; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'D': - if (command) - { - if (modified == CharT{}) - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - auto const& ymd = fds.ymd; - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(ymd.month()) << CharT{'/'}; - os.width(2); - os << static_cast(ymd.day()) << CharT{'/'}; - os.width(2); - os << static_cast(ymd.year()) % 100; - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'F': - if (command) - { - if (modified == CharT{}) - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - auto const& ymd = fds.ymd; - save_ostream _(os); - os.imbue(std::locale::classic()); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(4); - os << static_cast(ymd.year()) << CharT{'-'}; - os.width(2); - os << static_cast(ymd.month()) << CharT{'-'}; - os.width(2); - os << static_cast(ymd.day()); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'g': - case 'G': - if (command) - { - if (modified == CharT{}) - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - auto ld = local_days(fds.ymd); - auto y = year_month_day{ld + days{3}}.year(); - auto start = local_days((y-years{1})/December/Thursday[last]) + - (Monday-Thursday); - if (ld < start) - --y; - if (*fmt == CharT{'G'}) - os << y; - else - { - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << std::abs(static_cast(y)) % 100; - } - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'H': - case 'I': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - if (insert_negative) - { - os << '-'; - insert_negative = false; - } - auto hms = fds.tod; -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - auto h = *fmt == CharT{'I'} ? date::make12(hms.hours()) : hms.hours(); - if (h < hours{10}) - os << CharT{'0'}; - os << h.count(); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_hour = static_cast(hms.hours().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'j': - if (command) - { - if (modified == CharT{}) - { - if (fds.ymd.ok() || fds.has_tod) - { - days doy; - if (fds.ymd.ok()) - { - auto ld = local_days(fds.ymd); - auto y = fds.ymd.year(); - doy = ld - local_days(y/January/1) + days{1}; - } - else - { - doy = duration_cast(fds.tod.to_duration()); - } - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(3); - os << doy.count(); - } - else - { - os.setstate(std::ios::failbit); - } - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'm': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.month().ok()) - os.setstate(std::ios::failbit); - auto m = static_cast(fds.ymd.month()); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - if (m < 10) - os << CharT{'0'}; - os << m; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_mon = static_cast(m-1); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'M': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - if (insert_negative) - { - os << '-'; - insert_negative = false; - } -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - if (fds.tod.minutes() < minutes{10}) - os << CharT{'0'}; - os << fds.tod.minutes().count(); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_min = static_cast(fds.tod.minutes().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'n': - if (command) - { - if (modified == CharT{}) - os << CharT{'\n'}; - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'p': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); -#if !ONLY_C_LOCALE - const CharT f[] = {'%', *fmt}; - tm.tm_hour = static_cast(fds.tod.hours().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); -#else - if (date::is_am(fds.tod.hours())) - os << ampm_names().first[0]; - else - os << ampm_names().first[1]; -#endif - } - else - { - os << CharT{'%'} << modified << *fmt; - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'Q': - case 'q': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - auto d = fds.tod.to_duration(); - if (*fmt == 'q') - os << get_units(typename decltype(d)::period::type{}); - else - os << d.count(); - } - else - { - os << CharT{'%'} << modified << *fmt; - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'r': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); -#if !ONLY_C_LOCALE - const CharT f[] = {'%', *fmt}; - tm.tm_hour = static_cast(fds.tod.hours().count()); - tm.tm_min = static_cast(fds.tod.minutes().count()); - tm.tm_sec = static_cast(fds.tod.seconds().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); -#else - hh_mm_ss tod(duration_cast(fds.tod.to_duration())); - save_ostream _(os); - os.fill('0'); - os.width(2); - os << date::make12(tod.hours()).count() << CharT{':'}; - os.width(2); - os << tod.minutes().count() << CharT{':'}; - os.width(2); - os << tod.seconds().count() << CharT{' '}; - if (date::is_am(tod.hours())) - os << ampm_names().first[0]; - else - os << ampm_names().first[1]; -#endif - } - else - { - os << CharT{'%'} << modified << *fmt; - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'R': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - if (fds.tod.hours() < hours{10}) - os << CharT{'0'}; - os << fds.tod.hours().count() << CharT{':'}; - if (fds.tod.minutes() < minutes{10}) - os << CharT{'0'}; - os << fds.tod.minutes().count(); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'S': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - if (insert_negative) - { - os << '-'; - insert_negative = false; - } -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - os << fds.tod.s_; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_sec = static_cast(fds.tod.s_.seconds().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 't': - if (command) - { - if (modified == CharT{}) - os << CharT{'\t'}; - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'T': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - os << fds.tod; - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'u': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - auto wd = extract_weekday(os, fds); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - os << (wd != 0 ? wd : 7u); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_wday = static_cast(wd); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'U': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - auto const& ymd = fds.ymd; - if (!ymd.ok()) - os.setstate(std::ios::failbit); - auto ld = local_days(ymd); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - auto st = local_days(Sunday[1]/January/ymd.year()); - if (ld < st) - os << CharT{'0'} << CharT{'0'}; - else - { - auto wn = duration_cast(ld - st).count() + 1; - if (wn < 10) - os << CharT{'0'}; - os << wn; - } - } - #if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_year = static_cast(ymd.year()) - 1900; - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; - tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'V': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - auto ld = local_days(fds.ymd); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - auto y = year_month_day{ld + days{3}}.year(); - auto st = local_days((y-years{1})/12/Thursday[last]) + - (Monday-Thursday); - if (ld < st) - { - --y; - st = local_days((y - years{1})/12/Thursday[last]) + - (Monday-Thursday); - } - auto wn = duration_cast(ld - st).count() + 1; - if (wn < 10) - os << CharT{'0'}; - os << wn; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - auto const& ymd = fds.ymd; - tm.tm_year = static_cast(ymd.year()) - 1900; - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; - tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'w': - if (command) - { - auto wd = extract_weekday(os, fds); - if (os.fail()) - return os; -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - os << wd; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_wday = static_cast(wd); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - else - { - os << CharT{'%'} << modified << *fmt; - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'W': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - auto const& ymd = fds.ymd; - if (!ymd.ok()) - os.setstate(std::ios::failbit); - auto ld = local_days(ymd); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - auto st = local_days(Monday[1]/January/ymd.year()); - if (ld < st) - os << CharT{'0'} << CharT{'0'}; - else - { - auto wn = duration_cast(ld - st).count() + 1; - if (wn < 10) - os << CharT{'0'}; - os << wn; - } - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_year = static_cast(ymd.year()) - 1900; - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; - tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'X': - if (command) - { - if (modified == CharT{'O'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); -#if !ONLY_C_LOCALE - tm = std::tm{}; - tm.tm_sec = static_cast(fds.tod.seconds().count()); - tm.tm_min = static_cast(fds.tod.minutes().count()); - tm.tm_hour = static_cast(fds.tod.hours().count()); - CharT f[3] = {'%'}; - auto fe = std::begin(f) + 1; - if (modified == CharT{'E'}) - *fe++ = modified; - *fe++ = *fmt; - facet.put(os, os, os.fill(), &tm, std::begin(f), fe); -#else - os << fds.tod; -#endif - } - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'y': - if (command) - { - if (!fds.ymd.year().ok()) - os.setstate(std::ios::failbit); - auto y = static_cast(fds.ymd.year()); -#if !ONLY_C_LOCALE - if (modified == CharT{}) - { -#endif - y = std::abs(y) % 100; - if (y < 10) - os << CharT{'0'}; - os << y; -#if !ONLY_C_LOCALE - } - else - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_year = y - 1900; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'Y': - if (command) - { - if (modified == CharT{'O'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.year().ok()) - os.setstate(std::ios::failbit); - auto y = fds.ymd.year(); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - save_ostream _(os); - os.imbue(std::locale::classic()); - os << y; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'E'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_year = static_cast(y) - 1900; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'z': - if (command) - { - if (offset_sec == nullptr) - { - // Can not format %z with unknown offset - os.setstate(ios::failbit); - return os; - } - auto m = duration_cast(*offset_sec); - auto neg = m < minutes{0}; - m = date::abs(m); - auto h = duration_cast(m); - m -= h; - if (neg) - os << CharT{'-'}; - else - os << CharT{'+'}; - if (h < hours{10}) - os << CharT{'0'}; - os << h.count(); - if (modified != CharT{}) - os << CharT{':'}; - if (m < minutes{10}) - os << CharT{'0'}; - os << m.count(); - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'Z': - if (command) - { - if (modified == CharT{}) - { - if (abbrev == nullptr) - { - // Can not format %Z with unknown time_zone - os.setstate(ios::failbit); - return os; - } - for (auto c : *abbrev) - os << CharT(c); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'E': - case 'O': - if (command) - { - if (modified == CharT{}) - { - modified = *fmt; - } - else - { - os << CharT{'%'} << modified << *fmt; - command = nullptr; - modified = CharT{}; - } - } - else - os << *fmt; - break; - case '%': - if (command) - { - if (modified == CharT{}) - { - os << CharT{'%'}; - command = nullptr; - } - else - { - os << CharT{'%'} << modified << CharT{'%'}; - command = nullptr; - modified = CharT{}; - } - } - else - command = fmt; - break; - default: - if (command) - { - os << CharT{'%'}; - command = nullptr; - } - if (modified != CharT{}) - { - os << modified; - modified = CharT{}; - } - os << *fmt; - break; - } - } - if (command) - os << CharT{'%'}; - if (modified != CharT{}) - os << modified; - return os; -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const year& y) -{ - using CT = std::chrono::seconds; - fields fds{y/0/0}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const month& m) -{ - using CT = std::chrono::seconds; - fields fds{m/0/nanyear}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const day& d) -{ - using CT = std::chrono::seconds; - fields fds{d/0/nanyear}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const weekday& wd) -{ - using CT = std::chrono::seconds; - fields fds{wd}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const year_month& ym) -{ - using CT = std::chrono::seconds; - fields fds{ym/0}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const month_day& md) -{ - using CT = std::chrono::seconds; - fields fds{md/nanyear}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const year_month_day& ymd) -{ - using CT = std::chrono::seconds; - fields fds{ymd}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const std::chrono::duration& d) -{ - using Duration = std::chrono::duration; - using CT = typename std::common_type::type; - fields fds{hh_mm_ss{d}}; - return to_stream(os, fmt, fds); -} - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const local_time& tp, const std::string* abbrev = nullptr, - const std::chrono::seconds* offset_sec = nullptr) -{ - using CT = typename std::common_type::type; - auto ld = std::chrono::time_point_cast(tp); - fields fds; - if (ld <= tp) - fds = fields{year_month_day{ld}, hh_mm_ss{tp-local_seconds{ld}}}; - else - fds = fields{year_month_day{ld - days{1}}, - hh_mm_ss{days{1} - (local_seconds{ld} - tp)}}; - return to_stream(os, fmt, fds, abbrev, offset_sec); -} - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const sys_time& tp) -{ - using std::chrono::seconds; - using CT = typename std::common_type::type; - const std::string abbrev("UTC"); - CONSTDATA seconds offset{0}; - auto sd = std::chrono::time_point_cast(tp); - fields fds; - if (sd <= tp) - fds = fields{year_month_day{sd}, hh_mm_ss{tp-sys_seconds{sd}}}; - else - fds = fields{year_month_day{sd - days{1}}, - hh_mm_ss{days{1} - (sys_seconds{sd} - tp)}}; - return to_stream(os, fmt, fds, &abbrev, &offset); -} - -// format - -template -auto -format(const std::locale& loc, const CharT* fmt, const Streamable& tp) - -> decltype(to_stream(std::declval&>(), fmt, tp), - std::basic_string{}) -{ - std::basic_ostringstream os; - os.exceptions(std::ios::failbit | std::ios::badbit); - os.imbue(loc); - to_stream(os, fmt, tp); - return os.str(); -} - -template -auto -format(const CharT* fmt, const Streamable& tp) - -> decltype(to_stream(std::declval&>(), fmt, tp), - std::basic_string{}) -{ - std::basic_ostringstream os; - os.exceptions(std::ios::failbit | std::ios::badbit); - to_stream(os, fmt, tp); - return os.str(); -} - -template -auto -format(const std::locale& loc, const std::basic_string& fmt, - const Streamable& tp) - -> decltype(to_stream(std::declval&>(), fmt.c_str(), tp), - std::basic_string{}) -{ - std::basic_ostringstream os; - os.exceptions(std::ios::failbit | std::ios::badbit); - os.imbue(loc); - to_stream(os, fmt.c_str(), tp); - return os.str(); -} - -template -auto -format(const std::basic_string& fmt, const Streamable& tp) - -> decltype(to_stream(std::declval&>(), fmt.c_str(), tp), - std::basic_string{}) -{ - std::basic_ostringstream os; - os.exceptions(std::ios::failbit | std::ios::badbit); - to_stream(os, fmt.c_str(), tp); - return os.str(); -} - -// parse - -namespace detail -{ - -template -bool -read_char(std::basic_istream& is, CharT fmt, std::ios::iostate& err) -{ - auto ic = is.get(); - if (Traits::eq_int_type(ic, Traits::eof()) || - !Traits::eq(Traits::to_char_type(ic), fmt)) - { - err |= std::ios::failbit; - is.setstate(std::ios::failbit); - return false; - } - return true; -} - -template -unsigned -read_unsigned(std::basic_istream& is, unsigned m = 1, unsigned M = 10) -{ - unsigned x = 0; - unsigned count = 0; - while (true) - { - auto ic = is.peek(); - if (Traits::eq_int_type(ic, Traits::eof())) - break; - auto c = static_cast(Traits::to_char_type(ic)); - if (!('0' <= c && c <= '9')) - break; - (void)is.get(); - ++count; - x = 10*x + static_cast(c - '0'); - if (count == M) - break; - } - if (count < m) - is.setstate(std::ios::failbit); - return x; -} - -template -int -read_signed(std::basic_istream& is, unsigned m = 1, unsigned M = 10) -{ - auto ic = is.peek(); - if (!Traits::eq_int_type(ic, Traits::eof())) - { - auto c = static_cast(Traits::to_char_type(ic)); - if (('0' <= c && c <= '9') || c == '-' || c == '+') - { - if (c == '-' || c == '+') - { - (void)is.get(); - --M; - } - auto x = static_cast(read_unsigned(is, std::max(m, 1u), M)); - if (!is.fail()) - { - if (c == '-') - x = -x; - return x; - } - } - } - if (m > 0) - is.setstate(std::ios::failbit); - return 0; -} - -template -long double -read_long_double(std::basic_istream& is, unsigned m = 1, unsigned M = 10) -{ - unsigned count = 0; - unsigned fcount = 0; - unsigned long long i = 0; - unsigned long long f = 0; - bool parsing_fraction = false; -#if ONLY_C_LOCALE - typename Traits::int_type decimal_point = '.'; -#else - auto decimal_point = Traits::to_int_type( - std::use_facet>(is.getloc()).decimal_point()); -#endif - while (true) - { - auto ic = is.peek(); - if (Traits::eq_int_type(ic, Traits::eof())) - break; - if (Traits::eq_int_type(ic, decimal_point)) - { - decimal_point = Traits::eof(); - parsing_fraction = true; - } - else - { - auto c = static_cast(Traits::to_char_type(ic)); - if (!('0' <= c && c <= '9')) - break; - if (!parsing_fraction) - { - i = 10*i + static_cast(c - '0'); - } - else - { - f = 10*f + static_cast(c - '0'); - ++fcount; - } - } - (void)is.get(); - if (++count == M) - break; - } - if (count < m) - { - is.setstate(std::ios::failbit); - return 0; - } - return static_cast(i) + static_cast(f)/std::pow(10.L, fcount); -} - -struct rs -{ - int& i; - unsigned m; - unsigned M; -}; - -struct ru -{ - int& i; - unsigned m; - unsigned M; -}; - -struct rld -{ - long double& i; - unsigned m; - unsigned M; -}; - -template -void -read(std::basic_istream&) -{ -} - -template -void -read(std::basic_istream& is, CharT a0, Args&& ...args); - -template -void -read(std::basic_istream& is, rs a0, Args&& ...args); - -template -void -read(std::basic_istream& is, ru a0, Args&& ...args); - -template -void -read(std::basic_istream& is, int a0, Args&& ...args); - -template -void -read(std::basic_istream& is, rld a0, Args&& ...args); - -template -void -read(std::basic_istream& is, CharT a0, Args&& ...args) -{ - // No-op if a0 == CharT{} - if (a0 != CharT{}) - { - auto ic = is.peek(); - if (Traits::eq_int_type(ic, Traits::eof())) - { - is.setstate(std::ios::failbit | std::ios::eofbit); - return; - } - if (!Traits::eq(Traits::to_char_type(ic), a0)) - { - is.setstate(std::ios::failbit); - return; - } - (void)is.get(); - } - read(is, std::forward(args)...); -} - -template -void -read(std::basic_istream& is, rs a0, Args&& ...args) -{ - auto x = read_signed(is, a0.m, a0.M); - if (is.fail()) - return; - a0.i = x; - read(is, std::forward(args)...); -} - -template -void -read(std::basic_istream& is, ru a0, Args&& ...args) -{ - auto x = read_unsigned(is, a0.m, a0.M); - if (is.fail()) - return; - a0.i = static_cast(x); - read(is, std::forward(args)...); -} - -template -void -read(std::basic_istream& is, int a0, Args&& ...args) -{ - if (a0 != -1) - { - auto u = static_cast(a0); - CharT buf[std::numeric_limits::digits10+2u] = {}; - auto e = buf; - do - { - *e++ = static_cast(CharT(u % 10) + CharT{'0'}); - u /= 10; - } while (u > 0); -#if defined(__GNUC__) && __GNUC__ >= 11 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstringop-overflow" -#endif - std::reverse(buf, e); -#if defined(__GNUC__) && __GNUC__ >= 11 -#pragma GCC diagnostic pop -#endif - for (auto p = buf; p != e && is.rdstate() == std::ios::goodbit; ++p) - read(is, *p); - } - if (is.rdstate() == std::ios::goodbit) - read(is, std::forward(args)...); -} - -template -void -read(std::basic_istream& is, rld a0, Args&& ...args) -{ - auto x = read_long_double(is, a0.m, a0.M); - if (is.fail()) - return; - a0.i = x; - read(is, std::forward(args)...); -} - -template -inline -void -checked_set(T& value, T from, T not_a_value, std::basic_ios& is) -{ - if (!is.fail()) - { - if (value == not_a_value) - value = std::move(from); - else if (value != from) - is.setstate(std::ios::failbit); - } -} - -} // namespace detail; - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - fields& fds, std::basic_string* abbrev, - std::chrono::minutes* offset) -{ - using std::numeric_limits; - using std::ios; - using std::chrono::duration; - using std::chrono::duration_cast; - using std::chrono::seconds; - using std::chrono::minutes; - using std::chrono::hours; - using detail::round_i; - typename std::basic_istream::sentry ok{is, true}; - if (ok) - { - date::detail::save_istream ss(is); - is.fill(' '); - is.flags(std::ios::skipws | std::ios::dec); - is.width(0); -#if !ONLY_C_LOCALE - auto& f = std::use_facet>(is.getloc()); - std::tm tm{}; -#endif - const CharT* command = nullptr; - auto modified = CharT{}; - auto width = -1; - - CONSTDATA int not_a_year = numeric_limits::min(); - CONSTDATA int not_a_2digit_year = 100; - CONSTDATA int not_a_century = numeric_limits::min(); - CONSTDATA int not_a_month = 0; - CONSTDATA int not_a_day = 0; - CONSTDATA int not_a_hour = numeric_limits::min(); - CONSTDATA int not_a_hour_12_value = 0; - CONSTDATA int not_a_minute = not_a_hour; - CONSTDATA Duration not_a_second = Duration::min(); - CONSTDATA int not_a_doy = -1; - CONSTDATA int not_a_weekday = 8; - CONSTDATA int not_a_week_num = 100; - CONSTDATA int not_a_ampm = -1; - CONSTDATA minutes not_a_offset = minutes::min(); - - int Y = not_a_year; // c, F, Y * - int y = not_a_2digit_year; // D, x, y * - int g = not_a_2digit_year; // g * - int G = not_a_year; // G * - int C = not_a_century; // C * - int m = not_a_month; // b, B, h, m, c, D, F, x * - int d = not_a_day; // c, d, D, e, F, x * - int j = not_a_doy; // j * - int wd = not_a_weekday; // a, A, u, w * - int H = not_a_hour; // c, H, R, T, X * - int I = not_a_hour_12_value; // I, r * - int p = not_a_ampm; // p, r * - int M = not_a_minute; // c, M, r, R, T, X * - Duration s = not_a_second; // c, r, S, T, X * - int U = not_a_week_num; // U * - int V = not_a_week_num; // V * - int W = not_a_week_num; // W * - std::basic_string temp_abbrev; // Z * - minutes temp_offset = not_a_offset; // z * - - using detail::read; - using detail::rs; - using detail::ru; - using detail::rld; - using detail::checked_set; - for (; *fmt != CharT{} && !is.fail(); ++fmt) - { - switch (*fmt) - { - case 'a': - case 'A': - case 'u': - case 'w': // wd: a, A, u, w - if (command) - { - int trial_wd = not_a_weekday; - if (*fmt == 'a' || *fmt == 'A') - { - if (modified == CharT{}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - is.setstate(err); - if (!is.fail()) - trial_wd = tm.tm_wday; -#else - auto nm = detail::weekday_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - if (!is.fail()) - trial_wd = i % 7; -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - } - else // *fmt == 'u' || *fmt == 'w' - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - read(is, ru{trial_wd, 1, width == -1 ? - 1u : static_cast(width)}); - if (!is.fail()) - { - if (*fmt == 'u') - { - if (!(1 <= trial_wd && trial_wd <= 7)) - { - trial_wd = not_a_weekday; - is.setstate(ios::failbit); - } - else if (trial_wd == 7) - trial_wd = 0; - } - else // *fmt == 'w' - { - if (!(0 <= trial_wd && trial_wd <= 6)) - { - trial_wd = not_a_weekday; - is.setstate(ios::failbit); - } - } - } - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - is.setstate(err); - if (!is.fail()) - trial_wd = tm.tm_wday; - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - } - if (trial_wd != not_a_weekday) - checked_set(wd, trial_wd, not_a_weekday, is); - } - else // !command - read(is, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - break; - case 'b': - case 'B': - case 'h': - if (command) - { - if (modified == CharT{}) - { - int ttm = not_a_month; -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - ttm = tm.tm_mon + 1; - is.setstate(err); -#else - auto nm = detail::month_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - if (!is.fail()) - ttm = i % 12 + 1; -#endif - checked_set(m, ttm, not_a_month, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'c': - if (command) - { - if (modified != CharT{'O'}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - checked_set(Y, tm.tm_year + 1900, not_a_year, is); - checked_set(m, tm.tm_mon + 1, not_a_month, is); - checked_set(d, tm.tm_mday, not_a_day, is); - checked_set(H, tm.tm_hour, not_a_hour, is); - checked_set(M, tm.tm_min, not_a_minute, is); - checked_set(s, duration_cast(seconds{tm.tm_sec}), - not_a_second, is); - } - is.setstate(err); -#else - // "%a %b %e %T %Y" - auto nm = detail::weekday_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - checked_set(wd, static_cast(i % 7), not_a_weekday, is); - ws(is); - nm = detail::month_names(); - i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - checked_set(m, static_cast(i % 12 + 1), not_a_month, is); - ws(is); - int td = not_a_day; - read(is, rs{td, 1, 2}); - checked_set(d, td, not_a_day, is); - ws(is); - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - int tH; - int tM; - long double S{}; - read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, - CharT{':'}, rld{S, 1, w}); - checked_set(H, tH, not_a_hour, is); - checked_set(M, tM, not_a_minute, is); - checked_set(s, round_i(duration{S}), - not_a_second, is); - ws(is); - int tY = not_a_year; - read(is, rs{tY, 1, 4u}); - checked_set(Y, tY, not_a_year, is); -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'x': - if (command) - { - if (modified != CharT{'O'}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - checked_set(Y, tm.tm_year + 1900, not_a_year, is); - checked_set(m, tm.tm_mon + 1, not_a_month, is); - checked_set(d, tm.tm_mday, not_a_day, is); - } - is.setstate(err); -#else - // "%m/%d/%y" - int ty = not_a_2digit_year; - int tm = not_a_month; - int td = not_a_day; - read(is, ru{tm, 1, 2}, CharT{'/'}, ru{td, 1, 2}, CharT{'/'}, - rs{ty, 1, 2}); - checked_set(y, ty, not_a_2digit_year, is); - checked_set(m, tm, not_a_month, is); - checked_set(d, td, not_a_day, is); -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'X': - if (command) - { - if (modified != CharT{'O'}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - checked_set(H, tm.tm_hour, not_a_hour, is); - checked_set(M, tm.tm_min, not_a_minute, is); - checked_set(s, duration_cast(seconds{tm.tm_sec}), - not_a_second, is); - } - is.setstate(err); -#else - // "%T" - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - int tH = not_a_hour; - int tM = not_a_minute; - long double S{}; - read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, - CharT{':'}, rld{S, 1, w}); - checked_set(H, tH, not_a_hour, is); - checked_set(M, tM, not_a_minute, is); - checked_set(s, round_i(duration{S}), - not_a_second, is); -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'C': - if (command) - { - int tC = not_a_century; -#if !ONLY_C_LOCALE - if (modified == CharT{}) - { -#endif - read(is, rs{tC, 1, width == -1 ? 2u : static_cast(width)}); -#if !ONLY_C_LOCALE - } - else - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - auto tY = tm.tm_year + 1900; - tC = (tY >= 0 ? tY : tY-99) / 100; - } - is.setstate(err); - } -#endif - checked_set(C, tC, not_a_century, is); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'D': - if (command) - { - if (modified == CharT{}) - { - int tn = not_a_month; - int td = not_a_day; - int ty = not_a_2digit_year; - read(is, ru{tn, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, - ru{td, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, - rs{ty, 1, 2}); - checked_set(y, ty, not_a_2digit_year, is); - checked_set(m, tn, not_a_month, is); - checked_set(d, td, not_a_day, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'F': - if (command) - { - if (modified == CharT{}) - { - int tY = not_a_year; - int tn = not_a_month; - int td = not_a_day; - read(is, rs{tY, 1, width == -1 ? 4u : static_cast(width)}, - CharT{'-'}, ru{tn, 1, 2}, CharT{'-'}, ru{td, 1, 2}); - checked_set(Y, tY, not_a_year, is); - checked_set(m, tn, not_a_month, is); - checked_set(d, td, not_a_day, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'd': - case 'e': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - int td = not_a_day; - read(is, rs{td, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(d, td, not_a_day, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - command = nullptr; - width = -1; - modified = CharT{}; - if ((err & ios::failbit) == 0) - checked_set(d, tm.tm_mday, not_a_day, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'H': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - int tH = not_a_hour; - read(is, ru{tH, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(H, tH, not_a_hour, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(H, tm.tm_hour, not_a_hour, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'I': - if (command) - { - if (modified == CharT{}) - { - int tI = not_a_hour_12_value; - // reads in an hour into I, but most be in [1, 12] - read(is, rs{tI, 1, width == -1 ? 2u : static_cast(width)}); - if (!(1 <= tI && tI <= 12)) - is.setstate(ios::failbit); - checked_set(I, tI, not_a_hour_12_value, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'j': - if (command) - { - if (modified == CharT{}) - { - int tj = not_a_doy; - read(is, ru{tj, 1, width == -1 ? 3u : static_cast(width)}); - checked_set(j, tj, not_a_doy, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'M': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - int tM = not_a_minute; - read(is, ru{tM, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(M, tM, not_a_minute, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(M, tm.tm_min, not_a_minute, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'm': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - int tn = not_a_month; - read(is, rs{tn, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(m, tn, not_a_month, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(m, tm.tm_mon + 1, not_a_month, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'n': - case 't': - if (command) - { - if (modified == CharT{}) - { - // %n matches a single white space character - // %t matches 0 or 1 white space characters - auto ic = is.peek(); - if (Traits::eq_int_type(ic, Traits::eof())) - { - ios::iostate err = ios::eofbit; - if (*fmt == 'n') - err |= ios::failbit; - is.setstate(err); - break; - } - if (isspace(ic)) - { - (void)is.get(); - } - else if (*fmt == 'n') - is.setstate(ios::failbit); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'p': - if (command) - { - if (modified == CharT{}) - { - int tp = not_a_ampm; -#if !ONLY_C_LOCALE - tm = std::tm{}; - tm.tm_hour = 1; - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - is.setstate(err); - if (tm.tm_hour == 1) - tp = 0; - else if (tm.tm_hour == 13) - tp = 1; - else - is.setstate(err); -#else - auto nm = detail::ampm_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - tp = static_cast(i); -#endif - checked_set(p, tp, not_a_ampm, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - - break; - case 'r': - if (command) - { - if (modified == CharT{}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - checked_set(H, tm.tm_hour, not_a_hour, is); - checked_set(M, tm.tm_min, not_a_hour, is); - checked_set(s, duration_cast(seconds{tm.tm_sec}), - not_a_second, is); - } - is.setstate(err); -#else - // "%I:%M:%S %p" - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - long double S{}; - int tI = not_a_hour_12_value; - int tM = not_a_minute; - read(is, ru{tI, 1, 2}, CharT{':'}, ru{tM, 1, 2}, - CharT{':'}, rld{S, 1, w}); - checked_set(I, tI, not_a_hour_12_value, is); - checked_set(M, tM, not_a_minute, is); - checked_set(s, round_i(duration{S}), - not_a_second, is); - ws(is); - auto nm = detail::ampm_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - checked_set(p, static_cast(i), not_a_ampm, is); -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'R': - if (command) - { - if (modified == CharT{}) - { - int tH = not_a_hour; - int tM = not_a_minute; - read(is, ru{tH, 1, 2}, CharT{'\0'}, CharT{':'}, CharT{'\0'}, - ru{tM, 1, 2}, CharT{'\0'}); - checked_set(H, tH, not_a_hour, is); - checked_set(M, tM, not_a_minute, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'S': - if (command) - { - #if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - long double S{}; - read(is, rld{S, 1, width == -1 ? w : static_cast(width)}); - checked_set(s, round_i(duration{S}), - not_a_second, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(s, duration_cast(seconds{tm.tm_sec}), - not_a_second, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'T': - if (command) - { - if (modified == CharT{}) - { - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - int tH = not_a_hour; - int tM = not_a_minute; - long double S{}; - read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, - CharT{':'}, rld{S, 1, w}); - checked_set(H, tH, not_a_hour, is); - checked_set(M, tM, not_a_minute, is); - checked_set(s, round_i(duration{S}), - not_a_second, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'Y': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'O'}) -#endif - { - int tY = not_a_year; - read(is, rs{tY, 1, width == -1 ? 4u : static_cast(width)}); - checked_set(Y, tY, not_a_year, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'E'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(Y, tm.tm_year + 1900, not_a_year, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'y': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - int ty = not_a_2digit_year; - read(is, ru{ty, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(y, ty, not_a_2digit_year, is); - } -#if !ONLY_C_LOCALE - else - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(Y, tm.tm_year + 1900, not_a_year, is); - is.setstate(err); - } -#endif - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'g': - if (command) - { - if (modified == CharT{}) - { - int tg = not_a_2digit_year; - read(is, ru{tg, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(g, tg, not_a_2digit_year, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'G': - if (command) - { - if (modified == CharT{}) - { - int tG = not_a_year; - read(is, rs{tG, 1, width == -1 ? 4u : static_cast(width)}); - checked_set(G, tG, not_a_year, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'U': - if (command) - { - if (modified == CharT{}) - { - int tU = not_a_week_num; - read(is, ru{tU, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(U, tU, not_a_week_num, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'V': - if (command) - { - if (modified == CharT{}) - { - int tV = not_a_week_num; - read(is, ru{tV, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(V, tV, not_a_week_num, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'W': - if (command) - { - if (modified == CharT{}) - { - int tW = not_a_week_num; - read(is, ru{tW, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(W, tW, not_a_week_num, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'E': - case 'O': - if (command) - { - if (modified == CharT{}) - { - modified = *fmt; - } - else - { - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - } - else - read(is, *fmt); - break; - case '%': - if (command) - { - if (modified == CharT{}) - read(is, *fmt); - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - command = fmt; - break; - case 'z': - if (command) - { - int tH, tM; - minutes toff = not_a_offset; - bool neg = false; - auto ic = is.peek(); - if (!Traits::eq_int_type(ic, Traits::eof())) - { - auto c = static_cast(Traits::to_char_type(ic)); - if (c == '-') - { - neg = true; - (void)is.get(); - } - else if (c == '+') - (void)is.get(); - } - if (modified == CharT{}) - { - read(is, rs{tH, 2, 2}); - if (!is.fail()) - toff = hours{std::abs(tH)}; - if (is.good()) - { - ic = is.peek(); - if (!Traits::eq_int_type(ic, Traits::eof())) - { - auto c = static_cast(Traits::to_char_type(ic)); - if ('0' <= c && c <= '9') - { - read(is, ru{tM, 2, 2}); - if (!is.fail()) - toff += minutes{tM}; - } - } - } - } - else - { - read(is, rs{tH, 1, 2}); - if (!is.fail()) - toff = hours{std::abs(tH)}; - if (is.good()) - { - ic = is.peek(); - if (!Traits::eq_int_type(ic, Traits::eof())) - { - auto c = static_cast(Traits::to_char_type(ic)); - if (c == ':') - { - (void)is.get(); - read(is, ru{tM, 2, 2}); - if (!is.fail()) - toff += minutes{tM}; - } - } - } - } - if (neg) - toff = -toff; - checked_set(temp_offset, toff, not_a_offset, is); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'Z': - if (command) - { - if (modified == CharT{}) - { - std::basic_string buf; - while (is.rdstate() == std::ios::goodbit) - { - auto i = is.rdbuf()->sgetc(); - if (Traits::eq_int_type(i, Traits::eof())) - { - is.setstate(ios::eofbit); - break; - } - auto wc = Traits::to_char_type(i); - auto c = static_cast(wc); - // is c a valid time zone name or abbreviation character? - if (!(CharT{1} < wc && wc < CharT{127}) || !(isalnum(c) || - c == '_' || c == '/' || c == '-' || c == '+')) - break; - buf.push_back(c); - is.rdbuf()->sbumpc(); - } - if (buf.empty()) - is.setstate(ios::failbit); - checked_set(temp_abbrev, buf, {}, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - default: - if (command) - { - if (width == -1 && modified == CharT{} && '0' <= *fmt && *fmt <= '9') - { - width = static_cast(*fmt) - '0'; - while ('0' <= fmt[1] && fmt[1] <= '9') - width = 10*width + static_cast(*++fmt) - '0'; - } - else - { - if (modified == CharT{}) - read(is, CharT{'%'}, width, *fmt); - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - } - else // !command - { - if (isspace(static_cast(*fmt))) - { - // space matches 0 or more white space characters - if (is.good()) - ws(is); - } - else - read(is, *fmt); - } - break; - } - } - // is.fail() || *fmt == CharT{} - if (is.rdstate() == ios::goodbit && command) - { - if (modified == CharT{}) - read(is, CharT{'%'}, width); - else - read(is, CharT{'%'}, width, modified); - } - if (!is.fail()) - { - if (y != not_a_2digit_year) - { - // Convert y and an optional C to Y - if (!(0 <= y && y <= 99)) - goto broken; - if (C == not_a_century) - { - if (Y == not_a_year) - { - if (y >= 69) - C = 19; - else - C = 20; - } - else - { - C = (Y >= 0 ? Y : Y-100) / 100; - } - } - int tY; - if (C >= 0) - tY = 100*C + y; - else - tY = 100*(C+1) - (y == 0 ? 100 : y); - if (Y != not_a_year && Y != tY) - goto broken; - Y = tY; - } - if (g != not_a_2digit_year) - { - // Convert g and an optional C to G - if (!(0 <= g && g <= 99)) - goto broken; - if (C == not_a_century) - { - if (G == not_a_year) - { - if (g >= 69) - C = 19; - else - C = 20; - } - else - { - C = (G >= 0 ? G : G-100) / 100; - } - } - int tG; - if (C >= 0) - tG = 100*C + g; - else - tG = 100*(C+1) - (g == 0 ? 100 : g); - if (G != not_a_year && G != tG) - goto broken; - G = tG; - } - if (Y < static_cast(year::min()) || Y > static_cast(year::max())) - Y = not_a_year; - bool computed = false; - if (G != not_a_year && V != not_a_week_num && wd != not_a_weekday) - { - year_month_day ymd_trial = sys_days(year{G-1}/December/Thursday[last]) + - (Monday-Thursday) + weeks{V-1} + - (weekday{static_cast(wd)}-Monday); - if (Y == not_a_year) - Y = static_cast(ymd_trial.year()); - else if (year{Y} != ymd_trial.year()) - goto broken; - if (m == not_a_month) - m = static_cast(static_cast(ymd_trial.month())); - else if (month(static_cast(m)) != ymd_trial.month()) - goto broken; - if (d == not_a_day) - d = static_cast(static_cast(ymd_trial.day())); - else if (day(static_cast(d)) != ymd_trial.day()) - goto broken; - computed = true; - } - if (Y != not_a_year && U != not_a_week_num && wd != not_a_weekday) - { - year_month_day ymd_trial = sys_days(year{Y}/January/Sunday[1]) + - weeks{U-1} + - (weekday{static_cast(wd)} - Sunday); - if (year{Y} != ymd_trial.year()) - goto broken; - if (m == not_a_month) - m = static_cast(static_cast(ymd_trial.month())); - else if (month(static_cast(m)) != ymd_trial.month()) - goto broken; - if (d == not_a_day) - d = static_cast(static_cast(ymd_trial.day())); - else if (day(static_cast(d)) != ymd_trial.day()) - goto broken; - computed = true; - } - if (Y != not_a_year && W != not_a_week_num && wd != not_a_weekday) - { - year_month_day ymd_trial = sys_days(year{Y}/January/Monday[1]) + - weeks{W-1} + - (weekday{static_cast(wd)} - Monday); - if (year{Y} != ymd_trial.year()) - goto broken; - if (m == not_a_month) - m = static_cast(static_cast(ymd_trial.month())); - else if (month(static_cast(m)) != ymd_trial.month()) - goto broken; - if (d == not_a_day) - d = static_cast(static_cast(ymd_trial.day())); - else if (day(static_cast(d)) != ymd_trial.day()) - goto broken; - computed = true; - } - if (j != not_a_doy && Y != not_a_year) - { - auto ymd_trial = year_month_day{local_days(year{Y}/1/1) + days{j-1}}; - if (m == not_a_month) - m = static_cast(static_cast(ymd_trial.month())); - else if (month(static_cast(m)) != ymd_trial.month()) - goto broken; - if (d == not_a_day) - d = static_cast(static_cast(ymd_trial.day())); - else if (day(static_cast(d)) != ymd_trial.day()) - goto broken; - j = not_a_doy; - } - auto ymd = year{Y}/m/d; - if (ymd.ok()) - { - if (wd == not_a_weekday) - wd = static_cast((weekday(sys_days(ymd)) - Sunday).count()); - else if (wd != static_cast((weekday(sys_days(ymd)) - Sunday).count())) - goto broken; - if (!computed) - { - if (G != not_a_year || V != not_a_week_num) - { - sys_days sd = ymd; - auto G_trial = year_month_day{sd + days{3}}.year(); - auto start = sys_days((G_trial - years{1})/December/Thursday[last]) + - (Monday - Thursday); - if (sd < start) - { - --G_trial; - if (V != not_a_week_num) - start = sys_days((G_trial - years{1})/December/Thursday[last]) - + (Monday - Thursday); - } - if (G != not_a_year && G != static_cast(G_trial)) - goto broken; - if (V != not_a_week_num) - { - auto V_trial = duration_cast(sd - start).count() + 1; - if (V != V_trial) - goto broken; - } - } - if (U != not_a_week_num) - { - auto start = sys_days(Sunday[1]/January/ymd.year()); - auto U_trial = floor(sys_days(ymd) - start).count() + 1; - if (U != U_trial) - goto broken; - } - if (W != not_a_week_num) - { - auto start = sys_days(Monday[1]/January/ymd.year()); - auto W_trial = floor(sys_days(ymd) - start).count() + 1; - if (W != W_trial) - goto broken; - } - } - } - fds.ymd = ymd; - if (I != not_a_hour_12_value) - { - if (!(1 <= I && I <= 12)) - goto broken; - if (p != not_a_ampm) - { - // p is in [0, 1] == [AM, PM] - // Store trial H in I - if (I == 12) - --p; - I += p*12; - // Either set H from I or make sure H and I are consistent - if (H == not_a_hour) - H = I; - else if (I != H) - goto broken; - } - else // p == not_a_ampm - { - // if H, make sure H and I could be consistent - if (H != not_a_hour) - { - if (I == 12) - { - if (H != 0 && H != 12) - goto broken; - } - else if (!(I == H || I == H+12)) - { - goto broken; - } - } - else // I is ambiguous, AM or PM? - goto broken; - } - } - if (H != not_a_hour) - { - fds.has_tod = true; - fds.tod = hh_mm_ss{hours{H}}; - } - if (M != not_a_minute) - { - fds.has_tod = true; - fds.tod.m_ = minutes{M}; - } - if (s != not_a_second) - { - fds.has_tod = true; - fds.tod.s_ = detail::decimal_format_seconds{s}; - } - if (j != not_a_doy) - { - fds.has_tod = true; - fds.tod.h_ += hours{days{j}}; - } - if (wd != not_a_weekday) - fds.wd = weekday{static_cast(wd)}; - if (abbrev != nullptr) - *abbrev = std::move(temp_abbrev); - if (offset != nullptr && temp_offset != not_a_offset) - *offset = temp_offset; - } - return is; - } -broken: - is.setstate(ios::failbit); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, year& y, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.year().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - y = fds.ymd.year(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, month& m, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.month().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - m = fds.ymd.month(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, day& d, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.day().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - d = fds.ymd.day(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, weekday& wd, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.wd.ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - wd = fds.wd; - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, year_month& ym, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.month().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - ym = fds.ymd.year()/fds.ymd.month(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, month_day& md, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.month().ok() || !fds.ymd.day().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - md = fds.ymd.month()/fds.ymd.day(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - year_month_day& ymd, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - ymd = fds.ymd; - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - sys_time& tp, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = typename std::common_type::type; - using detail::round_i; - std::chrono::minutes offset_local{}; - auto offptr = offset ? offset : &offset_local; - fields fds{}; - fds.has_tod = true; - date::from_stream(is, fmt, fds, abbrev, offptr); - if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) - is.setstate(std::ios::failbit); - if (!is.fail()) - tp = round_i(sys_days(fds.ymd) - *offptr + fds.tod.to_duration()); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - local_time& tp, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = typename std::common_type::type; - using detail::round_i; - fields fds{}; - fds.has_tod = true; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) - is.setstate(std::ios::failbit); - if (!is.fail()) - tp = round_i(local_seconds{local_days(fds.ymd)} + fds.tod.to_duration()); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - std::chrono::duration& d, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using Duration = std::chrono::duration; - using CT = typename std::common_type::type; - using detail::round_i; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.has_tod) - is.setstate(std::ios::failbit); - if (!is.fail()) - d = round_i(fds.tod.to_duration()); - return is; -} - -template , - class Alloc = std::allocator> -struct parse_manip -{ - const std::basic_string format_; - Parsable& tp_; - std::basic_string* abbrev_; - std::chrono::minutes* offset_; - -public: - parse_manip(std::basic_string format, Parsable& tp, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) - : format_(std::move(format)) - , tp_(tp) - , abbrev_(abbrev) - , offset_(offset) - {} - -#if HAS_STRING_VIEW - parse_manip(const CharT* format, Parsable& tp, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) - : format_(format) - , tp_(tp) - , abbrev_(abbrev) - , offset_(offset) - {} - - parse_manip(std::basic_string_view format, Parsable& tp, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) - : format_(format) - , tp_(tp) - , abbrev_(abbrev) - , offset_(offset) - {} -#endif // HAS_STRING_VIEW -}; - -template -std::basic_istream& -operator>>(std::basic_istream& is, - const parse_manip& x) -{ - return date::from_stream(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_); -} - -template -inline -auto -parse(const std::basic_string& format, Parsable& tp) - -> decltype(date::from_stream(std::declval&>(), - format.c_str(), tp), - parse_manip{format, tp}) -{ - return {format, tp}; -} - -template -inline -auto -parse(const std::basic_string& format, Parsable& tp, - std::basic_string& abbrev) - -> decltype(date::from_stream(std::declval&>(), - format.c_str(), tp, &abbrev), - parse_manip{format, tp, &abbrev}) -{ - return {format, tp, &abbrev}; -} - -template -inline -auto -parse(const std::basic_string& format, Parsable& tp, - std::chrono::minutes& offset) - -> decltype(date::from_stream(std::declval&>(), - format.c_str(), tp, - std::declval*>(), - &offset), - parse_manip{format, tp, nullptr, &offset}) -{ - return {format, tp, nullptr, &offset}; -} - -template -inline -auto -parse(const std::basic_string& format, Parsable& tp, - std::basic_string& abbrev, std::chrono::minutes& offset) - -> decltype(date::from_stream(std::declval&>(), - format.c_str(), tp, &abbrev, &offset), - parse_manip{format, tp, &abbrev, &offset}) -{ - return {format, tp, &abbrev, &offset}; -} - -// const CharT* formats - -template -inline -auto -parse(const CharT* format, Parsable& tp) - -> decltype(date::from_stream(std::declval&>(), format, tp), - parse_manip{format, tp}) -{ - return {format, tp}; -} - -template -inline -auto -parse(const CharT* format, Parsable& tp, std::basic_string& abbrev) - -> decltype(date::from_stream(std::declval&>(), format, - tp, &abbrev), - parse_manip{format, tp, &abbrev}) -{ - return {format, tp, &abbrev}; -} - -template -inline -auto -parse(const CharT* format, Parsable& tp, std::chrono::minutes& offset) - -> decltype(date::from_stream(std::declval&>(), format, - tp, std::declval*>(), &offset), - parse_manip{format, tp, nullptr, &offset}) -{ - return {format, tp, nullptr, &offset}; -} - -template -inline -auto -parse(const CharT* format, Parsable& tp, - std::basic_string& abbrev, std::chrono::minutes& offset) - -> decltype(date::from_stream(std::declval&>(), format, - tp, &abbrev, &offset), - parse_manip{format, tp, &abbrev, &offset}) -{ - return {format, tp, &abbrev, &offset}; -} - -// duration streaming - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, - const std::chrono::duration& d) -{ - return os << detail::make_string::from(d.count()) + - detail::get_units(typename Period::type{}); -} - -} // namespace date - -#ifdef _MSC_VER -# pragma warning(pop) -#endif - -#ifdef __GNUC__ -# pragma GCC diagnostic pop -#endif - -#endif // DATE_H