pax_global_header00006660000000000000000000000064136150157100014511gustar00rootroot0000000000000052 comment=5c706350e74c5bd5910ca1792ad5c739960cef10 libpmemobj-cpp-1.9/000077500000000000000000000000001361501571000142625ustar00rootroot00000000000000libpmemobj-cpp-1.9/.clang-format000066400000000000000000000016651361501571000166450ustar00rootroot00000000000000AccessModifierOffset: -8 AlignOperands: false AllowShortBlocksOnASingleLine: false AllowShortFunctionsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: true AlwaysBreakTemplateDeclarations: true BasedOnStyle: LLVM BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false AfterFunction: true AfterNamespace: true AfterObjCDeclaration: false AfterStruct: false AfterUnion: false BeforeCatch: false BeforeElse: false IndentBraces: false BreakBeforeBraces: Custom BreakStringLiterals: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ContinuationIndentWidth: 8 FixNamespaceComments: false IndentCaseLabels: false IndentCaseLabels: true IndentWidth: 8 PointerAlignment: Right SpaceBeforeParens: ControlStatements SpacesBeforeTrailingComments: 1 SpacesInContainerLiterals: false SpacesInCStyleCastParentheses: false UseTab: Always libpmemobj-cpp-1.9/.gitattributes000066400000000000000000000001071361501571000171530ustar00rootroot00000000000000* text=auto eol=lf *.jpg binary *.png binary *.gif binary *.ico binary libpmemobj-cpp-1.9/.github/000077500000000000000000000000001361501571000156225ustar00rootroot00000000000000libpmemobj-cpp-1.9/.github/workflows/000077500000000000000000000000001361501571000176575ustar00rootroot00000000000000libpmemobj-cpp-1.9/.github/workflows/gha.yml000066400000000000000000000120131361501571000211360ustar00rootroot00000000000000# # The 'XXX_DISABLE_' suffix is used twice in this file to disable two actions: # 1) XXX_DISABLE_${CI_FILE_PUSH_IMAGE_TO_REPO} - disables pushing the rebuilt Docker image and # 2) XXX_DISABLE_AUTO_DOC_UPDATE - disables making pull requests with the update of documentation. # Those two actions are disabled, because they conflict with the same ones run on Travis. # Only one CI (Travis or GitHub Actions) can run them at the time, so they can be enabled here, # when we decide to switch from Travis to GitHub Actions. The 'XXX_DISABLE_' suffix should be removed then. # name: CPP on: [push, pull_request] env: GITHUB_REPO: pmem/libpmemobj-cpp DOCKERHUB_REPO: pmem/libpmemobj-cpp jobs: linux: name: Linux runs-on: ubuntu-latest env: HOST_WORKDIR: /home/runner/work/libpmemobj-cpp/libpmemobj-cpp WORKDIR: utils/docker strategy: matrix: CONFIG: ["N=1 OS=ubuntu OS_VER=19.10 TYPE=debug PUSH_IMAGE=1", "N=2 OS=fedora OS_VER=31 TYPE=debug PUSH_IMAGE=1", "N=3 OS=ubuntu OS_VER=19.10 TYPE=release", "N=4 OS=fedora OS_VER=31 TYPE=release XXX_DISABLE_AUTO_DOC_UPDATE=1", "N=5 OS=ubuntu OS_VER=19.10 TYPE=valgrind", "N=6 OS=ubuntu OS_VER=19.10 TYPE=memcheck_drd", "N=7 OS=ubuntu OS_VER=19.10 TYPE=package", "N=8 OS=fedora OS_VER=31 TYPE=package", "N=9 OS=ubuntu OS_VER=19.10 TYPE=coverage", "N=10 OS=ubuntu OS_VER=19.10 TYPE=coverity"] steps: - name: Clone the git repo uses: actions/checkout@v1 - name: Change ownership of the repo run: sudo chown -R 1000.1000 $HOST_WORKDIR - name: Pull or rebuild the image run: cd $WORKDIR && ${{ matrix.CONFIG }} ./pull-or-rebuild-image.sh - name: Run the build run: cd $WORKDIR && ${{ matrix.CONFIG }} ./build.sh - name: Push the image run: cd $WORKDIR && source ./set-vars.sh && ${{ matrix.CONFIG }} /bin/bash -c "if [[ -f XXX_DISABLE_${CI_FILE_PUSH_IMAGE_TO_REPO} ]]; then images/push-image.sh $OS-$OS_VER; fi" windows: name: Windows runs-on: windows-latest env: platform: x64 VCPKG_DEFAULT_TRIPLET: x64-windows GENERATOR: "Visual Studio 16 2019" ARCH: "x64" PMDK_VERSION: "1.7" CMAKE_TOOLCHAIN_FILE: "C:\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake" CMAKE_INSTALL_PREFIX: "C:\\install\\libpmemobj-cpp" WORKDIR: "D:\\a\\libpmemobj-cpp\\libpmemobj-cpp\\" MSBUILD: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\MSBuild\\Current\\Bin" strategy: matrix: CONFIG: [Debug, Release] steps: - name: Update PATH run: | echo "::add-path::$Env:MSBUILD" - name: Cache vcpkg packages uses: actions/cache@v1 id: cache-vcpkg-packages with: path: C:/vcpkg/packages key: vcpkg-packages-45e5a67 - name: Cache vcpkg installed uses: actions/cache@v1 id: cache-vcpkg-installed with: path: C:/vcpkg/installed key: vcpkg-installed-45e5a67 - name: Clone the git repo uses: actions/checkout@v1 - name: Update vcpkg's pmdk version working-directory: C:\\vcpkg run: | git checkout 2019.11 git apply -v "${env:WORKDIR}\\.github\\workflows\\update_pmdk\\portfile.cmake.patch" Copy-Item -Path "${env:WORKDIR}\\.github\\workflows\\update_pmdk\\controlfile" -Destination "C:\\vcpkg\\ports\\pmdk\\CONTROL" - name: Install PMDK run: | vcpkg install pmdk:x64-windows vcpkg install sfml:x64-windows vcpkg install tbb:x64-windows vcpkg update vcpkg upgrade pmdk:x64-windows --no-dry-run - name: vcpkg integrate install run: vcpkg integrate install - name: Configure run: cmake . -Bbuild -G "${env:GENERATOR}" -A "${env:ARCH}" -DCMAKE_TOOLCHAIN_FILE="${env:CMAKE_TOOLCHAIN_FILE}" -DCMAKE_INSTALL_PREFIX="${env:CMAKE_INSTALL_PREFIX}" -DLIBPMEMOBJ_VERSION="${env:PMDK_VERSION}" -DTESTS_USE_FORCED_PMEM=ON -DTESTS_TBB=ON - name: Build run: msbuild build/ALL_BUILD.vcxproj /property:Configuration=${{ matrix.CONFIG }} /verbosity:minimal /m - name: Tests working-directory: build run: ctest -C ${{ matrix.CONFIG }} --output-on-failure --timeout 540 - name: Install working-directory: build run: msbuild INSTALL.vcxproj /verbosity:minimal /m - name: Examples working-directory: examples/map_cli run: | cmake . -G "${env:GENERATOR}" -A "${env:ARCH}" -DCMAKE_TOOLCHAIN_FILE="${env:CMAKE_TOOLCHAIN_FILE}" -DCMAKE_PREFIX_PATH="${env:CMAKE_INSTALL_PREFIX}" msbuild ALL_BUILD.vcxproj /verbosity:minimal /m libpmemobj-cpp-1.9/.github/workflows/update_pmdk/000077500000000000000000000000001361501571000221545ustar00rootroot00000000000000libpmemobj-cpp-1.9/.github/workflows/update_pmdk/controlfile000066400000000000000000000001641361501571000244200ustar00rootroot00000000000000Source: pmdk Version: 1.8-dev Homepage: https://github.com/pmem/pmdk Description: Persistent Memory Development Kit libpmemobj-cpp-1.9/.github/workflows/update_pmdk/portfile.cmake.patch000066400000000000000000000024721361501571000261050ustar00rootroot00000000000000diff --git a/ports/pmdk/portfile.cmake b/ports/pmdk/portfile.cmake index e8f1c309e..9f2d3a4a4 100644 --- a/ports/pmdk/portfile.cmake +++ b/ports/pmdk/portfile.cmake @@ -4,3 +4,3 @@ vcpkg_fail_port_install(ON_ARCH "arm" "x86") -set(PMDK_VERSION "1.7") +set(PMDK_VERSION "1.8") @@ -9,4 +9,4 @@ vcpkg_from_github( REPO pmem/pmdk - REF bc5e309485df61c452d08367e4b13ba9dfed5071 #Commit id corresponding to the version 1.7 - SHA512 15bee6a046746e4ab7e827bb36685bc5d9cdffdbc68ba86eb71e2c4bd84eb4fed4586c09174257bfd87ea178c8ee9865a8824842d7d1df67e0ae79ff80cf650e + REF 45e5a673eecd9a6302b8af6c4901b60f9a239b04 # devel-1.8: doc: fix indentation; 17.01.2020 + SHA512 ac3e23657ac5c0f62cae5f4211aaea336330b9258a8e48315acd07b597ccc78e76b0f8bd63014f00c28b73c615255a1c69f27326f641d9590eaa3460bb2de374 HEAD_REF master @@ -19,3 +19,3 @@ vcpkg_build_msbuild( PROJECT_PATH ${SOURCE_PATH}/src/PMDK.sln - TARGET "Solution Items\\libpmem,Solution Items\\libpmemlog,Solution Items\\libpmemblk,Solution Items\\libpmemobj,Solution Items\\libpmempool,Solution Items\\libvmem,Solution Items\\Tools\\pmempool" + TARGET "Solution Items\\libpmem,Solution Items\\libpmemlog,Solution Items\\libpmemblk,Solution Items\\libpmemobj,Solution Items\\libpmempool,Solution Items\\Tools\\pmempool" OPTIONS /p:SRCVERSION=${PMDK_VERSION} libpmemobj-cpp-1.9/.gitignore000066400000000000000000000002221361501571000162460ustar00rootroot00000000000000.* !.gitignore !.gitattributes !.github/ !.clang-format !.travis.yml !.mailmap !.version build/ *~ *.swp ~* tags include/libpmemobj++/version.hpp libpmemobj-cpp-1.9/.mailmap000066400000000000000000000002151361501571000157010ustar00rootroot00000000000000Igor Chorążewicz Michał Biesek Krzysztof Kajrewicz libpmemobj-cpp-1.9/.travis.yml000077700000000000000000000000001361501571000204212travis.ymlustar00rootroot00000000000000libpmemobj-cpp-1.9/.version000066400000000000000000000000031361501571000157410ustar00rootroot000000000000001.9libpmemobj-cpp-1.9/CMakeLists.txt000066400000000000000000000345751361501571000170400ustar00rootroot00000000000000# # Copyright 2018-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cmake_minimum_required(VERSION 3.3) project(libpmemobj-cpp C CXX) set(VERSION_MAJOR 1) set(VERSION_MINOR 9) set(VERSION_PATCH 0) #set(VERSION_PRERELEASE rc2) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}) if (VERSION_PATCH GREATER 0) set(VERSION ${VERSION}.${VERSION_PATCH}) endif() if (VERSION_PRERELEASE) set(VERSION ${VERSION}-${VERSION_PRERELEASE}) endif() set(LIBPMEMOBJ_REQUIRED_VERSION 1.8) set(LIBPMEM_REQUIRED_VERSION 1.7) set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) # Treat CMAKE_CXX_STANDARD as a requirement set(CXX_STANDARD_REQUIRED ON) set(CXX_STANDARD 14 CACHE STRING "C++ language standard") set(CMAKE_CXX_STANDARD ${CXX_STANDARD}) include(FindPerl) include(FindThreads) include(CMakeDependentOption) include(CMakePackageConfigHelpers) include(CheckCXXSourceCompiles) include(CheckCXXCompilerFlag) include(GNUInstallDirs) include(${CMAKE_SOURCE_DIR}/cmake/functions.cmake) option(BUILD_EXAMPLES "build examples" ON) option(BUILD_TESTS "build tests" ON) option(BUILD_DOC "build documentation" ON) option(BUILD_BENCHMARKS "build benchmarks" ON) option(COVERAGE "run coverage test" OFF) option(DEVELOPER_MODE "enable developer checks" OFF) option(CHECK_CPP_STYLE "check code style of C++ sources" OFF) option(TRACE_TESTS "more verbose test outputs" OFF) option(USE_ASAN "enable AddressSanitizer (debugging)" OFF) option(USE_UBSAN "enable UndefinedBehaviorSanitizer (debugging)" OFF) option(TESTS_USE_FORCED_PMEM "run tests with PMEM_IS_PMEM_FORCE=1" OFF) option(TESTS_USE_VALGRIND "enable tests with valgrind (if found)" ON) option(TESTS_CONCURRENT_HASH_MAP_DRD_HELGRIND "enable concurrent_hash_map tests with drd and helgrind (can only be run on PMEM)." OFF) option(TESTS_LONG "enable long running tests" OFF) option(TESTS_TBB "enable tests which require TBB" OFF) option(TESTS_COMPATIBILITY "enable compatibility tests (requires internet connection)" OFF) option(TEST_ARRAY "enable testing of pmem::obj::array" ON) option(TEST_VECTOR "enable testing of pmem::obj::vector" ON) option(TEST_STRING "enable testing of pmem::obj::string (depends on TEST_VECTOR)" ON) option(TEST_CONCURRENT_HASHMAP "enable testing of pmem::obj::concurrent_hash_map (depends on TEST_STRING)" ON) option(TEST_SEGMENT_VECTOR_ARRAY_EXPSIZE "enable testing of pmem::obj::segment_vector with array as segment_vector_type and exponential_size_policy" ON) option(TEST_SEGMENT_VECTOR_VECTOR_EXPSIZE "enable testing of pmem::obj::segment_vector with vector as segment_vector_type and exponential_size_policy" ON) option(TEST_SEGMENT_VECTOR_VECTOR_FIXEDSIZE "enable testing of pmem::obj::segment_vector with vector as segment_vector_type and fixed_size_policy" ON) option(TEST_ENUMERABLE_THREAD_SPECIFIC "enable testing of pmem::obj::experimental::enumerable_thread_specific" ON) option(INSTALL_ARRAY "enable installation of pmem::obj::array" ON) option(INSTALL_VECTOR "enable installation of pmem::obj::vector" ON) option(INSTALL_STRING "enable installation of pmem::obj::string (depends on INSTALL_VECTOR)" ON) option(INSTALL_CONCURRENT_HASHMAP "enable installation of pmem::obj::concurrent_hash_map (depends on INSTALL_STRING and INSTALL_SEGMENT_VECTOR)" ON) option(INSTALL_SEGMENT_VECTOR "enable installation of pmem::obj::segment_vector" ON) # Do not treat include directories from the interfaces # of consumed Imported Targets as SYSTEM by default. set(CMAKE_NO_SYSTEM_FROM_IMPORTED 1) # Required for MSVC to correctly define __cplusplus add_flag("/Zc:__cplusplus") set(TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/test CACHE STRING "working directory for tests") if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug") endif (NOT CMAKE_BUILD_TYPE) if(EXISTS "${CMAKE_SOURCE_DIR}/.git") execute_process(COMMAND git describe OUTPUT_VARIABLE SRCVERSION WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) if(NOT SRCVERSION) execute_process(COMMAND git log -1 --format=%h OUTPUT_VARIABLE SRCVERSION WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_STRIP_TRAILING_WHITESPACE) endif() else() execute_process(COMMAND cat .version OUTPUT_VARIABLE SRCVERSION WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_STRIP_TRAILING_WHITESPACE) endif() if(NOT WIN32) find_package(PkgConfig QUIET) endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) if(NOT PERL_FOUND) message(FATAL_ERROR "Perl not found") endif() if (PERL_VERSION_STRING VERSION_LESS 5.16) message(FATAL_ERROR "Too old Perl (<5.16)") endif() # Some tests and examples require clang >= 8.0 # because of the following bug: # https://bugs.llvm.org/show_bug.cgi?id=28280 # which is fixed in clang v8.0. set(CLANG_REQUIRED_BY_DESTRUCTOR_REFERENCE_BUG "8.0") find_program(CLANG NAMES clang) if(CLANG) get_program_version_major_minor(${CLANG} CLANG_VERSION) if(CLANG_VERSION VERSION_LESS CLANG_REQUIRED_BY_DESTRUCTOR_REFERENCE_BUG) set(CLANG_DESTRUCTOR_REFERENCE_BUG_PRESENT 1) endif() endif() if(BUILD_TESTS OR BUILD_EXAMPLES) if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMOBJ REQUIRED libpmemobj>=${LIBPMEMOBJ_REQUIRED_VERSION}) pkg_check_modules(LIBPMEM REQUIRED libpmem>=${LIBPMEM_REQUIRED_VERSION}) else() find_package(LIBPMEMOBJ REQUIRED ${LIBPMEMOBJ_REQUIRED_VERSION}) find_package(LIBPMEM REQUIRED ${LIBPMEM_REQUIRED_VERSION}) endif() if (LIBPMEMOBJ_VERSION) string(REGEX REPLACE "\\+git.*" "" LIBPMEMOBJ_VERSION_SHORT ${LIBPMEMOBJ_VERSION}) string(REGEX REPLACE "-rc.*" "" LIBPMEMOBJ_VERSION_SHORT ${LIBPMEMOBJ_VERSION_SHORT}) string(REPLACE "." ";" VERSION_LIST ${LIBPMEMOBJ_VERSION_SHORT}) list(GET VERSION_LIST 0 LIBPMEMOBJ_VERSION_MAJOR) list(GET VERSION_LIST 1 LIBPMEMOBJ_VERSION_MINOR) list(LENGTH VERSION_LIST OBJ_VER_COMPS) if (${OBJ_VER_COMPS} LESS 3) list(APPEND VERSION_LIST "0") endif() list(GET VERSION_LIST 2 LIBPMEMOBJ_VERSION_PATCH) else() message(WARNING "cannot detect libpmemobj version, some tests will be skipped") # assume 0.0.0 set(LIBPMEMOBJ_VERSION_MAJOR 0) set(LIBPMEMOBJ_VERSION_MINOR 0) set(LIBPMEMOBJ_VERSION_PATCH 0) endif() math(EXPR LIBPMEMOBJ_VERSION_NUM "${LIBPMEMOBJ_VERSION_PATCH} + ${LIBPMEMOBJ_VERSION_MINOR} * 100 + ${LIBPMEMOBJ_VERSION_MAJOR} * 10000") include(tbb) endif() add_executable(check_license EXCLUDE_FROM_ALL utils/check_license/check-license.c) add_custom_target(checkers ALL) add_custom_target(cppstyle) add_custom_target(cppformat) add_custom_target(check-whitespace) add_custom_target(check-license COMMAND ${CMAKE_SOURCE_DIR}/utils/check_license/check-headers.sh ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}/check_license ${CMAKE_SOURCE_DIR}/LICENSE) add_dependencies(check-license check_license) add_custom_target(check-whitespace-main COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/utils/check_whitespace ${CMAKE_SOURCE_DIR}/utils/check_license/*.sh ${CMAKE_SOURCE_DIR}/README.md) add_dependencies(check-whitespace check-whitespace-main) add_custom_target(tests) if(CHECK_CPP_STYLE) find_program(CLANG_FORMAT NAMES clang-format clang-format-9.0) set(CLANG_FORMAT_REQUIRED "9.0") if(CLANG_FORMAT) get_program_version_major_minor(${CLANG_FORMAT} CLANG_FORMAT_VERSION) if(NOT (CLANG_FORMAT_VERSION VERSION_EQUAL CLANG_FORMAT_REQUIRED)) message(FATAL_ERROR "required clang-format version is ${CLANG_FORMAT_REQUIRED} (found version: ${CLANG_FORMAT_VERSION})") endif() else() message(WARNING "clang-format not found - C++ sources will not be checked (required version: ${CLANG_FORMAT_REQUIRED})") endif() endif() if(DEVELOPER_MODE) add_flag(-Werror) # XXX: WX for windows execute_process(COMMAND ${PERL_EXECUTABLE} -MText::Diff -e "" ERROR_QUIET RESULT_VARIABLE PERL_TEXT_DIFF_STATUS) if (PERL_TEXT_DIFF_STATUS) message(FATAL_ERROR "Text::Diff Perl module not found (install libtext-diff-perl or perl-Text-Diff)") endif() add_dependencies(checkers cppstyle) add_dependencies(checkers check-whitespace) add_dependencies(checkers check-license) endif(DEVELOPER_MODE) add_cppstyle(include ${CMAKE_CURRENT_SOURCE_DIR}/include/libpmemobj++/*.hpp) add_cppstyle(include-container ${CMAKE_CURRENT_SOURCE_DIR}/include/libpmemobj++/container/*.hpp) add_cppstyle(include-container-detail ${CMAKE_CURRENT_SOURCE_DIR}/include/libpmemobj++/container/detail/*.hpp) add_cppstyle(include-detail ${CMAKE_CURRENT_SOURCE_DIR}/include/libpmemobj++/detail/*.hpp) add_cppstyle(include-experimental ${CMAKE_CURRENT_SOURCE_DIR}/include/libpmemobj++/experimental/*.hpp) add_check_whitespace(include ${CMAKE_CURRENT_SOURCE_DIR}/include/libpmemobj++/*.hpp) add_check_whitespace(include-container ${CMAKE_CURRENT_SOURCE_DIR}/include/libpmemobj++/container/*.hpp) add_check_whitespace(include-container-detail ${CMAKE_CURRENT_SOURCE_DIR}/include/libpmemobj++/container/detail/*.hpp) add_check_whitespace(include-detail ${CMAKE_CURRENT_SOURCE_DIR}/include/libpmemobj++/detail/*.hpp) add_check_whitespace(include-experimental ${CMAKE_CURRENT_SOURCE_DIR}/include/libpmemobj++/experimental/*.hpp) add_check_whitespace(cmake-main ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) add_check_whitespace(cmake-helpers ${CMAKE_CURRENT_SOURCE_DIR}/cmake/*.cmake) configure_file(${CMAKE_SOURCE_DIR}/cmake/version.hpp.in ${CMAKE_SOURCE_DIR}/include/libpmemobj++/version.hpp @ONLY) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.hpp" PATTERN "array.hpp" EXCLUDE PATTERN "vector.hpp" EXCLUDE PATTERN "string.hpp" EXCLUDE PATTERN "basic_string.hpp" EXCLUDE PATTERN "contiguous_iterator.hpp" EXCLUDE PATTERN "slice.hpp" EXCLUDE PATTERN "concurrent_hash_map.hpp" EXCLUDE PATTERN "segment_vector.hpp" EXCLUDE PATTERN "enumerable_thread_specific.hpp" EXCLUDE) if(INSTALL_ARRAY) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "array.hpp") endif() if(INSTALL_VECTOR) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "vector.hpp") endif() if(INSTALL_STRING) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "basic_string.hpp") install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "string.hpp") endif() if(INSTALL_CONCURRENT_HASHMAP) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "concurrent_hash_map.hpp") install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "enumerable_thread_specific.hpp") endif() if(INSTALL_ARRAY OR INSTALL_VECTOR OR INSTALL_STRING) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "contiguous_iterator.hpp") install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "slice.hpp") endif() if(INSTALL_SEGMENT_VECTOR) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "segment_vector.hpp") endif() install(DIRECTORY examples/ DESTINATION ${CMAKE_INSTALL_DOCDIR}/examples FILES_MATCHING PATTERN "*.*pp") configure_file(${CMAKE_SOURCE_DIR}/cmake/libpmemobj++.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libpmemobj++.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libpmemobj++.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) configure_file( "${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) configure_package_config_file(${CMAKE_SOURCE_DIR}/cmake/libpmemobj++-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/libpmemobj++-config.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/libpmemobj++/cmake PATH_VARS CMAKE_INSTALL_LIBDIR CMAKE_INSTALL_INCLUDEDIR) write_basic_package_version_file(libpmemobj++-config-version.cmake VERSION ${VERSION} COMPATIBILITY AnyNewerVersion) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libpmemobj++-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/libpmemobj++-config-version.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/libpmemobj++/cmake) include_directories(include) include(${CMAKE_SOURCE_DIR}/cmake/check_compiling_issues.cmake) if(PKG_CONFIG_FOUND) pkg_check_modules(VALGRIND QUIET valgrind) else() find_package(VALGRIND QUIET) endif() if(VALGRIND_FOUND) add_flag(-DLIBPMEMOBJ_CPP_VG_MEMCHECK_ENABLED=1) add_flag(-DLIBPMEMOBJ_CPP_VG_DRD_ENABLED=1) add_flag(-DLIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED=1) include_directories(${VALGRIND_INCLUDE_DIRS}) find_pmemcheck() if(VALGRIND_PMEMCHECK_FOUND) add_flag(-DLIBPMEMOBJ_CPP_VG_PMEMCHECK_ENABLED=1) endif() endif() if(BUILD_TESTS) if(TEST_DIR) enable_testing() else() message(WARNING "TEST_DIR is empty - 'make test' will not work") endif() add_subdirectory(tests) endif() if(BUILD_DOC) add_subdirectory(doc) endif() if (BUILD_BENCHMARKS) add_subdirectory(benchmarks) endif() if(BUILD_EXAMPLES AND NO_GCC_VARIADIC_TEMPLATE_BUG) add_subdirectory(examples) elseif(BUILD_EXAMPLES) message(WARNING "Skipping build of examples because of compiler issue") endif() if(NOT "${CPACK_GENERATOR}" STREQUAL "") include(${CMAKE_SOURCE_DIR}/cmake/packages.cmake) endif() libpmemobj-cpp-1.9/ChangeLog000066400000000000000000000120131361501571000160310ustar00rootroot00000000000000Fri Jan 31 2020 Szymon Romik * Version 1.9 This release moves segment_vector out of experimental namespace and directory. It means that abovementioned container is guaranteed to have stable API and on-media layout. It also introduces defragmentation functionality with new defrag class in pmem::detail namespace and helper classes (which currently reside in "detail" directory): enumerable_thread_specific and volatile_state. With this release, we also decreased restart time of concurrent_hash_map (with a usage of the enumerable_thread_specific feature). New features: - new methods for pmem::obj::string (find() and its overloads) - defragmentation feature as a separate class in pmem::obj namespace and support for the defragmentation in containers: concurrent_hash_map, vector, string - removed template parameters from persistent_ptr_base class (make it type agnostic) and moved to the public API - new methods for pmem::obj::concurrent_hash_map (insert_or_assign and its overloads) Optimizations: - bucket rehashing in concurrent_hash_map moved to transaction - faster concurrent_hash_map restart (with "persistent TLS") Other changes: - added GitHub Actions as an additional CI - added documentation and doc snippets with some usage examples - added compatibility tests for different libpmemobj-cpp versions Fri Jan 24 2020 Szymon Romik * Version 1.8.1 This release fixes minor bugs. Notable changes: - fix compilation error in concurrent_hash_map - fix possible deadlock in erase method in concurrent_hash_map Thu Oct 03 2019 Szymon Romik * Version 1.8 This release moves persistent array/vector/string/concurrent_hash_map out from experimental namespace and directory. It means that abovementioned containers are guaranteed to have stable API and on-media layout. They are placed now in include/libpmemobj++/container/ directory and pmem::obj namespace. This release introduces also a new experimental container - persistent segment vector with templated segment policies. New features: - experimental persistent segment vector container Bug fixes: - fixed insert method in persistent vector - fixed move assignment operator in persistent array Other changes: - extended error messages in exceptions - added examples with simplekv using persistent containers - added modifiers methods for persistent string - added template locks for concurrent_hash_map - added layout versioning for concurrent_hash_map Optimizations: - concurrent_hash_map insert method implemented with tx - optimized snapshotting in persistent vector/string Wed Jun 26 2019 Szymon Romik * Version 1.7 This release introduces new experimental container - persistent concurrent hashmap and persistent string with limited functionality. Experimental features: - persistent concurrent hashmap - persistent string (constructors and modifiers) Bug fixes: - add option to force define IS_TRIVIALLY_COPYABLE - mark allocation_flag constructor as explicit - add missing include to v.hpp Fri Mar 15 2019 Igor Chorążewicz * Version 1.6 This release introduces ctl support, allocation flags, snapshot method and new experimental persistent container - vector. New features: - add support for pmemobj_ctl_set/get/exec - expose allocation flags in make_persistent(_atomic) - transaction: add snapshot method Experimental features: - persistent vector container Other changes: - automatically start transaction inside pmem::obj::experimental::array modifier methods (assignment operators, swap and fill) - add const methods for pmem::obj::experimental::array - add Valgrind instrumentation support - remove experimental const_contiguous_iterator - add get with arguments method overload for v Bug fixes: - throw an exception when dropping pmem lock failed - fix crash when previous transaction failed to start - fix forwarding parameters to constructor in make_persistent_atomic Optimizations: - decrease number of persistent_ptr dereferences in make_persistent_array Tue Feb 19 2018 Marcin Ślusarz * Version 1.5.1 This release fixes minor bugs and improves documentation. Notable changes: - fix v swap, assignment operators and constructors - change conversion operator from T() to T&() in v<> - fix range_snapshotting initialization in array.hpp. - fix range_snapshotting_iterator behaviour for snapshot_size == 0. Fri Oct 26 2018 Marcin Ślusarz * Version 1.5 This is the first release of libpmemobj-cpp as a separate project. It introduces one persistent container - array, which has std::array like interface. Currently it is considered experimental and lives in experimental namespace. We have also cleaned up some function names that, in retrospect, were chosen poorly. Functions with old names are still there but are deprecated. Experimental features: - volatile resides on pmem class - persistent array container libpmemobj-cpp-1.9/LICENSE000066400000000000000000000033061361501571000152710ustar00rootroot00000000000000Copyright 2018, Intel Corporation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Everything in this source tree is covered by the previous license with the following exceptions: * tests/external/libcxx which contain tests adapted from llvm libcxx, and is covered by license contained in that directory. libpmemobj-cpp-1.9/README.md000066400000000000000000000056531361501571000155520ustar00rootroot00000000000000libpmemobj-cpp =============== [![Build Status](https://travis-ci.org/pmem/libpmemobj-cpp.svg?branch=master)](https://travis-ci.org/pmem/libpmemobj-cpp) [![Build status](https://github.com/pmem/libpmemobj-cpp/workflows/CPP/badge.svg)](https://github.com/pmem/libpmemobj-cpp/actions) [![libpmemobj-cpp version](https://img.shields.io/github/tag/pmem/libpmemobj-cpp.svg)](https://github.com/pmem/libpmemobj-cpp/releases/latest) [![Coverity Scan Build Status](https://scan.coverity.com/projects/15911/badge.svg)](https://scan.coverity.com/projects/pmem-libpmemobj-cpp) [![Coverage Status](https://codecov.io/github/pmem/libpmemobj-cpp/coverage.svg?branch=master)](https://codecov.io/gh/pmem/libpmemobj-cpp/branch/master) C++ bindings for libpmemobj (https://github.com/pmem/pmdk) More information in include/libpmemobj++/README.md # How to build # ## Requirements: ## - cmake >= 3.3 - libpmemobj-dev(el) >= 1.8 (https://pmem.io/pmdk/) - compiler with C++11 support: - GCC >= 4.8.1 (C++11 is supported in GCC since version 4.8.1, but it does not support expanding variadic template variables in lambda expressions, which is required to build persistent containers and is possible with GCC >= 4.9.0. If you want to build libpmemobj-cpp without testing containers, use flag TEST_XXX=OFF (separate flag for each container)) - clang >= 3.3 - for testing and development: - valgrind-devel (at best with [pmemcheck support](https://github.com/pmem/valgrind)) - clang format 9.0 - perl ## On Linux ## ```sh $ mkdir build $ cd build $ cmake .. $ make $ make install ``` #### When developing: #### ```sh $ ... $ cmake .. -DCMAKE_BUILD_TYPE=Debug -DDEVELOPER_MODE=1 -DCHECK_CPP_STYLE=1 $ ... $ ctest --output-on-failure ``` #### To build packages #### ```sh ... cmake .. -DCPACK_GENERATOR="$GEN" -DCMAKE_INSTALL_PREFIX=/usr make package ``` $GEN is type of package generator and can be RPM or DEB CMAKE_INSTALL_PREFIX must be set to a destination were packages will be installed #### To use with Valgrind #### In order to build your application with libpmemobj-cpp and [pmemcheck](https://github.com/pmem/valgrind) / memcheck / helgrind / drd, Valgrind instrumentation must be enabled during compilation by adding flags: - LIBPMEMOBJ_CPP_VG_PMEMCHECK_ENABLED=1 for pmemcheck instrumentation, - LIBPMEMOBJ_CPP_VG_MEMCHECK_ENABLED=1 for memcheck instrumentation, - LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED=1 for helgrind instrumentation, - LIBPMEMOBJ_CPP_VG_DRD_ENABLED=1 for drd instrumentation, or - LIBPMEMOBJ_CPP_VG_ENABLED=1 for all Valgrind instrumentations (including pmemcheck). If there are no memcheck / helgrind / drd / pmemcheck headers installed on your system, build will fail. ## On Windows ## #### Install libpmemobj via vcpkg #### ```sh vcpkg install pmdk:x64-windows vcpkg integrate install ``` ```sh ... cmake . -Bbuild -G "Visual Studio 14 2015 Win64" -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake msbuild build/ALL_BUILD.vcxproj ``` libpmemobj-cpp-1.9/appveyor.yml000066400000000000000000000003511361501571000166510ustar00rootroot00000000000000version: 1.4.{build} os: Visual Studio 2017 platform: x64 environment: VCPKG_DEFAULT_TRIPLET: x64-windows GENERATOR: "Visual Studio 14 2015 Win64" configuration: - Debug - Release build_script: - exit 0 test_script: - exit 0 libpmemobj-cpp-1.9/benchmarks/000077500000000000000000000000001361501571000163775ustar00rootroot00000000000000libpmemobj-cpp-1.9/benchmarks/CMakeLists.txt000066400000000000000000000054241361501571000211440ustar00rootroot00000000000000# # Copyright 2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if(MSVC_VERSION) add_flag(-W4) else() add_flag(-Wall) endif() add_flag(-Wpointer-arith) add_flag(-Wsign-compare) add_flag(-Wunreachable-code-return) add_flag(-Wmissing-variable-declarations) add_flag(-fno-common) add_flag(-Wunused-macros) add_flag(-Wsign-conversion) add_flag(-ggdb DEBUG) add_flag(-DDEBUG DEBUG) add_flag("-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2" RELEASE) include_directories(${LIBPMEMOBJ_INCLUDE_DIRS} .) link_directories(${LIBPMEMOBJ_LIBRARY_DIRS}) function(add_benchmark name) set(srcs ${ARGN}) prepend(srcs ${CMAKE_CURRENT_SOURCE_DIR} ${srcs}) add_executable(benchmark-${name} ${srcs}) target_link_libraries(benchmark-${name} ${LIBPMEMOBJ_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) endfunction() add_check_whitespace(benchmarks-cmake ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) add_cppstyle(benchmarks-common ${CMAKE_CURRENT_SOURCE_DIR}/*.*pp) add_check_whitespace(benchmarks-common ${CMAKE_CURRENT_SOURCE_DIR}/*.*pp) add_cppstyle(benchmarks-concurrent_hash_map ${CMAKE_CURRENT_SOURCE_DIR}/concurrent_hash_map/*.*pp) add_check_whitespace(benchmarks-concurrent_hash_map ${CMAKE_CURRENT_SOURCE_DIR}/concurrent_hash_map/*.*pp) if (TEST_CONCURRENT_HASHMAP) add_benchmark(concurrent_hash_map_insert_open concurrent_hash_map/insert_open.cpp) endif() libpmemobj-cpp-1.9/benchmarks/concurrent_hash_map/000077500000000000000000000000001361501571000224215ustar00rootroot00000000000000libpmemobj-cpp-1.9/benchmarks/concurrent_hash_map/insert_open.cpp000066400000000000000000000113211361501571000254500ustar00rootroot00000000000000/* * Copyright 2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * insert_open.cpp -- this simple benchmarks is used to measure time of * inserting specified number of elements and time of runtime_initialize(). */ #include #include #include #include #include #include #include #include #include #include #include "../measure.hpp" #ifndef _WIN32 #include #define CREATE_MODE_RW (S_IWUSR | S_IRUSR) #else #include #define CREATE_MODE_RW (S_IWRITE | S_IREAD) #endif static const std::string LAYOUT = "insert_open"; using key_type = pmem::obj::p; using value_type = pmem::obj::p; using persistent_map_type = pmem::obj::concurrent_hash_map; struct root { pmem::obj::persistent_ptr pptr; }; void insert(pmem::obj::pool &pop, size_t n_inserts, size_t n_threads) { auto map = pop.root()->pptr; assert(map != nullptr); map->runtime_initialize(); std::vector v; for (size_t i = 0; i < n_threads; i++) { v.emplace_back( [&](size_t tid) { int begin = tid * n_inserts; int end = begin + int(n_inserts); for (int i = begin; i < end; ++i) { persistent_map_type::value_type val(i, i); map->insert(val); } }, i); } for (auto &t : v) t.join(); assert(map->size() == n_inserts * n_threads); } void open(pmem::obj::pool &pop) { auto map = pop.root()->pptr; assert(map != nullptr); map->runtime_initialize(); assert(map->size() > 0); } int main(int argc, char *argv[]) { std::string usage = "usage: %s file-name "; if (argc < 3) { std::cerr << usage << std::endl; return 1; } auto mode = std::string(argv[2]); if (mode != "create" && mode != "open") { std::cerr << usage << std::endl; return 1; } if (mode == "create" && argc < 5) { std::cerr << usage << std::endl; return 1; } const char *path = argv[1]; pmem::obj::pool pop; if (mode == "create") { size_t n_inserts = std::stoull(argv[3]); size_t n_threads = std::stoull(argv[4]); if (n_inserts * n_threads == 0) { std::cerr << "n_inserts and n_threads must be > 0"; return 1; } try { auto pool_size = n_inserts * n_threads * sizeof(int) * 65 + 20 * PMEMOBJ_MIN_POOL; pop = pmem::obj::pool::create( path, LAYOUT, pool_size, CREATE_MODE_RW); pmem::obj::transaction::run(pop, [&] { pop.root()->pptr = pmem::obj::make_persistent< persistent_map_type>(); }); } catch (pmem::pool_error &pe) { std::cerr << "!pool::create: " << pe.what() << std::endl; return 1; } std::cout << measure([&] { insert(pop, n_inserts, n_threads); }) << "ms" << std::endl; } else { try { pop = pmem::obj::pool::open(path, LAYOUT); } catch (pmem::pool_error &pe) { std::cerr << "!pool::open: " << pe.what() << std::endl; return 1; } std::cout << measure([&] { open(pop); }) << "ms" << std::endl; } pop.close(); return 0; } libpmemobj-cpp-1.9/benchmarks/measure.hpp000066400000000000000000000035461361501571000205610ustar00rootroot00000000000000/* * Copyright 2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include template static typename TimeUnit::rep measure(F &&func) { auto start = std::chrono::steady_clock::now(); func(); auto duration = std::chrono::duration_cast( std::chrono::steady_clock::now() - start); return duration.count(); } libpmemobj-cpp-1.9/cmake/000077500000000000000000000000001361501571000153425ustar00rootroot00000000000000libpmemobj-cpp-1.9/cmake/FindLIBPMEM.cmake000066400000000000000000000040201361501571000202260ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. find_path(LIBPMEM_INCLUDE_DIR libpmem.h) find_library(LIBPMEM_LIBRARY NAMES pmem libpmem) set(LIBPMEM_LIBRARIES ${LIBPMEM_LIBRARY}) set(LIBPMEM_INCLUDE_DIRS ${LIBPMEM_INCLUDE_DIR}) set(MSG_NOT_FOUND "libpmem NOT found (set CMAKE_PREFIX_PATH to point the location)") if(NOT (LIBPMEM_INCLUDE_DIR AND LIBPMEM_LIBRARY)) if(LIBPMEM_FIND_REQUIRED) message(FATAL_ERROR ${MSG_NOT_FOUND}) else() message(WARNING ${MSG_NOT_FOUND}) endif() endif() mark_as_advanced(LIBPMEM_LIBRARY LIBPMEM_INCLUDE_DIR) libpmemobj-cpp-1.9/cmake/FindLIBPMEMOBJ.cmake000066400000000000000000000041021361501571000205620ustar00rootroot00000000000000# # Copyright 2018-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. find_path(LIBPMEMOBJ_INCLUDE_DIR libpmemobj.h) find_library(LIBPMEMOBJ_LIBRARY NAMES pmemobj libpmemobj) set(LIBPMEMOBJ_LIBRARIES ${LIBPMEMOBJ_LIBRARY}) set(LIBPMEMOBJ_INCLUDE_DIRS ${LIBPMEMOBJ_INCLUDE_DIR}) set(MSG_NOT_FOUND "libpmemobj NOT found (set CMAKE_PREFIX_PATH to point the location)") if(NOT (LIBPMEMOBJ_INCLUDE_DIR AND LIBPMEMOBJ_LIBRARY)) if(LIBPMEMOBJ_FIND_REQUIRED) message(FATAL_ERROR ${MSG_NOT_FOUND}) else() message(WARNING ${MSG_NOT_FOUND}) endif() endif() mark_as_advanced(LIBPMEMOBJ_LIBRARY LIBPMEMOBJ_INCLUDE_DIR) libpmemobj-cpp-1.9/cmake/check_compiling_issues.cmake000066400000000000000000000137321361501571000230630ustar00rootroot00000000000000# # Copyright 2019-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(SAVED_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) set(SAVED_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES}) if(NOT MSVC_VERSION) # Even if we are ensuring that we use CMAKE_CXX_STANDARD >= 14, check if # shared_mutex header file is available for the current compiler version # because CXX_STANDARD is being set to 14 for --c++1y parameter if(CXX_STANDARD GREATER_EQUAL 14) set(CMAKE_REQUIRED_FLAGS "--std=c++${CMAKE_CXX_STANDARD} -c") CHECK_CXX_SOURCE_COMPILES( "#include int main() {}" NO_SHARED_MUTEX_BUG) else() set(NO_SHARED_MUTEX_BUG TRUE) endif() # Check for issues with older gcc compilers which do not expand variadic # template variables in lambda expressions. This functionality is being # used in libpmemobj-cpp containers, lack of its support resuls with # FATAL_ERROR unless you disable containers tests by setting CMake flag # TEST_XXX=OFF (separate flag for each container). set(CMAKE_REQUIRED_FLAGS "--std=c++11 -Wno-error -c") CHECK_CXX_SOURCE_COMPILES( "void print() {} template void print(const T&, const Args &...arg) { auto f = [&]{ print(arg...);}; } int main() { print(1, 2, 3); return 0; }" NO_GCC_VARIADIC_TEMPLATE_BUG) if(NOT NO_GCC_VARIADIC_TEMPLATE_BUG) if(TEST_ARRAY OR TEST_VECTOR OR TEST_STRING OR TEST_CONCURRENT_HASHMAP OR TEST_SEGMENT_VECTOR_ARRAY_EXPSIZE OR TEST_SEGMENT_VECTOR_VECTOR_EXPSIZE OR TEST_SEGMENT_VECTOR_VECTOR_FIXEDSIZE OR TEST_ENUMERABLE_THREAD_SPECIFIC) message(FATAL_ERROR "Compiler does not support expanding variadic template variables in lambda expressions. For more information about compiler requirements, check README.md.") elseif() message(WARNING "Compiler does not support expanding variadic template variables in lambda expressions. Some tests will be skipped and some functionalities won't be installed. For more information about compiler requirements, check README.md.") endif() endif() # Check for issues with older gcc compilers if "inline" aggregate initialization # works for array class members https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65815 CHECK_CXX_SOURCE_COMPILES( "struct array { int data[2]; }; struct X { array a = { 1, 2 }; }; int main() { return 0; }" NO_GCC_AGGREGATE_INITIALIZATION_BUG) # Check for issues related to aggregate initialization in new expression. # Following code will fail for LLVM compiler https://bugs.llvm.org/show_bug.cgi?id=39988 set(CMAKE_REQUIRED_FLAGS "--std=c++11 -Wno-error") CHECK_CXX_SOURCE_COMPILES( "template struct A { A() {}; ~A() {}; }; struct B { A a; A b; }; int main() { new B{}; return 0; }" NO_CLANG_BRACE_INITIALIZATION_NEWEXPR_BUG) # Check for issues with older clang compilers which assert on delete persistent<[][]>. set(CMAKE_REQUIRED_INCLUDES ${CMAKE_SOURCE_DIR}/include ${LIBPMEMOBJ_INCLUDE_DIRS}) set(CMAKE_REQUIRED_FLAGS "--std=c++11 -Wno-error -c") CHECK_CXX_SOURCE_COMPILES( "#include using namespace pmem::obj; int main() { delete_persistent(make_persistent(2), 2); return 0; }" NO_CLANG_TEMPLATE_BUG) # This is a workaround for older incompatible versions of libstdc++ and clang. # Please see https://llvm.org/bugs/show_bug.cgi?id=15517 for more info. set(CMAKE_REQUIRED_FLAGS "--std=c++11 -Wno-error -include future") CHECK_CXX_SOURCE_COMPILES( "int main() { return 0; }" NO_CHRONO_BUG) else() set(NO_SHARED_MUTEX_BUG TRUE) set(NO_GCC_VARIADIC_TEMPLATE_BUG TRUE) set(NO_GCC_AGGREGATE_INITIALIZATION_BUG TRUE) set(NO_CLANG_BRACE_INITIALIZATION_NEWEXPR_BUG TRUE) set(NO_CLANG_TEMPLATE_BUG TRUE) set(NO_CHRONO_BUG TRUE) endif() if(CXX_STANDARD LESS 14 OR NOT NO_SHARED_MUTEX_BUG) message(WARNING "volatile_state not supported (required C++14 compliant compiler)") set(VOLATILE_STATE_PRESENT OFF) else() set(VOLATILE_STATE_PRESENT ON) endif() set(CMAKE_REQUIRED_FLAGS "--std=c++${CMAKE_CXX_STANDARD} -c") CHECK_CXX_SOURCE_COMPILES( "#include int main() { std::max_align_t var; return 0; }" MAX_ALIGN_TYPE_EXISTS) set(CMAKE_REQUIRED_FLAGS "--std=c++${CMAKE_CXX_STANDARD} -c") CHECK_CXX_SOURCE_COMPILES( "#include int main() { #if !__cpp_lib_is_aggregate static_assert(false, \"\"); #endif }" AGGREGATE_INITIALIZATION_AVAILABLE ) set(CMAKE_REQUIRED_INCLUDES ${SAVED_CMAKE_REQUIRED_INCLUDES}) set(CMAKE_REQUIRED_FLAGS ${SAVED_CMAKE_REQUIRED_FLAGS}) libpmemobj-cpp-1.9/cmake/cmake_uninstall.cmake.in000066400000000000000000000020241361501571000221200ustar00rootroot00000000000000# From: https://cmake.org/Wiki/CMake_FAQ if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") exec_program("@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif(NOT "${rm_retval}" STREQUAL 0) else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") endforeach(file) libpmemobj-cpp-1.9/cmake/functions.cmake000066400000000000000000000146621361501571000203650ustar00rootroot00000000000000# # Copyright 2018-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # functions.cmake - helper functions for CMakeLists.txt # function(join SEP OUT VALUES) string(REPLACE ";" "${SEP}" JOIN_TMP "${VALUES}") set(${OUT} "${JOIN_TMP}" PARENT_SCOPE) endfunction() # prepends prefix to list of strings function(prepend var prefix) set(listVar "") foreach(f ${ARGN}) list(APPEND listVar "${prefix}/${f}") endforeach(f) set(${var} "${listVar}" PARENT_SCOPE) endfunction() # Checks whether flag is supported by current C++ compiler and appends # it to the relevant cmake variable. # 1st argument is a flag # 2nd (optional) argument is a build type (debug, release) macro(add_flag flag) string(REPLACE - _ flag2 ${flag}) string(REPLACE " " _ flag2 ${flag2}) string(REPLACE = "_" flag2 ${flag2}) set(check_name "CXX_HAS_${flag2}") check_cxx_compiler_flag(${flag} ${check_name}) if (${${check_name}}) if (${ARGC} EQUAL 1) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}") else() set(CMAKE_CXX_FLAGS_${ARGV1} "${CMAKE_CXX_FLAGS_${ARGV1}} ${flag}") endif() endif() endmacro() macro(add_sanitizer_flag flag) set(SAVED_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES} -fsanitize=${flag}") check_cxx_compiler_flag("-fsanitize=${flag}" CXX_HAS_ASAN_UBSAN) if(CXX_HAS_ASAN_UBSAN) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=${flag}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=${flag}") else() message("${flag} sanitizer not supported") endif() set(CMAKE_REQUIRED_LIBRARIES ${SAVED_CMAKE_REQUIRED_LIBRARIES}) endmacro() # Generates cppstyle-$name and cppformat-$name targets and attaches them # as dependencies of global "cppformat" target. # cppstyle-$name target verifies C++ style of files in current source dir. # cppformat-$name target reformats files in current source dir. # If more arguments are used, then they are used as files to be checked # instead. # ${name} must be unique. function(add_cppstyle name) if(NOT CLANG_FORMAT) return() endif() if(${ARGC} EQUAL 1) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/cppstyle-${name}-status DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/utils/cppstyle ${CLANG_FORMAT} check ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/cppstyle-${name}-status ) add_custom_target(cppformat-${name} COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/utils/cppstyle ${CLANG_FORMAT} format ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp ) else() add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/cppstyle-${name}-status DEPENDS ${ARGN} COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/utils/cppstyle ${CLANG_FORMAT} check ${ARGN} COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/cppstyle-${name}-status ) add_custom_target(cppformat-${name} COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/utils/cppstyle ${CLANG_FORMAT} format ${ARGN} ) endif() add_custom_target(cppstyle-${name} DEPENDS ${CMAKE_BINARY_DIR}/cppstyle-${name}-status) add_dependencies(cppstyle cppstyle-${name}) add_dependencies(cppformat cppformat-${name}) endfunction() # Generates check-whitespace-$name target and attaches it as a dependency # of global "check-whitespace" target. # ${name} must be unique. function(add_check_whitespace name) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/check-whitespace-${name}-status DEPENDS ${ARGN} COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/utils/check_whitespace ${ARGN} COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/check-whitespace-${name}-status) add_custom_target(check-whitespace-${name} DEPENDS ${CMAKE_BINARY_DIR}/check-whitespace-${name}-status) add_dependencies(check-whitespace check-whitespace-${name}) endfunction() # Sets ${ret} to version of program specified by ${name} in major.minor format function(get_program_version_major_minor name ret) execute_process(COMMAND ${name} --version OUTPUT_VARIABLE cmd_ret ERROR_QUIET) STRING(REGEX MATCH "([0-9]+.)([0-9]+)" VERSION ${cmd_ret}) SET(${ret} ${VERSION} PARENT_SCOPE) endfunction() function(find_pmemcheck) set(ENV{PATH} ${VALGRIND_PREFIX}/bin:$ENV{PATH}) execute_process(COMMAND valgrind --tool=pmemcheck --help RESULT_VARIABLE VALGRIND_PMEMCHECK_RET OUTPUT_QUIET ERROR_QUIET) if(VALGRIND_PMEMCHECK_RET) set(VALGRIND_PMEMCHECK_FOUND 0 CACHE INTERNAL "") else() set(VALGRIND_PMEMCHECK_FOUND 1 CACHE INTERNAL "") endif() if(VALGRIND_PMEMCHECK_FOUND) execute_process(COMMAND valgrind --tool=pmemcheck true ERROR_VARIABLE PMEMCHECK_OUT OUTPUT_QUIET) string(REGEX MATCH ".*pmemcheck-([0-9.]*),.*" PMEMCHECK_OUT "${PMEMCHECK_OUT}") set(PMEMCHECK_VERSION ${CMAKE_MATCH_1} CACHE INTERNAL "") else() message(WARNING "Valgrind pmemcheck NOT found.") endif() endfunction() libpmemobj-cpp-1.9/cmake/libpmemobj++-config.cmake.in000066400000000000000000000036461361501571000224730ustar00rootroot00000000000000# # Copyright 2018, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @PACKAGE_INIT@ find_path(LIBPMEMOBJ_INCLUDE_DIR libpmemobj.h) find_library(LIBPMEMOBJ_LIBRARY NAMES pmemobj libpmemobj) find_library(LIBPMEM_LIBRARY NAMES pmem libpmem) set_and_check(LIBPMEMOBJ++_INCLUDE "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") set(LIBPMEMOBJ++_LIBRARIES ${LIBPMEMOBJ_LIBRARY} ${LIBPMEM_LIBRARY}) set(LIBPMEMOBJ++_INCLUDE_DIRS ${LIBPMEMOBJ++_INCLUDE} ${LIBPMEMOBJ_INCLUDE_DIR}) libpmemobj-cpp-1.9/cmake/libpmemobj++.pc.in000066400000000000000000000004571361501571000205470ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ Name: libpmemobj++ Description: libpmemobj++ - c++ bindings for libpmemobj library Version: @VERSION@ URL: https://github.com/pmem/libpmemobj-cpp Requires: libpmemobj >= @LIBPMEMOBJ_REQUIRED_VERSION@ Cflags: -I${includedir} libpmemobj-cpp-1.9/cmake/packages.cmake000066400000000000000000000077621361501571000201360ustar00rootroot00000000000000# # Copyright 2018-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # packages.cmake - CPack configuration for rpm and deb generation # string(TOUPPER "${CPACK_GENERATOR}" CPACK_GENERATOR) if(NOT ("${CPACK_GENERATOR}" STREQUAL "DEB" OR "${CPACK_GENERATOR}" STREQUAL "RPM")) message(FATAL_ERROR "Wrong CPACK_GENERATOR value, valid generators are: DEB, RPM") endif() set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") set(CMAKE_INSTALL_TMPDIR /tmp CACHE PATH "Output dir for tmp") set(CPACK_COMPONENTS_ALL_IN_ONE) # Filter out some of directories from %dir section, which are expected # to exist in filesystem. Leaving them might lead to conflicts with other # packages (for example with 'filesystem' package on fedora which specify # /usr, /usr/local, etc.) set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION ${CPACK_PACKAGING_INSTALL_PREFIX} ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/pkgconfig ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_INCDIR} ${CPACK_PACKAGING_INSTALL_PREFIX}/share ${CPACK_PACKAGING_INSTALL_PREFIX}/share/doc) set(CPACK_PACKAGE_NAME "libpmemobj++") set(CPACK_PACKAGE_VERSION ${VERSION}) set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "c++ bindings to libpmemobj") set(CPACK_PACKAGE_VENDOR "Intel") set(CPACK_RPM_PACKAGE_NAME "libpmemobj++-devel") set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries") set(CPACK_RPM_PACKAGE_LICENSE "BSD") set(CPACK_RPM_PACKAGE_ARCHITECTURE x86_64) set(CPACK_RPM_PACKAGE_REQUIRES "libpmemobj-devel >= ${LIBPMEMOBJ_REQUIRED_VERSION}") #set(CPACK_RPM_CHANGELOG_FILE ${CMAKE_SOURCE_DIR}/ChangeLog) set(CPACK_DEBIAN_PACKAGE_NAME "libpmemobj++-dev") set(CPACK_DEBIAN_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}) set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE amd64) set(CPACK_DEBIAN_PACKAGE_DEPENDS "libpmemobj-dev (>= ${LIBPMEMOBJ_REQUIRED_VERSION})") set(CPACK_DEBIAN_PACKAGE_MAINTAINER "marcin.slusarz@intel.com") if("${CPACK_GENERATOR}" STREQUAL "RPM") set(CPACK_PACKAGE_FILE_NAME ${CPACK_RPM_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}.${CPACK_RPM_PACKAGE_ARCHITECTURE}) elseif("${CPACK_GENERATOR}" STREQUAL "DEB") # We are using "gnutar" to avoid this bug: # https://gitlab.kitware.com/cmake/cmake/issues/14332 set(CPACK_DEBIAN_ARCHIVE_TYPE "gnutar") set(CPACK_PACKAGE_FILE_NAME ${CPACK_DEBIAN_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}) endif() set(targetDestDir ${CMAKE_INSTALL_TMPDIR}) include(CPack) libpmemobj-cpp-1.9/cmake/tbb.cmake000066400000000000000000000040521361501571000171140ustar00rootroot00000000000000# Copyright 2017-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if(PKG_CONFIG_FOUND) pkg_check_modules(TBB tbb) endif() if(NOT TBB_FOUND) # find_package without unsetting this var is not working correctly unset(TBB_FOUND CACHE) find_package(TBB COMPONENTS tbb) if(TBB_FOUND) message(STATUS "TBB package found without pkg-config") endif() set(TBB_LIBRARIES ${TBB_IMPORTED_TARGETS}) endif() if(NOT TBB_FOUND) message(WARNING "TBB not found. Please set TBB_DIR CMake variable if TBB \ is installed in a non-standard directory, like: -DTBB_DIR=") endif() libpmemobj-cpp-1.9/cmake/version.hpp.in000066400000000000000000000036731361501571000201560ustar00rootroot00000000000000/* * Copyright 2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file * Auto-generated file containing source version */ #ifndef LIBPMEMOBJ_CPP_VERSION_HPP #define LIBPMEMOBJ_CPP_VERSION_HPP #define LIBPMEMOBJ_CPP_VERSION_MAJOR @VERSION_MAJOR@ #define LIBPMEMOBJ_CPP_VERSION_MINOR @VERSION_MINOR@ #define LIBPMEMOBJ_CPP_VERSION_PATCH @VERSION_PATCH@ #define LIBPMEMOBJ_CPP_VERSION "@VERSION@" #endif /* LIBPMEMOBJ_CPP_VERSION_HPP */ libpmemobj-cpp-1.9/codecov.yml000066400000000000000000000002071361501571000164260ustar00rootroot00000000000000coverage: status: project: default: threshold: 0.2 ignore: - utils/ - tests/ - doc/ - examples/ libpmemobj-cpp-1.9/doc/000077500000000000000000000000001361501571000150275ustar00rootroot00000000000000libpmemobj-cpp-1.9/doc/CMakeLists.txt000066400000000000000000000042431361501571000175720ustar00rootroot00000000000000# # Copyright 2018-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. include(FindDoxygen) if(DOXYGEN_FOUND AND DOXYGEN_DOT_FOUND) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libpmemobj++.Doxyfile.in" "${CMAKE_CURRENT_BINARY_DIR}/libpmemobj++.Doxyfile" @ONLY) add_custom_target(doc ALL ${DOXYGEN_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/libpmemobj++.Doxyfile" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/cpp_html/ DESTINATION ${CMAKE_INSTALL_DOCDIR}) elseif(NOT DOXYGEN_FOUND) message(WARNING "Doxygen not found - documentation will not be generated") else() message(WARNING "Dot tool not found - documentation will not be generated") endif() libpmemobj-cpp-1.9/doc/libpmemobj++.Doxyfile.in000066400000000000000000000226271361501571000214200ustar00rootroot00000000000000#--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See https://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "PMDK C++ bindings" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "This is the C++ bindings documentation for PMDK's libpmemobj." # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo # to the output directory. PROJECT_LOGO = # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO these classes will be included in the various overviews. This option has # no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = YES # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. QUIET = YES #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. # Note: If this tag is empty the current directory is searched. INPUT = @CMAKE_SOURCE_DIR@/include/libpmemobj++ INPUT += @CMAKE_SOURCE_DIR@/include/libpmemobj++/README.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: https://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank the # following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, # *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, # *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. FILE_PATTERNS = *.hpp # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = @CMAKE_SOURCE_DIR@/examples/doc_snippets # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to strip. # Note that you can specify absolute paths here, but also relative paths, which will # be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. FULL_PATH_NAMES = YES STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@/include/ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name # of the header file containing the class definition is used. Otherwise one # should specify the list of include paths that are normally passed to the # compiler using the -I flag. STRIP_FROM_INC_PATH = @CMAKE_SOURCE_DIR@/include/ #--------------------------------------------------------------------------- # Configuration options related to the LATEX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES doxygen will generate LATEX output # The default value is: YES. GENERATE_LATEX = NO #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = cpp_html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to NO can help when comparing the output of multiple runs. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # The PREDEFINED tag can be used to specify one or more macro names that are # defined before the preprocessor is started (similar to the -D option of e.g. # gcc). The argument of the tag is a list of macros of the form: name or # name=definition (no spaces). If the definition and the "=" are omitted, "=1" # is assumed. To prevent a macro definition from being undefined via #undef or # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. PREDEFINED = __cpp_lib_uncaught_exceptions _WIN32 libpmemobj-cpp-1.9/examples/000077500000000000000000000000001361501571000161005ustar00rootroot00000000000000libpmemobj-cpp-1.9/examples/CMakeLists.txt000066400000000000000000000164331361501571000206470ustar00rootroot00000000000000# # Copyright 2018-2020, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if(MSVC_VERSION) add_flag(-W4) else() add_flag(-Wall) endif() add_flag(-Wpointer-arith) add_flag(-Wsign-compare) add_flag(-Wunreachable-code-return) add_flag(-Wmissing-variable-declarations) add_flag(-fno-common) #add_flag(-Wunused-macros) #add_flag(-Wsign-conversion) add_flag(-ggdb DEBUG) add_flag(-DDEBUG DEBUG) add_flag("-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2" RELEASE) if(USE_ASAN) add_sanitizer_flag(address) endif() if(USE_UBSAN) add_sanitizer_flag(undefined) endif() if(COVERAGE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -coverage") endif() include_directories(${LIBPMEMOBJ_INCLUDE_DIRS} .) link_directories(${LIBPMEMOBJ_LIBRARY_DIRS}) add_cppstyle(examples-common ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) add_check_whitespace(examples-common ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) add_check_whitespace(examples-cmake ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) add_cppstyle(examples-array ${CMAKE_CURRENT_SOURCE_DIR}/array/*.*pp) add_check_whitespace(examples-array ${CMAKE_CURRENT_SOURCE_DIR}/array/*.*pp) add_cppstyle(examples-doc_snippets ${CMAKE_CURRENT_SOURCE_DIR}/doc_snippets/*.*pp) add_check_whitespace(examples-doc_snippets ${CMAKE_CURRENT_SOURCE_DIR}/doc_snippets/*.*pp) add_cppstyle(examples-map_cli ${CMAKE_CURRENT_SOURCE_DIR}/map_cli/*.*pp) add_check_whitespace(examples-map_cli ${CMAKE_CURRENT_SOURCE_DIR}/map_cli/*.*pp) add_cppstyle(examples-panaconda ${CMAKE_CURRENT_SOURCE_DIR}/panaconda/*.*pp) add_check_whitespace(examples-panaconda ${CMAKE_CURRENT_SOURCE_DIR}/panaconda/*.*pp) add_cppstyle(examples-pman ${CMAKE_CURRENT_SOURCE_DIR}/pman/*.*pp) add_check_whitespace(examples-pman ${CMAKE_CURRENT_SOURCE_DIR}/pman/*.*pp) add_cppstyle(examples-pmpong ${CMAKE_CURRENT_SOURCE_DIR}/pmpong/*.*pp) add_check_whitespace(examples-pmpong ${CMAKE_CURRENT_SOURCE_DIR}/pmpong/*.*pp) add_cppstyle(examples-queue ${CMAKE_CURRENT_SOURCE_DIR}/queue/*.*pp) add_check_whitespace(examples-queue ${CMAKE_CURRENT_SOURCE_DIR}/queue/*.*pp) add_cppstyle(examples-simplekv ${CMAKE_CURRENT_SOURCE_DIR}/simplekv/*.*pp) add_check_whitespace(examples-simplekv ${CMAKE_CURRENT_SOURCE_DIR}/simplekv/*.*pp) add_cppstyle(examples-simplekv_rebuild ${CMAKE_CURRENT_SOURCE_DIR}/simplekv_rebuild/*.*pp) add_check_whitespace(examples-simplekv_rebuild ${CMAKE_CURRENT_SOURCE_DIR}/simplekv_rebuild/*.*pp) function(add_example name) set(srcs ${ARGN}) prepend(srcs ${CMAKE_CURRENT_SOURCE_DIR} ${srcs}) add_executable(example-${name} ${srcs}) target_link_libraries(example-${name} ${LIBPMEMOBJ_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) endfunction() if(PKG_CONFIG_FOUND) pkg_check_modules(CURSES QUIET ncurses) else() # Specifies that we want FindCurses to find ncurses and not just any # curses library set(CURSES_NEED_NCURSES TRUE) find_package(Curses QUIET) endif() if(PKG_CONFIG_FOUND) pkg_check_modules(SFML QUIET sfml-all>=2.4) else() # SFML 2.5 has different cmake interface than <= 2.4 so previous versions are not supported find_package(SFML 2.5 QUIET COMPONENTS graphics window system) set(SFML_LIBRARIES sfml-graphics sfml-window sfml-system) endif() if (TEST_ARRAY) add_example(queue queue/queue.cpp) endif() if (TEST_VECTOR) add_example(simplekv simplekv/simplekv.cpp) if(NOT CLANG_DESTRUCTOR_REFERENCE_BUG_PRESENT) add_example(simplekv_rebuild simplekv_rebuild/simplekv_rebuild.cpp) else() message(WARNING "skipping simplekv_rebuild example - it requires clang >= ${CLANG_REQUIRED_BY_DESTRUCTOR_REFERENCE_BUG}") endif() endif() if(CURSES_FOUND) add_example(pman pman/pman.cpp) target_include_directories(example-pman PUBLIC ${CURSES_INCLUDE_DIR}) target_link_libraries(example-pman ${CURSES_LIBRARIES}) else() message(WARNING "ncurses not found - pman won't be build") endif() if(SFML_FOUND) # XXX: this can only be run in Release mode - in Debug SFML doesn't add all dependencies automatically add_example(pmpong pmpong/Ball.cpp pmpong/GameController.cpp pmpong/GameOverView.cpp pmpong/GameView.cpp pmpong/MainGame.cpp pmpong/MenuView.cpp pmpong/Paddle.cpp pmpong/PongGameStatus.cpp pmpong/Pool.cpp) target_include_directories(example-pmpong PUBLIC ${SFML_INCLUDE_DIR}) target_link_libraries(example-pmpong ${SFML_LIBRARIES}) if(NOT WIN32) find_program(FCLIST NAMES fc-list) if(NOT FCLIST) message(WARNING "fc-list not found. Install fontconfig to allow examples-pmpong to automatically find fonts.") endif() execute_process(COMMAND bash -c "fc-list --format='%{file}\n' | head -n1 | tr -d '\n'" OUTPUT_VARIABLE FONT_PATH ERROR_QUIET) set(font ${FONT_PATH}) else() set(font "C:/Windows/Fonts/Arial.ttf") endif() target_compile_options(example-pmpong PUBLIC -DLIBPMEMOBJ_CPP_PMPONG_FONT_PATH="${font}") else() message(WARNING "SFML 2.4 or newer not found - pmpong won't be build") endif() if(CURSES_FOUND) add_example(panaconda panaconda/panaconda.cpp) target_include_directories(example-panaconda PUBLIC ${CURSES_INCLUDE_DIR}) target_link_libraries(example-panaconda ${CURSES_LIBRARIES}) else() message(WARNING "ncurses not found - panaconda won't be build") endif() add_example(map_cli map_cli/map_cli.cpp) add_example(array array/array.cpp) add_library(doc_snippets_v OBJECT doc_snippets/v.cpp) add_library(doc_snippets_persistent OBJECT doc_snippets/persistent.cpp) add_library(doc_snippets_make_persistent OBJECT doc_snippets/make_persistent.cpp) add_library(doc_snippets_mutex OBJECT doc_snippets/mutex.cpp) add_library(doc_snippets_pool OBJECT doc_snippets/pool.cpp) add_library(doc_snippets_transaction OBJECT doc_snippets/transaction.cpp) add_library(doc_snippets_concurrent_hash_map OBJECT doc_snippets/concurrent_hash_map.cpp) add_library(doc_snippets_segment_vector OBJECT doc_snippets/segment_vector.cpp) if (TEST_VECTOR) add_library(doc_snippets_defrag OBJECT doc_snippets/defrag.cpp) else() message(WARNING "skipping doc_snippets_defrag example - it requires vector enabled") endif() libpmemobj-cpp-1.9/examples/README000066400000000000000000000005421361501571000167610ustar00rootroot00000000000000This directory contains examples for libpmemobj-cpp, the library providing a transactional object store for pmem. Some of these examples are explained in more detail here: https://pmem.io/libpmemobj-cpp/ If you're looking for documentation to get you started using PMDK, start here: https://pmem.io/pmdk and follow the links to examples and man pages. libpmemobj-cpp-1.9/examples/array/000077500000000000000000000000001361501571000172165ustar00rootroot00000000000000libpmemobj-cpp-1.9/examples/array/CMakeLists.txt000066400000000000000000000041141361501571000217560ustar00rootroot00000000000000# # Copyright 2018-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cmake_minimum_required(VERSION 3.3) project(array CXX) set(CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 11) include(FindThreads) if(NOT WIN32) find_package(PkgConfig QUIET) endif() if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMOBJ++ REQUIRED libpmemobj++) else() find_package(LIBPMEMOBJ++ REQUIRED) endif() link_directories(${LIBPMEMOBJ++_LIBRARY_DIRS}) add_executable(array array.cpp) target_include_directories(array PUBLIC ${LIBPMEMOBJ++_INCLUDE_DIRS} . ..) target_link_libraries(array ${LIBPMEMOBJ++_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) libpmemobj-cpp-1.9/examples/array/README.md000066400000000000000000000017541361501571000205040ustar00rootroot00000000000000## Basic usage: After [building](https://github.com/pmem/libpmemobj-cpp#how-to-build) the source, navigate to the `/build/examples/` directory. Operations include: `alloc`, `realloc`, `free`, and `print`. `$ ./example-array [print|alloc|free|realloc] ` ## Options: ### Alloc: `$ ./example-array alloc ` Example: ``` $ ./example-array file alloc myArray 4 $ ./example-array file print myArray myArray = [0, 1, 2, 3] ``` ### Realloc: `$ ./example-array realloc ` Example: ``` $ ./example-array file realloc myArray 7 $ ./example-array file print myArray myArray = [0, 1, 2, 3, 0, 0, 0] ``` ### Free: `$ ./example-array free ` Example: ``` $ ./example-array file free myArray $ ./example-array file print myArray No array found with name: myArray ``` ### Print: `$ ./example-array print ` Example output can be seen in above example sections. libpmemobj-cpp-1.9/examples/array/array.cpp000066400000000000000000000223611361501571000210440ustar00rootroot00000000000000/* * Copyright 2018-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * array.cpp -- array example implemented using libpmemobj C++ bindings */ #include "libpmemobj_cpp_examples_common.hpp" #include #include #include #include #include #include #include using namespace pmem; using namespace pmem::obj; namespace { // array_op: available array operations enum class array_op { UNKNOWN, PRINT, FREE, REALLOC, ALLOC, MAX_ARRAY_OP }; const int POOLSIZE = 1024 * 1024 * 64; const int MAX_BUFFLEN = 30; const std::string LAYOUT = ""; std::string prog_name; // parse_array_op: parses the operation string and returns the matching array_op array_op parse_array_op(const char *str) { if (strcmp(str, "print") == 0) return array_op::PRINT; else if (strcmp(str, "free") == 0) return array_op::FREE; else if (strcmp(str, "realloc") == 0) return array_op::REALLOC; else if (strcmp(str, "alloc") == 0) return array_op::ALLOC; else return array_op::UNKNOWN; } } namespace examples { class pmem_array { // array_list: struct to hold name, size, array and pointer to next struct array_list { char name[MAX_BUFFLEN]; p size; persistent_ptr array; persistent_ptr next; }; persistent_ptr head = nullptr; public: // add_array: allocate space on heap for new array and add it to head void add_array(pool_base &pop, const char *name, int size) { if (find_array(name) != nullptr) { std::cout << "Array with name: " << name << " already exists. "; std::cout << "If you prefer, you can reallocate this array " << std::endl; print_usage(array_op::REALLOC, "./example-array"); } else if (size < 1) { std::cout << "size must be a non-negative integer" << std::endl; print_usage(array_op::ALLOC, "./example-array"); } else { transaction::run(pop, [&] { auto new_array = make_persistent(); strcpy(new_array->name, name); new_array->size = (size_t)size; new_array->array = make_persistent(size); new_array->next = nullptr; // assign values to newArray->array for (size_t i = 0; i < new_array->size; i++) new_array->array[i] = i; new_array->next = head; head = new_array; }); } } // delete_array: deletes array from the array_list and removes // previously allocated space on heap void delete_array(pool_base &pop, const char *name) { // prevArr will equal head if array wanted is either first OR // second element persistent_ptr prev_arr = find_array(name, true); // if array_list length = 0 OR array not found in list if (prev_arr == nullptr) { std::cout << "No array found with name: " << name << std::endl; return; } persistent_ptr cur_arr; if (strcmp(prev_arr->name, name) == 0) { // cur = prev= head, either only one element in list or // array is first element cur_arr = head; } else { cur_arr = prev_arr->next; } transaction::run(pop, [&] { if (head == cur_arr) head = cur_arr->next; else prev_arr->next = cur_arr->next; delete_persistent(cur_arr->array, cur_arr->size); delete_persistent(cur_arr); }); } // print_array: prints array_list contents to cout void print_array(const char *name) { persistent_ptr arr = find_array(name); if (arr == nullptr) { std::cout << "No array found with name: " << name << std::endl; } else { std::cout << arr->name << " = ["; for (size_t i = 0; i < arr->size - 1; i++) std::cout << arr->array[i] << ", "; std::cout << arr->array[arr->size - 1] << "]" << std::endl; } } // resize: reallocate space on heap to change the size of the array void resize(pool_base &pop, const char *name, int size) { persistent_ptr arr = find_array(name); if (arr == nullptr) { std::cout << "No array found with name: " << name << std::endl; } else if (size < 1) { std::cout << "size must be a non-negative integer" << std::endl; print_usage(array_op::REALLOC, prog_name); } else { transaction::run(pop, [&] { persistent_ptr new_array = make_persistent(size); size_t copy_size = arr->size; if ((size_t)size < arr->size) copy_size = (size_t)size; for (size_t i = 0; i < copy_size; i++) new_array[i] = arr->array[i]; delete_persistent(arr->array, arr->size); arr->size = (size_t)size; arr->array = new_array; }); } } // print_usage: prints usage for each type of array operation void print_usage(array_op op, std::string arg_zero) { switch (op) { case array_op::PRINT: std::cerr << "print array usage: " << arg_zero << " print " << std::endl; break; case array_op::FREE: std::cerr << "free array usage: " << arg_zero << " free " << std::endl; break; case array_op::REALLOC: std::cerr << "realloc array usage: " << arg_zero << " realloc " << std::endl; break; case array_op::ALLOC: std::cerr << "alloc array usage: " << arg_zero << " alloc " << std::endl; break; default: std::cerr << "usage: " << arg_zero << " " << std::endl; } } private: // find_array: loops through head to find array with specified name persistent_ptr find_array(const char *name, bool find_prev = false) { if (head == nullptr) return head; persistent_ptr cur = head; persistent_ptr prev = head; while (cur) { if (strcmp(cur->name, name) == 0) { if (find_prev) return prev; else return cur; } prev = cur; cur = cur->next; } return nullptr; } }; } int main(int argc, char *argv[]) { /* * Inputs should be one of: * ./example-array print * ./example-array free * ./example-array realloc * ./example-array alloc * // currently only enabled for arrays of int */ prog_name = argv[0]; if (argc < 4) { std::cerr << "usage: " << prog_name << " " << std::endl; return 1; } // check length of array name to ensure doesn't exceed buffer. const char *name = argv[3]; if (strlen(name) > MAX_BUFFLEN) { std::cout << "Name exceeds buffer length of 30 characters. Please shorten and try again." << std::endl; return 1; } const char *file = argv[1]; pool pop; if (file_exists(file) != 0) pop = pool::create(file, LAYOUT, POOLSIZE, CREATE_MODE_RW); else pop = pool::open(file, LAYOUT); persistent_ptr arr = pop.root(); array_op op = parse_array_op(argv[2]); switch (op) { case array_op::PRINT: if (argc == 4) arr->print_array(name); else arr->print_usage(op, prog_name); break; case array_op::FREE: if (argc == 4) arr->delete_array(pop, name); else arr->print_usage(op, prog_name); break; case array_op::REALLOC: if (argc == 5) arr->resize(pop, name, atoi(argv[4])); else arr->print_usage(op, prog_name); break; case array_op::ALLOC: if (argc == 5) arr->add_array(pop, name, atoi(argv[4])); else arr->print_usage(op, prog_name); break; default: std::cout << "Ruh roh! You passed an invalid operation!" << std::endl; arr->print_usage(op, prog_name); break; } pop.close(); return 0; } libpmemobj-cpp-1.9/examples/doc_snippets/000077500000000000000000000000001361501571000205725ustar00rootroot00000000000000libpmemobj-cpp-1.9/examples/doc_snippets/concurrent_hash_map.cpp000066400000000000000000000117661361501571000253330ustar00rootroot00000000000000/* * Copyright 2019-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * concurrent_hash_map.cpp -- C++ documentation snippets. */ //! [concurrent_hash_map_example] #include #include #include #include #include #include using namespace pmem::obj; // In this example we will be using concurrent_hash_map with p type for // both keys and values using hashmap_type = concurrent_hash_map, p>; const int THREADS_NUM = 30; const bool remove_hashmap = true; // This is basic example and we only need to use concurrent_hash_map. Hence we // will correlate memory pool root object with single instance of persistent // pointer to hasmap_type struct root { persistent_ptr pptr; }; int main(int argc, char *argv[]) { if (argc != 2) std::cerr << "usage: " << argv[0] << " file-name" << std::endl; auto path = argv[1]; auto pop = pool::open(path, "concurrent_hash_map example"); auto r = pop.root(); if (r->pptr == nullptr) { // Logic when file didn't exist when open() was called. After // the pool was created, we have to allocate object of // hashmap_type and attach it to the root object. pmem::obj::transaction::run(pop, [&] { r->pptr = make_persistent(); }); } else { // Logic when file already exists. After opening of the pool we // have to call runtime_initialize() function in order to // recalculate mask and check for consistentcy. pop.root()->pptr->runtime_initialize(); // defragment the whole pool at the beginning pop.root()->pptr->defragment(); } auto &map = *pop.root()->pptr; std::vector threads; threads.reserve(static_cast(THREADS_NUM)); // Insert THREADS_NUM / 3 key-value pairs to the hashmap. This operation // is thread-safe. for (int i = 0; i < THREADS_NUM / 3; ++i) { threads.emplace_back([&]() { for (int i = 0; i < 10 * THREADS_NUM; ++i) { map.insert(hashmap_type::value_type(i, i)); } }); } // Erase THREADS_NUM /3 key-value pairs from the hashmap. This operation // is thread-safe. for (int i = 0; i < THREADS_NUM / 3; ++i) { threads.emplace_back([&]() { for (int i = 0; i < 10 * THREADS_NUM; ++i) { map.erase(i); } }); } // Check if given key is in the hashmap. For the time of an accessor // life, the read-write lock is taken on the item. for (int i = 0; i < THREADS_NUM / 3; ++i) { threads.emplace_back([&]() { for (int i = 0; i < 10 * THREADS_NUM; ++i) { hashmap_type::accessor acc; bool res = map.find(acc, i); if (res) { assert(acc->first == i); assert(acc->second >= i); acc->second.get_rw() += 1; pop.persist(acc->second); } } }); } for (auto &t : threads) { t.join(); } // defragment the whole pool at the end map.defragment(); // Erase remaining itemes in map. This function is not thread-safe, // hence the function is being called only after thread execution has // completed. map.clear(); // If hash map is to be removed, free_data() method should be called // first. Otherwise, if deallocating internal hash map metadata in // a destructor fail program might terminate. if (remove_hashmap) { map.free_data(); // map.clear() // WRONG // After free_data() concurrent hash map cannot be used anymore! transaction::run(pop, [&] { delete_persistent(pop.root()->pptr); }); } pop.close(); return 0; } //! [concurrent_hash_map_example] libpmemobj-cpp-1.9/examples/doc_snippets/defrag.cpp000066400000000000000000000074411361501571000225340ustar00rootroot00000000000000/* * Copyright 2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ //! [defrag_usage_example] #include #include #include #include #include #include using namespace pmem::obj; void defrag_example() { struct root { persistent_ptr i; persistent_ptr> v; persistent_ptr> v2; }; auto pop = pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); auto r = pop.root(); persistent_ptr i_ptr; transaction::run(pop, [&] { r->i = make_persistent(5); r->v = make_persistent>(); r->v2 = make_persistent>(); i_ptr = make_persistent(10); }); r->v->push_back(15); /* Create a defrag object for elements in the current pool */ defrag my_defrag(pop); /* And add all selected pointers for the defragmentation */ my_defrag.add(r->i); /* * Adding ptr> means also adding internal container's * pointer(s), because it ('vector' in this case) implements * method 'for_each_ptr'. */ my_defrag.add(r->v); /* * We can also add just the reference of an element (in this case * vector). This means the persistent_ptr ('r->v2' in this * case) itself won't be added for the defragmentation. */ my_defrag.add(*r->v2); my_defrag.add(i_ptr); /* * Out of curosity, we can check if a class of an object is * defragmentable or not. */ std::cout << is_defragmentable>(); /* false */ static_assert(is_defragmentable>(), "should not assert"); pobj_defrag_result result; try { /* * Start when all chosen pointers are added. It can throw an * error, when failed (e.g. to allocate) in any moment of the * process. */ result = my_defrag.run(); } catch (pmem::defrag_error &e) { std::cerr << e.what() << "No. of the relocated objects: " << e.result.relocated << " out of total: " << e.result.total << " processed." << std::endl; } /* After successful defragmentation result contains basic summary */ std::cout << "No. of relocated objects: " << result.relocated << " out of total: " << result.total << " processed." << std::endl; } //! [defrag_usage_example] libpmemobj-cpp-1.9/examples/doc_snippets/make_persistent.cpp000066400000000000000000000156131361501571000245010ustar00rootroot00000000000000/* * Copyright 2016-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * make_persistent.cpp -- C++ documentation snippets. */ //! [make_example] #include #include #include #include #include #include using namespace pmem::obj; void make_persistent_example() { struct compound_type { compound_type(int val, double dval) : some_variable(val), some_other_variable(dval) { } void set_some_variable(int val) { some_variable = val; } p some_variable; p some_other_variable; }; // pool root structure struct root { persistent_ptr comp; // }; // create a pmemobj pool auto pop = pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); auto proot = pop.root(); // typical usage schemes transaction::run(pop, [&] { // allocation with constructor argument passing proot->comp = make_persistent(1, 2.0); // transactionally delete the object, ~compound_type() is called delete_persistent(proot->comp); }); // throws an transaction_scope_error exception auto arr1 = make_persistent(2, 15.0); delete_persistent(arr1); } //! [make_example] //! [make_array_example] #include #include #include #include #include #include using namespace pmem::obj; void make_persistent_array_example() { struct compound_type { compound_type() : some_variable(0), some_other_variable(0) { } void set_some_variable(int val) { some_variable = val; } p some_variable; p some_other_variable; }; // pool root structure struct root { persistent_ptr comp; // }; // create a pmemobj pool auto pop = pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); auto proot = pop.root(); // typical usage schemes transaction::run(pop, [&] { // allocate an array of 20 objects - compound_type must be // default constructible proot->comp = make_persistent(20); // another allocation method auto arr1 = make_persistent(); // transactionally delete arrays , ~compound_type() is called delete_persistent(proot->comp, 20); delete_persistent(arr1); }); // throws an transaction_scope_error exception auto arr1 = make_persistent(); delete_persistent(arr1); } //! [make_array_example] //! [make_atomic_example] #include #include #include #include #include #include using namespace pmem::obj; void make_persistent_atomic_example() { struct compound_type { compound_type(int val, double dval) : some_variable(val), some_other_variable(dval) { } void set_some_variable(int val) { some_variable = val; } p some_variable; p some_other_variable; }; // pool root structure struct root { persistent_ptr comp; // }; // create a pmemobj pool auto pop = pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); auto proot = pop.root(); // typical usage schemes // atomic allocation and construction with arguments passing make_persistent_atomic(pop, proot->comp, 1, 2.0); // atomic object deallocation, ~compound_type() is not called delete_persistent(proot->comp); // error prone cases transaction::run(pop, [&] { // possible invalid state in case of transaction abort make_persistent_atomic(pop, proot->comp, 1, 1.3); delete_persistent_atomic(proot->comp); }); } //! [make_atomic_example] //! [make_array_atomic_example] #include #include #include #include #include #include using namespace pmem::obj; void make_persistent_array_atomic_example() { struct compound_type { compound_type() : some_variable(0), some_other_variable(0) { } void set_some_variable(int val) { some_variable = val; } p some_variable; p some_other_variable; }; // pool root structure struct root { persistent_ptr comp; // }; // create a pmemobj pool auto pop = pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); auto proot = pop.root(); // typical usage schemes // atomic array allocation and construction - the compound_type has to // be default constructible make_persistent_atomic(pop, proot->comp, 20); persistent_ptr arr; make_persistent_atomic(pop, arr); // atomic array deallocation, no destructor being called delete_persistent_atomic(proot->comp, 20); delete_persistent_atomic(arr); // error prone cases transaction::run(pop, [&] { // possible invalid state in case of transaction abort make_persistent_atomic(pop, proot->comp, 30); delete_persistent_atomic(proot->comp, 30); }); } //! [make_array_atomic_example] libpmemobj-cpp-1.9/examples/doc_snippets/mutex.cpp000066400000000000000000000106761361501571000224520ustar00rootroot00000000000000/* * Copyright 2016-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * mutex.cpp -- C++ documentation snippets. */ //! [unique_guard_example] #include #include #include #include namespace nvobj = pmem::obj; void unique_guard_example() { // pool root structure struct root { nvobj::mutex pmutex; }; // create a pmemobj pool auto pop = nvobj::pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); auto proot = pop.root(); // typical usage schemes std::lock_guard guard(proot->pmutex); std::unique_lock other_guard(proot->pmutex); } //! [unique_guard_example] //! [shared_mutex_example] #include #include #include #include namespace nvobj = pmem::obj; void shared_mutex_example() { // pool root structure struct root { nvobj::shared_mutex pmutex; }; // create a pmemobj pool auto pop = nvobj::pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); auto proot = pop.root(); // typical usage schemes proot->pmutex.lock_shared(); std::unique_lock guard(proot->pmutex); } //! [shared_mutex_example] //! [timed_mutex_example] #include #include #include #include namespace nvobj = pmem::obj; void timed_mutex_example() { // pool root structure struct root { nvobj::timed_mutex pmutex; }; // create a pmemobj pool auto pop = nvobj::pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); auto proot = pop.root(); const auto timeout = std::chrono::milliseconds(100); // typical usage schemes proot->pmutex.try_lock_for(timeout); proot->pmutex.try_lock_until(std::chrono::steady_clock::now() + timeout); } //! [timed_mutex_example] //! [cond_var_example] #include #include #include #include #include #include namespace nvobj = pmem::obj; void cond_var_example() { // pool root structure struct root { nvobj::mutex pmutex; nvobj::condition_variable cond; int counter; }; // create a pmemobj pool auto pop = nvobj::pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); auto proot = pop.root(); // run worker to bump up the counter std::thread worker([&] { std::unique_lock lock(proot->pmutex); while (proot->counter < 1000) ++proot->counter; // unlock before notifying to avoid blocking on waiting thread lock.unlock(); // notify the waiting thread proot->cond.notify_one(); }); std::unique_lock lock(proot->pmutex); // wait on condition variable proot->cond.wait(lock, [&] { return proot->counter >= 1000; }); worker.join(); } //! [cond_var_example] libpmemobj-cpp-1.9/examples/doc_snippets/persistent.cpp000066400000000000000000000155311361501571000235030ustar00rootroot00000000000000/* * Copyright 2016-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * persistent.cpp -- C++ documentation snippets. */ //! [p_property_example] #include #include #include #include using namespace pmem::obj; void p_property_example() { struct compound_type { void set_some_variable(int val) { some_variable = val; } int some_variable; double some_other_variable; }; // pool root structure static struct root { p counter; // this is OK p whoops; // this is hard to use } proot; // create a pmemobj pool auto pop = pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); // typical usage schemes transaction::run(pop, [&] { proot.counter = 12; // atomic // one way to change `whoops` proot.whoops.get_rw().set_some_variable(2); proot.whoops.get_rw().some_other_variable = 3.0; }); // Changing a p<> variable outside of a transaction is a volatile // modification. No way to ensure persistence in case of power failure. proot.counter = 12; } //! [p_property_example] //! [persistent_ptr_example] #include #include #include #include #include using namespace pmem::obj; void persistent_ptr_example() { struct compound_type { void set_some_variable(int val) { some_variable = val; } int some_variable; double some_other_variable; }; // pool root structure struct root { persistent_ptr comp; } proot; // create a pmemobj pool auto pop = pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); // typical usage schemes transaction::run(pop, [&] { proot.comp = make_persistent(); // allocation proot.comp->set_some_variable(12); // call function proot.comp->some_other_variable = 2.3; // set variable }); // reading from the persistent_ptr compound_type tmp = *proot.comp; (void)tmp; // Changing a persistent_ptr<> variable outside of a transaction is a // volatile modification. No way to ensure persistence in case of power // failure. proot.comp->some_variable = 12; } //! [persistent_ptr_example] //! [persistent_ptr_casting_example] #include #include #include #include #include #include using namespace pmem::obj; struct root { persistent_ptr pfoo; }; void persistent_ptr_conversion_example(pool &pop) { /* Casting persistent_ptr to persistent_ptr_base */ transaction::run(pop, [&] { /* Good: any persistent_ptr can be stored in a base ptr */ persistent_ptr_base i_ptr_base = make_persistent(10); /* * Wrong: even though raw pointer can be used to create new * persistent_ptr it's not advised to use it this way, since * there's no information about underlying/template type */ persistent_ptr dptr = i_ptr_base.raw(); std::cout << *dptr; // contains trash data /* * Acceptable: it's not advised, but it will work properly. * Although, you have to be sure the underlying type is correct */ persistent_ptr iptr_nonbase = i_ptr_base.raw(); std::cout << *iptr_nonbase; // contains proper data /* * Wrong: illegal call for derived constructor. * no viable conversion from 'persistent_ptr_base' to * 'persistent_ptr' */ // iptr_nonbase = i_ptr_base; /* * Wrong: conversion from base class to persistent_ptr is not * possible: no matching conversion from 'persistent_ptr_base' * to 'persistent_ptr' */ // iptr_nonbase = static_cast>(i_ptr_base); /* Good: you can use base and ptr classes with volatile pointer */ persistent_ptr i_ptr = make_persistent(10); persistent_ptr_base *i_ptr_ref = &i_ptr; std::cout << i_ptr_ref->raw().off; // contains PMEMoid's data }); struct A { uint64_t a; }; struct B { uint64_t b; }; struct C : public A, public B { uint64_t c; }; /* Convertible types, using struct A, B and C */ transaction::run(pop, [] { /* Good: conversion from type C to B, using copy constructor */ auto cptr = make_persistent(); persistent_ptr bptr = cptr; std::cout << (bptr->b == cptr->b); // thanks to conversion and // recalculating offsets it's correct /* * Good: conversion from type C to B, using converting * assignment operator */ persistent_ptr bptr2; bptr2 = cptr; std::cout << (bptr2->b == cptr->b); // true /* Good: direct conversion using static_cast */ persistent_ptr bptr3 = static_cast>(cptr); std::cout << (bptr3->b == cptr->b); // true /* * Wrong: conversion to base class and then to different ptr. * class no matching conversion from 'persistent_ptr_base' to * 'persistent_ptr' */ // auto cptr2 = (persistent_ptr_base)cptr; // persistent_ptr bptr4 = // static_cast>(cptr2); /* * Wrong: conversion to 'void *' and then to different ptr * class. ambiguous conversion from 'void *' to * 'persistent_ptr' */ // auto cptr3 = (void *)&cptr; // persistent_ptr bptr5 = // static_cast>(cptr3); }); } //! [persistent_ptr_casting_example] libpmemobj-cpp-1.9/examples/doc_snippets/pool.cpp000066400000000000000000000071171361501571000222550ustar00rootroot00000000000000/* * Copyright 2016-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * pool.cpp -- C++ documentation snippets. */ //! [pool_example] #include #include #include #include using namespace pmem::obj; void pool_example() { // pool root structure struct root { p some_array[42]; p some_other_array[42]; p some_variable; }; // create a pmemobj pool auto pop = pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); // close a pmemobj pool pop.close(); // or open a pmemobj pool pop = pool::open("poolfile", "layout"); // typical usage schemes auto root_obj = pop.root(); // low-level memory manipulation root_obj->some_variable = 3.2; pop.persist(root_obj->some_variable); pop.memset_persist(root_obj->some_array, 2, sizeof(root_obj->some_array)); pop.memcpy_persist(root_obj->some_other_array, root_obj->some_array, sizeof(root_obj->some_array)); pop.close(); // check pool consistency pool::check("poolfile", "layout"); } //! [pool_example] //! [pool_base_example] #include #include #include #include using namespace pmem::obj; void pool_base_example() { struct some_struct { p some_array[42]; p some_other_array[42]; p some_variable; }; // create a pmemobj pool auto pop = pool_base::create("poolfile", "", PMEMOBJ_MIN_POOL); // close a pmemobj pool pop.close(); // or open a pmemobj pool pop = pool_base::open("poolfile", ""); // no "root" object available in pool_base persistent_ptr pval; make_persistent_atomic(pop, pval); // low-level memory manipulation pval->some_variable = 3; pop.persist(pval->some_variable); pop.memset_persist(pval->some_array, 2, sizeof(pval->some_array)); pop.memcpy_persist(pval->some_other_array, pval->some_array, sizeof(pval->some_array)); pop.close(); // check pool consistency pool_base::check("poolfile", ""); } //! [pool_base_example] libpmemobj-cpp-1.9/examples/doc_snippets/segment_vector.cpp000066400000000000000000000101731361501571000243240ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * segment_vector.cpp -- C++ documentation snippets. */ //! [segment_vector_example] #include #include #include #include #include const int N_ELEMENTS = 4096; const int SEGMENT_SIZE = 1024; using namespace pmem::obj; using value_t = p; using segment_t = pmem::obj::vector; /* * exponential_size_array_policy<> is equivalent to: * exponential_size_array_policy */ using seg_vec_exp_arr = segment_vector>; /* * exponential_size_vector_policy<> is equivalent to: * exponential_size_vector_policy */ using seg_vec_exp_vec = segment_vector>; /* * fixed_size_vector_policy is equivalent to: * fixed_size_vector_policy */ using seg_vec_fix_vec = segment_vector>; struct root { persistent_ptr pptr0; persistent_ptr pptr1; persistent_ptr pptr2; persistent_ptr pptr3; }; int main(int argc, char *argv[]) { if (argc != 2) std::cerr << "usage: " << argv[0] << " file-name" << std::endl; auto path = argv[1]; auto pop = pool::open(path, "segment_vector example"); auto r = pop.root(); if (r->pptr0 == nullptr) { pmem::obj::transaction::run(pop, [&] { r->pptr0 = make_persistent(); r->pptr1 = make_persistent(); r->pptr2 = make_persistent(); r->pptr3 = make_persistent(); }); } auto &seg_vec_0 = *pop.root()->pptr0; auto &seg_vec_1 = *pop.root()->pptr1; auto &seg_vec_2 = *pop.root()->pptr2; auto &seg_vec_3 = *pop.root()->pptr3; for (int i = 0; i < N_ELEMENTS; ++i) { seg_vec_0[i] = i; } seg_vec_1 = seg_vec_exp_arr(seg_vec_0.cbegin(), seg_vec_0.cend()); seg_vec_2 = seg_vec_exp_vec(seg_vec_1.cbegin(), seg_vec_1.cend()); seg_vec_3 = seg_vec_fix_vec(seg_vec_2.cbegin(), seg_vec_2.cend()); for (int i = 0; i < N_ELEMENTS; ++i) { assert(seg_vec_1[i] = seg_vec_0[i]); assert(seg_vec_2[i] = seg_vec_1[i]); assert(seg_vec_3[i] = seg_vec_2[i]); } seg_vec_0.clear(); seg_vec_1.clear(); seg_vec_2.clear(); seg_vec_3.clear(); delete_persistent(r->pptr0); delete_persistent(r->pptr1); delete_persistent(r->pptr2); delete_persistent(r->pptr3); pop.close(); return 0; } //! [segment_vector_example] libpmemobj-cpp-1.9/examples/doc_snippets/transaction.cpp000066400000000000000000000156661361501571000236410ustar00rootroot00000000000000/* * Copyright 2016-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * transaction.cpp -- C++ documentation snippets. */ /* * The following might be necessary to compile the examples on older compilers. */ #if !defined(__cpp_lib_uncaught_exceptions) && !defined(_MSC_VER) || \ (_MSC_VER < 1900) #define __cpp_lib_uncaught_exceptions 201411 namespace std { int uncaught_exceptions() noexcept { return 0; } } /* namespace std */ #endif /* __cpp_lib_uncaught_exceptions */ //! [general_tx_example] #include #include #include #include #include #include #include using namespace pmem::obj; void general_tx_example() { // pool root structure struct root { mutex pmutex; shared_mutex shared_pmutex; p count; persistent_ptr another_root; }; // create a pmemobj pool auto pop = pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); auto proot = pop.root(); // typical usage schemes try { // take locks and start a transaction transaction::run( pop, [&]() { // atomically allocate objects proot->another_root = make_persistent(); // atomically modify objects proot->count++; }, proot->pmutex, proot->shared_pmutex); } catch (pmem::transaction_error &) { // a transaction error occurred, transaction got aborted // reacquire locks if necessary } catch (...) { // some other exception got propagated from within the tx // reacquire locks if necessary } } //! [general_tx_example] //! [manual_tx_example] #include #include #include #include #include #include #include using namespace pmem::obj; int manual_tx_example() { // pool root structure struct root { mutex pmutex; shared_mutex shared_pmutex; p count; persistent_ptr another_root; }; // create a pmemobj pool auto pop = pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); auto proot = pop.root(); try { transaction::manual tx(pop, proot->pmutex, proot->shared_pmutex); // atomically allocate objects proot->another_root = make_persistent(); // atomically modify objects proot->count++; // It's necessary to commit the transaction manually and // it has to be the last operation in the transaction. transaction::commit(); } catch (pmem::transaction_error &) { // an internal transaction error occurred, tx aborted // reacquire locks if necessary } catch (...) { // some other exception thrown, tx aborted // reacquire locks if necessary } // In complex cases with library calls, remember to check the status of // the previous transaction. return transaction::error(); } //! [manual_tx_example] //! [automatic_tx_example] #include #include #include #include #include #include #include using namespace pmem::obj; int automatic_tx_example() { // pool root structure struct root { mutex pmutex; shared_mutex shared_pmutex; p count; persistent_ptr another_root; }; // create a pmemobj pool auto pop = pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); auto proot = pop.root(); try { transaction::automatic tx(pop, proot->pmutex, proot->shared_pmutex); // atomically allocate objects proot->another_root = make_persistent(); // atomically modify objects proot->count++; // manual transaction commit is no longer necessary } catch (pmem::transaction_error &) { // an internal transaction error occurred, tx aborted // reacquire locks if necessary } catch (...) { // some other exception thrown, tx aborted // reacquire locks if necessary } // In complex cases with library calls, remember to check the status of // the previous transaction. return transaction::error(); } //! [automatic_tx_example] //! [tx_callback_example] #include #include #include #include #include using namespace pmem::obj; void tx_callback_example() { // pool root structure struct root { p count; }; // create a pmemobj pool auto pop = pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); bool cb_called = false; auto internal_tx_function = [&] { // callbacks can be registered even in inner transaction but // will be called when outer transaction ends transaction::run(pop, [&] { transaction::register_callback( transaction::stage::oncommit, [&] { cb_called = true; }); }); // cb_called is false here if internal_tx_function is called // inside another transaction }; try { transaction::run(pop, [&] { internal_tx_function(); }); // cb_called == true if transaction ended successfully } catch (pmem::transaction_error &) { // an internal transaction error occurred, tx aborted // reacquire locks if necessary } catch (...) { // some other exception thrown, tx aborted // reacquire locks if necessary } } //! [tx_callback_example] libpmemobj-cpp-1.9/examples/doc_snippets/v.cpp000066400000000000000000000043551361501571000215520ustar00rootroot00000000000000/* * Copyright 2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * v.cpp -- C++ documentation snippets. */ //! [v_property_example] #include #include #include #include using namespace pmem::obj; using namespace pmem::obj::experimental; void v_property_example() { struct foo { foo() : counter(10) { } int counter; }; // pool root structure struct root { v f; }; // create a pmemobj pool auto pop = pool::create("poolfile", "layout", PMEMOBJ_MIN_POOL); auto proot = pop.root(); assert(proot->f.get().counter == 10); proot->f.get().counter++; assert(proot->f.get().counter == 11); } //! [v_property_example] libpmemobj-cpp-1.9/examples/libpmemobj_cpp_examples_common.hpp000066400000000000000000000050751361501571000250500ustar00rootroot00000000000000/* * Copyright 2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_COMMON_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_COMMON_HPP #include #ifndef _WIN32 #include #define CREATE_MODE_RW (S_IWUSR | S_IRUSR) /* * file_exists -- checks if file exists */ static inline int file_exists(char const *file) { return access(file, F_OK); } /* * find_last_set_64 -- returns last set bit position or -1 if set bit not found */ static inline int find_last_set_64(uint64_t val) { return 64 - __builtin_clzll(val) - 1; } #else #include #include #include #define CREATE_MODE_RW (S_IWRITE | S_IREAD) /* * file_exists -- checks if file exists */ static inline int file_exists(char const *file) { return _access(file, 0); } /* * find_last_set_64 -- returns last set bit position or -1 if set bit not found */ static inline int find_last_set_64(uint64_t val) { DWORD lz = 0; if (BitScanReverse64(&lz, val)) return (int)lz; else return -1; } #endif #endif /* LIBPMEMOBJ_CPP_EXAMPLES_COMMON_HPP */ libpmemobj-cpp-1.9/examples/libpmemobj_cpp_examples_list.hpp000066400000000000000000000103561361501571000245310ustar00rootroot00000000000000/* * Copyright 2016-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * list.hpp -- Implementation of list */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_LIST_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_LIST_HPP #include #include #include #include namespace examples { template class list { class list_entry { public: list_entry() = delete; list_entry(pmem::obj::persistent_ptr previous, pmem::obj::persistent_ptr value) { val = value; next = nullptr; prev = previous; } pmem::obj::persistent_ptr prev; pmem::obj::persistent_ptr next; pmem::obj::persistent_ptr val; }; public: list() { head = nullptr; tail = head; len = 0; } /** * Push back the new element. */ void push_back(pmem::obj::persistent_ptr val) { auto tmp = pmem::obj::make_persistent(tail, val); if (head == nullptr) head = tmp; else tail->next = tmp; tail = tmp; ++len; } /** * Pop the last element out from the list and return * the pointer to it */ pmem::obj::persistent_ptr pop_back() { assert(head != nullptr); auto tmp = tail; tail = tmp->prev; if (tail == nullptr) head = tail; else tail->next = nullptr; return tmp->val; } /** * Return the pointer to the next element */ pmem::obj::persistent_ptr erase(unsigned id) { return remove_elm(get_elm(id)); } /* clear - clear the whole list */ void clear() { while (head != nullptr) { auto e = head; head = remove_elm(e); } } /** * Get element with given id in list */ pmem::obj::persistent_ptr get(unsigned id) { auto elm = get_elm(id); if (elm == nullptr) return nullptr; return elm->val; } /** * Return number of elements in list */ unsigned size() const { return len; } private: pmem::obj::persistent_ptr get_elm(unsigned id) { if (id >= len) return nullptr; auto tmp = head; for (unsigned i = 0; i < id; i++) tmp = tmp->next; return tmp; } pmem::obj::persistent_ptr remove_elm(pmem::obj::persistent_ptr elm) { assert(elm != nullptr); auto tmp = elm->next; pmem::obj::delete_persistent(elm->val); /* removing item is head */ if (elm == head) head = elm->next; else elm->prev->next = elm->next; /* removing item is tail */ if (elm == tail) tail = elm->prev; else elm->next->prev = elm->prev; --len; pmem::obj::delete_persistent(elm); return tmp; } pmem::obj::p len; pmem::obj::persistent_ptr head; pmem::obj::persistent_ptr tail; }; }; #endif /* LIBPMEMOBJ_CPP_EXAMPLES_LIST_HPP */ libpmemobj-cpp-1.9/examples/map_cli/000077500000000000000000000000001361501571000175045ustar00rootroot00000000000000libpmemobj-cpp-1.9/examples/map_cli/CMakeLists.txt000066400000000000000000000041251361501571000222460ustar00rootroot00000000000000# # Copyright 2018-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cmake_minimum_required(VERSION 3.3) project(map_cli CXX) set(CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 11) include(FindThreads) if(NOT WIN32) find_package(PkgConfig QUIET) endif() if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMOBJ++ REQUIRED libpmemobj++) else() find_package(LIBPMEMOBJ++ REQUIRED) endif() link_directories(${LIBPMEMOBJ++_LIBRARY_DIRS}) add_executable(map_cli map_cli.cpp) target_include_directories(map_cli PUBLIC ${LIBPMEMOBJ++_INCLUDE_DIRS} . ..) target_link_libraries(map_cli ${LIBPMEMOBJ++_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) libpmemobj-cpp-1.9/examples/map_cli/ctree_map_persistent.hpp000066400000000000000000000236071361501571000244440ustar00rootroot00000000000000/* * Copyright 2016-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_CTREE_MAP_PERSISTENT_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_CTREE_MAP_PERSISTENT_HPP #include #include #include #include #include #include #include #include #include #include #include #define BIT_IS_SET(n, i) (!!((n) & (1ULL << (i)))) namespace nvobj = pmem::obj; namespace examples { /** * C++ implementation of a persistent ctree. * * Based on the volatile version. This version was implemented to show how much * effort is needed to convert a volatile structure into a persistent one using * C++ obj bindings. All API functions are atomic in respect to persistency. */ template class ctree_map_p { public: /** Convenience typedef for the key type. */ typedef K key_type; /** Convenience typedef for the value type. */ typedef nvobj::persistent_ptr value_type; /** Convenience typedef for the callback function. */ typedef std::function callback; /** * Default constructor. */ ctree_map_p() { auto pop = nvobj::pool_by_vptr(this); nvobj::transaction::run(pop, [&] { this->root = nvobj::make_persistent(); }); } ctree_map_p(const ctree_map_p &other) = delete; ctree_map_p &operator=(const ctree_map_p &other) = delete; /** * Insert or update the given value under the given key. * * The map takes ownership of the value. * * @param key The key to insert under. * @param value The value to be inserted. * * @return 0 on success, negative values on error. */ int insert(key_type key, value_type value) { auto dest_entry = root; while (dest_entry->inode != nullptr) { auto n = dest_entry->inode; dest_entry = n->entries[BIT_IS_SET(key, n->diff)]; } entry e(key, value); auto pop = nvobj::pool_by_vptr(this); nvobj::transaction::run(pop, [&] { if (dest_entry->key == 0 || dest_entry->key == key) { nvobj::delete_persistent(dest_entry->value); *dest_entry = e; } else { insert_leaf( &e, find_crit_bit(dest_entry->key, key)); } }); return 0; } /** * Allocating insert. * * Creates a new value_type instance and inserts it into the tree. * * @param key The key to insert under. * @param args variadic template parameter for object construction * arguments. * * @return 0 on success, negative values on error. */ template int insert_new(key_type key, const Args &... args) { auto pop = nvobj::pool_by_vptr(this); nvobj::transaction::run(pop, [&] { return insert(key, nvobj::make_persistent(args...)); }); return -1; } /** * Remove a value from the tree. * * The tree no longer owns the value. * * @param key The key for which the value will be removed. * * @return The value if it is in the tree, nullptr otherwise. */ value_type remove(key_type key) { nvobj::persistent_ptr parent = nullptr; auto leaf = get_leaf(key, &parent); if (leaf == nullptr) return nullptr; auto ret = leaf->value; auto pop = nvobj::pool_by_vptr(this); nvobj::transaction::run(pop, [&] { if (parent == nullptr) { leaf->key = 0; leaf->value = nullptr; } else { auto n = parent->inode; *parent = *( n->entries[parent->inode->entries[0] ->key == leaf->key]); /* cleanup entries and the unnecessary node */ nvobj::delete_persistent(n->entries[0]); nvobj::delete_persistent(n->entries[1]); nvobj::delete_persistent(n); } }); return ret; } /** * Remove entry from tree and deallocate it. * * @param key The key denoting the entry to be removed. * * @return 0 on success, negative values on error. */ int remove_free(key_type key) { auto pop = nvobj::pool_by_vptr(this); nvobj::transaction::run( pop, [&] { nvobj::delete_persistent(remove(key)); }); return 0; } /** * Clear the tree and deallocate all entries. */ int clear() { auto pop = nvobj::pool_by_vptr(this); nvobj::transaction::run(pop, [&] { if (this->root->inode) { this->root->inode->clear(); nvobj::delete_persistent( this->root->inode); this->root->inode = nullptr; } nvobj::delete_persistent(this->root->value); this->root->value = nullptr; this->root->key = 0; }); return 0; } /** * Return the value from the tree for the given key. * * @param key The key for which the value will be returned. * * @return The value if it is in the tree, nullptr otherwise. */ value_type get(key_type key) { auto ret = get_leaf(key, nullptr); return ret ? ret->value : nullptr; } /** * Check if an entry for the given key is in the tree. * * @param key The key to check. * * @return 0 on */ int lookup(key_type key) const { return get(key) != nullptr; } /** * Call clb for each element in the tree. * * @param clb The callback to be called. * @param args The arguments forwarded to the callback. * * @return 0 if tree empty, clb return value otherwise. */ int foreach (callback clb, void *args) { if (is_empty()) return 0; return foreach_node(root, clb, args); } /** * Check if tree is empty. * * @return 1 if empty, 0 otherwise. */ int is_empty() const { return root->value == nullptr && root->inode == nullptr; } /** * Check tree consistency. * * @return 0 on success, negative values on error. */ int check() const { return 0; } /** * Destructor. */ ~ctree_map_p() { clear(); } private: struct node; /* * Entry holding the value. */ struct entry { entry() : key(0), inode(nullptr), value(nullptr) { } entry(key_type _key, value_type _value) : key(_key), inode(nullptr), value(_value) { } nvobj::p key; nvobj::persistent_ptr inode; value_type value; void clear() { if (inode) { inode->clear(); nvobj::delete_persistent(inode); inode = nullptr; } nvobj::delete_persistent(value); value = nullptr; } }; /* * Internal node pointing to two entries. */ struct node { node() : diff(0) { entries[0] = nullptr; entries[1] = nullptr; } nvobj::p diff; /* most significant differing bit */ nvobj::persistent_ptr entries[2]; void clear() { if (entries[0]) { entries[0]->clear(); nvobj::delete_persistent(entries[0]); entries[0] = nullptr; } if (entries[1]) { entries[1]->clear(); nvobj::delete_persistent(entries[1]); entries[1] = nullptr; } } }; /* * Find critical bit. */ static int find_crit_bit(key_type lhs, key_type rhs) { return find_last_set_64(lhs ^ rhs); } /* * Insert leaf into the tree. */ void insert_leaf(const entry *e, int diff) { auto new_node = nvobj::make_persistent(); new_node->diff = diff; int d = BIT_IS_SET(e->key, new_node->diff); new_node->entries[d] = nvobj::make_persistent(*e); auto dest_entry = root; while (dest_entry->inode != nullptr) { auto n = dest_entry->inode; if (n->diff < new_node->diff) break; dest_entry = n->entries[BIT_IS_SET(e->key, n->diff)]; } new_node->entries[!d] = nvobj::make_persistent(*dest_entry); dest_entry->key = 0; dest_entry->inode = new_node; dest_entry->value = nullptr; } /* * Fetch leaf from the tree. */ nvobj::persistent_ptr get_leaf(key_type key, nvobj::persistent_ptr *parent) { auto n = root; nvobj::persistent_ptr p = nullptr; while (n->inode != nullptr) { p = n; n = n->inode->entries[BIT_IS_SET(key, n->inode->diff)]; } if (n->key == key) { if (parent) *parent = p; return n; } return nullptr; } /* * Recursive foreach on nodes. */ int foreach_node(const nvobj::persistent_ptr e, callback clb, void *arg) { int ret = 0; if (e->inode != nullptr) { auto n = e->inode; if (foreach_node(n->entries[0], clb, arg) == 0) foreach_node(n->entries[1], clb, arg); } else { ret = clb(e->key, e->value, arg); } return ret; } /* Tree root */ nvobj::persistent_ptr root; }; } /* namespace examples */ #endif /* LIBPMEMOBJ_CPP_EXAMPLES_CTREE_MAP_PERSISTENT_HPP */ libpmemobj-cpp-1.9/examples/map_cli/ctree_map_transient.hpp000066400000000000000000000205171361501571000242500ustar00rootroot00000000000000/* * Copyright 2016-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_CTREE_MAP_VOLATILE_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_CTREE_MAP_VOLATILE_HPP #include #include #include #include #ifdef _WIN32 #include #endif #define BIT_IS_SET(n, i) (!!((n) & (1ULL << (i)))) namespace examples { /** * C++ implementation of a volatile ctree. * * Based on the C implementation. */ template class ctree_map_transient { public: /** Convenience typedef for the key type. */ typedef K key_type; /** Convenience typedef for the value type. */ typedef T *value_type; /** Convenience typedef for the callback function. */ typedef std::function callback; /** * Default constructor. */ ctree_map_transient() : root(new entry()) { } ctree_map_transient(const ctree_map_transient &other) = delete; ctree_map_transient & operator=(const ctree_map_transient &other) = delete; /** * Insert or update the given value under the given key. * * The map takes ownership of the value. * * @param key The key to insert under. * @param value The value to be inserted. * * @return 0 on success, negative values on error. */ int insert(uint64_t key, value_type value) { auto dest_entry = root; while (dest_entry->inode != nullptr) { auto n = dest_entry->inode; dest_entry = n->entries[BIT_IS_SET(key, n->diff)]; } entry e(key, value); if (dest_entry->key == 0 || dest_entry->key == key) { delete dest_entry->value; *dest_entry = e; } else { insert_leaf(&e, ctree_map_transient::find_crit_bit( dest_entry->key, key)); } return 0; } /** * Allocating insert. * * Creates a new value_type instance and inserts it into the tree. * * @param key The key to insert under. * @param args variadic template parameter for object construction * arguments. * * @return 0 on success, negative values on error. */ template int insert_new(key_type key, const Args &... args) { return insert(key, new T(args...)); } /** * Remove a value from the tree. * * The tree no longer owns the value. * * @param key The key for which the value will be removed. * * @return The value if it is in the tree, nullptr otherwise. */ value_type remove(key_type key) { entry *parent = nullptr; auto leaf = get_leaf(key, &parent); if (leaf == nullptr) return nullptr; auto ret = leaf->value; if (parent == nullptr) { leaf->key = 0; leaf->value = nullptr; } else { auto n = parent->inode; *parent = *(n->entries[parent->inode->entries[0]->key == leaf->key]); /* cleanup both entries and the unnecessary node */ delete n->entries[0]; delete n->entries[1]; delete n; } return ret; } /** * Remove entry from tree and deallocate it. * * @param key The key denoting the entry to be removed. * * @return 0 on success, negative values on error. */ int remove_free(key_type key) { delete remove(key); return 0; } /** * Clear the tree and deallocate all entries. */ int clear() { if (root->inode) { root->inode->clear(); delete root->inode; root->inode = nullptr; } delete root->value; root->value = nullptr; root->key = 0; return 0; } /** * Return the value from the tree for the given key. * * @param key The key for which the value will be returned. * * @return The value if it is in the tree, nullptr otherwise. */ value_type get(key_type key) { auto ret = get_leaf(key, nullptr); return ret ? ret->value : nullptr; } /** * Check if an entry for the given key is in the tree. * * @param key The key to check. * * @return 0 on */ int lookup(key_type key) const { return get(key) != nullptr; } /** * Call clb for each element in the tree. * * @param clb The callback to be called. * @param args The arguments forwarded to the callback. * * @return 0 if tree empty, clb return value otherwise. */ int foreach (callback clb, void *args) { if (is_empty()) return 0; return foreach_node(root, clb, args); } /** * Check if tree is empty. * * @return 1 if empty, 0 otherwise. */ int is_empty() const { return root->value == nullptr && root->inode == nullptr; } /** * Check tree consistency. * * @return 0 on success, negative values on error. */ int check() const { return 0; } /** * Destructor. */ ~ctree_map_transient() { clear(); delete root; } private: struct node; /* * Entry holding the value. */ struct entry { entry() : key(0), inode(nullptr), value(nullptr) { } entry(key_type _key, value_type _value) : key(_key), inode(nullptr), value(_value) { } key_type key; node *inode; value_type value; /* * Clear the entry. */ void clear() { if (inode) { inode->clear(); delete inode; } delete value; } }; /* * Internal node pointing to two entries. */ struct node { node() : diff(0) { entries[0] = nullptr; entries[1] = nullptr; } int diff; /* most significant differing bit */ entry *entries[2]; /* * Clear the node. */ void clear() { if (entries[0]) { entries[0]->clear(); delete entries[0]; } if (entries[1]) { entries[1]->clear(); delete entries[1]; } } }; /* * Find critical bit. */ static int find_crit_bit(key_type lhs, key_type rhs) { return find_last_set_64(lhs ^ rhs); } /* * Insert leaf into the tree. */ void insert_leaf(const entry *e, int diff) { auto new_node = new node(); new_node->diff = diff; int d = BIT_IS_SET(e->key, new_node->diff); new_node->entries[d] = new entry(*e); auto dest_entry = root; while (dest_entry->inode != nullptr) { auto n = dest_entry->inode; if (n->diff < new_node->diff) break; dest_entry = n->entries[BIT_IS_SET(e->key, n->diff)]; } new_node->entries[!d] = new entry(*dest_entry); dest_entry->key = 0; dest_entry->inode = new_node; dest_entry->value = nullptr; } /* * Fetch leaf from the tree. */ entry * get_leaf(uint64_t key, entry **parent) { auto n = root; entry *p = nullptr; while (n->inode != nullptr) { p = n; n = n->inode->entries[BIT_IS_SET(key, n->inode->diff)]; } if (n->key == key) { if (parent) *parent = p; return n; } return nullptr; } /* * Recursive foreach on nodes. */ int foreach_node(const entry *e, callback clb, void *arg) { int ret = 0; if (e->inode != nullptr) { auto n = e->inode; if (foreach_node(n->entries[0], clb, arg) == 0) foreach_node(n->entries[1], clb, arg); } else { ret = clb(e->key, e->value, arg); } return ret; } /* Tree root */ entry *root; }; } /* namespace examples */ #endif /* LIBPMEMOBJ_CPP_EXAMPLES_CTREE_MAP_VOLATILE_HPP */ libpmemobj-cpp-1.9/examples/map_cli/map_cli.cpp000066400000000000000000000146721361501571000216260ustar00rootroot00000000000000/* * Copyright 2016-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ctree_map_persistent.hpp" #include "ctree_map_transient.hpp" #include #include #include #include #include namespace { using pmem::obj::delete_persistent; using pmem::obj::make_persistent; using pmem::obj::persistent_ptr; using pmem::obj::pool; using pmem::obj::pool_base; using pmem::obj::transaction; /* convenience typedefs */ typedef long long value_t; typedef uint64_t key_type; typedef examples::ctree_map_p pmap; typedef examples::ctree_map_transient vmap; const std::string LAYOUT = ""; /* available map operations */ enum queue_op { UNKNOWN_QUEUE_OP, MAP_INSERT, MAP_INSERT_NEW, MAP_GET, MAP_REMOVE, MAP_REMOVE_FREE, MAP_CLEAR, MAP_PRINT, MAX_QUEUE_OP }; /* queue operations strings */ const char *ops_str[MAX_QUEUE_OP] = {"", "insert", "insert_new", "get", "remove", "remove_free", "clear", "print"}; /* * parse_queue_op -- parses the operation string and returns matching queue_op */ queue_op parse_queue_op(const char *str) { for (int i = 0; i < MAX_QUEUE_OP; ++i) if (strcmp(str, ops_str[i]) == 0) return (queue_op)i; return UNKNOWN_QUEUE_OP; } struct root { persistent_ptr ptree; }; /* * printer -- (internal) print the value for the given key */ template int printer(key_type key, T value, void *) { std::cout << "map[" << key << "] = " << *value << std::endl; return 0; } /* * insert -- (internal) insert value into the map */ template void insert(pool_base pop, T &map, char *argv[], int &argn) { map->insert(atoll(argv[argn]), new value_t(atoll(argv[argn + 1]))); argn += 2; } /* * remove -- (internal) remove value from map */ template void remove(pool_base pop, T &map, char *argv[], int &argn) { auto val = map->remove(atoll(argv[argn++])); if (val) { std::cout << *val << std::endl; delete val; } else { std::cout << "Entry not found\n"; } } /* * remove -- (internal) remove specialization for persistent ctree */ template <> void remove>(pool_base pop, persistent_ptr &map, char *argv[], int &argn) { auto val = map->remove(atoll(argv[argn++])); if (val) { std::cout << *val << std::endl; transaction::run(pop, [&] { delete_persistent(val); }); } else { std::cout << "Entry not found\n"; } } /* * insert -- (internal) insert specialization for persistent ctree */ template <> void insert>(pool_base pop, persistent_ptr &map, char *argv[], int &argn) { transaction::run(pop, [&] { map->insert(atoll(argv[argn]), make_persistent(atoll(argv[argn + 1]))); }); argn += 2; } /* * exec_op -- (internal) execute single operation */ template void exec_op(pool_base pop, T &map, queue_op op, char *argv[], int &argn) { switch (op) { case MAP_INSERT_NEW: map->insert_new(atoll(argv[argn]), atoll(argv[argn + 1])); argn += 2; break; case MAP_INSERT: insert(pop, map, argv, argn); break; case MAP_GET: { auto val = map->get(atoll(argv[argn++])); if (val) std::cout << *val << std::endl; else std::cout << "key not found\n"; break; } case MAP_REMOVE: remove(pop, map, argv, argn); break; case MAP_REMOVE_FREE: map->remove_free(atoll(argv[argn++])); break; case MAP_CLEAR: map->clear(); break; case MAP_PRINT: map->foreach (printer, nullptr); break; default: throw std::invalid_argument("invalid queue operation"); } } } int main(int argc, char *argv[]) { if (argc < 4) { std::cerr << "usage: " << argv[0] << " file-name [insert |insert_new |get |remove | remove_free ]" << std::endl; return 1; } std::string path = argv[1]; std::string type = argv[2]; pool pop; try { if (file_exists(path.c_str()) != 0) { pop = pool::create(path, LAYOUT, PMEMOBJ_MIN_POOL, CREATE_MODE_RW); } else { pop = pool::open(path, LAYOUT); } } catch (pmem::pool_error &e) { std::cerr << e.what() << std::endl; return 1; } persistent_ptr q; try { q = pop.root(); } catch (std::exception &e) { std::cerr << e.what() << std::endl; pop.close(); return 1; } if (!q->ptree) { try { transaction::run(pop, [&] { q->ptree = make_persistent(); }); } catch (pmem::transaction_error &e) { std::cerr << e.what() << std::endl; pop.close(); return 1; } } auto vtree = std::make_shared(); for (int i = 3; i < argc;) { queue_op op = parse_queue_op(argv[i++]); try { if (type == "volatile") exec_op(pop, vtree, op, argv, i); else exec_op(pop, q->ptree, op, argv, i); } catch (std::exception &e) { std::cerr << e.what() << std::endl; break; } } pop.close(); return 0; } libpmemobj-cpp-1.9/examples/panaconda/000077500000000000000000000000001361501571000200245ustar00rootroot00000000000000libpmemobj-cpp-1.9/examples/panaconda/CMakeLists.txt000066400000000000000000000045151361501571000225710ustar00rootroot00000000000000# # Copyright 2018-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cmake_minimum_required(VERSION 3.3) project(panaconda CXX) set(CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 11) include(FindThreads) if(NOT WIN32) find_package(PkgConfig QUIET) endif() if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMOBJ++ REQUIRED libpmemobj++) pkg_check_modules(CURSES REQUIRED ncurses) else() find_package(LIBPMEMOBJ++ REQUIRED) # Specifies that we want FindCurses to find ncurses and not just any # curses library set(CURSES_NEED_NCURSES TRUE) find_package(Curses REQUIRED) endif() link_directories(${LIBPMEMOBJ++_LIBRARY_DIRS}) add_executable(panaconda panaconda.cpp) target_include_directories(panaconda PUBLIC ${CURSES_INCLUDE_DIR} ${LIBPMEMOBJ++_INCLUDE_DIRS} . ..) target_link_libraries(panaconda ${LIBPMEMOBJ++_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${CURSES_LIBRARIES}) libpmemobj-cpp-1.9/examples/panaconda/README000066400000000000000000000020341361501571000207030ustar00rootroot00000000000000This directory contains an example application implemented using libpmemobj, it's a game in which all the objects are stored on persistent memory. This means that the game process can be safely killed and then resumed. To launch the game: ./panaconda /path/game/session/file or ./panaconda /path/game/session/file -m /path/config/file/conf/cfg The second option allow you to define your own maze. Meaning of symbols in config file is given below: '1' - wall '0' - space conf.cfg contains example of predefined maze. The file with the game session will either be created if it doesn't exist or opened if it contains a valid pool. Controls: move - arrow keys quit - 'q' new game - 'n' This game demonstrates the usage of the very basics of the libpmemobj C++ bindings. It demonstrates pool management, persistent pointers and transactions. ** DEPENDENCIES: ** In order to build the game you need to install ncurses development package. rpm-based systems : ncurses-devel dpkg-based systems: libncursesX-dev (where X is the API/ABI version) libpmemobj-cpp-1.9/examples/panaconda/conf.cfg000066400000000000000000000017351361501571000214400ustar00rootroot0000000000000011111111111111111111111111111111 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000111000000000111000000001 10000000111000010000111000000001 10000000000000111000000000000001 10000000000001111100000000000001 10000000000000000000000000000001 10000011111111111111111111000001 10000000011111111111111000000001 10000000000000111100000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 10000000000000000000000000000001 11111111111111111111111111111111libpmemobj-cpp-1.9/examples/panaconda/panaconda.cpp000066400000000000000000000503461361501571000224640ustar00rootroot00000000000000/* * Copyright 2016-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * panaconda.cpp -- example usage of libpmemobj C++ bindings */ #ifdef __FreeBSD__ #define _WITH_GETLINE #endif #include #include #include #include #include #ifdef __FreeBSD__ #include /* Need pkg, not system, version */ #else #include #endif #include "panaconda.hpp" #define LAYOUT_NAME "panaconda" #define DEFAULT_DELAY 120000 #define SNAKE_START_POS_X 5 #define SNAKE_START_POS_Y 5 #define SNAKE_START_DIR (direction::RIGHT) #define SNAKE_STAR_SEG_NO 5 #define BOARD_STATIC_SIZE_ROW 40 #define BOARD_STATIC_SIZE_COL 30 #define PLAYER_POINTS_PER_HIT 10 using examples::list; using pmem::pool_error; using pmem::transaction_error; using pmem::transaction_scope_error; using pmem::obj::delete_persistent; using pmem::obj::make_persistent; using pmem::obj::persistent_ptr; using pmem::obj::pool; using pmem::obj::transaction; /* * Color_pair */ color_pair::color_pair() : color_bg(COLOR_BLACK), color_fg(COLOR_BLACK) { } color_pair::color_pair(const int col_fg, const int col_bg) : color_bg(col_bg), color_fg(col_fg) { } /* * Helper */ struct color_pair helper::get_color(const object_type obj_type) { struct color_pair res; switch (obj_type) { case SNAKE_SEGMENT: res = color_pair(COLOR_WHITE, COLOR_BLACK); break; case WALL: res = color_pair(COLOR_BLUE, COLOR_BLUE); break; case FOOD: res = color_pair(COLOR_RED, COLOR_BLACK); break; default: std::cout << "Error: get_color - wrong value passed!" << std::endl; assert(0); } return res; } int helper::parse_params(int argc, char *argv[], struct parameters *par) { int opt; std::string app = argv[0]; while ((opt = getopt(argc, argv, "m:")) != -1) { switch (opt) { case 'm': par->use_maze = true; par->maze_path = optarg; break; default: helper::print_usage(app); return -1; } } if (optind < argc) { par->name = argv[optind]; } else { helper::print_usage(app); return -1; } return 0; } inline void helper::sleep(int time) { clock_t curr_time = clock(); while (clock() < (curr_time + time)) { } } inline void helper::print_usage(std::string &name) { std::cout << "Usage: " << name << " [-m ] \n"; } /* * Point */ point::point() : x(0), y(0) { } point::point(int x, int y) : x(x), y(y) { } bool operator==(point &point1, point &point2) { return point1.x == point2.x && point1.y == point2.y; } /* * Shape */ element_shape::element_shape(int shape) { int n_curves_symbol = get_symbol(shape); val = COLOR_PAIR(shape) | n_curves_symbol; } int element_shape::get_val() { return val; } int element_shape::get_symbol(int shape) { int symbol = 0; switch (shape) { case SNAKE_SEGMENT: symbol = ACS_DIAMOND; break; case WALL: symbol = ACS_BLOCK; break; case FOOD: symbol = ACS_CKBOARD; break; default: symbol = ACS_DIAMOND; break; } return symbol; } /* * Element */ board_element::board_element() : position(make_persistent(0, 0)), shape(make_persistent(SNAKE_SEGMENT)), element_dir(direction::LEFT) { } board_element::board_element(int px, int py, pmem::obj::persistent_ptr shape, direction dir) : position(make_persistent(px, py)), shape(shape), element_dir(dir) { } board_element::board_element(point p, pmem::obj::persistent_ptr shape, direction dir) : position(make_persistent(p.x, p.y)), shape(shape), element_dir(dir) { } board_element::board_element(const board_element &element) : position( make_persistent(element.position->x, element.position->y)), shape(element.shape), element_dir(UNDEFINED) { } board_element::~board_element() { pmem::obj::delete_persistent(position); position = nullptr; pmem::obj::delete_persistent(shape); shape = nullptr; } persistent_ptr board_element::calc_new_position(const direction dir) { persistent_ptr pt = make_persistent(position->x, position->y); switch (dir) { case direction::DOWN: pt->y = pt->y + 1; break; case direction::LEFT: pt->x = pt->x - 1; break; case direction::RIGHT: pt->x = pt->x + 1; break; case direction::UP: pt->y = pt->y - 1; break; default: break; } return pt; } void board_element::set_position(const persistent_ptr new_point) { position = new_point; } persistent_ptr board_element::get_position(void) { return position; } void board_element::print(void) { mvaddch(position->y, position->x, shape->get_val()); } void board_element::print_double_col(void) { mvaddch(position->y, (2 * position->x), shape->get_val()); } void board_element::print_single_double_col(void) { mvaddch(position->y, (2 * position->x), shape->get_val()); mvaddch(position->y, (2 * position->x - 1), shape->get_val()); } direction board_element::get_direction(void) { return element_dir; } void board_element::set_direction(const direction dir) { element_dir = dir; } /* * Snake */ snake::snake() { snake_segments = make_persistent>(); for (unsigned i = 0; i < SNAKE_STAR_SEG_NO; ++i) { persistent_ptr shape = make_persistent(SNAKE_SEGMENT); persistent_ptr element = make_persistent(SNAKE_START_POS_X - i, SNAKE_START_POS_Y, shape, SNAKE_START_DIR); snake_segments->push_back(element); } last_seg_position = point(); last_seg_dir = direction::RIGHT; } snake::~snake() { snake_segments->clear(); delete_persistent>(snake_segments); } void snake::move(const direction dir) { int snake_size = snake_segments->size(); persistent_ptr new_position_point; last_seg_position = *(snake_segments->get(snake_size - 1)->get_position().get()); last_seg_dir = snake_segments->get(snake_size - 1)->get_direction(); for (int i = (snake_size - 1); i >= 0; --i) { if (i == 0) { new_position_point = snake_segments->get(i)->calc_new_position(dir); snake_segments->get(i)->set_direction(dir); } else { new_position_point = snake_segments->get(i)->calc_new_position( snake_segments->get(i - 1) ->get_direction()); snake_segments->get(i)->set_direction( snake_segments->get(i - 1)->get_direction()); } snake_segments->get(i)->set_position(new_position_point); } } void snake::print(void) { int i = 0; persistent_ptr segp; while ((segp = snake_segments->get(i++)) != nullptr) segp->print_double_col(); } void snake::add_segment(void) { persistent_ptr shape = make_persistent(SNAKE_SEGMENT); persistent_ptr segp = make_persistent( last_seg_position, shape, last_seg_dir); snake_segments->push_back(segp); } bool snake::check_point_against_segments(point point) { int i = 0; bool result = false; persistent_ptr segp; while ((segp = snake_segments->get(i++)) != nullptr) { if (point == *(segp->get_position().get())) { result = true; break; } } return result; } point snake::get_head_point(void) { return *(snake_segments->get(0)->get_position().get()); } direction snake::get_direction(void) { return snake_segments->get(0)->get_direction(); } point snake::get_next_point(const direction dir) { return *(snake_segments->get(0)->calc_new_position(dir).get()); } /* * Board */ game_board::game_board() { persistent_ptr shape = make_persistent(FOOD); food = make_persistent(0, 0, shape, direction::UNDEFINED); layout = make_persistent>(); anaconda = make_persistent(); size_row = 20; size_col = 20; } game_board::~game_board() { layout->clear(); delete_persistent>(layout); delete_persistent(anaconda); delete_persistent(food); } void game_board::print(const int score) { const int offset_y = 2 * size_col + 5; const int offset_x = 2; int i = 0; persistent_ptr elmp; while ((elmp = layout->get(i++)) != nullptr) elmp->print_single_double_col(); anaconda->print(); food->print_double_col(); mvprintw((offset_x + 0), offset_y, " ##### panaconda ##### "); mvprintw((offset_x + 1), offset_y, " # # "); mvprintw((offset_x + 2), offset_y, " # q - quit # "); mvprintw((offset_x + 3), offset_y, " # n - new game # "); mvprintw((offset_x + 4), offset_y, " # # "); mvprintw((offset_x + 5), offset_y, " ##################### "); mvprintw((offset_x + 7), offset_y, " Score: %d ", score); } void game_board::print_game_over(const int score) { int x = size_col / 3; int y = size_row / 6; mvprintw(y + 0, x, "####### ####### # # #######"); mvprintw(y + 1, x, "# # # ## ## # "); mvprintw(y + 2, x, "# ### ####### # # # # #### "); mvprintw(y + 3, x, "# # # # # # # # "); mvprintw(y + 4, x, "####### # # # # #######"); mvprintw(y + 6, x, "####### # # ####### #######"); mvprintw(y + 7, x, "# # # # # # #"); mvprintw(y + 8, x, "# # # # #### #######"); mvprintw(y + 9, x, "# # # # # # # "); mvprintw(y + 10, x, "####### # ####### # #"); mvprintw(y + 12, x, " Last score: %d ", score); mvprintw(y + 14, x, " q - quit"); mvprintw(y + 15, x, " n - new game"); } int game_board::creat_dynamic_layout(const unsigned row_no, char *const buffer) { persistent_ptr element; persistent_ptr shape; for (unsigned i = 0; i < size_col; ++i) { if (buffer[i] == config_file_symbol::SYM_WALL) { shape = make_persistent(WALL); element = make_persistent( i, row_no, shape, direction::UNDEFINED); layout->push_back(element); } } return 0; } int game_board::creat_static_layout(void) { persistent_ptr element; persistent_ptr shape; size_row = BOARD_STATIC_SIZE_ROW; size_col = BOARD_STATIC_SIZE_COL; // first and last row for (unsigned i = 0; i < size_col; ++i) { shape = make_persistent(WALL); element = make_persistent(i, 0, shape, direction::UNDEFINED); layout->push_back(element); shape = make_persistent(WALL); element = make_persistent( i, (size_row - 1), shape, direction::UNDEFINED); layout->push_back(element); } // middle rows for (unsigned i = 1; i < size_row; ++i) { shape = make_persistent(WALL); element = make_persistent(0, i, shape, direction::UNDEFINED); layout->push_back(element); shape = make_persistent(WALL); element = make_persistent( (size_col - 1), i, shape, direction::UNDEFINED); layout->push_back(element); } return 0; } bool game_board::is_snake_collision(point point) { return anaconda->check_point_against_segments(point); } bool game_board::is_wall_collision(point point) { int i = 0; bool result = false; persistent_ptr wallp; while ((wallp = layout->get(i++)) != nullptr) { if (point == *(wallp->get_position().get())) { result = true; break; } } return result; } bool game_board::is_collision(point point) { return is_snake_collision(point) || is_wall_collision(point); } bool game_board::is_snake_head_food_hit(void) { bool result = false; point head_point = anaconda->get_head_point(); if (head_point == *(food->get_position().get())) { result = true; } return result; } void game_board::set_new_food(const point point) { persistent_ptr shape = make_persistent(FOOD); delete_persistent(food); food = make_persistent(point, shape, direction::UNDEFINED); } void game_board::create_new_food(void) { const int max_repeat = 50; int count = 0; int rand_row = 0; int rand_col = 0; while (count < max_repeat) { rand_row = 1 + rand() % (get_size_row() - 2); rand_col = 1 + rand() % (get_size_col() - 2); point food_point(rand_col, rand_row); if (!is_collision(food_point)) { set_new_food(food_point); break; } count++; } } snake_event game_board::move_snake(const direction dir) { snake_event event = snake_event::EV_OK; point next_pt = anaconda->get_next_point(dir); if (is_collision(next_pt)) { event = snake_event::EV_COLLISION; } else { anaconda->move(dir); } return event; } void game_board::add_snake_segment(void) { anaconda->add_segment(); } unsigned game_board::get_size_row(void) { return size_row; } void game_board::set_size_row(const unsigned size_r) { size_row = size_r; } unsigned game_board::get_size_col(void) { return size_col; } void game_board::set_size_col(const unsigned size_c) { size_col = size_c; } direction game_board::get_snake_dir(void) { return anaconda->get_direction(); } /* * Game_state */ game_state::game_state() { } game_state::~game_state() { } pmem::obj::persistent_ptr game_state::get_board() { return board; } pmem::obj::persistent_ptr game_state::get_player() { return player; } void game_state::init(void) { board = make_persistent(); player = make_persistent(); } void game_state::clean_pool(void) { delete_persistent(board); board = nullptr; delete_persistent(player); player = nullptr; } /* * Player */ game_player::game_player() : score(0), state(STATE_PLAY) { } game_player::~game_player() { } int game_player::get_score(void) { return score; } play_state game_player::get_state(void) { return state; } void game_player::set_state(const play_state st) { state = st; } void game_player::update_score(void) { score = score + PLAYER_POINTS_PER_HIT; } /* * Game */ game::game(struct parameters *par) { pool pop; initscr(); start_color(); nodelay(stdscr, true); curs_set(0); keypad(stdscr, true); params = par; if (pool::check(params->name, LAYOUT_NAME) == 1) pop = pool::open(params->name, LAYOUT_NAME); else pop = pool::create(params->name, LAYOUT_NAME, PMEMOBJ_MIN_POOL * 10, 0666); state = pop; direction_key = direction::UNDEFINED; last_key = KEY_CLEAR; delay = DEFAULT_DELAY; init_colors(); srand(time(nullptr)); } game::~game() { } void game::init_colors(void) { struct color_pair color_pair = helper::get_color(SNAKE_SEGMENT); init_pair(SNAKE_SEGMENT, color_pair.color_fg, color_pair.color_bg); color_pair = helper::get_color(WALL); init_pair(WALL, color_pair.color_fg, color_pair.color_bg); color_pair = helper::get_color(FOOD); init_pair(FOOD, color_pair.color_fg, color_pair.color_bg); } void game::init(void) { int ret = 0; persistent_ptr r = state.root(); if (r->get_board() == nullptr) { transaction::run(state, [&r, &ret, this]() { r->init(); if (params->use_maze) ret = parse_conf_create_dynamic_layout(); else ret = r->get_board()->creat_static_layout(); r->get_board()->create_new_food(); }); if (ret) { clean_pool(); clear_prog(); throw std::runtime_error("Error: Config file error!"); } } direction_key = r->get_board()->get_snake_dir(); } void game::process_step(void) { snake_event ret_event = EV_OK; persistent_ptr r = state.root(); transaction::run(state, [&]() { ret_event = r->get_board()->move_snake(direction_key); if (EV_COLLISION == ret_event) { r->get_player()->set_state(play_state::STATE_GAMEOVER); return; } else { if (r->get_board()->is_snake_head_food_hit()) { r->get_board()->create_new_food(); r->get_board()->add_snake_segment(); r->get_player()->update_score(); } } }); r->get_board()->print(r->get_player()->get_score()); } inline bool game::is_stopped(void) { return action::ACTION_QUIT == last_key; } void game::set_direction_key(void) { switch (last_key) { case KEY_LEFT: if (direction::RIGHT != direction_key) direction_key = direction::LEFT; break; case KEY_RIGHT: if (direction::LEFT != direction_key) direction_key = direction::RIGHT; break; case KEY_UP: if (direction::DOWN != direction_key) direction_key = direction::UP; break; case KEY_DOWN: if (direction::UP != direction_key) direction_key = direction::DOWN; break; default: break; } } void game::process_key(const int lastkey) { last_key = lastkey; set_direction_key(); if (action::ACTION_NEW_GAME == last_key) { clean_pool(); init(); } } void game::clean_pool(void) { persistent_ptr r = state.root(); transaction::run(state, [&]() { r->clean_pool(); }); } void game::process_delay(void) { helper::sleep(delay); } void game::clear_screen(void) { erase(); } void game::game_over(void) { persistent_ptr r = state.root(); r->get_board()->print_game_over(r->get_player()->get_score()); } bool game::is_game_over(void) { persistent_ptr r = state.root(); return (r->get_player()->get_state() == play_state::STATE_GAMEOVER); } void game::clear_prog(void) { state.close(); endwin(); } int game::parse_conf_create_dynamic_layout(void) { FILE *cfg_file; char *line = nullptr; size_t len = 0; unsigned i = 0; ssize_t col_no = 0; cfg_file = fopen(params->maze_path.c_str(), "r"); if (cfg_file == nullptr) return -1; persistent_ptr r = state.root(); while ((col_no = getline(&line, &len, cfg_file)) != -1) { if (i == 0) r->get_board()->set_size_col(col_no - 1); try { transaction::run(state, [&]() { r->get_board()->creat_dynamic_layout(i, line); }); } catch (transaction_error &err) { std::cout << err.what() << std::endl; } catch (transaction_scope_error &tse) { std::cout << tse.what() << std::endl; } i++; } r->get_board()->set_size_row(i); free(line); fclose(cfg_file); return 0; } /* * main */ int main(int argc, char *argv[]) { struct parameters params; params.use_maze = false; if (helper::parse_params(argc, argv, ¶ms)) return -1; int ret = -1; try { std::unique_ptr snake_game{new game(¶ms)}; snake_game->init(); while (!snake_game->is_stopped()) { int input = getch(); snake_game->process_key(input); if (snake_game->is_game_over()) { snake_game->game_over(); } else { snake_game->process_delay(); snake_game->clear_screen(); snake_game->process_step(); } } snake_game->clear_prog(); ret = 0; } catch (transaction_error &err) { std::cout << err.what() << std::endl; } catch (transaction_scope_error &tse) { std::cout << tse.what() << std::endl; } catch (pool_error &pe) { std::cout << pe.what() << std::endl; } catch (std::logic_error &le) { std::cout << le.what() << std::endl; } catch (std::runtime_error &re) { std::cout << re.what() << std::endl; } return ret; } libpmemobj-cpp-1.9/examples/panaconda/panaconda.hpp000066400000000000000000000145561361501571000224740ustar00rootroot00000000000000/* * Copyright 2016-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * panaconda.hpp -- example usage of libpmemobj C++ bindings */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_PANACONDA_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_PANACONDA_HPP #include #include #include #include #include class board_element; enum direction { UNDEFINED, DOWN, RIGHT, UP, LEFT }; enum object_type { SNAKE_SEGMENT, WALL, FOOD }; enum config_file_symbol { SYM_NOTHING = '0', SYM_WALL = '1' }; enum play_state { STATE_NEW, STATE_PLAY, STATE_GAMEOVER }; enum snake_event { EV_OK, EV_COLLISION }; enum action { ACTION_NEW_GAME = 'n', ACTION_QUIT = 'q' }; typedef pmem::obj::persistent_ptr> element_list; struct color_pair { color_pair(); color_pair(const int col_fg, const int col_bg); int color_bg; int color_fg; }; struct parameters { bool use_maze; std::string name; std::string maze_path; }; class helper { public: static color_pair get_color(const object_type obj_type); static int parse_params(int argc, char *argv[], struct parameters *params); static inline void sleep(int time); static inline void print_usage(std::string &name); }; class point { public: pmem::obj::p x; pmem::obj::p y; point(); point(int x, int y); friend bool operator==(point &point1, point &point2); }; bool operator==(point &point1, point &point2); class element_shape { public: element_shape() = default; element_shape(int shape); int get_val(); private: pmem::obj::p val; int get_symbol(int shape); }; class board_element { public: board_element(); board_element(int px, int py, pmem::obj::persistent_ptr shape, direction dir); board_element(point p, pmem::obj::persistent_ptr shape, direction dir); board_element(const board_element &element); ~board_element(); pmem::obj::persistent_ptr calc_new_position(const direction dir); void print(void); void print_double_col(void); void print_single_double_col(void); pmem::obj::persistent_ptr get_position(void); void set_position(const pmem::obj::persistent_ptr new_point); direction get_direction(void); void set_direction(const direction dir); private: pmem::obj::persistent_ptr position; pmem::obj::persistent_ptr shape; pmem::obj::p element_dir; }; class snake { public: snake(); ~snake(); void move(const direction dir); void print(void); void add_segment(void); bool check_point_against_segments(point point); point get_head_point(void); direction get_direction(void); point get_next_point(const direction dir); private: element_list snake_segments; pmem::obj::p last_seg_position; pmem::obj::p last_seg_dir; }; class game_board { public: game_board(); ~game_board(); void print(const int score); void print_game_over(const int score); unsigned get_size_row(void); void set_size_row(const unsigned size_r); unsigned get_size_col(void); void set_size_col(const unsigned size_c); int creat_dynamic_layout(const unsigned row_no, char *const buffer); int creat_static_layout(void); bool is_snake_head_food_hit(void); void create_new_food(void); bool is_collision(point point); snake_event move_snake(const direction dir); direction get_snake_dir(void); void add_snake_segment(void); private: pmem::obj::persistent_ptr anaconda; pmem::obj::persistent_ptr food; element_list layout; pmem::obj::p size_row; pmem::obj::p size_col; void set_new_food(const point point); bool is_snake_collision(point point); bool is_wall_collision(point point); }; class game_player { public: game_player(); ~game_player(); int get_score(void); void update_score(void); play_state get_state(void); void set_state(const play_state st); private: pmem::obj::p score; pmem::obj::p state; }; class game_state { public: game_state(); ~game_state(); pmem::obj::persistent_ptr get_board(); pmem::obj::persistent_ptr get_player(); void init(void); void clean_pool(void); private: pmem::obj::persistent_ptr board; pmem::obj::persistent_ptr player; }; class game { public: game(struct parameters *par); ~game(); void init(void); void init_colors(void); void process_step(void); void process_key(const int lastkey); inline bool is_stopped(void); void process_delay(void); void clear_screen(void); bool is_game_over(void); void game_over(void); void clear_prog(void); private: pmem::obj::pool state; int last_key; int delay; struct parameters *params; direction direction_key; void clean_pool(void); void set_direction_key(void); int parse_conf_create_dynamic_layout(void); }; #endif /* LIBPMEMOBJ_CPP_EXAMPLES_PANACONDA_HPP */ libpmemobj-cpp-1.9/examples/pman/000077500000000000000000000000001361501571000170335ustar00rootroot00000000000000libpmemobj-cpp-1.9/examples/pman/CMakeLists.txt000066400000000000000000000044641361501571000216030ustar00rootroot00000000000000# # Copyright 2018-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cmake_minimum_required(VERSION 3.3) project(pman CXX) set(CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 11) include(FindThreads) if(NOT WIN32) find_package(PkgConfig QUIET) endif() if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMOBJ++ REQUIRED libpmemobj++) pkg_check_modules(CURSES REQUIRED ncurses) else() find_package(LIBPMEMOBJ++ REQUIRED) # Specifies that we want FindCurses to find ncurses and not just any # curses library set(CURSES_NEED_NCURSES TRUE) find_package(Curses REQUIRED) endif() link_directories(${LIBPMEMOBJ++_LIBRARY_DIRS}) add_executable(pman pman.cpp) target_include_directories(pman PUBLIC ${CURSES_INCLUDE_DIR} ${LIBPMEMOBJ++_INCLUDE_DIRS} . ..) target_link_libraries(pman ${LIBPMEMOBJ++_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${CURSES_LIBRARIES}) libpmemobj-cpp-1.9/examples/pman/README000066400000000000000000000015061361501571000177150ustar00rootroot00000000000000This directory contains an example application implemented using libpmemobj, it's a game in which all the objects are stored on persistent memory. This means that the game process can be safely killed and then resumed. To launch the game: ./pman [map file] The file with the game session will either be created if it doesn't exist or opened if it contains a valid pool. Controls: move - arrow keys, jkli place bomb - spacebar, b quit - 'q' resume - 'r' This game demonstrates the usage of the very basics of the libpmemobj C++ bindings. It demonstrates pool management, persistent pointers and transactions. ** DEPENDENCIES: ** In order to build the game you need to install ncurses development package. rpm-based systems : ncurses-devel dpkg-based systems: libncursesX-dev (where X is the API/ABI version) libpmemobj-cpp-1.9/examples/pman/map000066400000000000000000000031501361501571000175320ustar00rootroot00000000000000######################################## # # ## # # # # ############ # ## # ############ # # # # # # ## # # # # # # ###### # # ###### # # # # # # # # ## # # # # # # ##### #### ## #### ##### # # # # # ## # # # # # # # ############ ############ # # # # # # # # # # # # # # # ######## ###### ######## # # # # # # # ####### ### ########### ### ######## # # # ## # # # ####### ######## ## ####### ######## # # # ## # # # # ############ # ## # ############ # # # # # # # # # ########## ##### #### ########## # # # # # # . # # # # # # # # # # # # # ########## ########## ########## # # # # # # # # # ############ # ## # ############ # # # # ## # # # ####### ######## ## ####### ######## # # # ## # # # ####### ### ########### ### ######## # # # # # # # ######## ###### ######## # # # # # # # # # # # # # # # ############ ############ # # # # # # # ## # # # # # ##### #### ## #### ##### # # # # # # # ## # # # # # # # ###### # # ###### # # # # # # ## # # # # # # ############ # ## # ############ # # # # ## # # ######################################## libpmemobj-cpp-1.9/examples/pman/pman.cpp000066400000000000000000000631061361501571000205000ustar00rootroot00000000000000/* * Copyright 2016-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * pman.cpp -- example usage of libpmemobj C++ bindings */ #include #include #include #include #include #ifdef __FreeBSD__ #include /* Need pkg, not system, version */ #else #include #endif #include #define LAYOUT_NAME "pman" #define SIZE 40 #define MAX_SIZE 38 #define MAX_BOMBS 5 #define KEY_SPACE 32 #define RAND_FIELD() (rand() % (SIZE - 2) + 1) #define EXPLOSION_TIME 20 #define EXPLOSION_COUNTER 80 #define GAME_DELAY 40000 #define SLEEP(t) \ do { \ struct timespec req = {0, (t)*1000}; \ while (nanosleep(&req, &req) == -1 && errno == EINTR) \ ; \ } while (0) using pmem::obj::delete_persistent; using pmem::obj::make_persistent; using pmem::obj::p; using pmem::obj::persistent_ptr; using pmem::obj::pool; using pmem::obj::pool_base; using pmem::obj::transaction; namespace examples { class state; } /* namespace examples */ namespace { pool pop; } namespace examples { enum position { UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT, POS_MIDDLE, POS_MAX, }; enum direction { DOWN, RIGHT, UP, LEFT, STOP, }; enum field { FREE, FOOD, WALL, PLAYER, ALIEN, EXPLOSION, BONUS, LIFE, BOMB, }; class point { public: point() = default; point(int xf, int yf); point(position cor); void move_back(); void move_home(); /* x component of object's position */ p x; /* y component of object's position */ p y; /* x component of object's previous position */ p prev_x; /* y component of object's previous position */ p prev_y; /* type of field of object */ p cur_field; /* type of field where object stood before */ p prev_field; protected: void move(); /* direction in which object is moving */ p dir; private: /* starting position of the object */ p home; }; class bomb : public point { public: bomb(int xf, int yf); void progress(); void explosion(); void print_time(); /* flag determining is bomb exploded */ p exploded; /* flag determining is bomb used */ p used; private: /* counter determining where change of bomb state is necessary*/ p timer; }; typedef persistent_ptr> bomb_vec; class player : public point { public: player(position cor); void progress(int in, bomb_vec *bombs); }; class alien : public point { public: alien(position cor); void progress(); void move_back_alien(); private: p rand_pos; }; class intro : public point { public: intro(int x, int y, direction d); void progress(); private: /* random color in which object will be displayed*/ p color; /* number determining object's path on the board*/ p num; }; class board_state { public: board_state(const std::string &map_file); ~board_state(); void reset_params(); void reset_board(); void print(unsigned hs); void reset(); void dead(); bool is_free(int x, int y); void set_board_elm(persistent_ptr p); void add_points(int x, int y); bool is_last_alien_killed(int x, int y); void set_explosion(int x, int y, field f); void explosion(int x, int y, field f); inline field get_board_elm(int x, int y) { return board[y * SIZE + x]; } inline void set_board_elm(int x, int y, field f) { board[y * SIZE + x] = f; } p level; /* number of level */ p timer; /* measure time since game start */ p n_aliens; /* number of not killed aliens */ p highscore; /* score of the best game */ p score; /* current score */ p game_over; /* set true if game is over */ private: unsigned long shape(field f); void set_bonus(field f); void set_board(const std::string &map_file); int find_wall(int x, int y, direction dir); p life; /* number of lives left for player */ persistent_ptr board; /* current state of board */ persistent_ptr board_tmpl; /* board template loaded from file*/ }; class state { public: state(); bool init(const std::string &map_file); void game(); private: bool intro_loop(); void print_start(); void print_game_over(); void new_game(const std::string &map_file); void reset_game(); void resume(); void one_move(int in); void collision(); void reset(); void next_level(); void reset_bombs(); bool is_collision(persistent_ptr p1, persistent_ptr p2); /* pointer to player type object */ persistent_ptr pl; /* pointer to board state */ persistent_ptr board; /* pointer to vector of alien type objects */ persistent_ptr> aliens; /* pointer to vector of intro type objects */ persistent_ptr> intro_p; /* pointer to vector of bomb type objects */ bomb_vec bombs; /* the best score player has ever achieved */ p highscore; }; /* * point::point -- overloaded constructor for point class */ point::point(int xf, int yf) : x{xf}, y{yf}, prev_x{xf}, prev_y{yf}, cur_field{FREE}, prev_field{FREE}, dir{DOWN}, home{UP_LEFT} {}; /* * point::point -- overloaded constructor for point class */ point::point(position cor) : x{0}, y{0}, prev_x{0}, prev_y{0}, cur_field{FREE}, prev_field{FREE}, dir{DOWN}, home{cor} { move_home(); } /* * point::move_home -- move object to it's home position */ void point::move_home() { prev_x = x; prev_y = y; switch (home) { case UP_LEFT: x = 1; y = 1; break; case UP_RIGHT: x = MAX_SIZE; y = 1; break; case DOWN_LEFT: x = 1; y = MAX_SIZE; break; case DOWN_RIGHT: x = MAX_SIZE; y = MAX_SIZE; break; case POS_MIDDLE: x = MAX_SIZE / 2; y = MAX_SIZE / 2; break; default: break; } } /* * point::move_back -- move object to it's previous position */ void point::move_back() { x = prev_x; y = prev_y; } /* * point::move -- move object in proper direction */ void point::move() { int tmp_x = 0, tmp_y = 0; switch (dir) { case LEFT: tmp_x = -1; break; case RIGHT: tmp_x = 1; break; case UP: tmp_y = -1; break; case DOWN: tmp_y = 1; break; default: break; } prev_x = x; prev_y = y; x = x + tmp_x; y = y + tmp_y; } /* * intro::intro -- overloaded constructor for intro class */ intro::intro(int x, int y, direction d) : point(x, y) { color = (field)(rand() % BOMB); if (d == DOWN || d == LEFT) num = y; else num = SIZE - y; dir = d; } /* * intro::progress -- perform one move */ void intro::progress() { move(); mvaddch(y, x * 2, COLOR_PAIR(color) | ACS_DIAMOND); int max_size = SIZE - num; if ((x == num && y == num) || (x == num && y == max_size) || (x == max_size && y == num) || (x == max_size && y == max_size)) dir = (direction)((dir + 1) % STOP); } /* * bomb::bomb -- overloaded constructor for bomb class */ bomb::bomb(int xf, int yf) : point(xf, yf) { cur_field = BOMB; exploded = false; used = false; timer = EXPLOSION_COUNTER; } /* * bomb::progress -- checks in which board_state is bomb */ void bomb::progress() { timer = timer - 1; if (exploded == false && timer == 0) explosion(); else if (timer == 0) used = true; } /* * bomb::explosion -- change board_state of bomb on exploded */ void bomb::explosion() { exploded = true; timer = EXPLOSION_TIME; } /* * bomb::print_time -- print time to explosion */ void bomb::print_time() { if (!exploded) mvprintw(y, x * 2, "%u", timer / 10); } /* * player::player -- overloaded constructor for player class */ player::player(position cor) : point(cor) { cur_field = PLAYER; } /* * player::progress -- checks input from keyboard and sets proper direction */ void player::progress(int in, bomb_vec *bombs) { switch (in) { case KEY_LEFT: case 'j': dir = LEFT; break; case KEY_RIGHT: case 'l': dir = RIGHT; break; case KEY_UP: case 'i': dir = UP; break; case KEY_DOWN: case 'k': dir = DOWN; break; case KEY_SPACE: case 'b': dir = STOP; if ((*bombs)->size() <= MAX_BOMBS) (*bombs)->push_back( make_persistent(x, y)); break; } move(); dir = STOP; } /* * alien::alien -- overloaded constructor for alien class */ alien::alien(position cor) : point(cor), rand_pos(false) { cur_field = ALIEN; prev_field = FOOD; } /* * alien::progress -- rand and set direction and move alien */ void alien::progress() { if (rand_pos || rand() % 10 == 0) dir = (direction)(rand() % STOP); rand_pos = false; move(); } /* * alien::move_back_alien -- move alien to previous position */ void alien::move_back_alien() { rand_pos = true; move_back(); } /* * board_state -- constructor for class board_state initializes boards and * needed variables */ board_state::board_state(const std::string &map_file) : highscore(0) { reset_params(); board = make_persistent(SIZE * SIZE); board_tmpl = make_persistent(SIZE * SIZE); for (int i = 0; i < SIZE * SIZE; ++i) set_board_elm(i, 0, FREE); set_board(map_file); } board_state::~board_state() { delete_persistent(board, SIZE * SIZE); delete_persistent(board_tmpl, SIZE * SIZE); } /* board_state::reset_params -- reset game parameters */ void board_state::reset_params() { life = 3; level = 1; n_aliens = 1; score = 0; timer = 0; game_over = false; } /* board_state::reset_board -- reset board state from template */ void board_state::reset_board() { for (auto i = 0; i < SIZE * SIZE; i++) board[i] = board_tmpl[i]; set_bonus(BONUS); set_bonus(LIFE); } /* * board_state::print -- print current board and information about game */ void board_state::print(unsigned hs) { for (int i = 0; i < SIZE; i++) { for (int j = 0; j < SIZE; j++) { if (get_board_elm(j, i) != FREE) mvaddch(i, j * 2, shape(get_board_elm(j, i))); } } if (score > hs) highscore = score; mvprintw(SIZE + 1, 0, "Score: %d\t\tHighscore: %u\t\tLevel: %u\t Timer: %u", (unsigned)score, (unsigned)highscore, (unsigned)level, (unsigned)timer); mvaddch(8, SIZE * 2 + 5, shape(FOOD)); mvprintw(8, SIZE * 2 + 10, " +1 point"); mvaddch(16, SIZE * 2 + 5, shape(BONUS)); mvprintw(16, SIZE * 2 + 10, " +50 point"); mvaddch(24, SIZE * 2 + 5, shape(ALIEN)); mvprintw(24, SIZE * 2 + 10, " +100 point"); mvaddch(32, SIZE * 2 + 5, shape(LIFE)); mvprintw(32, SIZE * 2 + 10, " +1 life"); for (unsigned i = 0; i < life; i++) mvaddch(SIZE + 3, static_cast(SIZE + life - i * 2), shape(PLAYER)); } /* * board_state::dead -- executed when player lose life */ void board_state::dead() { life = life - 1; if (life <= 0) { game_over = true; } } /* * board_state::reset -- clean board to start new level */ void board_state::reset() { reset_board(); n_aliens = level; timer = 0; } /* * board_state::is_free -- check whether field is free */ bool board_state::is_free(int x, int y) { return !(get_board_elm(x, y) == WALL || get_board_elm(x, y) == BOMB); } /* * board_state::add_points -- check type of field and give proper number of * points */ void board_state::add_points(int x, int y) { switch (get_board_elm(x, y)) { case FOOD: score = score + 1; break; case BONUS: score = score + 50; set_bonus(BONUS); break; case LIFE: if (life < 3) life = life + 1; set_bonus(LIFE); break; default: break; } } /* * board_state::is_last_alien_killed -- remove alien from board and check * whether any other alien stayed on the board */ bool board_state::is_last_alien_killed(int x, int y) { set_board_elm(x, y, FREE); n_aliens = n_aliens - 1; score = score + 100; if (n_aliens != 0) return false; level = level + 1; return true; } /* * board_state::set_board_elm -- set object on its current position on the board * and clean previous position */ void board_state::set_board_elm(persistent_ptr p) { set_board_elm(p->x, p->y, p->cur_field); if (!(p->x == p->prev_x && p->y == p->prev_y)) set_board_elm(p->prev_x, p->prev_y, p->prev_field); } /* * board_state::set_explosion --set exploded fields in proper way */ void board_state::set_explosion(int x, int y, field f) { field prev_f = get_board_elm(x, y); if (prev_f == BONUS || prev_f == LIFE) set_bonus(prev_f); set_board_elm(x, y, f); } /* * board_state::explosion -- mark exploded fields as exploded or free */ void board_state::explosion(int x, int y, field f) { for (int i = find_wall(x, y, UP); i < find_wall(x, y, DOWN); i++) set_explosion(x, i, f); for (int i = find_wall(x, y, LEFT); i < find_wall(x, y, RIGHT); i++) set_explosion(i, y, f); } /* * board_state::shape -- assign proper shape to different types of fields */ unsigned long board_state::shape(field f) { auto color = COLOR_PAIR(f); if (f == FOOD) return color | ACS_BULLET; else if (f == WALL || f == EXPLOSION) return color | ACS_CKBOARD; else return color | ACS_DIAMOND; } /* * board_state::set_bonus -- find free field and set the bonus there */ void board_state::set_bonus(field f) { int x, y; x = RAND_FIELD(); y = RAND_FIELD(); while (get_board_elm(x, y) != FOOD && get_board_elm(x, y) != FREE) { x = RAND_FIELD(); y = RAND_FIELD(); } set_board_elm(x, y, f); } /* * board_state::set_board -- set board with initial values from file */ void board_state::set_board(const std::string &map_file) { std::ifstream board_file; board_file.open(map_file.c_str()); if (board_file.fail()) assert(0); char num; for (int i = 0; i < SIZE; i++) { for (int j = 0; j < SIZE; j++) { board_file.get(num); if (num == '#') set_board_elm(j, i, WALL); else if (num == ' ') set_board_elm(j, i, FOOD); else set_board_elm(j, i, FREE); } board_file.get(num); } for (auto i = 0; i < SIZE * SIZE; i++) board_tmpl[i] = board[i]; board_file.close(); set_bonus(BONUS); set_bonus(LIFE); } /* * board_state::find_wall -- finds first wall from given point in given * direction */ int board_state::find_wall(int x, int y, direction dir) { switch (dir) { case LEFT: { for (int i = x; i >= 0; i--) { if (get_board_elm(i, y) == WALL) return i + 1; } break; } case RIGHT: { for (int i = x; i <= SIZE; i++) { if (get_board_elm(i, y) == WALL) return i; } break; } case UP: { for (int i = y; i >= 0; i--) { if (get_board_elm(x, i) == WALL) return i + 1; } break; } case DOWN: { for (int i = y; i <= SIZE; i++) { if (get_board_elm(x, i) == WALL) return i; } break; } default: break; } return 0; } /* * state::init -- initialize game */ bool state::init(const std::string &map_file) { int in; if (board == nullptr || pl == nullptr) new_game(map_file); else { while ((in = getch()) != 'y') { mvprintw(SIZE / 4, SIZE / 4, "Do you want to continue the game? [y/n]"); if (in == 'n') { resume(); break; } } if (in == 'y' && intro_p->size() == 0) return false; } { transaction::manual tx(pop); if (intro_p->size() == 0) { for (int i = 0; i < SIZE / 4; i++) { intro_p->push_back( make_persistent(i, i, DOWN)); intro_p->push_back(make_persistent( SIZE - i, i, LEFT)); intro_p->push_back(make_persistent( i, SIZE - i, RIGHT)); intro_p->push_back(make_persistent( SIZE - i, SIZE - i, UP)); } } transaction::commit(); } if (intro_loop()) return true; { transaction::manual tx(pop); intro_p->clear(); transaction::commit(); } return false; } /* * state::game -- process game loop */ void state::game() { int in; while ((in = getch()) != 'q') { SLEEP(GAME_DELAY); erase(); if (in == 'r') resume(); if (!board->game_over) one_move(in); else print_game_over(); } } /* * state::intro_loop -- display intro an wait for user's reaction */ bool state::intro_loop() { int in; while ((in = getch()) != 'q') { print_start(); unsigned i = 0; persistent_ptr p; { transaction::manual tx(pop); while ((p = intro_p->get(i++)) != nullptr) p->progress(); transaction::commit(); } SLEEP(GAME_DELAY); if (in == 's') return true; } return false; } /* * state::print_start -- print intro inscription */ void state::print_start() { erase(); int x = static_cast(SIZE / 1.8); int y = static_cast(SIZE / 2.5); mvprintw(y + 0, x, "####### # # ####### # #"); mvprintw(y + 1, x, "# # ## ## # # ## #"); mvprintw(y + 2, x, "####### # # # # ####### # # #"); mvprintw(y + 3, x, "# # # # # # # # #"); mvprintw(y + 4, x, "# # # # # # ##"); mvprintw(y + 8, x, " Press 's' to start "); mvprintw(y + 9, x, " Press 'q' to quit "); } /* * state::print_game_over -- print game over inscription */ void state::print_game_over() { erase(); int x = SIZE / 3; int y = SIZE / 6; mvprintw(y + 0, x, "####### ####### # # #######"); mvprintw(y + 1, x, "# # # ## ## # "); mvprintw(y + 2, x, "# ### ####### # # # # #### "); mvprintw(y + 3, x, "# # # # # # # # "); mvprintw(y + 4, x, "####### # # # # #######"); mvprintw(y + 6, x, "####### # # ####### #######"); mvprintw(y + 7, x, "# # # # # # #"); mvprintw(y + 8, x, "# # # # #### #######"); mvprintw(y + 9, x, "# # # # # # # "); mvprintw(y + 10, x, "####### # ####### # #"); mvprintw(y + 13, x, " Your final score is %u ", (unsigned)board->score); if (board->score == highscore) mvprintw(y + 14, x, " YOU'VE BEATEN YOUR BEST SCORE! "); mvprintw(y + 16, x, " Press 'q' to quit "); mvprintw(y + 17, x, " Press 'r' to resume "); } /* * state::new_game -- allocate board_state, player and aliens if root is empty */ void state::new_game(const std::string &map_file) { transaction::manual tx(pop); board = make_persistent(map_file); pl = make_persistent(POS_MIDDLE); intro_p = make_persistent>(); bombs = make_persistent>(); aliens = make_persistent>(); aliens->push_back(make_persistent(UP_LEFT)); transaction::commit(); } /* * state::reset_game -- reset the game from the board template */ void state::reset_game() { transaction::manual tx(pop); board->reset_params(); board->reset_board(); pl = make_persistent(POS_MIDDLE); intro_p = make_persistent>(); bombs = make_persistent>(); aliens = make_persistent>(); aliens->push_back(make_persistent(UP_LEFT)); transaction::commit(); } /* * state::resume -- clean root pointer and start a new game */ void state::resume() { { transaction::manual tx(pop); delete_persistent(pl); pl = nullptr; aliens->clear(); delete_persistent>(aliens); bombs->clear(); delete_persistent>(bombs); intro_p->clear(); delete_persistent>(intro_p); transaction::commit(); } reset_game(); } /* * state::one_move -- process one round where every object moves one time */ void state::one_move(int in) { unsigned i = 0; persistent_ptr a; persistent_ptr b; transaction::manual tx(pop); board->timer = board->timer + 1; pl->progress(in, &bombs); while ((a = aliens->get(i++)) != nullptr) a->progress(); i = 0; while ((b = bombs->get(i++)) != nullptr) { b->progress(); if (b->exploded) board->explosion(b->x, b->y, EXPLOSION); if (b->used) { board->explosion(b->x, b->y, FREE); bombs->erase(--i); } } collision(); board->print(highscore); highscore = board->highscore; i = 0; while ((b = bombs->get(i++)) != nullptr) b->print_time(); transaction::commit(); } /* * state::collision -- check for collisions between any two objects */ void state::collision() { unsigned i = 0; persistent_ptr a; persistent_ptr b; while ((b = bombs->get(i++)) != nullptr) { if (!b->exploded) { if (board->get_board_elm(b->x, b->y) == EXPLOSION) b->explosion(); board->set_board_elm(b); } } i = 0; while ((a = aliens->get(i++)) != nullptr) { if (board->get_board_elm(a->x, a->y) == EXPLOSION) { bool is_over = board->is_last_alien_killed(a->prev_x, a->prev_y); aliens->erase(--i); if (is_over) { if (board->get_board_elm(pl->x, pl->y) == EXPLOSION) board->dead(); next_level(); return; } } } bool dead = false; i = 0; while ((a = aliens->get(i++)) != nullptr) { /*check collision alien with wall or bomb */ if (!board->is_free(a->x, a->y)) a->move_back_alien(); /*check collision alien with player */ if (is_collision(pl, a)) dead = true; /*check collision alien with alien */ unsigned j = 0; persistent_ptr a2; while ((a2 = aliens->get(j++)) != nullptr) { if (a != a2 && is_collision(a, a2)) { a->move_back_alien(); break; } } field prev_f = board->get_board_elm(a->x, a->y); board->set_board_elm(a); if (prev_f != ALIEN && prev_f != PLAYER) a->prev_field = prev_f; } if (!board->is_free(pl->x, pl->y)) pl->move_back(); if (board->get_board_elm(pl->x, pl->y) == EXPLOSION || dead) { board->dead(); reset(); return; } board->add_points(pl->x, pl->y); board->set_board_elm(pl); SLEEP(10000); } /* * state::reset -- move objects on their home positions */ void state::reset() { unsigned i = 0; persistent_ptr a; while ((a = aliens->get(i++)) != nullptr) { a->move_home(); board->set_board_elm(a); } pl->move_back(); pl->move_home(); board->set_board_elm(pl); reset_bombs(); } /* * state::next_level -- clean board, create proper number of aliens and * start new level */ void state::next_level() { reset_bombs(); board->reset(); for (unsigned i = 0; i < board->n_aliens; i++) aliens->push_back(make_persistent( (position)((UP_LEFT + i) % (POS_MAX - 1)))); pl->move_home(); } /* * state::reset_bombs -- remove all bombs */ void state::reset_bombs() { unsigned i = 0; persistent_ptr b; while ((b = bombs->get(i++)) != nullptr) { if (b->exploded) board->explosion(b->x, b->y, FREE); } bombs->clear(); } /* * state::is_collision -- check if there is collision between given objects */ bool state::is_collision(persistent_ptr p1, persistent_ptr p2) { if (p1->x == p2->x && p1->y == p2->y) return true; else if (p1->prev_x == p2->x && p1->prev_y == p2->y && p1->x == p2->prev_x && p1->y == p2->prev_y) return true; return false; } } /* namespace examples */ namespace { void print_usage(const std::string &binary) { std::cout << "Usage:\n" << binary << " [map_file]\n"; } } int main(int argc, char *argv[]) { if (argc < 2 || argc > 3) { print_usage(argv[0]); return 1; } std::string name = argv[1]; std::string map_path = "map"; if (argc == 3) map_path = argv[2]; int ret = -1; try { if (pool::check(name, LAYOUT_NAME) == 1) pop = pool::open(name, LAYOUT_NAME); else pop = pool::create( name, LAYOUT_NAME, PMEMOBJ_MIN_POOL * 2); initscr(); start_color(); init_pair(examples::FOOD, COLOR_YELLOW, COLOR_BLACK); init_pair(examples::WALL, COLOR_WHITE, COLOR_BLACK); init_pair(examples::PLAYER, COLOR_CYAN, COLOR_BLACK); init_pair(examples::ALIEN, COLOR_RED, COLOR_BLACK); init_pair(examples::EXPLOSION, COLOR_CYAN, COLOR_BLACK); init_pair(examples::BONUS, COLOR_YELLOW, COLOR_BLACK); init_pair(examples::LIFE, COLOR_MAGENTA, COLOR_BLACK); nodelay(stdscr, true); curs_set(0); keypad(stdscr, true); persistent_ptr r = pop.root(); if ((r != nullptr) && (r->init(map_path) != false)) r->game(); endwin(); pop.close(); ret = 0; } catch (pmem::transaction_error &err) { std::cerr << err.what() << std::endl; } catch (pmem::transaction_scope_error &tse) { std::cerr << tse.what() << std::endl; } catch (pmem::pool_error &pe) { std::cerr << pe.what() << std::endl; } catch (std::logic_error &le) { std::cerr << le.what() << std::endl; } return ret; } libpmemobj-cpp-1.9/examples/pmpong/000077500000000000000000000000001361501571000174005ustar00rootroot00000000000000libpmemobj-cpp-1.9/examples/pmpong/Ball.cpp000066400000000000000000000074311361501571000207630ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "Ball.hpp" #include "Pool.hpp" Ball::Ball(int x, int y) { this->x = x; this->y = y; velocity = pmem::obj::make_persistent(); this->velocity->x = 0; this->velocity->y = 0; } Ball::~Ball() { pmem::obj::transaction::run( Pool::getGamePool()->getPoolToTransaction(), [&] { pmem::obj::delete_persistent(velocity); }); } void Ball::move() { setXY(this->x + (int)this->velocity->x, this->y + (int)this->velocity->y); } void Ball::collisionWithWindow() { if (this->y <= SCORE_VIEW_OFFSET + HORIZONAL_LINE_OFFSET || this->y + getBallShape().getRadius() * 2 >= WINDOW_HEIGHT - HORIZONAL_LINE_OFFSET) { setVelocityY(velocity->y * -1); } } void Ball::increaseVelocity() { if (velocity->x < 0) { setVelocityX(velocity->x - BALL_VELOCITY_INCREMENTING); } else { setVelocityX(velocity->x + BALL_VELOCITY_INCREMENTING); } if (velocity->y < 0) { setVelocityY(velocity->y - BALL_VELOCITY_INCREMENTING); } else { setVelocityY(velocity->y + BALL_VELOCITY_INCREMENTING); } } void Ball::setX(int xArg) { pmem::obj::transaction::run(Pool::getGamePool()->getPoolToTransaction(), [&] { x = xArg; }); } void Ball::setY(int yArg) { pmem::obj::transaction::run(Pool::getGamePool()->getPoolToTransaction(), [&] { y = yArg; }); } void Ball::setVelocityX(float xArg) { pmem::obj::transaction::run(Pool::getGamePool()->getPoolToTransaction(), [&] { velocity->x = xArg; }); } void Ball::setVelocityY(float yArg) { pmem::obj::transaction::run(Pool::getGamePool()->getPoolToTransaction(), [&] { velocity->y = yArg; }); } void Ball::setXY(int xArg, int yArg) { pmem::obj::transaction::run(Pool::getGamePool()->getPoolToTransaction(), [&] { x = xArg; y = yArg; }); } void Ball::init() { setXY(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2); setVelocityX(0); setVelocityY(0); } int Ball::getX() { return this->x; } int Ball::getY() { return this->y; } pmem::obj::persistent_ptr Ball::getVelocity() { return this->velocity; } sf::CircleShape Ball::getBallShape() { sf::CircleShape shapeToRet; shapeToRet.setRadius(BALL_SIZE); shapeToRet.setPosition(sf::Vector2f((float)this->x, (float)this->y)); return shapeToRet; } libpmemobj-cpp-1.9/examples/pmpong/Ball.hpp000066400000000000000000000045711361501571000207720ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_BALL_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_BALL_HPP #include "GameConstants.hpp" #include #include #include #include class Ball { public: Ball(int x, int y); ~Ball(); void move(); void collisionWithWindow(); void increaseVelocity(); void setX(int xArg); void setY(int yArg); void setVelocityX(float xArg); void setVelocityY(float yArg); void setXY(int xArg, int yArg); void init(); int getX(); int getY(); pmem::obj::persistent_ptr getVelocity(); sf::CircleShape getBallShape(); private: pmem::obj::p x; pmem::obj::p y; pmem::obj::persistent_ptr velocity; }; #endif /* LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_BALL_HPP */ libpmemobj-cpp-1.9/examples/pmpong/CMakeLists.txt000066400000000000000000000057231361501571000221470ustar00rootroot00000000000000# # Copyright 2018-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cmake_minimum_required(VERSION 3.3) project(pmpong CXX) set(CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 11) include(FindThreads) if(NOT WIN32) find_package(PkgConfig QUIET) endif() if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMOBJ++ REQUIRED libpmemobj++) pkg_check_modules(SFML REQUIRED sfml-all>=2.4) else() find_package(LIBPMEMOBJ++ REQUIRED) # SFML 2.5 has different cmake interface than <= 2.4 so previous versions are not supported find_package(SFML 2.5 REQUIRED COMPONENTS graphics window system) set(SFML_LIBRARIES sfml-graphics sfml-window sfml-system) endif() link_directories(${LIBPMEMOBJ++_LIBRARY_DIRS}) add_executable(pmpong Ball.cpp GameController.cpp GameOverView.cpp GameView.cpp MainGame.cpp MenuView.cpp Paddle.cpp PongGameStatus.cpp Pool.cpp) target_include_directories(pmpong PUBLIC ${SFML_INCLUDE_DIR} ${LIBPMEMOBJ++_INCLUDE_DIRS} .) target_link_libraries(pmpong ${LIBPMEMOBJ++_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${SFML_LIBRARIES}) if(NOT WIN32) find_program(FCLIST NAMES fc-list) if(NOT FCLIST) message(WARNING "fc-list not found. Install fontconfig to allow examples-pmpong to automatically find fonts.") endif() execute_process(COMMAND bash -c "fc-list --format='%{file}\n' | head -n1 | tr -d '\n'" OUTPUT_VARIABLE FONT_PATH ERROR_QUIET) set(font ${FONT_PATH}) else() set(font "C:/Windows/Fonts/Arial.ttf") endif() target_compile_options(pmpong PUBLIC -DLIBPMEMOBJ_CPP_PMPONG_FONT_PATH="${font}") libpmemobj-cpp-1.9/examples/pmpong/GameConstants.hpp000066400000000000000000000062721361501571000226660ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_GAMECONSTANTS_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_GAMECONSTANTS_HPP #include #include #include #include #include #include #include #include #define PADDLE_VELOCITY_PLAYER 4 #define PADDLE_VELOCITY_COMPUTER 20 #define WINDOW_HEIGHT 500 #define WINDOW_WIDTH 700 #define BALL_VELOCITY_INCREMENTING 0.2f #define FRAMERATE_LIMIT 70 #define PADDLE_HEIGHT 100 #define PADDLE_WIDTH 12 #define BALL_SIZE 7 #define MENU_ITEMS 4 #define POINTS_TO_WIN 10 #define BALL_PLAYERS_SPEED 4.0f #define BALL_COMPUTER_SPEED 11.0f #define VERTICAL_LINE_OFFSET 15 #define HORIZONAL_LINE_OFFSET 30 #define LINE_THICKNESS 3 #define SCORE_VIEW_OFFSET 20 #define GAME_NAME "pmpong" #define GAMEVIEW_SCORE_FONTSIZE 20 #define MENUVIEW_ITEMS_FONTSIZE 30 #define GAMEOVER_FONTSIZE 45 #define MENUITEM_OFFSET 100 #define GAMOVERVIEW_OFFSET 50 #define LAYOUT_NAME "DEFAULT_LAYOUT_NAME" #define DEFAULT_POOLFILE_NAME "DEFAULT_FILENAME" static inline sf::Font getFont() { std::string font_path = ""; #ifdef LIBPMEMOBJ_CPP_PMPONG_FONT_PATH font_path = LIBPMEMOBJ_CPP_PMPONG_FONT_PATH; #endif auto env = getenv("LIBPMEMOBJ_CPP_PMPONG_FONT_PATH"); if (env != nullptr) font_path = env; sf::Font font; if (!font.loadFromFile(font_path)) { throw std::runtime_error( "Cannot find fonts. Please set environmental variable LIBPMEMOBJ_CPP_PMPONG_FONT_PATH" " to path to existing font file"); } return font; } #endif /* LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_GAMECONSTANTS_HPP */ libpmemobj-cpp-1.9/examples/pmpong/GameController.cpp000066400000000000000000000151111361501571000230200ustar00rootroot00000000000000/* * Copyright 2017-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "GameController.hpp" #include "Pool.hpp" GameController::GameController() { gameStatus = pmem::obj::make_persistent(); } GameController::~GameController() { pmem::obj::transaction::run( Pool::getGamePool()->getPoolToTransaction(), [&] { pmem::obj::delete_persistent( gameStatus); }); } void GameController::gameLoop(bool isSimulation) { sf::RenderWindow *gameWindow = new sf::RenderWindow( sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), GAME_NAME); gameWindow->setFramerateLimit(FRAMERATE_LIMIT); sf::Font font = getFont(); View *menuView = new MenuView(font); View *gameView = new GameView(font); View *gameOverView = new GameOverView(font); while (gameWindow->isOpen()) { sf::Event event; while (gameWindow->pollEvent(event)) { if (event.type == sf::Event::Closed) gameWindow->close(); } gameWindow->clear(); if (isSimulation) { if (gameStatus->getGameState() != game_state::SIMULATE) { resetGameStatus(); gameStatus->setIsGameToResume(false); gameStatus->setGameState(game_state::SIMULATE); } gameMatchSimulation(gameWindow, gameView); } else { if (gameStatus->getGameState() == game_state::MATCH) { gameMatch(gameWindow, gameView); } else if (gameStatus->getGameState() == game_state::MENU) { menu(gameWindow, menuView); } else if (gameStatus->getGameState() == game_state::SIMULATE) { gameMatchSimulation(gameWindow, gameView); } else if (gameStatus->getGameState() == game_state::GAME_OVER) { gameOver(gameWindow, gameOverView); } } } delete menuView; delete gameView; delete gameOverView; delete gameWindow; } void GameController::gameOver(sf::RenderWindow *gameWindow, View *view) { view->prepareView(*gameStatus); view->displayView(gameWindow); sf::Event event; while (gameWindow->pollEvent(event)) { if (event.type == sf::Event::KeyPressed) { switch (event.key.code) { case sf::Keyboard::Return: gameStatus->setIsGameToResume(false); gameStatus->setGameState( game_state::MENU); break; default: break; } } if (event.type == sf::Event::Closed) gameWindow->close(); } } void GameController::menu(sf::RenderWindow *gameWindow, View *view) { view->prepareView(*gameStatus); view->displayView(gameWindow); sf::Event event; while (gameWindow->pollEvent(event)) { if (event.type == sf::Event::KeyPressed) { handleEventKeypress(event, gameWindow); } if (event.type == sf::Event::Closed) gameWindow->close(); } } void GameController::handleEventKeypress(sf::Event &event, sf::RenderWindow *gameWindow) { switch (event.key.code) { case sf::Keyboard::Up: gameStatus->setMenuItem( gameStatus->getMenuItem() == 0 ? MENU_ITEMS - 1 : gameStatus->getMenuItem() - 1); break; case sf::Keyboard::Down: gameStatus->setMenuItem( (gameStatus->getMenuItem() + 1) % MENU_ITEMS); break; case sf::Keyboard::Return: if (gameStatus->getMenuItem() == NEW_GAME) { resetGameStatus(); gameStatus->setIsGameToResume(true); gameStatus->setGameState(game_state::MATCH); } else if (gameStatus->getMenuItem() == RESUME && gameStatus->getIsGameToResume()) { gameStatus->setGameState(game_state::MATCH); } else if (gameStatus->getMenuItem() == SIMULATION) { resetGameStatus(); gameStatus->setIsGameToResume(false); gameStatus->setGameState(game_state::SIMULATE); } else if (gameStatus->getMenuItem() == EXIT) { gameWindow->close(); } break; default: break; } } void GameController::gameMatch(sf::RenderWindow *gameWindow, View *view) { if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Space)) gameStatus->startBall(BALL_PLAYERS_SPEED); gameStatus->movePaddles(); gameStatus->lookForCollisions(true); gameStatus->actualizeStatus(); view->prepareView(*gameStatus); view->displayView(gameWindow); if (gameStatus->score()) { if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Space)) gameStatus->startBall(BALL_PLAYERS_SPEED); } if (gameStatus->checkIfAnyPlayerWon()) { gameStatus->setGameState(game_state::GAME_OVER); } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Escape)) { gameStatus->setGameState(game_state::MENU); } } void GameController::gameMatchSimulation(sf::RenderWindow *gameWindow, View *view) { gameStatus->startBall(BALL_COMPUTER_SPEED); gameStatus->simulate(); gameStatus->lookForCollisions(false); gameStatus->actualizeStatus(); if (gameStatus->score()) gameStatus->startBall(BALL_COMPUTER_SPEED); view->prepareView(*gameStatus); view->displayView(gameWindow); if (gameStatus->checkIfAnyPlayerWon()) { gameStatus->setGameState(game_state::GAME_OVER); } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Escape)) { gameStatus->setGameState(game_state::MENU); } } void GameController::resetGameStatus() { pmem::obj::transaction::run( Pool::getGamePool()->getPoolToTransaction(), [&] { pmem::obj::delete_persistent( gameStatus); gameStatus = pmem::obj::make_persistent(); }); } libpmemobj-cpp-1.9/examples/pmpong/GameController.hpp000066400000000000000000000051161361501571000230310ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_GAMECONTROLLER_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_GAMECONTROLLER_HPP #include "GameConstants.hpp" #include "GameOverView.hpp" #include "GameView.hpp" #include "MenuView.hpp" #include "PongGameStatus.hpp" #include #include #include #include #include class GameController { public: GameController(); ~GameController(); void gameLoop(bool isSimulation = false); private: pmem::obj::persistent_ptr gameStatus; void gameOver(sf::RenderWindow *gameWindow, View *view); void menu(sf::RenderWindow *gameWindow, View *view); void handleEventKeypress(sf::Event &event, sf::RenderWindow *gameWindow); void gameMatch(sf::RenderWindow *gameWindow, View *view); void gameMatchSimulation(sf::RenderWindow *gameWindow, View *view); void resetGameStatus(); }; #endif /* LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_GAMECONTROLLER_HPP */ libpmemobj-cpp-1.9/examples/pmpong/GameOverView.cpp000066400000000000000000000056161361501571000224540ustar00rootroot00000000000000/* * Copyright 2017, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "GameOverView.hpp" GameOverView::GameOverView(sf::Font &font) { gameOver.setString("GAME OVER"); entContinue.setString("press ENTER to continue"); playerWinner.setString(""); gameOver.setFont(font); playerWinner.setFont(font); entContinue.setFont(font); gameOver.setCharacterSize(GAMEOVER_FONTSIZE); playerWinner.setCharacterSize(MENUVIEW_ITEMS_FONTSIZE); entContinue.setCharacterSize(MENUVIEW_ITEMS_FONTSIZE); gameOver.setPosition( WINDOW_WIDTH / 2 - gameOver.getGlobalBounds().width / 2, 0); playerWinner.setPosition( WINDOW_WIDTH / 2 - playerWinner.getGlobalBounds().width / 2, GAMOVERVIEW_OFFSET * 2); entContinue.setPosition(WINDOW_WIDTH / 2 - entContinue.getGlobalBounds().width / 2, WINDOW_HEIGHT - GAMOVERVIEW_OFFSET); gameOver.setFillColor(sf::Color::Red); playerWinner.setFillColor(sf::Color::Green); entContinue.setFillColor(sf::Color::White); } GameOverView::~GameOverView() { } void GameOverView::prepareView(PongGameStatus &gameStatus) { if (gameStatus.getPlayer1()->getPoints() == POINTS_TO_WIN) playerWinner.setString("LEFT PLAYER WON!"); else playerWinner.setString("RIGHT PLAYER WON!"); } void GameOverView::displayView(sf::RenderWindow *gameWindow) { gameWindow->clear(); gameWindow->draw(gameOver); gameWindow->draw(playerWinner); gameWindow->draw(entContinue); gameWindow->display(); } libpmemobj-cpp-1.9/examples/pmpong/GameOverView.hpp000066400000000000000000000041731361501571000224560ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_GAMEOVERVIEW_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_GAMEOVERVIEW_HPP #include "GameConstants.hpp" #include "PongGameStatus.hpp" #include "View.hpp" #include class GameOverView : public View { public: GameOverView(sf::Font &font); ~GameOverView(); virtual void prepareView(PongGameStatus &gameStatus); virtual void displayView(sf::RenderWindow *gameWindow); private: sf::Text gameOver; sf::Text playerWinner; sf::Text entContinue; }; #endif /* LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_GAMEOVERVIEW_HPP */ libpmemobj-cpp-1.9/examples/pmpong/GameView.cpp000066400000000000000000000116441361501571000216160ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "GameView.hpp" GameView::GameView(sf::Font &font) { sf::Color elementsColor(224, 224, 224); scoreP1.setFont(font); scoreP2.setFont(font); scoreP1.setCharacterSize(GAMEVIEW_SCORE_FONTSIZE); scoreP2.setCharacterSize(GAMEVIEW_SCORE_FONTSIZE); scoreP1.setPosition(WINDOW_WIDTH / 2 - SCORE_VIEW_OFFSET, SCORE_VIEW_OFFSET); scoreP2.setPosition(WINDOW_WIDTH / 2 + SCORE_VIEW_OFFSET - scoreP2.getGlobalBounds().width, SCORE_VIEW_OFFSET); scoreP1.setFillColor(sf::Color::Green); scoreP2.setFillColor(sf::Color::Green); upperLine.setPosition(VERTICAL_LINE_OFFSET, scoreP1.getPosition().y + HORIZONAL_LINE_OFFSET); upperLine.setSize(sf::Vector2f(WINDOW_WIDTH - 2 * VERTICAL_LINE_OFFSET, LINE_THICKNESS)); upperLine.setFillColor(elementsColor); downLine.setPosition(VERTICAL_LINE_OFFSET, WINDOW_HEIGHT - HORIZONAL_LINE_OFFSET); downLine.setSize(sf::Vector2f(WINDOW_WIDTH - 2 * VERTICAL_LINE_OFFSET + LINE_THICKNESS, LINE_THICKNESS)); downLine.setFillColor(elementsColor); leftLine.setPosition(VERTICAL_LINE_OFFSET, scoreP1.getPosition().y + HORIZONAL_LINE_OFFSET); leftLine.setSize(sf::Vector2f( LINE_THICKNESS, WINDOW_HEIGHT - (scoreP1.getPosition().y + 2 * HORIZONAL_LINE_OFFSET))); leftLine.setFillColor(elementsColor); rightLine.setPosition(WINDOW_WIDTH - VERTICAL_LINE_OFFSET, scoreP1.getPosition().y + HORIZONAL_LINE_OFFSET); rightLine.setSize(sf::Vector2f( LINE_THICKNESS, WINDOW_HEIGHT - (scoreP1.getPosition().y + 2 * HORIZONAL_LINE_OFFSET))); rightLine.setFillColor(elementsColor); court.setPosition(VERTICAL_LINE_OFFSET + LINE_THICKNESS, scoreP1.getPosition().y + HORIZONAL_LINE_OFFSET); court.setSize(sf::Vector2f( WINDOW_WIDTH - 2 * VERTICAL_LINE_OFFSET, WINDOW_HEIGHT - (scoreP1.getPosition().y + 2 * HORIZONAL_LINE_OFFSET))); court.setFillColor(sf::Color(60, 132, 48)); ballShape.setRadius(BALL_SIZE); ballShape.setPosition(sf::Vector2f(0, 0)); ballShape.setFillColor(elementsColor); rightPaddleShape.setSize(sf::Vector2f(PADDLE_WIDTH, PADDLE_HEIGHT)); leftPaddleShape.setSize(sf::Vector2f(PADDLE_WIDTH, PADDLE_HEIGHT)); leftPaddleShape.setPosition(sf::Vector2f(0, 0)); rightPaddleShape.setPosition(sf::Vector2f(0, 0)); leftPaddleShape.setFillColor(sf::Color::Red); rightPaddleShape.setFillColor(sf::Color::Red); } GameView::~GameView() { } void GameView::prepareView(PongGameStatus &gameStatus) { scoreP1.setString(std::to_string(gameStatus.getPlayer1()->getPoints())); scoreP2.setString(std::to_string(gameStatus.getPlayer2()->getPoints())); ballShape.setPosition( sf::Vector2f((float)gameStatus.getBall()->getX(), (float)gameStatus.getBall()->getY())); leftPaddleShape.setPosition( sf::Vector2f((float)gameStatus.getPlayer1()->getX(), (float)gameStatus.getPlayer1()->getY())); rightPaddleShape.setPosition( sf::Vector2f((float)gameStatus.getPlayer2()->getX(), (float)gameStatus.getPlayer2()->getY())); } void GameView::displayView(sf::RenderWindow *gameWindow) { gameWindow->clear(); gameWindow->draw(court); gameWindow->draw(upperLine); gameWindow->draw(leftLine); gameWindow->draw(downLine); gameWindow->draw(rightLine); gameWindow->draw(scoreP1); gameWindow->draw(scoreP2); gameWindow->draw(ballShape); gameWindow->draw(leftPaddleShape); gameWindow->draw(rightPaddleShape); gameWindow->display(); } libpmemobj-cpp-1.9/examples/pmpong/GameView.hpp000066400000000000000000000045261361501571000216240ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_GAMEVIEW_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_GAMEVIEW_HPP #include "GameConstants.hpp" #include "PongGameStatus.hpp" #include "View.hpp" #include #include class GameView : public View { public: GameView(sf::Font &font); ~GameView(); virtual void prepareView(PongGameStatus &gameStatus); virtual void displayView(sf::RenderWindow *gameWindow); private: sf::Text scoreP1; sf::Text scoreP2; sf::RectangleShape upperLine; sf::RectangleShape downLine; sf::RectangleShape leftLine; sf::RectangleShape rightLine; sf::RectangleShape court; sf::CircleShape ballShape; sf::RectangleShape leftPaddleShape; sf::RectangleShape rightPaddleShape; }; #endif /* LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_GAMEVIEW_HPP */ libpmemobj-cpp-1.9/examples/pmpong/MainGame.cpp000066400000000000000000000054441361501571000215710ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "Pool.hpp" #include #include #include #include #include int main(int argc, char *argv[]) { int exitCode = EXIT_FAILURE; std::string mode = ""; if (argc == 3) { mode = argv[2]; } if (argc < 2 || argc > 3 || (argc == 3 && mode != "-s")) { std::cout << "Usage: ./pmpong [options]" << std::endl << "Options: " << std::endl << "-s, simulates game between 2 AI players" << std::endl; return exitCode; } std::string fileName = argv[1]; try { Pool *pool = Pool::getGamePoolFromFile(fileName); pmem::obj::persistent_ptr gameController = pool->getGameController(); if (mode == "-s") gameController->gameLoop(true); else gameController->gameLoop(); delete pool; exitCode = EXIT_SUCCESS; } catch (pmem::transaction_error &err) { std::cerr << err.what() << std::endl; } catch (pmem::transaction_scope_error &tse) { std::cerr << tse.what() << std::endl; } catch (pmem::pool_error &pe) { std::cerr << pe.what() << std::endl; } catch (std::logic_error &le) { std::cerr << le.what() << std::endl; } catch (std::exception &exc) { std::cerr << exc.what() << std::endl; } return exitCode; } libpmemobj-cpp-1.9/examples/pmpong/MenuView.cpp000066400000000000000000000052261361501571000216500ustar00rootroot00000000000000/* * Copyright 2017, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "MenuView.hpp" MenuView::MenuView(sf::Font &font) { menuItems[NEW_GAME].setString("NEW GAME"); menuItems[RESUME].setString("RESUME"); menuItems[SIMULATION].setString("SIMULATION"); menuItems[EXIT].setString("EXIT"); for (int i = 0; i < MENU_ITEMS; i++) { menuItems[i].setFont(font); menuItems[i].setCharacterSize(MENUVIEW_ITEMS_FONTSIZE); menuItems[i].setPosition( WINDOW_WIDTH / 2 - (float)menuItems[i].getGlobalBounds().width / 2, (float)(i + 1) * MENUITEM_OFFSET - MENUVIEW_ITEMS_FONTSIZE); } } MenuView::~MenuView() { } void MenuView::prepareView(PongGameStatus &gameStatus) { for (int i = 0; i < MENU_ITEMS; i++) { if (i == gameStatus.getMenuItem()) { menuItems[i].setFillColor(sf::Color::Green); } else if (i == RESUME && !gameStatus.getIsGameToResume()) { menuItems[RESUME].setFillColor(sf::Color::White); } else { menuItems[i].setFillColor(sf::Color::Red); } } } void MenuView::displayView(sf::RenderWindow *gameWindow) { gameWindow->clear(); for (int i = 0; i < MENU_ITEMS; i++) { gameWindow->draw(menuItems[i]); } gameWindow->display(); } libpmemobj-cpp-1.9/examples/pmpong/MenuView.hpp000066400000000000000000000041721361501571000216540ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_MENUVIEW_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_MENUVIEW_HPP #include "GameConstants.hpp" #include "PongGameStatus.hpp" #include "View.hpp" #include enum menu_items { NEW_GAME, RESUME, SIMULATION, EXIT }; class MenuView : public View { public: MenuView(sf::Font &font); ~MenuView(); virtual void prepareView(PongGameStatus &gameStatus); virtual void displayView(sf::RenderWindow *gameWindow); private: sf::Text menuItems[MENU_ITEMS]; }; #endif /* LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_MENUVIEW_HPP */ libpmemobj-cpp-1.9/examples/pmpong/Paddle.cpp000066400000000000000000000075531361501571000213070ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "Paddle.hpp" #include "Pool.hpp" Paddle::Paddle(int x, int y) { this->x = x; this->y = y; this->points = 0; init(); } Paddle::~Paddle() { } void Paddle::moveUp(int velocity) { if (!(this->y - velocity < SCORE_VIEW_OFFSET + HORIZONAL_LINE_OFFSET + LINE_THICKNESS)) { setY(this->y - velocity); } else if (this->y - velocity < SCORE_VIEW_OFFSET + HORIZONAL_LINE_OFFSET + LINE_THICKNESS) { setY(SCORE_VIEW_OFFSET + HORIZONAL_LINE_OFFSET + LINE_THICKNESS); } } void Paddle::moveDown(int velocity) { if (!(this->y + PADDLE_HEIGHT + velocity > WINDOW_HEIGHT - HORIZONAL_LINE_OFFSET - LINE_THICKNESS)) { setY(this->y + velocity); } else if (this->y + PADDLE_HEIGHT + velocity > WINDOW_HEIGHT - HORIZONAL_LINE_OFFSET - LINE_THICKNESS) { setY(WINDOW_HEIGHT - HORIZONAL_LINE_OFFSET - PADDLE_HEIGHT); } } void Paddle::addPoint() { setPoints(points + 1); } void Paddle::init() { setY(WINDOW_HEIGHT / 2 - (int)getPaddleShape().getSize().y / 2); } void Paddle::adjustPaddleYtoBall(Ball &ball) { if (this->y > ball.getY()) moveUp(PADDLE_VELOCITY_COMPUTER); if (this->y + getPaddleShape().getGlobalBounds().height - ball.getBallShape().getRadius() * 4 < ball.getY()) moveDown(PADDLE_VELOCITY_COMPUTER); } void Paddle::collisionWithBall(Ball &ball, bool increaseBallSpeed) { if (ball.getBallShape().getGlobalBounds().intersects( getPaddleShape().getGlobalBounds())) { ball.setVelocityX(ball.getVelocity()->x * (-1)); if (increaseBallSpeed) ball.increaseVelocity(); } } int Paddle::getX() { return this->x; } int Paddle::getY() { return this->y; } int Paddle::getPoints() { return this->points; } sf::RectangleShape Paddle::getPaddleShape() { sf::RectangleShape shapeToRet; shapeToRet.setSize(sf::Vector2f(PADDLE_WIDTH, PADDLE_HEIGHT)); shapeToRet.setPosition(sf::Vector2f((float)this->x, (float)this->y)); return shapeToRet; } void Paddle::setPoints(int pointsArg) { pmem::obj::transaction::run(Pool::getGamePool()->getPoolToTransaction(), [&] { points = pointsArg; }); } void Paddle::setY(int yArg) { pmem::obj::transaction::run(Pool::getGamePool()->getPoolToTransaction(), [&] { y = yArg; }); } void Paddle::setX(int xArg) { pmem::obj::transaction::run(Pool::getGamePool()->getPoolToTransaction(), [&] { x = xArg; }); } libpmemobj-cpp-1.9/examples/pmpong/Paddle.hpp000066400000000000000000000046221361501571000213060ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_PADDLE_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_PADDLE_HPP #include "Ball.hpp" #include "GameConstants.hpp" #include #include #include #include class Paddle { public: Paddle(); Paddle(int x, int y); ~Paddle(); void moveUp(int velocity); void moveDown(int velocity); void addPoint(); void init(); void adjustPaddleYtoBall(Ball &ball); void collisionWithBall(Ball &ball, bool increaseBallSpeed); int getX(); int getY(); int getPoints(); sf::RectangleShape getPaddleShape(); private: pmem::obj::p y; pmem::obj::p x; pmem::obj::p points; void setPoints(int pointsArg); void setY(int yArg); void setX(int xArg); }; #endif /* LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_PADDLE_HPP */ libpmemobj-cpp-1.9/examples/pmpong/PongGameStatus.cpp000066400000000000000000000126311361501571000230100ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "PongGameStatus.hpp" #include "Pool.hpp" PongGameStatus::PongGameStatus() { player1 = pmem::obj::make_persistent( VERTICAL_LINE_OFFSET + LINE_THICKNESS, WINDOW_HEIGHT / 2); player2 = pmem::obj::make_persistent( WINDOW_WIDTH - VERTICAL_LINE_OFFSET - PADDLE_WIDTH, WINDOW_HEIGHT / 2); ball = pmem::obj::make_persistent(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2); menuItem = 0; isGameToResume = false; actualGameState = game_state::MENU; } PongGameStatus::~PongGameStatus() { pmem::obj::transaction::run( Pool::getGamePool()->getPoolToTransaction(), [&] { pmem::obj::delete_persistent(player1); pmem::obj::delete_persistent(player2); pmem::obj::delete_persistent(ball); }); } void PongGameStatus::startBall(float ballSpeed) { if (ball->getVelocity()->x == 0 && ball->getVelocity()->y == 0) { float x = randomizeFloatValue(1.5, 2.0); ball->setVelocityX(randomizeDirection() ? ballSpeed : -ballSpeed); ball->setVelocityY(randomizeDirection() ? x : -1 * x); } } void PongGameStatus::reset() { ball->init(); player1->init(); player2->init(); } void PongGameStatus::movePaddles() { if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) { player1->moveUp(PADDLE_VELOCITY_PLAYER); } if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S)) { player1->moveDown(PADDLE_VELOCITY_PLAYER); } if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Up)) { player2->moveUp(PADDLE_VELOCITY_PLAYER); } if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Down)) { player2->moveDown(PADDLE_VELOCITY_PLAYER); } } void PongGameStatus::lookForCollisions(bool increaseBallVelocity) { player1->collisionWithBall(*ball, increaseBallVelocity); player2->collisionWithBall(*ball, increaseBallVelocity); ball->collisionWithWindow(); } void PongGameStatus::actualizeStatus() { ball->move(); } void PongGameStatus::simulate() { if (ball->getVelocity()->x > 0) player2->adjustPaddleYtoBall(*ball); if (ball->getVelocity()->x < 0) player1->adjustPaddleYtoBall(*ball); } void PongGameStatus::setMenuItem(int numb) { pmem::obj::transaction::run(Pool::getGamePool()->getPoolToTransaction(), [&] { this->menuItem = numb; }); } void PongGameStatus::setIsGameToResume(bool isGameToRes) { pmem::obj::transaction::run(Pool::getGamePool()->getPoolToTransaction(), [&] { isGameToResume = isGameToRes; }); } void PongGameStatus::setGameState(game_state state) { pmem::obj::transaction::run(Pool::getGamePool()->getPoolToTransaction(), [&] { this->actualGameState = state; }); } int PongGameStatus::getMenuItem() { return this->menuItem; } float PongGameStatus::randomizeFloatValue(float min, float max) { return (min + 1) + (((float)rand()) / (float)RAND_MAX) * (max - (min + 1)); } bool PongGameStatus::score() { if (ball->getBallShape().getPosition().x > WINDOW_WIDTH - VERTICAL_LINE_OFFSET + LINE_THICKNESS - ball->getBallShape().getRadius() * 2) { player1->addPoint(); reset(); return true; } if (ball->getBallShape().getPosition().x < VERTICAL_LINE_OFFSET - LINE_THICKNESS) { player2->addPoint(); reset(); return true; } return false; } bool PongGameStatus::checkIfAnyPlayerWon() { if (getPlayer1()->getPoints() == POINTS_TO_WIN || getPlayer2()->getPoints() == POINTS_TO_WIN) return true; return false; } bool PongGameStatus::randomizeDirection() { static const int shift = static_cast(std::log2(RAND_MAX)); return (rand() >> shift) & 1; } bool PongGameStatus::getIsGameToResume() { return this->isGameToResume; } pmem::obj::persistent_ptr PongGameStatus::getPlayer1() { return this->player1; } pmem::obj::persistent_ptr PongGameStatus::getPlayer2() { return this->player2; } pmem::obj::persistent_ptr PongGameStatus::getBall() { return this->ball; } game_state PongGameStatus::getGameState() { return this->actualGameState; } libpmemobj-cpp-1.9/examples/pmpong/PongGameStatus.hpp000066400000000000000000000056611361501571000230220ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_PONGGAMESTATUS_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_PONGGAMESTATUS_HPP #include "Ball.hpp" #include "GameConstants.hpp" #include "Paddle.hpp" #include #include #include #include enum game_state { MATCH, MENU, GAME_OVER, SIMULATE }; class PongGameStatus { public: PongGameStatus(); ~PongGameStatus(); void startBall(float ballSpeed); void reset(); void movePaddles(); void lookForCollisions(bool increaseBallVelocity); void actualizeStatus(); void simulate(); void setMenuItem(int numb); void setIsGameToResume(bool isGameToRes); void setGameState(game_state state); int getMenuItem(); float randomizeFloatValue(float min, float max); bool score(); bool checkIfAnyPlayerWon(); bool randomizeDirection(); bool getIsGameToResume(); pmem::obj::persistent_ptr getPlayer1(); pmem::obj::persistent_ptr getPlayer2(); pmem::obj::persistent_ptr getBall(); game_state getGameState(); private: pmem::obj::persistent_ptr player1; pmem::obj::persistent_ptr player2; pmem::obj::persistent_ptr ball; pmem::obj::p menuItem; pmem::obj::p isGameToResume; pmem::obj::p actualGameState; }; #endif /* LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_PONGGAMESTATUS_HPP */ libpmemobj-cpp-1.9/examples/pmpong/Pool.cpp000066400000000000000000000051441361501571000210210ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "Pool.hpp" Pool *Pool::pongPool = nullptr; Pool::Pool(const std::string &fileName) { if (pmem::obj::pool::check(fileName, LAYOUT_NAME) == 1) { pool = pmem::obj::pool::open(fileName, LAYOUT_NAME); } else { pool = pmem::obj::pool::create( fileName, LAYOUT_NAME, PMEMOBJ_MIN_POOL * 6); } } Pool::~Pool() { pool.close(); } Pool * Pool::getGamePoolFromFile(const std::string &fileName) { if (pongPool == nullptr) pongPool = new Pool(fileName); return pongPool; } Pool * Pool::getGamePool() { if (pongPool == nullptr) { return getGamePoolFromFile(DEFAULT_POOLFILE_NAME); } return pongPool; } pmem::obj::persistent_ptr Pool::getGameController() { pmem::obj::persistent_ptr root = pool.root(); if (root != nullptr) { if (root->gam == nullptr) pmem::obj::transaction::run(pool, [&] { root->gam = pmem::obj::make_persistent< GameController>(); }); } return root->gam; } pmem::obj::pool & Pool::getPoolToTransaction() { return pool; } libpmemobj-cpp-1.9/examples/pmpong/Pool.hpp000066400000000000000000000045301361501571000210240ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_POOL_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_POOL_HPP #include "GameController.hpp" #include #include #include #include struct GameStruct { public: pmem::obj::persistent_ptr gam; }; class Pool { public: ~Pool(); static Pool *getGamePoolFromFile(const std::string &fileName); static Pool *getGamePool(); pmem::obj::persistent_ptr getGameController(); pmem::obj::pool &getPoolToTransaction(); private: Pool(const std::string &name); static Pool *pongPool; pmem::obj::pool pool; Pool(const Pool &); Pool &operator=(const Pool &); }; #endif /* LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_POOL_HPP */ libpmemobj-cpp-1.9/examples/pmpong/README000066400000000000000000000016711361501571000202650ustar00rootroot00000000000000This directory contains an example application implemented using libpmemobj, it's a game in which all the objects are stored on persistent memory. This means that the game process can be safely killed and then resumed. To launch the game: ./pmpong [mode] Mode option might be skipped if you want to run game with GUI or use -s to run game simulation. The file with the game session will either be created if it doesn't exist or opened if it contains a valid pool. Controls: move left paddle - up and down arrow keys move right paddle - 'w' and 's' pause - esc start ball - space This game demonstrates the usage of the very basics of the libpmemobj C++ bindings. It demonstrates pool management, persistent pointers and transactions. ** DEPENDENCIES: ** In order to build the game you need to have SFML 2.4+ installed. rpm-based systems : sudo dnf install SFML-devel dpkg-based systems: sudo apt-get install libsfml-dev libpmemobj-cpp-1.9/examples/pmpong/View.hpp000066400000000000000000000037261361501571000210330ustar00rootroot00000000000000/* * Copyright 2017-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_VIEW_HPP #define LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_VIEW_HPP #include "GameConstants.hpp" #include "PongGameStatus.hpp" #include class View { public: virtual ~View(){}; virtual void prepareView(PongGameStatus &gameStatus) = 0; virtual void displayView(sf::RenderWindow *gameWindow) = 0; }; #endif /* LIBPMEMOBJ_CPP_EXAMPLES_PMPONG_VIEW_HPP */ libpmemobj-cpp-1.9/examples/queue/000077500000000000000000000000001361501571000172245ustar00rootroot00000000000000libpmemobj-cpp-1.9/examples/queue/CMakeLists.txt000066400000000000000000000041131361501571000217630ustar00rootroot00000000000000# # Copyright 2018-2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cmake_minimum_required(VERSION 3.3) project(queue CXX) set(CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 11) include(FindThreads) if(NOT WIN32) find_package(PkgConfig QUIET) endif() if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMOBJ++ REQUIRED libpmemobj++) else() find_package(LIBPMEMOBJ++ REQUIRED) endif() link_directories(${LIBPMEMOBJ++_LIBRARY_DIRS}) add_executable(queue queue.cpp) target_include_directories(queue PUBLIC ${LIBPMEMOBJ++_INCLUDE_DIRS} . ..) target_link_libraries(queue ${LIBPMEMOBJ++_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) libpmemobj-cpp-1.9/examples/queue/queue.cpp000066400000000000000000000115631361501571000210620ustar00rootroot00000000000000/* * Copyright 2015-2018, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * queue.cpp -- queue example implemented using pmemobj cpp bindings * * Please see pmem.io blog posts for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define LAYOUT "queue" namespace { /* available queue operations */ enum queue_op { UNKNOWN_QUEUE_OP, QUEUE_PUSH, QUEUE_POP, QUEUE_SHOW, MAX_QUEUE_OP, }; /* queue operations strings */ const char *ops_str[MAX_QUEUE_OP] = {"", "push", "pop", "show"}; /* * parse_queue_op -- parses the operation string and returns matching queue_op */ queue_op parse_queue_op(const char *str) { for (int i = 0; i < MAX_QUEUE_OP; ++i) if (strcmp(str, ops_str[i]) == 0) return (queue_op)i; return UNKNOWN_QUEUE_OP; } } using pmem::obj::delete_persistent; using pmem::obj::make_persistent; using pmem::obj::p; using pmem::obj::persistent_ptr; using pmem::obj::pool; using pmem::obj::pool_base; using pmem::obj::transaction; namespace examples { /* * Persistent memory list-based queue * * A simple, not template based, implementation of queue using * libpmemobj C++ API. It demonstrates the basic features of persistent_ptr<> * and p<> classes. */ class pmem_queue { /* entry in the list */ struct pmem_entry { persistent_ptr next; p value; }; public: /* * Inserts a new element at the end of the queue. */ void push(pool_base &pop, uint64_t value) { transaction::run(pop, [&] { auto n = make_persistent(); n->value = value; n->next = nullptr; if (head == nullptr && tail == nullptr) { head = tail = n; } else { tail->next = n; tail = n; } }); } /* * Removes the first element in the queue. */ uint64_t pop(pool_base &pop) { uint64_t ret = 0; transaction::run(pop, [&] { if (head == nullptr) transaction::abort(EINVAL); ret = head->value; auto n = head->next; delete_persistent(head); head = n; if (head == nullptr) tail = nullptr; }); return ret; } /* * Prints the entire contents of the queue. */ void show(void) const { for (auto n = head; n != nullptr; n = n->next) std::cout << n->value << std::endl; } private: persistent_ptr head; persistent_ptr tail; }; } /* namespace examples */ int main(int argc, char *argv[]) { if (argc < 3) { std::cerr << "usage: " << argv[0] << " file-name [push [value]|pop|show]" << std::endl; return 1; } const char *path = argv[1]; queue_op op = parse_queue_op(argv[2]); pool pop; if (file_exists(path) != 0) { pop = pool::create( path, LAYOUT, PMEMOBJ_MIN_POOL, CREATE_MODE_RW); } else { pop = pool::open(path, LAYOUT); } auto q = pop.root(); switch (op) { case QUEUE_PUSH: q->push(pop, std::stoull(argv[3])); break; case QUEUE_POP: std::cout << q->pop(pop) << std::endl; break; case QUEUE_SHOW: q->show(); break; default: throw std::invalid_argument("invalid queue operation"); } pop.close(); return 0; } libpmemobj-cpp-1.9/examples/simplekv/000077500000000000000000000000001361501571000177325ustar00rootroot00000000000000libpmemobj-cpp-1.9/examples/simplekv/CMakeLists.txt000066400000000000000000000040471361501571000224770ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cmake_minimum_required(VERSION 3.3) project(simplekv CXX) set(CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 11) find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMOBJ++ REQUIRED libpmemobj++) else() find_package(LIBPMEMOBJ++ REQUIRED) endif() link_directories(${LIBPMEMOBJ++_LIBRARY_DIRS}) add_executable(simplekv simplekv.cpp) target_include_directories(simplekv PUBLIC ${LIBPMEMOBJ++_INCLUDE_DIRS} . ..) target_link_libraries(simplekv ${LIBPMEMOBJ++_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) libpmemobj-cpp-1.9/examples/simplekv/simplekv.cpp000066400000000000000000000056711361501571000223010ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * simplekv.cpp -- implementation of simple kv which uses vector to hold * values, string as a key and array to hold buckets */ #include #include #include #include "simplekv.hpp" using kv_type = simple_kv; struct root { pmem::obj::persistent_ptr kv; }; void show_usage(char *argv[]) { std::cerr << "usage: " << argv[0] << " file-name [get key|put key value]" << std::endl; } int main(int argc, char *argv[]) { if (argc < 3) { show_usage(argv); return 1; } const char *path = argv[1]; pmem::obj::pool pop; try { pop = pmem::obj::pool::open(path, "simplekv"); auto r = pop.root(); if (r->kv == nullptr) { pmem::obj::transaction::run(pop, [&] { r->kv = pmem::obj::make_persistent(); }); } if (std::string(argv[2]) == "get" && argc == 4) std::cout << r->kv->get(argv[3]) << std::endl; else if (std::string(argv[2]) == "put" && argc == 5) r->kv->put(argv[3], std::stoi(argv[4])); else { show_usage(argv); pop.close(); return 1; } } catch (pmem::pool_error &e) { std::cerr << e.what() << std::endl; std::cerr << "To create pool run: pmempool create obj --layout=simplekv -s 100M path_to_pool" << std::endl; } catch (std::exception &e) { std::cerr << e.what() << std::endl; } pop.close(); return 0; } libpmemobj-cpp-1.9/examples/simplekv/simplekv.hpp000066400000000000000000000070421361501571000223000ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * simplekv.hpp -- implementation of simple kv which uses vector to hold * values, string as a key and array to hold buckets */ #include #include #include #include #include #include #include #include #include #include #include #include /** * Value - type of the value stored in hashmap * N - number of buckets in hashmap */ template class simple_kv { private: using key_type = pmem::obj::string; using bucket_type = pmem::obj::vector>; using bucket_array_type = pmem::obj::array; using value_vector = pmem::obj::vector; bucket_array_type buckets; value_vector values; public: simple_kv() = default; const Value & get(const std::string &key) const { auto index = std::hash{}(key) % N; for (const auto &e : buckets[index]) { if (e.first == key) return values[e.second]; } throw std::out_of_range("no entry in simplekv"); } void put(const std::string &key, const Value &val) { auto index = std::hash{}(key) % N; /* get pool on which this simple_kv resides */ auto pop = pmem::obj::pool_by_vptr(this); /* search for element with specified key - if found * transactionally update its value */ for (const auto &e : buckets[index]) { if (e.first == key) { pmem::obj::transaction::run( pop, [&] { values[e.second] = val; }); return; } } /* if there is no element with specified key, insert new value * to the end of values vector and put reference in proper * bucket transactionally */ pmem::obj::transaction::run(pop, [&] { values.emplace_back(val); buckets[index].emplace_back(key, values.size() - 1); }); } }; libpmemobj-cpp-1.9/examples/simplekv_rebuild/000077500000000000000000000000001361501571000214405ustar00rootroot00000000000000libpmemobj-cpp-1.9/examples/simplekv_rebuild/CMakeLists.txt000066400000000000000000000041171361501571000242030ustar00rootroot00000000000000# # Copyright 2019, Intel Corporation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cmake_minimum_required(VERSION 3.3) project(simplekv_rebuild CXX) set(CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 11) find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(LIBPMEMOBJ++ REQUIRED libpmemobj++) else() find_package(LIBPMEMOBJ++ REQUIRED) endif() link_directories(${LIBPMEMOBJ++_LIBRARY_DIRS}) add_executable(simplekv_rebuild simplekv_rebuild.cpp) target_include_directories(simplekv_rebuild PUBLIC ${LIBPMEMOBJ++_INCLUDE_DIRS} . ..) target_link_libraries(simplekv_rebuild ${LIBPMEMOBJ++_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) libpmemobj-cpp-1.9/examples/simplekv_rebuild/simplekv_rebuild.cpp000066400000000000000000000066321361501571000255130ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * simplekv_rebuild.cpp -- example usage of simple kv which uses vector to hold * values, string as a key and array to hold buckets. This version stores only * vectors of keys and values on persistent memory and rebuilds volatile hashmap * on restart. * * This example expects user input from stdin. */ #include #include #include #include "simplekv_rebuild.hpp" using pmem_kv_type = simple_kv_persistent; using runtime_kv_type = simple_kv_runtime; struct root { pmem::obj::persistent_ptr kv; }; int main(int argc, char *argv[]) { if (argc < 2) { std::cerr << "usage: " << argv[0] << " file-name" << std::endl; return 1; } const char *path = argv[1]; pmem::obj::pool pop; try { pop = pmem::obj::pool::open(path, "simplekv_rebuild"); auto r = pop.root(); if (r->kv == nullptr) { pmem::obj::transaction::run(pop, [&] { r->kv = pmem::obj::make_persistent< pmem_kv_type>(); }); } auto runtime_kv = runtime_kv_type(r->kv.get()); std::cout << "usage: [get key|put key value|exit]" << std::endl; std::string op; while (std::cin >> op) { std::string key; int value; if (op == "get" && std::cin >> key) std::cout << runtime_kv.get(key) << std::endl; else if (op == "put" && std::cin >> key && std::cin >> value) runtime_kv.put(key, value); else if (op == "exit") break; else { std::cout << "usage: [get key|put key value|exit]" << std::endl; continue; } } } catch (pmem::pool_error &e) { std::cerr << e.what() << std::endl; std::cerr << "To create pool run: pmempool create obj --layout=simplekv_rebuild -s 100M path_to_pool" << std::endl; } catch (std::exception &e) { std::cerr << e.what() << std::endl; } pop.close(); return 0; } libpmemobj-cpp-1.9/examples/simplekv_rebuild/simplekv_rebuild.hpp000066400000000000000000000114651361501571000255200ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * simplekv_rebuild.hpp -- implementation of simple kv which uses vector to hold * values, string as a key and array to hold buckets. This version stores only * vectors of keys and values on persistent memory and rebuilds volatile hashmap * on restart. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include template struct simple_kv_persistent; /** * This class is runtime wrapper for simple_kv_peristent. * Value - type of the value stored in hashmap * N - number of buckets in hashmap */ template class simple_kv_runtime { private: using volatile_key_type = std::string; using bucket_entry_type = std::pair; using bucket_type = std::vector; using bucket_array_type = std::array; bucket_array_type buckets; simple_kv_persistent *data; public: simple_kv_runtime(simple_kv_persistent *data) { this->data = data; for (std::size_t i = 0; i < data->values.size(); i++) { auto volatile_key = std::string(data->keys[i].c_str(), data->keys[i].size()); auto index = std::hash{}(volatile_key) % N; buckets[index].emplace_back( bucket_entry_type{volatile_key, i}); } } const Value & get(const std::string &key) const { auto index = std::hash{}(key) % N; for (const auto &e : buckets[index]) { if (e.first == key) return data->values[e.second]; } throw std::out_of_range("no entry in simplekv"); } void put(const std::string &key, const Value &val) { auto index = std::hash{}(key) % N; /* get pool on which persistent data resides */ auto pop = pmem::obj::pool_by_vptr(data); /* search for element with specified key - if found * transactionally update its value */ for (const auto &e : buckets[index]) { if (e.first == key) { pmem::obj::transaction::run(pop, [&] { data->values[e.second] = val; }); return; } } /* if there is no element with specified key, insert new value * to the end of values vector and key to keys vector * transactionally */ pmem::obj::transaction::run(pop, [&] { data->values.emplace_back(val); data->keys.emplace_back(key); }); buckets[index].emplace_back(key, data->values.size() - 1); } }; /** * Class which is stored on persistent memory. * Value - type of the value stored in hashmap * N - number of buckets in hashmap */ template struct simple_kv_persistent { using key_type = pmem::obj::string; using value_vector = pmem::obj::vector; using key_vector = pmem::obj::vector; /* values and keys are stored in separate vectors to optimize * snapshotting. If they were stored as a pair in single vector * entire pair would have to be snapshotted in case of value update */ value_vector values; key_vector keys; }; libpmemobj-cpp-1.9/include/000077500000000000000000000000001361501571000157055ustar00rootroot00000000000000libpmemobj-cpp-1.9/include/libpmemobj++/000077500000000000000000000000001361501571000201535ustar00rootroot00000000000000libpmemobj-cpp-1.9/include/libpmemobj++/README.md000066400000000000000000000140001361501571000214250ustar00rootroot00000000000000libpmemobj-cpp {#mainpage} =========================== This is the C++ API for libpmemobj extended with persistent containers. During the development of libpmemobj, many difficulties were encountered and compromises were made to make the C API as much user-friendly as possible. This is mostly due to the semantics of the C language. Since C++ is a more expressive language, it was natural to try and bridge the gap using native C++ features. There are three main features of the C++ bindings: - the `persistent_ptr<>` smart pointer, - the `transaction`, which comes in two flavours - scoped and closure, - the `p<>` property. The main issue with the C API is the generic PMEMoid and its typed counterpart, the TOID. For them to be conveniently used in transactions, a large set of macros has been defined. This made using the generic pointer easier, yet still unintuitive. In C++, the `persistent_ptr<>` template makes it a lot easier providing well known smart pointer semantics and built-in transactions support. The other drawback of the C API is the transaction semantics. Manual usage of `setjmp` and `jmpbuf` is error prone, so they were once again wrapped in macros. They themselves have issues with undefined values of automatic variables (see the libpmemobj manpage for more details). The transactions defined in the C++ bindings try to fix the inadequacies of their C counterparts. The `p<>`, which is called the _persistent property_, was designed with seamless persistent memory integration in mind. It is designed to be used with basic types within classes, to signify that these members in fact reside in persistent memory and need to be handled appropriately. Please remember to take extra care when using _static class members_. They are not stored in persistent memory, therefore their value will _not_ always be consistent across subsequent executions or compilations of user applications. If you find any issues or have suggestion about these bindings please file an issue in https://github.com/pmem/libpmemobj-cpp/issues. There are also blog articles in https://pmem.io/blog/ which you might find helpful. Have fun! The PMDK team ### Compiler notice ### The C++ bindings require a C++11 compliant compiler, therefore the minimal versions of GCC and Clang are 4.8.1 and 3.3 respectively. However the pmem::obj::transaction::automatic class requires C++17, so you need a more recent version for this to be available (GCC 6.1/Clang 3.7). It is recommended to use these or newer versions of GCC or Clang. A usage of the libpmemobj-cpp containers requires GCC >= 4.9.0 (see explanation in the main README.md file). ### Standard notice ### Please note that the C++11 standard, section 3.8, states that a valid non-trivially default constructible object (in other words, not plain old data) must be properly constructed in the lifetime of the application. Libpmemobj, or any shared memory solution for that matter, does not strictly adhere to that constraint. We believe that in the future, languages that wish to support persistent memory will need to alter their semantics to establish a defined behavior for objects whose lifetimes exceed that of the application. In the meantime, the programs that wish to use persistent memory will need to rely on compiler-defined behavior. Our library, and by extension these bindings, have been extensively tested in g++, clang++ and MSVC++ to make sure that our solution is safe to use and practically speaking implementation defined. The only exception to this rule is the use of polymorphic types, which are notably forbidden when using C++ bindings. ### Important classes/functions ### * Transactional allocations - make_persistent.hpp * Transactional array allocations - make_persistent_array.hpp * Atomic allocations - make_persistent_atomic.hpp * Atomic array allocations - make_persistent_array_atomic.hpp * Resides on persistent memory property - [p](@ref pmem::obj::p) * Persistent smart pointer - [persistent_ptr](@ref pmem::obj::persistent_ptr) * Persistent memory transactions - [transaction](@ref pmem::obj::transaction) * Persistent memory resident mutex - [mutex](@ref pmem::obj::mutex) * Persistent memory pool - [pool](@ref pmem::obj::pool) * Defrag class - [defrag](@ref pmem::obj::defrag) ## Persistent containers ## The C++ standard library containers collection is something that persistent memory programmers may want to use. Containers manage the lifetime of held objects through allocation/creation and deallocation/destruction with the use of allocators. Implementing custom persistent allocator for C++ STL (Standard Template Library) containers has two main downsides: Implementation details: - STL containers do not use algorithms optimal from persistent memory programming point of view. - Persistent memory containers should have durability and consistency properties, while not every STL method guarantees strong exception safety. - Persistent memory containers should be designed with an awareness of fragmentation limitations. Memory layout: - The STL does not guarantee that the container layout will remain unchanged in new library versions. Due to these obstacles, the libpmemobj-cpp contains the set of custom, implemented-from-scratch containers with optimized on-media layouts and algorithms to fully exploit the potential and features of persistent memory. These methods guarantee atomicity, consistency and durability. Besides specific internal implementation details, libpmemobj-cpp persistent memory containers have the well-known STL-like interface and they work with STL algorithms. ### Available containers ### * array with STL-like interface - [pmem::obj::array](@ref pmem::obj::array) * string with STL-like interface - [pmem::obj::string](@ref pmem::obj::basic_string) * vector with STL-like interface - [pmem::obj::vector](@ref pmem::obj::vector) * segment_vector with std::vector-like interface (no STL counterpart) - [pmem::obj::segment_vector](@ref pmem::obj::segment_vector) * concurrent_hash_map (no STL counterpart) - [pmem::obj::concurrent_hash_map](@ref pmem::obj::concurrent_hash_map) libpmemobj-cpp-1.9/include/libpmemobj++/allocation_flag.hpp000066400000000000000000000101431361501571000240010ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file * allocation_flag - defines flags which can be passed to make_persistent */ #ifndef LIBPMEMOBJ_CPP_ALLOCATION_FLAG_HPP #define LIBPMEMOBJ_CPP_ALLOCATION_FLAG_HPP #include namespace pmem { namespace obj { /** * Type of flag which can be passed to make_persistent. * * Allowed flags are: * - allocation_flag::class_id(id) - allocate the object from the allocation * class with id equal to id. * - allocation_flag::no_flush() - skip flush on commit. * - allocation_flag::none() - do not change allocator behaviour. * * Flags can be combined with each other using operator|() */ struct allocation_flag { /** * Emplace constructor. */ explicit allocation_flag(uint64_t val) : value(val) { } /** * Allocate the object from the allocation class with id equal to id. */ static allocation_flag class_id(uint64_t id) { return allocation_flag(POBJ_CLASS_ID(id)); } /** * Skip flush on commit. */ static allocation_flag no_flush() { return allocation_flag(POBJ_XALLOC_NO_FLUSH); } /** * Do not change allocator behaviour. */ static allocation_flag none() { return allocation_flag(0); } /** * Check if flag is set. */ bool is_set(const allocation_flag &rhs) { return (value & rhs.value) != 0; } allocation_flag operator|(const allocation_flag &rhs) { return allocation_flag(value | rhs.value); } uint64_t value; }; /** * Type of flag which can be passed to make_persistent_atomic. * * Allowed flags are: * - allocation_flag_atomic::class_id(id) - allocate the object from the * allocation class with id equal to id. * - allocation_flag_atomic::none() - do not change allocator behaviour. * * Flags can be combined with each other using operator|() */ struct allocation_flag_atomic { /** * Emplace constructor. */ explicit allocation_flag_atomic(uint64_t val) : value(val) { } /** * Allocate the object from the allocation class with id equal to id. */ static allocation_flag_atomic class_id(uint64_t id) { return allocation_flag_atomic(POBJ_CLASS_ID(id)); } /** * Do not change allocator behaviour. */ static allocation_flag_atomic none() { return allocation_flag_atomic(0); } /** * Check if flag is set. */ bool is_set(const allocation_flag_atomic &rhs) { return (value & rhs.value) != 0; } allocation_flag_atomic operator|(const allocation_flag_atomic &rhs) { return allocation_flag_atomic(value | rhs.value); } uint64_t value; }; } /* namespace obj */ } /* namespace pmem */ #endif /* LIBPMEMOBJ_CPP_ALLOCATION_FLAG_HPP */ libpmemobj-cpp-1.9/include/libpmemobj++/allocator.hpp000066400000000000000000000310331361501571000226440ustar00rootroot00000000000000/* * Copyright 2016-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file * Persistent memory aware allocator. (EXPERIMENTAL) */ #ifndef LIBPMEMOBJ_CPP_ALLOCATOR_HPP #define LIBPMEMOBJ_CPP_ALLOCATOR_HPP #include #include #include #include #include #include namespace pmem { namespace obj { /** * Encapsulates object specific allocator functionality. Designed to be used * with C++ allocators. Can be specialized if necessary. */ template class object_traits { public: /* * Important typedefs. */ using value_type = T; using pointer = persistent_ptr; using const_pointer = persistent_ptr; using reference = value_type &; using const_reference = const value_type &; /** * Rebind to a different type. */ template struct rebind { using other = object_traits; }; /** * Defaulted constructor. */ object_traits() = default; /** * Defaulted destructor. */ ~object_traits() = default; /** * Type converting constructor. */ template ::value>::type> explicit object_traits(object_traits const &) { } /** * Create an object at a specific address. * * This should be called only within a transaction. * * @param[in] p the pointer to where the object will be constructed. * @param[in] t the object reference for copy construction. */ void construct(pointer p, const_reference t) { /* construct called on newly allocated objects */ detail::conditional_add_to_tx(p.get()); new (static_cast(p.get())) value_type(t); } /** * Create an object at a specific address. * * This should be called only within a transaction. * * @param[in] p the pointer to where the object will be constructed. * @param[in] args parameters passed to the object's constructor. */ template void construct(pointer p, Args &&... args) { detail::conditional_add_to_tx(p.get()); new (static_cast(p.get())) value_type(std::forward(args)...); } /** * Destroy an object based on a pointer. * * This should be called only within a transaction. * * @param[in] p the pointer to the object to be destroyed. */ void destroy(pointer p) { /* XXX should we allow modifications outside of tx? */ if (pmemobj_tx_stage() == TX_STAGE_WORK) { pmemobj_tx_add_range_direct((void *)p.get(), sizeof(p)); } detail::destroy(*p); } }; /** * Object traits specialization for the void type. Designed to be used * with C++ allocators. Can be specialized if necessary. */ template <> class object_traits { public: /* * Important typedefs. */ using value_type = void; using pointer = persistent_ptr; /** * Rebind to a different type. */ template struct rebind { using other = object_traits; }; /** * Defaulted constructor. */ object_traits() = default; /** * Defaulted destructor. */ ~object_traits() = default; /** * Type converting constructor. */ template explicit object_traits(object_traits const &) { } }; /** * The allocation policy template for a given type. * * Can be specialized for a given type. Designed to be used with C++ allocators. * Can be specialized if necessary. */ template class standard_alloc_policy { public: /* * Important typedefs. */ using value_type = T; using pointer = persistent_ptr; using const_void_pointer = persistent_ptr; using size_type = std::size_t; using bool_type = bool; /** * Rebind to a different type. */ template struct rebind { using other = standard_alloc_policy; }; /** * Defaulted constructor. */ standard_alloc_policy() = default; /** * Defaulted destructor. */ ~standard_alloc_policy() = default; /** * Explicit copy constructor. */ explicit standard_alloc_policy(standard_alloc_policy const &) { } /** * Type converting constructor. */ template ::value>::type> explicit standard_alloc_policy(standard_alloc_policy const &) { } /** * Allocate storage for cnt objects of type T. Does not construct the * objects. * * @param[in] cnt the number of objects to allocate memory for. * * @throw transaction_scope_error if called outside of a transaction. */ pointer allocate(size_type cnt, const_void_pointer = 0) { if (pmemobj_tx_stage() != TX_STAGE_WORK) throw pmem::transaction_scope_error( "refusing to allocate memory outside of transaction scope"); /* allocate raw memory, no object construction */ return pmemobj_tx_alloc(sizeof(value_type) * cnt, detail::type_num()); } /** * Deallocates storage pointed to p, which must be a value returned by * a previous call to allocate that has not been invalidated by an * intervening call to deallocate. * * @param[in] p pointer to the memory to be deallocated. */ void deallocate(pointer p, size_type = 0) { if (pmemobj_tx_stage() != TX_STAGE_WORK) throw pmem::transaction_scope_error( "refusing to free memory outside of transaction scope"); if (pmemobj_tx_free(*p.raw_ptr()) != 0) throw pmem::transaction_free_error( "failed to delete persistent memory object") .with_pmemobj_errormsg(); } /** * The largest value that can meaningfully be passed to allocate(). * * @return largest value that can be passed to allocate. */ size_type max_size() const { return PMEMOBJ_MAX_ALLOC_SIZE / sizeof(value_type); } }; /** * Void specialization of the standard allocation policy. */ template <> class standard_alloc_policy { public: /* * Important typedefs. */ using value_type = void; using pointer = persistent_ptr; using const_pointer = persistent_ptr; using reference = value_type; using const_reference = const value_type; using size_type = std::size_t; using bool_type = bool; /** * Rebind to a different type. */ template struct rebind { using other = standard_alloc_policy; }; /** * Defaulted constructor. */ standard_alloc_policy() = default; /** * Defaulted destructor. */ ~standard_alloc_policy() = default; /** * Explicit copy constructor. */ explicit standard_alloc_policy(standard_alloc_policy const &) { } /** * Type converting constructor. */ template explicit standard_alloc_policy(standard_alloc_policy const &) { } /** * Allocate storage for cnt bytes. Assumes sizeof(void) = 1. * * @param[in] cnt the number of bytes to be allocated. * * @throw transaction_scope_error if called outside of a transaction. */ pointer allocate(size_type cnt, const_pointer = 0) { if (pmemobj_tx_stage() != TX_STAGE_WORK) throw pmem::transaction_scope_error( "refusing to allocate memory outside of transaction scope"); /* allocate raw memory, no object construction */ return pmemobj_tx_alloc(1 /* void size */ * cnt, 0); } /** * Deallocates storage pointed to p, which must be a value returned by * a previous call to allocate that has not been invalidated by an * intervening call to deallocate. * * @param[in] p pointer to the memory to be deallocated. */ void deallocate(pointer p, size_type = 0) { if (pmemobj_tx_stage() != TX_STAGE_WORK) throw pmem::transaction_scope_error( "refusing to free memory outside of transaction scope"); if (pmemobj_tx_free(p.raw()) != 0) throw pmem::transaction_free_error( "failed to delete persistent memory object") .with_pmemobj_errormsg(); } /** * The largest value that can meaningfully be passed to allocate(). * * @return largest value that can be passed to allocate. */ size_type max_size() const { return PMEMOBJ_MAX_ALLOC_SIZE; } }; /** * Determines if memory from another allocator can be deallocated from this one. * * @return true. */ template inline bool operator==(standard_alloc_policy const &, standard_alloc_policy const &) { return true; } /** * Determines if memory from another allocator can be deallocated from this one. * * @return false. */ template inline bool operator==(standard_alloc_policy const &, OtherAllocator const &) { return false; } /** * (EXPERIMENTAL) Encapsulates the information about the persistent * memory allocation model using PMDK's libpmemobj. This information includes * the knowledge of the pointer type, their difference type, the type of the * size of objects in this allocation model as well as memory allocation and * deallocation primitives. */ template , typename Traits = object_traits> class allocator : public Policy, public Traits { private: /* * private typedefs */ using AllocationPolicy = Policy; using TTraits = Traits; public: /* * Important typedefs. */ using size_type = typename AllocationPolicy::size_type; using pointer = typename AllocationPolicy::pointer; using value_type = typename AllocationPolicy::value_type; /** * Rebind to a different type. */ template struct rebind { using other = allocator< U, typename AllocationPolicy::template rebind::other, typename TTraits::template rebind::other>; }; /** * Defaulted constructor. */ allocator() = default; /** * Defaulted destructor. */ ~allocator() = default; /** * Explicit copy constructor. */ explicit allocator(allocator const &rhs) : Policy(rhs), Traits(rhs) { } /** * Type converting constructor. */ template explicit allocator(allocator const &) { } /** * Type converting constructor. */ template explicit allocator(allocator const &rhs) : Policy(rhs), Traits(rhs) { } }; /** * Determines if memory from another allocator can be deallocated from this one. * * @param[in] lhs left hand side allocator. * @param[in] rhs right hand side allocator. * * @return true if allocators are equivalent in terms of deallocation, false * otherwise. */ template inline bool operator==(const allocator &lhs, const allocator &rhs) { return operator==(static_cast(lhs), static_cast(rhs)); } /** * Determines if memory from another allocator can be deallocated from this one. * * @param[in] lhs left hand side allocator. * @param[in] rhs right hand side allocator. * * @return false if allocators are equivalent in terms of deallocation, true * otherwise. */ template inline bool operator!=(const allocator &lhs, const OtherAllocator &rhs) { return !operator==(lhs, rhs); } } /* namespace obj */ } /* namespace pmem */ #endif /* LIBPMEMOBJ_CPP_ALLOCATOR_HPP */ libpmemobj-cpp-1.9/include/libpmemobj++/condition_variable.hpp000066400000000000000000000426401361501571000245250ustar00rootroot00000000000000/* * Copyright 2016-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file * Pmem-resident condition variable. */ #ifndef LIBPMEMOBJ_CPP_CONDVARIABLE_HPP #define LIBPMEMOBJ_CPP_CONDVARIABLE_HPP #include #include #include #include #include namespace pmem { namespace obj { /** * Persistent memory resident condition variable. * * This class is an implementation of a PMEM-resident condition * variable which mimics in behavior the C++11 std::condition_variable. The * typical usage example would be: * @snippet doc_snippets/mutex.cpp cond_var_example */ class condition_variable { typedef std::chrono::system_clock clock_type; public: /** The handle typedef to the underlying basic type. */ typedef PMEMcond *native_handle_type; /** * Default constructor. * * @throw lock_error when the condition_variable is not from persistent * memory. */ condition_variable() { PMEMobjpool *pop; if ((pop = pmemobj_pool_by_ptr(&pcond)) == nullptr) throw pmem::lock_error( 1, std::generic_category(), "Persistent condition variable not from persistent memory."); pmemobj_cond_zero(pop, &pcond); } /** * Defaulted destructor. */ ~condition_variable() = default; /** * Notify and unblock one thread waiting on `*this` condition. * * Does nothing when no threads are waiting. It is unspecified * which thread is selected for unblocking. * * @throw lock_error when the signal fails on the #pcond. */ void notify_one() { PMEMobjpool *pop = pmemobj_pool_by_ptr(this); if (int ret = pmemobj_cond_signal(pop, &this->pcond)) throw pmem::lock_error( ret, std::system_category(), "Error notifying one on a condition variable.") .with_pmemobj_errormsg(); } /** * Notify and unblock all threads waiting on `*this` condition. * * Does nothing when no threads are waiting. */ void notify_all() { PMEMobjpool *pop = pmemobj_pool_by_ptr(this); if (int ret = pmemobj_cond_broadcast(pop, &this->pcond)) throw pmem::lock_error( ret, std::system_category(), "Error notifying all on a condition variable.") .with_pmemobj_errormsg(); } /** * Makes the current thread block until the condition variable * is notified or it is woken up by some other measure. * * This releases the lock, blocks the current thread and adds * it to the list of threads waiting on `*this` condition * variable. The lock needs to be acquired and owned by the * calling thread. The lock is automatically reacquired after * the call to wait. * * @param[in,out] lock a PMEM-resident obj::mutex. * * @throw lock_error when unlocking the lock or waiting on * #pcond fails. */ void wait(mutex &lock) { this->wait_impl(lock); } /** * Makes the current thread block until the condition variable * is notified or it is woken up by some other measure. * * This releases the lock, blocks the current thread and adds * it to the list of threads waiting on `*this` condition * variable. The lock needs to be acquired and owned by the * calling thread. The lock is automatically reacquired after * the call to wait. * * @param[in,out] lock a Lock object which meets the * BasicLockableConcept. Needs to be based on a PMEM-resident * obj::mutex. * * @throw lock_error when unlocking the lock or waiting * on #pcond fails. */ template void wait(Lock &lock) { this->wait_impl(*lock.mutex()); } /** * Makes the current thread block until the condition variable * is notified. * * This releases the lock, blocks the current thread and adds * it to the list of threads waiting on `*this` condition * variable. The lock needs to be acquired and owned by the * calling thread. The lock is automatically reacquired after * the call to wait. * This version is immune to spurious wake ups due to the * provided predicate. * * @param[in,out] lock a PMEM-resident obj::mutex. * @param[in] pred predicate which returns `false` if waiting is * to be continued. * * @throw lock_error when unlocking the lock or waiting on * #pcond fails. */ template void wait(mutex &lock, Predicate pred) { this->wait_impl(lock, std::move(pred)); } /** * Makes the current thread block until the condition variable * is notified. * * This releases the lock, blocks the current thread and adds * it to the list of threads waiting on `*this` condition * variable. The lock needs to be acquired and owned by the * calling thread. The lock is automatically reacquired after * the call to wait. * This version is immune to spurious wake ups due to the * provided predicate. * * @param[in,out] lock a Lock object which meets the * BasicLockableConcept. Needs to be based on a PMEM-resident * obj::mutex. * @param[in] pred predicate which returns `false` if waiting is * to be continued. * * @throw lock_error when unlocking the lock or waiting on * #pcond fails. */ template void wait(Lock &lock, Predicate pred) { this->wait_impl(*lock.mutex(), std::move(pred)); } /** * Makes the current thread block until the condition variable * is notified, a specific time is reached or it is woken up by * some other measure. * * This releases the lock, blocks the current thread and adds * it to the list of threads waiting on `*this` condition * variable. The lock needs to be acquired and owned by the * calling thread. The lock is automatically reacquired after * the call to wait. * * @param[in,out] lock a PMEM-resident obj::mutex. * @param[in] timeout a specific point in time, which when * reached unblocks the thread. * * @return std::cv_status::timeout on timeout, * std::cv_status::no_timeout otherwise. * * @throw lock_error when unlocking the lock or waiting on * #pcond fails. */ template std::cv_status wait_until(mutex &lock, const std::chrono::time_point &timeout) { return this->wait_until_impl(lock, timeout); } /** * Makes the current thread block until the condition variable * is notified, a specific time is reached or it is woken up by * some other measure. * * This releases the lock, blocks the current thread and adds * it to the list of threads waiting on `*this` condition * variable. The lock needs to be acquired and owned by the * calling thread. The lock is automatically reacquired after * the call to wait. * * @param[in,out] lock a Lock object which meets the * BasicLockableConcept. Needs to be based on a PMEM-resident * obj::mutex. * @param[in] timeout a specific point in time, which when * reached unblocks the thread. * * @return std::cv_status::timeout on timeout, * std::cv_status::no_timeout otherwise. * * @throw lock_error when unlocking the lock or waiting on * #pcond fails. */ template std::cv_status wait_until(Lock &lock, const std::chrono::time_point &timeout) { return this->wait_until_impl(*lock.mutex(), timeout); } /** * Makes the current thread block until the condition variable * is notified or a specific time is reached. * * This releases the lock, blocks the current thread and adds * it to the list of threads waiting on `*this` condition * variable. The lock needs to be acquired and owned by the * calling thread. The lock is automatically reacquired after * the call to wait. * * @param[in,out] lock a PMEM-resident obj::mutex. * @param[in] timeout a specific point in time, which when * reached unblocks the thread. * @param[in] pred predicate which returns `false` if waiting is * to be continued. * * @return `false` if pred evaluates to `false` after timeout * expired, otherwise `true`. * * @throw lock_error when unlocking the lock or waiting on * #pcond fails. */ template bool wait_until(mutex &lock, const std::chrono::time_point &timeout, Predicate pred) { return this->wait_until_impl(lock, timeout, std::move(pred)); } /** * Makes the current thread block until the condition variable * is notified or a specific time is reached. * * This releases the lock, blocks the current thread and adds * it to the list of threads waiting on `*this` condition * variable. The lock needs to be acquired and owned by the * calling thread. The lock is automatically reacquired after * the call to wait. * * @param[in,out] lock a Lock object which meets the * BasicLockableConcept. Needs to be based on a PMEM-resident * obj::mutex. * @param[in] timeout a specific point in time, which when * reached unblocks the thread. * @param[in] pred predicate which returns `false` if waiting is * to be continued. * * @return `false` if pred evaluates to `false` after timeout * expired, otherwise `true`. * * @throw lock_error when unlocking the lock or waiting on * #pcond fails. */ template bool wait_until(Lock &lock, const std::chrono::time_point &timeout, Predicate pred) { return this->wait_until_impl(*lock.mutex(), timeout, std::move(pred)); } /** * Makes the current thread block until the condition variable * is notified, the specified amount of time passes or it is * woken up by some other measure. * * This releases the lock, blocks the current thread and adds * it to the list of threads waiting on `*this` condition * variable. The lock needs to be acquired and owned by the * calling thread. The lock is automatically reacquired after * the call to wait. * * @param[in,out] lock a Lock object which meets the * BasicLockableConcept. Needs to be based on a PMEM-resident * obj::mutex. * @param[in] rel_time a specific duration, which when * expired unblocks the thread. * * @return std::cv_status::timeout on timeout, * std::cv_status::no_timeout otherwise. * * @throw lock_error when unlocking the lock or waiting on * #pcond fails. */ template std::cv_status wait_for(Lock &lock, const std::chrono::duration &rel_time) { return this->wait_until_impl(*lock.mutex(), clock_type::now() + rel_time); } /** * Makes the current thread block until the condition variable * is notified or the specified amount of time passes. * * This releases the lock, blocks the current thread and adds * it to the list of threads waiting on `*this` condition * variable. The lock needs to be acquired and owned by the * calling thread. The lock is automatically reacquired after * the call to wait. * * @param[in,out] lock a Lock object which meets the * BasicLockableConcept. Needs to be based on a PMEM-resident * obj::mutex. * @param[in] rel_time a specific duration, which when * expired unblocks the thread. * @param[in] pred predicate which returns `false` if waiting is * to be continued. * * @return `false` if pred evaluates to `false` after timeout * expired, otherwise `true`. * * @throw lock_error when unlocking the lock or waiting on * #pcond fails. */ template bool wait_for(Lock &lock, const std::chrono::duration &rel_time, Predicate pred) { return this->wait_until_impl(*lock.mutex(), clock_type::now() + rel_time, std::move(pred)); } /** * Makes the current thread block until the condition variable * is notified, the specified amount of time passes or it is * woken up by some other measure. * * This releases the lock, blocks the current thread and adds * it to the list of threads waiting on `*this` condition * variable. The lock needs to be acquired and owned by the * calling thread. The lock is automatically reacquired after * the call to wait. * * @param[in,out] lock a PMEM-resident obj::mutex. * @param[in] rel_time a specific duration, which when * expired unblocks the thread. * * @return std::cv_status::timeout on timeout, * std::cv_status::no_timeout otherwise. * * @throw lock_error when unlocking the lock or waiting on * #pcond fails. */ template std::cv_status wait_for(mutex &lock, const std::chrono::duration &rel_time) { return this->wait_until_impl(lock, clock_type::now() + rel_time); } /** * Makes the current thread block until the condition variable * is notified or the specified amount of time passes. * * This releases the lock, blocks the current thread and adds * it to the list of threads waiting on `*this` condition * variable. The lock needs to be acquired and owned by the * calling thread. The lock is automatically reacquired after * the call to wait. * * @param[in,out] lock a PMEM-resident obj::mutex. * @param[in] rel_time a specific duration, which when * expired unblocks the thread. * @param[in] pred predicate which returns `false` if waiting is * to be continued. * * @return `false` if pred evaluates to `false` after timeout * expired, otherwise `true`. * * @throw lock_error when unlocking the lock or waiting on * #pcond fails. */ template bool wait_for(mutex &lock, const std::chrono::duration &rel_time, Predicate pred) { return this->wait_until_impl(lock, clock_type::now() + rel_time, std::move(pred)); } /** * Access a native handle to this condition variable. * * @return a pointer to PMEMcond. */ native_handle_type native_handle() noexcept { return &this->pcond; } /** * Deleted assignment operator. */ condition_variable &operator=(const condition_variable &) = delete; /** * Deleted copy constructor. */ condition_variable(const condition_variable &) = delete; private: /** * Internal implementation of the wait call. */ void wait_impl(mutex &lock) { PMEMobjpool *pop = pmemobj_pool_by_ptr(this); if (int ret = pmemobj_cond_wait(pop, &this->pcond, lock.native_handle())) throw pmem::lock_error( ret, std::system_category(), "Error waiting on a condition variable.") .with_pmemobj_errormsg(); } /** * Internal implementation of the wait call. */ template void wait_impl(mutex &lock, Predicate pred) { while (!pred()) this->wait(lock); } /** * Internal implementation of the wait_until call. */ template std::cv_status wait_until_impl( mutex &lock, const std::chrono::time_point &abs_timeout) { PMEMobjpool *pop = pmemobj_pool_by_ptr(this); /* convert to my clock */ const typename Clock::time_point their_now = Clock::now(); const clock_type::time_point my_now = clock_type::now(); const auto delta = abs_timeout - their_now; const auto my_rel = my_now + delta; struct timespec ts = detail::timepoint_to_timespec(my_rel); auto ret = pmemobj_cond_timedwait(pop, &this->pcond, lock.native_handle(), &ts); if (ret == 0) return std::cv_status::no_timeout; else if (ret == ETIMEDOUT) return std::cv_status::timeout; else throw pmem::lock_error( ret, std::system_category(), "Error waiting on a condition variable.") .with_pmemobj_errormsg(); } /** * Internal implementation of the wait_until call. */ template bool wait_until_impl( mutex &lock, const std::chrono::time_point &abs_timeout, Predicate pred) { while (!pred()) if (this->wait_until_impl(lock, abs_timeout) == std::cv_status::timeout) return pred(); return true; } /** A POSIX style PMEM-resident condition variable.*/ PMEMcond pcond; }; } /* namespace obj */ } /* namespace pmem */ #endif /* LIBPMEMOBJ_CPP_CONDVARIABLE_HPP */ libpmemobj-cpp-1.9/include/libpmemobj++/container/000077500000000000000000000000001361501571000221355ustar00rootroot00000000000000libpmemobj-cpp-1.9/include/libpmemobj++/container/array.hpp000066400000000000000000000505561361501571000237770ustar00rootroot00000000000000/* * Copyright 2018-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file * Array container with std::array compatible interface. */ #ifndef LIBPMEMOBJ_CPP_ARRAY_HPP #define LIBPMEMOBJ_CPP_ARRAY_HPP #include #include #include #include #include #include #include #include #include namespace pmem { namespace obj { /** * pmem::obj::array - persistent container with std::array compatible interface. * * pmem::obj::array can only be stored on pmem. Creating array on * stack will result with "pool_error" exception. * * All methods which allow write access to specific element will add it to an * active transaction. * * All methods which return non-const pointer to raw data add entire array * to a transaction. * * When a non-const iterator is returned it adds part of the array * to a transaction while traversing. */ template struct array { template struct standard_array_traits { using type = Y[N]; }; /* zero-sized array support */ template struct standard_array_traits { struct _alignment_struct { Y _data[1]; }; struct alignas(_alignment_struct) type { char _data[sizeof(_alignment_struct)]; }; }; /* Member types */ using value_type = T; using pointer = value_type *; using const_pointer = const value_type *; using reference = value_type &; using const_reference = const value_type &; using iterator = pmem::detail::basic_contiguous_iterator; using const_iterator = const_pointer; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using range_snapshotting_iterator = pmem::detail::range_snapshotting_iterator; /* Underlying array */ typename standard_array_traits::type _data; /** * Defaulted constructor. */ array() = default; /** * Defaulted copy constructor. */ array(const array &) = default; /** * Defaulted move constructor. * * Performs member-wise move but do NOT add moved-from array to the * transaction. */ array(array &&) = default; /** * Copy assignment operator - perform assignment from other * pmem::obj::array. * * This function creates a transaction internally. * * @throw transaction_error when adding the object to the * transaction failed. * @throw pmem::pool_error if an object is not in persistent memory. */ array & operator=(const array &other) { /* * _get_pool should be called before self assignment check to * maintain the same behaviour for all arguments. */ auto pop = _get_pool(); if (this == &other) return *this; transaction::run(pop, [&] { detail::conditional_add_to_tx( this, 1, POBJ_XADD_ASSUME_INITIALIZED); std::copy(other.cbegin(), other.cend(), _get_data()); }); return *this; } /** * Move assignment operator - perform move assignment from other * pmem::obj::array. * * This function creates a transaction internally. * * @throw transaction_error when adding the object to the * transaction failed. * @throw pmem::pool_error if an object is not in persistent memory. */ array & operator=(array &&other) { /* * _get_pool should be called before self assignment check to * maintain the same behaviour for all arguments. */ auto pop = _get_pool(); if (this == &other) return *this; transaction::run(pop, [&] { detail::conditional_add_to_tx( this, 1, POBJ_XADD_ASSUME_INITIALIZED); detail::conditional_add_to_tx( &other, 1, POBJ_XADD_ASSUME_INITIALIZED); std::move(other._get_data(), other._get_data() + size(), _get_data()); }); return *this; } /** * Access element at specific index and add it to a transaction. * * @throw std::out_of_range if index is out of bound. * @throw transaction_error when adding the object to the * transaction failed. */ reference at(size_type n) { if (n >= N) throw std::out_of_range("array::at"); detail::conditional_add_to_tx(_get_data() + n, 1, POBJ_XADD_ASSUME_INITIALIZED); return _get_data()[n]; } /** * Access element at specific index. * * @throw std::out_of_range if index is out of bound. */ const_reference at(size_type n) const { if (n >= N) throw std::out_of_range("array::at"); return _get_data()[n]; } /** * Access element at specific index. * * @throw std::out_of_range if index is out of bound. */ const_reference const_at(size_type n) const { if (n >= N) throw std::out_of_range("array::const_at"); return _get_data()[n]; } /** * Access element at specific index and add it to a transaction. * No bounds checking is performed. * * @throw transaction_error when adding the object to the * transaction failed. */ reference operator[](size_type n) { detail::conditional_add_to_tx(_get_data() + n, 1, POBJ_XADD_ASSUME_INITIALIZED); return _get_data()[n]; } /** * Access element at specific index. * No bounds checking is performed. */ const_reference operator[](size_type n) const { return _get_data()[n]; } /** * Returns raw pointer to the underlying data * and adds entire array to a transaction. * * @throw transaction_error when adding the object to the * transaction failed. */ T * data() { detail::conditional_add_to_tx(this, 1, POBJ_XADD_ASSUME_INITIALIZED); return _get_data(); } /** * Returns const raw pointer to the underlying data. */ const T * data() const noexcept { return _get_data(); } /** * Returns const raw pointer to the underlying data. */ const T * cdata() const noexcept { return _get_data(); } /** * Returns an iterator to the beginning. * * @throw transaction_error when adding the object to the * transaction failed. */ iterator begin() { return iterator(_get_data()); } /** * Returns an iterator to the end. * * @throw transaction_error when adding the object to the * transaction failed. */ iterator end() { return iterator(_get_data() + size()); } /** * Returns const iterator to the beginning. */ const_iterator begin() const noexcept { return const_iterator(_get_data()); } /** * Returns const iterator to the beginning. */ const_iterator cbegin() const noexcept { return const_iterator(_get_data()); } /** * Returns a const iterator to the end. */ const_iterator end() const noexcept { return const_iterator(_get_data() + size()); } /** * Returns a const iterator to the end. */ const_iterator cend() const noexcept { return const_iterator(_get_data() + size()); } /** * Returns a reverse iterator to the beginning. * * @throw transaction_error when adding the object to the * transaction failed. */ reverse_iterator rbegin() { return reverse_iterator(iterator(_get_data() + size())); } /** * Returns a reverse iterator to the end. * * @throw transaction_error when adding the object to the * transaction failed. */ reverse_iterator rend() { return reverse_iterator(iterator(_get_data())); } /** * Returns a const reverse iterator to the beginning. */ const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(cend()); } /** * Returns a const reverse iterator to the beginning. */ const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } /** * Returns a const reverse iterator to the end. */ const_reverse_iterator rend() const noexcept { return const_reverse_iterator(cbegin()); } /** * Returns a const reverse iterator to the beginning. */ const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } /** * Access the first element and add this element to a transaction. * * @throw transaction_error when adding the object to the * transaction failed. */ reference front() { detail::conditional_add_to_tx(_get_data(), 1, POBJ_XADD_ASSUME_INITIALIZED); return _get_data()[0]; } /** * Access the last element and add this element to a transaction. * * @throw transaction_error when adding the object to the * transaction failed. */ reference back() { detail::conditional_add_to_tx(&_get_data()[size() - 1], 1, POBJ_XADD_ASSUME_INITIALIZED); return _get_data()[size() - 1]; } /** * Access the first element. */ const_reference front() const { return _get_data()[0]; } /** * Access the first element. */ const_reference cfront() const { return _get_data()[0]; } /** * Access the last element. */ const_reference back() const { return _get_data()[size() - 1]; } /** * Access the last element. */ const_reference cback() const { return _get_data()[size() - 1]; } /** * Returns slice and snapshots requested range. * * @param[in] start start index of requested range. * @param[in] n number of elements in range. * * @return slice from start to start + n. * * @throw std::out_of_range if any element of the range would be * outside of the array. */ slice range(size_type start, size_type n) { if (start + n > N) throw std::out_of_range("array::range"); detail::conditional_add_to_tx(_get_data() + start, n, POBJ_XADD_ASSUME_INITIALIZED); return {_get_data() + start, _get_data() + start + n}; } /** * Returns slice. * * @param[in] start start index of requested range. * @param[in] n number of elements in range. * @param[in] snapshot_size number of elements which should be * snapshotted in a bulk while traversing this slice. * If provided value is larger or equal to n, entire range is * added to a transaction. If value is equal to 0 no snapshotting * happens. * * @return slice from start to start + n. * * @throw std::out_of_range if any element of the range would be * outside of the array. */ slice range(size_type start, size_type n, size_type snapshot_size) { if (start + n > N) throw std::out_of_range("array::range"); if (snapshot_size > n) snapshot_size = n; return {range_snapshotting_iterator(_get_data() + start, _get_data() + start, n, snapshot_size), range_snapshotting_iterator(_get_data() + start + n, _get_data() + start, n, snapshot_size)}; } /** * Returns const slice. * * @param[in] start start index of requested range. * @param[in] n number of elements in range. * * @return slice from start to start + n. * * @throw std::out_of_range if any element of the range would be * outside of the array. */ slice range(size_type start, size_type n) const { if (start + n > N) throw std::out_of_range("array::range"); return {const_iterator(_get_data() + start), const_iterator(_get_data() + start + n)}; } /** * Returns const slice. * * @param[in] start start index of requested range. * @param[in] n number of elements in range. * * @return slice from start to start + n. * * @throw std::out_of_range if any element of the range would be * outside of the array. */ slice crange(size_type start, size_type n) const { if (start + n > N) throw std::out_of_range("array::crange"); return {const_iterator(_get_data() + start), const_iterator(_get_data() + start + n)}; } /** * Returns size of the array. */ constexpr size_type size() const noexcept { return N; } /** * Returns the maximum size of the array. */ constexpr size_type max_size() const noexcept { return N; } /** * Checks whether array is empty. */ constexpr bool empty() const noexcept { return size() == 0; } /** * Fills array with specified value inside internal transaction. * * @throw transaction_error when adding the object to the * transaction failed. * @throw pmem::pool_error if an object is not in persistent memory. */ void fill(const_reference value) { auto pop = _get_pool(); transaction::run(pop, [&] { detail::conditional_add_to_tx( this, 1, POBJ_XADD_ASSUME_INITIALIZED); std::fill(_get_data(), _get_data() + size(), value); }); } /** * Swaps content with other array's content inside internal transaction. * * @throw transaction_error when adding the object to the * transaction failed. * @throw pmem::pool_error if an object is not in persistent memory. */ template typename std::enable_if::type swap(array &other) { /* * _get_pool should be called before self assignment check to * maintain the same behaviour for all arguments. */ auto pop = _get_pool(); if (this == &other) return; transaction::run(pop, [&] { detail::conditional_add_to_tx( this, 1, POBJ_XADD_ASSUME_INITIALIZED); detail::conditional_add_to_tx( &other, 1, POBJ_XADD_ASSUME_INITIALIZED); std::swap_ranges(_get_data(), _get_data() + size(), other._get_data()); }); } /** * Swap for zero-sized array. */ template typename std::enable_if::type swap(array &other) { static_assert(!std::is_const::value, "cannot swap zero-sized array of type 'const T'"); } private: /** * Support for non-zero sized array. */ template typename std::enable_if::type _get_data() { return this->_data; } /** * Support for non-zero sized array. */ template typename std::enable_if::type _get_data() const { return this->_data; } /** * Support for zero sized array. * Return value is a unique address (address of the array itself); */ template typename std::enable_if::type _get_data() { return reinterpret_cast(&this->_data); } /** * Support for zero sized array. */ template typename std::enable_if::type _get_data() const { return reinterpret_cast(&this->_data); } /** * Check whether object is on pmem and return pool_base instance. * * @throw pmem::pool_error if an object is not in persistent memory. */ pool_base _get_pool() const { auto pop = pmemobj_pool_by_ptr(this); if (pop == nullptr) throw pmem::pool_error( "Object outside of pmemobj pool."); return pool_base(pop); } }; /** * Non-member equal operator. */ template inline bool operator==(const array &lhs, const array &rhs) { return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin()); } /** * Non-member not-equal operator. */ template inline bool operator!=(const array &lhs, const array &rhs) { return !(lhs == rhs); } /** * Non-member less than operator. */ template inline bool operator<(const array &lhs, const array &rhs) { return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()); } /** * Non-member greater than operator. */ template inline bool operator>(const array &lhs, const array &rhs) { return rhs < lhs; } /** * Non-member greater or equal operator. */ template inline bool operator>=(const array &lhs, const array &rhs) { return !(lhs < rhs); } /** * Non-member less or equal operator. */ template inline bool operator<=(const array &lhs, const array &rhs) { return !(lhs > rhs); } /** * Non-member cbegin. */ template typename pmem::obj::array::const_iterator cbegin(const pmem::obj::array &a) { return a.cbegin(); } /** * Non-member cend. */ template typename pmem::obj::array::const_iterator cend(const pmem::obj::array &a) { return a.cend(); } /** * Non-member crbegin. */ template typename pmem::obj::array::const_reverse_iterator crbegin(const pmem::obj::array &a) { return a.crbegin(); } /** * Non-member crend. */ template typename pmem::obj::array::const_reverse_iterator crend(const pmem::obj::array &a) { return a.crend(); } /** * Non-member begin. */ template typename pmem::obj::array::iterator begin(pmem::obj::array &a) { return a.begin(); } /** * Non-member begin. */ template typename pmem::obj::array::const_iterator begin(const pmem::obj::array &a) { return a.begin(); } /** * Non-member end. */ template typename pmem::obj::array::iterator end(pmem::obj::array &a) { return a.end(); } /** * Non-member end. */ template typename pmem::obj::array::const_iterator end(const pmem::obj::array &a) { return a.end(); } /** * Non-member rbegin. */ template typename pmem::obj::array::reverse_iterator rbegin(pmem::obj::array &a) { return a.rbegin(); } /** * Non-member rbegin. */ template typename pmem::obj::array::const_reverse_iterator rbegin(const pmem::obj::array &a) { return a.rbegin(); } /** * Non-member rend. */ template typename pmem::obj::array::reverse_iterator rend(pmem::obj::array &a) { return a.rend(); } /** * Non-member rend. */ template typename pmem::obj::array::const_reverse_iterator rend(const pmem::obj::array &a) { return a.rend(); } /** * Non-member swap function. */ template inline void swap(pmem::obj::array &lhs, pmem::obj::array &rhs) { lhs.swap(rhs); } /** * Non-member get function. */ template T & get(pmem::obj::array &a) { static_assert(I < N, "Index out of bounds in std::get<> (pmem::obj::array)"); return a.at(I); } /** * Non-member get function. */ template T && get(pmem::obj::array &&a) { static_assert(I < N, "Index out of bounds in std::get<> (pmem::obj::array)"); return std::move(a.at(I)); } /** * Non-member get function. */ template const T & get(const pmem::obj::array &a) noexcept { static_assert(I < N, "Index out of bounds in std::get<> (pmem::obj::array)"); return a.at(I); } /** * Non-member get function. */ template const T && get(const pmem::obj::array &&a) noexcept { static_assert(I < N, "Index out of bounds in std::get<> (pmem::obj::array)"); return std::move(a.at(I)); } } /* namespace obj */ } /* namespace pmem */ #endif /* LIBPMEMOBJ_CPP_ARRAY_HPP */ libpmemobj-cpp-1.9/include/libpmemobj++/container/basic_string.hpp000066400000000000000000004213421361501571000253230ustar00rootroot00000000000000/* * Copyright 2019-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file * String container with std::basic_string compatible interface. */ #ifndef LIBPMEMOBJ_CPP_BASIC_STRING_HPP #define LIBPMEMOBJ_CPP_BASIC_STRING_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace pmem { namespace obj { /** * pmem::obj::string - persistent container with std::basic_string compatible * interface. * * The implementation is still missing some methods. */ template > class basic_string { public: /* Member types */ using traits_type = Traits; using value_type = CharT; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using reference = value_type &; using const_reference = const value_type &; using pointer = value_type *; using const_pointer = const value_type *; using iterator = pmem::detail::basic_contiguous_iterator; using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using for_each_ptr_function = std::function; /* Number of characters which can be stored using sso */ static constexpr size_type sso_capacity = (32 - 8) / sizeof(CharT) - 1; /* Constructors */ basic_string(); basic_string(size_type count, CharT ch); basic_string(const basic_string &other, size_type pos, size_type count = npos); basic_string(const std::basic_string &other, size_type pos, size_type count = npos); basic_string(const CharT *s, size_type count); basic_string(const CharT *s); template < typename InputIt, typename Enable = typename std::enable_if< pmem::detail::is_input_iterator::value>::type> basic_string(InputIt first, InputIt last); basic_string(const basic_string &other); basic_string(const std::basic_string &other); basic_string(basic_string &&other); basic_string(std::initializer_list ilist); /* Destructor */ ~basic_string(); /* Assignment operators */ basic_string &operator=(const basic_string &other); basic_string &operator=(const std::basic_string &other); basic_string &operator=(basic_string &&other); basic_string &operator=(const CharT *s); basic_string &operator=(CharT ch); basic_string &operator=(std::initializer_list ilist); /* Assignment methods */ basic_string &assign(size_type count, CharT ch); basic_string &assign(const basic_string &other); basic_string &assign(const std::basic_string &other); basic_string &assign(const basic_string &other, size_type pos, size_type count = npos); basic_string &assign(const std::basic_string &other, size_type pos, size_type count = npos); basic_string &assign(const CharT *s, size_type count); basic_string &assign(const CharT *s); template ::type> basic_string &assign(InputIt first, InputIt last); basic_string &assign(basic_string &&other); basic_string &assign(std::initializer_list ilist); /* Element access */ reference at(size_type n); const_reference at(size_type n) const; const_reference const_at(size_type n) const; reference operator[](size_type n); const_reference operator[](size_type n) const; CharT &front(); const CharT &front() const; const CharT &cfront() const; CharT &back(); const CharT &back() const; const CharT &cback() const; CharT *data(); const CharT *data() const noexcept; const CharT *cdata() const noexcept; const CharT *c_str() const noexcept; void for_each_ptr(for_each_ptr_function func); /* Iterators */ iterator begin(); const_iterator begin() const noexcept; const_iterator cbegin() const noexcept; iterator end(); const_iterator end() const noexcept; const_iterator cend() const noexcept; reverse_iterator rbegin(); const_reverse_iterator rbegin() const noexcept; const_reverse_iterator crbegin() const noexcept; reverse_iterator rend(); const_reverse_iterator rend() const noexcept; const_reverse_iterator crend() const noexcept; /* Capacity */ bool empty() const noexcept; size_type size() const noexcept; size_type length() const noexcept; size_type max_size() const noexcept; size_type capacity() const noexcept; void resize(size_type count, CharT ch); void resize(size_type n); void reserve(size_type new_cap = 0); void shrink_to_fit(); void clear(); /* Modifiers */ basic_string &erase(size_type index = 0, size_type count = npos); iterator erase(const_iterator pos); iterator erase(const_iterator first, const_iterator last); /* We add following overloads to resolve erase(0) ambiguity */ template ::value>::type> basic_string &erase(T param); template ::value>::type> iterator erase(T param); void pop_back(); basic_string &append(size_type count, CharT ch); basic_string &append(const basic_string &str); basic_string &append(const basic_string &str, size_type pos, size_type count = npos); basic_string &append(const CharT *s, size_type count); basic_string &append(const CharT *s); template ::type> basic_string &append(InputIt first, InputIt last); basic_string &append(std::initializer_list ilist); void push_back(CharT ch); basic_string &operator+=(const basic_string &str); basic_string &operator+=(const CharT *s); basic_string &operator+=(CharT c); basic_string &operator+=(std::initializer_list ilist); basic_string &insert(size_type index, size_type count, CharT ch); basic_string &insert(size_type index, const CharT *s); basic_string &insert(size_type index, const CharT *s, size_type count); basic_string &insert(size_type index, const basic_string &str); basic_string &insert(size_type index1, const basic_string &str, size_type index2, size_type count = npos); iterator insert(const_iterator pos, CharT ch); iterator insert(const_iterator pos, size_type count, CharT ch); template ::type> iterator insert(const_iterator pos, InputIt first, InputIt last); iterator insert(const_iterator pos, std::initializer_list ilist); template ::value>::type> basic_string &insert(T param, size_type count, CharT ch); template ::value>::type> iterator insert(T param, size_type count, CharT ch); basic_string &replace(size_type index, size_type count, const basic_string &str); basic_string &replace(const_iterator first, const_iterator last, const basic_string &str); basic_string &replace(size_type index, size_type count, const basic_string &str, size_type index2, size_type count2 = npos); template ::type> basic_string &replace(const_iterator first, const_iterator last, InputIt first2, InputIt last2); basic_string &replace(const_iterator first, const_iterator last, const CharT *s, size_type count2); basic_string &replace(const_iterator first, const_iterator last, const CharT *s); basic_string &replace(size_type index, size_type count, size_type count2, CharT ch); basic_string &replace(const_iterator first, const_iterator last, size_type count2, CharT ch); basic_string &replace(size_type index, size_type count, const CharT *s, size_type count2); basic_string &replace(size_type index, size_type count, const CharT *s); basic_string &replace(const_iterator first, const_iterator last, std::initializer_list ilist); size_type copy(CharT *s, size_type count, size_type index = 0) const; int compare(const basic_string &other) const; int compare(const std::basic_string &other) const; int compare(size_type pos, size_type count, const basic_string &other) const; int compare(size_type pos, size_type count, const std::basic_string &other) const; int compare(size_type pos1, size_type count1, const basic_string &other, size_type pos2, size_type count2 = npos) const; int compare(size_type pos1, size_type count1, const std::basic_string &other, size_type pos2, size_type count2 = npos) const; int compare(const CharT *s) const; int compare(size_type pos, size_type count, const CharT *s) const; int compare(size_type pos, size_type count1, const CharT *s, size_type count2) const; /* Search */ size_type find(const basic_string &str, size_type pos = 0) const noexcept; size_type find(const CharT *s, size_type pos, size_type count) const; size_type find(const CharT *s, size_type pos = 0) const; size_type find(CharT ch, size_type pos = 0) const noexcept; size_type rfind(const basic_string &str, size_type pos = npos) const noexcept; size_type rfind(const CharT *s, size_type pos, size_type count) const; size_type rfind(const CharT *s, size_type pos = npos) const; size_type rfind(CharT ch, size_type pos = npos) const noexcept; size_type find_first_of(const basic_string &str, size_type pos = 0) const noexcept; size_type find_first_of(const CharT *s, size_type pos, size_type count) const; size_type find_first_of(const CharT *s, size_type pos = 0) const; size_type find_first_of(CharT ch, size_type pos = 0) const noexcept; size_type find_first_not_of(const basic_string &str, size_type pos = 0) const noexcept; size_type find_first_not_of(const CharT *s, size_type pos, size_type count) const; size_type find_first_not_of(const CharT *s, size_type pos = 0) const; size_type find_first_not_of(CharT ch, size_type pos = 0) const noexcept; size_type find_last_of(const basic_string &str, size_type pos = npos) const noexcept; size_type find_last_of(const CharT *s, size_type pos, size_type count) const; size_type find_last_of(const CharT *s, size_type pos = npos) const; size_type find_last_of(CharT ch, size_type pos = npos) const noexcept; size_type find_last_not_of(const basic_string &str, size_type pos = npos) const noexcept; size_type find_last_not_of(const CharT *s, size_type pos, size_type count) const; size_type find_last_not_of(const CharT *s, size_type pos = npos) const; size_type find_last_not_of(CharT ch, size_type pos = npos) const noexcept; /* Special value. The exact meaning depends on the context. */ static const size_type npos = static_cast(-1); private: using sso_type = array; using non_sso_type = vector; /** * This union holds sso data inside of an array and non sso data inside * a vector. If vector is used, it must be manually created and * destroyed. * * _size is used to store length in case when SSO is used. It is the * same type as first member of data field. This means that it can be * safely accessed through both sso (_size variable) and non_sso (as * size in a vector) no matter which one is used. * * C++11 §9.2/18 says: * If a standard-layout union contains two or more standard-layout * structs that share a common initial sequence, and if the * standard-layout union object currently contains one of these * standard-layout structs, it is permitted to inspect the common * initial part of any of them. */ union { struct { /* * EXACTLY the same type as first member in vector * Holds size for sso string, bit specified by _sso_mask * indicates if sso is used. */ p _size; sso_type _data; } sso; struct { non_sso_type _data; } non_sso; }; /* * MSB is used because vector is known not to use entire range of * size_type. */ static constexpr size_type _sso_mask = 1ULL << (std::numeric_limits::digits - 1); /* helper functions */ bool is_sso_used() const; void destroy_data(); template < typename InputIt, typename Enable = typename std::enable_if< pmem::detail::is_input_iterator::value>::type> size_type get_size(InputIt first, InputIt last) const; size_type get_size(size_type count, value_type ch) const; size_type get_size(const basic_string &other) const; template pointer replace_content(Args &&... args); template pointer initialize(Args &&... args); void allocate(size_type capacity); template < typename InputIt, typename Enable = typename std::enable_if< pmem::detail::is_input_iterator::value>::type> pointer assign_sso_data(InputIt first, InputIt last); pointer assign_sso_data(size_type count, value_type ch); pointer assign_sso_data(basic_string &&other); template < typename InputIt, typename Enable = typename std::enable_if< pmem::detail::is_input_iterator::value>::type> pointer assign_large_data(InputIt first, InputIt last); pointer assign_large_data(size_type count, value_type ch); pointer assign_large_data(basic_string &&other); pool_base get_pool() const; void check_pmem() const; void check_tx_stage_work() const; void check_pmem_tx() const; void add_sso_to_tx(size_type first, size_type num) const; size_type get_sso_size() const; void enable_sso(); void disable_sso(); void set_sso_size(size_type new_size); void sso_to_large(size_t new_capacity); void large_to_sso(); typename basic_string::non_sso_type &non_sso_data(); typename basic_string::sso_type &sso_data(); const typename basic_string::non_sso_type & non_sso_data() const; const typename basic_string::sso_type &sso_data() const; }; /** * Default constructor. Construct an empty container. * * @pre must be called in transaction scope. * * @throw pmem::pool_error if an object is not in persistent memory. * @throw pmem::transaction_scope_error if constructor wasn't called in * transaction. */ template basic_string::basic_string() { check_pmem_tx(); sso._size = 0; allocate(0); initialize(0U, value_type('\0')); } /** * Construct the container with count copies of elements with value ch. * * @param[in] count number of elements to construct. * @param[in] ch value of all constructed elements. * * @pre must be called in transaction scope. * * @throw pmem::pool_error if an object is not in persistent memory. * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. * @throw pmem::transaction_scope_error if constructor wasn't called in * transaction. */ template basic_string::basic_string(size_type count, CharT ch) { check_pmem_tx(); sso._size = 0; allocate(count); initialize(count, ch); } /** * Construct the string with a substring * [pos, min(pos+count, other.size()) of other. * * @param[in] other string from which substring will be copied. * @param[in] pos start position of substring in other. * @param[in] count length of substring. * * @pre must be called in transaction scope. * * @throw std::out_of_range is pos > other.size() * @throw pmem::pool_error if an object is not in persistent memory. * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. * @throw pmem::transaction_scope_error if constructor wasn't called in * transaction. */ template basic_string::basic_string(const basic_string &other, size_type pos, size_type count) { check_pmem_tx(); sso._size = 0; if (pos > other.size()) throw std::out_of_range("Index out of range."); if (count == npos || pos + count > other.size()) count = other.size() - pos; auto first = static_cast(pos); auto last = first + static_cast(count); allocate(count); initialize(other.cbegin() + first, other.cbegin() + last); } /** * Construct the string with a substring * [pos, min(pos+count, other.size()) of std::basic_string other. * * @param[in] other std::basic_string from which substring will * be copied. * @param[in] pos start position of substring in other. * @param[in] count length of substring. * * @pre must be called in transaction scope. * * @throw std::out_of_range is pos > other.size() * @throw pmem::pool_error if an object is not in persistent memory. * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. * @throw pmem::transaction_scope_error if constructor wasn't called in * transaction. */ template basic_string::basic_string(const std::basic_string &other, size_type pos, size_type count) { check_pmem_tx(); sso._size = 0; if (pos > other.size()) throw std::out_of_range("Index out of range."); if (count == npos || pos + count > other.size()) count = other.size() - pos; auto first = static_cast(pos); auto last = first + static_cast(count); allocate(count); initialize(other.cbegin() + first, other.cbegin() + last); } /** * Construct the string with the first count elements of C-style * string s. * * @param[in] s pointer to source string. * @param[in] count length of the resulting string. * * @pre must be called in transaction scope. * * @throw pmem::pool_error if an object is not in persistent memory. * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. * @throw pmem::transaction_scope_error if constructor wasn't called in * transaction. */ template basic_string::basic_string(const CharT *s, size_type count) { check_pmem_tx(); sso._size = 0; allocate(count); initialize(s, s + count); } /** * Construct the string with the contents of s. * * @param[in] s pointer to source string. * * @pre must be called in transaction scope. * * @throw pmem::pool_error if an object is not in persistent memory. * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. * @throw pmem::transaction_scope_error if constructor wasn't called in * transaction. */ template basic_string::basic_string(const CharT *s) { check_pmem_tx(); sso._size = 0; auto length = traits_type::length(s); allocate(length); initialize(s, s + length); } /** * Construct the string with the contents of the range [first, last). * This constructor only participates in overload resolution if InputIt * satisfies InputIterator. * * @param[in] first iterator to beginning of the range. * @param[in] last iterator to end of the range. * * @pre must be called in transaction scope. * * @throw pmem::pool_error if an object is not in persistent memory. * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. * @throw pmem::transaction_scope_error if constructor wasn't called in * transaction. */ template template basic_string::basic_string(InputIt first, InputIt last) { auto len = std::distance(first, last); assert(len >= 0); check_pmem_tx(); sso._size = 0; allocate(static_cast(len)); initialize(first, last); } /** * Copy constructor. Construct the string with the copy of the contents * of other. * * @param[in] other reference to the string to be copied. * * @pre must be called in transaction scope. * * @throw pmem::pool_error if an object is not in persistent memory. * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. * @throw pmem::transaction_scope_error if constructor wasn't called in * transaction. */ template basic_string::basic_string(const basic_string &other) { check_pmem_tx(); sso._size = 0; allocate(other.size()); initialize(other.cbegin(), other.cend()); } /** * Copy constructor. Construct the string with the copy of the contents * of std::basic_string other. * * @param[in] other reference to the std::basic_string to be * copied. * * @pre must be called in transaction scope. * * @throw pmem::pool_error if an object is not in persistent memory. * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. * @throw pmem::transaction_scope_error if constructor wasn't called in * transaction. */ template basic_string::basic_string(const std::basic_string &other) : basic_string(other.cbegin(), other.cend()) { } /** * Move constructor. Construct the string with the contents of other * using move semantics. * * @param[in] other rvalue reference to the string to be moved from. * * @pre must be called in transaction scope. * * @throw pmem::pool_error if an object is not in persistent memory. * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. * @throw pmem::transaction_scope_error if constructor wasn't called in * transaction. */ template basic_string::basic_string(basic_string &&other) { check_pmem_tx(); sso._size = 0; allocate(other.size()); initialize(std::move(other)); if (other.is_sso_used()) other.initialize(0U, value_type('\0')); } /** * Construct the container with the contents of the initializer list * init. * * @param[in] ilist initializer list with content to be constructed. * * @pre must be called in transaction scope. * * @throw pmem::pool_error if an object is not in persistent memory. * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. * @throw pmem::transaction_scope_error if constructor wasn't called in * transaction. */ template basic_string::basic_string(std::initializer_list ilist) { check_pmem_tx(); sso._size = 0; allocate(ilist.size()); initialize(ilist.begin(), ilist.end()); } /** * Destructor. * * XXX: implement free_data() */ template basic_string::~basic_string() { if (!is_sso_used()) detail::destroy(non_sso_data()); } /** * Copy assignment operator. Replace the string with contents of other * transactionally. * * @param[in] other reference to the string to be copied. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::operator=(const basic_string &other) { return assign(other); } /** * Copy assignment operator. Replace the string with contents of * std::basic_string other. * * @param[in] other reference to the std::basic_string to be * copied. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::operator=(const std::basic_string &other) { return assign(other); } /** * Move assignment operator. Replace the string with the contents of * other using move semantics transactionally. * * @param[in] other rvalue reference to the string to be moved from. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::operator=(basic_string &&other) { return assign(std::move(other)); } /** * Replace the contents with copy of C-style string s transactionally. * * @param[in] s pointer to source string. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::operator=(const CharT *s) { return assign(s); } /** * Replace the contents with character ch transactionally. * * @param[in] ch character. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::operator=(CharT ch) { return assign(1, ch); } /** * Replace the contents with those of the initializer list ilist * transactionally. * * @param[in] ilist initializer_list of characters. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::operator=(std::initializer_list ilist) { return assign(ilist); } /** * Replace the contents with count copies of character ch * transactionally. * * @param[in] count number of characters. * @param[in] ch character. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::assign(size_type count, CharT ch) { auto pop = get_pool(); transaction::run(pop, [&] { replace_content(count, ch); }); return *this; } /** * Replace the string with the copy of the contents of other * transactionally. * * @param[in] other reference to the string to be copied. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::assign(const basic_string &other) { if (&other == this) return *this; auto pop = get_pool(); transaction::run( pop, [&] { replace_content(other.cbegin(), other.cend()); }); return *this; } /** * Replace the string with the copy of the contents of * std::basic_string other. * * @param[in] other reference to the std::basic_string to be * copied. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::assign(const std::basic_string &other) { return assign(other.cbegin(), other.cend()); } /** * Replace the contents with a substring * [pos, std::min(pos+count, other.size()) of other transactionally. * * @param[in] other string from which substring will be copied. * @param[in] pos start position of substring in other. * @param[in] count length of substring. * * @throw std::out_of_range is pos > other.size() * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::assign(const basic_string &other, size_type pos, size_type count) { if (pos > other.size()) throw std::out_of_range("Index out of range."); if (count == npos || pos + count > other.size()) count = other.size() - pos; auto pop = get_pool(); auto first = static_cast(pos); auto last = first + static_cast(count); transaction::run(pop, [&] { replace_content(other.cbegin() + first, other.cbegin() + last); }); return *this; } /** * Replace the contents with a substring * [pos, std::min(pos+count, other.size()) of std::basic_string * other transactionally. * * @param[in] other std::basic_string from which substring will * be copied. * @param[in] pos start position of substring in other. * @param[in] count length of substring. * * @throw std::out_of_range is pos > other.size() * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::assign(const std::basic_string &other, size_type pos, size_type count) { if (pos > other.size()) throw std::out_of_range("Index out of range."); if (count == npos || pos + count > other.size()) count = other.size() - pos; return assign(other.c_str() + pos, count); } /** * Replace the contents with the first count elements of C-style string * s transactionally. * * @param[in] s pointer to source string. * @param[in] count length of the string. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::assign(const CharT *s, size_type count) { auto pop = get_pool(); transaction::run(pop, [&] { replace_content(s, s + count); }); return *this; } /** * Replace the contents with copy of C-style string s transactionally. * * @param[in] s pointer to source string. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::assign(const CharT *s) { auto pop = get_pool(); auto length = traits_type::length(s); transaction::run(pop, [&] { replace_content(s, s + length); }); return *this; } /** * Replace the contents with copies of elements in the range [first, * last) transactionally. This function participates in overload * resolution only if InputIt satisfies InputIterator. * * @param[in] first iterator to beginning of the range. * @param[in] last iterator to end of the range. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template template basic_string & basic_string::assign(InputIt first, InputIt last) { auto pop = get_pool(); transaction::run(pop, [&] { replace_content(first, last); }); return *this; } /** * Replace the string with the contents of other using move semantics * transactionally. Other is left in valid state with size equal to 0. * * @param[in] other rvalue reference to the string to be moved from. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::assign(basic_string &&other) { if (&other == this) return *this; auto pop = get_pool(); transaction::run(pop, [&] { replace_content(std::move(other)); if (other.is_sso_used()) other.initialize(0U, value_type('\0')); }); return *this; } /** * replace_content the contents with those of the initializer list ilist * transactionally. * * @param[in] ilist initializer_list of characters. * * @throw pmem::transaction_alloc_error when allocating memory for * underlying storage in transaction failed. */ template basic_string & basic_string::assign(std::initializer_list ilist) { return assign(ilist.begin(), ilist.end()); } /** * Iterates over all internal pointers and executes a callback function * on each of them. In this implementation, it just calls for_each_ptr() * of the vector stored in SSO. * * @param func callback function to call on internal pointer. */ template void basic_string::for_each_ptr(for_each_ptr_function func) { if (!is_sso_used()) { non_sso._data.for_each_ptr(func); } } /** * Return an iterator to the beginning. * * @return an iterator pointing to the first element in the string. */ template typename basic_string::iterator basic_string::begin() { return is_sso_used() ? iterator(&*sso_data().begin()) : iterator(&*non_sso_data().begin()); } /** * Return const iterator to the beginning. * * @return const iterator pointing to the first element in the string. */ template typename basic_string::const_iterator basic_string::begin() const noexcept { return cbegin(); } /** * Return const iterator to the beginning. * * @return const iterator pointing to the first element in the string. */ template typename basic_string::const_iterator basic_string::cbegin() const noexcept { return is_sso_used() ? const_iterator(&*sso_data().cbegin()) : const_iterator(&*non_sso_data().cbegin()); } /** * Return an iterator to past the end. * * @return iterator referring to the past-the-end element in the string. */ template typename basic_string::iterator basic_string::end() { return begin() + static_cast(size()); } /** * Return const iterator to past the end. * * @return const_iterator referring to the past-the-end element in the * string. */ template typename basic_string::const_iterator basic_string::end() const noexcept { return cbegin() + static_cast(size()); } /** * Return const iterator to past the end. * * @return const_iterator referring to the past-the-end element in the * string. */ template typename basic_string::const_iterator basic_string::cend() const noexcept { return cbegin() + static_cast(size()); } /** * Return a reverse iterator to the beginning. * * @return a reverse iterator pointing to the last element in * non-reversed string. */ template typename basic_string::reverse_iterator basic_string::rbegin() { return reverse_iterator(end()); } /** * Return a const reverse iterator to the beginning. * * @return a const reverse iterator pointing to the last element in * non-reversed string. */ template typename basic_string::const_reverse_iterator basic_string::rbegin() const noexcept { return crbegin(); } /** * Return a const reverse iterator to the beginning. * * @return a const reverse iterator pointing to the last element in * non-reversed string. */ template typename basic_string::const_reverse_iterator basic_string::crbegin() const noexcept { return const_reverse_iterator(cend()); } /** * Return a reverse iterator to the end. * * @return reverse iterator referring to character preceding first * character in the non-reversed string. */ template typename basic_string::reverse_iterator basic_string::rend() { return reverse_iterator(begin()); } /** * Return a const reverse iterator to the end. * * @return const reverse iterator referring to character preceding * first character in the non-reversed string. */ template typename basic_string::const_reverse_iterator basic_string::rend() const noexcept { return crend(); } /** * Return a const reverse iterator to the end. * * @return const reverse iterator referring to character preceding * first character in the non-reversed string. */ template typename basic_string::const_reverse_iterator basic_string::crend() const noexcept { return const_reverse_iterator(cbegin()); } /** * Access element at specific index with bounds checking and snapshot it * if there is an active transaction. * * @param[in] n index number. * * @return reference to element number n in underlying array. * * @throw std::out_of_range if n is not within the range of the * container. * @throw pmem::transaction_error when adding the object to the * transaction failed. */ template typename basic_string::reference basic_string::at(size_type n) { if (n >= size()) throw std::out_of_range("string::at"); return is_sso_used() ? sso_data()[n] : non_sso_data()[n]; } /** * Access element at specific index with bounds checking. * * @param[in] n index number. * * @return const_reference to element number n in underlying array. * * @throw std::out_of_range if n is not within the range of the * container. */ template typename basic_string::const_reference basic_string::at(size_type n) const { return const_at(n); } /** * Access element at specific index with bounds checking. In * contradiction to at(), const_at() will return const_reference not * depending on the const-qualification of the object it is called on. * std::basic_string doesn't provide const_at() method. * * @param[in] n index number. * * @return const_reference to element number n in underlying array. * * @throw std::out_of_range if n is not within the range of the * container. */ template typename basic_string::const_reference basic_string::const_at(size_type n) const { if (n >= size()) throw std::out_of_range("string::const_at"); return is_sso_used() ? static_cast(sso_data())[n] : static_cast(non_sso_data())[n]; } /** * Access element at specific index and snapshot it if there is an * active transaction. No bounds checking is performed. * * @param[in] n index number. * * @return reference to element number n in underlying array. * * @throw pmem::transaction_error when adding the object to the * transaction failed. */ template typename basic_string::reference basic_string::operator[](size_type n) { return is_sso_used() ? sso_data()[n] : non_sso_data()[n]; } /** * Access element at specific index. No bounds checking is performed. * * @param[in] n index number. * * @return const_reference to element number n in underlying array. */ template typename basic_string::const_reference basic_string::operator[](size_type n) const { return is_sso_used() ? sso_data()[n] : non_sso_data()[n]; } /** * Access first element and snapshot it if there is an * active transaction. * * @return reference to first element in string. * * @throw pmem::transaction_error when adding the object to the * transaction failed. */ template CharT & basic_string::front() { return (*this)[0]; } /** * Access first element. * * @return const reference to first element in string. */ template const CharT & basic_string::front() const { return cfront(); } /** * Access first element. In contradiction to front(), cfront() will * return const_reference not depending on the const-qualification of * the object it is called on. std::basic_string doesn't provide * cfront() method. * * @return const reference to first element in string. */ template const CharT & basic_string::cfront() const { return static_cast(*this)[0]; } /** * Access last element and snapshot it if there is an * active transaction. * * @return reference to last element in string. * * @throw pmem::transaction_error when adding the object to the * transaction failed. */ template CharT & basic_string::back() { return (*this)[size() - 1]; } /** * Access last element. * * @return const reference to last element in string. */ template const CharT & basic_string::back() const { return cback(); } /** * Access last element. In contradiction to back(), cback() will return * const_reference not depending on the const-qualification of the * object it is called on. std::basic_string doesn't provide * cback() method. * * @return const reference to last element in string. */ template const CharT & basic_string::cback() const { return static_cast(*this)[size() - 1]; } /** * @return number of CharT elements in the string. */ template typename basic_string::size_type basic_string::size() const noexcept { if (is_sso_used()) return get_sso_size(); else if (non_sso_data().size() == 0) return 0; else return non_sso_data().size() - 1; } /** * @return pointer to underlying data. * * @throw transaction_error when adding data to the * transaction failed. */ template CharT * basic_string::data() { return is_sso_used() ? sso_data().range(0, get_sso_size() + 1).begin() : non_sso_data().data(); } /** * Remove characters from string starting at index transactionally. * Length of the string to erase is determined as the smaller of count and * size() - index. * * @param[in] index first character to remove. * @param[in] count number of characters to remove. * * @return *this * * @pre index <= size() * * @post size() = size() - std::min(count, size() - index) * * @throw std::out_of_range if index > size(). * @throw pmem::transaction_error when snapshotting failed. * @throw rethrows destructor exception. */ template basic_string & basic_string::erase(size_type index, size_type count) { auto sz = size(); if (index > sz) throw std::out_of_range("Index exceeds size."); count = (std::min)(count, sz - index); auto pop = get_pool(); auto first = begin() + static_cast(index); auto last = first + static_cast(count); if (is_sso_used()) { transaction::run(pop, [&] { auto move_len = sz - index - count; auto new_size = sz - count; auto range = sso_data().range(index, move_len + 1); traits_type::move(range.begin(), &*last, move_len); set_sso_size(new_size); assert(range.end() - 1 == &sso_data()._data[index + move_len]); *(range.end() - 1) = value_type('\0'); }); } else { non_sso_data().erase(first, last); } return *this; } /** * Remove character from string at pos position transactionally. * * @param[in] pos position of character to be removed. * * @return Iterator following the removed element. If the iterator pos * refers to the last element, the end() iterator is returned. * * @pre pos <= size() * * @post size() = size() - 1 * * @throw std::out_of_range if pos > size(). * @throw pmem::transaction_error when snapshotting failed. * @throw rethrows destructor exception. */ template typename basic_string::iterator basic_string::erase(const_iterator pos) { return erase(pos, pos + 1); } /** * Remove characters from string at [first, last) range transactionally. * * @param[in] first begin of the range of characters to be removed. * @param[in] last end of the range of characters to be removed. * * @return Iterator which points to the element pointed by the last iterator * before the erase operation. If no such element exists then end() iterator is * returned. * * @pre first and last are valid iterators on *this. * * @post size() = size() - std::distance(first, last) * * @throw std::out_of_range if [first, last) is not a valid range of *this. * @throw pmem::transaction_error when snapshotting failed. * @throw rethrows destructor exception. */ template typename basic_string::iterator basic_string::erase(const_iterator first, const_iterator last) { size_type index = static_cast(std::distance(cbegin(), first)); size_type len = static_cast(std::distance(first, last)); erase(index, len); return begin() + static_cast(index); } /** * Remove the last character from the string transactionally. * * @pre !empty() * * @post size() = size() - 1 * * @throw pmem::transaction_error when snapshotting failed. * @throw rethrows destructor exception. */ template void basic_string::pop_back() { erase(size() - 1, 1); } /** * Append count copies of character ch to the string transactionally. * * @param[in] count number of characters to append. * @param[in] ch character value to append. * * @return *this * * @post size() == size() + count * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::append(size_type count, CharT ch) { auto sz = size(); auto new_size = sz + count; if (new_size > max_size()) throw std::length_error("Size exceeds max size."); if (is_sso_used()) { auto pop = get_pool(); transaction::run(pop, [&] { if (new_size > sso_capacity) { sso_to_large(new_size); non_sso_data().insert( non_sso_data().cbegin() + static_cast( sz), count, ch); } else { add_sso_to_tx(sz, count + 1); traits_type::assign(&sso_data()._data[sz], count, ch); assert(new_size == sz + count); set_sso_size(new_size); sso_data()._data[new_size] = value_type('\0'); } }); } else { non_sso_data().insert(non_sso_data().cbegin() + static_cast(sz), count, ch); } return *this; } /** * Append string str transactionally. * * @param[in] str string to append. * * @return *this * * @post size() == size() + str.size() * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::append(const basic_string &str) { return append(str.data(), str.size()); } /** * Append substring [pos, pos + count) of str string transactionally. * Length of the string to append is determined as the smaller of count and * str.size() - pos. * * @param[in] str string to append. * @param[in] pos index of the first character to append. * @param[in] count characters to append. * * @return *this * * @pre pos <= str.size() * * @post size() == size() + std::min(count, str.size() - pos). * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::out_of_range if pos > str.size(). * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::append(const basic_string &str, size_type pos, size_type count) { auto sz = str.size(); if (pos > sz) throw std::out_of_range("Index out of range."); count = (std::min)(count, sz - pos); append(str.data() + pos, count); return *this; } /** * Append characters in the range [s, s + count) transactionally. * * @param[in] s pointer to C-style string to append. * @param[in] count characters to append. * * @return *this * * @post size() == size() + count. * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::append(const CharT *s, size_type count) { return append(s, s + count); } /** * Append C-style string transactionally. * Length of the string is determined by the first null character. * * @param[in] s pointer to C-style string to append. * * @return *this * * @post size() == size() + traits::length(s). * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::append(const CharT *s) { return append(s, traits_type::length(s)); } /** * Append characters in the range [first, last) transactionally. * This overload participates in overload resolution only if * InputIt qualifies as InputIterator. * * @param[in] first begin of the range of characters to append. * @param[in] last end of the range of characters to append. * * @return *this * * @post size() == size() + std::distance(first, last) * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template template basic_string & basic_string::append(InputIt first, InputIt last) { auto sz = size(); auto count = static_cast(std::distance(first, last)); auto new_size = sz + count; if (new_size > max_size()) throw std::length_error("Size exceeds max size."); if (is_sso_used()) { auto pop = get_pool(); transaction::run(pop, [&] { if (new_size > sso_capacity) { /* 1) Cache C-style string in case of * self-append, because it will be destroyed * when switching from sso to large string. * * 2) We cache in std::vector instead of * std::string because of overload deduction * ambiguity on Windows */ std::vector str(first, last); sso_to_large(new_size); non_sso_data().insert( non_sso_data().cbegin() + static_cast( sz), str.begin(), str.end()); } else { add_sso_to_tx(sz, count + 1); std::copy(first, last, &sso_data()._data[sz]); assert(new_size == sz + count); set_sso_size(new_size); sso_data()._data[new_size] = value_type('\0'); } }); } else { non_sso_data().insert(non_sso_data().cbegin() + static_cast(sz), first, last); } return *this; } /** * Append characters from the ilist initializer list transactionally. * * @param[in] ilist initializer list with characters to append from * * @return *this * * @post size() == size() + std::distance(ilist.begin(), ilist.end()) * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::append(std::initializer_list ilist) { return append(ilist.begin(), ilist.end()); } /** * Append character ch at the end of the string transactionally. * * @param[in] ch character to append * * @post size() == size() + 1 * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template void basic_string::push_back(CharT ch) { append(static_cast(1), ch); } /** * Append string str transactionally. * * @param[in] str string to append. * * @return *this * * @post size() == size() + str.size() * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::operator+=(const basic_string &str) { return append(str); } /** * Append C-style string transactionally. * Length of the string is determined by the first null character. * * @param[in] s pointer to C-style string to append. * * @return *this * * @post size() == size() + traits::length(s). * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::operator+=(const CharT *s) { return append(s); } /** * Append character ch at the end of the string transactionally. * * @param[in] ch character to append * * @post size() == size() + 1 * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new_size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::operator+=(CharT ch) { push_back(ch); return *this; } /** * Append characters from the ilist initializer list transactionally. * * @param[in] ilist initializer list with characters to append from * * @return *this * * @post size() == size() + ilist.size() * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::operator+=(std::initializer_list ilist) { return append(ilist); } /** * Insert count copies of ch character at index transactionally. * * @param[in] index position at which the content will be inserted * @param[in] count number of characters to insert * @param[in] ch character to insert * * @return *this * * @post size() == size() + count * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::out_of_range if index > size(). * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::insert(size_type index, size_type count, CharT ch) { if (index > size()) throw std::out_of_range("Index out of range."); auto pos = cbegin() + static_cast(index); insert(pos, count, ch); return *this; } /** * Insert null-terminated C-style string pointed by s of the length determined * by the first null character at index transactionally. * * @param[in] index position at which the content will be inserted. * @param[in] s pointer to C-style string to insert. * * @return *this * * @post size() == size() + traits::length(s). * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::out_of_range if index > size(). * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::insert(size_type index, const CharT *s) { return insert(index, s, traits_type::length(s)); } /** * Insert characters in the range [s, s+ count) at index transactionally. * * @param[in] index position at which the content will be inserted. * @param[in] s pointer to C-style string to insert. * @param[in] count number of characters to insert. * * @return *this * * @post size() == size() + count * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::out_of_range if index > size(). * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::insert(size_type index, const CharT *s, size_type count) { if (index > size()) throw std::out_of_range("Index out of range."); auto pos = cbegin() + static_cast(index); insert(pos, s, s + count); return *this; } /** * Insert str string at index transactionally. * * @param[in] index position at which the content will be inserted. * @param[in] str string to insert. * * @return *this * * @post size() == size() + str.size() * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::out_of_range if index > size(). * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::insert(size_type index, const basic_string &str) { return insert(index, str.data(), str.size()); } /** * Insert a str.substr(index2, count) string at index1 transactionally. * * @param[in] index1 position at which the content will be inserted. * @param[in] str string to insert. * @param[in] index2 position of the first character in str to insert. * @param[in] count number of characters to insert. * * @return *this * * @post size() == size() + std::min(count, str.size() - index2) * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::out_of_range if index1 > size() or str.size() > index2. * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::insert(size_type index1, const basic_string &str, size_type index2, size_type count) { auto sz = str.size(); if (index1 > size() || index2 > sz) throw std::out_of_range("Index out of range."); count = (std::min)(count, sz - index2); return insert(index1, str.data() + index2, count); } /** * Insert character ch before the character pointed by pos transactionally. * * @param[in] pos iterator before which the character will be inserted. * @param[in] ch character to insert. * * @return iterator to the copy of the first inserted character or pos if no * characters were inserted. * * @pre pos is valid iterator on *this. * * @post size() == size() + 1 * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template typename basic_string::iterator basic_string::insert(const_iterator pos, CharT ch) { return insert(pos, 1, ch); } /** * Insert count copies of character ch before the character pointed by pos * transactionally. * * @param[in] pos iterator before which the character will be inserted. * @param[in] count number of characters to insert. * @param[in] ch character to insert. * * @return iterator to the copy of the first inserted character or pos if no * characters were inserted. * * @pre pos is valid iterator on *this. * * @post size() == size() + count * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template typename basic_string::iterator basic_string::insert(const_iterator pos, size_type count, CharT ch) { auto sz = size(); if (sz + count > max_size()) throw std::length_error("Count exceeds max size."); auto new_size = sz + count; auto pop = get_pool(); auto index = static_cast(std::distance(cbegin(), pos)); transaction::run(pop, [&] { if (is_sso_used() && new_size <= sso_capacity) { auto len = sz - index; add_sso_to_tx(index, len + count + 1); traits_type::move(&sso_data()._data[index + count], &sso_data()._data[index], len); traits_type::assign(&sso_data()._data[index], count, ch); assert(new_size == index + len + count); set_sso_size(new_size); sso_data()._data[new_size] = value_type('\0'); } else { if (is_sso_used()) sso_to_large(new_size); non_sso_data().insert( non_sso_data().begin() + static_cast(index), count, ch); } }); return iterator(&data()[static_cast(index)]); } /** * Insert characters from [first, last) range before the character pointed by * pos transactionally. * * @param[in] pos iterator before which the character will be inserted. * @param[in] first begin of the range of characters to insert. * @param[in] last end of the range of characters to insert. * * @return iterator to the copy of the first inserted character or pos if no * characters were inserted. * * @pre pos is valid iterator on *this. * * @post size() == size() + std::distance(first, last) * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template template typename basic_string::iterator basic_string::insert(const_iterator pos, InputIt first, InputIt last) { auto sz = size(); auto count = static_cast(std::distance(first, last)); if (sz + count > max_size()) throw std::length_error("Count exceeds max size."); auto pop = get_pool(); auto new_size = sz + count; auto index = static_cast(std::distance(cbegin(), pos)); transaction::run(pop, [&] { if (is_sso_used() && new_size <= sso_capacity) { auto len = sz - index; add_sso_to_tx(index, len + count + 1); traits_type::move(&sso_data()._data[index + count], &sso_data()._data[index], len); std::copy(first, last, &sso_data()._data[index]); assert(new_size == index + len + count); set_sso_size(new_size); sso_data()._data[new_size] = value_type('\0'); } else { if (is_sso_used()) { /* 1) Cache C-style string in case of * self-insert, because it will be destroyed * when switching from sso to large string. * * 2) We cache in std::vector instead of * std::string because of overload deduction * ambiguity on Windows */ std::vector str(first, last); sso_to_large(new_size); non_sso_data().insert( non_sso_data().begin() + static_cast( index), str.begin(), str.end()); } else { non_sso_data().insert( non_sso_data().begin() + static_cast( index), first, last); } } }); return iterator(&data()[static_cast(index)]); } /** * Insert characters from initializer list ilist before the character pointed by * pos transactionally. * * @param[in] pos iterator before which the character will be inserted. * @param[in] ilist initializer list of characters to insert. * * @return iterator to the copy of the first inserted character or pos if no * characters were inserted. * * @pre pos is valid iterator on *this. * * @post size() == size() + ilist.size() * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template typename basic_string::iterator basic_string::insert(const_iterator pos, std::initializer_list ilist) { return insert(pos, ilist.begin(), ilist.end()); } /** * Replace range [index, index + count) with the content of str string * transactionally. * * @param[in] index start of the substring that will be replaced. * @param[in] count length of the substring that will be replaced. * @param[in] str that is used for the replacement. * * @return *this * * @post size() == size() - (std::min)(count, size() - index) + str.size() * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::out_of_range if index > size(). * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::replace(size_type index, size_type count, const basic_string &str) { return replace(index, count, str.data(), str.size()); } /** * Replace range [first, last) with the content of str string transactionally. * * @param[in] first begin of the range of characters that will be replaced. * @param[in] last end of the range of characters that will be replaced. * @param[in] str that that will be used for replacement. * * @return *this * * @post size() == size() - std::distance(first, last) + str.size() * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::replace(const_iterator first, const_iterator last, const basic_string &str) { return replace(first, last, str.data(), str.data() + str.size()); } /** * Replace range [index, index + count) with the substring [index2, index2 + * count2) of str string transactionally. If either count2 == npos or count2 * exceeds size of str string then substring [index2, str.size()) is used. * * @param[in] index start of the substring that will be replaced. * @param[in] count length of the substring that will be replaced. * @param[in] str that is used for the replacement. * @param[in] index2 start of the substring that will be used for replacement. * @param[in] count2 length of the substring that will be used for replacement. * * @return *this * * @post size() == size() - (std::min)(count, size() - index) + * (std::min)(count2, str.size() - index2) * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::out_of_range if index > size() or index2 > str.size(). * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::replace(size_type index, size_type count, const basic_string &str, size_type index2, size_type count2) { auto sz = str.size(); if (index2 > sz) throw std::out_of_range("Index out of range."); count2 = (std::min)(count2, sz - index2); return replace(index, count, str.data() + index2, count2); } /** * Replace range [first, last) with the characters in [first2, last2) range * transactionally. * * @param[in] first begin of the range of characters that will be replaced. * @param[in] last end of the range of characters that will be replaced. * @param[in] first2 begin of the range of characters that will be used for * replacement. * @param[in] last2 end of the range of characters that will be used for * replacement. * * @return *this * * @post size() == size() - std::distance(first, last) + std::distance(first2, * last2). * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template template basic_string & basic_string::replace(const_iterator first, const_iterator last, InputIt first2, InputIt last2) { auto sz = size(); auto index = static_cast(std::distance(cbegin(), first)); auto count = static_cast(std::distance(first, last)); auto count2 = static_cast(std::distance(first2, last2)); count = (std::min)(count, sz - index); if (sz - count + count2 > max_size()) throw std::length_error("Count exceeds max size."); auto new_size = sz - count + count2; auto pop = get_pool(); transaction::run(pop, [&] { if (is_sso_used() && new_size <= sso_capacity) { add_sso_to_tx(index, new_size - index + 1); assert(count2 < new_size + 1); traits_type::move(&sso_data()._data[index + count2], &sso_data()._data[index + count], sz - index - count); std::copy(first2, last2, &sso_data()._data[index]); set_sso_size(new_size); sso_data()._data[new_size] = value_type('\0'); } else { /* 1) Cache C-style string in case of * self-replace, because it will be destroyed * when switching from sso to large string. * * 2) We cache in std::vector instead of * std::string because of overload deduction * ambiguity on Windows */ std::vector str(first2, last2); if (is_sso_used()) { sso_to_large(new_size); } auto beg = begin() + static_cast(index); auto end = beg + static_cast(count); non_sso_data().erase(beg, end); non_sso_data().insert(beg, str.begin(), str.end()); } if (!is_sso_used() && new_size <= sso_capacity) large_to_sso(); }); return *this; } /** * Replace range [first, last) with the characters in [s, s + count2) range * transactionally. * * @param[in] first begin of the range of characters that will be replaced. * @param[in] last end of the range of characters that will be replaced. * @param[in] s pointer to C-style string that will be used for replacement. * @param[in] count2 number of characters that will be used for replacement. * * @return *this * * @post size() == size() - std::distance(first, last) + count2. * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::replace(const_iterator first, const_iterator last, const CharT *s, size_type count2) { return replace(first, last, s, s + count2); } /** * Replace range [index, index + count) with the characters in [s, s + count2) * range transactionally. * * @param[in] index start of the substring that will be replaced. * @param[in] count length of the substring that will be replaced. * @param[in] s pointer to C-style string that will be used for replacement. * @param[in] count2 number of characters that will be used for replacement. * * @return *this * * @post size() == size() - (std::min)(count, size() - index) + count2. * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::out_of_range if index > size(). * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::replace(size_type index, size_type count, const CharT *s, size_type count2) { if (index > size()) throw std::out_of_range("Index out of range."); auto first = cbegin() + static_cast(index); auto last = first + static_cast(count); return replace(first, last, s, s + count2); } /** * Replace range [index, index + count) with the characters in [s, s + * traits::length(s)) range transactionally. * * @param[in] index start of the substring that will be replaced. * @param[in] count length of the substring that will be replaced. * @param[in] s pointer to C-style string that will be used for replacement. * * @return *this * * @post size() == size() - (std::min)(count, size() - index) + * traits::length(s). * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::out_of_range if index > size(). * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::replace(size_type index, size_type count, const CharT *s) { return replace(index, count, s, traits_type::length(s)); } /** * Replace range [index, index + count) with count2 copies of ch character * transactionally. * * @param[in] index start of the substring that will be replaced. * @param[in] count length of the substring that will be replaced. * @param[in] count2 number of characters that will be used for replacement. * @param[in] ch character that will be used for replacement. * * @return *this * * @post size() == size() - (std::min)(count, size() - index) + count2. * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::out_of_range if index > size(). * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::replace(size_type index, size_type count, size_type count2, CharT ch) { if (index > size()) throw std::out_of_range("Index out of range."); auto first = cbegin() + static_cast(index); auto last = first + static_cast(count); return replace(first, last, count2, ch); } /** * Replace range [first, last) with count2 copies of ch character * transactionally. * * @param[in] first begin of the range of characters that will be replaced. * @param[in] last end of the range of characters that will be replaced. * @param[in] count2 number of characters that will be used for replacement. * @param[in] ch character that will be used for replacement. * * @return *this * * @post size() == size() - std::distance(first, last) + count2. * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::replace(const_iterator first, const_iterator last, size_type count2, CharT ch) { auto sz = size(); auto index = static_cast(std::distance(cbegin(), first)); auto count = static_cast(std::distance(first, last)); count = (std::min)(count, sz - index); if (sz - count + count2 > max_size()) throw std::length_error("Count exceeds max size."); auto new_size = sz - count + count2; auto pop = get_pool(); transaction::run(pop, [&] { if (is_sso_used() && new_size <= sso_capacity) { add_sso_to_tx(index, new_size - index + 1); assert(count2 < new_size + 1); traits_type::move(&sso_data()._data[index + count2], &sso_data()._data[index + count], sz - index - count); traits_type::assign(&sso_data()._data[index], count2, ch); set_sso_size(new_size); sso_data()._data[new_size] = value_type('\0'); } else { if (is_sso_used()) { sso_to_large(new_size); } auto beg = begin() + static_cast(index); auto end = beg + static_cast(count); non_sso_data().erase(beg, end); non_sso_data().insert(beg, count2, ch); } if (!is_sso_used() && new_size <= sso_capacity) large_to_sso(); }); return *this; } /** * Replace range [first, last) with the characters in [s, s + traits::length(s)) * range transactionally. * * @param[in] first begin of the range of characters that will be replaced. * @param[in] last end of the range of characters that will be replaced. * @param[in] s pointer to C-style string that will be used for replacement. * * @return *this * * @post size() == size() - std::distance(first, last) + traits::length(s). * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::replace(const_iterator first, const_iterator last, const CharT *s) { return replace(first, last, s, traits_type::length(s)); } /** * Replace range [first, last) with characters in initializer list ilist * transactionally. * * @param[in] first begin of the range of characters that will be replaced. * @param[in] last end of the range of characters that will be replaced. * @param[in] ilist initializer list of characters that will be used for * replacement. * * @return *this * * @post size() == size() - std::distance(first, last) + ilist.size(). * @post capacity() == sso_capacity if new size is less than or equal to * sso_capacity, or the smallest next power of 2, bigger than new size if it is * greater than old capacity, or remains the same if there is enough space to * store all new elements. * * @throw std::length_error if new size > max_size(). * @throw pmem::transaction_alloc_error when allocating new memory failed. * @throw pmem::transaction_free_error when freeing old underlying array failed. * @throw rethrows constructor exception. */ template basic_string & basic_string::replace(const_iterator first, const_iterator last, std::initializer_list ilist) { return replace(first, last, ilist.begin(), ilist.end()); } /** * Copy [index, index + count) substring of *this to C-style string. * If either count == npos or count exceeds size of *this string then substring * [index, size()) is used. Resulting C-style string is not null-terminated. * * @param[in] s pointer to destination C-style string. * @param[in] count length of the substring. * @param[in] index start of the substring that will be copied. * * @return number of copied characters. * * @throw std::out_of_range if index > size(). */ template typename basic_string::size_type basic_string::copy(CharT *s, size_type count, size_type index) const { auto sz = size(); if (index > sz) throw std::out_of_range("Index out of range."); auto len = (std::min)(count, sz - index); traits_type::copy(s, data() + index, len); return len; } /** * Compares [pos, pos + count1) substring of this to * [s, s + count2) substring of s. * * If count > size() - pos, substring is equal to [pos, size()). * * @param[in] pos beginning of substring of this. * @param[in] count1 length of substring of this. * @param[in] s C-style string to compare to. * @param[in] count2 length of substring of s. * * @return negative value if substring of *this < substring of s in * lexicographical order, zero if substring of *this == substring of * s and positive value if substring of *this > substring of s. * * @throw std::out_of_range is pos > size() */ template int basic_string::compare(size_type pos, size_type count1, const CharT *s, size_type count2) const { if (pos > size()) throw std::out_of_range("Index out of range."); if (count1 > size() - pos) count1 = size() - pos; auto ret = traits_type::compare(cdata() + pos, s, std::min(count1, count2)); if (ret != 0) return ret; if (count1 < count2) return -1; else if (count1 == count2) return 0; else return 1; } /** * Finds the first substring equal str. * * @param[in] str string to search for * @param[in] pos position where the search starts from * * @return Position of the first character of the found substring or * npos if no such substring is found. */ template typename basic_string::size_type basic_string::find(const basic_string &str, size_type pos) const noexcept { return find(str.data(), pos, str.size()); } /** * Finds the first substring equal to the range [s, s+count). * This range may contain null characters. * * @param[in] s pointer to the C-style string to search for * @param[in] pos position where the search starts from * @param[in] count length of the substring to search for * * @return Position of the first character of the found substring or * npos if no such substring is found. */ template typename basic_string::size_type basic_string::find(const CharT *s, size_type pos, size_type count) const { auto sz = size(); if (pos > sz) return npos; if (count == 0) return pos; while (pos + count <= sz) { auto found = traits_type::find(cdata() + pos, sz - pos, s[0]); if (!found) return npos; pos = static_cast(std::distance(cdata(), found)); if (traits_type::compare(found, s, count) == 0) { return pos; } ++pos; } return npos; } /** * Finds the first substring equal to the C-style string pointed to by s. * The length of the string is determined by the first null character. * * @param[in] s pointer to the C-style string to search for * @param[in] pos position where the search starts from * * @return Position of the first character of the found substring or * npos if no such substring is found. */ template typename basic_string::size_type basic_string::find(const CharT *s, size_type pos) const { return find(s, pos, traits_type::length(s)); } /** * Finds the first character ch * * @param[in] ch character to search for * @param[in] pos position where the search starts from * * @return Position of the first character equal to ch, or npos if no such * character is found. */ template typename basic_string::size_type basic_string::find(CharT ch, size_type pos) const noexcept { return find(&ch, pos, 1); } /** * Finds the last substring equal to str. * If npos or any value not smaller than size()-1 is passed as pos, whole string * will be searched. * @param[in] str string to search for * @param[in] pos position where the search starts from * * @return Position (as an offset from start of the string) of the first * character of the found substring or npos if no such substring is found */ template typename basic_string::size_type basic_string::rfind(const basic_string &str, size_type pos) const noexcept { return rfind(str.cdata(), pos, str.size()); } /** * Finds the last substring equal to the range [s, s+count). * This range can include null characters. When pos is specified, the search * only includes sequences of characters that begin at or before position pos, * ignoring any possible match beginning after pos. If npos or any value not * smaller than size()-1 is passed as pos, whole string will be searched. * * @param[in] s pointer to the C-style string to search for * @param[in] pos position where the search starts from * @param[in] count length of the substring to search for * * @return Position (as an offset from start of the string) of the first * character of the found substring or npos if no such substring is found. If * searching for an empty string retrurn pos, if also pos is greater than the * size of the string - it returns size */ template typename basic_string::size_type basic_string::rfind(const CharT *s, size_type pos, size_type count) const { if (count <= size()) { pos = (std::min)(size() - count, pos); do { if (traits_type::compare(cdata() + pos, s, count) == 0) return pos; } while (pos-- > 0); } return npos; } /** * Finds the last substring equal to the C-style string pointed to by s. * The length of the string is determined by the first null character * If npos or any value not smaller than size()-1 is passed as pos, whole string * will be searched. * * @param[in] s pointer to the C-style string to search for * @param[in] pos position where the search starts from * * @return Position (as an offset from start of the string) of the first * character of the found substring or npos if no such substring is found */ template typename basic_string::size_type basic_string::rfind(const CharT *s, size_type pos) const { return rfind(s, pos, traits_type::length(s)); } /** * Finds the last character equal to ch. * If npos or any value not smaller than size()-1 is passed as pos, whole string * will be searched. * * @param[in] ch character to search for * @param[in] pos position where the search starts from * * @return Position (as an offset from start of the string) of the first * character equal to ch or npos if no such character is found */ template typename basic_string::size_type basic_string::rfind(CharT ch, size_type pos) const noexcept { return rfind(&ch, pos, 1); } /** * Finds the first character equal to any of the characters in str. * * @param[in] str string identifying characters to search for * @param[in] pos position where the search starts from * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_first_of(const basic_string &str, size_type pos) const noexcept { return find_first_of(str.cdata(), pos, str.size()); } /** * Finds the first character equal to any of the characters * in the range [s, s+count). This range can include null characters. * * @param[in] s pointer to the C-style string identifying characters to search *for * @param[in] pos position at which to begin searching * @param[in] count length of the C-style string identifying characters to *search for * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_first_of(const CharT *s, size_type pos, size_type count) const { size_type first_of = npos; for (const CharT *c = s; c != s + count; ++c) { size_type found = find(*c, pos); if (found != npos && found < first_of) first_of = found; } return first_of; } /** * Finds the first character equal to any of the characters in the C-style * string pointed to by s. The length of the string is determined by the first * null character * * @param[in] s pointer to the C-style string identifying characters to search * for * @param[in] pos position at which to begin searching * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_first_of(const CharT *s, size_type pos) const { return find_first_of(s, pos, traits_type::length(s)); } /** * Finds the first character equal to ch * * @param[in] ch character to search for * @param[in] pos position at which to begin searching * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_first_of(CharT ch, size_type pos) const noexcept { return find(ch, pos); } /** * Finds the first character equal to none of the characters in str. * * @param[in] str string identifying characters to search for * @param[in] pos position where the search starts from * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_first_not_of(const basic_string &str, size_type pos) const noexcept { return find_first_not_of(str.cdata(), pos, str.size()); } /** * Finds the first character equal to none of the characters * in the range [s, s+count). This range can include null characters. * * @param[in] s pointer to the C-style string identifying characters to search * for * @param[in] pos position at which to begin searching * @param[in] count length of the C-style string identifying characters to *search for * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_first_not_of(const CharT *s, size_type pos, size_type count) const { if (pos >= size()) return npos; for (auto it = cbegin() + pos; it != cend(); ++it) if (!traits_type::find(s, count, *it)) return static_cast( std::distance(cbegin(), it)); return npos; } /** * Finds the first character equal to none of the characters in the C-style *string pointed to by s. The length of the string is determined by the first *null character * * @param[in] s pointer to the C-style string identifying characters to search * for * @param[in] pos position at which to begin searching * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_first_not_of(const CharT *s, size_type pos) const { return find_first_not_of(s, pos, traits_type::length(s)); } /** * Finds the first character not equal to ch * * @param[in] ch character to search for * @param[in] pos position at which to begin searching * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_first_not_of(CharT ch, size_type pos) const noexcept { return find_first_not_of(&ch, pos, 1); } /** * Finds the last character equal to any of the characters in str. * * @param[in] str string identifying characters to search for * @param[in] pos position where the search starts from * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_last_of(const basic_string &str, size_type pos) const noexcept { return find_last_of(str.cdata(), pos, str.size()); } /** * Finds the last character equal to any of the characters * in the range [s, s+count). This range can include null characters. * * @param[in] s pointer to the C-style string identifying characters to search *for * @param[in] pos position at which to begin searching * @param[in] count length of the C-style string identifying characters to *search for * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_last_of(const CharT *s, size_type pos, size_type count) const { if (size() == 0 || count == 0) return npos; bool found = false; size_type last_of = 0; for (const CharT *c = s; c != s + count; ++c) { size_type position = rfind(*c, pos); if (position != npos) { found = true; if (position > last_of) last_of = position; } } if (!found) return npos; return last_of; } /** * Finds the last character equal to any of the characters in the C-style string * pointed to by s. The length of the string is determined by the * first null character * * @param[in] s pointer to the C-style string identifying characters to search * for * @param[in] pos position at which to begin searching * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_last_of(const CharT *s, size_type pos) const { return find_last_of(s, pos, traits_type::length(s)); } /** * Finds the last character equal to ch * * @param[in] ch character to search for * @param[in] pos position at which to begin searching * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_last_of(CharT ch, size_type pos) const noexcept { return rfind(ch, pos); } /** * Finds the last character equal to none of the characters in str. * * @param[in] str string identifying characters to search for * @param[in] pos position where the search starts from * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_last_not_of(const basic_string &str, size_type pos) const noexcept { return find_last_not_of(str.cdata(), pos, str.size()); } /** * Finds the last character equal to none of the characters * in the range [s, s+count). This range can include null characters. * * @param[in] s pointer to the C-style string identifying characters to search *for * @param[in] pos position at which to begin searching * @param[in] count length of the C-style string identifying characters to *search for * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_last_not_of(const CharT *s, size_type pos, size_type count) const { if (size() > 0) { pos = (std::min)(pos, size() - 1); do { if (!traits_type::find(s, count, *(cdata() + pos))) return pos; } while (pos-- > 0); } return npos; } /** * Finds the last character equal to none of the characters in the C-style *string pointed to by s. The length of the string is determined by the first *null character * * @param[in] s pointer to the C-style string identifying characters to search * for * @param[in] pos position at which to begin searching * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_last_not_of(const CharT *s, size_type pos) const { return find_last_not_of(s, pos, traits_type::length(s)); } /** * Finds the last character not equal to ch * * @param[in] ch character to search for * @param[in] pos position at which to begin searching * * @return Position of the found character or npos if no such character is * found. */ template typename basic_string::size_type basic_string::find_last_not_of(CharT ch, size_type pos) const noexcept { return find_last_not_of(&ch, pos, 1); } /** * Compares this string to other. * * @param[in] other string to compare to. * * @return negative value if *this < other in lexicographical order, * zero if *this == other and positive value if *this > other. */ template int basic_string::compare(const basic_string &other) const { return compare(0, size(), other.cdata(), other.size()); } /** * Compares this string to std::basic_string other. * * @param[in] other std::basic_string to compare to. * * @return negative value if *this < other in lexicographical order, * zero if *this == other and positive value if *this > other. */ template int basic_string::compare( const std::basic_string &other) const { return compare(0, size(), other.data(), other.size()); } /** * Compares [pos, pos + count) substring of this to other. * If count > size() - pos, substring is equal to [pos, size()). * * @param[in] pos beginning of the substring. * @param[in] count length of the substring. * @param[in] other string to compare to. * * @return negative value if substring < other in lexicographical order, * zero if substring == other and positive value if substring > other. * * @throw std::out_of_range is pos > size() */ template int basic_string::compare(size_type pos, size_type count, const basic_string &other) const { return compare(pos, count, other.cdata(), other.size()); } /** * Compares [pos, pos + count) substring of this to * std::basic_string other. If count > size() - pos, substring is * equal to [pos, size()). * * @param[in] pos beginning of the substring. * @param[in] count length of the substring. * @param[in] other std::basic_string to compare to. * * @return negative value if substring < other in lexicographical order, * zero if substring == other and positive value if substring > other. * * @throw std::out_of_range is pos > size() */ template int basic_string::compare( size_type pos, size_type count, const std::basic_string &other) const { return compare(pos, count, other.data(), other.size()); } /** * Compares [pos1, pos1 + count1) substring of this to * [pos2, pos2 + count2) substring of other. * * If count1 > size() - pos, substring is equal to [pos1, size()). * * @param[in] pos1 beginning of substring of this. * @param[in] count1 length of substring of this. * @param[in] other string to compare to. * @param[in] pos2 beginning of substring of other. * @param[in] count2 length of substring of other. * * @return negative value if substring of *this < substring of other in * lexicographical order, zero if substring of *this == substring of * other and positive value if substring of *this > substring of other. * * @throw std::out_of_range is pos1 > size() or pos2 > other.size() */ template int basic_string::compare(size_type pos1, size_type count1, const basic_string &other, size_type pos2, size_type count2) const { if (pos2 > other.size()) throw std::out_of_range("Index out of range."); if (count2 > other.size() - pos2) count2 = other.size() - pos2; return compare(pos1, count1, other.cdata() + pos2, count2); } /** * Compares [pos1, pos1 + count1) substring of this to * [pos2, pos2 + count2) substring of std::basic_string other. * * If count1 > size() - pos, substring is equal to [pos1, size()). * * @param[in] pos1 beginning of substring of this. * @param[in] count1 length of substring of this. * @param[in] other std::basic_string to compare to. * @param[in] pos2 beginning of substring of other. * @param[in] count2 length of substring of other. * * @return negative value if substring of *this < substring of other in * lexicographical order, zero if substring of *this == substring of * other and positive value if substring of *this > substring of other. * * @throw std::out_of_range is pos1 > size() or pos2 > other.size() */ template int basic_string::compare(size_type pos1, size_type count1, const std::basic_string &other, size_type pos2, size_type count2) const { if (pos2 > other.size()) throw std::out_of_range("Index out of range."); if (count2 > other.size() - pos2) count2 = other.size() - pos2; return compare(pos1, count1, other.data() + pos2, count2); } /** * Compares this string to s. * * @param[in] s C-style string to compare to. * * @return negative value if *this < s in lexicographical order, * zero if *this == s and positive value if *this > s. */ template int basic_string::compare(const CharT *s) const { return compare(0, size(), s, traits_type::length(s)); } /** * Compares [pos, pos + count) substring of this to s. * If count > size() - pos, substring is equal to [pos, size()). * * @param[in] pos beginning of the substring. * @param[in] count length of the substring. * @param[in] s C-style string to compare to. * * @return negative value if substring < s in lexicographical order, * zero if substring == s and positive value if substring > s. * * @throw std::out_of_range is pos > size() */ template int basic_string::compare(size_type pos, size_type count, const CharT *s) const { return compare(pos, count, s, traits_type::length(s)); } /** * @return const pointer to underlying data. */ template const CharT * basic_string::cdata() const noexcept { return is_sso_used() ? sso_data().cdata() : non_sso_data().cdata(); } /** * @return pointer to underlying data. */ template const CharT * basic_string::data() const noexcept { return cdata(); } /** * @return pointer to underlying data. */ template const CharT * basic_string::c_str() const noexcept { return cdata(); } /** * @return number of CharT elements in the string. */ template typename basic_string::size_type basic_string::length() const noexcept { return size(); } /** * @return maximum number of elements the string is able to hold. */ template typename basic_string::size_type basic_string::max_size() const noexcept { return PMEMOBJ_MAX_ALLOC_SIZE / sizeof(CharT) - 1; } /** * @return number of characters that can be held in currently allocated * storage. */ template typename basic_string::size_type basic_string::capacity() const noexcept { return is_sso_used() ? sso_capacity : non_sso_data().capacity() - 1; } /** * Resize the string to count characters transactionally. If the current * size is greater than count, the string is reduced to its first count * elements. If the current size is less than count, additional * characters of ch value are appended. * * @param[in] count new size of the container. * @param[in] ch character to initialize elements. * * @post capacity() == std::max(count, capacity()) * @post size() == count * * @throw std::length_error if count > max_size() * @throw rethrows constructor exception. * @throw rethrows destructor exception. * @throw pmem::transaction_error when snapshotting failed. * @throw pmem::transaction_free_error when freeing old underlying array * failed. */ template void basic_string::resize(size_type count, CharT ch) { if (count > max_size()) throw std::length_error("Count exceeds max size."); auto sz = size(); auto pop = get_pool(); transaction::run(pop, [&] { if (count > sz) { append(count - sz, ch); } else if (is_sso_used()) { set_sso_size(count); sso_data()[count] = value_type('\0'); } else { non_sso_data().resize(count + 1, ch); non_sso_data().back() = value_type('\0'); } }); } /** * Resize the string to count characters transactionally. If the current * size is greater than count, the string is reduced to its first count * elements. If the current size is less than count, additional * default-initialized characters are appended. * * @param[in] count new size of the container. * * @post capacity() == std::max(count, capacity()) * @post size() == count * * @throw std::length_error if count > max_size() * @throw rethrows constructor exception. * @throw rethrows destructor exception. * @throw pmem::transaction_error when snapshotting failed. * @throw pmem::transaction_free_error when freeing old underlying array * failed. */ template void basic_string::resize(size_type count) { resize(count, CharT()); } /** * Increase the capacity of the string to new_cap transactionally. If * new_cap is greater than the current capacity(), new storage is * allocated, otherwise the method does nothing. If new_cap is greater * than capacity(), all iterators, including the past-the-end iterator, * and all references to the elements are invalidated. Otherwise, no * iterators or references are invalidated. * * @param[in] new_cap new capacity. * * @post capacity() == max(capacity(), capacity_new) * * @throw rethrows destructor exception. * @throw std::length_error if new_cap > max_size(). * @throw pmem::transaction_error when snapshotting failed. * @throw pmem::transaction_alloc_error when allocating new memory * failed. * @throw pmem::transaction_free_error when freeing old underlying array * failed. */ template void basic_string::reserve(size_type new_cap) { if (new_cap > max_size()) throw std::length_error("New capacity exceeds max size."); if (new_cap < capacity() || new_cap <= sso_capacity) return; if (is_sso_used()) { auto pop = get_pool(); transaction::run(pop, [&] { sso_to_large(new_cap); }); } else { non_sso_data().reserve(new_cap + 1); } } /** * Remove unused capacity transactionally. If large string is used * capacity will be set to current size. If sso is used nothing happens. * * @post capacity() == std::min(sso_capacity, capacity()) * * @throw pmem::transaction_error when snapshotting failed. * @throw pmem::transaction_alloc_error when reallocating failed. * @throw pmem::transaction_free_error when freeing old underlying array * failed. * @throw rethrows constructor exception. * @throw rethrows destructor exception. */ template void basic_string::shrink_to_fit() { if (is_sso_used()) return; if (size() <= sso_capacity) { auto pop = get_pool(); transaction::run(pop, [&] { large_to_sso(); }); } else { non_sso_data().shrink_to_fit(); } } /** * Remove all characters from the string transactionally. * All pointers, references, and iterators are invalidated. * * @post size() == 0 * * @throw pmem::transaction_error when snapshotting failed. * @throw rethrows destructor exception. */ template void basic_string::clear() { erase(begin(), end()); } /** * @return true if string is empty, false otherwise. */ template bool basic_string::empty() const noexcept { return size() == 0; } template bool basic_string::is_sso_used() const { return (sso._size & _sso_mask) != 0; } template void basic_string::destroy_data() { assert(pmemobj_tx_stage() == TX_STAGE_WORK); if (is_sso_used()) { add_sso_to_tx(0, get_sso_size() + 1); /* sso.data destructor does not have to be called */ } else { non_sso_data().free_data(); detail::destroy(non_sso_data()); } } /** * Overload of generic get_size method used to calculate size * based on provided parameters. * * Return std::distance(first, last) for pair of iterators. */ template template typename basic_string::size_type basic_string::get_size(InputIt first, InputIt last) const { return static_cast(std::distance(first, last)); } /** * Overload of generic get_size method used to calculate size * based on provided parameters. * * Return count for (count, value) */ template typename basic_string::size_type basic_string::get_size(size_type count, value_type ch) const { return count; } /** * Overload of generic get_size method used to calculate size * based on provided parameters. * * Return size of other basic_string */ template typename basic_string::size_type basic_string::get_size(const basic_string &other) const { return other.size(); } /** * Generic function which replace_content current content based on provided * parameters. Allowed parameters are: * - size_type count, CharT value * - InputIt first, InputIt last * - basic_string && */ template template typename basic_string::pointer basic_string::replace_content(Args &&... args) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); auto new_size = get_size(std::forward(args)...); /* If non_sso.data is used and there is enough capacity */ if (!is_sso_used() && new_size <= capacity()) return assign_large_data(std::forward(args)...); destroy_data(); allocate(new_size); return initialize(std::forward(args)...); } /** * Generic function which initializes memory based on provided * parameters - forwards parameters to initialize function of either * non_sso.data or sso.data. Allowed parameters are: * - size_type count, CharT value * - InputIt first, InputIt last * - basic_string && * * @pre must be called in transaction scope. * @pre memory must be allocated before initialization. */ template template typename basic_string::pointer basic_string::initialize(Args &&... args) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); auto size = get_size(std::forward(args)...); if (is_sso_used()) { auto ptr = assign_sso_data(std::forward(args)...); set_sso_size(size); return ptr; } else { return assign_large_data(std::forward(args)...); } } /** * Allocate storage for container of capacity bytes. * Based on capacity determine if sso or large string is used. * * @pre data must be uninitialized. * @pre must be called in transaction scope. * * @param[in] capacity bytes to allocate. */ template void basic_string::allocate(size_type capacity) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); if (capacity <= sso_capacity) { enable_sso(); } else { disable_sso(); } /* * array is aggregate type so it's not required to call * a constructor. */ if (!is_sso_used()) { detail::conditional_add_to_tx(&non_sso_data(), 1, POBJ_XADD_NO_SNAPSHOT); detail::create(&non_sso_data()); non_sso_data().reserve(capacity + 1); } } /** * Initialize sso data. Overload for pair of iterators */ template template typename basic_string::pointer basic_string::assign_sso_data(InputIt first, InputIt last) { auto size = static_cast(std::distance(first, last)); assert(pmemobj_tx_stage() == TX_STAGE_WORK); assert(size <= sso_capacity); add_sso_to_tx(0, size + 1); std::copy(first, last, &sso_data()._data[0]); sso_data()._data[size] = value_type('\0'); return &sso_data()[0]; } /** * Initialize sso data. Overload for (count, value). */ template typename basic_string::pointer basic_string::assign_sso_data(size_type count, value_type ch) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); assert(count <= sso_capacity); add_sso_to_tx(0, count + 1); traits_type::assign(&sso_data()._data[0], count, ch); sso_data()._data[count] = value_type('\0'); return &sso_data()[0]; } /** * Initialize sso data. Overload for rvalue reference of basic_string. */ template typename basic_string::pointer basic_string::assign_sso_data(basic_string &&other) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); return assign_sso_data(other.cbegin(), other.cend()); } /** * Initialize non_sso.data - call constructor of non_sso.data. * Overload for pair of iterators. */ template template typename basic_string::pointer basic_string::assign_large_data(InputIt first, InputIt last) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); auto size = static_cast(std::distance(first, last)); non_sso_data().reserve(size + 1); non_sso_data().assign(first, last); non_sso_data().push_back(value_type('\0')); return non_sso_data().data(); } /** * Initialize non_sso.data - call constructor of non_sso.data. * Overload for (count, value). */ template typename basic_string::pointer basic_string::assign_large_data(size_type count, value_type ch) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); non_sso_data().reserve(count + 1); non_sso_data().assign(count, ch); non_sso_data().push_back(value_type('\0')); return non_sso_data().data(); } /** * Initialize non_sso.data - call constructor of non_sso.data. * Overload for rvalue reference of basic_string. */ template typename basic_string::pointer basic_string::assign_large_data(basic_string &&other) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); if (other.is_sso_used()) return assign_large_data(other.cbegin(), other.cend()); non_sso_data() = std::move(other.non_sso_data()); return non_sso_data().data(); } /** * Return pool_base instance and assert that object is on pmem. */ template pool_base basic_string::get_pool() const { auto pop = pmemobj_pool_by_ptr(this); assert(pop != nullptr); return pool_base(pop); } /** * @throw pmem::pool_error if an object is not in persistent memory. */ template void basic_string::check_pmem() const { if (pmemobj_pool_by_ptr(this) == nullptr) throw pmem::pool_error("Object is not on pmem."); } /** * @throw pmem::transaction_scope_error if called outside of a transaction. */ template void basic_string::check_tx_stage_work() const { if (pmemobj_tx_stage() != TX_STAGE_WORK) throw pmem::transaction_scope_error( "Call made out of transaction scope."); } /** * @throw pmem::pool_error if an object is not in persistent memory. * @throw pmem::transaction_scope_error if called outside of a transaction. */ template void basic_string::check_pmem_tx() const { check_pmem(); check_tx_stage_work(); } /** * Snapshot sso data. */ template void basic_string::add_sso_to_tx(size_type idx_first, size_type num) const { assert(idx_first + num <= sso_capacity + 1); assert(is_sso_used()); auto initialized_num = get_sso_size() + 1 - idx_first; /* Snapshot elements in range [idx_first, sso_size + 1 (null)) */ detail::conditional_add_to_tx(&sso_data()._data[0] + idx_first, (std::min)(initialized_num, num)); if (num > initialized_num) { /* Elements after sso_size + 1 do not have to be snapshotted */ detail::conditional_add_to_tx( &sso_data()._data[0] + get_sso_size() + 1, num - initialized_num, POBJ_XADD_NO_SNAPSHOT); } } /** * Return size of sso string. */ template typename basic_string::size_type basic_string::get_sso_size() const { return sso._size & ~_sso_mask; } /** * Enable sso string. */ template void basic_string::enable_sso() { /* temporary size_type must be created to avoid undefined reference * linker error */ sso._size |= (size_type)(_sso_mask); } /** * Disable sso string. */ template void basic_string::disable_sso() { sso._size &= ~_sso_mask; } /** * Set size for sso. */ template void basic_string::set_sso_size(size_type new_size) { sso._size = new_size | _sso_mask; } /** * Resize sso string to large string. * Capacity is equal new_capacity plus sizeof(CharT) bytes for null character. * Content of sso string is preserved and copied to the large string object. * * @pre must be called in transaction scope. * * @post sso is not used. * * @param[in] new_capacity capacity of constructed large string. */ template void basic_string::sso_to_large(size_t new_capacity) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); assert(new_capacity > sso_capacity); assert(is_sso_used()); auto sz = size(); sso_type tmp; std::copy(cbegin(), cend(), tmp.data()); tmp[sz] = value_type('\0'); destroy_data(); allocate(new_capacity); auto begin = tmp.cbegin(); auto end = begin + sz; initialize(begin, end); assert(!is_sso_used()); }; /** * Resize large string to sso string of size() size. * Content of large string is preserved and copied to the sso string. * * @pre must be called in transaction scope. * @pre size() of large string must be less than or equal sso_capacity * * @post sso is used. */ template void basic_string::large_to_sso() { assert(pmemobj_tx_stage() == TX_STAGE_WORK); assert(!is_sso_used()); auto sz = size(); assert(sz <= sso_capacity); sso_type tmp; std::copy(cbegin(), cbegin() + sz, tmp.data()); tmp[sz] = value_type('\0'); destroy_data(); allocate(sz); auto begin = tmp.cbegin(); auto end = begin + sz; initialize(begin, end); assert(is_sso_used()); }; template typename basic_string::non_sso_type & basic_string::non_sso_data() { assert(!is_sso_used()); return non_sso._data; } template typename basic_string::sso_type & basic_string::sso_data() { assert(is_sso_used()); return sso._data; } template const typename basic_string::non_sso_type & basic_string::non_sso_data() const { assert(!is_sso_used()); return non_sso._data; } template const typename basic_string::sso_type & basic_string::sso_data() const { assert(is_sso_used()); return sso._data; } /** * Participate in overload resolution only if T is convertible to size_type. * Call basic_string &erase(size_type index, size_type count = npos) if enabled. */ template template basic_string & basic_string::erase(T param) { return erase(static_cast(param)); } /** * Participate in overload resolution only if T is not convertible to size_type. * Call iterator erase(const_iterator pos) if enabled. */ template template typename basic_string::iterator basic_string::erase(T param) { return erase(static_cast(param)); } /** * Participate in overload resolution only if T is convertible to size_type. * Call basic_string &insert(size_type index, size_type count, CharT ch) if * enabled. */ template template basic_string & basic_string::insert(T param, size_type count, CharT ch) { return insert(static_cast(param), count, ch); } /** * Participate in overload resolution only if T is not convertible to size_type. * Call iterator insert(const_iterator pos, size_type count, CharT ch) if * enabled. */ template template typename basic_string::iterator basic_string::insert(T param, size_type count, CharT ch) { return insert(static_cast(param), count, ch); } /** * Non-member equal operator. */ template bool operator==(const basic_string &lhs, const basic_string &rhs) { return lhs.compare(rhs) == 0; } /** * Non-member not equal operator. */ template bool operator!=(const basic_string &lhs, const basic_string &rhs) { return lhs.compare(rhs) != 0; } /** * Non-member less than operator. */ template bool operator<(const basic_string &lhs, const basic_string &rhs) { return lhs.compare(rhs) < 0; } /** * Non-member less or equal operator. */ template bool operator<=(const basic_string &lhs, const basic_string &rhs) { return lhs.compare(rhs) <= 0; } /** * Non-member greater than operator. */ template bool operator>(const basic_string &lhs, const basic_string &rhs) { return lhs.compare(rhs) > 0; } /** * Non-member greater or equal operator. */ template bool operator>=(const basic_string &lhs, const basic_string &rhs) { return lhs.compare(rhs) >= 0; } /** * Non-member equal operator. */ template bool operator==(const CharT *lhs, const basic_string &rhs) { return rhs.compare(lhs) == 0; } /** * Non-member not equal operator. */ template bool operator!=(const CharT *lhs, const basic_string &rhs) { return rhs.compare(lhs) != 0; } /** * Non-member less than operator. */ template bool operator<(const CharT *lhs, const basic_string &rhs) { return rhs.compare(lhs) > 0; } /** * Non-member less or equal operator. */ template bool operator<=(const CharT *lhs, const basic_string &rhs) { return rhs.compare(lhs) >= 0; } /** * Non-member greater than operator. */ template bool operator>(const CharT *lhs, const basic_string &rhs) { return rhs.compare(lhs) < 0; } /** * Non-member greater or equal operator. */ template bool operator>=(const CharT *lhs, const basic_string &rhs) { return rhs.compare(lhs) <= 0; } /** * Non-member equal operator. */ template bool operator==(const basic_string &lhs, const CharT *rhs) { return lhs.compare(rhs) == 0; } /** * Non-member not equal operator. */ template bool operator!=(const basic_string &lhs, const CharT *rhs) { return lhs.compare(rhs) != 0; } /** * Non-member less than operator. */ template bool operator<(const basic_string &lhs, const CharT *rhs) { return lhs.compare(rhs) < 0; } /** * Non-member less or equal operator. */ template bool operator<=(const basic_string &lhs, const CharT *rhs) { return lhs.compare(rhs) <= 0; } /** * Non-member greater than operator. */ template bool operator>(const basic_string &lhs, const CharT *rhs) { return lhs.compare(rhs) > 0; } /** * Non-member greater or equal operator. */ template bool operator>=(const basic_string &lhs, const CharT *rhs) { return lhs.compare(rhs) >= 0; } /** * Non-member equal operator. */ template bool operator==(const std::basic_string &lhs, const basic_string &rhs) { return rhs.compare(lhs) == 0; } /** * Non-member not equal operator. */ template bool operator!=(const std::basic_string &lhs, const basic_string &rhs) { return rhs.compare(lhs) != 0; } /** * Non-member less than operator. */ template bool operator<(const std::basic_string &lhs, const basic_string &rhs) { return rhs.compare(lhs) > 0; } /** * Non-member less or equal operator. */ template bool operator<=(const std::basic_string &lhs, const basic_string &rhs) { return rhs.compare(lhs) >= 0; } /** * Non-member greater than operator. */ template bool operator>(const std::basic_string &lhs, const basic_string &rhs) { return rhs.compare(lhs) < 0; } /** * Non-member greater or equal operator. */ template bool operator>=(const std::basic_string &lhs, const basic_string &rhs) { return rhs.compare(lhs) <= 0; } /** * Non-member equal operator. */ template bool operator==(const basic_string &lhs, const std::basic_string &rhs) { return lhs.compare(rhs) == 0; } /** * Non-member not equal operator. */ template bool operator!=(const basic_string &lhs, const std::basic_string &rhs) { return lhs.compare(rhs) != 0; } /** * Non-member less than operator. */ template bool operator<(const basic_string &lhs, const std::basic_string &rhs) { return lhs.compare(rhs) < 0; } /** * Non-member less or equal operator. */ template bool operator<=(const basic_string &lhs, const std::basic_string &rhs) { return lhs.compare(rhs) <= 0; } /** * Non-member greater than operator. */ template bool operator>(const basic_string &lhs, const std::basic_string &rhs) { return lhs.compare(rhs) > 0; } /** * Non-member greater or equal operator. */ template bool operator>=(const basic_string &lhs, const std::basic_string &rhs) { return lhs.compare(rhs) >= 0; } } /* namespace obj */ } /* namespace pmem */ #endif /* LIBPMEMOBJ_CPP_BASIC_STRING_HPP */ libpmemobj-cpp-1.9/include/libpmemobj++/container/concurrent_hash_map.hpp000066400000000000000000002464741361501571000267110ustar00rootroot00000000000000/* * Copyright 2019-2020, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file * A persistent version of concurrent hash map implementation * Ref: https://arxiv.org/abs/1509.02235 */ #ifndef PMEMOBJ_CONCURRENT_HASH_MAP_HPP #define PMEMOBJ_CONCURRENT_HASH_MAP_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for std::distance #include #include #include #include #include #include namespace std { /** * Specialization of std::hash for p */ template struct hash> { size_t operator()(const pmem::obj::p &x) const { return hash()(x.get_ro()); } }; } /* namespace std */ namespace pmem { namespace obj { namespace concurrent_hash_map_internal { template class shared_mutex_scoped_lock { using rw_mutex_type = SharedMutexT; public: shared_mutex_scoped_lock(const shared_mutex_scoped_lock &) = delete; shared_mutex_scoped_lock & operator=(const shared_mutex_scoped_lock &) = delete; /** Default constructor. Construct lock that has not acquired a mutex.*/ shared_mutex_scoped_lock() : mutex(nullptr), is_writer(false) { } /** Acquire lock on given mutex. */ shared_mutex_scoped_lock(rw_mutex_type &m, bool write = true) : mutex(nullptr) { acquire(m, write); } /** Release lock (if lock is held). */ ~shared_mutex_scoped_lock() { if (mutex) release(); } /** Acquire lock on given mutex. */ void acquire(rw_mutex_type &m, bool write = true) { is_writer = write; mutex = &m; if (write) mutex->lock(); else mutex->lock_shared(); } /** * Release lock. */ void release() { assert(mutex); rw_mutex_type *m = mutex; mutex = nullptr; if (is_writer) { m->unlock(); } else { m->unlock_shared(); } } /** * Try acquire lock on given mutex. * */ bool try_acquire(rw_mutex_type &m, bool write = true) { assert(!mutex); bool result; is_writer = write; result = write ? m.try_lock() : m.try_lock_shared(); if (result) mutex = &m; return result; } protected: /** * The pointer to the current mutex that is held, or NULL if no mutex is * held. */ rw_mutex_type *mutex; /** * If mutex!=NULL, then is_writer is true if holding a writer lock, * false if holding a reader lock. Not defined if not holding a lock. */ bool is_writer; }; /* class shared_mutex_scoped_lock */ template using scoped_lock_upgrade_to_writer = decltype(std::declval().upgrade_to_writer()); template using scoped_lock_has_upgrade_to_writer = detail::supports; template using scoped_lock_downgrade_to_reader = decltype(std::declval().downgrade_to_reader()); template using scoped_lock_has_downgrade_to_reader = detail::supports; template ::value &&scoped_lock_has_downgrade_to_reader::value> class scoped_lock_traits { public: using scope_lock_type = ScopedLockType; static bool initial_rw_state(bool write) { /* For upgradeable locks, initial state is always read */ return false; } static bool upgrade_to_writer(scope_lock_type &lock) { return lock.upgrade_to_writer(); } static bool downgrade_to_reader(scope_lock_type &lock) { return lock.downgrade_to_reader(); } }; template class scoped_lock_traits { public: using scope_lock_type = ScopedLockType; static bool initial_rw_state(bool write) { /* For non-upgradeable locks, we take lock in required mode * immediately */ return write; } static bool upgrade_to_writer(scope_lock_type &lock) { /* This overload is for locks which do not support upgrade * operation. For those locks, upgrade_to_writer should not be * called when holding a read lock */ return true; } static bool downgrade_to_reader(scope_lock_type &lock) { /* This overload is for locks which do not support downgrade * operation. For those locks, downgrade_to_reader should never * be called */ assert(false); return false; } }; } template , typename KeyEqual = std::equal_to, typename MutexType = pmem::obj::shared_mutex, typename ScopedLockType = concurrent_hash_map_internal:: shared_mutex_scoped_lock> class concurrent_hash_map; /** @cond INTERNAL */ namespace concurrent_hash_map_internal { /* Helper method which throws an exception when called in a tx */ static inline void check_outside_tx() { if (pmemobj_tx_stage() != TX_STAGE_NONE) throw pmem::transaction_scope_error( "Function called inside transaction scope."); } template using transparent_key_equal = typename Hash::transparent_key_equal; template using has_transparent_key_equal = detail::supports; template ::value> struct key_equal_type { using type = typename Hash::transparent_key_equal; }; template struct key_equal_type { using type = Pred; }; template void assert_not_locked(Mutex &mtx) { #ifndef NDEBUG ScopedLockType scoped_lock; assert(scoped_lock.try_acquire(mtx)); scoped_lock.release(); #else (void)mtx; #endif } template struct hash_map_node { /**Mutex type. */ using mutex_t = MutexType; /** Scoped lock type for mutex. */ using scoped_t = ScopedLockType; using value_type = detail::pair; /** Persistent pointer type for next. */ using node_ptr_t = detail::persistent_pool_ptr< hash_map_node>; /** Next node in chain. */ node_ptr_t next; /** Node mutex. */ mutex_t mutex; /** Item stored in node */ value_type item; hash_map_node(const node_ptr_t &_next, const Key &key) : next(_next), item(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple()) { } hash_map_node(const node_ptr_t &_next, const Key &key, const T &t) : next(_next), item(key, t) { } hash_map_node(const node_ptr_t &_next, value_type &&i) : next(_next), item(std::move(i)) { } template hash_map_node(const node_ptr_t &_next, Args &&... args) : next(_next), item(std::forward(args)...) { } hash_map_node(const node_ptr_t &_next, const value_type &i) : next(_next), item(i) { } /** Copy constructor is deleted */ hash_map_node(const hash_map_node &) = delete; /** Assignment operator is deleted */ hash_map_node &operator=(const hash_map_node &) = delete; }; /* struct node */ /** * The class provides the way to access certain properties of segments * used by hash map */ template class segment_traits { public: /** segment index type */ using segment_index_t = size_t; using size_type = size_t; using bucket_type = Bucket; protected: /** PMDK has limitation for allocation size. */ constexpr static size_type max_allocation_size = PMEMOBJ_MAX_ALLOC_SIZE; /** First big block that has fixed size. */ constexpr static segment_index_t first_big_block = 27; /* TODO: avoid hardcoded value; need constexpr similar to: * Log2(max_allocation_size / sizeof(bucket_type)) */ /** Max number of buckets per segment. */ constexpr static size_type big_block_size = size_type(1) << first_big_block; /* Block size in bytes cannot exceed max_allocation_size */ static_assert((big_block_size * sizeof(bucket_type)) < max_allocation_size, "Block size exceeds max_allocation_size"); /** @returns index of the first block in the @arg seg. */ constexpr static segment_index_t first_block_in_segment(segment_index_t seg) { return seg < first_big_block ? seg : (first_big_block + (segment_index_t(1) << (seg - first_big_block)) - 1); } /** @returns number of blocks in the @arg seg. */ constexpr static size_type blocks_in_segment(segment_index_t seg) { return seg < first_big_block ? segment_index_t(1) : segment_index_t(1) << (seg - first_big_block); } /** @returns number of buckets in the @arg b. */ constexpr static size_type block_size(segment_index_t b) { return b < first_big_block ? segment_size(b ? b : 1) : big_block_size; } public: /** Number of embedded segments*/ constexpr static segment_index_t embedded_segments = 1; /** Count of buckets in the embedded segments */ constexpr static size_type embedded_buckets = 1 << embedded_segments; /** Maximum number of segments */ constexpr static segment_index_t number_of_segments = 32; /** Count of segments in the first block. */ static const size_type first_block = 8; /** @return maximum number of blocks */ constexpr static segment_index_t number_of_blocks() { return first_block_in_segment(number_of_segments); } /** @return segment index of given index in the array. */ static segment_index_t segment_index_of(size_type index) { return segment_index_t(detail::Log2(index | 1)); } /** @return the first array index of given segment. */ constexpr static segment_index_t segment_base(segment_index_t k) { return (segment_index_t(1) << k) & ~segment_index_t(1); } /** @return segment size except for @arg k == 0. */ constexpr static size_type segment_size(segment_index_t k) { return size_type(1) << k; // fake value for k == 0 } static_assert( embedded_segments < first_big_block, "Number of embedded segments cannot exceed max_allocation_size"); }; /* End of class segment_traits */ /** * Implements logic to work with segments in the hashmap. * * When number of elements stored in the hashmap exceeds the threshold, * the rehash operation is performed. Each new segment doubles the * number of buckets in the hashmap. * * PMDK has limitation for max allocation size. Therefore, at some * point new segment cannot be allocated as one contiguous memory block. * * block - array of buckets, continues in memory * segment - logical abstraction, might consist of several blocks. * * @class segment_facade_impl provides an abstraction and hides details * of how actually segment is allocated in memory. */ template class segment_facade_impl : public SegmentTraits { private: using traits_type = SegmentTraits; using traits_type::block_size; using traits_type::blocks_in_segment; using traits_type::embedded_buckets; using traits_type::embedded_segments; using traits_type::first_block; using traits_type::first_block_in_segment; using traits_type::segment_base; using traits_type::segment_size; public: using table_reference = typename std::conditional::type; using table_pointer = typename std::conditional::type; using bucket_type = typename traits_type::bucket_type; using segment_index_t = typename traits_type::segment_index_t; using size_type = typename traits_type::size_type; /** Constructor */ segment_facade_impl(table_reference table, segment_index_t s) : my_table(&table), my_seg(s) { assert(my_seg < traits_type::number_of_segments); } /** Copy constructor */ segment_facade_impl(const segment_facade_impl &src) : my_table(src.my_table), my_seg(src.my_seg) { } segment_facade_impl(segment_facade_impl &&src) = default; /** Assignment operator */ segment_facade_impl & operator=(const segment_facade_impl &src) { my_table = src.my_table; my_seg = src.my_seg; return *this; } /** Move assignment operator */ segment_facade_impl & operator=(segment_facade_impl &&src) { my_table = src.my_table; my_seg = src.my_seg; return *this; } /** * Access @arg i bucket in the segment. * @param[in] i bucket index in the segment. Should be in range * [0, segment size). * @returns reference to the bucket. */ bucket_type &operator[](size_type i) const { assert(i < size()); segment_index_t table_block = first_block_in_segment(my_seg); size_type b_size = block_size(table_block); table_block += i / b_size; i = i % b_size; return (*my_table)[table_block][static_cast(i)]; } /** * Go to the next segment. */ segment_facade_impl & operator++() { ++my_seg; return *this; } /** * Go to the next segment. Postfix form. */ segment_facade_impl operator++(int) { segment_facade_impl tmp = *this; ++(*this); return tmp; } /** * Go to the previous segment. */ segment_facade_impl & operator--() { --my_seg; return *this; } /** * Go to the previous segment. Postfix form. */ segment_facade_impl operator--(int) { segment_facade_impl tmp = *this; --(*this); return tmp; } /** * Increments given segment by @arg off elements */ segment_facade_impl & operator+=(segment_index_t off) { my_seg += off; return *this; } /** * Decrements given segment by @arg off elements */ segment_facade_impl & operator-=(segment_index_t off) { my_seg -= off; return *this; } /** * @returns new segment which current segment + @arg off */ segment_facade_impl operator+(segment_index_t off) const { return segment_facade_impl(*(this->my_table), this->my_seg + off); } /** * @returns new segment which current segment - @arg off */ segment_facade_impl operator-(segment_index_t off) const { return segment_facade_impl(*(this->my_table), this->my_seg - off); } /** * Allocates new segment. */ void enable(pool_base &pop) { assert(my_seg >= embedded_segments); if (my_seg < first_block) { enable_first_block(pop); } else { enable_big_segment(pop); } } /** * Deallocates the segment. */ void disable() { assert(my_seg >= embedded_segments); if (my_seg < first_block) { if (my_seg == embedded_segments) { size_type sz = segment_size(first_block) - embedded_buckets; delete_persistent( (*my_table)[my_seg], sz); } (*my_table)[my_seg] = nullptr; } else { block_range blocks = segment_blocks(my_seg); for (segment_index_t b = blocks.first; b < blocks.second; ++b) { if ((*my_table)[b] != nullptr) { delete_persistent( (*my_table)[b], block_size(b)); (*my_table)[b] = nullptr; } } } } /** * @returns size of the segment. */ constexpr size_type size() const { return segment_size(my_seg ? my_seg : 1); } /** * Checks if the segment is enabled. * @returns true if the segment is ebabled. Otherwise returns * false. */ bool is_valid() const { block_range blocks = segment_blocks(my_seg); for (segment_index_t b = blocks.first; b < blocks.second; ++b) { if ((*my_table)[b] == nullptr) return false; } return true; } private: using block_range = std::pair; /** * @return block indexes [begin, end) for corresponding segment */ static block_range segment_blocks(segment_index_t seg) { segment_index_t begin = first_block_in_segment(seg); return block_range(begin, begin + blocks_in_segment(seg)); } void enable_first_block(pool_base &pop) { assert(my_seg == embedded_segments); { transaction::manual tx(pop); size_type sz = segment_size(first_block) - embedded_buckets; (*my_table)[my_seg] = make_persistent(sz); persistent_ptr base = (*my_table)[embedded_segments].raw(); for (segment_index_t s = my_seg + 1; s < first_block; ++s) { std::ptrdiff_t off = static_cast( segment_base(s) - segment_base(my_seg)); (*my_table)[s] = (base + off).raw(); } transaction::commit(); } } void enable_big_segment(pool_base &pop) { block_range blocks = segment_blocks(my_seg); { transaction::manual tx(pop); for (segment_index_t b = blocks.first; b < blocks.second; ++b) { assert((*my_table)[b] == nullptr); (*my_table)[b] = make_persistent( block_size(b)); } transaction::commit(); } } /** Pointer to the table of blocks */ table_pointer my_table; /** Segment index */ segment_index_t my_seg; }; /* End of class segment_facade_impl */ /** * Base class of concurrent_hash_map. * Implements logic not dependent to Key/Value types. * MutexType - type of mutex used by buckets. * ScopedLockType - type of scoped lock for mutex. */ template class hash_map_base { public: using mutex_t = MutexType; using scoped_t = ScopedLockType; /** Size type. */ using size_type = size_t; /** Type of a hash code. */ using hashcode_type = size_t; /** Node base type. */ using node = hash_map_node; /** Node base pointer. */ using node_ptr_t = detail::persistent_pool_ptr; /** Bucket type. */ struct bucket { using mutex_t = MutexType; using scoped_t = ScopedLockType; /** Bucket mutex. */ mutex_t mutex; /** Atomic flag to indicate if bucket rehashed */ p> rehashed; /** List of the nodes stored in the bucket. */ node_ptr_t node_list; /** Default constructor */ bucket() : node_list(nullptr) { #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED VALGRIND_HG_DISABLE_CHECKING(&rehashed, sizeof(rehashed)); #endif rehashed.get_rw() = false; } /** * @returns true if bucket rehashed and ready to use. * Otherwise, @returns false if rehash is * required */ bool is_rehashed(std::memory_order order) { return rehashed.get_ro().load(order); } void set_rehashed(std::memory_order order) { rehashed.get_rw().store(true, order); } /** Copy constructor is deleted */ bucket(const bucket &) = delete; /** Assignment operator is deleted */ bucket &operator=(const bucket &) = delete; }; /* End of struct bucket */ /** Segment traits */ using segment_traits_t = segment_traits; /** Segment index type. */ using segment_index_t = typename segment_traits_t::segment_index_t; /** Count of buckets in the embedded segments */ static const size_type embedded_buckets = segment_traits_t::embedded_buckets; /** Count of segments in the first block. */ static const size_type first_block = segment_traits_t::first_block; /** Size of a block_table. */ constexpr static size_type block_table_size = segment_traits_t::number_of_blocks(); /** Segment pointer. */ using segment_ptr_t = persistent_ptr; /** Bucket pointer. */ using bucket_ptr_t = persistent_ptr; /** Block pointers table type. */ using blocks_table_t = segment_ptr_t[block_table_size]; /** Segment mutex type. */ using segment_enable_mutex_t = pmem::obj::mutex; /** Data specific for every thread using concurrent_hash_map */ struct tls_data_t { p size_diff = 0; std::aligned_storage<56, 8> padding; }; using tls_t = detail::enumerable_thread_specific; enum feature_flags : uint32_t { FEATURE_CONSISTENT_SIZE = 1 }; /** Compat and incompat features of a layout */ struct features { p compat; p incompat; }; /* --------------------------------------------------------- */ /** ID of persistent memory pool where hash map resides. */ p my_pool_uuid; /** Specifies features of a hashmap, used to check compatibility between * header and the data */ features layout_features; /** In future, my_mask can be implemented using v<> (8 bytes * overhead) */ std::aligned_storage::type my_mask_reserved; /** Hash mask = sum of allocated segment sizes - 1. */ /* my_mask always restored on restart. */ std::atomic my_mask; /* Size of value (key and value pair) stored in a pool */ std::size_t value_size; /** Padding to the end of cacheline */ std::aligned_storage<24, 8>::type padding1; /** * Segment pointers table. Also prevents false sharing between my_mask * and my_size. */ blocks_table_t my_table; /* It must be in separate cache line from my_mask due to performance * effects */ /** Size of container in stored items. */ std::atomic my_size; /** Padding to the end of cacheline */ std::aligned_storage<24, 8>::type padding2; /** Thread specific data */ persistent_ptr tls_ptr; /** * This variable holds real size after hash_map is initialized. * It holds real value of size only after initialization (before any * insert/remove). */ p on_init_size; /** Reserved for future use */ std::aligned_storage<40, 8>::type reserved; /** Segment mutex used to enable new segment. */ segment_enable_mutex_t my_segment_enable_mutex; /** Zero segment. */ bucket my_embedded_segment[embedded_buckets]; /* --------------------------------------------------------- */ /** Features supported by this header */ static constexpr features header_features() { return {FEATURE_CONSISTENT_SIZE, 0}; } const std::atomic & mask() const noexcept { return my_mask; } std::atomic & mask() noexcept { return my_mask; } size_t size() const { return my_size.load(std::memory_order_relaxed); } p & thread_size_diff() { assert(this->tls_ptr != nullptr); return this->tls_ptr->local().size_diff; } /** Process any information which was saved to tls and clears tls */ void tls_restore() { assert(this->tls_ptr != nullptr); pool_base pop = pool_base{pmemobj_pool_by_ptr(this)}; int64_t last_run_size = 0; for (auto &data : *tls_ptr) last_run_size += data.size_diff; /* Make sure that on_init_size + last_run_size >= 0 */ assert(last_run_size >= 0 || static_cast(static_cast(last_run_size) + on_init_size) >= 0); transaction::run(pop, [&] { on_init_size += static_cast(last_run_size); tls_ptr->clear(); }); this->my_size = on_init_size; } /** Const segment facade type */ using const_segment_facade_t = segment_facade_impl; /** Non-const segment facade type */ using segment_facade_t = segment_facade_impl; /** Default constructor */ hash_map_base() { static_assert( sizeof(size_type) == sizeof(std::atomic), "std::atomic should have the same layout as underlying integral type"); #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED VALGRIND_HG_DISABLE_CHECKING(&my_mask, sizeof(my_mask)); #endif layout_features = {0, 0}; PMEMoid oid = pmemobj_oid(this); assert(!OID_IS_NULL(oid)); my_pool_uuid = oid.pool_uuid_lo; pool_base pop = get_pool_base(); /* enable embedded segments */ for (size_type i = 0; i < segment_traits_t::embedded_segments; ++i) { my_table[i] = pmemobj_oid(my_embedded_segment + segment_traits_t::segment_base(i)); segment_facade_t seg(my_table, i); mark_rehashed(pop, seg); } on_init_size = 0; value_size = 0; this->tls_ptr = nullptr; } /* * Should be called before concurrent_hash_map destructor is called. * Otherwise, program can terminate if an exception occurs wile freeing * memory inside dtor. */ void free_tls() { auto pop = get_pool_base(); if ((layout_features.compat & FEATURE_CONSISTENT_SIZE) && tls_ptr) { transaction::run(pop, [&] { delete_persistent(tls_ptr); tls_ptr = nullptr; }); } } /** * Re-calculate mask value on each process restart. */ void calculate_mask() { #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED VALGRIND_HG_DISABLE_CHECKING(&my_size, sizeof(my_size)); VALGRIND_HG_DISABLE_CHECKING(&my_mask, sizeof(my_mask)); #endif #if LIBPMEMOBJ_CPP_VG_PMEMCHECK_ENABLED VALGRIND_PMC_REMOVE_PMEM_MAPPING(&my_size, sizeof(my_size)); VALGRIND_PMC_REMOVE_PMEM_MAPPING(&my_mask, sizeof(my_mask)); #endif hashcode_type m = embedded_buckets - 1; const_segment_facade_t segment( my_table, segment_traits_t::embedded_segments); while (segment.is_valid()) { m += segment.size(); ++segment; } mask().store(m, std::memory_order_relaxed); } /** * Initialize buckets in the new segment. */ template void mark_rehashed(pool_base &pop, segment_facade_t &segment) { for (size_type i = 0; i < segment.size(); ++i) { bucket *b = &(segment[i]); assert_not_locked(b->mutex); b->set_rehashed(std::memory_order_relaxed); } if (Flush) { /* Flush in separate loop to avoid read-after-flush */ for (size_type i = 0; i < segment.size(); ++i) { bucket *b = &(segment[i]); pop.flush(b->rehashed); } pop.drain(); } } /** * Enable new segment in the hashmap */ void enable_segment(segment_index_t k, bool is_initial = false) { assert(k); pool_base pop = get_pool_base(); size_type sz; if (k >= first_block) { segment_facade_t new_segment(my_table, k); sz = new_segment.size(); if (!new_segment.is_valid()) new_segment.enable(pop); if (is_initial) { mark_rehashed(pop, new_segment); } /* double it to get entire capacity of the container */ sz <<= 1; } else { /* the first block */ assert(k == segment_traits_t::embedded_segments); for (segment_index_t i = k; i < first_block; ++i) { segment_facade_t new_segment(my_table, i); if (!new_segment.is_valid()) new_segment.enable(pop); if (is_initial) { mark_rehashed(pop, new_segment); } } sz = segment_traits_t::segment_size(first_block); } #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED ANNOTATE_HAPPENS_BEFORE(&my_mask); #endif mask().store(sz - 1, std::memory_order_release); } /** * Get bucket by (masked) hashcode. * @return pointer to the bucket. */ bucket * get_bucket(hashcode_type h) const { segment_index_t s = segment_traits_t::segment_index_of(h); h -= segment_traits_t::segment_base(s); const_segment_facade_t segment(my_table, s); assert(segment.is_valid()); return &(segment[h]); } /** * Check for mask race */ inline bool check_mask_race(hashcode_type h, hashcode_type &m) const { hashcode_type m_now, m_old = m; m_now = mask().load(std::memory_order_acquire); #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED ANNOTATE_HAPPENS_AFTER(&(this->my_mask)); #endif if (m_old != m_now) return check_rehashing_collision(h, m_old, m = m_now); return false; } /** * Process mask race, check for rehashing collision */ bool check_rehashing_collision(hashcode_type h, hashcode_type m_old, hashcode_type m) const { assert(m_old != m); if ((h & m_old) != (h & m)) { /* mask changed for this hashcode, rare event condition * above proves that 'h' has some other bits set beside * 'm_old', find next applicable mask after m_old */ for (++m_old; !(h & m_old); m_old <<= 1) ; m_old = (m_old << 1) - 1; /* get full mask from a bit */ assert((m_old & (m_old + 1)) == 0 && m_old <= m); /* check whether it is rehashing/ed */ bucket *b = get_bucket(h & m_old); return b->is_rehashed(std::memory_order_acquire); } return false; } /** * Insert a node to bucket. * @pre must be called inside transaction. */ template void insert_new_node_internal(bucket *b, detail::persistent_pool_ptr &new_node, Args &&... args) { assert(pmemobj_tx_stage() == TX_STAGE_WORK); new_node = pmem::obj::make_persistent( b->node_list, std::forward(args)...); b->node_list = new_node; /* bucket is locked */ } /** * Insert a node. * @return new size. */ template size_type insert_new_node(bucket *b, detail::persistent_pool_ptr &new_node, Args &&... args) { pool_base pop = get_pool_base(); /* * This is only true when called from singlethreaded methods * like swap() or operator=. In that case it's safe to directly * modify on_init_size. */ if (pmemobj_tx_stage() == TX_STAGE_WORK) { insert_new_node_internal(b, new_node, std::forward(args)...); this->on_init_size++; } else { auto &size_diff = thread_size_diff(); pmem::obj::transaction::run(pop, [&] { insert_new_node_internal( b, new_node, std::forward(args)...); ++size_diff; }); } /* Increment volatile size */ return ++(this->my_size); } /** * Checks load factor and decides if new segment should be allocated. * @return true if new segment was allocated and false otherwise */ bool check_growth(hashcode_type m, size_type sz) { if (sz >= m) { segment_index_t new_seg = static_cast(detail::Log2( m + 1)); /* optimized segment_index_of */ assert(segment_facade_t(my_table, new_seg - 1) .is_valid()); std::unique_lock lock( my_segment_enable_mutex, std::try_to_lock); if (lock) { if (mask().load(std::memory_order_relaxed) == m) { /* Otherwise, other thread enable this * segment */ enable_segment(new_seg); return true; } } } return false; } /** * Prepare enough segments for number of buckets */ void reserve(size_type buckets) { if (buckets == 0) return; --buckets; bool is_initial = this->size() == 0; for (size_type m = mask(); buckets > m; m = mask()) enable_segment( segment_traits_t::segment_index_of(m + 1), is_initial); } /** * Swap hash_map_base * @throws std::transaction_error in case of PMDK transaction failed */ void internal_swap(hash_map_base &table) { pool_base p = get_pool_base(); { transaction::manual tx(p); this->my_pool_uuid.swap(table.my_pool_uuid); /* * As internal_swap can only be called * from one thread, and there can be an outer * transaction we must make sure that mask and size * changes are transactional */ transaction::snapshot((size_t *)&this->my_mask); transaction::snapshot((size_t *)&this->my_size); this->mask() = table.mask().exchange( this->mask(), std::memory_order_relaxed); this->my_size = table.my_size.exchange( this->my_size, std::memory_order_relaxed); /* Swap consistent size */ std::swap(this->tls_ptr, table.tls_ptr); for (size_type i = 0; i < embedded_buckets; ++i) this->my_embedded_segment[i].node_list.swap( table.my_embedded_segment[i].node_list); for (size_type i = segment_traits_t::embedded_segments; i < block_table_size; ++i) this->my_table[i].swap(table.my_table[i]); transaction::commit(); } } /** * Get the persistent memory pool where hashmap resides. * @returns pmem::obj::pool_base object. */ pool_base get_pool_base() { PMEMobjpool *pop = pmemobj_pool_by_oid(PMEMoid{my_pool_uuid, 0}); return pool_base(pop); } }; /* End of class hash_map_base */ /** * Meets requirements of a forward iterator for STL * Value is either the T or const T type of the container. * @ingroup containers */ template class hash_map_iterator { public: using iterator_category = std::forward_iterator_tag; using difference_type = ptrdiff_t; using map_type = Container; using value_type = typename map_type::value_type; using node = typename map_type::node; using bucket = typename map_type::bucket; using map_ptr = typename std::conditional::type; using reference = typename std::conditional::type; using pointer = typename std::conditional::type; template friend bool operator==(const hash_map_iterator &i, const hash_map_iterator &j); template friend bool operator!=(const hash_map_iterator &i, const hash_map_iterator &j); friend class hash_map_iterator; #if !defined(_MSC_VER) || defined(__INTEL_COMPILER) private: template friend class ::pmem::obj::concurrent_hash_map; #else public: /* workaround */ #endif hash_map_iterator(map_ptr map, size_t index) : my_map(map), my_index(index), my_bucket(nullptr), my_node(nullptr) { if (my_index <= my_map->mask()) { bucket_accessor acc(my_map, my_index); my_bucket = acc.get(); my_node = static_cast( my_bucket->node_list.get(my_map->my_pool_uuid)); if (!my_node) { advance_to_next_bucket(); } } } public: /** Construct undefined iterator. */ hash_map_iterator() = default; /** Copy constructor. */ hash_map_iterator(const hash_map_iterator &other) : my_map(other.my_map), my_index(other.my_index), my_bucket(other.my_bucket), my_node(other.my_node) { } /** Copy constructor for const iterator from non-const iterator */ template ::type> hash_map_iterator(const hash_map_iterator &other) : my_map(other.my_map), my_index(other.my_index), my_bucket(other.my_bucket), my_node(other.my_node) { } hash_map_iterator &operator=(const hash_map_iterator &it) = default; /** Indirection (dereference). */ reference operator*() const { assert(my_node); return my_node->item; } /** Member access. */ pointer operator->() const { return &operator*(); } /** Prefix increment. */ hash_map_iterator & operator++() { my_node = static_cast( my_node->next.get((my_map->my_pool_uuid))); if (!my_node) advance_to_next_bucket(); return *this; } /** Postfix increment. */ hash_map_iterator operator++(int) { hash_map_iterator old(*this); operator++(); return old; } private: /** Concurrent_hash_map over which we are iterating. */ map_ptr my_map = nullptr; /** Bucket index for current item. */ size_t my_index = 0; /** Pointer to bucket. */ bucket *my_bucket = nullptr; /** Pointer to node that has current item. */ node *my_node = nullptr; class bucket_accessor { public: bucket_accessor(map_ptr m, size_t index) { my_bucket = m->get_bucket(index); } bucket * get() const { return my_bucket; } private: bucket *my_bucket; }; void advance_to_next_bucket() { size_t k = my_index + 1; assert(my_bucket); while (k <= my_map->mask()) { bucket_accessor acc(my_map, k); my_bucket = acc.get(); if (my_bucket->node_list) { my_node = static_cast( my_bucket->node_list.get( my_map->my_pool_uuid)); my_index = k; return; } ++k; } my_bucket = 0; my_node = 0; my_index = k; } }; template bool operator==(const hash_map_iterator &i, const hash_map_iterator &j) { return i.my_node == j.my_node && i.my_map == j.my_map; } template bool operator!=(const hash_map_iterator &i, const hash_map_iterator &j) { return i.my_node != j.my_node || i.my_map != j.my_map; } } /* namespace concurrent_hash_map_internal */ /** @endcond */ /** * Persistent memory aware implementation of Intel TBB concurrent_hash_map. * The implementation is based on a concurrent hash table algorithm * (https://arxiv.org/ftp/arxiv/papers/1509/1509.02235.pdf) where elements * assigned to buckets based on a hash code are calculated from a key. * In addition to concurrent find, insert, and erase operations, the algorithm * employs resizing and on-demand per-bucket rehashing. The hash table consists * of an array of buckets, and each bucket consists of a list of nodes and a * read-write lock to control concurrent access by multiple threads. * * Each time, the pool with concurrent_hash_map is being opened, the * concurrent_hash_map requires runtime_initialize() to be called (in order to * recalculate mask and restore the size). * * find(), insert(), erase() (and all overloads) are guaranteed to be * thread-safe. * * When a thread holds accessor to an element with a certain key, it is not * allowed to call find, insert nor erase with that key. * * MutexType defines type of read write lock used in concurrent_hash_map. * ScopedLockType defines a mutex wrapper that provides RAII-style mechanism * for owning a mutex. It should implement following methods and constructors: * ScopedLockType() * ScopedLockType(rw_mutex_type &m, bool write = true) * void acquire(rw_mutex_type &m, bool write) * void release() * bool try_acquire(rw_mutex_type &m, bool write) * * and optionally: * bool upgrade_to_writer() * bool downgrade_to_reader() * bool is_writer (variable) * * Implementing all optional methods and supplying is_writer variable can * improve performance if MutexType supports efficient upgrading and * downgrading operations. * * Testing note: * In some case, helgrind and drd might report lock ordering errors for * concurrent_hash_map. This might happen when calling find, insert or erase * while already holding an accessor to some element. * * The typical usage example would be: * @snippet doc_snippets/concurrent_hash_map.cpp concurrent_hash_map_example */ template class concurrent_hash_map : protected concurrent_hash_map_internal::hash_map_base { template friend class concurrent_hash_map_internal::hash_map_iterator; public: using size_type = typename concurrent_hash_map_internal::hash_map_base< Key, T, MutexType, ScopedLockType>::size_type; using hashcode_type = typename concurrent_hash_map_internal::hash_map_base< Key, T, MutexType, ScopedLockType>::hashcode_type; using key_type = Key; using mapped_type = T; using value_type = typename concurrent_hash_map_internal::hash_map_base< Key, T, MutexType, ScopedLockType>::node::value_type; using difference_type = ptrdiff_t; using pointer = value_type *; using const_pointer = const value_type *; using reference = value_type &; using const_reference = const value_type &; using iterator = concurrent_hash_map_internal::hash_map_iterator< concurrent_hash_map, false>; using const_iterator = concurrent_hash_map_internal::hash_map_iterator< concurrent_hash_map, true>; using hasher = Hash; using key_equal = typename concurrent_hash_map_internal::key_equal_type< Hash, KeyEqual>::type; protected: using mutex_t = MutexType; using scoped_t = ScopedLockType; /* * Explicitly use methods and types from template base class */ using hash_map_base = concurrent_hash_map_internal::hash_map_base; using hash_map_base::calculate_mask; using hash_map_base::check_growth; using hash_map_base::check_mask_race; using hash_map_base::embedded_buckets; using hash_map_base::FEATURE_CONSISTENT_SIZE; using hash_map_base::get_bucket; using hash_map_base::get_pool_base; using hash_map_base::header_features; using hash_map_base::insert_new_node; using hash_map_base::internal_swap; using hash_map_base::layout_features; using hash_map_base::mask; using hash_map_base::reserve; using tls_t = typename hash_map_base::tls_t; using node = typename hash_map_base::node; using node_mutex_t = typename node::mutex_t; using node_ptr_t = typename hash_map_base::node_ptr_t; using bucket = typename hash_map_base::bucket; using bucket_lock_type = typename bucket::scoped_t; using segment_index_t = typename hash_map_base::segment_index_t; using segment_traits_t = typename hash_map_base::segment_traits_t; using segment_facade_t = typename hash_map_base::segment_facade_t; using scoped_lock_traits_type = concurrent_hash_map_internal::scoped_lock_traits; friend class const_accessor; using persistent_node_ptr_t = detail::persistent_pool_ptr; void delete_node(const node_ptr_t &n) { delete_persistent( detail::static_persistent_pool_pointer_cast(n) .get_persistent_ptr(this->my_pool_uuid)); } template persistent_node_ptr_t search_bucket(const K &key, bucket *b) const { assert(b->is_rehashed(std::memory_order_relaxed)); persistent_node_ptr_t n = detail::static_persistent_pool_pointer_cast( b->node_list); while (n && !key_equal{}(key, n.get(this->my_pool_uuid)->item.first)) { n = detail::static_persistent_pool_pointer_cast( n.get(this->my_pool_uuid)->next); } return n; } /** * Bucket accessor is to find, rehash, acquire a lock, and access a * bucket */ class bucket_accessor : public bucket_lock_type { bucket *my_b; public: bucket_accessor(bucket_accessor &&b) noexcept : my_b(b.my_b) { bucket_lock_type::mutex = b.bucket_lock_type::mutex; bucket_lock_type::is_writer = b.bucket_lock_type::is_writer; b.my_b = nullptr; b.bucket_lock_type::mutex = nullptr; b.bucket_lock_type::is_writer = false; } bucket_accessor(concurrent_hash_map *base, const hashcode_type h, bool writer = false) { acquire(base, h, writer); } /** * Find a bucket by masked hashcode, optionally rehash, and * acquire the lock */ inline void acquire(concurrent_hash_map *base, const hashcode_type h, bool writer = false) { my_b = base->get_bucket(h); if (my_b->is_rehashed(std::memory_order_acquire) == false && bucket_lock_type::try_acquire(this->my_b->mutex, /*write=*/true)) { if (my_b->is_rehashed( std::memory_order_relaxed) == false) { /* recursive rehashing */ base->rehash_bucket(my_b, h); } } else { bucket_lock_type::acquire(my_b->mutex, writer); } assert(my_b->is_rehashed(std::memory_order_relaxed)); } /** * Check whether bucket is locked for write */ bool is_writer() const { return bucket_lock_type::is_writer; } /** * Get bucket pointer * @return pointer to the underlying bucket */ bucket * get() const { return my_b; } /** * Overloaded arrow operator * @returns pointer to the underlying bucket */ bucket *operator->() const { return this->get(); } }; /** * Serial bucket accessor used to access bucket in a serial operations. */ class serial_bucket_accessor { bucket *my_b; public: serial_bucket_accessor(concurrent_hash_map *base, const hashcode_type h, bool writer = false) { acquire(base, h, writer); } /* * Find a bucket by masked hashcode, optionally rehash */ inline void acquire(concurrent_hash_map *base, const hashcode_type h, bool writer = false) { my_b = base->get_bucket(h); if (my_b->is_rehashed(std::memory_order_relaxed) == false) { /* recursive rehashing */ base->rehash_bucket(my_b, h); } assert(my_b->is_rehashed(std::memory_order_relaxed)); } /** * This method is added for consistency with bucket_accessor * class * * @return Always returns true */ bool is_writer() const { return true; } /** * Get bucket pointer * @return pointer to the bucket */ bucket * get() const { return my_b; } /** * Overloaded arrow operator * @returns pointer to the underlying bucket */ bucket *operator->() const { return this->get(); } }; hashcode_type get_hash_code(node_ptr_t &n) { return hasher{}( detail::static_persistent_pool_pointer_cast(n)( this->my_pool_uuid) ->item.first); } template void rehash_bucket(bucket *b_new, const hashcode_type h) { using accessor_type = typename std::conditional< serial, serial_bucket_accessor, bucket_accessor>::type; using scoped_lock_traits_type = concurrent_hash_map_internal::scoped_lock_traits< accessor_type>; /* First two bucket should be always rehashed */ assert(h > 1); pool_base pop = get_pool_base(); node_ptr_t *p_new = &(b_new->node_list); /* This condition is only true when there was a failure just * before setting rehashed flag */ if (*p_new != nullptr) { assert(!b_new->is_rehashed(std::memory_order_relaxed)); b_new->set_rehashed(std::memory_order_relaxed); pop.persist(b_new->rehashed); return; } /* get parent mask from the topmost bit */ hashcode_type mask = (1u << detail::Log2(h)) - 1; assert((h & mask) < h); accessor_type b_old( this, h & mask, scoped_lock_traits_type::initial_rw_state(true)); pmem::obj::transaction::run(pop, [&] { /* get full mask for new bucket */ mask = (mask << 1) | 1; assert((mask & (mask + 1)) == 0 && (h & mask) == h); restart: for (node_ptr_t *p_old = &(b_old->node_list), n = *p_old; n; n = *p_old) { hashcode_type c = get_hash_code(n); #ifndef NDEBUG hashcode_type bmask = h & (mask >> 1); bmask = bmask == 0 ? 1 /* minimal mask of parent bucket */ : (1u << (detail::Log2(bmask) + 1)) - 1; assert((c & bmask) == (h & bmask)); #endif if ((c & mask) == h) { if (!b_old.is_writer() && !scoped_lock_traits_type:: upgrade_to_writer(b_old)) { goto restart; /* node ptr can be invalid due * to concurrent erase */ } /* Add to new b_new */ *p_new = n; /* exclude from b_old */ *p_old = n(this->my_pool_uuid)->next; p_new = &(n(this->my_pool_uuid)->next); } else { /* iterate to next item */ p_old = &(n(this->my_pool_uuid)->next); } } *p_new = nullptr; }); /* mark rehashed */ b_new->set_rehashed(std::memory_order_release); pop.persist(b_new->rehashed); } void check_incompat_features() { if (layout_features.incompat != header_features().incompat) throw pmem::layout_error( "Incompat flags mismatch, for more details go to: https://pmem.io/pmdk/cpp_obj/ \n"); if ((layout_features.compat & FEATURE_CONSISTENT_SIZE) && this->value_size != sizeof(value_type)) throw pmem::layout_error( "Size of value_type is different than the one stored in the pool \n"); } public: class accessor; /** * Combines data access, locking, and garbage collection. */ class const_accessor : protected node::scoped_t /*which derived from no_copy*/ { friend class concurrent_hash_map; friend class accessor; using node_ptr_t = pmem::obj::persistent_ptr; using node::scoped_t::try_acquire; public: /** * Type of value */ using value_type = const typename concurrent_hash_map::value_type; /** * @returns true if accessor does not hold any element, false * otherwise. */ bool empty() const { return !my_node; } /** * Release accessor. * Cannot be called inside of a transaction. * * @throw transaction_scope_error if called inside transaction */ void release() { concurrent_hash_map_internal::check_outside_tx(); if (my_node) { node::scoped_t::release(); my_node = 0; } } /** * @return reference to associated value in hash table. */ const_reference operator*() const { assert(my_node); return my_node->item; } /** * @returns pointer to associated value in hash table. */ const_pointer operator->() const { return &operator*(); } /** * Create empty result * * Cannot be used in a transaction. */ const_accessor() : my_node(OID_NULL), my_hash() { concurrent_hash_map_internal::check_outside_tx(); } /** * Destroy result after releasing the underlying reference. */ ~const_accessor() { my_node = OID_NULL; // scoped lock's release() is called // in its destructor } protected: node_ptr_t my_node; hashcode_type my_hash; }; /** * Allows write access to elements and combines data access, locking, * and garbage collection. */ class accessor : public const_accessor { public: /** Type of value. */ using value_type = typename concurrent_hash_map::value_type; /** Return reference to associated value in hash table. */ reference operator*() const { assert(this->my_node); return this->my_node->item; } /** Return pointer to associated value in hash table. */ pointer operator->() const { return &operator*(); } }; /** * Construct empty table. */ concurrent_hash_map() : hash_map_base() { runtime_initialize(); } /** * Construct empty table with n preallocated buckets. This number * serves also as initial concurrency level. */ concurrent_hash_map(size_type n) : hash_map_base() { runtime_initialize(); reserve(n); } /** * Copy constructor */ concurrent_hash_map(const concurrent_hash_map &table) : hash_map_base() { runtime_initialize(); reserve(table.size()); internal_copy(table); } /** * Move constructor */ concurrent_hash_map(concurrent_hash_map &&table) : hash_map_base() { runtime_initialize(); swap(table); } /** * Construction table with copying iteration range */ template concurrent_hash_map(I first, I last) { runtime_initialize(); reserve(static_cast(std::distance(first, last))); internal_copy(first, last); } /** * Construct table with initializer list */ concurrent_hash_map(std::initializer_list il) { runtime_initialize(); reserve(il.size()); internal_copy(il.begin(), il.end()); } /** * Initialize persistent concurrent hash map after process restart. * MUST be called every time after process restart. * Not thread safe. * * @throw pmem::layout_error if hashmap was created using incompatible * version of libpmemobj-cpp */ void runtime_initialize() { check_incompat_features(); calculate_mask(); /* * Handle case where hash_map was created without * FEATURE_CONSISTENT_SIZE. */ if (!(layout_features.compat & FEATURE_CONSISTENT_SIZE)) { auto actual_size = std::distance(this->begin(), this->end()); assert(actual_size >= 0); this->my_size = static_cast(actual_size); auto pop = get_pool_base(); transaction::run(pop, [&] { this->tls_ptr = make_persistent(); this->on_init_size = static_cast(actual_size); this->value_size = sizeof(value_type); layout_features.compat |= FEATURE_CONSISTENT_SIZE; }); } else { assert(this->tls_ptr != nullptr); this->tls_restore(); } assert(this->size() == size_type(std::distance(this->begin(), this->end()))); } [[deprecated( "runtime_initialize(bool) is now deprecated, use runtime_initialize(void)")]] void runtime_initialize(bool graceful_shutdown) { check_incompat_features(); calculate_mask(); if (!graceful_shutdown) { auto actual_size = std::distance(this->begin(), this->end()); assert(actual_size >= 0); this->my_size = static_cast(actual_size); } else { assert(this->size() == size_type(std::distance(this->begin(), this->end()))); } } /** * Assignment * Not thread safe. * * @throw std::transaction_error in case of PMDK transaction failure * @throw pmem::transaction_alloc_error when allocating new memory * failed. * @throw pmem::transaction_free_error when freeing old underlying array * failed. * @throw rethrows constructor exception. */ concurrent_hash_map & operator=(const concurrent_hash_map &table) { if (this != &table) { clear(); internal_copy(table); } return *this; } /** * Assignment * Not thread safe. * * @throw std::transaction_error in case of PMDK transaction failure * @throw pmem::transaction_alloc_error when allocating new memory * failed. * @throw pmem::transaction_free_error when freeing old underlying array * failed. * @throw rethrows constructor exception. */ concurrent_hash_map & operator=(std::initializer_list il) { clear(); reserve(il.size()); internal_copy(il.begin(), il.end()); return *this; } /** * Rehashes and optionally resizes the whole table. * Useful to optimize performance before or after concurrent * operations. * Not thread safe. * * @throw pmem::transaction_scope_error if called inside transaction */ void rehash(size_type n = 0); /** * Clear hash map content * Not thread safe. * * @throws pmem::transaction_error in case of PMDK transaction failure */ void clear(); /* * Should be called before concurrent_hash_map destructor is called. * Otherwise, program can terminate if an exception occurs while freeing * memory inside dtor. * * Hash map can NOT be used after free_data() was called (unless this * was done in a transaction and transaction aborted). * * @throw std::transaction_error in case of PMDK transaction failure * @throw pmem::transaction_free_error when freeing underlying memory * failed. */ void free_data() { auto pop = get_pool_base(); transaction::run(pop, [&] { clear(); this->free_tls(); }); } /** * Clear table and destroy it. */ ~concurrent_hash_map() { free_data(); } //------------------------------------------------------------------------ // STL support - not thread-safe methods //------------------------------------------------------------------------ /** * @returns an iterator to the beginning * Not thread safe. * * @throw pmem::transaction_scope_error if called inside transaction */ iterator begin() { return iterator(this, 0); } /** * @returns an iterator to the end * Not thread safe. */ iterator end() { return iterator(this, mask() + 1); } /** * @returns an iterator to the beginning * Not thread safe. */ const_iterator begin() const { return const_iterator(this, 0); } /** * @returns an iterator to the end * Not thread safe. */ const_iterator end() const { return const_iterator(this, mask() + 1); } /** * @returns number of items in table. */ size_type size() const { return hash_map_base::size(); } /** * @returns true if size()==0. */ bool empty() const { return this->size() == 0; } /** * Upper bound on size. */ size_type max_size() const { return (~size_type(0)) / sizeof(node); } /** * @returns the current number of buckets */ size_type bucket_count() const { return mask() + 1; } /** * Swap two instances. Iterators are invalidated. Not thread safe. */ void swap(concurrent_hash_map &table); //------------------------------------------------------------------------ // concurrent map operations //------------------------------------------------------------------------ /** * @return count of items (0 or 1) * * @throw pmem::transaction_scope_error if called inside transaction */ size_type count(const Key &key) const { concurrent_hash_map_internal::check_outside_tx(); return const_cast(this)->internal_find( key, nullptr, false); } /** * This overload only participates in overload resolution if the * qualified-id Hash::transparent_key_equal is valid and denotes a type. * This assumes that such Hash is callable with both K and Key type, and * that its key_equal is transparent, which, together, allows calling * this function without constructing an instance of Key * * @return count of items (0 or 1) * * @throw pmem::transaction_scope_error if called inside transaction */ template ::value, K>::type> size_type count(const K &key) const { concurrent_hash_map_internal::check_outside_tx(); return const_cast(this)->internal_find( key, nullptr, false); } /** * Find item and acquire a read lock on the item. * @return true if item is found, false otherwise. * * @throw pmem::transaction_scope_error if called inside transaction */ bool find(const_accessor &result, const Key &key) const { concurrent_hash_map_internal::check_outside_tx(); result.release(); return const_cast(this)->internal_find( key, &result, false); } /** * Find item and acquire a read lock on the item. * * This overload only participates in overload resolution if the * qualified-id Hash::transparent_key_equal is valid and denotes a type. * This assumes that such Hash is callable with both K and Key type, and * that its key_equal is transparent, which, together, allows calling * this function without constructing an instance of Key * * @return true if item is found, false otherwise. * * @throw pmem::transaction_scope_error if called inside transaction */ template ::value, K>::type> bool find(const_accessor &result, const K &key) const { concurrent_hash_map_internal::check_outside_tx(); result.release(); return const_cast(this)->internal_find( key, &result, false); } /** * Find item and acquire a write lock on the item. * @return true if item is found, false otherwise. * * @throw pmem::transaction_scope_error if called inside transaction */ bool find(accessor &result, const Key &key) { concurrent_hash_map_internal::check_outside_tx(); result.release(); return internal_find(key, &result, true); } /** * Find item and acquire a write lock on the item. * * This overload only participates in overload resolution if the * qualified-id Hash::transparent_key_equal is valid and denotes a type. * This assumes that such Hash is callable with both K and Key type, and * that its key_equal is transparent, which, together, allows calling * this function without constructing an instance of Key * * @return true if item is found, false otherwise. * * @throw pmem::transaction_scope_error if called inside transaction */ template ::value, K>::type> bool find(accessor &result, const K &key) { concurrent_hash_map_internal::check_outside_tx(); result.release(); return internal_find(key, &result, true); } /** * Insert item (if not already present) and * acquire a read lock on the item. * @return true if item is new. * @throw pmem::transaction_alloc_error on allocation failure. * @throw pmem::transaction_scope_error if called inside transaction */ bool insert(const_accessor &result, const Key &key) { concurrent_hash_map_internal::check_outside_tx(); result.release(); return internal_insert(key, &result, false, key); } /** * Insert item (if not already present) and * acquire a write lock on the item. * @returns true if item is new. * @throw pmem::transaction_alloc_error on allocation failure. * @throw pmem::transaction_scope_error if called inside transaction */ bool insert(accessor &result, const Key &key) { concurrent_hash_map_internal::check_outside_tx(); result.release(); return internal_insert(key, &result, true, key); } /** * Insert item by copying if there is no such key present already and * acquire a read lock on the item. * @return true if item is new. * @throw pmem::transaction_alloc_error on allocation failure. * @throw pmem::transaction_scope_error if called inside transaction */ bool insert(const_accessor &result, const value_type &value) { concurrent_hash_map_internal::check_outside_tx(); result.release(); return internal_insert(value.first, &result, false, value); } /** * Insert item by copying if there is no such key present already and * acquire a write lock on the item. * @return true if item is new. * * @throw pmem::transaction_scope_error if called inside transaction */ bool insert(accessor &result, const value_type &value) { concurrent_hash_map_internal::check_outside_tx(); result.release(); return internal_insert(value.first, &result, true, value); } /** * Insert item by copying if there is no such key present already * @return true if item is inserted. * * @throw pmem::transaction_scope_error if called inside transaction */ bool insert(const value_type &value) { concurrent_hash_map_internal::check_outside_tx(); return internal_insert(value.first, nullptr, false, value); } /** * Insert item by copying if there is no such key present already and * acquire a read lock on the item. * @return true if item is new. * @throw pmem::transaction_alloc_error on allocation failure. * @throw pmem::transaction_scope_error if called inside transaction */ bool insert(const_accessor &result, value_type &&value) { concurrent_hash_map_internal::check_outside_tx(); result.release(); return internal_insert(value.first, &result, false, std::move(value)); } /** * Insert item by copying if there is no such key present already and * acquire a write lock on the item. * @return true if item is new. * @throw pmem::transaction_alloc_error on allocation failure. * @throw pmem::transaction_scope_error if called inside transaction */ bool insert(accessor &result, value_type &&value) { concurrent_hash_map_internal::check_outside_tx(); result.release(); return internal_insert(value.first, &result, true, std::move(value)); } /** * Insert item by copying if there is no such key present already * @return true if item is inserted. * @throw pmem::transaction_alloc_error on allocation failure. * @throw pmem::transaction_scope_error if called inside transaction */ bool insert(value_type &&value) { concurrent_hash_map_internal::check_outside_tx(); return internal_insert(value.first, nullptr, false, std::move(value)); } /** * Insert range [first, last) * @throw pmem::transaction_alloc_error on allocation failure. * @throw pmem::transaction_scope_error if called inside transaction */ template void insert(I first, I last) { concurrent_hash_map_internal::check_outside_tx(); for (; first != last; ++first) insert(*first); } /** * Insert initializer list * @throw pmem::transaction_alloc_error on allocation failure. * @throw pmem::transaction_scope_error if called inside transaction */ void insert(std::initializer_list il) { concurrent_hash_map_internal::check_outside_tx(); insert(il.begin(), il.end()); } /** * Inserts item if there is no such key present already, assigns * provided value otherwise. * @return return true if the insertion took place and false if the * assignment took place. * @throw pmem::transaction_alloc_error on allocation failure. * @throw pmem::transaction_scope_error if called inside transaction */ template bool insert_or_assign(const key_type &key, M &&obj) { concurrent_hash_map_internal::check_outside_tx(); accessor acc; auto result = internal_insert(key, &acc, true, key, std::forward(obj)); if (!result) { pool_base pop = get_pool_base(); pmem::obj::transaction::manual tx(pop); acc->second = std::forward(obj); pmem::obj::transaction::commit(); } return result; } /** * Inserts item if there is no such key present already, assigns * provided value otherwise. * @return return true if the insertion took place and false if the * assignment took place. * @throw pmem::transaction_alloc_error on allocation failure. * @throw pmem::transaction_scope_error if called inside transaction */ template bool insert_or_assign(key_type &&key, M &&obj) { concurrent_hash_map_internal::check_outside_tx(); accessor acc; auto result = internal_insert(key, &acc, true, std::move(key), std::forward(obj)); if (!result) { pool_base pop = get_pool_base(); pmem::obj::transaction::manual tx(pop); acc->second = std::forward(obj); pmem::obj::transaction::commit(); } return result; } /** * Inserts item if there is no such key-comparable type present already, * assigns provided value otherwise. * @return return true if the insertion took place and false if the * assignment took place. * @throw pmem::transaction_alloc_error on allocation failure. * @throw pmem::transaction_scope_error if called inside transaction */ template < typename K, typename M, typename = typename std::enable_if< concurrent_hash_map_internal::has_transparent_key_equal< hasher>::value && std::is_constructible::value, K>::type> bool insert_or_assign(K &&key, M &&obj) { concurrent_hash_map_internal::check_outside_tx(); accessor acc; auto result = internal_insert(key, &acc, true, std::forward(key), std::forward(obj)); if (!result) { pool_base pop = get_pool_base(); pmem::obj::transaction::manual tx(pop); acc->second = std::forward(obj); pmem::obj::transaction::commit(); } return result; } /** * Remove element with corresponding key * * @return true if element was deleted by this call * @throws pmem::transaction_free_error in case of PMDK unable to free * the memory * @throw pmem::transaction_scope_error if called inside transaction */ bool erase(const Key &key) { concurrent_hash_map_internal::check_outside_tx(); return internal_erase(key); } /** * Defragment the given (by 'start_percent' and 'amount_percent') part * of buckets of the hash map. The algorithm is 'opportunistic' - * if it is not able to lock a bucket it will just skip it. * * @return result struct containing a number of relocated and total * processed objects. * * @throw std::range_error if the range: * [start_percent, start_percent + amount_percent] * is incorrect. * * @throw rethrows pmem::defrag_error when a failure during * defragmentation occurs. Even if this error is thrown, * some of objects could have been relocated, * see in such case defrag_error.result for summary stats. * */ pobj_defrag_result defragment(double start_percent = 0, double amount_percent = 100) { double end_percent = start_percent + amount_percent; if (start_percent < 0 || start_percent >= 100 || end_percent < 0 || end_percent > 100 || start_percent >= end_percent) { throw std::range_error("incorrect range"); } size_t max_index = mask().load(std::memory_order_acquire); size_t start_index = (start_percent * max_index) / 100; size_t end_index = (end_percent * max_index) / 100; #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED ANNOTATE_HAPPENS_AFTER(&(this->my_mask)); #endif /* Create defrag object for elements in the current pool */ pmem::obj::defrag my_defrag(this->get_pool_base()); mutex_vector mv; /* * Locks are taken in the backward order to avoid deadlocks * with the rehashing of buckets. * * We do '+ 1' and '- 1' to handle the 'i == 0' case. */ for (size_t i = end_index + 1; i >= start_index + 1; i--) { /* * All locks will be unlocked automatically * in the destructor of 'mv'. */ bucket *b = mv.push_and_try_lock(this, i - 1); if (b == nullptr) continue; defrag_save_nodes(b, my_defrag); } return my_defrag.run(); } /** * Remove element with corresponding key * * This overload only participates in overload resolution if the * qualified-id Hash::transparent_key_equal is valid and denotes a type. * This assumes that such Hash is callable with both K and Key type, and * that its key_equal is transparent, which, together, allows calling * this function without constructing an instance of Key * * @return true if element was deleted by this call * @throws pmem::transaction_free_error in case of PMDK unable to free * the memory * @throw pmem::transaction_scope_error if called inside transaction */ template ::value, K>::type> bool erase(const K &key) { concurrent_hash_map_internal::check_outside_tx(); return internal_erase(key); } protected: /* * Try to acquire the mutex for read or write. * * If acquiring succeeds returns true, otherwise retries for few times. * If acquiring fails after all attempts returns false. */ bool try_acquire_item(const_accessor *result, node_mutex_t &mutex, bool write); /** * Vector of locks to be unlocked at the destruction time. * MutexType - type of mutex used by buckets. */ class mutex_vector { public: using mutex_t = MutexType; /** Save pointer to the lock in the vector and lock it. */ bucket * push_and_try_lock(concurrent_hash_map *base, hashcode_type h) { vec.emplace_back(base, h, true /*writer*/); bucket *b = vec.back().get(); auto node_ptr = static_cast( b->node_list.get(base->my_pool_uuid)); while (node_ptr) { const_accessor ca; if (!base->try_acquire_item(&ca, node_ptr->mutex, /*write=*/true)) { vec.pop_back(); return nullptr; } node_ptr = static_cast(node_ptr->next.get( (base->my_pool_uuid))); } return b; } private: std::vector vec; }; template bool internal_find(const K &key, const_accessor *result, bool write); template bool internal_insert(const K &key, const_accessor *result, bool write, Args &&... args); /* Obtain pointer to node and lock bucket */ template persistent_node_ptr_t get_node(const K &key, bucket_accessor &b) { /* find a node */ auto n = search_bucket(key, b.get()); if (!n) { if (Bucket_rw_lock && !b.is_writer() && !scoped_lock_traits_type::upgrade_to_writer(b)) { /* Rerun search_list, in case another * thread inserted the item during the * upgrade. */ n = search_bucket(key, b.get()); if (n) { /* unfortunately, it did */ scoped_lock_traits_type:: downgrade_to_reader(b); return n; } } } return n; } template bool internal_erase(const K &key); void clear_segment(segment_index_t s); /** * Copy "source" to *this, where *this must start out empty. */ void internal_copy(const concurrent_hash_map &source); template void internal_copy(I first, I last); /** * Internal method used by defragment(). * Adds nodes to the defragmentation list. */ void defrag_save_nodes(bucket *b, pmem::obj::defrag &defrag) { auto node_ptr = static_cast( b->node_list.get(this->my_pool_uuid)); while (node_ptr) { /* * We do not perform the defragmentation * on node pointers, because nodes * always have the same size. */ defrag.add(node_ptr->item.first); defrag.add(node_ptr->item.second); node_ptr = static_cast( node_ptr->next.get((this->my_pool_uuid))); } } }; // class concurrent_hash_map template bool concurrent_hash_map::try_acquire_item(const_accessor *result, node_mutex_t &mutex, bool write) { /* acquire the item */ if (!result->try_acquire(mutex, write)) { for (detail::atomic_backoff backoff(true);;) { if (result->try_acquire(mutex, write)) break; if (!backoff.bounded_pause()) return false; } } return true; } template template bool concurrent_hash_map::internal_find(const K &key, const_accessor *result, bool write) { assert(!result || !result->my_node); hashcode_type m = mask().load(std::memory_order_acquire); #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED ANNOTATE_HAPPENS_AFTER(&(this->my_mask)); #endif assert((m & (m + 1)) == 0); hashcode_type const h = hasher{}(key); persistent_node_ptr_t node; while (true) { /* get bucket and acquire the lock */ bucket_accessor b( this, h & m, scoped_lock_traits_type::initial_rw_state(false)); node = get_node(key, b); if (!node) { /* Element was possibly relocated, try again */ if (check_mask_race(h, m)) { b.release(); continue; } else { return false; } } /* No need to acquire the item or item acquired */ if (!result || try_acquire_item( result, node.get(this->my_pool_uuid)->mutex, write)) break; /* the wait takes really long, restart the * operation */ b.release(); std::this_thread::yield(); m = mask().load(std::memory_order_acquire); #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED ANNOTATE_HAPPENS_AFTER(&(this->my_mask)); #endif } if (result) { result->my_node = node.get_persistent_ptr(this->my_pool_uuid); result->my_hash = h; } return true; } template template bool concurrent_hash_map::internal_insert(const K &key, const_accessor *result, bool write, Args &&... args) { assert(!result || !result->my_node); hashcode_type m = mask().load(std::memory_order_acquire); #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED ANNOTATE_HAPPENS_AFTER(&(this->my_mask)); #endif assert((m & (m + 1)) == 0); hashcode_type const h = hasher{}(key); persistent_node_ptr_t node; size_t new_size = 0; bool inserted = false; while (true) { /* get bucket and acquire the lock */ bucket_accessor b( this, h & m, scoped_lock_traits_type::initial_rw_state(true)); node = get_node(key, b); if (!node) { /* Element was possibly relocated, try again */ if (check_mask_race(h, m)) { b.release(); continue; } /* insert and set flag to grow the container */ new_size = insert_new_node(b.get(), node, std::forward(args)...); inserted = true; } /* No need to acquire the item or item acquired */ if (!result || try_acquire_item( result, node.get(this->my_pool_uuid)->mutex, write)) break; /* the wait takes really long, restart the * operation */ b.release(); std::this_thread::yield(); m = mask().load(std::memory_order_acquire); #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED ANNOTATE_HAPPENS_AFTER(&(this->my_mask)); #endif } if (result) { result->my_node = node.get_persistent_ptr(this->my_pool_uuid); result->my_hash = h; } check_growth(m, new_size); return inserted; } template template bool concurrent_hash_map::internal_erase(const K &key) { node_ptr_t n; hashcode_type const h = hasher{}(key); hashcode_type m = mask().load(std::memory_order_acquire); #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED ANNOTATE_HAPPENS_AFTER(&(this->my_mask)); #endif pool_base pop = get_pool_base(); restart : { /* lock scope */ /* get bucket */ bucket_accessor b(this, h & m, scoped_lock_traits_type::initial_rw_state(true)); search: node_ptr_t *p = &b->node_list; n = *p; while (n && !key_equal{}(key, detail::static_persistent_pool_pointer_cast( n)(this->my_pool_uuid) ->item.first)) { p = &n(this->my_pool_uuid)->next; n = *p; } if (!n) { /* not found, but mask could be changed */ if (check_mask_race(h, m)) goto restart; return false; } else if (!b.is_writer() && !scoped_lock_traits_type::upgrade_to_writer(b)) { if (check_mask_race(h, m)) /* contended upgrade, check mask */ goto restart; goto search; } persistent_ptr del = n(this->my_pool_uuid); { /* We cannot remove this element immediately because * other threads might work with this element via * accessors. The item_locker required to wait while * other threads use the node. */ const_accessor acc; if (!try_acquire_item(&acc, del->mutex, true)) { /* the wait takes really long, restart the operation */ b.release(); std::this_thread::yield(); m = mask().load(std::memory_order_acquire); goto restart; } } assert(pmemobj_tx_stage() == TX_STAGE_NONE); auto &size_diff = this->thread_size_diff(); /* Only one thread can delete it due to write lock on the bucket */ transaction::run(pop, [&] { *p = del->next; delete_node(del); --size_diff; }); --(this->my_size); } return true; } template void concurrent_hash_map::swap( concurrent_hash_map &table) { internal_swap(table); } template void concurrent_hash_map::rehash( size_type sz) { concurrent_hash_map_internal::check_outside_tx(); reserve(sz); hashcode_type m = mask(); /* only the last segment should be scanned for rehashing size or first * index of the last segment */ hashcode_type b = (m + 1) >> 1; /* zero or power of 2 */ assert((b & (b - 1)) == 0); for (; b <= m; ++b) { bucket *bp = get_bucket(b); concurrent_hash_map_internal::assert_not_locked( bp->mutex); /* XXX Need to investigate if this statement is needed */ if (bp->is_rehashed(std::memory_order_relaxed) == false) rehash_bucket(bp, b); } } template void concurrent_hash_map::clear() { hashcode_type m = mask(); assert((m & (m + 1)) == 0); #ifndef NDEBUG /* check consistency */ for (segment_index_t b = 0; b <= m; ++b) { bucket *bp = get_bucket(b); concurrent_hash_map_internal::assert_not_locked( bp->mutex); } #endif pool_base pop = get_pool_base(); { /* transaction scope */ transaction::manual tx(pop); assert(this->tls_ptr != nullptr); this->tls_ptr->clear(); this->on_init_size = 0; segment_index_t s = segment_traits_t::segment_index_of(m); assert(s + 1 == this->block_table_size || !segment_facade_t(this->my_table, s + 1).is_valid()); do { clear_segment(s); } while (s-- > 0); /* * As clear can only be called * from one thread, and there can be an outer * transaction we must make sure that mask and size * changes are transactional */ transaction::snapshot((size_t *)&this->my_mask); transaction::snapshot((size_t *)&this->my_size); mask().store(embedded_buckets - 1, std::memory_order_relaxed); this->my_size = 0; transaction::commit(); } } template void concurrent_hash_map::clear_segment(segment_index_t s) { segment_facade_t segment(this->my_table, s); assert(segment.is_valid()); size_type sz = segment.size(); for (segment_index_t i = 0; i < sz; ++i) { for (node_ptr_t n = segment[i].node_list; n; n = segment[i].node_list) { segment[i].node_list = n(this->my_pool_uuid)->next; delete_node(n); } } if (s >= segment_traits_t::embedded_segments) segment.disable(); } template void concurrent_hash_map:: internal_copy(const concurrent_hash_map &source) { auto pop = get_pool_base(); reserve(source.size()); internal_copy(source.begin(), source.end()); } template template void concurrent_hash_map::internal_copy(I first, I last) { hashcode_type m = mask(); for (; first != last; ++first) { hashcode_type h = hasher{}(first->first); bucket *b = get_bucket(h & m); assert(b->is_rehashed(std::memory_order_relaxed)); detail::persistent_pool_ptr p; insert_new_node(b, p, *first); } } template inline bool operator==(const concurrent_hash_map &a, const concurrent_hash_map &b) { if (a.size() != b.size()) return false; typename concurrent_hash_map::const_iterator i(a.begin()), i_end(a.end()); typename concurrent_hash_map::const_iterator j, j_end(b.end()); for (; i != i_end; ++i) { j = b.equal_range(i->first).first; if (j == j_end || !(i->second == j->second)) return false; } return true; } template inline bool operator!=(const concurrent_hash_map &a, const concurrent_hash_map &b) { return !(a == b); } template inline void swap(concurrent_hash_map &a, concurrent_hash_map &b) { a.swap(b); } } /* namespace obj */ } /* namespace pmem */ #endif /* PMEMOBJ_CONCURRENT_HASH_MAP_HPP */ libpmemobj-cpp-1.9/include/libpmemobj++/container/detail/000077500000000000000000000000001361501571000233775ustar00rootroot00000000000000libpmemobj-cpp-1.9/include/libpmemobj++/container/detail/contiguous_iterator.hpp000066400000000000000000000232741361501571000302300ustar00rootroot00000000000000/* * Copyright 2018-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file * Iterators for contiguous persistent containers. */ #ifndef LIBPMEMOBJ_CPP_CONTIGUOUS_ITERATOR_HPP #define LIBPMEMOBJ_CPP_CONTIGUOUS_ITERATOR_HPP #include #include #include #include namespace pmem { namespace detail { /** * Base class for iterators which satisfies RandomAccessIterator * and operate on contiguous memory. */ template struct contiguous_iterator { /** * Constructor taking a pointer. */ constexpr contiguous_iterator(Pointer begin) : ptr(begin) { } /** * Dereference operator. */ Reference operator*() const { return *ptr; } /** * Arrow operator. */ Pointer operator->() const { return ptr; } /** * Prefix increment operator. */ Iterator & operator++() { static_cast(this)->change_by(1); return *static_cast(this); } /** * Postfix increment operator. */ Iterator operator++(int) { Iterator tmp(*static_cast(this)); static_cast(this)->change_by(1); return tmp; } /** * Prefix decrement operator. */ Iterator & operator--() { static_cast(this)->change_by(-1); return *static_cast(this); } /** * Postfix decrement operator. */ Iterator operator--(int) { Iterator tmp(*static_cast(this)); static_cast(this)->change_by(-1); return tmp; } /** * Addition assignment operator. */ Iterator & operator+=(std::ptrdiff_t n) { static_cast(this)->change_by(n); return *static_cast(this); } /** * Subtraction assignment operator. */ Iterator & operator-=(std::ptrdiff_t n) { static_cast(this)->change_by(-n); return *static_cast(this); } /** * Addition operator. */ Iterator operator+(std::ptrdiff_t n) const { Iterator tmp(*static_cast(this)); tmp += n; return tmp; } /** * Subtraction operator overload for integral type. */ Iterator operator-(std::ptrdiff_t n) const { Iterator tmp(*static_cast(this)); tmp -= n; return tmp; } /** * Subtraction operator overload Iterator type. */ friend std::ptrdiff_t operator-(const Iterator &lhs, const Iterator &rhs) { return lhs.ptr - rhs.ptr; } /** * Element access operator. */ Reference operator[](std::size_t n) { return ptr[n]; } Pointer get_ptr() const { return ptr; } protected: /** * Function for changing underlying pointer. * This is where static polymorphism is used. Derived classes * can override this method and snapshot data if necessary. */ void change_by(std::ptrdiff_t n) { ptr += n; } Pointer ptr; }; /** * Non-const iterator which adds elements to a transaction in a bulk. * * This is done by dividing underlying array into ranges of specified * (snapshot_size) size. If iterator is incremented/decremented/etc. * so that it is moved to another range, this new range is added to * a transaction. * * For example, let's assume snapshot_size = 2, N = 6. This gives us: * 0 1 | 2 3 | 4 5 * * If iterator is moved from 1 to 3, that means it is now in another * range, and that range must be added to a transaction * (elements 2 and 3). */ template struct range_snapshotting_iterator : public contiguous_iterator, T &, T *> { using iterator_category = std::random_access_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; using reference = T &; using pointer = T *; using base_type = contiguous_iterator, reference, pointer>; /** * Constructor taking pointer to data, pointer to the beginning * of the array and snapshot_size. */ range_snapshotting_iterator(pointer ptr = nullptr, pointer data = nullptr, std::size_t size = 0, std::size_t snapshot_size = 1) : base_type(ptr), data(data), size(size), snapshot_size(snapshot_size) { assert(data <= ptr); if (snapshot_size > 0) snapshot_range(ptr); } /** * Conversion operator to const T*. */ operator const T *() const { return this->ptr; } /** * Element access operator. * * Adds element to a transaction. */ reference operator[](std::size_t n) { detail::conditional_add_to_tx(&this->ptr[n], 1, POBJ_XADD_ASSUME_INITIALIZED); return base_type::operator[](n); } /** * Non-member swap function. */ friend void swap(range_snapshotting_iterator &lhs, range_snapshotting_iterator &rhs) { std::swap(lhs.ptr, rhs.ptr); std::swap(lhs.data, rhs.data); std::swap(lhs.size, rhs.size); std::swap(lhs.snapshot_size, rhs.snapshot_size); } template friend struct contiguous_iterator; protected: void change_by(std::ptrdiff_t n) { conditional_snapshot_range(this->ptr, n); base_type::change_by(n); } private: /* * Conditionally snapshot range of length snapshot_size, * which contain address equal to ptr + diff. */ void conditional_snapshot_range(pointer ptr, difference_type diff) { if (snapshot_size == 0) return; auto new_ptr = ptr + diff; /* if new pointer is outside of the array */ if (new_ptr < data || new_ptr >= data + size) return; /* if new pointer is in the same range */ if (static_cast(ptr - data) / snapshot_size == static_cast(new_ptr - data) / snapshot_size) return; snapshot_range(new_ptr); } void snapshot_range(pointer ptr) { /* align index to snapshot_size */ auto range_begin = ptr - static_cast(ptr - data) % snapshot_size; auto range_size = snapshot_size; if (range_begin + range_size > data + size) range_size = static_cast(data + size - range_begin); #ifndef NDEBUG verify_range(range_begin, range_size); #endif detail::conditional_add_to_tx(range_begin, range_size, POBJ_XADD_ASSUME_INITIALIZED); } #ifndef NDEBUG void verify_range(pointer range_begin, uint64_t range_size) { auto range_offset = static_cast(range_begin - data); assert(range_begin >= data); assert(range_offset % snapshot_size == 0); assert((range_offset + range_size) % snapshot_size == 0 || range_begin + range_size == data + size); } #endif pointer data; std::size_t size; std::size_t snapshot_size; }; /** * Default non-const iterator which adds element to a transaction * on every access. */ template struct basic_contiguous_iterator : public contiguous_iterator, T &, T *> { using iterator_category = std::random_access_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; using reference = T &; using pointer = T *; using base_type = contiguous_iterator, reference, pointer>; /** * Constructor taking pointer and snapshotting function as * arguments. */ basic_contiguous_iterator(pointer ptr = nullptr) : base_type(ptr) { } /** * Conversion operator to const T*. */ operator const T *() const { return this->ptr; } /** * Dereference operator which adds dereferenced element to * a transaction. */ reference operator*() const { detail::conditional_add_to_tx(this->ptr, 1, POBJ_XADD_ASSUME_INITIALIZED); return base_type::operator*(); } /** * Arrow operator which adds underlying element to * a transactions. */ pointer operator->() const { detail::conditional_add_to_tx(this->ptr, 1, POBJ_XADD_ASSUME_INITIALIZED); return base_type::operator->(); } /** * Element access operator. * * Adds range containing specified element to a transaction. */ reference operator[](std::size_t n) { detail::conditional_add_to_tx(&this->ptr[n], 1, POBJ_XADD_ASSUME_INITIALIZED); return base_type::operator[](n); } /** * Non-member swap function. */ friend void swap(basic_contiguous_iterator &lhs, basic_contiguous_iterator &rhs) { std::swap(lhs.ptr, rhs.ptr); } }; } /* namespace detail */ } /* namespace pmem */ #endif /* LIBPMEMOBJ_CPP_CONTIGUOUS_ITERATOR_HPP */ libpmemobj-cpp-1.9/include/libpmemobj++/container/detail/segment_vector_policies.hpp000066400000000000000000000151631361501571000310310ustar00rootroot00000000000000/* * Copyright 2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file * A persistent version of segment vector implementation. */ #ifndef LIBPMEMOBJ_SEGMENT_VECTOR_POLICIES_HPP #define LIBPMEMOBJ_SEGMENT_VECTOR_POLICIES_HPP #include #include #include #include namespace pmem { namespace obj { namespace segment_vector_internal { template using array_64 = array; template using resize_method = decltype(std::declval().resize(std::declval())); template using container_has_resize = detail::supports; template ::value> struct segment_vector_resize { using segment_vector_type = Container; static void resize(segment_vector_type &c, size_t n) { c.resize(n); } }; template struct segment_vector_resize { using segment_vector_type = Container; static void resize(segment_vector_type &c, size_t n) { } }; template