[
  {
    "path": ".clang-format",
    "content": "Language: Cpp\nAccessModifierOffset: -4\nAlignAfterOpenBracket: DontAlign\nAlignArrayOfStructures: Left\nAlignConsecutiveAssignments: None\nAlignConsecutiveBitFields: None\nAlignConsecutiveDeclarations: None\nAlignConsecutiveMacros: None\nAlignEscapedNewlines: Left\nAlignOperands: Align\nAlignTrailingComments: true\nAllowAllArgumentsOnNextLine: false\nAllowAllParametersOfDeclarationOnNextLine: false\nAllowShortEnumsOnASingleLine: false\nAllowShortBlocksOnASingleLine: Never\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: None\nAllowShortLambdasOnASingleLine: None\nAllowShortIfStatementsOnASingleLine: Never\nAllowShortLoopsOnASingleLine: false\nAlwaysBreakAfterDefinitionReturnType: None\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: false\nAlwaysBreakTemplateDeclarations: MultiLine\nBinPackArguments: true\nBinPackParameters: true\nBraceWrapping: \n  AfterCaseLabel: true\n  AfterClass: true\n  AfterControlStatement: Always\n  AfterEnum: true\n  AfterFunction: true\n  AfterNamespace: true\n  AfterObjCDeclaration: true\n  AfterStruct: true\n  AfterUnion: true\n  AfterExternBlock: true\n  BeforeCatch: true\n  BeforeElse: true\n  BeforeLambdaBody: false\n  BeforeWhile: false\n  IndentBraces: false\n  SplitEmptyFunction: true\n  SplitEmptyRecord: true\n  SplitEmptyNamespace: true\nBreakBeforeBinaryOperators: None\nBreakBeforeConceptDeclarations: Always\nBreakBeforeBraces: Allman\nBreakBeforeInheritanceComma: false\nBreakInheritanceList: AfterColon\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializers: BeforeComma\nBreakAfterJavaFieldAnnotations: false\nBreakStringLiterals: true\nColumnLimit: 0\nCommentPragmas: '^ IWYU pragma: '\nQualifierAlignment: Leave\nCompactNamespaces: false\nConstructorInitializerIndentWidth: 0\nContinuationIndentWidth: 0\nCpp11BracedListStyle: true\nDeriveLineEnding: true\nDerivePointerAlignment: false\nDisableFormat: false\nEmptyLineAfterAccessModifier: Never\nEmptyLineBeforeAccessModifier: LogicalBlock\nExperimentalAutoDetectBinPacking: false\nPackConstructorInitializers: NextLine\nBasedOnStyle: ''\nConstructorInitializerAllOnOneLineOrOnePerLine: false\nAllowAllConstructorInitializersOnNextLine: true\nFixNamespaceComments: false\nIncludeBlocks: Preserve\nIndentAccessModifiers: false\nIndentCaseLabels: false\nIndentCaseBlocks: false\nIndentGotoLabels: true\nIndentPPDirectives: BeforeHash\nIndentExternBlock: AfterExternBlock\nIndentRequiresClause: true\nIndentWidth: 4\nIndentWrappedFunctionNames: false\nInsertBraces: true\nInsertTrailingCommas: None\nJavaScriptQuotes: Leave\nJavaScriptWrapImports: true\nKeepEmptyLinesAtTheStartOfBlocks: true\nLambdaBodyIndentation: OuterScope\nMacroBlockBegin: ''\nMacroBlockEnd: ''\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nObjCBinPackProtocolList: Auto\nObjCBlockIndentWidth: 2\nObjCBreakBeforeNestedBlockParam: true\nObjCSpaceAfterProperty: false\nObjCSpaceBeforeProtocolList: true\nPenaltyBreakAssignment: 2\nPenaltyBreakBeforeFirstCallParameter: 19\nPenaltyBreakComment: 300\nPenaltyBreakFirstLessLess: 120\nPenaltyBreakOpenParenthesis: 0\nPenaltyBreakString: 1000\nPenaltyBreakTemplateDeclaration: 10\nPenaltyExcessCharacter: 1000000\nPenaltyReturnTypeOnItsOwnLine: 60\nPenaltyIndentedWhitespace: 0\nPointerAlignment: Left\nPPIndentWidth: -1\nReferenceAlignment: Pointer\nReflowComments: true\nRemoveBracesLLVM: false\nRequiresClausePosition: OwnLine\nSeparateDefinitionBlocks: Leave\nShortNamespaceLines: 1\nSortIncludes: Never\nSortUsingDeclarations: false\nSpaceAfterCStyleCast: false\nSpaceAfterLogicalNot: false\nSpaceAfterTemplateKeyword: false\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeCaseColon: false\nSpaceBeforeCpp11BracedList: false\nSpaceBeforeCtorInitializerColon: false\nSpaceBeforeInheritanceColon: true\nSpaceBeforeParens: Never\nSpaceBeforeParensOptions:\n  AfterControlStatements: true\n  AfterForeachMacros: true\n  AfterFunctionDefinitionName: false\n  AfterFunctionDeclarationName: false\n  AfterIfMacros: true\n  AfterOverloadedOperator: false\n  AfterRequiresInClause: false\n  AfterRequiresInExpression: false\n  BeforeNonEmptyParentheses: false\nSpaceAroundPointerQualifiers: Default\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceInEmptyBlock: false\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 1\nSpacesInAngles: Never\nSpacesInConditionalStatement: false\nSpacesInContainerLiterals: true\nSpacesInCStyleCastParentheses: false\nSpacesInLineCommentPrefix:\n  Minimum: 1\n  Maximum: -1\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nSpaceBeforeSquareBrackets: false\nBitFieldColonSpacing: Both\nStandard: Latest\nTabWidth: 4\nUseCRLF: false\nUseTab: Never"
  },
  {
    "path": ".github/workflows/common.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: [ \"master\" ]\n  pull_request:\n    branches: [ \"master\" ]\n\nenv:\n  BUILD_TYPE: Debug\n\njobs:\n  build-linux:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n\n    - name: Configure CMake\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE:STRING=${{env.BUILD_TYPE}} -DBUILD_TESTING:BOOL=ON\n\n    - name: Build\n      run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}\n\n    - name: Test\n      working-directory: ${{github.workspace}}/build${{ matrix.subdir }}\n      run: ctest -VV -C ${{env.BUILD_TYPE}}\n\n  build-windows:\n    runs-on: windows-latest\n\n    steps:\n    - uses: actions/checkout@v3\n\n    - name: Configure CMake\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_CONFIGURATION_TYPES:STRING=${{env.BUILD_TYPE}} -DBUILD_TESTING:BOOL=ON\n\n    - name: Build\n      run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}\n\n    - name: Test\n      working-directory: ${{github.workspace}}/build\n      run: |\n        Add-Content $env:GITHUB_PATH \"${{github.workspace}}\\build\\Debug\"\n        ctest -VV -C ${{env.BUILD_TYPE}}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Prerequisites\n*.d\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Fortran module files\n*.mod\n*.smod\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n*.lib\n\n# Executables\n*.exe\n*.out\n*.app\n\n# IDEs\n.vs\n*.user\nout\nbin\nbuild"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.23) # required for file sets\r\n\r\nproject(NotEnoughStandards VERSION 1.1.0)\r\n\r\nif(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})\r\n    option(BUILD_TESTING \"Build Not Enough Standards' tests\" OFF)\r\nelse() # don't use caller cache\r\n    set(BUILD_TESTING OFF)\r\nendif()\r\n\r\nadd_library(NotEnoughStandards INTERFACE)\r\ntarget_compile_features(NotEnoughStandards INTERFACE cxx_std_20)\r\ntarget_sources(NotEnoughStandards INTERFACE\r\n    FILE_SET HEADERS\r\n    BASE_DIRS include\r\n    FILES\r\n        include/nes/shared_library.hpp\r\n        include/nes/shared_memory.hpp\r\n        include/nes/named_mutex.hpp\r\n        include/nes/semaphore.hpp\r\n        include/nes/named_semaphore.hpp\r\n        include/nes/pipe.hpp\r\n        include/nes/process.hpp\r\n        include/nes/thread_pool.hpp\r\n    )\r\n\r\nif(UNIX)\r\n    target_link_libraries(NotEnoughStandards INTERFACE dl)\r\n    if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL ANDROID)\r\n        target_link_libraries(NotEnoughStandards INTERFACE pthread rt)\r\n    endif()\r\nendif()\r\n\r\nif(BUILD_TESTING OR NOT_ENOUGH_STANDARDS_BUILD_TESTS)\r\n    include(CTest)\r\n    get_property(multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)\r\n\r\n    add_executable(NotEnoughStandardsTest tests/common.hpp tests/process.cpp)\r\n    target_link_libraries(NotEnoughStandardsTest PRIVATE NotEnoughStandards)\r\n\r\n    add_executable(NotEnoughStandardsTestOther tests/common.hpp tests/process_other.cpp)\r\n    target_link_libraries(NotEnoughStandardsTestOther PRIVATE NotEnoughStandards)\r\n\r\n    add_library(NotEnoughStandardsTestLib SHARED tests/common.hpp tests/library.cpp)\r\n    set_target_properties(NotEnoughStandardsTestLib PROPERTIES PREFIX \"\")\r\n    target_link_libraries(NotEnoughStandardsTestLib PRIVATE NotEnoughStandards)\r\n\r\n    add_test(NAME NotEnoughStandardsTest COMMAND NotEnoughStandardsTest)\r\n    if(multi_config)\r\n        set_tests_properties(NotEnoughStandardsTest PROPERTIES WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)\r\n    endif()\r\n\r\n    add_executable(NotEnoughStandardsThreadPoolTest tests/common.hpp tests/thread_pool_test.cpp)\r\n    target_link_libraries(NotEnoughStandardsThreadPoolTest PRIVATE NotEnoughStandards)\r\n    add_test(NAME NotEnoughStandardsThreadPoolTest COMMAND NotEnoughStandardsThreadPoolTest)\r\nendif()\r\n\r\ninclude(CMakePackageConfigHelpers)\r\n\r\nconfigure_package_config_file(\r\n    ${CMAKE_CURRENT_SOURCE_DIR}/cmake/NotEnoughStandards.cmake.in\r\n    ${CMAKE_CURRENT_BINARY_DIR}/NotEnoughStandardsConfig.cmake\r\n    INSTALL_DESTINATION lib/cmake/NotEnoughStandards\r\n)\r\n\r\nwrite_basic_package_version_file(\r\n    ${CMAKE_CURRENT_BINARY_DIR}/NotEnoughStandardsConfigVersion.cmake\r\n    VERSION ${PROJECT_VERSION}\r\n    COMPATIBILITY SameMajorVersion\r\n)\r\n\r\ninstall(TARGETS NotEnoughStandards\r\n    EXPORT NotEnoughStandardsTargets\r\n    FILE_SET HEADERS DESTINATION include\r\n)\r\n\r\ninstall(EXPORT NotEnoughStandardsTargets\r\n    DESTINATION lib/cmake/NotEnoughStandards\r\n    NAMESPACE NotEnoughStandards::\r\n)\r\n\r\ninstall(FILES\r\n    ${CMAKE_CURRENT_BINARY_DIR}/NotEnoughStandardsConfigVersion.cmake\r\n    ${CMAKE_CURRENT_BINARY_DIR}/NotEnoughStandardsConfig.cmake\r\n    DESTINATION lib/cmake/NotEnoughStandards\r\n)\r\n\r\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Alexy Pellegrini\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Not Enough Standards\n\nNot Enough Standards is a modern header-only C++17 and C++20 library that provides platform-independent utilities. The goal of this library is to extend the standard library with recurent features, such as process management, shared library loading or thread pools. To reach that goal the library is written in a very standard compliant way, from the coding-style to the naming convention.\n\n## Features\n\nNot Enough Standards works on any posix-compliant system and also on Windows.\n\n* [Shared library loading](https://github.com/Alairion/not-enough-standards/wiki/shared_library.hpp)\n* [Process management](https://github.com/Alairion/not-enough-standards/wiki/process.hpp)\n* Inter-process communication ([pipes](https://github.com/Alairion/not-enough-standards/wiki/pipe.hpp), [shared memory](https://github.com/Alairion/not-enough-standards/wiki/shared_memory.hpp))\n* Inter-process synchronization ([named mutexes](https://github.com/Alairion/not-enough-standards/wiki/named_mutex.hpp), [named semaphores](https://github.com/Alairion/not-enough-standards/wiki/names_semaphore.hpp))\n* Synchronization primitives ([semaphores](https://github.com/Alairion/not-enough-standards/wiki/semaphore.hpp))\n* [Thread pools](https://github.com/Alairion/not-enough-standards/wiki/thread_pool.hpp)\n\nCheck out the [Wiki](https://github.com/Alairion/not-enough-standards/wiki) for more informations.\n\n## Installation\n\nNot Enough Standards requires a C++17 compiler, and a C++20 compiler for thread pools.\n\nAs any header only library, Not Enough Standards is designed to be directly included in your project, by copying the files you need in your project's directory.\n\nYou may also use it as a CMake subproject using `add_subdirectory`, or by find package, and use it as any other library:\n```\ntarget_link_libraries(xxx PRIVATE NotEnoughStandards::NotEnoughStandards)\n```\n\nThe files of the library are independent from each others, so if you only need one specific feature, you can use only the header that contains it.   \nActually the only file with a dependency is `process.hpp` which defines more features if `pipe.hpp` is available.\n\n## Usage\n\nHere is a short example using Not Enough Standards:\n\n#### main.cpp\n```cpp\n#include <iostream>\n#include <nes/process.hpp>\n\nint main()\n{\n    //nes::this_process namespace can be used to modify current process or get informations about it.\n    std::cout << \"Current process has id \" << nes::this_process::get_id() << std::endl; \n    std::cout << \"Its current directory is \\\"\" << nes::this_process::working_directory() << \"\\\"\" << std::endl;\n\n    //Create a child process\n    nes::process other{\"other_process\", {\"Hey!\", \"\\\\\\\"12\\\"\\\"\\\\\\\\\", \"\\\\42\\\\\", \"It's \\\"me\\\"!\"}, nes::process_options::grab_stdout};\n    \n    //Read the entire standard output of the child process. (nes::process_options::grab_stdout must be specified on process creation)\n    std::cout << other.stdout_stream().rdbuf() << std::endl;\n\n    //As a std::thread, a nes::process must be joined if it is not detached.\n    if(other.joinable())\n        other.join();\n\n    //Once joined, we can check its return code.\n    std::cout << \"Other process ended with code: \" << other.return_code() << std::endl;\n}\n```\n\n#### other.cpp\n\n```cpp\n#include <iostream>\n#include <nes/process.hpp>\n\nint main(int argc, char** argv)\n{\n    //Output some informations about this process\n    std::cout << \"Hello world! I'm Other!\\n\";\n    std::cout << \"You gave me \" << argc << \" arguments:\";\n    for(int i{}; i < argc; ++i)\n        std::cout << \"[\" << argv[i] << \"] \";\n    std::cout << '\\n';\n    std::cout << \"My working directory is \\\"\" << nes::this_process::working_directory() << \"\\\"\" << std::endl;\n}\n```\n\n#### Output\n\n```\nCurrent process has id 3612\nIts current directory is \"/...\"\nHello world! I'm Other!\nYou gave me 5 arguments:[not_enough_standards_other.exe] [Hey!] [\\\"12\"\"\\\\\\] [\\42\\] [It's \"me\"!] \nMy working directory is \"/...\"\n\nOther process ended with code: 0\n```\n\n## License\n\nNot Enough Standards use the [MIT license](https://opensource.org/licenses/MIT).\n"
  },
  {
    "path": "cmake/NotEnoughStandards.cmake.in",
    "content": "@PACKAGE_INIT@\n\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/NotEnoughStandardsTargets.cmake\")\ncheck_required_components(\"@PROJECT_NAME@\")\n"
  },
  {
    "path": "include/nes/named_mutex.hpp",
    "content": "///////////////////////////////////////////////////////////\r\n/// Copyright 2019 Alexy Pellegrini\r\n///\r\n/// Permission is hereby granted, free of charge,\r\n/// to any person obtaining a copy of this software\r\n/// and associated documentation files (the \"Software\"),\r\n/// to deal in the Software without restriction,\r\n/// including without limitation the rights to use,\r\n/// copy, modify, merge, publish, distribute, sublicense,\r\n/// and/or sell copies of the Software, and to permit\r\n/// persons to whom the Software is furnished to do so,\r\n/// subject to the following conditions:\r\n///\r\n/// The above copyright notice and this permission notice\r\n/// shall be included in all copies or substantial portions\r\n/// of the Software.\r\n///\r\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\r\n/// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\r\n/// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\r\n/// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\r\n/// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r\n/// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r\n/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE\r\n/// OR OTHER DEALINGS IN THE SOFTWARE.\r\n///////////////////////////////////////////////////////////\r\n\r\n#ifndef NOT_ENOUGH_STANDARDS_NAMED_MUTEX\r\n#define NOT_ENOUGH_STANDARDS_NAMED_MUTEX\r\n\r\n#if defined(_WIN32)\r\n    #define NES_WIN32_NAMED_MUTEX\r\n    #ifndef NOMINMAX\r\n        #define NOMINMAX\r\n    #endif\r\n    #ifndef WIN32_LEAN_AND_MEAN\r\n        #define WIN32_LEAN_AND_MEAN\r\n    #endif\r\n    #include <Windows.h>\r\n#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))\r\n    #define NES_POSIX_NAMED_MUTEX\r\n    #include <unistd.h>\r\n    #include <pthread.h>\r\n    #include <time.h>\r\n    #include <fcntl.h>\r\n    #include <string.h>\r\n    #include <sys/mman.h>\r\n#else\r\n    #error \"Not enough standards does not support this environment.\"\r\n#endif\r\n\r\n#ifdef NES_INLINE_NAMESPACE\r\n    #define NES_INLINE_NAMESPACE_BEGIN        \\\r\n        inline namespace NES_INLINE_NAMESPACE \\\r\n        {\r\n    #define NES_INLINE_NAMESPACE_END }\r\n#else\r\n    #define NES_INLINE_NAMESPACE_BEGIN\r\n    #define NES_INLINE_NAMESPACE_END\r\n#endif\r\n\r\n#include <string>\r\n#include <utility>\r\n#include <stdexcept>\r\n#include <cassert>\r\n#include <chrono>\r\n\r\n#if defined(NES_WIN32_NAMED_MUTEX)\r\n\r\nnamespace nes\r\n{\r\n\r\nNES_INLINE_NAMESPACE_BEGIN\r\n\r\ninline constexpr const char named_mutex_root[] = \"Local\\\\\";\r\n\r\nnamespace impl\r\n{\r\n\r\nstruct named_mutex_base\r\n{\r\n    HANDLE create_or_open(const std::string& name)\r\n    {\r\n        const auto native_name{to_wide(named_mutex_root + name)};\r\n\r\n        HANDLE handle{CreateMutexW(nullptr, FALSE, std::data(native_name))};\r\n        if(!handle)\r\n        {\r\n            if(GetLastError() == ERROR_ACCESS_DENIED)\r\n            {\r\n                handle = OpenMutexW(SYNCHRONIZE, FALSE, std::data(native_name));\r\n                if(!handle)\r\n                {\r\n                    throw std::runtime_error{\"Failed to open named mutex. \" + get_error_message()};\r\n                }\r\n            }\r\n            else\r\n            {\r\n                throw std::runtime_error{\"Failed to create named mutex. \" + get_error_message()};\r\n            }\r\n        }\r\n\r\n        return handle;\r\n    }\r\n\r\n    std::wstring to_wide(const std::string& path)\r\n    {\r\n        assert(std::size(path) < 0x7FFFFFFFu && \"Wrong path.\");\r\n\r\n        if(std::empty(path))\r\n        {\r\n            return {};\r\n        }\r\n\r\n        std::wstring out_path{};\r\n        out_path.resize(static_cast<std::size_t>(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), nullptr, 0)));\r\n\r\n        if(!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), std::data(out_path), static_cast<int>(std::size(out_path))))\r\n        {\r\n            throw std::runtime_error{\"Failed to convert the path to wide.\"};\r\n        }\r\n\r\n        return out_path;\r\n    }\r\n\r\n    std::string get_error_message() const\r\n    {\r\n        return \"#\" + std::to_string(GetLastError());\r\n    }\r\n};\r\n\r\n}\r\n\r\nclass named_mutex : impl::named_mutex_base\r\n{\r\npublic:\r\n    using native_handle_type = HANDLE;\r\n\r\npublic:\r\n    explicit named_mutex(const std::string& name)\r\n    : m_handle{create_or_open(name)}\r\n    {\r\n    }\r\n\r\n    ~named_mutex()\r\n    {\r\n        CloseHandle(m_handle);\r\n    }\r\n\r\n    named_mutex(const named_mutex&) = delete;\r\n    named_mutex& operator=(const named_mutex&) = delete;\r\n    named_mutex(named_mutex&&) noexcept = delete;\r\n    named_mutex& operator=(named_mutex&&) noexcept = delete;\r\n\r\n    void lock()\r\n    {\r\n        if(WaitForSingleObject(m_handle, INFINITE) == WAIT_FAILED)\r\n        {\r\n            throw std::runtime_error{\"Failed to lock mutex. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    bool try_lock()\r\n    {\r\n        return WaitForSingleObject(m_handle, 0) == WAIT_OBJECT_0;\r\n    }\r\n\r\n    void unlock()\r\n    {\r\n        ReleaseMutex(m_handle);\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\nprivate:\r\n    native_handle_type m_handle{};\r\n};\r\n\r\nclass timed_named_mutex : impl::named_mutex_base\r\n{\r\npublic:\r\n    using native_handle_type = HANDLE;\r\n\r\npublic:\r\n    explicit timed_named_mutex(const std::string& name)\r\n    : m_handle{create_or_open(name)}\r\n    {\r\n    }\r\n\r\n    ~timed_named_mutex()\r\n    {\r\n        CloseHandle(m_handle);\r\n    }\r\n\r\n    timed_named_mutex(const timed_named_mutex&) = delete;\r\n    timed_named_mutex& operator=(const timed_named_mutex&) = delete;\r\n    timed_named_mutex(timed_named_mutex&&) noexcept = delete;\r\n    timed_named_mutex& operator=(timed_named_mutex&&) noexcept = delete;\r\n\r\n    void lock()\r\n    {\r\n        if(WaitForSingleObject(m_handle, INFINITE) == WAIT_FAILED)\r\n        {\r\n            throw std::runtime_error{\"Failed to lock mutex. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    bool try_lock()\r\n    {\r\n        return WaitForSingleObject(m_handle, 0) == WAIT_OBJECT_0;\r\n    }\r\n\r\n    template<class Rep, class Period>\r\n    bool try_lock_for(const std::chrono::duration<Rep, Period>& timeout)\r\n    {\r\n        return WaitForSingleObject(m_handle, static_cast<DWORD>(std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count())) == WAIT_OBJECT_0;\r\n    }\r\n\r\n    template<class Clock, class Duration>\r\n    bool try_lock_until(const std::chrono::time_point<Clock, Duration>& time_point)\r\n    {\r\n        const auto current_time{Clock::now()};\r\n        if(time_point < current_time)\r\n        {\r\n            return try_lock();\r\n        }\r\n\r\n        return try_lock_for(time_point - current_time);\r\n    }\r\n\r\n    void unlock()\r\n    {\r\n        ReleaseMutex(m_handle);\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\nprivate:\r\n    native_handle_type m_handle{};\r\n};\r\n\r\nclass recursive_named_mutex : public named_mutex\r\n{\r\n};\r\n\r\nclass recursive_timed_named_mutex : public timed_named_mutex\r\n{\r\n};\r\n\r\nNES_INLINE_NAMESPACE_END\r\n\r\n}\r\n\r\n#elif defined(NES_POSIX_NAMED_MUTEX)\r\n\r\nnamespace nes\r\n{\r\n\r\nNES_INLINE_NAMESPACE_BEGIN\r\n\r\ninline constexpr const char named_mutex_root[] = \"/\";\r\n\r\nnamespace impl\r\n{\r\n\r\nstruct mutex_data\r\n{\r\n    std::uint64_t opened{};\r\n    pthread_mutex_t mutex{};\r\n};\r\n\r\nstruct mutex_base\r\n{\r\n    int memory{-1};\r\n    mutex_data* data{};\r\n};\r\n\r\ninline mutex_base create_or_open_mutex(const std::string& name, bool recursive)\r\n{\r\n    const auto native_name{named_mutex_root + name};\r\n\r\n    int shm_handle{shm_open(std::data(native_name), O_RDWR | O_CREAT, 0660)};\r\n    if(shm_handle == -1)\r\n    {\r\n        throw std::runtime_error{\"Failed to allocate space for named mutex. \" + std::string{strerror(errno)}};\r\n    }\r\n\r\n    if(ftruncate(shm_handle, sizeof(mutex_data)) == -1)\r\n    {\r\n        close(shm_handle);\r\n        throw std::runtime_error{\"Failed to truncate shared memory for named mutex. \" + std::string{strerror(errno)}};\r\n    }\r\n\r\n    auto* ptr{reinterpret_cast<mutex_data*>(mmap(nullptr, sizeof(mutex_data), PROT_READ | PROT_WRITE, MAP_SHARED, shm_handle, 0))};\r\n    if(ptr == MAP_FAILED)\r\n    {\r\n        close(shm_handle);\r\n        throw std::runtime_error{\"Failed to map shared memory for named mutex. \" + std::string{strerror(errno)}};\r\n    }\r\n\r\n    if(!ptr->opened)\r\n    {\r\n        pthread_mutexattr_t attr{};\r\n        pthread_mutexattr_init(&attr);\r\n\r\n        auto clean_and_throw = [ptr, shm_handle, &attr](const std::string& error_str, int error)\r\n        {\r\n            munmap(ptr, sizeof(mutex_data));\r\n            close(shm_handle);\r\n            pthread_mutexattr_destroy(&attr);\r\n            throw std::runtime_error{error_str + std::string{strerror(error)}};\r\n        };\r\n\r\n        if(auto error = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); error != 0)\r\n        {\r\n            clean_and_throw(\"Failed to set process shared attribute of mutex. \", error);\r\n        }\r\n\r\n        if(auto error = pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST); error != 0)\r\n        {\r\n            clean_and_throw(\"Failed to set robust attribute of mutex. \", error);\r\n        }\r\n\r\n        if(recursive)\r\n        {\r\n            if(auto error = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); error != 0)\r\n            {\r\n                clean_and_throw(\"Failed to set recursive attribute of mutex. \", error);\r\n            }\r\n        }\r\n\r\n        if(auto error = pthread_mutex_init(&ptr->mutex, &attr); error != 0)\r\n        {\r\n            clean_and_throw(\"Failed to init mutex. \", error);\r\n        }\r\n\r\n        pthread_mutexattr_destroy(&attr);\r\n\r\n        ptr->opened = 1;\r\n    }\r\n\r\n    return mutex_base{shm_handle, ptr};\r\n}\r\n\r\ninline void close_mutex(mutex_base& mutex)\r\n{\r\n    if(mutex.data)\r\n    {\r\n        munmap(std::exchange(mutex.data, nullptr), sizeof(mutex_data));\r\n    }\r\n\r\n    if(mutex.memory != -1)\r\n    {\r\n        close(std::exchange(mutex.memory, -1));\r\n    }\r\n}\r\n\r\ninline void lock_mutex(mutex_base& mutex)\r\n{\r\n    auto error{pthread_mutex_lock(&mutex.data->mutex)};\r\n    if(error == EOWNERDEAD)\r\n    {\r\n        pthread_mutex_consistent(&mutex.data->mutex);\r\n    }\r\n    else if(error != 0)\r\n    {\r\n        throw std::runtime_error{\"Failed to lock mutex. \" + std::string{strerror(error)}};\r\n    }\r\n}\r\n\r\ninline bool try_lock_mutex(mutex_base& mutex)\r\n{\r\n    auto error{pthread_mutex_trylock(&mutex.data->mutex)};\r\n    if(error == EOWNERDEAD)\r\n    {\r\n        pthread_mutex_consistent(&mutex.data->mutex);\r\n        return true;\r\n    }\r\n\r\n    return !error;\r\n}\r\n\r\ninline bool try_lock_mutex_until(mutex_base& mutex, const timespec& time)\r\n{\r\n    auto error{pthread_mutex_timedlock(&mutex.data->mutex, &time)};\r\n    if(error == EOWNERDEAD)\r\n    {\r\n        pthread_mutex_consistent(&mutex.data->mutex);\r\n        return true;\r\n    }\r\n\r\n    return !error;\r\n}\r\n\r\n}\r\n\r\nclass named_mutex\r\n{\r\npublic:\r\n    using native_handle_type = pthread_mutex_t*;\r\n\r\npublic:\r\n    explicit named_mutex(const std::string& name)\r\n    : m_handle{impl::create_or_open_mutex(name, false)}\r\n    {\r\n    }\r\n\r\n    ~named_mutex()\r\n    {\r\n        impl::close_mutex(m_handle);\r\n    }\r\n\r\n    named_mutex(const named_mutex&) = delete;\r\n    named_mutex& operator=(const named_mutex&) = delete;\r\n    named_mutex(named_mutex&&) noexcept = delete;\r\n    named_mutex& operator=(named_mutex&&) noexcept = delete;\r\n\r\n    void lock()\r\n    {\r\n        impl::lock_mutex(m_handle);\r\n    }\r\n\r\n    bool try_lock()\r\n    {\r\n        return impl::try_lock_mutex(m_handle);\r\n    }\r\n\r\n    void unlock()\r\n    {\r\n        pthread_mutex_unlock(&m_handle.data->mutex);\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return &m_handle.data->mutex;\r\n    }\r\n\r\nprivate:\r\n    impl::mutex_base m_handle{};\r\n};\r\n\r\nclass timed_named_mutex\r\n{\r\npublic:\r\n    using native_handle_type = pthread_mutex_t*;\r\n\r\npublic:\r\n    explicit timed_named_mutex(const std::string& name)\r\n    : m_handle{impl::create_or_open_mutex(name, false)}\r\n    {\r\n    }\r\n\r\n    ~timed_named_mutex()\r\n    {\r\n        impl::close_mutex(m_handle);\r\n    }\r\n\r\n    timed_named_mutex(const timed_named_mutex&) = delete;\r\n    timed_named_mutex& operator=(const timed_named_mutex&) = delete;\r\n    timed_named_mutex(timed_named_mutex&&) noexcept = delete;\r\n    timed_named_mutex& operator=(timed_named_mutex&&) noexcept = delete;\r\n\r\n    void lock()\r\n    {\r\n        impl::lock_mutex(m_handle);\r\n    }\r\n\r\n    bool try_lock()\r\n    {\r\n        return impl::try_lock_mutex(m_handle);\r\n    }\r\n\r\n    template<class Rep, class Period>\r\n    bool try_lock_for(const std::chrono::duration<Rep, Period>& timeout)\r\n    {\r\n        return try_lock_until(std::chrono::system_clock::now() + timeout);\r\n    }\r\n\r\n    template<class Clock, class Duration>\r\n    bool try_lock_until(const std::chrono::time_point<Clock, Duration>& time_point)\r\n    {\r\n        const auto seconds{std::chrono::time_point_cast<std::chrono::seconds>(time_point)};\r\n        const auto nanoseconds{std::chrono::duration_cast<std::chrono::nanoseconds>(time_point - seconds)};\r\n\r\n        timespec time{};\r\n        time.tv_sec = static_cast<std::time_t>(seconds.time_since_epoch().count());\r\n        time.tv_nsec = static_cast<long>(nanoseconds.count());\r\n\r\n        return impl::try_lock_mutex_until(m_handle, time);\r\n    }\r\n\r\n    void unlock()\r\n    {\r\n        pthread_mutex_unlock(&m_handle.data->mutex);\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return &m_handle.data->mutex;\r\n    }\r\n\r\nprivate:\r\n    impl::mutex_base m_handle{};\r\n};\r\n\r\nclass recursive_named_mutex\r\n{\r\npublic:\r\n    using native_handle_type = pthread_mutex_t*;\r\n\r\npublic:\r\n    explicit recursive_named_mutex(const std::string& name)\r\n    : m_handle{impl::create_or_open_mutex(name, true)}\r\n    {\r\n    }\r\n\r\n    ~recursive_named_mutex()\r\n    {\r\n        impl::close_mutex(m_handle);\r\n    }\r\n\r\n    recursive_named_mutex(const recursive_named_mutex&) = delete;\r\n    recursive_named_mutex& operator=(const recursive_named_mutex&) = delete;\r\n    recursive_named_mutex(recursive_named_mutex&&) noexcept = delete;\r\n    recursive_named_mutex& operator=(recursive_named_mutex&&) noexcept = delete;\r\n\r\n    void lock()\r\n    {\r\n        impl::lock_mutex(m_handle);\r\n    }\r\n\r\n    bool try_lock()\r\n    {\r\n        return impl::try_lock_mutex(m_handle);\r\n    }\r\n\r\n    void unlock()\r\n    {\r\n        pthread_mutex_unlock(&m_handle.data->mutex);\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return &m_handle.data->mutex;\r\n    }\r\n\r\nprivate:\r\n    impl::mutex_base m_handle{};\r\n};\r\n\r\nclass recursive_timed_named_mutex\r\n{\r\npublic:\r\n    using native_handle_type = pthread_mutex_t*;\r\n\r\npublic:\r\n    explicit recursive_timed_named_mutex(const std::string& name)\r\n    : m_handle{impl::create_or_open_mutex(name, true)}\r\n    {\r\n    }\r\n\r\n    ~recursive_timed_named_mutex()\r\n    {\r\n        impl::close_mutex(m_handle);\r\n    }\r\n\r\n    recursive_timed_named_mutex(const recursive_timed_named_mutex&) = delete;\r\n    recursive_timed_named_mutex& operator=(const recursive_timed_named_mutex&) = delete;\r\n    recursive_timed_named_mutex(recursive_timed_named_mutex&&) noexcept = delete;\r\n    recursive_timed_named_mutex& operator=(recursive_timed_named_mutex&&) noexcept = delete;\r\n\r\n    void lock()\r\n    {\r\n        impl::lock_mutex(m_handle);\r\n    }\r\n\r\n    bool try_lock()\r\n    {\r\n        return impl::try_lock_mutex(m_handle);\r\n    }\r\n\r\n    template<class Rep, class Period>\r\n    bool try_lock_for(const std::chrono::duration<Rep, Period>& timeout)\r\n    {\r\n        return try_lock_until(std::chrono::system_clock::now() + timeout);\r\n    }\r\n\r\n    template<class Clock, class Duration>\r\n    bool try_lock_until(const std::chrono::time_point<Clock, Duration>& time_point)\r\n    {\r\n        const auto seconds{std::chrono::time_point_cast<std::chrono::seconds>(time_point)};\r\n        const auto nanoseconds{std::chrono::duration_cast<std::chrono::nanoseconds>(time_point - seconds)};\r\n\r\n        timespec time{};\r\n        time.tv_sec = static_cast<std::time_t>(seconds.time_since_epoch().count());\r\n        time.tv_nsec = static_cast<long>(nanoseconds.count());\r\n\r\n        return impl::try_lock_mutex_until(m_handle, time);\r\n    }\r\n\r\n    void unlock()\r\n    {\r\n        pthread_mutex_unlock(&m_handle.data->mutex);\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return &m_handle.data->mutex;\r\n    }\r\n\r\nprivate:\r\n    impl::mutex_base m_handle{};\r\n};\r\n\r\nNES_INLINE_NAMESPACE_END\r\n\r\n}\r\n\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/nes/named_semaphore.hpp",
    "content": "///////////////////////////////////////////////////////////\r\n/// Copyright 2019 Alexy Pellegrini\r\n///\r\n/// Permission is hereby granted, free of charge,\r\n/// to any person obtaining a copy of this software\r\n/// and associated documentation files (the \"Software\"),\r\n/// to deal in the Software without restriction,\r\n/// including without limitation the rights to use,\r\n/// copy, modify, merge, publish, distribute, sublicense,\r\n/// and/or sell copies of the Software, and to permit\r\n/// persons to whom the Software is furnished to do so,\r\n/// subject to the following conditions:\r\n///\r\n/// The above copyright notice and this permission notice\r\n/// shall be included in all copies or substantial portions\r\n/// of the Software.\r\n///\r\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\r\n/// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\r\n/// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\r\n/// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\r\n/// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r\n/// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r\n/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE\r\n/// OR OTHER DEALINGS IN THE SOFTWARE.\r\n///////////////////////////////////////////////////////////\r\n\r\n#ifndef NOT_ENOUGH_STANDARDS_NAMED_SEMAPHORE\r\n#define NOT_ENOUGH_STANDARDS_NAMED_SEMAPHORE\r\n\r\n#if defined(_WIN32)\r\n    #define NES_WIN32_NAMED_SEMAPHORE\r\n    #ifndef NOMINMAX\r\n        #define NOMINMAX\r\n    #endif\r\n    #ifndef WIN32_LEAN_AND_MEAN\r\n        #define WIN32_LEAN_AND_MEAN\r\n    #endif\r\n    #include <Windows.h>\r\n#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))\r\n    #define NES_POSIX_NAMED_SEMAPHORE\r\n    #include <unistd.h>\r\n    #include <string.h>\r\n    #include <semaphore.h>\r\n    #include <fcntl.h>\r\n#else\r\n    #error \"Not enough standards does not support this environment.\"\r\n#endif\r\n\r\n#ifdef NES_INLINE_NAMESPACE\r\n    #define NES_INLINE_NAMESPACE_BEGIN        \\\r\n        inline namespace NES_INLINE_NAMESPACE \\\r\n        {\r\n    #define NES_INLINE_NAMESPACE_END }\r\n#else\r\n    #define NES_INLINE_NAMESPACE_BEGIN\r\n    #define NES_INLINE_NAMESPACE_END\r\n#endif\r\n\r\n#include <cassert>\r\n#include <string>\r\n#include <chrono>\r\n#include <limits>\r\n#include <utility>\r\n#include <stdexcept>\r\n\r\n#if defined(NES_WIN32_NAMED_SEMAPHORE)\r\n\r\nnamespace nes\r\n{\r\n\r\nNES_INLINE_NAMESPACE_BEGIN\r\n\r\ninline constexpr const char named_semaphore_root[] = \"Local\\\\\";\r\n\r\nclass named_semaphore\r\n{\r\npublic:\r\n    using native_handle_type = HANDLE;\r\n\r\npublic:\r\n    explicit named_semaphore(const std::string& name, std::size_t initial_count = 0)\r\n    {\r\n        const auto native_name{to_wide(named_semaphore_root + name)};\r\n\r\n        m_handle = CreateSemaphoreW(nullptr, static_cast<LONG>(initial_count), std::numeric_limits<LONG>::max(), std::data(native_name));\r\n        if(!m_handle)\r\n        {\r\n            if(GetLastError() == ERROR_ACCESS_DENIED)\r\n            {\r\n                m_handle = OpenSemaphoreW(SYNCHRONIZE, FALSE, std::data(native_name));\r\n                if(!m_handle)\r\n                {\r\n                    throw std::runtime_error{\"Failed to open semaphore. \" + get_error_message()};\r\n                }\r\n            }\r\n            else\r\n            {\r\n                throw std::runtime_error{\"Failed to create semaphore. \" + get_error_message()};\r\n            }\r\n        }\r\n    }\r\n\r\n    ~named_semaphore()\r\n    {\r\n        CloseHandle(m_handle);\r\n    }\r\n\r\n    named_semaphore(const named_semaphore&) = delete;\r\n    named_semaphore& operator=(const named_semaphore&) = delete;\r\n    named_semaphore(named_semaphore&& other) noexcept = delete;\r\n    named_semaphore& operator=(named_semaphore&& other) noexcept = delete;\r\n\r\n    void acquire()\r\n    {\r\n        if(WaitForSingleObject(m_handle, INFINITE))\r\n        {\r\n            throw std::runtime_error{\"Failed to decrement semaphore count. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    bool try_acquire()\r\n    {\r\n        return WaitForSingleObject(m_handle, 0) == WAIT_OBJECT_0;\r\n    }\r\n\r\n    void release()\r\n    {\r\n        if(!ReleaseSemaphore(m_handle, 1, nullptr))\r\n        {\r\n            throw std::runtime_error{\"Failed to increment semaphore count. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\nprivate:\r\n    std::wstring to_wide(const std::string& path)\r\n    {\r\n        assert(std::size(path) < 0x7FFFFFFFu && \"Wrong path.\");\r\n\r\n        if(std::empty(path))\r\n        {\r\n            return {};\r\n        }\r\n\r\n        std::wstring out_path{};\r\n        out_path.resize(static_cast<std::size_t>(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), nullptr, 0)));\r\n\r\n        if(!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), std::data(out_path), static_cast<int>(std::size(out_path))))\r\n        {\r\n            throw std::runtime_error{\"Failed to convert the path to wide.\"};\r\n        }\r\n\r\n        return out_path;\r\n    }\r\n\r\n    std::string get_error_message() const\r\n    {\r\n        return \"#\" + std::to_string(GetLastError());\r\n    }\r\n\r\nprivate:\r\n    native_handle_type m_handle{};\r\n};\r\n\r\nclass timed_named_semaphore\r\n{\r\npublic:\r\n    using native_handle_type = HANDLE;\r\n\r\npublic:\r\n    explicit timed_named_semaphore(const std::string& name, std::size_t initial_count = 0)\r\n    {\r\n        const auto native_name{to_wide(named_semaphore_root + name)};\r\n\r\n        m_handle = CreateSemaphoreW(nullptr, static_cast<LONG>(initial_count), std::numeric_limits<LONG>::max(), std::data(native_name));\r\n        if(!m_handle)\r\n        {\r\n            if(GetLastError() == ERROR_ACCESS_DENIED)\r\n            {\r\n                m_handle = OpenSemaphoreW(SYNCHRONIZE, FALSE, std::data(native_name));\r\n                if(!m_handle)\r\n                {\r\n                    throw std::runtime_error{\"Failed to open semaphore. \" + get_error_message()};\r\n                }\r\n            }\r\n            else\r\n            {\r\n                throw std::runtime_error{\"Failed to create semaphore. \" + get_error_message()};\r\n            }\r\n        }\r\n    }\r\n\r\n    ~timed_named_semaphore()\r\n    {\r\n        CloseHandle(m_handle);\r\n    }\r\n\r\n    timed_named_semaphore(const timed_named_semaphore&) = delete;\r\n    timed_named_semaphore& operator=(const timed_named_semaphore&) = delete;\r\n    timed_named_semaphore(timed_named_semaphore&& other) noexcept = delete;\r\n    timed_named_semaphore& operator=(timed_named_semaphore&& other) noexcept = delete;\r\n\r\n    void acquire()\r\n    {\r\n        if(WaitForSingleObject(m_handle, INFINITE))\r\n        {\r\n            throw std::runtime_error{\"Failed to decrement semaphore count. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    bool try_acquire()\r\n    {\r\n        return WaitForSingleObject(m_handle, 0) == WAIT_OBJECT_0;\r\n    }\r\n\r\n    template<class Rep, class Period>\r\n    bool try_acquire_for(const std::chrono::duration<Rep, Period>& timeout)\r\n    {\r\n        return WaitForSingleObject(m_handle, std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()) == WAIT_OBJECT_0;\r\n    }\r\n\r\n    template<class Clock, class Duration>\r\n    bool try_acquire_until(const std::chrono::time_point<Clock, Duration>& time_point)\r\n    {\r\n        const auto current_time{Clock::now()};\r\n        if(time_point < current_time)\r\n        {\r\n            return try_acquire();\r\n        }\r\n\r\n        return try_acquire_for(time_point - current_time);\r\n    }\r\n\r\n    void release()\r\n    {\r\n        if(!ReleaseSemaphore(m_handle, 1, nullptr))\r\n        {\r\n            throw std::runtime_error{\"Failed to increment semaphore count. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\nprivate:\r\n    std::wstring to_wide(const std::string& path)\r\n    {\r\n        assert(std::size(path) < 0x7FFFFFFFu && \"Wrong path.\");\r\n\r\n        if(std::empty(path))\r\n        {\r\n            return {};\r\n        }\r\n\r\n        std::wstring out_path{};\r\n        out_path.resize(static_cast<std::size_t>(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), nullptr, 0)));\r\n\r\n        if(!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), std::data(out_path), static_cast<int>(std::size(out_path))))\r\n        {\r\n            throw std::runtime_error{\"Failed to convert the path to wide.\"};\r\n        }\r\n\r\n        return out_path;\r\n    }\r\n\r\n    std::string get_error_message() const\r\n    {\r\n        return \"#\" + std::to_string(GetLastError());\r\n    }\r\n\r\nprivate:\r\n    native_handle_type m_handle{};\r\n};\r\n\r\nNES_INLINE_NAMESPACE_END\r\n\r\n}\r\n\r\n#elif defined(NES_POSIX_NAMED_SEMAPHORE)\r\n\r\nnamespace nes\r\n{\r\n\r\nNES_INLINE_NAMESPACE_BEGIN\r\n\r\ninline constexpr const char named_semaphore_root[] = \"/\";\r\n\r\nclass named_semaphore\r\n{\r\npublic:\r\n    using native_handle_type = sem_t*;\r\n\r\npublic:\r\n    explicit named_semaphore(const std::string& name, std::size_t initial_count = 0)\r\n    {\r\n        const auto native_name{named_semaphore_root + name};\r\n\r\n        m_handle = sem_open(std::data(native_name), O_CREAT, 0660, initial_count);\r\n        if(m_handle == SEM_FAILED)\r\n        {\r\n            throw std::runtime_error{\"Failed to create semaphore. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    ~named_semaphore()\r\n    {\r\n        sem_close(m_handle);\r\n    }\r\n\r\n    named_semaphore(const named_semaphore&) = delete;\r\n    named_semaphore& operator=(const named_semaphore&) = delete;\r\n    named_semaphore(named_semaphore&& other) noexcept = delete;\r\n    named_semaphore& operator=(named_semaphore&& other) noexcept = delete;\r\n\r\n    void acquire()\r\n    {\r\n        if(sem_wait(m_handle) == -1)\r\n        {\r\n            throw std::runtime_error{\"Failed to decrement semaphore count. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    bool try_acquire()\r\n    {\r\n        return !sem_trywait(m_handle);\r\n    }\r\n\r\n    void release()\r\n    {\r\n        if(sem_post(m_handle) == -1)\r\n        {\r\n            throw std::runtime_error{\"Failed to increment semaphore count. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\nprivate:\r\n    native_handle_type m_handle{};\r\n};\r\n\r\nclass timed_named_semaphore\r\n{\r\npublic:\r\n    using native_handle_type = sem_t*;\r\n\r\npublic:\r\n    explicit timed_named_semaphore(const std::string& name, std::size_t initial_count = 0)\r\n    {\r\n        const auto native_name{named_semaphore_root + name};\r\n\r\n        m_handle = sem_open(std::data(native_name), O_CREAT, 0660, initial_count);\r\n        if(m_handle == SEM_FAILED)\r\n        {\r\n            throw std::runtime_error{\"Failed to create semaphore. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    ~timed_named_semaphore()\r\n    {\r\n        sem_close(m_handle);\r\n    }\r\n\r\n    timed_named_semaphore(const timed_named_semaphore&) = delete;\r\n    timed_named_semaphore& operator=(const timed_named_semaphore&) = delete;\r\n    timed_named_semaphore(timed_named_semaphore&& other) noexcept = delete;\r\n    timed_named_semaphore& operator=(timed_named_semaphore&& other) noexcept = delete;\r\n\r\n    void acquire()\r\n    {\r\n        if(sem_wait(m_handle) == -1)\r\n        {\r\n            throw std::runtime_error{\"Failed to decrement semaphore count. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    bool try_acquire()\r\n    {\r\n        return !sem_trywait(m_handle);\r\n    }\r\n\r\n    template<class Rep, class Period>\r\n    bool try_lock_for(const std::chrono::duration<Rep, Period>& timeout)\r\n    {\r\n        return try_lock_until(std::chrono::system_clock::now() + timeout);\r\n    }\r\n\r\n    template<class Clock, class Duration>\r\n    bool try_lock_until(const std::chrono::time_point<Clock, Duration>& time_point)\r\n    {\r\n        const auto seconds{std::chrono::time_point_cast<std::chrono::seconds>(time_point)};\r\n        const auto nanoseconds{std::chrono::duration_cast<std::chrono::nanoseconds>(time_point - seconds)};\r\n\r\n        timespec time{};\r\n        time.tv_sec = static_cast<std::time_t>(seconds.time_since_epoch().count());\r\n        time.tv_nsec = static_cast<long>(nanoseconds.count());\r\n\r\n        return !sem_timedwait(m_handle, &time);\r\n    }\r\n\r\n    void release()\r\n    {\r\n        if(sem_post(m_handle) == -1)\r\n        {\r\n            throw std::runtime_error{\"Failed to increment semaphore count. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\nprivate:\r\n    native_handle_type m_handle{};\r\n};\r\n\r\nNES_INLINE_NAMESPACE_END\r\n\r\n}\r\n\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/nes/pipe.hpp",
    "content": "///////////////////////////////////////////////////////////\r\n/// Copyright 2019 Alexy Pellegrini\r\n///\r\n/// Permission is hereby granted, free of charge,\r\n/// to any person obtaining a copy of this software\r\n/// and associated documentation files (the \"Software\"),\r\n/// to deal in the Software without restriction,\r\n/// including without limitation the rights to use,\r\n/// copy, modify, merge, publish, distribute, sublicense,\r\n/// and/or sell copies of the Software, and to permit\r\n/// persons to whom the Software is furnished to do so,\r\n/// subject to the following conditions:\r\n///\r\n/// The above copyright notice and this permission notice\r\n/// shall be included in all copies or substantial portions\r\n/// of the Software.\r\n///\r\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\r\n/// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\r\n/// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\r\n/// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\r\n/// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r\n/// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r\n/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE\r\n/// OR OTHER DEALINGS IN THE SOFTWARE.\r\n///////////////////////////////////////////////////////////\r\n\r\n#ifndef NOT_ENOUGH_STANDARDS_PIPE\r\n#define NOT_ENOUGH_STANDARDS_PIPE\r\n\r\n#if defined(_WIN32)\r\n    #define NES_WIN32_PIPE\r\n    #ifndef NOMINMAX\r\n        #define NOMINMAX\r\n    #endif\r\n    #ifndef WIN32_LEAN_AND_MEAN\r\n        #define WIN32_LEAN_AND_MEAN\r\n    #endif\r\n    #include <Windows.h>\r\n#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))\r\n    #define NES_POSIX_PIPE\r\n    #include <unistd.h>\r\n    #include <fcntl.h>\r\n    #include <sys/types.h>\r\n    #include <sys/stat.h>\r\n#else\r\n    #error \"Not enough standards does not support this environment.\"\r\n#endif\r\n\r\n#ifdef NES_INLINE_NAMESPACE\r\n    #define NES_INLINE_NAMESPACE_BEGIN        \\\r\n        inline namespace NES_INLINE_NAMESPACE \\\r\n        {\r\n    #define NES_INLINE_NAMESPACE_END }\r\n#else\r\n    #define NES_INLINE_NAMESPACE_BEGIN\r\n    #define NES_INLINE_NAMESPACE_END\r\n#endif\r\n\r\n#include <vector>\r\n#include <algorithm>\r\n#include <streambuf>\r\n#include <istream>\r\n#include <ostream>\r\n#include <memory>\r\n#include <cassert>\r\n#include <utility>\r\n\r\n#if defined(NES_WIN32_PIPE)\r\n\r\nnamespace nes\r\n{\r\n\r\nNES_INLINE_NAMESPACE_BEGIN\r\n\r\ninline constexpr const char pipe_root[] = \"\\\\\\\\.\\\\pipe\\\\\";\r\n\r\ntemplate<typename CharT, typename Traits>\r\nclass basic_pipe_istream;\r\ntemplate<typename CharT, typename Traits>\r\nclass basic_pipe_ostream;\r\ntemplate<typename CharT = char, typename Traits = std::char_traits<CharT>>\r\nstd::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe();\r\n\r\ntemplate<typename CharT, typename Traits = std::char_traits<CharT>>\r\nclass basic_pipe_streambuf : public std::basic_streambuf<CharT, Traits>\r\n{\r\nprivate:\r\n    using parent_type = std::basic_streambuf<CharT, Traits>;\r\n\r\npublic:\r\n    using char_type = CharT;\r\n    using traits_type = Traits;\r\n    using int_type = typename Traits::int_type;\r\n    using pos_type = typename Traits::pos_type;\r\n    using off_type = typename Traits::off_type;\r\n\r\npublic:\r\n    static constexpr std::size_t buf_size{1024};\r\n\r\npublic:\r\n    basic_pipe_streambuf() = default;\r\n\r\n    explicit basic_pipe_streambuf(const std::string& name, std::ios_base::openmode mode)\r\n    {\r\n        open(name, mode);\r\n    }\r\n\r\n    virtual ~basic_pipe_streambuf()\r\n    {\r\n        close();\r\n    }\r\n\r\n    basic_pipe_streambuf(const basic_pipe_streambuf&) = delete;\r\n    basic_pipe_streambuf& operator=(const basic_pipe_streambuf&) = delete;\r\n\r\n    basic_pipe_streambuf(basic_pipe_streambuf&& other) noexcept\r\n    : parent_type{other}\r\n    , m_buffer{std::move(other.m_buffer)}\r\n    , m_handle{std::exchange(other.m_handle, INVALID_HANDLE_VALUE)}\r\n    , m_mode{std::exchange(other.m_mode, std::ios_base::openmode{})}\r\n    {\r\n    }\r\n\r\n    basic_pipe_streambuf& operator=(basic_pipe_streambuf&& other) noexcept\r\n    {\r\n        parent_type::operator=(other);\r\n        m_buffer = std::move(other.m_buffer);\r\n        m_handle = std::exchange(other.m_handle, m_handle);\r\n        m_mode = std::exchange(other.m_mode, m_mode);\r\n\r\n        return *this;\r\n    }\r\n\r\n    bool open(const std::string& name, std::ios_base::openmode mode)\r\n    {\r\n        assert(!((mode & std::ios_base::in) && (mode & std::ios_base::out)) && \"nes::basic_pipe_streambuf::open called with mode = std::ios_base::in | std::ios_base::out.\");\r\n\r\n        close();\r\n\r\n        const auto native_name{to_wide(pipe_root + name)};\r\n        DWORD native_mode{mode & std::ios_base::in ? GENERIC_READ : GENERIC_WRITE};\r\n\r\n        HANDLE handle = CreateFileW(std::data(native_name), native_mode, 0, nullptr, OPEN_EXISTING, 0, nullptr);\r\n        if(handle == INVALID_HANDLE_VALUE)\r\n        {\r\n            if(GetLastError() == ERROR_FILE_NOT_FOUND)\r\n            {\r\n                native_mode = mode & std::ios_base::in ? PIPE_ACCESS_INBOUND : PIPE_ACCESS_OUTBOUND;\r\n\r\n                handle = CreateNamedPipeW(std::data(native_name), native_mode, PIPE_READMODE_BYTE | PIPE_WAIT, 1, buf_size, buf_size, 0, nullptr);\r\n                if(handle == INVALID_HANDLE_VALUE)\r\n                {\r\n                    return false;\r\n                }\r\n\r\n                if(!ConnectNamedPipe(handle, nullptr))\r\n                {\r\n                    CloseHandle(handle);\r\n                    return false;\r\n                }\r\n            }\r\n        }\r\n\r\n        m_buffer.resize(buf_size);\r\n        parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);\r\n        m_handle = handle;\r\n        m_mode = mode;\r\n\r\n        return true;\r\n    }\r\n\r\n    bool is_open() const noexcept\r\n    {\r\n        return m_handle != INVALID_HANDLE_VALUE;\r\n    }\r\n\r\n    void close()\r\n    {\r\n        if(is_open())\r\n        {\r\n            sync();\r\n\r\n            m_mode = std::ios_base::openmode{};\r\n            CloseHandle(std::exchange(m_handle, INVALID_HANDLE_VALUE));\r\n            parent_type::setp(nullptr, nullptr);\r\n            parent_type::setg(nullptr, nullptr, nullptr);\r\n        }\r\n    }\r\n\r\nprivate:\r\n    friend class process;\r\n    friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();\r\n\r\n    basic_pipe_streambuf(HANDLE handle, std::ios_base::openmode mode)\r\n    : m_handle{handle}\r\n    , m_mode{mode}\r\n    {\r\n        m_buffer.resize(buf_size);\r\n        parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);\r\n    }\r\n\r\nprotected:\r\n    virtual int sync() override\r\n    {\r\n        if(m_mode & std::ios_base::out)\r\n        {\r\n            const std::ptrdiff_t count{parent_type::pptr() - parent_type::pbase()};\r\n\r\n            DWORD written{};\r\n            if(!WriteFile(m_handle, reinterpret_cast<const CHAR*>(std::data(m_buffer)), static_cast<DWORD>(count) * sizeof(char_type), &written, nullptr))\r\n            {\r\n                return -1;\r\n            }\r\n\r\n            parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);\r\n        }\r\n\r\n        return 0;\r\n    }\r\n\r\n    virtual int_type overflow(int_type c = traits_type::eof()) override\r\n    {\r\n        assert(m_mode & std::ios_base::out && \"Write operation on a read only pipe.\");\r\n\r\n        if(traits_type::eq_int_type(c, traits_type::eof()))\r\n        {\r\n            DWORD written{};\r\n            if(!WriteFile(m_handle, reinterpret_cast<const CHAR*>(std::data(m_buffer)), static_cast<DWORD>(buf_size) * sizeof(char_type), &written, nullptr))\r\n            {\r\n                return traits_type::eof();\r\n            }\r\n\r\n            parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);\r\n        }\r\n        else\r\n        {\r\n            *parent_type::pptr() = traits_type::to_char_type(c);\r\n            parent_type::pbump(1);\r\n        }\r\n\r\n        return traits_type::not_eof(c);\r\n    }\r\n\r\n    virtual std::streamsize xsputn(const char_type* s, std::streamsize count) override\r\n    {\r\n        assert(m_mode & std::ios_base::out && \"Write operation on a read only pipe.\");\r\n        sync(); // empty putarea before performing a write\r\n\r\n        DWORD written{};\r\n        if(!WriteFile(m_handle, reinterpret_cast<const CHAR*>(s), static_cast<DWORD>(count) * sizeof(char_type), &written, nullptr))\r\n        {\r\n            return 0;\r\n        }\r\n\r\n        return static_cast<std::streamsize>(written);\r\n    }\r\n\r\n    virtual int_type underflow() override\r\n    {\r\n        assert(m_mode & std::ios_base::in && \"Read operation on a write only pipe.\");\r\n\r\n        if(parent_type::gptr() == parent_type::egptr())\r\n        {\r\n            DWORD read{};\r\n            if(!ReadFile(m_handle, reinterpret_cast<CHAR*>(std::data(m_buffer)), static_cast<DWORD>(buf_size * sizeof(char_type)), &read, nullptr) || read == 0)\r\n            {\r\n                return traits_type::eof();\r\n            }\r\n\r\n            parent_type::setg(std::data(m_buffer), std::data(m_buffer), std::data(m_buffer) + (read / sizeof(char_type)));\r\n        }\r\n\r\n        return traits_type::to_int_type(*parent_type::gptr());\r\n    }\r\n\r\n    virtual std::streamsize xsgetn(char_type* s, std::streamsize count) override\r\n    {\r\n        assert(m_mode & std::ios_base::in && \"Read operation on a write only pipe.\");\r\n\r\n        // to support mixing formatted and unformatted input, we have to flush the get buffer\r\n        const std::ptrdiff_t available = parent_type::egptr() - parent_type::gptr();\r\n        if(available > 0)\r\n        {\r\n            std::copy_n(parent_type::gptr(), (std::min)(available, count), s);\r\n            // if we still have buffered input update gptr\r\n            if(available > count)\r\n            {\r\n                parent_type::setg(parent_type::eback(), parent_type::gptr() + count, parent_type::egptr());\r\n                return count;\r\n            }\r\n\r\n            // resets get buffer, this will force an underflow on next formatted input\r\n            parent_type::setg(nullptr, nullptr, nullptr);\r\n            if(count == available)\r\n            {\r\n                return count;\r\n            }\r\n\r\n            // perform a read to fulfill the requested count if possible\r\n            count -= available;\r\n            s += available;\r\n        }\r\n\r\n        DWORD read{};\r\n        if(!ReadFile(m_handle, reinterpret_cast<CHAR*>(s), static_cast<DWORD>(count) * sizeof(char_type), &read, nullptr))\r\n        {\r\n            return 0;\r\n        }\r\n\r\n        return static_cast<std::streamsize>(read / sizeof(char_type)) + available;\r\n    }\r\n\r\nprivate:\r\n    std::wstring to_wide(std::string path)\r\n    {\r\n        assert(std::size(path) < 0x7FFFFFFFu && \"Wrong path.\");\r\n\r\n        if(std::empty(path))\r\n        {\r\n            return {};\r\n        }\r\n\r\n        std::transform(std::begin(path), std::end(path), std::begin(path), [](char c)\r\n        {\r\n            return c == '/' ? '\\\\' : c;\r\n        });\r\n\r\n        std::wstring out_path{};\r\n        out_path.resize(static_cast<std::size_t>(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), nullptr, 0)));\r\n\r\n        if(!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), std::data(out_path), static_cast<int>(std::size(out_path))))\r\n        {\r\n            throw std::runtime_error{\"Failed to convert the path to wide.\"};\r\n        }\r\n\r\n        return out_path;\r\n    }\r\n\r\nprivate:\r\n    std::vector<CharT> m_buffer{};\r\n    HANDLE m_handle{INVALID_HANDLE_VALUE};\r\n    std::ios_base::openmode m_mode{};\r\n};\r\n\r\ntemplate<typename CharT, typename Traits = std::char_traits<CharT>>\r\nclass basic_pipe_istream : public std::basic_istream<CharT, Traits>\r\n{\r\nprivate:\r\n    using parent_type = std::basic_istream<CharT, Traits>;\r\n\r\npublic:\r\n    using char_type = CharT;\r\n    using traits_type = Traits;\r\n    using int_type = typename Traits::int_type;\r\n    using pos_type = typename Traits::pos_type;\r\n    using off_type = typename Traits::off_type;\r\n\r\npublic:\r\n    basic_pipe_istream() = default;\r\n\r\n    explicit basic_pipe_istream(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)\r\n    : parent_type{nullptr}\r\n    {\r\n        parent_type::rdbuf(m_buffer.get());\r\n        open(name, mode);\r\n    }\r\n\r\n    virtual ~basic_pipe_istream() = default;\r\n\r\n    basic_pipe_istream(const basic_pipe_istream&) = delete;\r\n    basic_pipe_istream& operator=(const basic_pipe_istream&) = delete;\r\n\r\n    basic_pipe_istream(basic_pipe_istream&& other) noexcept\r\n    : parent_type{std::move(other)}\r\n    {\r\n        std::swap(m_buffer, other.m_buffer);\r\n        parent_type::rdbuf(m_buffer.get());\r\n    }\r\n\r\n    basic_pipe_istream& operator=(basic_pipe_istream&& other) noexcept\r\n    {\r\n        parent_type::operator=(std::move(other));\r\n        std::swap(m_buffer, other.m_buffer);\r\n\r\n        parent_type::rdbuf(m_buffer.get());\r\n\r\n        return *this;\r\n    }\r\n\r\n    void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)\r\n    {\r\n        m_buffer->open(name, mode);\r\n        parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);\r\n    }\r\n\r\n    bool is_open() const noexcept\r\n    {\r\n        return m_buffer->is_open();\r\n    }\r\n\r\n    void close()\r\n    {\r\n        m_buffer->close();\r\n    }\r\n\r\n    basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept\r\n    {\r\n        return m_buffer.get();\r\n    }\r\n\r\nprivate:\r\n    friend class process;\r\n    friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();\r\n\r\n    basic_pipe_istream(basic_pipe_streambuf<char_type, traits_type> buffer)\r\n    : parent_type{nullptr}\r\n    , m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}\r\n    {\r\n        parent_type::rdbuf(m_buffer.get());\r\n    }\r\n\r\nprivate:\r\n    std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};\r\n};\r\n\r\ntemplate<typename CharT, typename Traits = std::char_traits<CharT>>\r\nclass basic_pipe_ostream : public std::basic_ostream<CharT, Traits>\r\n{\r\nprivate:\r\n    using parent_type = std::basic_ostream<CharT, Traits>;\r\n\r\npublic:\r\n    using char_type = CharT;\r\n    using traits_type = Traits;\r\n    using int_type = typename Traits::int_type;\r\n    using pos_type = typename Traits::pos_type;\r\n    using off_type = typename Traits::off_type;\r\n\r\npublic:\r\n    basic_pipe_ostream() = default;\r\n\r\n    explicit basic_pipe_ostream(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)\r\n    : parent_type{nullptr}\r\n    {\r\n        parent_type::rdbuf(m_buffer.get());\r\n        open(name, mode);\r\n    }\r\n\r\n    virtual ~basic_pipe_ostream() = default;\r\n\r\n    basic_pipe_ostream(const basic_pipe_ostream&) = delete;\r\n    basic_pipe_ostream& operator=(const basic_pipe_ostream&) = delete;\r\n\r\n    basic_pipe_ostream(basic_pipe_ostream&& other) noexcept\r\n    : parent_type{std::move(other)}\r\n    {\r\n        std::swap(m_buffer, other.m_buffer);\r\n        parent_type::rdbuf(m_buffer.get());\r\n    }\r\n\r\n    basic_pipe_ostream& operator=(basic_pipe_ostream&& other) noexcept\r\n    {\r\n        parent_type::operator=(std::move(other));\r\n        std::swap(m_buffer, other.m_buffer);\r\n\r\n        parent_type::rdbuf(m_buffer.get());\r\n\r\n        return *this;\r\n    }\r\n\r\n    void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)\r\n    {\r\n        m_buffer->open(name, mode);\r\n        parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);\r\n    }\r\n\r\n    bool is_open() const noexcept\r\n    {\r\n        return m_buffer->is_open();\r\n    }\r\n\r\n    void close()\r\n    {\r\n        m_buffer->close();\r\n    }\r\n\r\n    basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept\r\n    {\r\n        return m_buffer.get();\r\n    }\r\n\r\nprivate:\r\n    friend class process;\r\n    friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();\r\n\r\n    basic_pipe_ostream(basic_pipe_streambuf<char_type, traits_type> buffer)\r\n    : parent_type{nullptr}\r\n    , m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}\r\n    {\r\n        parent_type::rdbuf(m_buffer.get());\r\n    }\r\n\r\nprivate:\r\n    std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};\r\n};\r\n\r\ntemplate<typename CharT, typename Traits>\r\nstd::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe()\r\n{\r\n    HANDLE input{};\r\n    HANDLE output{};\r\n\r\n    if(!CreatePipe(&input, &output, nullptr, 0))\r\n    {\r\n        throw std::runtime_error{\"Failed to create pipe\"};\r\n    }\r\n\r\n    // clang-format off\r\n    return std::make_pair(\r\n        basic_pipe_istream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{input, std::ios_base::in}},\r\n        basic_pipe_ostream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{output, std::ios_base::out}});\r\n    // clang-format on\r\n}\r\n\r\nusing pipe_streambuf = basic_pipe_streambuf<char>;\r\nusing pipe_istream = basic_pipe_istream<char>;\r\nusing pipe_ostream = basic_pipe_ostream<char>;\r\n\r\nNES_INLINE_NAMESPACE_END\r\n\r\n}\r\n\r\n#elif defined(NES_POSIX_PIPE)\r\n\r\nnamespace nes\r\n{\r\n\r\nNES_INLINE_NAMESPACE_BEGIN\r\n\r\ninline constexpr const char pipe_root[] = \"/tmp/\";\r\n\r\ntemplate<typename CharT, typename Traits>\r\nclass basic_pipe_istream;\r\ntemplate<typename CharT, typename Traits>\r\nclass basic_pipe_ostream;\r\ntemplate<typename CharT = char, typename Traits = std::char_traits<CharT>>\r\nstd::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe();\r\n\r\ntemplate<typename CharT, typename Traits = std::char_traits<CharT>>\r\nclass basic_pipe_streambuf : public std::basic_streambuf<CharT, Traits>\r\n{\r\nprivate:\r\n    using parent_type = std::basic_streambuf<CharT, Traits>;\r\n\r\npublic:\r\n    using char_type = CharT;\r\n    using traits_type = Traits;\r\n    using int_type = typename Traits::int_type;\r\n    using pos_type = typename Traits::pos_type;\r\n    using off_type = typename Traits::off_type;\r\n\r\npublic:\r\n    static constexpr std::size_t buf_size{1024};\r\n\r\npublic:\r\n    basic_pipe_streambuf() = default;\r\n\r\n    explicit basic_pipe_streambuf(const std::string& name, std::ios_base::openmode mode)\r\n    : parent_type{nullptr}\r\n    {\r\n        open(name, mode);\r\n    }\r\n\r\n    virtual ~basic_pipe_streambuf()\r\n    {\r\n        close();\r\n    }\r\n\r\n    basic_pipe_streambuf(const basic_pipe_streambuf&) = delete;\r\n    basic_pipe_streambuf& operator=(const basic_pipe_streambuf&) = delete;\r\n\r\n    basic_pipe_streambuf(basic_pipe_streambuf&& other) noexcept\r\n    : parent_type{std::move(other)}\r\n    , m_buffer{std::move(other.m_buffer)}\r\n    , m_handle{std::exchange(other.m_handle, 0)}\r\n    , m_mode{std::exchange(other.m_mode, std::ios_base::openmode{})}\r\n    {\r\n    }\r\n\r\n    basic_pipe_streambuf& operator=(basic_pipe_streambuf&& other) noexcept\r\n    {\r\n        parent_type::operator=(std::move(other));\r\n        m_buffer = std::move(other.m_buffer);\r\n        m_handle = std::exchange(other.m_handle, m_handle);\r\n        m_mode = std::exchange(other.m_mode, m_mode);\r\n\r\n        return *this;\r\n    }\r\n\r\n    bool open(const std::string& name, std::ios_base::openmode mode)\r\n    {\r\n        assert(!((mode & std::ios_base::in) && (mode & std::ios_base::out)) && \"nes::basic_pipe_streambuf::open called with mode = std::ios_base::in | std::ios_base::out.\");\r\n\r\n        close();\r\n\r\n        const auto native_name{pipe_root + name};\r\n        if(mkfifo(std::data(native_name), 0660) != 0 && errno != EEXIST)\r\n        {\r\n            return false;\r\n        }\r\n\r\n        const int native_mode{mode & std::ios_base::in ? O_RDONLY : O_WRONLY};\r\n        int handle = ::open(std::data(native_name), native_mode);\r\n        if(handle < 0)\r\n        {\r\n            return false;\r\n        }\r\n\r\n        m_buffer.resize(buf_size);\r\n        parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);\r\n        m_handle = handle;\r\n        m_mode = mode;\r\n\r\n        return true;\r\n    }\r\n\r\n    bool is_open() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\n    void close()\r\n    {\r\n        if(is_open())\r\n        {\r\n            sync();\r\n\r\n            m_mode = std::ios_base::openmode{};\r\n            ::close(std::exchange(m_handle, 0));\r\n            parent_type::setp(nullptr, nullptr);\r\n            parent_type::setg(nullptr, nullptr, nullptr);\r\n        }\r\n    }\r\n\r\nprivate:\r\n    friend class process;\r\n    friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();\r\n\r\n    basic_pipe_streambuf(int handle, std::ios_base::openmode mode)\r\n    : m_handle{handle}\r\n    , m_mode{mode}\r\n    {\r\n        m_buffer.resize(buf_size);\r\n        parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);\r\n    }\r\n\r\nprotected:\r\n    virtual int sync() override\r\n    {\r\n        if(m_mode & std::ios_base::out)\r\n        {\r\n            const std::ptrdiff_t count{parent_type::pptr() - parent_type::pbase()};\r\n\r\n            if(write(m_handle, reinterpret_cast<const char*>(std::data(m_buffer)), count * sizeof(char_type)) < 0)\r\n            {\r\n                return -1;\r\n            }\r\n\r\n            parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);\r\n        }\r\n\r\n        return 0;\r\n    }\r\n\r\n    virtual int_type overflow(int_type c = traits_type::eof()) override\r\n    {\r\n        assert(m_mode & std::ios_base::out && \"Write operation on a read only pipe.\");\r\n\r\n        if(traits_type::eq_int_type(c, traits_type::eof()))\r\n        {\r\n            if(write(m_handle, reinterpret_cast<const char*>(std::data(m_buffer)), std::size(m_buffer) * sizeof(char_type)))\r\n            {\r\n                return traits_type::eof();\r\n            }\r\n\r\n            parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);\r\n        }\r\n        else\r\n        {\r\n            *parent_type::pptr() = traits_type::to_char_type(c);\r\n            parent_type::pbump(1);\r\n        }\r\n\r\n        return traits_type::not_eof(c);\r\n    }\r\n\r\n    virtual std::streamsize xsputn(const char_type* s, std::streamsize count) override\r\n    {\r\n        assert(m_mode & std::ios_base::out && \"Write operation on a read only pipe.\");\r\n        sync(); // empty putarea before performing a write\r\n\r\n        const auto written = write(m_handle, reinterpret_cast<const char*>(s), count * sizeof(char_type));\r\n        if(written < 0)\r\n        {\r\n            return 0;\r\n        }\r\n\r\n        return static_cast<std::streamsize>(written);\r\n    }\r\n\r\n    virtual int_type underflow() override\r\n    {\r\n        assert(m_mode & std::ios_base::in && \"Read operation on a write only pipe.\");\r\n\r\n        if(parent_type::gptr() == parent_type::egptr())\r\n        {\r\n            const auto _read = read(m_handle, reinterpret_cast<char*>(std::data(m_buffer)), buf_size * sizeof(char_type));\r\n            if(_read <= 0)\r\n            {\r\n                return traits_type::eof();\r\n            }\r\n\r\n            parent_type::setg(std::data(m_buffer), std::data(m_buffer), std::data(m_buffer) + (_read / sizeof(char_type)));\r\n        }\r\n\r\n        return traits_type::to_int_type(*parent_type::gptr());\r\n    }\r\n\r\n    virtual std::streamsize xsgetn(char_type* s, std::streamsize count) override\r\n    {\r\n        assert(m_mode & std::ios_base::in && \"Read operation on a write only pipe.\");\r\n\r\n        // to support mixing formatted and unformatted input, we have to flush the get buffer\r\n        const std::ptrdiff_t available = parent_type::egptr() - parent_type::gptr();\r\n        if(available > 0)\r\n        {\r\n            std::copy_n(parent_type::gptr(), (std::min)(available, count), s);\r\n            // if we still have buffered input update gptr\r\n            if(available > count)\r\n            {\r\n                parent_type::setg(parent_type::eback(), parent_type::gptr() + count, parent_type::egptr());\r\n                return count;\r\n            }\r\n\r\n            // resets get buffer, this will force an underflow on next formatted input\r\n            parent_type::setg(nullptr, nullptr, nullptr);\r\n            if(count == available)\r\n            {\r\n                return count;\r\n            }\r\n\r\n            // perform a read to fulfill the requested count if possible\r\n            count -= available;\r\n            s += available;\r\n        }\r\n\r\n        const auto _read = read(m_handle, reinterpret_cast<char*>(s), count * sizeof(char_type));\r\n        if(_read < 0)\r\n        {\r\n            return 0;\r\n        }\r\n\r\n        return static_cast<std::streamsize>(_read / sizeof(char_type)) + available;\r\n    }\r\n\r\nprivate:\r\n    std::vector<CharT> m_buffer{};\r\n    int m_handle{};\r\n    std::ios_base::openmode m_mode{};\r\n};\r\n\r\ntemplate<typename CharT, typename Traits = std::char_traits<CharT>>\r\nclass basic_pipe_istream : public std::basic_istream<CharT, Traits>\r\n{\r\nprivate:\r\n    using parent_type = std::basic_istream<CharT, Traits>;\r\n\r\npublic:\r\n    using char_type = CharT;\r\n    using traits_type = Traits;\r\n    using int_type = typename Traits::int_type;\r\n    using pos_type = typename Traits::pos_type;\r\n    using off_type = typename Traits::off_type;\r\n\r\npublic:\r\n    basic_pipe_istream() = default;\r\n\r\n    explicit basic_pipe_istream(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)\r\n    : parent_type{nullptr}\r\n    {\r\n        parent_type::rdbuf(m_buffer.get());\r\n        open(name, mode);\r\n    }\r\n\r\n    virtual ~basic_pipe_istream() = default;\r\n\r\n    basic_pipe_istream(const basic_pipe_istream&) = delete;\r\n    basic_pipe_istream& operator=(const basic_pipe_istream&) = delete;\r\n\r\n    basic_pipe_istream(basic_pipe_istream&& other) noexcept\r\n    : parent_type{std::move(other)}\r\n    {\r\n        std::swap(m_buffer, other.m_buffer);\r\n        parent_type::rdbuf(m_buffer.get());\r\n    }\r\n\r\n    basic_pipe_istream& operator=(basic_pipe_istream&& other) noexcept\r\n    {\r\n        parent_type::operator=(std::move(other));\r\n        std::swap(m_buffer, other.m_buffer);\r\n\r\n        parent_type::rdbuf(m_buffer.get());\r\n\r\n        return *this;\r\n    }\r\n\r\n    void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)\r\n    {\r\n        m_buffer->open(name, mode);\r\n        parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);\r\n    }\r\n\r\n    bool is_open() const noexcept\r\n    {\r\n        return m_buffer->is_open();\r\n    }\r\n\r\n    void close()\r\n    {\r\n        m_buffer->close();\r\n    }\r\n\r\n    basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept\r\n    {\r\n        return m_buffer.get();\r\n    }\r\n\r\nprivate:\r\n    friend class process;\r\n    friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();\r\n\r\n    basic_pipe_istream(basic_pipe_streambuf<char_type, traits_type> buffer)\r\n    : parent_type{nullptr}\r\n    , m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}\r\n    {\r\n        parent_type::rdbuf(m_buffer.get());\r\n    }\r\n\r\nprivate:\r\n    std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};\r\n};\r\n\r\ntemplate<typename CharT, typename Traits = std::char_traits<CharT>>\r\nclass basic_pipe_ostream : public std::basic_ostream<CharT, Traits>\r\n{\r\nprivate:\r\n    using parent_type = std::basic_ostream<CharT, Traits>;\r\n\r\npublic:\r\n    using char_type = CharT;\r\n    using traits_type = Traits;\r\n    using int_type = typename Traits::int_type;\r\n    using pos_type = typename Traits::pos_type;\r\n    using off_type = typename Traits::off_type;\r\n\r\npublic:\r\n    basic_pipe_ostream() = default;\r\n\r\n    explicit basic_pipe_ostream(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)\r\n    : parent_type{nullptr}\r\n    {\r\n        parent_type::rdbuf(m_buffer.get());\r\n        open(name, mode);\r\n    }\r\n\r\n    virtual ~basic_pipe_ostream() = default;\r\n\r\n    basic_pipe_ostream(const basic_pipe_ostream&) = delete;\r\n    basic_pipe_ostream& operator=(const basic_pipe_ostream&) = delete;\r\n\r\n    basic_pipe_ostream(basic_pipe_ostream&& other) noexcept\r\n    : parent_type{std::move(other)}\r\n    {\r\n        std::swap(m_buffer, other.m_buffer);\r\n        parent_type::rdbuf(m_buffer.get());\r\n    }\r\n\r\n    basic_pipe_ostream& operator=(basic_pipe_ostream&& other) noexcept\r\n    {\r\n        parent_type::operator=(std::move(other));\r\n        std::swap(m_buffer, other.m_buffer);\r\n\r\n        parent_type::rdbuf(m_buffer.get());\r\n\r\n        return *this;\r\n    }\r\n\r\n    void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)\r\n    {\r\n        m_buffer->open(name, mode);\r\n        parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);\r\n    }\r\n\r\n    bool is_open() const noexcept\r\n    {\r\n        return m_buffer->is_open();\r\n    }\r\n\r\n    void close()\r\n    {\r\n        m_buffer->close();\r\n    }\r\n\r\n    basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept\r\n    {\r\n        return m_buffer.get();\r\n    }\r\n\r\nprivate:\r\n    friend class process;\r\n    friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();\r\n\r\n    basic_pipe_ostream(basic_pipe_streambuf<char_type, traits_type> buffer)\r\n    : parent_type{nullptr}\r\n    , m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}\r\n    {\r\n        parent_type::rdbuf(m_buffer.get());\r\n    }\r\n\r\nprivate:\r\n    std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};\r\n};\r\n\r\ntemplate<typename CharT, typename Traits>\r\nstd::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe()\r\n{\r\n    int fd[2];\r\n\r\n    if(pipe(fd))\r\n    {\r\n        throw std::runtime_error{\"Failed to create pipe\"};\r\n    }\r\n\r\n    // clang-format off\r\n    return std::make_pair(\r\n        basic_pipe_istream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{fd[0], std::ios_base::in}},\r\n        basic_pipe_ostream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{fd[1], std::ios_base::out}});\r\n    // clang-format on\r\n}\r\n\r\nusing pipe_streambuf = basic_pipe_streambuf<char>;\r\nusing pipe_istream = basic_pipe_istream<char>;\r\nusing pipe_ostream = basic_pipe_ostream<char>;\r\n\r\nNES_INLINE_NAMESPACE_END\r\n\r\n}\r\n\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/nes/process.hpp",
    "content": "///////////////////////////////////////////////////////////\r\n/// Copyright 2019 Alexy Pellegrini\r\n///\r\n/// Permission is hereby granted, free of charge,\r\n/// to any person obtaining a copy of this software\r\n/// and associated documentation files (the \"Software\"),\r\n/// to deal in the Software without restriction,\r\n/// including without limitation the rights to use,\r\n/// copy, modify, merge, publish, distribute, sublicense,\r\n/// and/or sell copies of the Software, and to permit\r\n/// persons to whom the Software is furnished to do so,\r\n/// subject to the following conditions:\r\n///\r\n/// The above copyright notice and this permission notice\r\n/// shall be included in all copies or substantial portions\r\n/// of the Software.\r\n///\r\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\r\n/// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\r\n/// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\r\n/// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\r\n/// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r\n/// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r\n/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE\r\n/// OR OTHER DEALINGS IN THE SOFTWARE.\r\n///////////////////////////////////////////////////////////\r\n\r\n#ifndef NOT_ENOUGH_STANDARDS_PROCESS\r\n#define NOT_ENOUGH_STANDARDS_PROCESS\r\n\r\n#if defined(_WIN32)\r\n    #define NES_WIN32_PROCESS\r\n    #ifndef NOMINMAX\r\n        #define NOMINMAX\r\n    #endif\r\n    #ifndef WIN32_LEAN_AND_MEAN\r\n        #define WIN32_LEAN_AND_MEAN\r\n    #endif\r\n    #include <Windows.h>\r\n#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))\r\n    #define NES_POSIX_PROCESS\r\n    #include <unistd.h>\r\n    #include <sys/wait.h>\r\n    #include <signal.h>\r\n    #include <string.h>\r\n    #include <limits.h>\r\n#else\r\n    #error \"Not enough standards does not support this environment.\"\r\n#endif\r\n\r\n#if __has_include(\"pipe.hpp\")\r\n    #define NES_PROCESS_PIPE_EXTENSION\r\n    #include \"pipe.hpp\"\r\n#endif\r\n\r\n#ifdef NES_INLINE_NAMESPACE\r\n    #define NES_INLINE_NAMESPACE_BEGIN        \\\r\n        inline namespace NES_INLINE_NAMESPACE \\\r\n        {\r\n    #define NES_INLINE_NAMESPACE_END }\r\n#else\r\n    #define NES_INLINE_NAMESPACE_BEGIN\r\n    #define NES_INLINE_NAMESPACE_END\r\n#endif\r\n\r\n#include <iostream>\r\n#include <string>\r\n#include <vector>\r\n#include <algorithm>\r\n#include <memory>\r\n#include <functional>\r\n#include <cassert>\r\n#include <utility>\r\n\r\n#if defined(NES_WIN32_PROCESS)\r\n\r\nnamespace nes\r\n{\r\n\r\nNES_INLINE_NAMESPACE_BEGIN\r\n\r\nclass process;\r\n\r\nnamespace impl\r\n{\r\n\r\nenum class id_t : DWORD\r\n{\r\n};\r\n\r\nconstexpr bool operator==(id_t lhs, id_t rhs) noexcept\r\n{\r\n    return static_cast<DWORD>(lhs) == static_cast<DWORD>(rhs);\r\n}\r\n\r\nconstexpr bool operator!=(id_t lhs, id_t rhs) noexcept\r\n{\r\n    return static_cast<DWORD>(lhs) != static_cast<DWORD>(rhs);\r\n}\r\n\r\nconstexpr bool operator<(id_t lhs, id_t rhs) noexcept\r\n{\r\n    return static_cast<DWORD>(lhs) < static_cast<DWORD>(rhs);\r\n}\r\n\r\nconstexpr bool operator<=(id_t lhs, id_t rhs) noexcept\r\n{\r\n    return static_cast<DWORD>(lhs) <= static_cast<DWORD>(rhs);\r\n}\r\n\r\nconstexpr bool operator>(id_t lhs, id_t rhs) noexcept\r\n{\r\n    return static_cast<DWORD>(lhs) > static_cast<DWORD>(rhs);\r\n}\r\n\r\nconstexpr bool operator>=(id_t lhs, id_t rhs) noexcept\r\n{\r\n    return static_cast<DWORD>(lhs) >= static_cast<DWORD>(rhs);\r\n}\r\n\r\nstruct auto_handle\r\n{\r\npublic:\r\n    constexpr auto_handle() = default;\r\n    auto_handle(HANDLE h)\r\n    : m_handle{h}\r\n    {\r\n    }\r\n\r\n    ~auto_handle()\r\n    {\r\n        if(m_handle != INVALID_HANDLE_VALUE)\r\n        {\r\n            CloseHandle(m_handle);\r\n        }\r\n    }\r\n\r\n    auto_handle(const auto_handle&) = delete;\r\n    auto_handle& operator=(const auto_handle&) = delete;\r\n\r\n    auto_handle(auto_handle&& other) noexcept\r\n    : m_handle{std::exchange(other.m_handle, INVALID_HANDLE_VALUE)}\r\n    {\r\n    }\r\n\r\n    auto_handle& operator=(auto_handle&& other) noexcept\r\n    {\r\n        m_handle = std::exchange(other.m_handle, m_handle);\r\n\r\n        return *this;\r\n    }\r\n\r\n    HANDLE release() noexcept\r\n    {\r\n        return std::exchange(m_handle, INVALID_HANDLE_VALUE);\r\n    }\r\n\r\n    operator HANDLE() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\n    HANDLE* operator&() noexcept\r\n    {\r\n        return &m_handle;\r\n    }\r\n\r\n    const HANDLE* operator&() const noexcept\r\n    {\r\n        return &m_handle;\r\n    }\r\n\r\n    operator bool() const noexcept\r\n    {\r\n        return m_handle != INVALID_HANDLE_VALUE;\r\n    }\r\n\r\nprivate:\r\n    HANDLE m_handle{INVALID_HANDLE_VALUE};\r\n};\r\n\r\n}\r\n\r\nenum class process_options : std::uint32_t\r\n{\r\n    none = 0x00,\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n    grab_stdout = 0x10,\r\n    grab_stderr = 0x20,\r\n    grab_stdin = 0x40\r\n    #endif\r\n};\r\n\r\nconstexpr process_options operator&(process_options left, process_options right) noexcept\r\n{\r\n    return static_cast<process_options>(static_cast<std::uint32_t>(left) & static_cast<std::uint32_t>(right));\r\n}\r\n\r\nconstexpr process_options& operator&=(process_options& left, process_options right) noexcept\r\n{\r\n    left = left & right;\r\n    return left;\r\n}\r\n\r\nconstexpr process_options operator|(process_options left, process_options right) noexcept\r\n{\r\n    return static_cast<process_options>(static_cast<std::uint32_t>(left) | static_cast<std::uint32_t>(right));\r\n}\r\n\r\nconstexpr process_options& operator|=(process_options& left, process_options right) noexcept\r\n{\r\n    left = left | right;\r\n    return left;\r\n}\r\n\r\nconstexpr process_options operator^(process_options left, process_options right) noexcept\r\n{\r\n    return static_cast<process_options>(static_cast<std::uint32_t>(left) ^ static_cast<std::uint32_t>(right));\r\n}\r\n\r\nconstexpr process_options& operator^=(process_options& left, process_options right) noexcept\r\n{\r\n    left = left ^ right;\r\n    return left;\r\n}\r\n\r\nconstexpr process_options operator~(process_options value) noexcept\r\n{\r\n    return static_cast<process_options>(~static_cast<std::uint32_t>(value));\r\n}\r\n\r\nclass process\r\n{\r\npublic:\r\n    using native_handle_type = HANDLE;\r\n    using return_code_type = DWORD;\r\n    using id = impl::id_t;\r\n\r\npublic:\r\n    constexpr process() noexcept = default;\r\n\r\n    explicit process(const std::string& path, const std::string& working_directory)\r\n    : process{path, {}, working_directory, {}}\r\n    {\r\n    }\r\n\r\n    explicit process(const std::string& path, process_options options)\r\n    : process{path, {}, {}, options}\r\n    {\r\n    }\r\n\r\n    explicit process(const std::string& path, const std::vector<std::string>& args, process_options options)\r\n    : process{path, args, {}, options}\r\n    {\r\n    }\r\n\r\n    explicit process(const std::string& path, const std::string& working_directory, process_options options)\r\n    : process{path, {}, working_directory, options}\r\n    {\r\n    }\r\n\r\n    explicit process(const std::string& path, std::vector<std::string> args = std::vector<std::string>{}, const std::string& working_directory = std::string{}, process_options options [[maybe_unused]] = process_options{})\r\n    {\r\n        assert(!std::empty(path) && \"nes::process::process called with empty path.\");\r\n\r\n        SECURITY_ATTRIBUTES security_attributes{};\r\n        security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);\r\n        security_attributes.bInheritHandle = TRUE;\r\n        security_attributes.lpSecurityDescriptor = nullptr;\r\n\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n        impl::auto_handle stdin_rd{};\r\n        impl::auto_handle stdout_rd{};\r\n        impl::auto_handle stderr_rd{};\r\n        impl::auto_handle stdin_wr{};\r\n        impl::auto_handle stdout_wr{};\r\n        impl::auto_handle stderr_wr{};\r\n\r\n        if(static_cast<bool>(options & process_options::grab_stdin))\r\n        {\r\n            if(!CreatePipe(&stdin_rd, &stdin_wr, &security_attributes, 0) || !SetHandleInformation(stdin_wr, HANDLE_FLAG_INHERIT, 0))\r\n            {\r\n                throw std::runtime_error{\"Failed to create stdin pipe. \" + get_error_message()};\r\n            }\r\n        }\r\n\r\n        if(static_cast<bool>(options & process_options::grab_stdout))\r\n        {\r\n            if(!CreatePipe(&stdout_rd, &stdout_wr, &security_attributes, 0) || !SetHandleInformation(stdout_rd, HANDLE_FLAG_INHERIT, 0))\r\n            {\r\n                throw std::runtime_error{\"Failed to create stdout pipe. \" + get_error_message()};\r\n            }\r\n        }\r\n\r\n        if(static_cast<bool>(options & process_options::grab_stderr))\r\n        {\r\n            if(!CreatePipe(&stderr_rd, &stderr_wr, &security_attributes, 0) || !SetHandleInformation(stderr_rd, HANDLE_FLAG_INHERIT, 0))\r\n            {\r\n                throw std::runtime_error{\"Failed to create stderr pipe. \" + get_error_message()};\r\n            }\r\n        }\r\n    #endif\r\n\r\n        args.insert(std::begin(args), path);\r\n\r\n        auto format_arg = [](const std::wstring& arg) -> std::wstring\r\n        {\r\n            if(arg.find_first_of(L\" \\t\\n\\v\\\"\") == std::wstring::npos)\r\n            {\r\n                return arg;\r\n            }\r\n\r\n            std::wstring out{L\"\\\"\"};\r\n            for(auto it = std::cbegin(arg); it != std::cend(arg); ++it)\r\n            {\r\n                if(*it == L'\\\\')\r\n                {\r\n                    std::size_t count{1};\r\n                    while(++it != std::cend(arg) && *it == L'\\\\')\r\n                    {\r\n                        ++count;\r\n                    }\r\n\r\n                    if(it == std::cend(arg))\r\n                    {\r\n                        out.append(count * 2, L'\\\\');\r\n                        break;\r\n                    }\r\n                    else if(*it == L'\\\"')\r\n                    {\r\n                        out.append(count * 2 + 1, L'\\\\');\r\n                        out.push_back(L'\\\"');\r\n                    }\r\n                    else\r\n                    {\r\n                        out.append(count, L'\\\\');\r\n                        out.push_back(*it);\r\n                    }\r\n                }\r\n                else if(*it == L'\\\"')\r\n                {\r\n                    out.push_back(L'\\\\');\r\n                    out.push_back(*it);\r\n                }\r\n                else\r\n                {\r\n                    out.push_back(*it);\r\n                }\r\n            }\r\n            out.push_back(L'\\\"');\r\n\r\n            return out;\r\n        };\r\n\r\n        std::wstring args_str{};\r\n        for(auto&& arg : args)\r\n        {\r\n            args_str += format_arg(to_wide(arg)) + L\" \";\r\n        }\r\n\r\n        const std::wstring native_working_directory{to_wide(working_directory)};\r\n        const std::wstring native_path{to_wide(path)};\r\n\r\n        STARTUPINFOW startup_info{};\r\n        startup_info.cb = sizeof(STARTUPINFOW);\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n        startup_info.hStdInput = stdin_rd;\r\n        startup_info.hStdOutput = stdout_wr;\r\n        startup_info.hStdError = stderr_wr;\r\n        if(static_cast<std::uint32_t>(options) != 0)\r\n        {\r\n            startup_info.dwFlags = STARTF_USESTDHANDLES;\r\n        }\r\n    #endif\r\n\r\n        PROCESS_INFORMATION process_info{};\r\n        if(!CreateProcessW(std::data(native_path), null_or_data(args_str), nullptr, nullptr, TRUE, 0, nullptr, null_or_data(native_working_directory), &startup_info, &process_info))\r\n        {\r\n            throw std::runtime_error{\"Failed to create process. \" + get_error_message()};\r\n        }\r\n\r\n        m_id = static_cast<id>(process_info.dwProcessId);\r\n        m_handle = process_info.hProcess;\r\n        m_thread_handle = process_info.hThread;\r\n\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n        if(static_cast<bool>(options & process_options::grab_stdin))\r\n        {\r\n            pipe_streambuf buffer{stdin_wr.release(), std::ios_base::out};\r\n            m_stdin_stream.reset(new pipe_ostream{std::move(buffer)});\r\n        }\r\n\r\n        if(static_cast<bool>(options & process_options::grab_stdout))\r\n        {\r\n            pipe_streambuf buffer{stdout_rd.release(), std::ios_base::in};\r\n            m_stdout_stream.reset(new pipe_istream{std::move(buffer)});\r\n        }\r\n\r\n        if(static_cast<bool>(options & process_options::grab_stderr))\r\n        {\r\n            pipe_streambuf buffer{stderr_rd.release(), std::ios_base::in};\r\n            m_stderr_stream.reset(new pipe_istream{std::move(buffer)});\r\n        }\r\n    #endif\r\n    }\r\n\r\n    ~process()\r\n    {\r\n        assert(!joinable() && \"nes::process::~process() called with joinable() returning true.\");\r\n\r\n        if(joinable())\r\n        {\r\n            std::terminate();\r\n        }\r\n    }\r\n\r\n    process(const process&) = delete;\r\n    process& operator=(const process&) = delete;\r\n\r\n    process(process&& other) noexcept\r\n    : m_id{std::exchange(other.m_id, id{})}\r\n    , m_return_code{std::exchange(other.m_return_code, return_code_type{})}\r\n    , m_handle{std::move(other.m_handle)}\r\n    , m_thread_handle{std::move(other.m_thread_handle)}\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n    , m_stdin_stream{std::move(other.m_stdin_stream)}\r\n    , m_stdout_stream{std::move(other.m_stdout_stream)}\r\n    , m_stderr_stream{std::move(other.m_stderr_stream)}\r\n    #endif\r\n    {\r\n    }\r\n\r\n    process& operator=(process&& other) noexcept\r\n    {\r\n        if(joinable())\r\n        {\r\n            std::terminate();\r\n        }\r\n\r\n        m_id = std::exchange(other.m_id, m_id);\r\n        m_return_code = std::exchange(other.m_return_code, m_return_code);\r\n        m_handle = std::move(other.m_handle);\r\n        m_thread_handle = std::move(other.m_thread_handle);\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n        m_stdin_stream = std::move(other.m_stdin_stream);\r\n        m_stdout_stream = std::move(other.m_stdout_stream);\r\n        m_stderr_stream = std::move(other.m_stderr_stream);\r\n    #endif\r\n\r\n        return *this;\r\n    }\r\n\r\n    void join()\r\n    {\r\n        assert(joinable() && \"nes::process::join() called with joinable() returning false.\");\r\n\r\n        if(WaitForSingleObject(m_handle, INFINITE))\r\n        {\r\n            throw std::runtime_error{\"Failed to join the process. \" + get_error_message()};\r\n        }\r\n\r\n        if(!GetExitCodeProcess(m_handle, reinterpret_cast<DWORD*>(&m_return_code)))\r\n        {\r\n            throw std::runtime_error{\"Failed to get the return code of the process. \" + get_error_message()};\r\n        }\r\n\r\n        close_process();\r\n    }\r\n\r\n    bool joinable() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\n    bool active() const\r\n    {\r\n        if(!m_handle)\r\n        {\r\n            return false;\r\n        }\r\n\r\n        DWORD result = WaitForSingleObject(m_handle, 0);\r\n        if(result == WAIT_FAILED)\r\n        {\r\n            throw std::runtime_error{\"Failed to get the state of the process. \" + get_error_message()};\r\n        }\r\n\r\n        return result == WAIT_TIMEOUT;\r\n    }\r\n\r\n    void detach()\r\n    {\r\n        assert(joinable() && \"nes::process::detach() called with joinable() returning false.\");\r\n\r\n        close_process();\r\n    }\r\n\r\n    bool kill()\r\n    {\r\n        assert(joinable() && \"nes::process::kill() called with joinable() returning false.\");\r\n\r\n        if(!TerminateProcess(m_handle, 1))\r\n        {\r\n            return false;\r\n        }\r\n\r\n        join();\r\n\r\n        return true;\r\n    }\r\n\r\n    return_code_type return_code() const noexcept\r\n    {\r\n        assert(!joinable() && \"nes::process::return_code() called with joinable() returning true.\");\r\n\r\n        return m_return_code;\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\n    id get_id() const noexcept\r\n    {\r\n        return m_id;\r\n    }\r\n\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n    pipe_ostream& stdin_stream() noexcept\r\n    {\r\n        return *m_stdin_stream;\r\n    }\r\n\r\n    pipe_istream& stdout_stream() noexcept\r\n    {\r\n        return *m_stdout_stream;\r\n    }\r\n\r\n    pipe_istream& stderr_stream() noexcept\r\n    {\r\n        return *m_stderr_stream;\r\n    }\r\n    #endif\r\n\r\nprivate:\r\n    std::wstring to_wide(const std::string& path)\r\n    {\r\n        assert(std::size(path) < 0x7FFFFFFFu && \"Wrong path.\");\r\n\r\n        if(std::empty(path))\r\n        {\r\n            return {};\r\n        }\r\n\r\n        std::wstring out_path{};\r\n        out_path.resize(static_cast<std::size_t>(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), nullptr, 0)));\r\n\r\n        if(!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), std::data(out_path), static_cast<int>(std::size(out_path))))\r\n        {\r\n            throw std::runtime_error{\"Failed to convert the path to wide.\"};\r\n        }\r\n\r\n        return out_path;\r\n    }\r\n\r\n    std::string get_error_message() const\r\n    {\r\n        return \"#\" + std::to_string(GetLastError());\r\n    }\r\n\r\n    void close_process()\r\n    {\r\n        m_id = id{};\r\n        CloseHandle(m_handle.release());\r\n        CloseHandle(m_thread_handle.release());\r\n    }\r\n\r\n    wchar_t* null_or_data(std::wstring& str)\r\n    {\r\n        return std::empty(str) ? nullptr : std::data(str);\r\n    }\r\n\r\n    const wchar_t* null_or_data(const std::wstring& str)\r\n    {\r\n        return std::empty(str) ? nullptr : std::data(str);\r\n    }\r\n\r\nprivate:\r\n    id m_id{};\r\n    return_code_type m_return_code{};\r\n    impl::auto_handle m_handle{};\r\n    impl::auto_handle m_thread_handle{};\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n    std::unique_ptr<pipe_ostream> m_stdin_stream{};\r\n    std::unique_ptr<pipe_istream> m_stdout_stream{};\r\n    std::unique_ptr<pipe_istream> m_stderr_stream{};\r\n    #endif\r\n};\r\n\r\nnamespace this_process\r\n{\r\n\r\ninline process::id get_id() noexcept\r\n{\r\n    return process::id{GetCurrentProcessId()};\r\n}\r\n\r\ninline std::string working_directory()\r\n{\r\n    const DWORD size{GetCurrentDirectoryW(0, nullptr)};\r\n\r\n    std::wstring native_path{};\r\n    native_path.resize(static_cast<std::size_t>(size));\r\n    GetCurrentDirectoryW(size, std::data(native_path));\r\n    native_path.pop_back(); // Because GetCurrentDirectoryW adds a null terminator\r\n\r\n    std::transform(std::begin(native_path), std::end(native_path), std::begin(native_path), [](wchar_t c)\r\n    {\r\n        return c == L'\\\\' ? L'/' : c;\r\n    });\r\n\r\n    std::string path{};\r\n    path.resize(static_cast<std::size_t>(WideCharToMultiByte(CP_UTF8, 0, std::data(native_path), static_cast<int>(std::size(native_path)), nullptr, 0, nullptr, nullptr)));\r\n\r\n    if(!WideCharToMultiByte(CP_UTF8, 0, std::data(native_path), static_cast<int>(std::size(native_path)), std::data(path), static_cast<int>(std::size(path)), nullptr, nullptr))\r\n    {\r\n        throw std::runtime_error{\"Failed to convert the path to UTF-8.\"};\r\n    }\r\n\r\n    return path;\r\n}\r\n\r\ninline bool change_working_directory(const std::string& path)\r\n{\r\n    std::wstring native_path{};\r\n    native_path.resize(static_cast<std::size_t>(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), nullptr, 0)));\r\n\r\n    if(!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), std::data(native_path), static_cast<int>(std::size(native_path))))\r\n    {\r\n        throw std::runtime_error{\"Failed to convert the path to wide.\"};\r\n    }\r\n\r\n    return SetCurrentDirectoryW(std::data(native_path));\r\n}\r\n\r\n}\r\n\r\nNES_INLINE_NAMESPACE_END\r\n\r\n}\r\n\r\ntemplate<typename CharT, typename Traits>\r\nstd::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, nes::process::id id)\r\n{\r\n    return os << static_cast<DWORD>(id);\r\n}\r\n\r\nnamespace std\r\n{\r\ntemplate<>\r\nstruct hash<nes::process::id>\r\n{\r\n    using argument_type = nes::process::id;\r\n    using result_type = std::size_t;\r\n\r\n    result_type operator()(const argument_type& s) const noexcept\r\n    {\r\n        return std::hash<DWORD>{}(static_cast<DWORD>(s));\r\n    }\r\n};\r\n}\r\n\r\n#elif defined(NES_POSIX_PROCESS)\r\n\r\nnamespace nes\r\n{\r\n\r\nNES_INLINE_NAMESPACE_BEGIN\r\n\r\nclass process;\r\n\r\nnamespace impl\r\n{\r\n\r\nenum class id_t : pid_t\r\n{\r\n};\r\n\r\nconstexpr bool operator==(id_t lhs, id_t rhs) noexcept\r\n{\r\n    return static_cast<pid_t>(lhs) == static_cast<pid_t>(rhs);\r\n}\r\n\r\nconstexpr bool operator!=(id_t lhs, id_t rhs) noexcept\r\n{\r\n    return static_cast<pid_t>(lhs) != static_cast<pid_t>(rhs);\r\n}\r\n\r\nconstexpr bool operator<(id_t lhs, id_t rhs) noexcept\r\n{\r\n    return static_cast<pid_t>(lhs) < static_cast<pid_t>(rhs);\r\n}\r\n\r\nconstexpr bool operator<=(id_t lhs, id_t rhs) noexcept\r\n{\r\n    return static_cast<pid_t>(lhs) <= static_cast<pid_t>(rhs);\r\n}\r\n\r\nconstexpr bool operator>(id_t lhs, id_t rhs) noexcept\r\n{\r\n    return static_cast<pid_t>(lhs) > static_cast<pid_t>(rhs);\r\n}\r\n\r\nconstexpr bool operator>=(id_t lhs, id_t rhs) noexcept\r\n{\r\n    return static_cast<pid_t>(lhs) >= static_cast<pid_t>(rhs);\r\n}\r\n\r\n#ifdef NES_PROCESS_PIPE_EXTENSION\r\nstruct auto_handle\r\n{\r\n    constexpr auto_handle() = default;\r\n    auto_handle(int h)\r\n    : m_handle{h}\r\n    {\r\n    }\r\n\r\n    ~auto_handle()\r\n    {\r\n        if(m_handle)\r\n        {\r\n            close(m_handle);\r\n        }\r\n    }\r\n\r\n    auto_handle(const auto_handle&) = delete;\r\n    auto_handle& operator=(const auto_handle&) = delete;\r\n\r\n    auto_handle(auto_handle&& other) noexcept\r\n    : m_handle{std::exchange(other.m_handle, -1)}\r\n    {\r\n    }\r\n\r\n    auto_handle& operator=(auto_handle&& other) noexcept\r\n    {\r\n        m_handle = std::exchange(other.m_handle, m_handle);\r\n\r\n        return *this;\r\n    }\r\n\r\n    int release() noexcept\r\n    {\r\n        return std::exchange(m_handle, -1);\r\n    }\r\n\r\n    operator int() noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\n    operator int() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\n    int* operator&() noexcept\r\n    {\r\n        return &m_handle;\r\n    }\r\n\r\n    const int* operator&() const noexcept\r\n    {\r\n        return &m_handle;\r\n    }\r\n\r\n    operator bool() const noexcept\r\n    {\r\n        return m_handle != -1;\r\n    }\r\n\r\n    int m_handle{-1};\r\n};\r\n#endif\r\n\r\n}\r\n\r\nenum class process_options : std::uint32_t\r\n{\r\n    none = 0x00,\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n    grab_stdout = 0x10,\r\n    grab_stderr = 0x20,\r\n    grab_stdin = 0x40\r\n    #endif\r\n};\r\n\r\nconstexpr process_options operator&(process_options left, process_options right) noexcept\r\n{\r\n    return static_cast<process_options>(static_cast<std::uint32_t>(left) & static_cast<std::uint32_t>(right));\r\n}\r\n\r\nconstexpr process_options& operator&=(process_options& left, process_options right) noexcept\r\n{\r\n    left = left & right;\r\n    return left;\r\n}\r\n\r\nconstexpr process_options operator|(process_options left, process_options right) noexcept\r\n{\r\n    return static_cast<process_options>(static_cast<std::uint32_t>(left) | static_cast<std::uint32_t>(right));\r\n}\r\n\r\nconstexpr process_options& operator|=(process_options& left, process_options right) noexcept\r\n{\r\n    left = left | right;\r\n    return left;\r\n}\r\n\r\nconstexpr process_options operator^(process_options left, process_options right) noexcept\r\n{\r\n    return static_cast<process_options>(static_cast<std::uint32_t>(left) ^ static_cast<std::uint32_t>(right));\r\n}\r\n\r\nconstexpr process_options& operator^=(process_options& left, process_options right) noexcept\r\n{\r\n    left = left ^ right;\r\n    return left;\r\n}\r\n\r\nconstexpr process_options operator~(process_options value) noexcept\r\n{\r\n    return static_cast<process_options>(~static_cast<std::uint32_t>(value));\r\n}\r\n\r\nclass process\r\n{\r\npublic:\r\n    using native_handle_type = pid_t;\r\n    using return_code_type = int;\r\n    using id = impl::id_t;\r\n\r\npublic:\r\n    constexpr process() noexcept = default;\r\n\r\n    explicit process(const std::string& path, const std::string& working_directory)\r\n    : process{path, {}, working_directory, {}}\r\n    {\r\n    }\r\n\r\n    explicit process(const std::string& path, process_options options)\r\n    : process{path, {}, {}, options}\r\n    {\r\n    }\r\n\r\n    explicit process(const std::string& path, const std::vector<std::string>& args, process_options options)\r\n    : process{path, args, {}, options}\r\n    {\r\n    }\r\n\r\n    explicit process(const std::string& path, const std::string& working_directory, process_options options)\r\n    : process{path, {}, working_directory, options}\r\n    {\r\n    }\r\n\r\n    explicit process(const std::string& path, std::vector<std::string> args = std::vector<std::string>{}, const std::string& working_directory = std::string{}, process_options options [[maybe_unused]] = process_options{})\r\n    {\r\n        assert(!std::empty(path) && \"nes::process::process called with empty path.\");\r\n\r\n        args.insert(std::begin(args), path);\r\n\r\n        std::vector<char*> native_args{};\r\n        native_args.resize(std::size(args) + 1);\r\n        for(std::size_t i{}; i < std::size(args); ++i)\r\n        {\r\n            native_args[i] = std::data(args[i]);\r\n        }\r\n\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n        impl::auto_handle stdin_fd[2]{};\r\n        impl::auto_handle stdout_fd[2]{};\r\n        impl::auto_handle stderr_fd[2]{};\r\n\r\n        if(static_cast<bool>(options & process_options::grab_stdin) && pipe(reinterpret_cast<int*>(stdin_fd)))\r\n        {\r\n            throw std::runtime_error{\"Failed to create stdin pipe. \" + std::string{strerror(errno)}};\r\n        }\r\n\r\n        if(static_cast<bool>(options & process_options::grab_stdout) && pipe(reinterpret_cast<int*>(stdout_fd)))\r\n        {\r\n            throw std::runtime_error{\"Failed to create stdout pipe. \" + std::string{strerror(errno)}};\r\n        }\r\n\r\n        if(static_cast<bool>(options & process_options::grab_stderr) && pipe(reinterpret_cast<int*>(stderr_fd)))\r\n        {\r\n            throw std::runtime_error{\"Failed to create stderr pipe. \" + std::string{strerror(errno)}};\r\n        }\r\n\r\n        const bool standard_streams{static_cast<bool>(options & process_options::grab_stdin) || static_cast<bool>(options & process_options::grab_stdout) || static_cast<bool>(options & process_options::grab_stderr)};\r\n    #else\r\n        constexpr bool standard_streams{false};\r\n    #endif\r\n\r\n        const pid_t id{fork()};\r\n        if(id < 0)\r\n        {\r\n            throw std::runtime_error{\"Failed to create process. \" + std::string{strerror(errno)}};\r\n        }\r\n        else if(id == 0)\r\n        {\r\n\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n            if(static_cast<bool>(options & process_options::grab_stdin))\r\n            {\r\n                if(dup2(stdin_fd[0], 0) == -1)\r\n                {\r\n                    _exit(EXIT_FAILURE);\r\n                }\r\n            }\r\n\r\n            if(static_cast<bool>(options & process_options::grab_stdout))\r\n            {\r\n                if(dup2(stdout_fd[1], 1) == -1)\r\n                {\r\n                    _exit(EXIT_FAILURE);\r\n                }\r\n            }\r\n\r\n            if(static_cast<bool>(options & process_options::grab_stderr))\r\n            {\r\n                if(dup2(stderr_fd[1], 2) == -1)\r\n                {\r\n                    _exit(EXIT_FAILURE);\r\n                }\r\n            }\r\n    #endif\r\n\r\n            if(!std::empty(working_directory))\r\n            {\r\n                if(chdir(std::data(working_directory)))\r\n                {\r\n                    _exit(EXIT_FAILURE);\r\n                }\r\n            }\r\n\r\n            execv(std::data(path), std::data(native_args));\r\n            _exit(EXIT_FAILURE);\r\n        }\r\n\r\n        m_id = id;\r\n\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n        if(static_cast<bool>(options & process_options::grab_stdin))\r\n        {\r\n            pipe_streambuf buf{stdin_fd[1].release(), std::ios_base::out};\r\n            m_stdin_stream.reset(new pipe_ostream{std::move(buf)});\r\n        }\r\n\r\n        if(static_cast<bool>(options & process_options::grab_stdout))\r\n        {\r\n            pipe_streambuf buf{stdout_fd[0].release(), std::ios_base::in};\r\n            m_stdout_stream.reset(new pipe_istream{std::move(buf)});\r\n        }\r\n\r\n        if(static_cast<bool>(options & process_options::grab_stderr))\r\n        {\r\n            pipe_streambuf buf{stderr_fd[0].release(), std::ios_base::in};\r\n            m_stderr_stream.reset(new pipe_istream{std::move(buf)});\r\n        }\r\n    #endif\r\n    }\r\n\r\n    ~process()\r\n    {\r\n        assert(!joinable() && \"nes::process::~process() called with joinable() returning true.\");\r\n\r\n        if(joinable())\r\n        {\r\n            std::terminate();\r\n        }\r\n    }\r\n\r\n    process(const process&) = delete;\r\n    process& operator=(const process&) = delete;\r\n\r\n    process(process&& other) noexcept\r\n    : m_id{std::exchange(other.m_id, -1)}\r\n    , m_return_code{std::exchange(other.m_return_code, return_code_type{})}\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n    , m_stdin_stream{std::move(other.m_stdin_stream)}\r\n    , m_stdout_stream{std::move(other.m_stdout_stream)}\r\n    , m_stderr_stream{std::move(other.m_stderr_stream)}\r\n    #endif\r\n    {\r\n    }\r\n\r\n    process& operator=(process&& other) noexcept\r\n    {\r\n        if(joinable())\r\n        {\r\n            std::terminate();\r\n        }\r\n\r\n        m_id = std::exchange(other.m_id, m_id);\r\n        m_return_code = std::exchange(other.m_return_code, m_return_code);\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n        m_stdin_stream = std::move(other.m_stdin_stream);\r\n        m_stdout_stream = std::move(other.m_stdout_stream);\r\n        m_stderr_stream = std::move(other.m_stderr_stream);\r\n    #endif\r\n\r\n        return *this;\r\n    }\r\n\r\n    void join()\r\n    {\r\n        assert(joinable() && \"nes::process::join() called with joinable() returning false.\");\r\n\r\n        int return_code{};\r\n        if(waitpid(m_id, &return_code, 0) == -1)\r\n        {\r\n            throw std::runtime_error{\"Failed to join the process. \" + std::string{strerror(errno)}};\r\n        }\r\n\r\n        m_id = -1;\r\n        m_return_code = WEXITSTATUS(return_code);\r\n    }\r\n\r\n    bool joinable() const noexcept\r\n    {\r\n        return m_id != -1;\r\n    }\r\n\r\n    bool active() const\r\n    {\r\n        return ::kill(m_id, 0) != ESRCH;\r\n    }\r\n\r\n    void detach()\r\n    {\r\n        assert(joinable() && \"nes::process::detach() called with joinable() returning false.\");\r\n        m_id = -1;\r\n    }\r\n\r\n    bool kill()\r\n    {\r\n        assert(joinable() && \"nes::process::kill() called with joinable() returning false.\");\r\n\r\n        if(::kill(m_id, SIGTERM))\r\n        {\r\n            return false;\r\n        }\r\n\r\n        join();\r\n\r\n        return true;\r\n    }\r\n\r\n    return_code_type return_code() const noexcept\r\n    {\r\n        assert(!joinable() && \"nes::process::return_code() called with joinable() returning true.\");\r\n\r\n        return m_return_code;\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_id;\r\n    }\r\n\r\n    id get_id() const noexcept\r\n    {\r\n        return static_cast<impl::id_t>(m_id);\r\n    }\r\n\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n    pipe_ostream& stdin_stream() noexcept\r\n    {\r\n        return *m_stdin_stream;\r\n    }\r\n\r\n    pipe_istream& stdout_stream() noexcept\r\n    {\r\n        return *m_stdout_stream;\r\n    }\r\n\r\n    pipe_istream& stderr_stream() noexcept\r\n    {\r\n        return *m_stderr_stream;\r\n    }\r\n    #endif\r\n\r\nprivate:\r\n    native_handle_type m_id{};\r\n    return_code_type m_return_code{};\r\n    #ifdef NES_PROCESS_PIPE_EXTENSION\r\n    std::unique_ptr<pipe_ostream> m_stdin_stream{};\r\n    std::unique_ptr<pipe_istream> m_stdout_stream{};\r\n    std::unique_ptr<pipe_istream> m_stderr_stream{};\r\n    #endif\r\n};\r\n\r\nnamespace this_process\r\n{\r\n\r\ninline process::id get_id() noexcept\r\n{\r\n    return process::id{getpid()};\r\n}\r\n\r\ninline std::string working_directory()\r\n{\r\n    std::string path{};\r\n    path.resize(256);\r\n\r\n    while(!getcwd(std::data(path), std::size(path)))\r\n    {\r\n        if(errno == ERANGE)\r\n        {\r\n            path.resize(std::size(path) * 2);\r\n        }\r\n        else\r\n        {\r\n            throw std::runtime_error{\"Failed to get the current working directory. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    path.resize(path.find_first_of('\\0'));\r\n\r\n    return path;\r\n}\r\n\r\ninline bool change_working_directory(const std::string& path)\r\n{\r\n    return chdir(std::data(path)) == 0;\r\n}\r\n\r\n}\r\n\r\nNES_INLINE_NAMESPACE_END\r\n\r\n}\r\n\r\ntemplate<typename CharT, typename Traits>\r\nstd::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, nes::process::id id)\r\n{\r\n    return os << static_cast<int>(id);\r\n}\r\n\r\nnamespace std\r\n{\r\ntemplate<>\r\nstruct hash<nes::process::id>\r\n{\r\n    using argument_type = nes::process::id;\r\n    using result_type = std::size_t;\r\n\r\n    result_type operator()(const argument_type& s) const noexcept\r\n    {\r\n        return std::hash<int>{}(static_cast<int>(s));\r\n    }\r\n};\r\n}\r\n\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/nes/semaphore.hpp",
    "content": "///////////////////////////////////////////////////////////\r\n/// Copyright 2019 Alexy Pellegrini\r\n///\r\n/// Permission is hereby granted, free of charge,\r\n/// to any person obtaining a copy of this software\r\n/// and associated documentation files (the \"Software\"),\r\n/// to deal in the Software without restriction,\r\n/// including without limitation the rights to use,\r\n/// copy, modify, merge, publish, distribute, sublicense,\r\n/// and/or sell copies of the Software, and to permit\r\n/// persons to whom the Software is furnished to do so,\r\n/// subject to the following conditions:\r\n///\r\n/// The above copyright notice and this permission notice\r\n/// shall be included in all copies or substantial portions\r\n/// of the Software.\r\n///\r\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\r\n/// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\r\n/// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\r\n/// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\r\n/// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r\n/// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r\n/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE\r\n/// OR OTHER DEALINGS IN THE SOFTWARE.\r\n///////////////////////////////////////////////////////////\r\n\r\n#ifndef NOT_ENOUGH_STANDARDS_SEMAPHORE\r\n#define NOT_ENOUGH_STANDARDS_SEMAPHORE\r\n\r\n#if defined(_WIN32)\r\n    #define NES_WIN32_SEMAPHORE\r\n    #ifndef NOMINMAX\r\n        #define NOMINMAX\r\n    #endif\r\n    #ifndef WIN32_LEAN_AND_MEAN\r\n        #define WIN32_LEAN_AND_MEAN\r\n    #endif\r\n    #include <Windows.h>\r\n#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))\r\n    #define NES_POSIX_SEMAPHORE\r\n    #include <unistd.h>\r\n    #include <string.h>\r\n    #include <semaphore.h>\r\n#else\r\n    #error \"Not enough standards does not support this environment.\"\r\n#endif\r\n\r\n#ifdef NES_INLINE_NAMESPACE\r\n    #define NES_INLINE_NAMESPACE_BEGIN        \\\r\n        inline namespace NES_INLINE_NAMESPACE \\\r\n        {\r\n    #define NES_INLINE_NAMESPACE_END }\r\n#else\r\n    #define NES_INLINE_NAMESPACE_BEGIN\r\n    #define NES_INLINE_NAMESPACE_END\r\n#endif\r\n\r\n#include <string>\r\n#include <chrono>\r\n#include <limits>\r\n#include <utility>\r\n#include <stdexcept>\r\n#include <memory>\r\n\r\n#if defined(NES_WIN32_SEMAPHORE)\r\n\r\nnamespace nes\r\n{\r\n\r\nNES_INLINE_NAMESPACE_BEGIN\r\n\r\nclass semaphore\r\n{\r\npublic:\r\n    using native_handle_type = HANDLE;\r\n\r\npublic:\r\n    explicit semaphore(std::size_t initial_count = 0)\r\n    {\r\n        m_handle = CreateSemaphoreW(nullptr, static_cast<LONG>(initial_count), std::numeric_limits<LONG>::max(), nullptr);\r\n        if(!m_handle)\r\n        {\r\n            throw std::runtime_error{\"Failed to create semaphore. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    ~semaphore()\r\n    {\r\n        if(m_handle)\r\n        {\r\n            CloseHandle(m_handle);\r\n        }\r\n    }\r\n\r\n    semaphore(const semaphore&) = delete;\r\n    semaphore& operator=(const semaphore&) = delete;\r\n    semaphore(semaphore&& other) noexcept = delete;\r\n    semaphore& operator=(semaphore&& other) noexcept = delete;\r\n\r\n    void acquire()\r\n    {\r\n        if(WaitForSingleObject(m_handle, INFINITE))\r\n        {\r\n            throw std::runtime_error{\"Failed to decrement semaphore count. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    bool try_acquire()\r\n    {\r\n        return WaitForSingleObject(m_handle, 0) == WAIT_OBJECT_0;\r\n    }\r\n\r\n    void release()\r\n    {\r\n        if(!ReleaseSemaphore(m_handle, 1, nullptr))\r\n        {\r\n            throw std::runtime_error{\"Failed to increment semaphore count. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\nprivate:\r\n    std::string get_error_message() const\r\n    {\r\n        return \"#\" + std::to_string(GetLastError());\r\n    }\r\n\r\nprivate:\r\n    native_handle_type m_handle{};\r\n};\r\n\r\nclass timed_semaphore\r\n{\r\npublic:\r\n    using native_handle_type = HANDLE;\r\n\r\npublic:\r\n    explicit timed_semaphore(std::size_t initial_count = 0)\r\n    {\r\n        m_handle = CreateSemaphoreW(nullptr, static_cast<LONG>(initial_count), std::numeric_limits<LONG>::max(), nullptr);\r\n        if(!m_handle)\r\n        {\r\n            throw std::runtime_error{\"Failed to create semaphore. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    ~timed_semaphore()\r\n    {\r\n        CloseHandle(m_handle);\r\n    }\r\n\r\n    timed_semaphore(const timed_semaphore&) = delete;\r\n    timed_semaphore& operator=(const timed_semaphore&) = delete;\r\n    timed_semaphore(timed_semaphore&& other) noexcept = delete;\r\n    timed_semaphore& operator=(timed_semaphore&& other) noexcept = delete;\r\n\r\n    void acquire()\r\n    {\r\n        if(WaitForSingleObject(m_handle, INFINITE))\r\n        {\r\n            throw std::runtime_error{\"Failed to decrement semaphore count. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    bool try_acquire()\r\n    {\r\n        return WaitForSingleObject(m_handle, 0) == WAIT_OBJECT_0;\r\n    }\r\n\r\n    template<class Rep, class Period>\r\n    bool try_acquire_for(const std::chrono::duration<Rep, Period>& timeout)\r\n    {\r\n        return WaitForSingleObject(m_handle, std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()) == WAIT_OBJECT_0;\r\n    }\r\n\r\n    template<class Clock, class Duration>\r\n    bool try_acquire_until(const std::chrono::time_point<Clock, Duration>& time_point)\r\n    {\r\n        const auto current_time{Clock::now()};\r\n        if(time_point < current_time)\r\n        {\r\n            return try_acquire();\r\n        }\r\n\r\n        return try_acquire_for(time_point - current_time);\r\n    }\r\n\r\n    void release()\r\n    {\r\n        if(!ReleaseSemaphore(m_handle, 1, nullptr))\r\n        {\r\n            throw std::runtime_error{\"Failed to increment semaphore count. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\nprivate:\r\n    std::string get_error_message() const\r\n    {\r\n        return \"#\" + std::to_string(GetLastError());\r\n    }\r\n\r\nprivate:\r\n    native_handle_type m_handle{};\r\n};\r\n\r\nNES_INLINE_NAMESPACE_END\r\n\r\n}\r\n\r\n#elif defined(NES_POSIX_SEMAPHORE)\r\n\r\nnamespace nes\r\n{\r\n\r\nNES_INLINE_NAMESPACE_BEGIN\r\n\r\nclass semaphore\r\n{\r\npublic:\r\n    using native_handle_type = sem_t*;\r\n\r\npublic:\r\n    explicit semaphore(std::size_t initial_count = 0)\r\n    {\r\n        if(sem_init(m_handle.get(), 0, initial_count) != 0)\r\n        {\r\n            throw std::runtime_error{\"Failed to create semaphore. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    ~semaphore()\r\n    {\r\n        sem_destroy(m_handle.get());\r\n    }\r\n\r\n    semaphore(const semaphore&) = delete;\r\n    semaphore& operator=(const semaphore&) = delete;\r\n    semaphore(semaphore&& other) noexcept = delete;\r\n    semaphore& operator=(semaphore&& other) noexcept = delete;\r\n\r\n    void acquire()\r\n    {\r\n        if(sem_wait(m_handle.get()) == -1)\r\n        {\r\n            throw std::runtime_error{\"Failed to decrement semaphore count. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    bool try_acquire()\r\n    {\r\n        return !sem_trywait(m_handle.get());\r\n    }\r\n\r\n    void release()\r\n    {\r\n        if(sem_post(m_handle.get()) == -1)\r\n        {\r\n            throw std::runtime_error{\"Failed to increment semaphore count. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle.get();\r\n    }\r\n\r\nprivate:\r\n    std::unique_ptr<sem_t> m_handle{std::make_unique<sem_t>()};\r\n};\r\n\r\nclass timed_semaphore\r\n{\r\npublic:\r\n    using native_handle_type = sem_t*;\r\n\r\npublic:\r\n    explicit timed_semaphore(std::size_t initial_count = 0)\r\n    {\r\n        if(sem_init(m_handle.get(), 0, initial_count) != 0)\r\n        {\r\n            throw std::runtime_error{\"Failed to create timed_semaphore. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    ~timed_semaphore()\r\n    {\r\n        sem_destroy(m_handle.get());\r\n    }\r\n\r\n    timed_semaphore(const timed_semaphore&) = delete;\r\n    timed_semaphore& operator=(const timed_semaphore&) = delete;\r\n    timed_semaphore(timed_semaphore&& other) noexcept = delete;\r\n    timed_semaphore& operator=(timed_semaphore&& other) noexcept = delete;\r\n\r\n    void acquire()\r\n    {\r\n        if(sem_wait(m_handle.get()) == -1)\r\n        {\r\n            throw std::runtime_error{\"Failed to decrement semaphore count. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    bool try_acquire()\r\n    {\r\n        return !sem_trywait(m_handle.get());\r\n    }\r\n\r\n    template<class Rep, class Period>\r\n    bool try_lock_for(const std::chrono::duration<Rep, Period>& timeout)\r\n    {\r\n        return try_lock_until(std::chrono::system_clock::now() + timeout);\r\n    }\r\n\r\n    template<class Clock, class Duration>\r\n    bool try_lock_until(const std::chrono::time_point<Clock, Duration>& time_point)\r\n    {\r\n        const auto seconds{std::chrono::time_point_cast<std::chrono::seconds>(time_point)};\r\n        const auto nanoseconds{std::chrono::duration_cast<std::chrono::nanoseconds>(time_point - seconds)};\r\n\r\n        timespec time{};\r\n        time.tv_sec = static_cast<std::time_t>(seconds.time_since_epoch().count());\r\n        time.tv_nsec = static_cast<long>(nanoseconds.count());\r\n\r\n        return !sem_timedwait(m_handle.get(), &time);\r\n    }\r\n\r\n    void release()\r\n    {\r\n        if(sem_post(m_handle.get()) == -1)\r\n        {\r\n            throw std::runtime_error{\"Failed to increment semaphore count. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle.get();\r\n    }\r\n\r\nprivate:\r\n    std::unique_ptr<sem_t> m_handle{std::make_unique<sem_t>()};\r\n};\r\n\r\nNES_INLINE_NAMESPACE_END\r\n\r\n}\r\n\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/nes/shared_library.hpp",
    "content": "///////////////////////////////////////////////////////////\r\n/// Copyright 2019 Alexy Pellegrini\r\n///\r\n/// Permission is hereby granted, free of charge,\r\n/// to any person obtaining a copy of this software\r\n/// and associated documentation files (the \"Software\"),\r\n/// to deal in the Software without restriction,\r\n/// including without limitation the rights to use,\r\n/// copy, modify, merge, publish, distribute, sublicense,\r\n/// and/or sell copies of the Software, and to permit\r\n/// persons to whom the Software is furnished to do so,\r\n/// subject to the following conditions:\r\n///\r\n/// The above copyright notice and this permission notice\r\n/// shall be included in all copies or substantial portions\r\n/// of the Software.\r\n///\r\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\r\n/// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\r\n/// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\r\n/// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\r\n/// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r\n/// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r\n/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE\r\n/// OR OTHER DEALINGS IN THE SOFTWARE.\r\n///////////////////////////////////////////////////////////\r\n\r\n#ifndef NOT_ENOUGH_STANDARDS_SHARED_LIBRARY\r\n#define NOT_ENOUGH_STANDARDS_SHARED_LIBRARY\r\n\r\n#if defined(_WIN32)\r\n    #define NES_WIN32_SHARED_LIBRARY\r\n    #ifndef NOMINMAX\r\n        #define NOMINMAX\r\n    #endif\r\n    #ifndef WIN32_LEAN_AND_MEAN\r\n        #define WIN32_LEAN_AND_MEAN\r\n    #endif\r\n    #include <Windows.h>\r\n#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))\r\n    #define NES_POSIX_SHARED_LIBRARY\r\n    #include <dlfcn.h>\r\n#else\r\n    #error \"Not enough standards does not support this environment.\"\r\n#endif\r\n\r\n#ifdef NES_INLINE_NAMESPACE\r\n    #define NES_INLINE_NAMESPACE_BEGIN        \\\r\n        inline namespace NES_INLINE_NAMESPACE \\\r\n        {\r\n    #define NES_INLINE_NAMESPACE_END }\r\n#else\r\n    #define NES_INLINE_NAMESPACE_BEGIN\r\n    #define NES_INLINE_NAMESPACE_END\r\n#endif\r\n\r\n#include <string>\r\n#include <algorithm>\r\n#include <cassert>\r\n#include <stdexcept>\r\n#include <utility>\r\n\r\n#if defined(NES_WIN32_SHARED_LIBRARY)\r\n\r\nnamespace nes\r\n{\r\n\r\nNES_INLINE_NAMESPACE_BEGIN\r\n\r\nstruct load_current_t\r\n{\r\n};\r\n\r\ninline constexpr load_current_t load_current{};\r\n\r\nclass shared_library\r\n{\r\npublic:\r\n    using native_handle_type = HINSTANCE;\r\n\r\npublic:\r\n    constexpr shared_library() noexcept = default;\r\n\r\n    explicit shared_library(load_current_t)\r\n    {\r\n        m_handle = GetModuleHandleW(nullptr);\r\n        if(!m_handle)\r\n        {\r\n            throw std::runtime_error{\"Failed to load current binary file. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    explicit shared_library(const std::string& path)\r\n    {\r\n        assert(!std::empty(path) && \"nes::shared_library::shared_library called with empty path.\");\r\n\r\n        m_handle = LoadLibraryW(to_wide(path).c_str());\r\n        if(!m_handle)\r\n        {\r\n            throw std::runtime_error{\"Failed to load binary file \\\"\" + path + \"\\\". \" + get_error_message()};\r\n        }\r\n\r\n        m_need_free = true;\r\n    }\r\n\r\n    ~shared_library()\r\n    {\r\n        if(m_handle && m_need_free)\r\n        {\r\n            FreeLibrary(m_handle);\r\n        }\r\n    }\r\n\r\n    shared_library(const shared_library&) = delete;\r\n    shared_library& operator=(const shared_library&) = delete;\r\n\r\n    shared_library(shared_library&& other) noexcept\r\n    : m_handle{std::exchange(other.m_handle, native_handle_type{})}\r\n    , m_need_free{std::exchange(other.m_need_free, false)}\r\n    {\r\n    }\r\n\r\n    shared_library& operator=(shared_library&& other) noexcept\r\n    {\r\n        m_handle = std::exchange(other.m_handle, m_handle);\r\n        m_need_free = std::exchange(other.m_need_free, m_need_free);\r\n\r\n        return *this;\r\n    }\r\n\r\n    template<typename Func, typename = std::enable_if_t<std::is_pointer_v<Func> && std::is_function_v<std::remove_pointer_t<Func>>>>\r\n    Func load(const std::string& symbol) const noexcept\r\n    {\r\n        assert(!std::empty(symbol) && \"nes::shared_library::load called with an empty symbol name.\");\r\n        assert(m_handle && \"nes::shared_library::load called with invalid handle.\");\r\n\r\n        return reinterpret_cast<Func>(reinterpret_cast<void (*)()>(GetProcAddress(m_handle, std::data(symbol))));\r\n    }\r\n\r\n    template<typename Func, typename = std::enable_if_t<std::is_function_v<Func>>>\r\n    Func* load(const std::string& symbol) const noexcept\r\n    {\r\n        return load<Func*>(symbol);\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\nprivate:\r\n    std::wstring to_wide(std::string path)\r\n    {\r\n        assert(std::size(path) < 0x7FFFFFFFu && \"Wrong path.\");\r\n\r\n        std::transform(std::begin(path), std::end(path), std::begin(path), [](char c)\r\n        {\r\n            return c == '/' ? '\\\\' : c;\r\n        });\r\n\r\n        std::wstring out_path{};\r\n        out_path.resize(static_cast<std::size_t>(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), nullptr, 0)));\r\n\r\n        if(!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), std::data(out_path), static_cast<int>(std::size(out_path))))\r\n        {\r\n            throw std::runtime_error{\"Failed to convert the path to wide.\"};\r\n        }\r\n\r\n        return out_path;\r\n    }\r\n\r\n    std::string get_error_message() const\r\n    {\r\n        return \"#\" + std::to_string(GetLastError());\r\n    }\r\n\r\n    native_handle_type m_handle{};\r\n    bool m_need_free{};\r\n};\r\n\r\nNES_INLINE_NAMESPACE_END\r\n\r\n}\r\n\r\n#elif defined(NES_POSIX_SHARED_LIBRARY)\r\n\r\nnamespace nes\r\n{\r\n\r\nNES_INLINE_NAMESPACE_BEGIN\r\n\r\nstruct load_current_t\r\n{\r\n};\r\n\r\ninline constexpr load_current_t load_current{};\r\n\r\nclass shared_library\r\n{\r\npublic:\r\n    using native_handle_type = void*;\r\n\r\npublic:\r\n    constexpr shared_library() noexcept = default;\r\n\r\n    explicit shared_library(load_current_t)\r\n    {\r\n        m_handle = dlopen(nullptr, RTLD_NOW);\r\n        if(!m_handle)\r\n        {\r\n            throw std::runtime_error{\"Failed to load current binary file. \" + std::string{dlerror()}};\r\n        }\r\n    }\r\n\r\n    explicit shared_library(const std::string& path)\r\n    {\r\n        assert(!std::empty(path) && \"nes::shared_library::shared_library called with empty path.\");\r\n\r\n        m_handle = dlopen(std::data(path), RTLD_NOW);\r\n        if(!m_handle)\r\n        {\r\n            throw std::runtime_error{\"Failed to load binary file \\\"\" + path + \"\\\". \" + std::string{dlerror()}};\r\n        }\r\n    }\r\n\r\n    ~shared_library()\r\n    {\r\n        if(m_handle)\r\n        {\r\n            dlclose(m_handle);\r\n        }\r\n    }\r\n\r\n    shared_library(const shared_library&) = delete;\r\n    shared_library& operator=(const shared_library&) = delete;\r\n\r\n    shared_library(shared_library&& other) noexcept\r\n    : m_handle{std::exchange(other.m_handle, native_handle_type{})}\r\n    {\r\n    }\r\n\r\n    shared_library& operator=(shared_library&& other) noexcept\r\n    {\r\n        m_handle = std::exchange(other.m_handle, m_handle);\r\n        return *this;\r\n    }\r\n\r\n    template<typename Func, typename = std::enable_if_t<std::is_pointer_v<Func> && std::is_function_v<std::remove_pointer_t<Func>>>>\r\n    Func load(const std::string& symbol) const noexcept\r\n    {\r\n        assert(!std::empty(symbol) && \"nes::shared_library::load called with an empty symbol name.\");\r\n        assert(m_handle && \"nes::shared_library::load called with invalid handle.\");\r\n\r\n        return reinterpret_cast<Func>(dlsym(m_handle, std::data(symbol)));\r\n    }\r\n\r\n    template<typename Func, typename = std::enable_if_t<std::is_function_v<Func>>>\r\n    Func* load(const std::string& symbol) const noexcept\r\n    {\r\n        return load<Func*>(symbol);\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\nprivate:\r\n    native_handle_type m_handle{};\r\n};\r\n\r\nNES_INLINE_NAMESPACE_END\r\n\r\n}\r\n\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/nes/shared_memory.hpp",
    "content": "///////////////////////////////////////////////////////////\r\n/// Copyright 2019 Alexy Pellegrini\r\n///\r\n/// Permission is hereby granted, free of charge,\r\n/// to any person obtaining a copy of this software\r\n/// and associated documentation files (the \"Software\"),\r\n/// to deal in the Software without restriction,\r\n/// including without limitation the rights to use,\r\n/// copy, modify, merge, publish, distribute, sublicense,\r\n/// and/or sell copies of the Software, and to permit\r\n/// persons to whom the Software is furnished to do so,\r\n/// subject to the following conditions:\r\n///\r\n/// The above copyright notice and this permission notice\r\n/// shall be included in all copies or substantial portions\r\n/// of the Software.\r\n///\r\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\r\n/// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\r\n/// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\r\n/// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\r\n/// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r\n/// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r\n/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE\r\n/// OR OTHER DEALINGS IN THE SOFTWARE.\r\n///////////////////////////////////////////////////////////\r\n\r\n#ifndef NOT_ENOUGH_STANDARDS_SHARED_MEMORY\r\n#define NOT_ENOUGH_STANDARDS_SHARED_MEMORY\r\n\r\n#if defined(_WIN32)\r\n    #define NES_WIN32_SHARED_MEMORY\r\n    #ifndef NOMINMAX\r\n        #define NOMINMAX\r\n    #endif\r\n    #ifndef WIN32_LEAN_AND_MEAN\r\n        #define WIN32_LEAN_AND_MEAN\r\n    #endif\r\n    #include <Windows.h>\r\n#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))\r\n    #define NES_POSIX_SHARED_MEMORY\r\n    #include <unistd.h>\r\n    #include <fcntl.h>\r\n    #include <string.h>\r\n    #include <sys/mman.h>\r\n    #include <sys/stat.h>\r\n#else\r\n    #error \"Not enough standards does not support this environment.\"\r\n#endif\r\n\r\n#include <string>\r\n#include <utility>\r\n#include <stdexcept>\r\n#include <memory>\r\n#include <cassert>\r\n\r\n#if defined(NES_WIN32_SHARED_MEMORY)\r\n\r\nnamespace nes\r\n{\r\n\r\ninline constexpr const char shared_memory_root[] = \"Local\\\\\";\r\n\r\nenum class shared_memory_options : std::uint32_t\r\n{\r\n    none = 0x00,\r\n    constant = 0x01\r\n};\r\n\r\nconstexpr shared_memory_options operator&(shared_memory_options left, shared_memory_options right) noexcept\r\n{\r\n    return static_cast<shared_memory_options>(static_cast<std::uint32_t>(left) & static_cast<std::uint32_t>(right));\r\n}\r\n\r\nconstexpr shared_memory_options& operator&=(shared_memory_options& left, shared_memory_options right) noexcept\r\n{\r\n    left = left & right;\r\n    return left;\r\n}\r\n\r\nconstexpr shared_memory_options operator|(shared_memory_options left, shared_memory_options right) noexcept\r\n{\r\n    return static_cast<shared_memory_options>(static_cast<std::uint32_t>(left) | static_cast<std::uint32_t>(right));\r\n}\r\n\r\nconstexpr shared_memory_options& operator|=(shared_memory_options& left, shared_memory_options right) noexcept\r\n{\r\n    left = left | right;\r\n    return left;\r\n}\r\n\r\nconstexpr shared_memory_options operator^(shared_memory_options left, shared_memory_options right) noexcept\r\n{\r\n    return static_cast<shared_memory_options>(static_cast<std::uint32_t>(left) ^ static_cast<std::uint32_t>(right));\r\n}\r\n\r\nconstexpr shared_memory_options& operator^=(shared_memory_options& left, shared_memory_options right) noexcept\r\n{\r\n    left = left ^ right;\r\n    return left;\r\n}\r\n\r\nconstexpr shared_memory_options operator~(shared_memory_options value) noexcept\r\n{\r\n    return static_cast<shared_memory_options>(~static_cast<std::uint32_t>(value));\r\n}\r\n\r\nnamespace impl\r\n{\r\n\r\ninline std::uintptr_t get_allocation_granularity() noexcept\r\n{\r\n    SYSTEM_INFO info{};\r\n    GetSystemInfo(&info);\r\n    return info.dwAllocationGranularity;\r\n}\r\n\r\nstatic const std::uintptr_t allocation_granularity_mask{~(get_allocation_granularity() - 1)};\r\n\r\ntemplate<class T>\r\nstruct is_unbounded_array : std::false_type\r\n{\r\n};\r\n\r\ntemplate<class T>\r\nstruct is_unbounded_array<T[]> : std::true_type\r\n{\r\n};\r\n\r\ntemplate<class T>\r\nstruct is_bounded_array : std::false_type\r\n{\r\n};\r\n\r\ntemplate<class T, std::size_t N>\r\nstruct is_bounded_array<T[N]> : std::true_type\r\n{\r\n};\r\n\r\n}\r\n\r\ntemplate<typename T>\r\nstruct map_deleter\r\n{\r\n    void operator()(T* ptr) const noexcept\r\n    {\r\n        if(ptr)\r\n        {\r\n            UnmapViewOfFile(reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(ptr) & impl::allocation_granularity_mask));\r\n        }\r\n    }\r\n};\r\n\r\ntemplate<typename T>\r\nstruct map_deleter<T[]>\r\n{\r\n    void operator()(T* ptr) const noexcept\r\n    {\r\n        if(ptr)\r\n        {\r\n            UnmapViewOfFile(reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(ptr) & impl::allocation_granularity_mask));\r\n        }\r\n    }\r\n};\r\n\r\ntemplate<typename T>\r\nusing unique_map_t = std::unique_ptr<T, map_deleter<T>>;\r\ntemplate<typename T>\r\nusing shared_map_t = std::shared_ptr<T>;\r\ntemplate<typename T>\r\nusing weak_map_t = std::weak_ptr<T>;\r\n\r\nclass shared_memory\r\n{\r\npublic:\r\n    using native_handle_type = HANDLE;\r\n\r\npublic:\r\n    constexpr shared_memory() noexcept = default;\r\n\r\n    explicit shared_memory(const std::string& name, std::uint64_t size)\r\n    {\r\n        assert(!std::empty(name) && \"nes::shared_memory::shared_memory called with empty name.\");\r\n        assert(size != 0 && \"nes::shared_memory::shared_memory called with size == 0.\");\r\n\r\n        const auto native_name{to_wide(shared_memory_root + name)};\r\n\r\n        m_handle = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, static_cast<DWORD>(size >> 32), static_cast<DWORD>(size), std::data(native_name));\r\n        if(!m_handle || GetLastError() == ERROR_ALREADY_EXISTS)\r\n        {\r\n            throw std::runtime_error{\"Failed to create shared memory. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    explicit shared_memory(const std::string& name, shared_memory_options options = shared_memory_options::none)\r\n    {\r\n        assert(!std::empty(name) && \"nes::shared_memory::shared_memory called with empty name.\");\r\n\r\n        const auto native_name{to_wide(shared_memory_root + name)};\r\n        const DWORD access = static_cast<bool>(options & shared_memory_options::constant) ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS;\r\n\r\n        m_handle = OpenFileMappingW(access, FALSE, std::data(native_name));\r\n        if(!m_handle)\r\n        {\r\n            throw std::runtime_error{\"Failed to open shared memory. \" + get_error_message()};\r\n        }\r\n    }\r\n\r\n    ~shared_memory()\r\n    {\r\n        if(m_handle)\r\n        {\r\n            CloseHandle(m_handle);\r\n        }\r\n    }\r\n\r\n    shared_memory(const shared_memory&) = delete;\r\n    shared_memory& operator=(const shared_memory&) = delete;\r\n\r\n    shared_memory(shared_memory&& other) noexcept\r\n    : m_handle{std::exchange(other.m_handle, nullptr)}\r\n    {\r\n    }\r\n\r\n    shared_memory& operator=(shared_memory&& other) noexcept\r\n    {\r\n        m_handle = std::exchange(other.m_handle, m_handle);\r\n\r\n        return *this;\r\n    }\r\n\r\n    template<typename T>\r\n    unique_map_t<T> map(std::uint64_t offset, shared_memory_options options = (std::is_const<T>::value ? shared_memory_options::constant : shared_memory_options::none)) const\r\n    {\r\n        static_assert(std::is_trivial<T>::value, \"Behaviour is undefined if T is not a trivial type.\");\r\n        static_assert(!impl::is_unbounded_array<T>::value, \"T can not be an unbounded array type, i.e. T[]. Specify the size, or use the second overload if you don't know it at compile-time\");\r\n        assert(m_handle && \"nes::shared_memory::map called with an invalid handle.\");\r\n\r\n        const DWORD access = static_cast<bool>(options & shared_memory_options::constant) ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS;\r\n        const auto aligned_offset{offset & impl::allocation_granularity_mask};\r\n        const auto real_size{static_cast<std::size_t>((offset - aligned_offset) + sizeof(T))};\r\n\r\n        auto* ptr{MapViewOfFile(m_handle, access, static_cast<DWORD>(aligned_offset >> 32), static_cast<DWORD>(aligned_offset), real_size)};\r\n        if(!ptr)\r\n        {\r\n            throw std::runtime_error{\"Failed to map shared memory. \" + get_error_message()};\r\n        }\r\n\r\n        ptr = reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(ptr) + (offset - aligned_offset));\r\n\r\n        return unique_map_t<T>{static_cast<T*>(ptr)};\r\n    }\r\n\r\n    template<typename T>\r\n    shared_map_t<T> shared_map(std::uint64_t offset, shared_memory_options options = (std::is_const<T>::value ? shared_memory_options::constant : shared_memory_options::none)) const\r\n    {\r\n        return shared_map_t<T>{map<T>(offset, options)};\r\n    }\r\n\r\n    template<typename T, typename ValueType = typename std::remove_extent<T>::type>\r\n    unique_map_t<T> map(std::uint64_t offset, std::size_t count, shared_memory_options options = (std::is_const<ValueType>::value ? shared_memory_options::constant : shared_memory_options::none)) const\r\n    {\r\n        static_assert(std::is_trivial<ValueType>::value, \"Behaviour is undefined if ValueType is not a trivial type.\");\r\n        static_assert(!impl::is_bounded_array<T>::value, \"T is an statically sized array, use the other overload of map instead of this one (remove the second parameter).\");\r\n        static_assert(impl::is_unbounded_array<T>::value, \"T must be an array type, i.e. T[].\");\r\n        assert(m_handle && \"nes::shared_memory::map called with an invalid handle.\");\r\n\r\n        const DWORD access = static_cast<bool>(options & shared_memory_options::constant) ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS;\r\n        const auto aligned_offset{offset & impl::allocation_granularity_mask};\r\n        const auto real_size{static_cast<std::size_t>((offset - aligned_offset) + (sizeof(ValueType) * count))};\r\n\r\n        auto* ptr{MapViewOfFile(m_handle, access, static_cast<DWORD>(aligned_offset >> 32), static_cast<DWORD>(aligned_offset), real_size)};\r\n        if(!ptr)\r\n        {\r\n            throw std::runtime_error{\"Failed to map shared memory. \" + get_error_message()};\r\n        }\r\n\r\n        ptr = reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(ptr) + (offset - aligned_offset));\r\n\r\n        return unique_map_t<T>{static_cast<ValueType*>(ptr)};\r\n    }\r\n\r\n    template<typename T, typename ValueType = typename std::remove_extent<T>::type>\r\n    shared_map_t<T> shared_map(std::uint64_t offset, std::size_t count, shared_memory_options options = (std::is_const<ValueType>::value ? shared_memory_options::constant : shared_memory_options::none)) const\r\n    {\r\n        return shared_map_t<T>{map<T>(offset, count, options)};\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\nprivate:\r\n    std::wstring to_wide(const std::string& path)\r\n    {\r\n        assert(std::size(path) < 0x7FFFFFFFu && \"Wrong path.\");\r\n\r\n        if(std::empty(path))\r\n        {\r\n            return {};\r\n        }\r\n\r\n        std::wstring out_path{};\r\n        out_path.resize(static_cast<std::size_t>(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), nullptr, 0)));\r\n\r\n        if(!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), std::data(out_path), static_cast<int>(std::size(out_path))))\r\n        {\r\n            throw std::runtime_error{\"Failed to convert the path to wide.\"};\r\n        }\r\n\r\n        return out_path;\r\n    }\r\n\r\n    std::string get_error_message() const\r\n    {\r\n        return \"#\" + std::to_string(GetLastError());\r\n    }\r\n\r\nprivate:\r\n    native_handle_type m_handle{};\r\n};\r\n\r\n}\r\n\r\n#elif defined(NES_POSIX_SHARED_MEMORY)\r\n\r\nnamespace nes\r\n{\r\n\r\ninline constexpr const char shared_memory_root[] = \"/\";\r\n\r\nenum class shared_memory_options : std::uint32_t\r\n{\r\n    none = 0x00,\r\n    constant = 0x01\r\n};\r\n\r\nconstexpr shared_memory_options operator&(shared_memory_options left, shared_memory_options right) noexcept\r\n{\r\n    return static_cast<shared_memory_options>(static_cast<std::uint32_t>(left) & static_cast<std::uint32_t>(right));\r\n}\r\n\r\nconstexpr shared_memory_options& operator&=(shared_memory_options& left, shared_memory_options right) noexcept\r\n{\r\n    left = left & right;\r\n    return left;\r\n}\r\n\r\nconstexpr shared_memory_options operator|(shared_memory_options left, shared_memory_options right) noexcept\r\n{\r\n    return static_cast<shared_memory_options>(static_cast<std::uint32_t>(left) | static_cast<std::uint32_t>(right));\r\n}\r\n\r\nconstexpr shared_memory_options& operator|=(shared_memory_options& left, shared_memory_options right) noexcept\r\n{\r\n    left = left | right;\r\n    return left;\r\n}\r\n\r\nconstexpr shared_memory_options operator^(shared_memory_options left, shared_memory_options right) noexcept\r\n{\r\n    return static_cast<shared_memory_options>(static_cast<std::uint32_t>(left) ^ static_cast<std::uint32_t>(right));\r\n}\r\n\r\nconstexpr shared_memory_options& operator^=(shared_memory_options& left, shared_memory_options right) noexcept\r\n{\r\n    left = left ^ right;\r\n    return left;\r\n}\r\n\r\nconstexpr shared_memory_options operator~(shared_memory_options value) noexcept\r\n{\r\n    return static_cast<shared_memory_options>(~static_cast<std::uint32_t>(value));\r\n}\r\n\r\nnamespace impl\r\n{\r\n\r\ninline std::uintptr_t get_allocation_granularity() noexcept\r\n{\r\n    return static_cast<std::uintptr_t>(sysconf(_SC_PAGE_SIZE));\r\n}\r\n\r\nstatic const std::uintptr_t allocation_granularity_mask{~(get_allocation_granularity() - 1)};\r\n\r\ntemplate<class T>\r\nstruct is_unbounded_array : std::false_type\r\n{\r\n};\r\n\r\ntemplate<class T>\r\nstruct is_unbounded_array<T[]> : std::true_type\r\n{\r\n};\r\n\r\ntemplate<class T>\r\nstruct is_bounded_array : std::false_type\r\n{\r\n};\r\n\r\ntemplate<class T, std::size_t N>\r\nstruct is_bounded_array<T[N]> : std::true_type\r\n{\r\n};\r\n\r\n}\r\n\r\ntemplate<typename T>\r\nstruct map_deleter\r\n{\r\n    void operator()(T* ptr) const noexcept\r\n    {\r\n        if(ptr)\r\n        {\r\n            const auto base_address{reinterpret_cast<std::uintptr_t>(ptr) & impl::allocation_granularity_mask};\r\n\r\n            munmap(reinterpret_cast<void*>(base_address), static_cast<std::size_t>(reinterpret_cast<std::uintptr_t>(ptr) - base_address) + sizeof(T));\r\n        }\r\n    }\r\n};\r\n\r\ntemplate<typename T>\r\nstruct map_deleter<T[]>\r\n{\r\n    void operator()(T* ptr) const noexcept\r\n    {\r\n        if(ptr)\r\n        {\r\n            const auto base_address{reinterpret_cast<std::uintptr_t>(ptr) & impl::allocation_granularity_mask};\r\n\r\n            munmap(reinterpret_cast<void*>(base_address), static_cast<std::size_t>(reinterpret_cast<std::uintptr_t>(ptr) - base_address) + (sizeof(T) * count));\r\n        }\r\n    }\r\n\r\n    std::size_t count{};\r\n};\r\n\r\ntemplate<typename T>\r\nusing unique_map_t = std::unique_ptr<T, map_deleter<T>>;\r\ntemplate<typename T>\r\nusing shared_map_t = std::shared_ptr<T>;\r\ntemplate<typename T>\r\nusing weak_map_t = std::weak_ptr<T>;\r\n\r\nclass shared_memory\r\n{\r\npublic:\r\n    using native_handle_type = int;\r\n\r\npublic:\r\n    constexpr shared_memory() noexcept = default;\r\n\r\n    explicit shared_memory(const std::string& name, std::uint64_t size)\r\n    {\r\n        assert(!std::empty(name) && \"nes::shared_memory::shared_memory called with empty name.\");\r\n        assert(size != 0 && \"nes::shared_memory::shared_memory called with size == 0.\");\r\n\r\n        const auto native_name{shared_memory_root + name};\r\n\r\n        m_handle = shm_open(std::data(native_name), O_RDWR | O_CREAT | O_TRUNC, 0660);\r\n        if(m_handle == -1)\r\n        {\r\n            throw std::runtime_error{\"Failed to create shared memory. \" + std::string{strerror(errno)}};\r\n        }\r\n\r\n        if(ftruncate(m_handle, static_cast<off_t>(size)) == -1)\r\n        {\r\n            close(m_handle);\r\n            throw std::runtime_error{\"Failed to set shared memory size. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    explicit shared_memory(const std::string& name, shared_memory_options options = shared_memory_options::none)\r\n    {\r\n        assert(!std::empty(name) && \"nes::shared_memory::shared_memory called with empty name.\");\r\n\r\n        const auto native_name{shared_memory_root + name};\r\n        const auto access = static_cast<bool>(options & shared_memory_options::constant) ? O_RDONLY : O_RDWR;\r\n\r\n        m_handle = shm_open(std::data(native_name), access, 0660);\r\n        if(m_handle == -1)\r\n        {\r\n            throw std::runtime_error{\"Failed to open shared memory. \" + std::string{strerror(errno)}};\r\n        }\r\n    }\r\n\r\n    ~shared_memory()\r\n    {\r\n        if(m_handle != -1)\r\n        {\r\n            close(m_handle);\r\n        }\r\n    }\r\n\r\n    shared_memory(const shared_memory&) = delete;\r\n    shared_memory& operator=(const shared_memory&) = delete;\r\n\r\n    shared_memory(shared_memory&& other) noexcept\r\n    : m_handle{std::exchange(other.m_handle, -1)}\r\n    {\r\n    }\r\n\r\n    shared_memory& operator=(shared_memory&& other) noexcept\r\n    {\r\n        m_handle = std::exchange(other.m_handle, m_handle);\r\n\r\n        return *this;\r\n    }\r\n\r\n    template<typename T>\r\n    unique_map_t<T> map(std::uint64_t offset, shared_memory_options options = (std::is_const<T>::value ? shared_memory_options::constant : shared_memory_options::none)) const\r\n    {\r\n        static_assert(std::is_trivial<T>::value, \"Behaviour is undefined if T is not a trivial type.\");\r\n        static_assert(!impl::is_unbounded_array<T>::value, \"T can not be an unbounded array type, i.e. T[]. Specify the size, or use the second overload if you don't know it at compile-time\");\r\n        assert(m_handle != -1 && \"nes::shared_memory::map called with an invalid handle.\");\r\n\r\n        const auto access = static_cast<bool>(options & shared_memory_options::constant) ? PROT_READ : PROT_READ | PROT_WRITE;\r\n        const auto aligned_offset{offset & impl::allocation_granularity_mask};\r\n        const auto real_size{static_cast<std::size_t>((offset - aligned_offset) + sizeof(T))};\r\n\r\n        auto* ptr{mmap(nullptr, real_size, access, MAP_SHARED, m_handle, static_cast<off_t>(aligned_offset))};\r\n        if(ptr == MAP_FAILED)\r\n        {\r\n            throw std::runtime_error{\"Failed to map shared memory. \" + std::string{strerror(errno)}};\r\n        }\r\n\r\n        ptr = reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(ptr) + (offset - aligned_offset));\r\n\r\n        return unique_map_t<T>{reinterpret_cast<T*>(ptr)};\r\n    }\r\n\r\n    template<typename T>\r\n    shared_map_t<T> shared_map(std::uint64_t offset, shared_memory_options options = (std::is_const<T>::value ? shared_memory_options::constant : shared_memory_options::none)) const\r\n    {\r\n        return shared_map_t<T>{map<T>(offset, options)};\r\n    }\r\n\r\n    template<typename T, typename ValueType = typename std::remove_extent<T>::type>\r\n    unique_map_t<T> map(std::uint64_t offset, std::size_t count, shared_memory_options options = (std::is_const<ValueType>::value ? shared_memory_options::constant : shared_memory_options::none)) const\r\n    {\r\n        static_assert(std::is_trivial<ValueType>::value, \"Behaviour is undefined if ValueType is not a trivial type.\");\r\n        static_assert(!impl::is_bounded_array<T>::value, \"T is an statically sized array, use the other overload of map instead of this one (remove the second parameter).\");\r\n        static_assert(impl::is_unbounded_array<T>::value, \"T must be an array type, i.e. T[].\");\r\n        assert(m_handle != -1 && \"nes::shared_memory::map called with an invalid handle.\");\r\n\r\n        const auto access = static_cast<bool>(options & shared_memory_options::constant) ? PROT_READ : PROT_READ | PROT_WRITE;\r\n        const auto aligned_offset{offset & impl::allocation_granularity_mask};\r\n        const auto real_size{static_cast<std::size_t>((offset - aligned_offset) + (sizeof(ValueType) * count))};\r\n\r\n        auto* ptr{mmap(nullptr, real_size, access, MAP_SHARED, m_handle, static_cast<off_t>(aligned_offset))};\r\n        if(ptr == MAP_FAILED)\r\n        {\r\n            throw std::runtime_error{\"Failed to map shared memory. \" + std::string{strerror(errno)}};\r\n        }\r\n\r\n        ptr = reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(ptr) + (offset - aligned_offset));\r\n\r\n        return unique_map_t<T>{reinterpret_cast<ValueType*>(ptr), map_deleter<T>{count}};\r\n    }\r\n\r\n    template<typename T, typename ValueType = typename std::remove_extent<T>::type>\r\n    shared_map_t<T> shared_map(std::uint64_t offset, std::size_t count, shared_memory_options options = (std::is_const<ValueType>::value ? shared_memory_options::constant : shared_memory_options::none)) const\r\n    {\r\n        return shared_map_t<T>{map<T>(offset, count, options)};\r\n    }\r\n\r\n    native_handle_type native_handle() const noexcept\r\n    {\r\n        return m_handle;\r\n    }\r\n\r\nprivate:\r\n    native_handle_type m_handle{-1};\r\n};\r\n\r\n}\r\n\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "include/nes/thread_pool.hpp",
    "content": "///////////////////////////////////////////////////////////\n/// Copyright 2020 Alexy Pellegrini\n///\n/// Permission is hereby granted, free of charge,\n/// to any person obtaining a copy of this software\n/// and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction,\n/// including without limitation the rights to use,\n/// copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit\n/// persons to whom the Software is furnished to do so,\n/// subject to the following conditions:\n///\n/// The above copyright notice and this permission notice\n/// shall be included in all copies or substantial portions\n/// of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\n/// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\n/// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n/// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\n/// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\n/// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE\n/// OR OTHER DEALINGS IN THE SOFTWARE.\n///////////////////////////////////////////////////////////\n\n#ifndef NOT_ENOUGH_STANDARDS_THREAD_POOL\n#define NOT_ENOUGH_STANDARDS_THREAD_POOL\n\n#ifdef NES_INLINE_NAMESPACE\n    #define NES_INLINE_NAMESPACE_BEGIN        \\\n        inline namespace NES_INLINE_NAMESPACE \\\n        {\n    #define NES_INLINE_NAMESPACE_END }\n#else\n    #define NES_INLINE_NAMESPACE_BEGIN\n    #define NES_INLINE_NAMESPACE_END\n#endif\n\n#include <vector>\n#include <atomic>\n#include <thread>\n#include <future>\n#include <condition_variable>\n#include <mutex>\n#include <functional>\n#include <iterator>\n#include <variant>\n#include <cassert>\n#include <span>\n#include <utility>\n\nnamespace nes\n{\n\nNES_INLINE_NAMESPACE_BEGIN\n\nnamespace impl\n{\n\nclass checkpoint_holder_base\n{\npublic:\n    checkpoint_holder_base(bool barrier)\n    : m_barrier{barrier}\n    {\n    }\n\n    virtual ~checkpoint_holder_base() = default;\n    checkpoint_holder_base(const checkpoint_holder_base&) = delete;\n    checkpoint_holder_base& operator=(const checkpoint_holder_base&) = delete;\n    checkpoint_holder_base(checkpoint_holder_base&&) = delete;\n    checkpoint_holder_base& operator=(checkpoint_holder_base&&) = delete;\n\n    virtual void set_reset_value(std::size_t value) noexcept = 0;\n    virtual void reset() = 0;\n    virtual bool count_down() noexcept = 0;\n    virtual bool check_barrier() const noexcept = 0;\n\n    bool is_barrier() const noexcept\n    {\n        return m_barrier;\n    }\n\nprivate:\n    bool m_barrier{};\n};\n\ntemplate<typename T>\nclass checkpoint_holder final : public checkpoint_holder_base\n{\npublic:\n    explicit checkpoint_holder(bool barrier, std::future<T>*& user_reference)\n    : checkpoint_holder_base{barrier}\n    {\n        user_reference = &m_future;\n    }\n\n    ~checkpoint_holder() = default;\n    checkpoint_holder(const checkpoint_holder&) = delete;\n    checkpoint_holder& operator=(const checkpoint_holder&) = delete;\n    checkpoint_holder(checkpoint_holder&&) = delete;\n    checkpoint_holder& operator=(checkpoint_holder&&) = delete;\n\n    void set_reset_value(std::size_t value) noexcept override\n    {\n        m_reset_value = value;\n    }\n\n    void reset() override\n    {\n        m_promise = std::promise<void>{};\n        m_future = m_promise.get_future();\n\n        m_counter.store(m_reset_value, std::memory_order_release);\n    }\n\n    bool count_down() noexcept override\n    {\n        const bool last{--(m_counter) == 0};\n\n        if(last)\n        {\n            m_promise.set_value();\n        }\n\n        return last;\n    }\n\n    bool check_barrier() const noexcept override\n    {\n        return m_counter.load(std::memory_order_acquire) == 1;\n    }\n\nprivate:\n    std::promise<T> m_promise{};\n    std::future<T> m_future{};\n    std::atomic<std::size_t> m_counter{};\n    std::size_t m_reset_value{};\n};\n\nclass checkpoint // checkpoints and barriers type\n{\npublic:\n    template<typename T>\n    checkpoint(bool barrier, std::future<T>*& user_reference)\n    : m_checkpoint{std::make_unique<checkpoint_holder<T>>(barrier, user_reference)}\n    {\n    }\n\n    ~checkpoint() = default;\n    checkpoint(const checkpoint&) = delete;\n    checkpoint& operator=(const checkpoint&) = delete;\n    checkpoint(checkpoint&&) = default;\n    checkpoint& operator=(checkpoint&&) = default;\n\n    void set_reset_value(std::size_t value) noexcept\n    {\n        m_checkpoint->set_reset_value(value);\n    }\n\n    void reset()\n    {\n        m_checkpoint->reset();\n    }\n\n    bool count_down() noexcept\n    {\n        return m_checkpoint->count_down();\n    }\n\n    bool check_barrier() const noexcept\n    {\n        return m_checkpoint->check_barrier();\n    }\n\n    bool is_barrier() const noexcept\n    {\n        return m_checkpoint->is_barrier();\n    }\n\n    checkpoint_holder_base* base() const noexcept\n    {\n        return m_checkpoint.get();\n    }\n\nprivate:\n    std::unique_ptr<checkpoint_holder_base> m_checkpoint{};\n};\n\nusing checkpoint_range = std::span<checkpoint_holder_base*>;\n\nclass task_holder_base // base class for type erasing in task\n{\npublic:\n    task_holder_base() = default;\n    virtual ~task_holder_base() = default;\n    task_holder_base(const task_holder_base&) = delete;\n    task_holder_base& operator=(const task_holder_base&) = delete;\n    task_holder_base(task_holder_base&&) = delete;\n    task_holder_base& operator=(task_holder_base&&) = delete;\n\n    virtual void reset() = 0;\n    virtual void execute() = 0;\n\n    // checkpoints to count down once this task is done\n    void set_checkpoint_range(checkpoint_range checkpoints)\n    {\n        m_checkpoints = checkpoints;\n    }\n\nprotected:\n    void trigger_checkpoints()\n    {\n        for(auto& checkpoint : m_checkpoints)\n        {\n            checkpoint->count_down();\n        }\n    }\n\nprivate:\n    checkpoint_range m_checkpoints{};\n};\n\n// task holder that not returns a value, used for execute function\ntemplate<typename Func>\nclass task_holder final : public task_holder_base\n{\npublic:\n    task_holder(Func&& func)\n    : m_func{std::move(func)}\n    {\n    }\n\n    ~task_holder() = default;\n    task_holder(const task_holder&) = delete;\n    task_holder& operator=(const task_holder&) = delete;\n    task_holder(task_holder&&) = delete;\n    task_holder& operator=(task_holder&&) = delete;\n\n    void execute() override\n    {\n        m_func();\n        trigger_checkpoints();\n    }\n\n    void reset() override\n    {\n    }\n\nprivate:\n    Func m_func;\n};\n\n// task holder that returns a value, used for invoke function\ntemplate<typename Func, typename Ret>\nclass return_task_holder final : public task_holder_base\n{\npublic:\n    return_task_holder(Func&& func, std::future<Ret>*& user_reference)\n    : m_func{std::move(func)}\n    {\n        user_reference = &m_future;\n    }\n\n    ~return_task_holder() = default;\n    return_task_holder(const return_task_holder&) = delete;\n    return_task_holder& operator=(const return_task_holder&) = delete;\n    return_task_holder(return_task_holder&&) = delete;\n    return_task_holder& operator=(return_task_holder&&) = delete;\n\n    void execute() override\n    {\n        if constexpr(std::is_same_v<Ret, void>)\n        {\n            m_func();\n            m_promise.set_value();\n        }\n        else\n        {\n            m_promise.set_value(m_func());\n        }\n\n        trigger_checkpoints();\n    }\n\n    void reset() override\n    {\n        m_promise = std::promise<Ret>{};\n        m_future = m_promise.get_future();\n    }\n\nprivate:\n    Func m_func;\n    std::promise<Ret> m_promise{};\n    std::future<Ret> m_future{};\n};\n\nclass task // actual execution unit\n{\npublic:\n    template<typename Func>\n    task(Func&& func)\n    : m_holder{std::make_unique<task_holder<Func>>(std::forward<Func>(func))}\n    {\n    }\n\n    template<typename Func, typename Ret>\n    task(Func&& func, std::future<Ret>*& user_reference)\n    : m_holder{std::make_unique<return_task_holder<Func, Ret>>(std::forward<Func>(func), user_reference)}\n    {\n    }\n\n    ~task() = default;\n    task(const task&) = delete;\n    task& operator=(const task&) = delete;\n    task(task&&) = default;\n    task& operator=(task&&) = default;\n\n    void set_checkpoint_range(checkpoint_range checkpoints)\n    {\n        m_holder->set_checkpoint_range(checkpoints);\n    }\n\n    void execute()\n    {\n        m_holder->execute();\n    }\n\n    void reset()\n    {\n        m_holder->reset();\n    }\n\n    task_holder_base* holder() const noexcept\n    {\n        return m_holder.get();\n    }\n\nprivate:\n    std::unique_ptr<task_holder_base> m_holder{};\n};\n\nclass fence_holder\n{\npublic:\n    fence_holder() = default;\n    ~fence_holder() = default;\n    fence_holder(const fence_holder&) = delete;\n    fence_holder& operator=(const fence_holder&) = delete;\n    fence_holder(fence_holder&&) = delete;\n    fence_holder& operator=(fence_holder&&) = delete;\n\n    void set_condition(std::condition_variable& condition) noexcept\n    {\n        m_condition = &condition;\n    }\n\n    void reset() noexcept\n    {\n        m_signaled.store(false, std::memory_order_release);\n    }\n\n    void signal() noexcept\n    {\n        m_signaled.store(true, std::memory_order_release);\n        m_condition->notify_one();\n    }\n\n    bool is_signaled() const noexcept\n    {\n        return m_signaled.load(std::memory_order_acquire);\n    }\n\nprivate:\n    std::condition_variable* m_condition{};\n    std::atomic<bool> m_signaled{};\n};\n\nclass fence\n{\npublic:\n    fence()\n    : m_holder{std::make_unique<fence_holder>()}\n    {\n    }\n\n    ~fence() = default;\n    fence(const fence&) = delete;\n    fence& operator=(const fence&) = delete;\n    fence(fence&&) = default;\n    fence& operator=(fence&&) = default;\n\n    void set_condition(std::condition_variable& condition) noexcept\n    {\n        m_holder->set_condition(condition);\n    }\n\n    void reset() noexcept\n    {\n        m_holder->reset();\n    }\n\n    void signal() noexcept\n    {\n        m_holder->signal();\n    }\n\n    bool is_signaled() const noexcept\n    {\n        return m_holder->is_signaled();\n    }\n\n    fence_holder* holder() const noexcept\n    {\n        return m_holder.get();\n    }\n\nprivate:\n    std::unique_ptr<fence_holder> m_holder{};\n};\n\nusing task_type = std::variant<checkpoint, task, fence>;\n\n}\n\ntemplate<typename T>\nclass task_result\n{\n    friend class task_builder;\n\npublic:\n    task_result() = default;\n    ~task_result() = default;\n    task_result(const task_result&) = delete;\n    task_result& operator=(const task_result&) = delete;\n\n    task_result(task_result&& other) noexcept\n    : m_state{std::exchange(other.m_state, nullptr)}\n    {\n    }\n\n    task_result& operator=(task_result&& other) noexcept\n    {\n        m_state = std::exchange(other.m_state, nullptr);\n\n        return *this;\n    }\n\n    T get()\n    {\n        return m_state->get();\n    }\n\n    bool valid() const noexcept\n    {\n        return m_state->valid();\n    }\n\n    void wait() const\n    {\n        m_state->wait();\n    }\n\n    template<class Rep, class Period>\n    bool wait_for(const std::chrono::duration<Rep, Period>& timeout) const\n    {\n        return m_state->wait_for(timeout) == std::future_status::ready;\n    }\n\n    template<class Rep, class Period>\n    bool wait_until(const std::chrono::duration<Rep, Period>& timeout) const\n    {\n        return m_state->wait_until(timeout) == std::future_status::ready;\n    }\n\nprivate:\n    std::future<T>* m_state{};\n};\n\nusing task_checkpoint = task_result<void>;\n\nclass task_fence\n{\n    friend class task_builder;\n\npublic:\n    task_fence() = default;\n    ~task_fence() = default;\n    task_fence(const task_fence&) = delete;\n    task_fence& operator=(const task_fence&) = delete;\n\n    task_fence(task_fence&& other) noexcept\n    : m_holder{std::exchange(other.m_holder, nullptr)}\n    {\n    }\n\n    task_fence& operator=(task_fence&& other) noexcept\n    {\n        m_holder = std::exchange(other.m_holder, nullptr);\n\n        return *this;\n    }\n\n    void signal() noexcept\n    {\n        m_holder->signal();\n    }\n\nprivate:\n    impl::fence_holder* m_holder{};\n};\n\nclass task_list\n{\n    friend class thread_pool;\n    friend class task_builder;\n\npublic:\n    constexpr task_list() = default;\n    ~task_list() = default;\n    task_list(const task_list&) = delete;\n    task_list& operator=(const task_list&) = delete;\n    task_list(task_list&&) = default;\n    task_list& operator=(task_list&&) = default;\n\nprivate:\n    void reset(std::condition_variable& condition)\n    {\n        for(auto& task : m_tasks)\n        {\n            std::visit([&condition](auto&& task)\n            {\n                using alternative_type = std::decay_t<decltype(task)>;\n\n                if constexpr(std::is_same_v<alternative_type, impl::fence>)\n                {\n                    task.set_condition(condition);\n                }\n\n                task.reset();\n            },\n            task);\n        }\n\n        m_current = std::begin(m_tasks);\n    }\n\n    // bool: is the task_list done after that ?\n    // count: number of task executable at call time\n    // outputit: outputis to store the tasks\n    template<typename OutputIt>\n    std::pair<bool, std::size_t> next(OutputIt output)\n    {\n        std::size_t count{};\n\n        while(m_current != std::end(m_tasks))\n        {\n            if(std::holds_alternative<impl::checkpoint>(*m_current))\n            {\n                auto& checkpoint{std::get<impl::checkpoint>(*m_current)};\n\n                if(checkpoint.is_barrier() && !checkpoint.check_barrier())\n                {\n                    return std::make_pair(false, count);\n                }\n\n                checkpoint.count_down();\n            }\n            else if(std::holds_alternative<impl::task>(*m_current))\n            {\n                auto& task{std::get<impl::task>(*m_current)};\n\n                *output++ = task.holder();\n                ++count;\n            }\n            else\n            {\n                auto& fence{std::get<impl::fence>(*m_current)};\n\n                if(!fence.is_signaled())\n                {\n                    return std::make_pair(false, count);\n                }\n            }\n\n            ++m_current;\n        }\n\n        return std::make_pair(true, count);\n    }\n\nprivate:\n    std::vector<impl::task_type> m_tasks{};\n    std::vector<impl::task_type>::iterator m_current{};\n    std::vector<impl::checkpoint_holder_base*> m_checkpoints{};\n};\n\nclass task_builder\n{\npublic:\n    explicit task_builder(std::uint32_t thread_count = std::thread::hardware_concurrency())\n    : m_thread_count{thread_count != 0 ? thread_count : 8}\n    {\n        m_tasks.reserve(32);\n    }\n\n    ~task_builder() = default;\n    task_builder(const task_builder&) = delete;\n    task_builder& operator=(const task_builder&) = delete;\n    task_builder(task_builder&&) = default;\n    task_builder& operator=(task_builder&&) = default;\n\n    template<typename Func, typename... Args>\n    void execute(Func&& func, Args&&... args)\n    {\n        push_task([func = std::forward<Func>(func), ... args = std::forward<Args>(args)]() mutable\n        {\n            std::invoke(func, args...);\n        });\n    }\n\n    template<typename Func, typename... Args>\n    [[nodiscard(\"Use execute instead of invoke when the returned future is not needed\")]] \n    auto invoke(Func&& func, Args&&... args)\n    {\n        using func_return_type = std::invoke_result_t<Func, Args...>;\n        using result_type = task_result<func_return_type>;\n\n        result_type output{};\n\n        push_task([func = std::forward<Func>(func), ... args = std::forward<Args>(args)]() mutable\n        {\n            return std::invoke(func, args...);\n        },\n        output.m_state);\n\n        return output;\n    }\n\n    template<typename Func, typename... Args>\n    void dispatch(std::uint32_t x, std::uint32_t y, std::uint32_t z, Func&& func, Args&&... args)\n    {\n        dispatch(x, y, z, std::numeric_limits<std::uint32_t>::max(), std::forward<Func>(func), std::forward<Args>(args)...);\n    }\n\n    template<typename Func, typename... Args>\n    void dispatch(std::uint32_t x, std::uint32_t y, std::uint32_t z, std::uint32_t max_invoke_per_task, Func&& func, Args&&... args)\n    {\n        assert(x != 0 && \"nes::task_builder::dispatch called with x == 0\");\n        assert(y != 0 && \"nes::task_builder::dispatch called with y == 0\");\n        assert(z != 0 && \"nes::task_builder::dispatch called with z == 0\");\n        assert(max_invoke_per_task != 0 && \"nes::task_builder::dispatch called with max_invoke_per_task == 0\");\n\n        const std::uint64_t total_calls{static_cast<std::uint64_t>(x) * y * z};\n\n        if(total_calls < m_thread_count) // one invocation per thread\n        {\n            for(std::uint32_t current_z{}; current_z < z; ++current_z)\n            {\n                for(std::uint32_t current_y{}; current_y < y; ++current_y)\n                {\n                    for(std::uint32_t current_x{}; current_x < x; ++current_x)\n                    {\n                        push_task([current_x, current_y, current_z, func = std::forward<Func>(func), ... args = std::forward<Args>(args)]() mutable\n                        {\n                            std::invoke(func, current_x, current_y, current_z, args...);\n                        });\n                    }\n                }\n            }\n        }\n        else if(total_calls <= static_cast<std::uint64_t>(max_invoke_per_task) * m_thread_count) // max_invoke_per_task or less invocations per thread\n        {\n            const std::uint64_t calls_per_thread{total_calls / m_thread_count};\n\n            std::uint64_t remainder{total_calls % m_thread_count};\n            std::uint64_t calls{};\n\n            while(calls < total_calls)\n            {\n                std::uint64_t count{calls_per_thread};\n\n                if(remainder > 0)\n                {\n                    ++count;\n                    --remainder;\n                }\n\n                push_task([calls, count, x, y, z_factor = (x * y), func = std::forward<Func>(func), ... args = std::forward<Args>(args)]() mutable\n                {\n                    for(std::uint64_t i{calls}; i < calls + count; ++i)\n                    {\n                        const auto current_x{static_cast<std::uint32_t>(i % x)};\n                        const auto current_y{static_cast<std::uint32_t>((i / x) % y)};\n                        const auto current_z{static_cast<std::uint32_t>(i / z_factor)};\n\n                        std::invoke(func, current_x, current_y, current_z, args...);\n                    }\n                });\n\n                calls += count;\n            }\n        }\n        else // create tasks that perform max_invoke_per_task invocations each, until we reach the total number of invocations\n        {\n            std::uint64_t calls{};\n            while(calls < total_calls)\n            {\n                std::uint64_t count{std::min(static_cast<std::size_t>(max_invoke_per_task), total_calls - calls)};\n\n                push_task([calls, count, x, y, z_factor = (x * y), func = std::forward<Func>(func), ... args = std::forward<Args>(args)]() mutable\n                {\n                    for(std::uint64_t i{calls}; i < calls + count; ++i)\n                    {\n                        const auto current_x{static_cast<std::uint32_t>(i % x)};\n                        const auto current_y{static_cast<std::uint32_t>((i / x) % y)};\n                        const auto current_z{static_cast<std::uint32_t>(i / z_factor)};\n\n                        std::invoke(func, current_x, current_y, current_z, args...);\n                    }\n                });\n\n                calls += count;\n            }\n        }\n    }\n\n    task_checkpoint barrier()\n    {\n        task_result<void> output{};\n        m_tasks.emplace_back(std::in_place_type<impl::checkpoint>, true, output.m_state);\n\n        return output;\n    }\n\n    [[nodiscard]] task_checkpoint checkpoint()\n    {\n        task_result<void> output{};\n        m_tasks.emplace_back(std::in_place_type<impl::checkpoint>, false, output.m_state);\n\n        return output;\n    }\n\n    [[nodiscard]] task_fence fence()\n    {\n        task_fence output{};\n        output.m_holder = std::get<impl::fence>(m_tasks.emplace_back(std::in_place_type<impl::fence>)).holder();\n\n        return output;\n    }\n\n    task_list build()\n    {\n        barrier(); // this barrier is used by the pool to know when all the tasks are done and the list can be returned to user\n\n        task_list output{};\n        output.m_tasks.reserve(std::size(m_tasks));\n        output.m_checkpoints.reserve(count_checkpoints());\n\n        auto begin{std::begin(m_tasks)};\n        auto current{begin};\n\n        std::size_t checkpoints_begin{};\n        std::size_t checkpoints_size{};\n\n        while(current != std::end(m_tasks))\n        {\n            if(std::holds_alternative<impl::checkpoint>(*current))\n            {\n                auto& checkpoint{std::get<impl::checkpoint>(*current)};\n\n                output.m_checkpoints.emplace_back(checkpoint.base());\n                ++checkpoints_size;\n\n                if(checkpoint.is_barrier())\n                {\n                    const auto checkpoints_it{std::begin(output.m_checkpoints) + checkpoints_begin};\n\n                    flush(output.m_tasks, checkpoints_it, checkpoints_it + checkpoints_size, begin, current + 1);\n\n                    checkpoints_begin += checkpoints_size;\n                    checkpoints_size = 0;\n\n                    begin = current + 1;\n                }\n            }\n\n            ++current;\n        }\n\n        m_tasks.clear();\n\n        return output;\n    }\n\nprivate:\n    template<typename... Args>\n    void push_task(Args&&... args)\n    {\n        m_tasks.emplace_back(std::in_place_type<impl::task>, std::forward<Args>(args)...);\n    }\n\n    template<typename InputIt, typename CheckpointIt>\n    void flush(std::vector<impl::task_type>& output, CheckpointIt checkpoints_begin, CheckpointIt checkpoints_end, InputIt begin, InputIt end)\n    {\n        std::size_t checkpoint_counter{};\n\n        while(begin != end)\n        {\n            if(std::holds_alternative<impl::checkpoint>(*begin))\n            {\n                auto checkpoint{std::get<impl::checkpoint>(std::move(*begin))};\n\n                checkpoint.set_reset_value(checkpoint_counter + 1); //+ 1 for the caller\n                output.emplace_back(std::in_place_type<impl::checkpoint>, std::move(checkpoint));\n\n                ++checkpoints_begin;\n            }\n            else if(std::holds_alternative<impl::task>(*begin))\n            {\n                auto task{std::get<impl::task>(std::move(*begin))};\n\n                task.set_checkpoint_range(impl::checkpoint_range{checkpoints_begin, checkpoints_end});\n                output.emplace_back(std::in_place_type<impl::task>, std::move(task));\n\n                ++checkpoint_counter;\n            }\n            else\n            {\n                auto fence{std::get<impl::fence>(std::move(*begin))};\n\n                output.emplace_back(std::in_place_type<impl::fence>, std::move(fence));\n            }\n\n            ++begin;\n        }\n    }\n\n    std::size_t count_checkpoints() const noexcept\n    {\n        return std::count_if(std::begin(m_tasks), std::end(m_tasks), [](auto&& task)\n        {\n            return std::holds_alternative<impl::checkpoint>(task);\n        });\n    }\n\nprivate:\n    std::uint32_t m_thread_count{};\n    std::vector<impl::task_type> m_tasks{};\n};\n\nclass thread_pool\n{\npublic:\n    explicit thread_pool(std::size_t thread_count = std::thread::hardware_concurrency())\n    {\n        const auto worker_base = [this]()\n        {\n            worker_main();\n        };\n\n        thread_count = thread_count != 0 ? thread_count : 8;\n\n        m_threads.reserve(thread_count);\n        for(std::size_t i{}; i < thread_count; ++i)\n        {\n            m_threads.emplace_back(worker_base);\n        }\n\n        m_tasks.reserve(4 * thread_count);\n        m_task_lists.reserve(4 * thread_count);\n    }\n\n    ~thread_pool()\n    {\n        std::unique_lock lock{m_mutex};\n        m_wait_condition.wait(lock, [this]\n        {\n            return std::empty(m_tasks) && std::empty(m_task_lists);\n        });\n\n        m_running = false;\n\n        lock.unlock();\n\n        m_worker_condition.notify_all();\n\n        for(auto&& thread : m_threads)\n        {\n            if(thread.joinable())\n            {\n                thread.join();\n            }\n        }\n    }\n\n    thread_pool(const thread_pool&) = delete;\n    thread_pool& operator=(const thread_pool&) = delete;\n    thread_pool(thread_pool&&) = delete;\n    thread_pool& operator=(thread_pool&&) = delete;\n\n    template<typename Func, typename... Args>\n    void execute(Func&& func, Args&&... args)\n    {\n        push_impl([func = std::forward<Func>(func), ... args = std::forward<Args>(args)]() mutable\n        {\n            std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);\n        });\n\n        m_worker_condition.notify_one();\n    }\n\n    template<typename Func, typename... Args>\n    [[nodiscard(\"Use execute instead of invoke when the returned future is not needed\")]]\n    auto invoke(Func&& func, Args&&... args)\n    {\n        using return_type = std::invoke_result_t<Func, Args...>;\n        using promise_type = std::promise<return_type>;\n        using future_type = std::future<return_type>;\n\n        promise_type promise{};\n        future_type future{promise.get_future()};\n\n        if constexpr(std::is_same_v<return_type, void>)\n        {\n            push_impl([promise = std::move(promise), func = std::forward<Func>(func), ... args = std::forward<Args>(args)]() mutable\n            {\n                std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);\n                promise.set_value();\n            });\n        }\n        else\n        {\n            push_impl([promise = std::move(promise), func = std::forward<Func>(func), ... args = std::forward<Args>(args)]() mutable\n            {\n                promise.set_value(std::invoke(std::forward<Func>(func), std::forward<Args>(args)...));\n            });\n        }\n\n        m_worker_condition.notify_one();\n\n        return future;\n    }\n\n    std::future<task_list> push(task_list&& list)\n    {\n        list.reset(m_worker_condition);\n\n        std::unique_lock lock{m_mutex};\n\n        auto& data{m_task_lists.emplace_back()};\n        data.list = std::move(list);\n\n        auto future{data.promise.get_future()};\n\n        const auto notify_count{update_task_lists()};\n\n        lock.unlock();\n\n        for(std::size_t i{}; i < std::min(notify_count, std::size(m_threads)); ++i)\n        {\n            m_worker_condition.notify_one();\n        }\n\n        return future;\n    }\n\n    void wait_idle()\n    {\n        std::unique_lock lock{m_mutex};\n        m_wait_condition.wait(lock, [this]\n        {\n            return std::empty(m_tasks) && std::empty(m_task_lists);\n        });\n    }\n\n    std::size_t thread_count() const noexcept\n    {\n        return std::size(m_threads);\n    }\n\nprivate:\n    void worker_main()\n    {\n        while(true)\n        {\n            std::unique_lock lock{m_mutex};\n            m_worker_condition.wait(lock, [this]\n            {\n                if(std::empty(m_tasks) && !std::empty(m_task_lists))\n                {\n                    const auto notify_count{update_task_lists()};\n\n                    for(std::size_t i{}; i < std::min(notify_count, std::size(m_threads)); ++i)\n                    {\n                        m_worker_condition.notify_one();\n                    }\n                }\n\n                if(std::empty(m_tasks) && std::empty(m_task_lists))\n                {\n                    m_wait_condition.notify_all();\n                }\n\n                return !m_running || !std::empty(m_tasks);\n            });\n\n            if(!m_running)\n            {\n                break;\n            }\n\n            auto task{std::move(m_tasks.front())};\n            m_tasks.erase(std::begin(m_tasks));\n\n            lock.unlock();\n\n            if(std::holds_alternative<impl::task>(task))\n            {\n                std::get<impl::task>(task).execute();\n            }\n            else\n            {\n                std::get<impl::task_holder_base*>(task)->execute();\n            }\n        }\n    }\n\n    template<typename Func>\n    void push_impl(Func&& func)\n    {\n        std::lock_guard lock{m_mutex};\n\n        m_tasks.emplace_back(std::in_place_type<impl::task>, std::forward<Func>(func));\n    }\n\n    std::size_t update_task_lists()\n    {\n        std::size_t notify_count{};\n        bool need_free{};\n\n        for(auto& list : m_task_lists)\n        {\n            const auto [end, count] = list.list.next(std::back_inserter(m_tasks));\n\n            if(end && count == 0)\n            {\n                list.promise.set_value(std::move(list.list));\n                list.need_free = true;\n\n                need_free = true;\n            }\n\n            notify_count += count;\n        }\n\n        if(need_free)\n        {\n            const auto predicate = [](const task_list_data& list)\n            {\n                return list.need_free;\n            };\n\n            m_task_lists.erase(std::remove_if(std::begin(m_task_lists), std::end(m_task_lists), predicate), std::end(m_task_lists));\n        }\n\n        return notify_count;\n    }\n\nprivate:\n    struct task_list_data\n    {\n        task_list list{};\n        std::promise<task_list> promise{};\n        bool need_free{};\n    };\n\nprivate:\n    std::vector<std::thread> m_threads{};\n\n    std::vector<std::variant<impl::task, impl::task_holder_base*>> m_tasks{};\n    std::vector<task_list_data> m_task_lists{};\n\n    std::condition_variable m_worker_condition{};\n    std::condition_variable m_wait_condition{};\n    std::mutex m_mutex{};\n\n    bool m_running{true};\n};\n\nNES_INLINE_NAMESPACE_END\n\n}\n\n#endif\n"
  },
  {
    "path": "tests/common.hpp",
    "content": "///////////////////////////////////////////////////////////\n/// Copyright 2019 Alexy Pellegrini\n///\n/// Permission is hereby granted, free of charge,\n/// to any person obtaining a copy of this software\n/// and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction,\n/// including without limitation the rights to use,\n/// copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit\n/// persons to whom the Software is furnished to do so,\n/// subject to the following conditions:\n///\n/// The above copyright notice and this permission notice\n/// shall be included in all copies or substantial portions\n/// of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\n/// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\n/// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n/// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\n/// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\n/// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE\n/// OR OTHER DEALINGS IN THE SOFTWARE.\n///////////////////////////////////////////////////////////\n\n#ifndef NOT_ENOUGH_STANDARDS_TESTS_COMMON\n#define NOT_ENOUGH_STANDARDS_TESTS_COMMON\n\n#include <cstdlib>\n#include <cstdint>\n#include <iostream>\n\n#define CHECK(expr, message)                                                                \\\n    do                                                                                      \\\n    {                                                                                       \\\n        if(!(expr))                                                                         \\\n        {                                                                                   \\\n            std::cerr << \"Error: Test failed at \" << __FILE__ << \" L.\" << __LINE__ << \":\\n\" \\\n                      << message << std::endl;                                              \\\n            std::exit(1);                                                                   \\\n        }                                                                                   \\\n    } while(0)\n\n\nenum class data_type : std::uint32_t\n{\n    uint32 = 1,\n    float64,\n    string\n};\n\ninline std::string data_type_to_string(data_type type)\n{\n    switch(type)\n    {\n    case data_type::uint32: return \"uint32\";\n    case data_type::float64: return \"float64\";\n    case data_type::string: return \"string\";\n    }\n\n    return \"Wrong type\";\n}\n\n#endif\n"
  },
  {
    "path": "tests/library.cpp",
    "content": "#include <cstdint>\n\n#ifdef _WIN32\n    #define NES_EXPORT __declspec(dllexport)\n#else\n    #define NES_EXPORT\n#endif\n\nextern \"C\" NES_EXPORT std::int32_t nes_lib_func();\nextern \"C\" NES_EXPORT std::int32_t nes_lib_func()\n{\n    return 42;\n}\n"
  },
  {
    "path": "tests/process.cpp",
    "content": "#include <iostream>\n#include <thread>\n#include <mutex>\n#include <shared_mutex>\n#include <iterator>\n#include <iomanip>\n#include <array>\n#include <cassert>\n#include <cstdlib>\n#include <random>\n\n#include <nes/pipe.hpp>\n#include <nes/shared_library.hpp>\n#include <nes/process.hpp>\n#include <nes/shared_memory.hpp>\n#include <nes/named_mutex.hpp>\n#include <nes/semaphore.hpp>\n#include <nes/named_semaphore.hpp>\n\n#include \"common.hpp\"\n\n#if defined(NES_WIN32_PROCESS)\nconstexpr const char* other_path{\"NotEnoughStandardsTestOther.exe\"};\nconstexpr const char* lib_path{\"NotEnoughStandardsTestLib.dll\"};\n#elif defined(NES_POSIX_PROCESS)\nconstexpr const char* other_path{\"./NotEnoughStandardsTestOther\"};\nconstexpr const char* lib_path{\"./NotEnoughStandardsTestLib.so\"};\n#endif\n\nstatic void shared_library_test()\n{\n    nes::shared_library lib{lib_path};\n\n    auto func = lib.load<std::int32_t()>(\"nes_lib_func\");\n\n    CHECK(func, \"Can not load library \\\"\" << lib_path << \"\\\"\");\n    const auto value{func()};\n    CHECK(value == 42, \"Function returned wrong value \" << value);\n}\n\nstatic void a_thread(nes::basic_pipe_istream<char>& is) noexcept\n{\n    data_type type{};\n\n    is.read(reinterpret_cast<char*>(&type), sizeof(data_type));\n    CHECK(type == data_type::uint32, \"Wrong data type, expected uint32 got \" << data_type_to_string(type));\n\n    std::uint32_t uint_value{};\n    is.read(reinterpret_cast<char*>(&uint_value), sizeof(std::uint32_t));\n    CHECK(uint_value == 42, \"Wrong value, expected 42 got \" << uint_value);\n\n    is.read(reinterpret_cast<char*>(&type), sizeof(data_type));\n    CHECK(type == data_type::float64, \"Wrong data type, expected float64 got \" << data_type_to_string(type));\n\n    double float_value{};\n    is.read(reinterpret_cast<char*>(&float_value), sizeof(double));\n    CHECK(float_value > 3.139 && float_value < 3.141, \"Wrong value, expected 3.14 got \" << float_value);\n\n    is.read(reinterpret_cast<char*>(&type), sizeof(data_type));\n    CHECK(type == data_type::string, \"Wrong data type, expected string got \" << data_type_to_string(type));\n\n    std::uint64_t str_size{};\n    is.read(reinterpret_cast<char*>(&str_size), sizeof(std::uint64_t));\n    std::string str_value{};\n    str_value.resize(str_size);\n    is.read(std::data(str_value), static_cast<std::streamsize>(str_size));\n\n    is.read(reinterpret_cast<char*>(&type), sizeof(data_type));\n    CHECK(type == data_type::string, \"Wrong data type, expected string got \" << data_type_to_string(type));\n    is.read(reinterpret_cast<char*>(&str_size), sizeof(std::uint64_t));\n    str_value.clear();\n    is >> str_value;\n    CHECK(str_value == \"Hello\", \"Wrong value, expected \\\"Hello\\\" got \\\"\" << str_value << \"\\\"\");\n    str_value.clear();\n    str_value.resize(3);\n    is.read(std::data(str_value), 3);\n    CHECK(str_value == \" wo\", \"Wrong value, expected \\\" wo\\\" got \\\"\" << str_value << \"\\\"\");\n    is >> str_value;\n    CHECK(str_value == \"rld!\", \"Wrong value, expected \\\"rld!\\\" got \\\"\" << str_value << \"\\\"\");\n\n}\n\nstatic void pipe_test()\n{\n    auto [is, os] = nes::make_anonymous_pipe();\n\n    std::thread thread{a_thread, std::ref(is)};\n\n    const data_type uint_type{data_type::uint32};\n    const std::uint32_t uint_value{42};\n\n    os.write(reinterpret_cast<const char*>(&uint_type), sizeof(data_type));\n    os.write(reinterpret_cast<const char*>(&uint_value), sizeof(std::uint32_t));\n\n    const data_type float_type{data_type::float64};\n    const double float_value{3.14};\n\n    os.write(reinterpret_cast<const char*>(&float_type), sizeof(data_type));\n    os.write(reinterpret_cast<const char*>(&float_value), sizeof(double));\n\n    const data_type str_type{data_type::string};\n    const std::string str_value{\"Hello world!\"};\n    const std::uint64_t size_size{std::size(str_value)};\n\n    os.write(reinterpret_cast<const char*>(&str_type), sizeof(data_type));\n    os.write(reinterpret_cast<const char*>(&size_size), sizeof(std::uint64_t));\n    os.write(std::data(str_value), static_cast<std::streamsize>(size_size));\n\n    // send it twice to test formatted input too\n    os.write(reinterpret_cast<const char*>(&str_type), sizeof(data_type));\n    os.write(reinterpret_cast<const char*>(&size_size), sizeof(std::uint64_t));\n    os.put(str_value[0]);\n    os.write(str_value.data() + 1, str_value.size() - 1);\n\n    os.close();\n\n    if(thread.joinable())\n    {\n        thread.join();\n    }\n}\n\nstatic void another_thread(const std::array<std::uint32_t, 8>& data, nes::semaphore& semaphore)\n{\n    for(std::uint32_t i{}; i < 8; ++i)\n    {\n        semaphore.acquire();\n\n        CHECK(data[i] == i, \"Wrong value expected \" << i << \" got \" << data[i]);\n    }\n}\n\nstatic void semaphore_test()\n{\n    std::array<std::uint32_t, 8> data{0, 1};\n    nes::semaphore semaphore{2};\n    std::thread thread{another_thread, std::cref(data), std::ref(semaphore)};\n\n    for(std::uint32_t i{2}; i < 8; ++i)\n    {\n        data[i] = i;\n\n        semaphore.release();\n    }\n\n    if(thread.joinable())\n    {\n        thread.join();\n    }\n}\n\nstatic void named_pipe_test()\n{\n    nes::process other{other_path, std::vector<std::string>{\"named pipe\"}, nes::process_options::grab_stdout};\n\n    nes::pipe_ostream os{\"nes_test_pipe\"};\n    CHECK(os, \"Failed to open pipe.\");\n\n    const data_type uint_type{data_type::uint32};\n    const std::uint32_t uint_value{42};\n\n    os.write(reinterpret_cast<const char*>(&uint_type), sizeof(data_type));\n    os.write(reinterpret_cast<const char*>(&uint_value), sizeof(std::uint32_t));\n\n    const data_type float_type{data_type::float64};\n    const double float_value{3.14};\n\n    os.write(reinterpret_cast<const char*>(&float_type), sizeof(data_type));\n    os.write(reinterpret_cast<const char*>(&float_value), sizeof(double));\n\n    const data_type str_type{data_type::string};\n    const std::string str_value{\"Hello world!\"};\n    const std::uint64_t size_size{std::size(str_value)};\n\n    os.write(reinterpret_cast<const char*>(&str_type), sizeof(data_type));\n    os.write(reinterpret_cast<const char*>(&size_size), sizeof(std::uint64_t));\n    os.write(std::data(str_value), static_cast<std::streamsize>(size_size));\n\n    os.close();\n\n    CHECK(other.joinable(), \"Process is not joinable\");\n    other.join();\n    CHECK(other.return_code() == 0, \"Other process failed with code \" << other.return_code() << \":\\n\"\n                                                                      << other.stdout_stream().rdbuf());\n}\n\nstatic void process_test()\n{\n    nes::process other{\n    other_path, {\"Hey!\", \"\\\\\\\"12\\\"\\\"\\\\\\\\\\\\\", \"\\\\42\\\\\", \"It's \\\"me\\\"!\"},\n     nes::process_options::grab_stdout\n    };\n    std::cout << other.stdout_stream().rdbuf() << std::endl;\n\n    CHECK(other.joinable(), \"Process is not joinable\");\n    other.join();\n    CHECK(other.return_code() == 0, \"Other process failed with code \" << other.return_code() << \":\\n\"\n                                                                      << other.stdout_stream().rdbuf());\n}\n\nstatic void process_kill_test()\n{\n    nes::process other{other_path, std::vector<std::string>{\"process kill\"}, nes::process_options::grab_stdout};\n    std::this_thread::sleep_for(std::chrono::milliseconds{200});\n    CHECK(other.kill(), \"Failed to kill other process\");\n    CHECK(!other.joinable(), \"Other is still joinable\");\n}\n\nstatic void shared_memory_test()\n{\n    nes::shared_memory memory{\"nes_test_shared_memory\", sizeof(std::uint64_t)};\n    auto value{memory.map<std::uint64_t>(0)};\n    CHECK(value, \"Failed to map shared memory\");\n\n    *value = 42;\n\n    CHECK(*value == 42, \"Failed to write shared memory\");\n\n    nes::process other{other_path, std::vector<std::string>{\"shared memory\"}, nes::process_options::grab_stdout};\n\n    CHECK(other.joinable(), \"Process is not joinable\");\n    other.join();\n    CHECK(other.return_code() == 0, \"Other process failed with code \" << other.return_code() << \":\\n\"\n                                                                      << other.stdout_stream().rdbuf());\n\n    CHECK(*value == 16777216, \"Wrong value in shared memory, expected 16777216 got \" << *value);\n\n    other = nes::process{other_path, std::vector<std::string>{\"shared memory bad\"}, nes::process_options::grab_stdout};\n\n    CHECK(other.joinable(), \"Process is not joinable\");\n    other.join();\n    CHECK(other.return_code() != 0, \"Other process must return an error\");\n    CHECK(*value == 16777216, \"Wrong value in shared memory, expected 16777216 got \" << *value);\n}\n\nstatic void named_mutex_test()\n{\n    nes::named_mutex mutex{\"nes_test_named_mutex\"};\n    std::unique_lock lock{mutex};\n\n    nes::process other{other_path, std::vector<std::string>{\"named mutex\"}, nes::process_options::grab_stdout};\n\n    lock.unlock();\n\n    CHECK(other.joinable(), \"Process is not joinable\");\n    other.join();\n    CHECK(other.return_code() == 0, \"Other process failed with code \" << other.return_code() << \":\\n\"\n                                                                      << other.stdout_stream().rdbuf());\n}\n\nstatic void timed_named_mutex_test()\n{\n    nes::timed_named_mutex mutex{\"nes_test_timed_named_mutex\"};\n    std::unique_lock lock{mutex};\n\n    nes::process other{other_path, std::vector<std::string>{\"timed named mutex\"}, nes::process_options::grab_stdout};\n\n    lock.unlock();\n\n    CHECK(other.joinable(), \"Process is not joinable\");\n    other.join();\n    CHECK(other.return_code() == 0, \"Other process failed with code \" << other.return_code() << \":\\n\"\n                                                                      << other.stdout_stream().rdbuf());\n}\n\nstatic void named_semaphore_test()\n{\n    nes::named_semaphore semaphore{\"nes_test_named_semaphore\"};\n\n    nes::process other{other_path, std::vector<std::string>{\"named semaphore\"}, nes::process_options::grab_stdout};\n\n    for(std::size_t i{}; i < 8; ++i)\n    {\n        semaphore.release();\n    }\n\n    CHECK(other.joinable(), \"Process is not joinable\");\n    other.join();\n    CHECK(other.return_code() == 0, \"Other process failed with code \" << other.return_code() << \":\\n\"\n                                                                      << other.stdout_stream().rdbuf());\n}\n\nint main()\n{\n    try\n    {\n        shared_library_test();\n        pipe_test();\n        semaphore_test();\n        process_test();\n        process_kill_test();\n        named_pipe_test();\n        shared_memory_test();\n        named_mutex_test();\n        timed_named_mutex_test();\n        named_semaphore_test();\n    }\n    catch(const std::exception& e)\n    {\n        CHECK(false, e.what());\n        return 1;\n    }\n}\n"
  },
  {
    "path": "tests/process_other.cpp",
    "content": "#include <iostream>\n#include <mutex>\n#include <thread>\n\n#include <nes/process.hpp>\n#include <nes/shared_memory.hpp>\n#include <nes/named_mutex.hpp>\n#include <nes/named_semaphore.hpp>\n\n#include \"common.hpp\"\n\n[[noreturn]] static void to_infinity_and_beyond()\n{\n    while(true)\n    {\n        std::this_thread::sleep_for(std::chrono::milliseconds{10});\n    }\n}\n\nstatic void named_pipe()\n{\n    nes::pipe_istream is{\"nes_test_pipe\"};\n    CHECK(is, \"Failed to open pipe.\");\n\n    data_type type{};\n    std::uint32_t uint_value{};\n    double float_value{};\n    std::string str_value{};\n    std::uint64_t str_size{};\n\n    is.read(reinterpret_cast<char*>(&type), sizeof(data_type));\n    CHECK(type == data_type::uint32, \"Wrong data type, expected uint32 got \" << data_type_to_string(type));\n\n    is.read(reinterpret_cast<char*>(&uint_value), sizeof(std::uint32_t));\n    CHECK(uint_value == 42, \"Wrong value, expected 42 got \" << uint_value);\n\n    is.read(reinterpret_cast<char*>(&type), sizeof(data_type));\n    CHECK(type == data_type::float64, \"Wrong data type, expected float64 got \" << data_type_to_string(type));\n\n    is.read(reinterpret_cast<char*>(&float_value), sizeof(double));\n    CHECK(float_value > 3.139 && float_value < 3.141, \"Wrong value, expected 3.14 got \" << float_value);\n\n    is.read(reinterpret_cast<char*>(&type), sizeof(data_type));\n    CHECK(type == data_type::string, \"Wrong data type, expected string got \" << data_type_to_string(type));\n\n    is.read(reinterpret_cast<char*>(&str_size), sizeof(std::uint64_t));\n    str_value.resize(str_size);\n    is.read(std::data(str_value), static_cast<std::streamsize>(str_size));\n\n    CHECK(str_value == \"Hello world!\", \"Wrong value, expected \\\"Hello world!\\\" got \\\"\" << str_value << \"\\\"\");\n}\n\nstatic void shared_memory()\n{\n    {\n        nes::shared_memory memory{\"nes_test_shared_memory\", nes::shared_memory_options::constant};\n        const auto value{*memory.map<const std::uint64_t>(0)};\n        CHECK(value == 42, \"Wrong value, expected 42 got \" << value);\n    }\n\n    {\n        nes::shared_memory new_memory{\"nes_test_shared_memory\"};\n        *new_memory.map<std::uint64_t>(0) = 16777216;\n    }\n}\n\nstatic void shared_memory_bad()\n{\n    nes::shared_memory memory{\"nes_test_shared_memory\", nes::shared_memory_options::constant};\n    *memory.map<std::uint64_t>(0) = 12;\n    // theorically unreachable\n}\n\nstatic void named_mutex()\n{\n    nes::named_mutex mutex{\"nes_test_named_mutex\"};\n    std::lock_guard lock{mutex}; // will throw in case of error\n}\n\nstatic void timed_named_mutex()\n{\n    nes::timed_named_mutex mutex{\"nes_test_timed_named_mutex\"};\n    std::unique_lock lock{mutex, std::defer_lock};\n\n    while(!lock.try_lock_for(std::chrono::milliseconds{10}))\n        ; // will throw in case of error\n}\n\nstatic void named_semaphore()\n{\n    nes::named_semaphore semaphore{\"nes_test_named_semaphore\"};\n\n    for(std::size_t i{}; i < 8; ++i)\n    {\n        semaphore.acquire(); // will throw in case of error\n    }\n}\n\nint main(int argc, char** argv)\n{\n    for(int i{}; i < argc; ++i)\n    {\n        try\n        {\n            using namespace std::string_view_literals;\n\n            if(argv[i] == \"process kill\"sv)\n            {\n                to_infinity_and_beyond();\n            }\n            else if(argv[i] == \"named pipe\"sv)\n            {\n                named_pipe();\n            }\n            else if(argv[i] == \"shared memory\"sv)\n            {\n                shared_memory();\n            }\n            else if(argv[i] == \"shared memory bad\"sv)\n            {\n                shared_memory_bad();\n            }\n            else if(argv[i] == \"named mutex\"sv)\n            {\n                named_mutex();\n            }\n            else if(argv[i] == \"timed named mutex\"sv)\n            {\n                timed_named_mutex();\n            }\n            else if(argv[i] == \"named semaphore\"sv)\n            {\n                named_semaphore();\n            }\n        }\n        catch(const std::exception& e)\n        {\n            std::cout << e.what() << std::endl;\n            return 1;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/thread_pool_test.cpp",
    "content": "#include <array>\n\n#include <nes/thread_pool.hpp>\n\n#include \"common.hpp\"\n\nstatic void smoke_test()\n{\n    static constexpr std::size_t buffer_size{8};\n\n    // Some buffers\n    std::array<std::uint32_t, buffer_size> input{32, 543, 4329, 12, 542, 656, 523, 98473};\n    std::array<std::uint32_t, buffer_size> temp{};\n    std::array<std::uint32_t, buffer_size> output{};\n\n    // The task builder\n    nes::task_builder builder{};\n\n    builder.dispatch(buffer_size, 1, 1, [&input, &temp](std::uint32_t x, std::uint32_t y [[maybe_unused]], std::uint32_t z [[maybe_unused]] )\n    {\n        temp[x] = input[x] * 2u;\n    });\n\n    nes::task_checkpoint checkpoint{builder.checkpoint()};\n    nes::task_fence fence{builder.fence()};\n\n    builder.dispatch(buffer_size, 1, 1, [&input, &temp, &output](std::uint32_t x, std::uint32_t y [[maybe_unused]], std::uint32_t z [[maybe_unused]] )\n    {\n        for(auto value : temp)\n        {\n            output[x] += (value + input[x]);\n        }\n    });\n\n    // Create a thread pool to run our task list.\n    nes::thread_pool thread_pool{};\n\n    std::future<nes::task_list> future{thread_pool.push(builder.build())};\n    checkpoint.wait();\n\n    constexpr std::array<std::uint32_t, buffer_size> temp_expected{64, 1086, 8658, 24, 1084, 1312, 1046, 196946};\n    CHECK(temp == temp_expected, \"Wrong array values\");\n\n    fence.signal();\n    future.wait();\n\n    constexpr std::array<std::uint32_t, buffer_size> output_expected{210476, 214564, 244852, 210316, 214556, 215468, 214404, 998004};\n    CHECK(output == output_expected, \"Wrong array values\");\n}\n\nint main()\n{\n    try\n    {\n        smoke_test();\n    }\n    catch(const std::exception& e)\n    {\n        CHECK(false, e.what());\n        return 1;\n    }\n}"
  }
]