Showing preview only (393K chars total). Download the full file or copy to clipboard to get everything.
Repository: tgockel/zookeeper-cpp
Branch: master
Commit: 425dfe0a5f50
Files: 96
Total size: 367.6 KB
Directory structure:
gitextract_87o4bcld/
├── .github/
│ └── ISSUE_TEMPLATE.md
├── .gitignore
├── .travis.yml
├── AUTHORS
├── CMakeLists.txt
├── CONTRIBUTING.rst
├── COPYING
├── README.md
├── cmake/
│ └── modules/
│ ├── BuildFunctions.cmake
│ ├── CodeCoverage.cmake
│ ├── ConfigurationSetting.cmake
│ ├── ListSplit.cmake
│ └── ZooKeeper.cmake
├── config/
│ ├── dev-env
│ ├── docker/
│ │ ├── debian-10/
│ │ │ └── Dockerfile
│ │ ├── opensuse-15/
│ │ │ └── Dockerfile
│ │ ├── ubuntu-16.04/
│ │ │ └── Dockerfile
│ │ ├── ubuntu-18.04/
│ │ │ └── Dockerfile
│ │ └── ubuntu-20.04/
│ │ └── Dockerfile
│ ├── make-doxygen
│ ├── make-package
│ ├── make-packages
│ ├── publish-doxygen
│ ├── run-tests
│ ├── travisci_rsa.enc
│ └── upload-coverage
├── doc/
│ └── Doxyfile
├── install/
│ └── deb/
│ ├── libzkpp/
│ │ ├── control.in
│ │ ├── postinst.in
│ │ └── shlibs.in
│ ├── libzkpp-dev/
│ │ └── control.in
│ ├── libzkpp-server/
│ │ ├── control.in
│ │ └── postinst.in
│ └── libzkpp-server-dev/
│ └── control.in
└── src/
└── zk/
├── acl.cpp
├── acl.hpp
├── acl_tests.cpp
├── buffer.hpp
├── client.cpp
├── client.hpp
├── client_tests.cpp
├── config.hpp
├── connection.cpp
├── connection.hpp
├── connection_tests.cpp
├── connection_zk.cpp
├── connection_zk.hpp
├── error.cpp
├── error.hpp
├── error_tests.cpp
├── exceptions.cpp
├── exceptions.hpp
├── forwards.hpp
├── future.hpp
├── multi.cpp
├── multi.hpp
├── multi_tests.cpp
├── optional.hpp
├── optional_tests.cpp
├── results.cpp
├── results.hpp
├── server/
│ ├── classpath.cpp
│ ├── classpath.hpp
│ ├── classpath_registration_template.cpp.in
│ ├── classpath_tests.cpp
│ ├── configuration.cpp
│ ├── configuration.hpp
│ ├── configuration_tests.cpp
│ ├── detail/
│ │ ├── close.cpp
│ │ ├── close.hpp
│ │ ├── event_handle.cpp
│ │ ├── event_handle.hpp
│ │ ├── pipe.cpp
│ │ ├── pipe.hpp
│ │ ├── pipe_tests.cpp
│ │ ├── subprocess.cpp
│ │ ├── subprocess.hpp
│ │ └── subprocess_tests.cpp
│ ├── package_registry.cpp
│ ├── package_registry.hpp
│ ├── package_registry_tests.cpp
│ ├── package_registry_tests.hpp
│ ├── server.cpp
│ ├── server.hpp
│ ├── server_group.cpp
│ ├── server_group.hpp
│ ├── server_group_tests.cpp
│ ├── server_tests.cpp
│ └── server_tests.hpp
├── string_view.hpp
├── tests/
│ ├── main.cpp
│ ├── test.cpp
│ └── test.hpp
├── types.cpp
├── types.hpp
└── types_tests.cpp
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
Don't forget the label!
- If you have a question, use the "question" tag.
================================================
FILE: .gitignore
================================================
*~
*.swp
*.orig
*.kdev4
build*
.kdev*
*.kdev4
packages/
.vs
x64/
*sdf
*.pc
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
## my files and cmake
out
out.*
# IDE files #
#############
nbproject
.~lock.*
.buildpath
.idea
.project
.settings
composer.lock
#Cmake
CMakeCache.txt
CMakeFiles
CMakeScripts
Makefile
cmake_install.cmake
install_manifest.txt
================================================
FILE: .travis.yml
================================================
language: cpp
sudo: required
dist: trusty
env:
matrix:
- DISTRO=ubuntu-16.04 CONFIG=Debug
- DISTRO=ubuntu-16.04 CONFIG=Release
- DISTRO=ubuntu-18.04 CONFIG=Debug
- DISTRO=ubuntu-18.04 CONFIG=Release
- DISTRO=ubuntu-20.04 CONFIG=Debug
- DISTRO=ubuntu-20.04 CONFIG=Release
- DISTRO=debian-10 CONFIG=Debug
- DISTRO=debian-10 CONFIG=Release
- DISTRO=opensuse-15 CONFIG=Debug
services:
- docker
before_install:
- docker build config/docker/${DISTRO} -t dev/zookeeper-cpp/${DISTRO}
script:
- echo ${COVERALLS_REPO_TOKEN} > ${TRAVIS_BUILD_DIR}/coveralls-repo-token
- if [[ ${CONFIG} == "Debug" ]]; then ./config/dev-env ${DISTRO} -- ./config/run-tests; fi
- if [[ ${CONFIG} == "Release" ]]; then ./config/make-package --dockerize ${DISTRO}; fi
after_success:
- if [[ ${DISTRO} != "ubuntu-20.04" ]]; then echo "Skipping documentation publishing due to non-main build environment"; exit 0; fi
- if [[ ${CONFIG} != "Debug" ]]; then echo "Skipp documentation publishing due to non-debug build"; exit 0; fi
- GIT_CURRENT_HASH=$(git rev-parse HEAD)
- GIT_MASTER_HASH=$(git rev-parse master)
- GIT_REMOTE_NAME=$(git remote)
- GIT_REMOTE_FETCH_PATH=$(git remote --verbose | grep -P '^'${GIT_REMOTE_NAME}'.*\(fetch\)$'
| awk '{print $2}')
- GIT_EXPECTED_PATH=https://github.com/tgockel/zookeeper-cpp.git
- echo "GIT_CURRENT_HASH=${GIT_CURRENT_HASH} GIT_REMOTE_NAME=${GIT_REMOTE_NAME} GIT_REMOTE_FETCH_PATH=${GIT_REMOTE_FETCH_PATH}"
- if [[ ${GIT_CURRENT_HASH} != ${GIT_MASTER_HASH} ]]; then echo "Skipping documentation publishing due to non-master ${GIT_CURRENT_HASH} (master=${GIT_MASTER_HASH})"; exit 0; fi
- if [[ ${GIT_REMOTE_FETCH_PATH} != ${GIT_EXPECTED_PATH} ]]; then echo "Skipping documentation publishing due to non-mainline remote ${GIT_REMOTE_FETCH_PATH}"; exit 0; fi
- sudo add-apt-repository --yes ppa:libreoffice/ppa
- sudo apt-get update
- sudo apt-get install --yes doxygen graphviz texlive-full
- openssl aes-256-cbc -K $encrypted_513b1ad04072_key -iv $encrypted_513b1ad04072_iv -in config/travisci_rsa.enc -out config/travisci_rsa -d
- chmod 0600 config/travisci_rsa
- cp config/travisci_rsa ~/.ssh/id_rsa
- "./config/publish-doxygen"
================================================
FILE: AUTHORS
================================================
Contributors
============
Travis Gockel <travis@gockelhut.com>
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.5)
file(READ src/zk/config.hpp CONFIG_HPP_STR)
string(REGEX REPLACE ".*# *define +ZKPP_VERSION_MAJOR +([0-9]+).*" "\\1" ZKPP_VERSION_MAJOR "${CONFIG_HPP_STR}")
string(REGEX REPLACE ".*# *define +ZKPP_VERSION_MINOR +([0-9]+).*" "\\1" ZKPP_VERSION_MINOR "${CONFIG_HPP_STR}")
string(REGEX REPLACE ".*# *define +ZKPP_VERSION_PATCH +([0-9]+).*" "\\1" ZKPP_VERSION_PATCH "${CONFIG_HPP_STR}")
set(ZKPP_VERSION "${ZKPP_VERSION_MAJOR}.${ZKPP_VERSION_MINOR}.${ZKPP_VERSION_PATCH}")
project(zookeeper-cpp
LANGUAGES CXX
VERSION "${ZKPP_VERSION}"
)
set(PROJECT_SO_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
message(STATUS "Software Version: ${ZKPP_VERSION}")
################################################################################
# CMake #
################################################################################
cmake_policy(VERSION 3.5)
cmake_policy(SET CMP0037 OLD) # allow generation of "test" target
set(CMAKE_REQUIRED_QUIET YES) # tell check_include_file_cxx to keep quiet
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/")
include(BuildFunctions)
include(CheckIncludeFileCXX)
include(ConfigurationSetting)
include(ListSplit)
include(ZooKeeper)
################################################################################
# Build Configuration #
################################################################################
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug")
message(STATUS "No build type selected, default to ${CMAKE_BUILD_TYPE}")
endif()
set(VALID_BUILD_TYPES Debug Release)
if(NOT ${CMAKE_BUILD_TYPE} IN_LIST VALID_BUILD_TYPES)
message(FATAL_ERROR "Invalid CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}\nValid build types are: ${VALID_BUILD_TYPES}")
endif()
message(STATUS "Configuration: ${CMAKE_BUILD_TYPE}")
set(ZKPP_SERVER_VERSIONS "3.5.5;3.4.14"
CACHE STRING "The ZooKeeper server versions to run tests against. The first in the list is the default."
)
message(STATUS "Features:")
build_option(NAME CODE_COVERAGE
DOC "Enable code coverage (turns on the test-coverage target)"
DEFAULT OFF
CONFIGS_ON Debug
)
configuration_setting(NAME BUFFER
DOC "Type to use for zk::buffer"
DEFAULT STD_VECTOR
OPTIONS
STD_VECTOR
STD_STRING
CUSTOM
)
configuration_setting(NAME FUTURE
DOC "Type to use for zk::future<T> and zk::promise<T>"
DEFAULT STD
OPTIONS
STD
STD_EXPERIMENTAL
BOOST
CUSTOM
)
set(CXX_STANDARD c++17
CACHE STRING "The language standard to target for C++."
)
set(CXX_WARNINGS "-Wall -Wextra -Wconversion -Werror")
set(CXX_EXTRA_FLAGS "-Wl,--no-as-needed")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=${CXX_STANDARD} ${CXX_WARNINGS} -ggdb3 ${CXX_EXTRA_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DZKPP_DEBUG=1")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
################################################################################
# External Libraries #
################################################################################
find_library(zookeeper_LIBRARIES zookeeper_mt)
set(ZKPP_LIB_DEPENDENCIES ${ZKPP_LIB_DEPENDENCIES} ${zookeeper_LIBRARIES})
include_directories("${PROJECT_SOURCE_DIR}/src")
if (ZKPP_BUILD_SETTING_FUTURE STREQUAL "BOOST")
find_package(Boost
1.52.0
REQUIRED
thread)
set(ZKPP_LIB_DEPENDENCIES ${ZKPP_LIB_DEPENDENCIES} ${Boost_LIBRARIES})
endif()
################################################################################
# GTest #
################################################################################
find_package(GTest)
if(GTest_FOUND)
# Make it look like find_library
set(gtest_LIBRARIES GTest::GTest)
elseif(EXISTS "/usr/src/googletest/googletest/src" OR EXISTS "/usr/src/gtest/src/")
# GTest's packaging on Ubuntu (googletest or libgtest-dev, depending on which version) contains all the source files
# instead of a library and headers. CMake has a package discovery for GTest, but it does not pick up on this for you,
# so we'll build it manually here.
message(STATUS "GTest is not installed, but the sources were found...adding them to the build")
if(EXISTS "/usr/src/googletest/googletest/src")
# Prefer the "googletest" version, as sometimes the "gtest" one is an empty directory (this seems to be the result
# of a packaging issue)
set(GTEST_SRC_ROOT "/usr/src/googletest/googletest/src")
else()
set(GTEST_SRC_ROOT "/usr/src/gtest/src")
endif()
if(EXISTS "${GTEST_SRC_ROOT}/gtest-all.cc")
set(gtest_lib_cpps "${GTEST_SRC_ROOT}/gtest-all.cc")
message(STATUS "Building with ${GTEST_SRC_ROOT}/gtest-all.cc")
else()
file(GLOB gtest_lib_cpps "${GTEST_SRC_ROOT}/gtest-*.cc")
message(STATUS "Building with gtest_lib_cpps=${gtest_lib_cpps}")
endif()
add_library(gtest SHARED ${gtest_lib_cpps})
# GTest uses relative imports incorrectly, so make sure to add it to the include path.
target_include_directories(gtest PRIVATE "${GTEST_SRC_ROOT}/..")
# Also disable -Werror
target_compile_options(gtest PRIVATE "-Wno-error")
set(gtest_LIBRARIES gtest)
else()
message(SEND_ERROR "GTest was not found")
endif()
################################################################################
# Building #
################################################################################
build_module(NAME zkpp-tests
PATH src/zk/tests
LINK_LIBRARIES
${gtest_LIBRARIES}
)
build_module(NAME zkpp
PATH src/zk
NO_RECURSE
LINK_LIBRARIES
${ZKPP_LIB_DEPENDENCIES}
)
build_module(NAME zkpp-server
PATH src/zk/server
LINK_LIBRARIES
zkpp
)
target_link_libraries(zkpp_tests zkpp-server zkpp-server_tests)
################################################################################
# ZooKeeper Server Testing #
################################################################################
foreach(server_version IN LISTS ZKPP_SERVER_VERSIONS)
find_zookeeper_server(VERSION "${server_version}"
OUTPUT_CLASSPATH server_classpath
)
set(generated_cpp "${CMAKE_CURRENT_BINARY_DIR}/generated/src/zk/server/classpath_registration_${server_version}.cpp")
configure_file(src/zk/server/classpath_registration_template.cpp.in "${generated_cpp}" @ONLY)
target_sources(zkpp-server_tests PRIVATE "${generated_cpp}")
endforeach()
################################################################################
# Targets #
################################################################################
add_custom_target(test
COMMAND $<TARGET_FILE:zkpp-tests_prog>
"--gtest_output=xml:test-results.xml"
"--gtest_death_test_style=threadsafe"
DEPENDS zkpp-tests_prog
BYPRODUCTS test-results.xml
USES_TERMINAL
)
# Similar to test, but run it inside of GDB with GTest options one would want when running in GDB.
add_custom_target(gdbtest
COMMAND "gdb"
"-args"
$<TARGET_FILE:zkpp-tests_prog>
"--gtest_output=xml:test-results-gdb.xml"
"--gtest_death_test_style=threadsafe"
"--gtest_break_on_failure=1"
"--gtest_catch_exceptions=0"
DEPENDS zkpp-tests_prog
BYPRODUCTS test-results-gdb.xml
USES_TERMINAL
)
if(ZKPP_BUILD_OPTION_CODE_COVERAGE)
include(CodeCoverage)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
setup_target_for_coverage(test-coverage zkpp-tests_prog coverage "--gtest_death_test_style=threadsafe")
endif()
################################################################################
# Packaging #
################################################################################
set(ZKPP_PACKAGE_SYSTEM ""
CACHE STRING "The packaging system to generate a package build file for (leave blank to not build a package)"
)
if(ZKPP_PACKAGE_SYSTEM)
message(STATUS "Packaging system ${ZKPP_PACKAGE_SYSTEM}:")
set(PROJECT_PACKAGE_VERSION "${PROJECT_VERSION}-1") # TODO: Allow arbitrary tags here.
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
set(PROJECT_BUILD_ARCHITECTURE "amd64")
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86")
set(PROJECT_BUILD_ARCHITECTURE "i386")
else()
set(PROJECT_BUILD_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
endif()
set(all_packages)
foreach(module zkpp zkpp-server)
message(STATUS " ${module}:")
set(package_name ${module})
string(REGEX REPLACE "-" "/" header_path "${module}")
string(REGEX REPLACE "pp" "" header_path "${header_path}")
if(ZKPP_PACKAGE_SYSTEM STREQUAL "DEB")
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/install/deb/lib${package_name}")
message(STATUS " lib${package_name}")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/install/deb/lib${package_name}/control.in"
"${CMAKE_CURRENT_BINARY_DIR}/install/lib${package_name}${PROJECT_SO_VERSION}/DEBIAN/control"
@ONLY IMMEDIATE
)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/install/deb/lib${package_name}/postinst.in")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/install/deb/lib${package_name}/postinst.in"
"${CMAKE_CURRENT_BINARY_DIR}/intermediate/lib${package_name}${PROJECT_SO_VERSION}/DEBIAN/postinst"
@ONLY IMMEDIATE
)
file(COPY "${CMAKE_CURRENT_BINARY_DIR}/intermediate/lib${package_name}${PROJECT_SO_VERSION}/DEBIAN/postinst"
DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/install/lib${package_name}${PROJECT_SO_VERSION}/DEBIAN"
FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)
endif()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/install/deb/lib${package_name}/shlibs.in")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/install/deb/lib${package_name}/shlibs.in"
"${CMAKE_CURRENT_BINARY_DIR}/install/lib${package_name}${PROJECT_SO_VERSION}/DEBIAN/shlibs"
@ONLY IMMEDIATE
)
endif()
add_custom_target("lib${package_name}.deb"
BYPRODUCTS "install/lib${package_name}${PROJECT_SO_VERSION}.deb"
COMMAND rm -rf "install/lib${package_name}${PROJECT_SO_VERSION}${CMAKE_INSTALL_PREFIX}/lib"
COMMAND mkdir -p "install/lib${package_name}${PROJECT_SO_VERSION}${CMAKE_INSTALL_PREFIX}/lib"
COMMAND cp "$<TARGET_FILE:${module}>" "install/lib${package_name}${PROJECT_SO_VERSION}${CMAKE_INSTALL_PREFIX}/lib"
COMMAND dpkg --build "install/lib${package_name}${PROJECT_SO_VERSION}"
DEPENDS
"${CMAKE_CURRENT_BINARY_DIR}/install/lib${package_name}${PROJECT_SO_VERSION}/DEBIAN/control"
${module}
)
list(APPEND all_packages "lib${package_name}.deb")
endif()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/install/deb/lib${package_name}-dev")
message(STATUS " lib${package_name}-dev")
set(staging_dir "install/lib${package_name}${PROJECT_SO_VERSION}-dev${CMAKE_INSTALL_PREFIX}/include/${header_path}")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/install/deb/lib${package_name}-dev/control.in"
"${CMAKE_CURRENT_BINARY_DIR}/install/lib${package_name}${PROJECT_SO_VERSION}-dev/DEBIAN/control"
@ONLY IMMEDIATE
)
add_custom_target("lib${package_name}-dev.deb"
BYPRODUCTS "install/lib${package_name}${PROJECT_SO_VERSION}-dev.deb"
COMMAND rm -rf "${staging_dir}"
COMMAND mkdir -p "${staging_dir}"
COMMAND cp -t "${staging_dir}" ${${module}_LIBRARY_HEADERS}
COMMAND dpkg --build "install/lib${package_name}${PROJECT_SO_VERSION}-dev"
DEPENDS
"${CMAKE_CURRENT_BINARY_DIR}/install/lib${package_name}${PROJECT_SO_VERSION}-dev/DEBIAN/control"
${${module}_LIBRARY_HEADERS}
)
list(APPEND all_packages "lib${package_name}-dev.deb")
endif()
else()
message(FATAL_ERROR "Unknown ZKPP_PACKAGE_SYSTEM=${ZKPP_PACKAGE_SYSTEM}")
endif()
endforeach()
add_custom_target(package
DEPENDS ${all_packages}
COMMENT "Built all packages"
)
endif()
================================================
FILE: CONTRIBUTING.rst
================================================
Contributing Guide
==================
So you want to contribute to the ZooKeeper C++ library?
I'd love your contribution!
Please help me.
Building
--------
Building the system only requires `CMake <https://cmake.org/>`_ and the standard-issue C++ compilation tools.
Docker
^^^^^^
Docker is the official mechanism for supporting multiple Linux distributions (see the
`TravisCI <https://travis-ci.org/tgockel/zookeeper-cpp>`_ build).
If you would like to do this at home, simply use the ``dev-env`` script::
$> cd /path/to/zookeeper-cpp
$> ./config/dev-env ubuntu-18.04
This will create a Docker image named something like ``dev/zookeeper-cpp/ubuntu-18.04`` and run that image with the
project's working directory mapped to ``~/zookeeper-cpp`` with you in control of a shell.
Inside Docker, you can now build::
root@0ae2f54b152b:~/zookeeper-cpp# mkdir build-debug
root@0ae2f54b152b:~/zookeeper-cpp# cd build-debug
root@0ae2f54b152b:~/zookeeper-cpp/build-debug# cmake -GNinja ..
... output ...
root@0ae2f54b152b:~/zookeeper-cpp/build-debug# ninja test
... output ...
This experience is pretty decent.
The biggest annoyance is editing within the Docker image makes files you touch owned by *root* (I suspect there is a way
to prevent this, but I am far from competent at Docker).
If you use `KDevelop <https://www.kdevelop.org/>`_, you can use the IDE to build and debug inside of these images with
`KDevelop Runtimes <http://www.proli.net/2017/05/23/kdevelop-runtimes-docker-and-flatpak-integration/>`_.
Process
-------
This library follows the `GitHub Fork + Pull Model <https://help.github.com/articles/about-pull-requests/>`_.
Below are the more project-specific steps.
Issue Tracker
^^^^^^^^^^^^^
All work *must* be tracked in the `Issue Tracker <https://github.com/tgockel/zookeeper-cpp/issues>`_, otherwise the
maintainer will have no idea what is going on.
Try to find an existing bug in the list of issues -- if you can't find it, open a new issue with a descriptive title and
descriptive description.
If you are unclear on if it should be a bug or not, mark it with a *Question* tag or just send me an
`email <mailto:travis@gockelhut.com>`_.
Assign the issue to yourself so I don't forget who is working on it.
For more granular tracking, the issue should move across the
`GitHub Project board <https://github.com/tgockel/zookeeper-cpp/projects/1>`_.
The columns of the project should be somewhat intuitive:
:Backlog:
Things we are planning on doing soon.
:Design:
System is being designed.
What this usually means is the API is being written.
*Please* write your API first -- it can save a lot of time in the long run.
:Implementation:
The component is currently being implemented.
:Pull Request:
There is an open pull request.
:Done:
Work is complete!
Developing
^^^^^^^^^^
1. Fork the repository.
2. Branch in your fork (not actually required, but generally considered a Good Idea).
3. Write your code.
4. If this is your first contribution, add yourself to ``AUTHORS`` (alphabetically).
5. Commit your code (somewhere in the commit message, be sure to mention "Issue #NN", where "NN" is the issue number you
were working on).
6. Watch your tests pass for all environments in TravisCI.
7. Issue a pull request from your branch to the master branch of the main repository.
8. Close the branch in your repository (not actually required, but clean repos are nice).
Sign Your Commits
"""""""""""""""""
When committing code, please `sign commits with GPG <https://help.github.com/articles/signing-commits-using-gpg/>`_.
This lets me know that work submitted by you was really created by you (security or something like that).
If you always want to sign commits instead of specifying ``-S`` on the command line every time, add it to your global
configuration::
$> git config --global user.signingkey ${YOUR_KEY_ID}
$> git config --global commit.gpgsign true
================================================
FILE: COPYING
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# ZooKeeper C++
A ZooKeeper client for C++.
It is [hosted on GitHub](https://github.com/tgockel/zookeeper-cpp),
[documented](https://tgockel.github.io/zookeeper-cpp/)
(including [previous versions](https://tgockel.github.io/zookeeper-cpp/version/)
of the software), and [tested](https://travis-ci.org/tgockel/zookeeper-cpp).
Features include (but are not necessarily limited to):
- Simple
- Connect with just a connection string
- Clients should not require factories
- Does not require knowledge of the Java or C APIs
- Configurable
- Use the parts you need
- Change parts to fit in your application
- Safe
- In the best case, illegal code should fail to compile
- An illegal action should throw an exception
- Utility functions have a [strong exception guarantee](http://www.gotw.ca/gotw/082.htm>)
- Stable
- Worry less about upgrading -- the API and ABI will not change out from under you
**NOTE**: This library is a work-in-progress.
All documentation you see here is subject to change and non-existence.
[](https://travis-ci.org/tgockel/zookeeper-cpp)
## Usage
Ultimately, the usage looks like this (assuming you have a ZooKeeper server running on your local host):
#include <zk/client.hpp>
#include <zk/multi.hpp>
#include <zk/server/configuration.hpp>
#include <zk/server/server.hpp>
#include <exception>
#include <iostream>
/** All result types are printable for debugging purposes. **/
template <typename T>
void print_thing(const zk::future<T>& result)
{
try
{
// Unwrap the future value, which will not block (based on usage), but could throw.
T value(result.get());
std::cerr << value << std::endl;
}
catch (const std::exception& ex)
{
// Error "handling"
std::cerr << "Exception: " << ex.what() << std::endl;
}
}
int main()
{
// Start a ZK server running on localhost (not needed if you just want a client, but great for testing and
// demonstration purposes).
zk::server::server server(zk::server::configuration::make_minimal("zk-data", 2181));
// zk::client::connect returns a future<zk::client>, which is delivered when the connection is established.
auto client = zk::client::connect("zk://127.0.0.1:2181")
.get();
// get_result has a zk::buffer and zk::stat.
client.get("/foo/bar")
.then(print_thing<zk::get_result>);
// get_children_result has a std::vector<std::string> for the path names and zk::stat for the parent stat.
client.get_children("/foo")
.then(print_thing<zk::get_children_result>);
// set_result has a zk::stat for the modified ZNode.
client.set("/foo/bar", "some data")
.then(print_thing<zk::set_result>);
// More explicit: client.create("/foo/baz", "more data", zk::acls::open_unsafe(), zk::create_mode::normal);
client.create("/foo/baz", "more data")
.then(print_thing<zk::create_result>);
client.get("/foo/bar")
.then([client] (const auto& get_res)
{
zk::version foo_bar_version = get_res.get().stat().data_version;
zk::multi_op txn =
{
zk::op::check("/foo", zk::version::any()),
zk::op::check("/foo/baz", foo_bar_version),
zk::op::create("/foo/bap", "hi", nullopt, zk::create_mode::sequential),
zk::op::erase("/foo/bzr"),
};
// multi_res's type is zk::future<zk::multi_result>
client.commit(txn).then(print_thing<zk::multi_result>);
});
// This is not strictly needed -- a client falling out of scope will auto-trigger close
client.close();
}
## Value-Added Features
The core library of `libzkpp` provides the primitives for connecting to and manipulating a ZooKeeper database.
This library also bundles a number of other features that are commonly required when working with a ZooKeeper cluster.
### `zk/curator`
Things in `zk/curator` have features found in the [Apache Curator](http://curator.apache.org/) project.
* Elections
* [Leader Latch](https://github.com/tgockel/zookeeper-cpp/issues/1)
* [Leader Election](https://github.com/tgockel/zookeeper-cpp/issues/2)
* Locks
* [Shared Reentrant Lock](https://github.com/tgockel/zookeeper-cpp/issues/3)
* [Shared Lock](https://github.com/tgockel/zookeeper-cpp/issues/4)
* [Shared Reentrant Read Write Lock](https://github.com/tgockel/zookeeper-cpp/issues/5)
* [Shared Semaphore](https://github.com/tgockel/zookeeper-cpp/issues/6)
* [Multi Shared Lock](https://github.com/tgockel/zookeeper-cpp/issues/7)
* Barriers
* [Barrier](https://github.com/tgockel/zookeeper-cpp/issues/8)
* [Double Barrier](https://github.com/tgockel/zookeeper-cpp/issues/9)
* Counters
* [Shared Counter](https://github.com/tgockel/zookeeper-cpp/issues/10)
* [Distributed Atomic Long](https://github.com/tgockel/zookeeper-cpp/issues/11)
* Caches
* [Path Cache](https://github.com/tgockel/zookeeper-cpp/issues/12)
* [Node Cache](https://github.com/tgockel/zookeeper-cpp/issues/13)
* [Tree Cache](https://github.com/tgockel/zookeeper-cpp/issues/14)
* Nodes
* [Persistent Node](https://github.com/tgockel/zookeeper-cpp/issues/15)
* [Persistent TTL Node](https://github.com/tgockel/zookeeper-cpp/issues/16)
* [Group Member](https://github.com/tgockel/zookeeper-cpp/issues/17)
None of the queue types are planned to be implemented.
The [Curator Documentation (TN4)](https://cwiki.apache.org/confluence/display/CURATOR/TN4) advises against their use,
claiming "it is a bad idea to use ZooKeeper as a Queue."
The authors of this library agree with this claim.
### `zk/fake`
This library also provides a fake version of ZooKeeper which operates in-memory.
It is meant to be used in your unit testing, when fine-grained control of behavior of ZooKeeper is needed.
This allows for the injection of arbitrary behavior into ZK, allowing you to simulate some of the hard-to-reproduce
issues like `zk::event_type::not_watching`, `zk::marshalling_error`, or timing bugs.
It also allows for fast creation and teardown of entire databases, which is commonly done in unit testing.
It is connected to through using a connection string of the form:
fake://{name}
To use this in unit tests link to `libzkpp_fake` and use `zk::fake::server`:
TEST(my_test)
{
// The default constructor uses a randomly-generated unique name
zk::fake::server server;
// Fetch that name through the connection_string
zk::client client(server.connection_string());
// use client normally
}
### `zk/server`
This library controls a ZooKeeper Java process on this machine.
It is meant to be used in applications that manage a ZooKeeper cluster from native code.
## Unsupported Functionality
If you are used to using ZooKeeper via the Java or C APIs, there are a few things that are explicitly not supported in
this library.
### Global Watches
There are two main ways to receive watch notifications: the global watch or through use a watcher objects.
In the Java API, the `ZooKeeper` client allows for a global
[Watcher](https://zookeeper.apache.org/doc/r3.4.10/api/org/apache/zookeeper/Watcher.html).
In the C API, `zookeeper_init` can be provided with a global function with the signature
`void (*)(zhandle_t* zh, int type, int state, const char* path, void* watcherCtx)` to achieve this same result.
Global watches are somewhat of a "legacy" feature -- the dual interface of global and callbacks is somewhat confusing.
As such, global watches are *not* supported by this library.
### Synchronous API
The C library offers both a synchronous and an asynchronous API.
This library offers only an asynchronous version.
If you prefer a synchronous API, call `get()` on the returned `future` to block until you receive the response.
### Non-Linux
Can you get this library working on platforms that are not Linux?
Maybe.
But Linux is the primary development, testing, and deployment platform of people writing distributed applications, so
this library is targetted at Linux.
## License
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0).
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
## F.A.Q.
### Why `erase` instead of `delete`?
In the Java and C APIs, the act of removing a ZNode is called `delete` and `zoo_delete`, respectively.
However, `delete` is a C++ keyword and cannot be used as a member function.
So, this library uses `erase`, which falls in line with standard C++ containers.
Alternatives such as calling the operation `delete_` look a bit worse (in the author's opinion).
### Why are watch calls separate?
In the Java and C APIs, adding a watch to a ZNode is an additional parameter to the `get`, `get_children`, or `exists`
calls while this library uses separate `watch`, `watch_children`, and `watch_exists` calls.
This is done because the return types are different between a simple fetch and setting a watch.
While `get` returns a `future<get_result>`, `watch` returns the slightly more complicated `future<watch_result>`.
The `future` in `watch_result::next()` would be disabled in cases where a flag is not set, and it would be ignored with
the majority of use cases.
This leads to an awkward API for simple calls.
An alternative used by other libraries is to provide a `std::function`, implying to not watch when the function is not
passed in.
This has a number of disadvantages:
- There is no good way to cancel a watch without giving an extra parameter.
With a `future`, you simply let it fall out of scope.
- Watches are delivered only once, which is obvious from a `future`-like API, but not obvious from a `function`-like
API.
- It is not obvious what the behavior should be if the original call returns in error.
With a `future`, the behavior is obvious, since you never receive the mechanisms to perform the watch.
In Java, the method of choice is to use the
[Watcher](https://zookeeper.apache.org/doc/r3.4.10/api/org/apache/zookeeper/Watcher.html) interface, but this feels
extremely out of place in C++ code.
### Where are all the `KeeperException`s?
This library uses an exception hierarchy with fewer exception codes than what are available in
[`KeeperException`](https://zookeeper.apache.org/doc/r3.4.10/api/org/apache/zookeeper/KeeperException.html).

Some exceptions are not present in this library because they are no longer used in the server implementation and will
not be used again; an example of this is `DataInconsistencyException`, which has not been used in ZooKeeper for a while.
In other cases, the error code would never be thrown by this library; examples of this are `NoWatcherException` (watch
removal happens implicitly in destructors) and `RuntimeInconsistencyException` (failed multi-ops throw a
`transaction_failed` containing only the index of the failed operation instead).
In other cases, the error codes have been merged into a single exception type, as there was much logical overlap.
Another distinction that was dropped is the difference between "system errors" (`Code.SYSTEMERROR`/`ZSYSTEMERROR`) and
"API errors" (`Code.APIERROR`/`ZAPIERROR`).
The general distinction is the origin of the error -- system errors are client-side (`invalid_arguments` -- other APIs:
`Code.BADARGUMENTS`/`ZBADARGUMENTS`), while API errors are server-size (`no_entry` -- other APIs:
`Code.NoNode`/`ZNONODE`).
This was dropped because this is not entirely meaningful from user's point of view.
As an example, `authentication_failed` is a subclass of `invalid_arguments`, even though the contents of the arguments
happen to be validated by the server instead of by the client.
### How can I contribute?
Pick an [open issue](https://github.com/tgockel/zookeeper-cpp/issues) and start working on it!
For more details, read the [CONTRIBUTING](https://github.com/tgockel/zookeeper-cpp/blob/master/CONTRIBUTING.rst) guide.
================================================
FILE: cmake/modules/BuildFunctions.cmake
================================================
include(CMakeParseArguments)
include(ListSplit)
# build_option
# Creates a build option, which is configurable via a CMake option. If the option is set to anything non-default, a
# macro with the name `ZKPP_ENABLE_${NAME}` is exported with the value of `0` or `1`.
#
# - NAME: The name of the build option.
# - DOC: Documentation to place in the configuration GUI.
# - DEFAULT: The default value of the configuration (ON or OFF).
# - CONFIGS_ON[]: List of build configurations this option should be ON for.
# - CONFIGS_OFF[]: List of build configurations this option should be OFF for.
function(build_option)
set(options)
set(oneValueArgs NAME DOC DEFAULT)
set(multiValueArgs CONFIGS_ON CONFIGS_OFF)
cmake_parse_arguments(OPT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT DEFINED VALID_BUILD_TYPES)
message(SEND_ERROR "VALID_BUILD_TYPES not set -- future build_option errors will be inaccurrate")
endif()
foreach(bt IN LISTS OPT_CONFIGS_ON OPT_CONFIGS_OFF)
if(NOT ${bt} IN_LIST VALID_BUILD_TYPES)
message(WARNING "Specified configuration for invalid CMAKE_BUILD_TYPE=${bt}")
endif()
endforeach()
if(${CMAKE_BUILD_TYPE} IN_LIST OPT_CONFIGS_ON)
set(ENABLED ON)
elseif(${CMAKE_BUILD_TYPE} IN_LIST OPT_CONFIGS_OFF)
set(ENABLED OFF)
else()
set(ENABLED ${OPT_DEFAULT})
endif()
set(ZKPP_BUILD_OPTION_${OPT_NAME} ${ENABLED}
CACHE BOOL "${OPT_DOC}"
)
if(ZKPP_BUILD_OPTION_${OPT_NAME})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZKPP_ENABLE_${OPT_NAME}=1" PARENT_SCOPE)
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZKPP_ENABLE_${OPT_NAME}=0" PARENT_SCOPE)
endif()
message(STATUS " ${OPT_NAME}: ${ENABLED}")
endfunction()
# build_module
# Adds a module to build.
#
# NAME: The name of this module.
# PATH: The path to find the source files for this module. It is legal to specify more than one PATH in this list.
# LINK_LIBRARIES: A list of libraries to link to
# PROTOTYPE: If set, the module should be considered a "prototype." It will not be built by default and does not
# consider warnings as errors.
# NO_RECURSE: Do not search recursively.
function(build_module)
set(options PROTOTYPE NO_RECURSE)
set(oneValueArgs NAME)
set(multiValueArgs LINK_LIBRARIES PATH)
cmake_parse_arguments(MODULE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
message(STATUS "${MODULE_NAME} : ${MODULE_PATH}")
if(MODULE_PROTOTYPE)
set(BUILD_PROTOTYPE_${MODULE_NAME} OFF
CACHE BOOL "Build the '${MODULE_NAME}' program?"
)
message(STATUS " ! prototype ${BUILD_PROTOTYPE_${MODULE_NAME}}")
if(NOT BUILD_PROTOTYPE_${MODULE_NAME})
return()
endif()
endif()
if(MODULE_NO_RECURSE)
set(CPP_SEARCH GLOB)
else()
set(CPP_SEARCH GLOB_RECURSE)
endif()
set(all_library_cpps)
set(all_library_hpps)
set(main_name)
foreach(subpath ${MODULE_PATH})
file(${CPP_SEARCH} local_library_cpps RELATIVE_PATH "." "${subpath}/*.cpp")
file(${CPP_SEARCH} local_library_hpps RELATIVE_PATH "." "${subpath}/*.hpp")
file(GLOB local_main_name RELATIVE_PATH "." "${subpath}/main.cpp")
if(local_main_name)
if(main_name)
message(SEND_ERROR "Found main.cpp in different paths for ${MODULE_NAME} (${main_name} and ${local_main_name})")
endif()
set(main_name ${local_main_name})
list(REMOVE_ITEM local_library_cpps ${local_main_name})
endif()
list(APPEND all_library_cpps ${local_library_cpps})
list(APPEND all_library_hpps ${local_library_hpps})
endforeach()
list_split(library_test_cpps library_cpps "${all_library_cpps}" "_tests.cpp")
list_split(library_test_hpps library_notest_hpps "${all_library_hpps}" "_tests.hpp")
list_split(library_detail_hpps library_hpps "${library_notest_hpps}" "detail")
list(APPEND library_cpps ${library_detail_hpps})
set(MODULE_TARGETS)
if(main_name)
message(STATUS " + executable")
list(APPEND MODULE_TARGETS ${MODULE_NAME}_prog)
add_executable(${MODULE_NAME}_prog ${main_name})
target_link_libraries(${MODULE_NAME}_prog ${MODULE_LINK_LIBRARIES})
set_target_properties(${MODULE_NAME}_prog
PROPERTIES
OUTPUT_NAME ${MODULE_NAME}
)
set(${MODULE_NAME}_MAIN_SOURCES main_name PARENT_SCOPE)
endif()
if(library_cpps)
list(LENGTH library_cpps library_cpps_length)
message(STATUS " + library (${library_cpps_length})")
list(APPEND MODULE_TARGETS ${MODULE_NAME})
add_library(${MODULE_NAME} SHARED ${library_cpps})
set_target_properties(${MODULE_NAME}
PROPERTIES
SOVERSION ${PROJECT_SO_VERSION}
VERSION ${PROJECT_SO_VERSION}
)
target_link_libraries(${MODULE_NAME} ${MODULE_LINK_LIBRARIES})
if(main_name)
target_link_libraries(${MODULE_NAME}_prog ${MODULE_NAME})
endif()
set(${MODULE_NAME}_LIBRARY_SOURCES ${library_cpps} PARENT_SCOPE)
set(${MODULE_NAME}_LIBRARY_HEADERS ${library_hpps} PARENT_SCOPE)
endif()
if(library_test_cpps)
list(LENGTH library_test_cpps library_test_cpps_length)
message(STATUS " + test library (${library_test_cpps_length})")
list(APPEND MODULE_TARGETS ${MODULE_NAME}_tests)
add_library(${MODULE_NAME}_tests SHARED ${library_test_cpps})
set_target_properties(${MODULE_NAME}_tests
PROPERTIES
SOVERSION ${PROJECT_SO_VERSION}
VERSION ${PROJECT_SO_VERSION}
)
target_link_libraries(${MODULE_NAME}_tests zkpp-tests)
if(library_cpps)
target_link_libraries(${MODULE_NAME}_tests ${MODULE_NAME})
endif()
target_link_libraries(zkpp-tests_prog ${MODULE_NAME}_tests)
endif()
if(MODULE_PROTOTYPE)
foreach(target ${MODULE_TARGETS})
set_target_properties(${target}
PROPERTIES
COMPILE_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error"
)
endforeach()
endif()
endfunction()
================================================
FILE: cmake/modules/CodeCoverage.cmake
================================================
# Copyright (c) 2012 - 2015, Lars Bilke
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. 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.
#
# 3. 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 HOLDER 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.
#
#
#
# 2012-01-31, Lars Bilke
# - Enable Code Coverage
#
# 2013-09-17, Joakim Söderberg
# - Added support for Clang.
# - Some additional usage instructions.
#
# USAGE:
# 0. (Mac only) If you use Xcode 5.1 make sure to patch geninfo as described here:
# http://stackoverflow.com/a/22404544/80480
#
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt:
# INCLUDE(CodeCoverage)
#
# 3. Set compiler flags to turn off optimization and enable coverage:
# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
#
# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
# which runs your test executable and produces a lcov code coverage report:
# Example:
# SETUP_TARGET_FOR_COVERAGE(
# my_coverage_target # Name for custom target.
# test_driver # Name of the test driver executable that runs the tests.
# # NOTE! This should always have a ZERO as exit code
# # otherwise the coverage generation will not complete.
# coverage # Name of output directory.
# )
#
# 4. Build a Debug build:
# cmake -DCMAKE_BUILD_TYPE=Debug ..
# make
# make my_coverage_target
#
#
# Check prereqs
FIND_PROGRAM( GCOV_PATH gcov )
FIND_PROGRAM( LCOV_PATH lcov )
FIND_PROGRAM( GENHTML_PATH genhtml )
FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
IF(NOT GCOV_PATH)
MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
ENDIF() # NOT GCOV_PATH
IF(NOT CMAKE_COMPILER_IS_GNUCXX)
# Clang version 3.0.0 and greater now supports gcov as well.
MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.")
IF(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
ENDIF()
ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX
SET(CMAKE_CXX_FLAGS_COVERAGE
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
SET(CMAKE_C_FLAGS_COVERAGE
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
MARK_AS_ADVANCED(
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
IF ( NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Coverage"))
MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" )
ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
# Param _targetname The name of new the custom make target
# Param _testrunner The name of the target which runs the tests.
# MUST return ZERO always, even on errors.
# If not, no coverage report will be created!
# Param _outputname lcov output is generated as _outputname.info
# HTML report is generated in _outputname/index.html
# Optional fourth parameter is passed as arguments to _testrunner
# Pass them in list form, e.g.: "-j;2" for -j 2
FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname)
IF(NOT LCOV_PATH)
MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
ENDIF() # NOT LCOV_PATH
IF(NOT GENHTML_PATH)
MESSAGE(FATAL_ERROR "genhtml not found! Aborting...")
ENDIF() # NOT GENHTML_PATH
# Setup target
ADD_CUSTOM_TARGET(${_targetname}
# Cleanup lcov
${LCOV_PATH} --directory . --zerocounters
# Run tests
COMMAND ${_testrunner} ${ARGV3}
# Capturing lcov counters and generating report
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info
COMMAND ${LCOV_PATH} --remove ${_outputname}.info '*_tests.cpp' '*generated*' '/usr/*' --output-file ${_outputname}.info.cleaned
COMMAND ${GENHTML_PATH} -o ${_outputname} ${_outputname}.info.cleaned
COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
)
# Show info where to find the report
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
COMMAND ;
COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report."
)
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE
================================================
FILE: cmake/modules/ConfigurationSetting.cmake
================================================
include(CMakeParseArguments)
# configuration_setting
# Creates a configuration setting, which is configurable via a CMake option. If the option is set to anything
# non-default, a macro with the name `ZKPP_${NAME}_USE_${OPTION_VALUE}` is exported as `1`.
#
# - NAME: The name of the configuration setting.
# - DOC: Documentation to place in the CMake configuration GUI.
# - DEFUALT: The default value of the configuration setting (some value from OPTIONS). This value must be synced with
# the C++ code or the behavior will be nonsense.
# - SET: Set the value to this. If unspecified, this will simply be the same as DEFUALT. However, this can be useful in
# cases where you wish to specify a non-default based on system information.
# - OPTIONS[]: List of valid options to set.
function(configuration_setting)
set(options)
set(oneValueArgs NAME DOC DEFAULT SET)
set(multiValueArgs OPTIONS)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT ARG_SET)
set(ARG_SET "${ARG_DEFAULT}")
endif()
set(ZKPP_BUILD_SETTING_${ARG_NAME} "${ARG_SET}"
CACHE STRING "${ARG_DOC}"
)
if(NOT ${ZKPP_BUILD_SETTING_${ARG_NAME}} IN_LIST ARG_OPTIONS)
message(SEND_ERROR "Invalid setting for ${ARG_NAME}: ${ZKPP_BUILD_SETTING_${ARG_NAME}}")
endif()
if(NOT ${ZKPP_BUILD_SETTING_${ARG_NAME}} STREQUAL ${ARG_DEFAULT})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZKPP_${ARG_NAME}_USE_${ZKPP_BUILD_SETTING_${ARG_NAME}}=1" PARENT_SCOPE)
endif()
message(STATUS " ${ARG_NAME}: ${ZKPP_BUILD_SETTING_${ARG_NAME}}")
endfunction()
================================================
FILE: cmake/modules/ListSplit.cmake
================================================
# list_split
# Split a list into values matching or not matching a given expression.
macro(list_split matching non_matching orig expression)
foreach(val ${orig})
if(${val} MATCHES ${expression})
list(APPEND ${matching} ${val})
else()
list(APPEND ${non_matching} ${val})
endif()
endforeach()
endmacro()
================================================
FILE: cmake/modules/ZooKeeper.cmake
================================================
# Utilities for configuring and running a ZooKeeper Server.
cmake_minimum_required(VERSION 3.5)
include(CMakeParseArguments)
find_package(Java COMPONENTS Runtime)
if(NOT Java_Runtime_FOUND)
message(FATAL_ERROR "Could not find Java Runtime")
endif()
# execute_jar
# Similar to execute_process, but drops "java -jar" in front for you so you can execute a JAR file in the same way you
# would a process.
macro(execute_jar)
execute_process(COMMAND "${Java_JAVA_EXECUTABLE}" "-jar" ${ARGN})
endmacro()
# execute_java_cp
# Similar to execute_process, but drops "java -cp" in front for you so you can execute a collection of Java locations in
# the same way you would a process (albeit more annoyingly).
macro(execute_java_cp)
execute_process(COMMAND "${Java_JAVA_EXECUTABLE}" "-cp" ${ARGN})
endmacro()
find_program(IVY_JAR
NAMES ivy.jar
PATHS "/usr/share/java"
)
if(NOT IVY_JAR)
message(FATAL_ERROR "Could not find Apache Ivy")
endif()
# find_zookeeper_server
# Get the ZooKeeper server JARs from Ivy.
#
# VERSION: ZooKeeper version to fetch. This can be any Ivy pattern (for example, "3.5+").
# OUTPUT_CLASSPATH: A variable to output a classpath that can be used to run the server.
#
# Example:
#
# find_zookeeper_server(VERSION "3.5+" OUTPUT_CLASSPATH ZOOKEEPER_SERVER_CLASSPATH)
# execute_java_cp("${ZOOKEEPER_SERVER_CLASSPATH}" "org.apache.zookeeper.server.quorum.QuorumPeerMain" 2181 zk-data)
function(find_zookeeper_server)
set(options)
set(oneValueArgs VERSION OUTPUT_CLASSPATH)
set(multiValueArgs)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT ARG_VERSION)
message(FATAL_ERROR "You must specify a VERSION to fetch")
endif()
if(NOT ARG_OUTPUT_CLASSPATH)
message(FATAL_ERROR "You must specify an OUTPUT_CLASSPATH")
endif()
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/ivy-cache")
set(TEMP_CLASSPATH_FILE "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/ivy-cache/zookeeper-${ARG_VERSION}.txt")
# There does not appear to be a good way to tell Ivy to be reasonable about progress reporting, so we'll silence it
# completely and hope the ellipsis in the status message will prevent people from thinking the system hung.
message(STATUS "Fetching ZooKeeper Server ${ARG_VERSION}...")
execute_jar(${IVY_JAR} "-dependency" "org.apache.zookeeper" "zookeeper" "${ARG_VERSION}"
"-cachepath" "${TEMP_CLASSPATH_FILE}"
OUTPUT_VARIABLE IVY_FETCH_OUTPUT
ERROR_VARIABLE IVY_FETCH_ERROR
)
if(EXISTS "${TEMP_CLASSPATH_FILE}")
file(READ "${TEMP_CLASSPATH_FILE}" CLASSPATH)
string(STRIP "${CLASSPATH}" CLASSPATH)
message(STATUS " > SUCCESS!")
set(${ARG_OUTPUT_CLASSPATH} "${CLASSPATH}" PARENT_SCOPE)
else()
message(SEND_ERROR "Could not fetch ZooKeeper Server ${ARG_VERSION}\n"
"Ivy fetch output:\n${IVY_FETCH_OUTPUT}\n"
"Ivy fetch errors:\n${IVY_FETCH_ERROR}"
)
endif()
endfunction()
================================================
FILE: config/dev-env
================================================
#!/bin/bash -e
# Create a build environment.
PROJECT_ROOT=$(readlink -f $(dirname $0)/..)
COMMAND=/bin/bash
DISTRO=
function show_usage {
echo "$0: Run a program in a Docker environment."
echo ""
echo "Options:"
echo ""
echo " --distro DISTRO:"
echo " The Linux distro to create the base container."
echo ""
echo " Available options:"
printf " "
printf " %s" $(ls $(dirname $0)/docker)
echo ""
echo " --:"
echo " Stop processing arguments and pass the remaining arguments to the"
echo " container."
echo ""
echo "Example:"
echo ""
echo " Enter into a bash shell:"
echo " $> $0 $(ls $(dirname $0)/docker | sort --random-sort | head -1)"
echo ""
echo " Build the package for a given distro:"
echo " $> $0 --distro=$(ls $(dirname $0)/docker | sort --random-sort | head -1) -- ./config/make-package"
}
if [[ $# -lt 1 ]]; then
show_usage
exit 1
else
UNRECOGNIZED=0
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
--distro)
DISTRO="$2"
shift 2
;;
--distro=*)
DISTRO="${key/--distro=/}"
shift
;;
--help)
show_usage
exit 1
;;
--)
shift
COMMAND=$@
break
;;
*)
if [[ -z "${DISTRO}" ]]; then
DISTRO="$key"
else
echo "Unrecognized option: $key"
UNRECOGNIZED=1
fi
shift
;;
esac
done
fi
if [[ -z "${DISTRO}" ]]; then
echo "Must set distro."
show_usage
exit 1
fi
IMAGE_NAME=dev/zookeeper-cpp/${DISTRO}
DOCKER_DIR=${PROJECT_ROOT}/config/docker/${DISTRO}
if ! [[ -e ${DOCKER_DIR}/Dockerfile ]]; then
echo "Specified distro \"${DISTRO}\" does not have a Dockerfile"
echo ""
show_usage
exit 1
fi
docker build ${DOCKER_DIR} -t ${IMAGE_NAME}
exec docker run \
--rm \
-v ${PROJECT_ROOT}:/root/zookeeper-cpp \
-w /root/zookeeper-cpp \
--security-opt seccomp=unconfined \
-it ${IMAGE_NAME} \
${COMMAND}
================================================
FILE: config/docker/debian-10/Dockerfile
================================================
FROM debian:10
LABEL maintainer="Travis Gockel <travis@gockelhut.com>"
RUN apt-get update \
&& apt-get install --yes \
cmake \
g++ \
grep \
googletest \
ivy \
lcov \
libgtest-dev \
libzookeeper-mt-dev \
ninja-build
CMD ["/root/zookeeper-cpp/config/run-tests"]
================================================
FILE: config/docker/opensuse-15/Dockerfile
================================================
FROM opensuse/leap:15
LABEL maintainer="Travis Gockel <travis@gockelhut.com>"
RUN zypper addrepo -f "https://download.opensuse.org/repositories/server:/database/openSUSE_Leap_15.2/" "serverdatabase" \
&& zypper --no-gpg-checks \
install -y \
apache-ivy \
cmake \
grep \
gcc-c++ \
git \
googletest-devel \
java-1_8_0-openjdk \
lcov \
libzookeeper2-devel \
ninja
CMD ["/root/zookeeper-cpp/config/run-tests"]
================================================
FILE: config/docker/ubuntu-16.04/Dockerfile
================================================
FROM ubuntu:16.04
LABEL maintainer="Travis Gockel <travis@gockelhut.com>"
RUN apt-get update
RUN apt-get install --yes software-properties-common
RUN add-apt-repository --yes ppa:ubuntu-toolchain-r/test
# You might ask why g++-6 is installed, even when we intend to build with GCC 7. It turns out that Coveralls won't work
# if it isn't installed due to...something. Instead of root causing the issue, we're going to ignore it and hope it gets
# fixed in a future release.
RUN apt-get update \
&& apt-get install --yes \
cmake \
grep \
g++-6 \
g++-7 \
ivy \
libgtest-dev \
libzookeeper-mt-dev \
ninja-build
# Code Coverage
RUN apt-get install --yes \
git \
lcov \
python-pip
RUN pip install --upgrade pip
RUN pip install \
cpp-coveralls \
pyyaml
RUN update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-7 99
CMD ["/root/zookeeper-cpp/config/run-tests"]
================================================
FILE: config/docker/ubuntu-18.04/Dockerfile
================================================
FROM ubuntu:18.04
LABEL maintainer="Travis Gockel <travis@gockelhut.com>"
RUN apt-get update \
&& apt-get install --yes \
cmake \
grep \
googletest \
g++-7 \
ivy \
lcov \
libgtest-dev \
libzookeeper-mt-dev \
ninja-build
RUN update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-7 99
CMD ["/root/zookeeper-cpp/config/run-tests"]
================================================
FILE: config/docker/ubuntu-20.04/Dockerfile
================================================
FROM ubuntu:20.04
LABEL maintainer="Travis Gockel <travis@gockelhut.com>"
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive \
apt-get install --yes \
cmake \
g++ \
grep \
googletest \
ivy \
lcov \
libgtest-dev \
libzookeeper-mt-dev \
ninja-build
CMD ["/root/zookeeper-cpp/config/run-tests"]
================================================
FILE: config/make-doxygen
================================================
#!/bin/bash -e
mkdir -p build/doc
doxygen doc/Doxyfile
================================================
FILE: config/make-package
================================================
#!/bin/bash -e
PROJECT_ROOT=$(readlink -f $(dirname $0)/..)
BUILD_DIR="$BUILD_DIR"
COPY_TO=""
DISTRO="$DISTRO"
PACKAGE_PREFIX=""
function show_usage {
echo "$0: Create a package for this operating system."
echo ""
echo "Usage: $0 [OPTION]..."
echo ""
echo "Options:"
echo ""
echo " --build-dir BUILD_DIR:"
echo " Set the build output directory. By default, this is build-release or"
echo " build-release-\${DISTRO} if --distro was set."
echo " --copy-to COPY_TO:"
echo " Copy the generated packages to the specified folder."
echo " --distro DISTRO:"
echo " Set the distro name."
echo " --dockerize DISTRO:"
echo " Perform the build inside of a dev-env-created Docker container. If used,"
echo " this must be the first option. The --distro flag is automatically set to"
echo " the same value."
echo " --package-prefix PREFIX:"
echo " Generated packages will be renamed with this prefix. By default, this"
echo " will be \"\${DISTRO}-\" if --distro was set."
echo ""
echo "Examples:"
echo ""
echo " Create a package inside an Ubuntu 18.04 container, copying the DEBs to a"
echo " packages folder."
echo " $> $0 --dockerize=ubuntu-18.04 --copy-to=packages"
}
UNRECOGNIZED=0
DOCKERIZE=0
FIRST=1
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
--build-dir)
BUILD_DIR="$2"
shift 2
;;
--build-dir=*)
BUILD_DIR="${key/--build-dir=/}"
shift
;;
--copy-to)
COPY_TO="$2"
shift 2
;;
--copy-to=*)
COPY_TO="${key/--copy-to=/}"
shift
;;
--distro)
DISTRO="$2"
shift 2
;;
--distro=*)
DISTRO="${key/--distro=/}"
shift
;;
--dockerize)
DISTRO="$2"
DOCKERIZE=1
shift 2
break
;;
--dockerize=*)
DISTRO="${key/--dockerize=/}"
DOCKERIZE=1
shift
break
;;
--help)
show_usage
exit 1
;;
--package-prefix)
PACKAGE_PREFIX="$2"
shift 2
;;
--package-prefix=*)
PACKAGE_PREFIX="${key/--package-prefix=/}"
shift
;;
*)
echo "Unrecognized option: $key"
UNRECOGNIZED=1
shift
;;
esac
FIRST=0
done
if [[ ${UNRECOGNIZED} -ne 0 ]]; then
show_usage
exit 1
fi
if [[ ${DOCKERIZE} -eq 1 ]]; then
if [[ ${FIRST} -eq 0 ]]; then
echo "If using --dockerize, it must be the first argument."
exit 1
fi
exec ${PROJECT_ROOT}/config/dev-env "${DISTRO}" -- \
./config/make-package \
--distro="${DISTRO}" \
$@
fi
if [[ -z "${BUILD_DIR}" ]]; then
if [[ -z "${DISTRO}" ]]; then
BUILD_DIR="build-release"
else
BUILD_DIR="build-release-${DISTRO}"
fi
fi
if [[ -z "${PACKAGE_PREFIX}" ]]; then
if [[ -n "${DISTRO}" ]]; then
PACKAGE_PREFIX="${DISTRO}-"
fi
fi
if $(hash rpm 2>/dev/null); then
PACKAGE_SYSTEM=RPM
PACKAGE_SUFFIX=rpm
function show_contents {
rpm -qlp "$1"
}
elif $(hash dpkg 2>/dev/null); then
PACKAGE_SYSTEM=DEB
PACKAGE_SUFFIX=deb
function show_contents {
dpkg --contents "${pkg_file}"
}
else
echo "Unknown packaging system for this operating system"
exit 2
fi
if [[ -e ${BUILD_DIR} ]]; then
echo "Build directory (${BUILD_DIR}) unclean -- deleting it"
rm -rf ${BUILD_DIR}
fi
mkdir ${BUILD_DIR}
cd ${BUILD_DIR}
cmake .. -DZKPP_PACKAGE_SYSTEM=${PACKAGE_SYSTEM} -DCMAKE_BUILD_TYPE=Release -GNinja
ninja package
cd -
for pkg_file in $(find "${BUILD_DIR}/install" -maxdepth 1 -name "*.${PACKAGE_SUFFIX}"); do
if [[ -n "${PACKAGE_PREFIX}" ]]; then
new_pkg_file="$(dirname -- ${pkg_file})/${PACKAGE_PREFIX}$(basename -- ${pkg_file})"
mv "${pkg_file}" "${new_pkg_file}"
pkg_file="${new_pkg_file}"
fi
echo "PACKAGE ${pkg_file}"
show_contents "${pkg_file}"
if [[ -n "${COPY_TO}" ]]; then
cp "${pkg_file}" "${COPY_TO}"
fi
done
================================================
FILE: config/make-packages
================================================
#!/bin/bash -e
PROJECT_ROOT=$(readlink -f $(dirname $0)/..)
function show_usage {
echo "$0: Create multiple packages."
echo ""
echo "Usage: $0 [OPTION]... [DISTRO]..."
echo ""
echo "Options:"
echo ""
echo " --all"
echo " Create packages for all known distributions. If this is specified, you"
echo " are not allowed to specify individual distributions."
echo " --copy-to COPY_TO:"
echo " Copy the generated packages to the specified folder."
echo " --:"
echo " Stop processing and pass the remaining arguments to make-package."
}
COPY_TO=
DISTROS=()
ALL=0
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
--all)
ALL=1
shift
;;
--copy-to)
COPY_TO="$2"
shift 2
;;
--copy-to=*)
COPY_TO="${key/--copy-to=/}"
shift
;;
--help)
show_usage
exit 1
;;
--)
# No shift: want to use the -- in the call
break
;;
*)
DISTROS+=(${key})
shift
;;
esac
done
if [[ ${ALL} -eq 1 ]]; then
if [[ ${#DISTROS[@]} -ne 0 ]]; then
echo "Specified --all, but also distributions: ${DISTROS[@]}"
echo ""
show_usage
exit 1
fi
# All distros with working package systems.
DISTROS=(ubuntu-16.04 ubuntu-17.10 ubuntu-18.04 ubuntu-18.10 debian-9)
fi
if [[ -n "${COPY_TO}" ]]; then
mkdir -p "${COPY_TO}"
COPY_TO="--copy-to=${COPY_TO}"
fi
for distro in ${DISTROS[@]}; do
echo "BUILDING PACKAGE FOR $distro"
${PROJECT_ROOT}/config/make-package --dockerize=${distro} ${COPY_TO} $@
done
================================================
FILE: config/publish-doxygen
================================================
#!/bin/bash -e
# Settings
REPO_PATH=git@github.com:tgockel/zookeeper-cpp.git
HTML_PATH=build/doc/html
COMMIT_USER="Documentation Builder"
COMMIT_EMAIL="travis@gockelhut.com"
CHANGESET=$(git rev-parse --verify HEAD)
# Get a clean version of the HTML documentation repo.
rm -rf ${HTML_PATH}
mkdir -p ${HTML_PATH}
git clone -b gh-pages "${REPO_PATH}" --single-branch ${HTML_PATH}
# rm all the files through git to prevent stale files.
cd ${HTML_PATH}
git rm -rf *.html *.js *.png *.css search
cd -
# Generate the HTML documentation.
./config/make-doxygen
# Create and commit the documentation repo.
cd ${HTML_PATH}
git add .
git config user.name "${COMMIT_USER}"
git config user.email "${COMMIT_EMAIL}"
git commit -m "Automated documentation build for changeset ${CHANGESET}."
git push origin gh-pages
cd -
================================================
FILE: config/run-tests
================================================
#!/bin/bash -e
PROJECT_ROOT=$(readlink -f $(dirname $0)/..)
BUILD_DIR=${PROJECT_ROOT}/build-ci
echo "${DISTRO} ${PROJECT_ROOT}"
c++ --version
if [[ -e ${BUILD_DIR} ]]; then
echo "Build directory (${BUILD_DIR}) unclean -- deleting it"
rm -rf ${BUILD_DIR}
fi
mkdir ${BUILD_DIR}
cd ${BUILD_DIR}
if $(hash lcov 2>/dev/null); then
# TODO(https://github.com/tgockel/zookeeper-cpp/issues/42): Disabling Coveralls coverage for now.
COVERAGE=0
else
COVERAGE=0
fi
cmake .. -DZKPP_BUILD_OPTION_CODE_COVERAGE=${COVERAGE}
make VERBOSE=1
make test
if [[ ${COVERAGE} -eq 1 ]]; then
${PROJECT_ROOT}/config/upload-coverage ${BUILD_DIR}
fi
================================================
FILE: config/upload-coverage
================================================
#!/bin/bash -e
# TODO: 99% sure there is a better way to do this
GCC_VERSION_MAJOR=$(c++ --version | grep -Po '\d\.\d\.\d' | grep -Po '^\d+' | head -1)
ls -l $1/../coveralls-repo-token
COVERALLS_REPO_TOKEN=$(cat $1/../coveralls-repo-token)
TRAVIS=true
CI=true
coveralls \
--repo-token="${COVERALLS_REPO_TOKEN}" \
--build-root $1 \
--exclude-pattern '_test\.cpp$' \
--exclude-pattern bits \
--gcov-options '\-lp' --gcov "gcov-${GCC_VERSION_MAJOR}"
================================================
FILE: doc/Doxyfile
================================================
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "zookeeper-cpp"
PROJECT_NUMBER =
PROJECT_BRIEF = "ZooKeeper Client for C++"
PROJECT_LOGO =
OUTPUT_DIRECTORY = build/doc
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF =
ALWAYS_DETAILED_SEC = YES
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = YES
STRIP_FROM_PATH = src
STRIP_FROM_INC_PATH = src
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = YES
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 4
ALIASES =
TCL_SUBST =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = YES
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = NO
SUBGROUPING = YES
INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO
TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = NO
EXTRACT_PRIVATE = NO
EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = YES
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = NO
SORT_MEMBERS_CTORS_1ST = YES
SORT_GROUP_NAMES = YES
SORT_BY_SCOPE_NAME = NO
STRICT_PROTO_MATCHING = NO
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_DIRECTORIES = NO
SHOW_FILES = YES
SHOW_NAMESPACES = YES
FILE_VERSION_FILTER =
LAYOUT_FILE =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = src README.md
INPUT_ENCODING = UTF-8
FILE_PATTERNS =
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS = *_tests.cpp *_tests.hpp tests detail
EXCLUDE_SYMBOLS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE = README.md
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = YES
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO
REFERENCES_LINK_SOURCE = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 220
HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = NO
HTML_DYNAMIC_SECTIONS = YES
HTML_INDEX_NUM_ENTRIES = 100
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = com.gockelhut.zookeeper-cpp
DOCSET_PUBLISHER_ID = com.gockelhut
DOCSET_PUBLISHER_NAME = Gockel
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
GENERATE_QHP = NO
QCH_FILE =
QHP_NAMESPACE = org.doxygen.Project
QHP_VIRTUAL_FOLDER = doc
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
QHG_LOCATION =
GENERATE_ECLIPSEHELP = NO
ECLIPSE_DOC_ID = com.gockelhut.zookeeper-cpp
DISABLE_INDEX = NO
GENERATE_TREEVIEW = YES
ENUM_VALUES_PER_LINE = 4
USE_INLINE_TREES = NO
TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
USE_MATHJAX = NO
MATHJAX_RELPATH = http://www.mathjax.org/mathjax
MATHJAX_EXTENSIONS =
SEARCHENGINE = YES
SERVER_BASED_SEARCH = NO
EXTERNAL_SEARCH = NO
SEARCHENGINE_URL =
SEARCHDATA_FILE = searchdata.xml
EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4
EXTRA_PACKAGES =
LATEX_HEADER =
LATEX_FOOTER =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_SOURCE_CODE = NO
LATEX_BIB_STYLE = plain
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_SCHEMA =
XML_DTD =
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED =
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = YES
MSCGEN_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = NO
DOT_NUM_THREADS = 0
DOT_FONTNAME = Helvetica
DOT_FONTSIZE = 10
DOT_FONTPATH =
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = YES
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = svg
INTERACTIVE_SVG = YES
DOT_PATH =
DOTFILE_DIRS = .
MSCFILE_DIRS =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = YES
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
================================================
FILE: install/deb/libzkpp/control.in
================================================
Package: libzkpp@PROJECT_SO_VERSION@
Version: @PROJECT_PACKAGE_VERSION@
Priority: optional
Maintainer: Travis Gockel <travis@gockelhut.com>
Architecture: @PROJECT_BUILD_ARCHITECTURE@
Section: libs
Depends: libzookeeper-mt2 (>= 3.4), libc6 (>= 2.18), libstdc++6 (>= 7-20170407)
Build-Depends: debhelper (>= 9), cmake (>= 3.5), libzookeeper-mt-dev (>= 3.4)
Homepage: https://tgockel.github.io/zookeeper-cpp/
Description: A ZooKeeper client for C++.
================================================
FILE: install/deb/libzkpp/postinst.in
================================================
#!/bin/sh -e
update-alternatives --install @CMAKE_INSTALL_PREFIX@/lib/libzkpp.so libzkpp @CMAKE_INSTALL_PREFIX@/lib/libzkpp.so.@PROJECT_SO_VERSION@ 10
================================================
FILE: install/deb/libzkpp/shlibs.in
================================================
libzkpp 0.2 libzkpp0.2 (>= 0.2.3)
================================================
FILE: install/deb/libzkpp-dev/control.in
================================================
Package: libzkpp@PROJECT_SO_VERSION@-dev
Version: @PROJECT_PACKAGE_VERSION@
Priority: optional
Maintainer: Travis Gockel <travis@gockelhut.com>
Architecture: all
Section: libdevel
Depends: libzkpp@PROJECT_SO_VERSION@ (= @PROJECT_PACKAGE_VERSION@)
Description: development files for zkpp
A ZooKeeper client for C++.
================================================
FILE: install/deb/libzkpp-server/control.in
================================================
Package: libzkpp-server@PROJECT_SO_VERSION@
Version: @PROJECT_PACKAGE_VERSION@
Priority: optional
Maintainer: Travis Gockel <travis@gockelhut.com>
Architecture: @PROJECT_BUILD_ARCHITECTURE@
Section: libs
Depends: libzkpp@PROJECT_SO_VERSION@ (= @PROJECT_PACKAGE_VERSION@)
Build-Depends: debhelper (>= 9), cmake (>= 3.5)
Homepage: https://tgockel.github.io/zookeeper-cpp/
Description: Control a ZooKeeper Java process on a single machine.
================================================
FILE: install/deb/libzkpp-server/postinst.in
================================================
#!/bin/sh -e
update-alternatives --install @CMAKE_INSTALL_PREFIX@/lib/libzkpp-server.so libzkpp-server @CMAKE_INSTALL_PREFIX@/lib/libzkpp-server.so.@PROJECT_SO_VERSION@ 10
================================================
FILE: install/deb/libzkpp-server-dev/control.in
================================================
Package: libzkpp-server@PROJECT_SO_VERSION@-dev
Version: @PROJECT_PACKAGE_VERSION@
Priority: optional
Maintainer: Travis Gockel <travis@gockelhut.com>
Architecture: all
Section: libdevel
Depends: libzkpp@PROJECT_SO_VERSION@-dev (= @PROJECT_PACKAGE_VERSION@),
libzkpp-server@PROJECT_SO_VERSION@ (= @PROJECT_PACKAGE_VERSION@)
Description: development files for zkpp_server@PROJECT_SO_VERSION@
Control a ZooKeeper Java process on a single machine.
================================================
FILE: src/zk/acl.cpp
================================================
#include "acl.hpp"
#include <ostream>
#include <sstream>
#include <tuple>
#include <utility>
namespace zk
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// permission //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::ostream& operator<<(std::ostream& os, const permission& self)
{
if (self == permission::none)
return os << "none";
else if (self == permission::all)
return os << "all";
bool first = true;
auto tick = [&] { return std::exchange(first, false) ? "" : "|"; };
if (allows(self, permission::read)) os << tick() << "read";
if (allows(self, permission::write)) os << tick() << "write";
if (allows(self, permission::create)) os << tick() << "create";
if (allows(self, permission::erase)) os << tick() << "erase";
if (allows(self, permission::admin)) os << tick() << "admin";
return os;
}
std::string to_string(const permission& self)
{
std::ostringstream os;
os << self;
return os.str();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// acl_rule //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
acl_rule::acl_rule(std::string scheme, std::string id, permission permissions) :
_scheme(std::move(scheme)),
_id(std::move(id)),
_permissions(permissions)
{ }
acl_rule::~acl_rule() noexcept
{ }
std::size_t hash(const acl_rule& self)
{
return std::hash<std::string>{}(self.scheme())
^ std::hash<std::string>{}(self.id())
^ std::hash<unsigned int>{}(static_cast<unsigned int>(self.permissions()));
}
bool operator==(const acl_rule& lhs, const acl_rule& rhs)
{
return lhs.scheme() == rhs.scheme()
&& lhs.id() == rhs.id()
&& lhs.permissions() == rhs.permissions();
}
bool operator!=(const acl_rule& lhs, const acl_rule& rhs)
{
return !(lhs == rhs);
}
bool operator< (const acl_rule& lhs, const acl_rule& rhs)
{
return std::tie(lhs.scheme(), lhs.id(), lhs.permissions()) < std::tie(rhs.scheme(), rhs.id(), rhs.permissions());
}
bool operator<=(const acl_rule& lhs, const acl_rule& rhs)
{
return !(rhs < lhs);
}
bool operator> (const acl_rule& lhs, const acl_rule& rhs)
{
return rhs < lhs;
}
bool operator>=(const acl_rule& lhs, const acl_rule& rhs)
{
return !(lhs < rhs);
}
std::ostream& operator<<(std::ostream& os, const acl_rule& self)
{
os << '(' << self.scheme();
if (!self.id().empty())
os << ':' << self.id();
os << ", " << self.permissions() << ')';
return os;
}
std::string to_string(const acl_rule& self)
{
std::ostringstream os;
os << self;
return os.str();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// acl //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
acl::acl(std::vector<acl_rule> rules) noexcept :
_impl(std::move(rules))
{ }
acl::~acl() noexcept
{ }
bool operator==(const acl& lhs, const acl& rhs)
{
return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend());
}
bool operator!=(const acl& lhs, const acl& rhs)
{
return !(lhs == rhs);
}
std::ostream& operator<<(std::ostream& os, const acl& self)
{
os << '[';
bool first = true;
for (const auto& x : self)
{
if (first)
first = false;
else
os << ", ";
os << x;
}
return os << ']';
}
std::string to_string(const acl& self)
{
std::ostringstream os;
os << self;
return os.str();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// acls //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const acl& acls::creator_all()
{
static acl instance = { { "auth", "", permission::all } };
return instance;
}
const acl& acls::open_unsafe()
{
static acl instance = { { "world", "anyone", permission::all } };
return instance;
}
const acl& acls::read_unsafe()
{
static acl instance = { { "world", "anyone", permission::read } };
return instance;
}
}
================================================
FILE: src/zk/acl.hpp
================================================
#pragma once
#include <zk/config.hpp>
#include <initializer_list>
#include <iosfwd>
#include <string>
#include <utility>
#include <vector>
#include "forwards.hpp"
namespace zk
{
/// \addtogroup Client
/// \{
/// Describes the ability of a user to perform a certain action. Permissions can be mixed together like integers with
/// \c | and \c &.
enum class permission : unsigned int
{
none = 0b00000, //!< No permissions are set (server could have been configured without ACL support).
read = 0b00001, //!< You can access the data of a node and can list its children.
write = 0b00010, //!< You can set the data of a node.
create = 0b00100, //!< You can create a child node.
erase = 0b01000, //!< You can erase a child node (but not necessarily this one).
admin = 0b10000, //!< You can alter permissions on this node.
all = 0b11111, //!< You can do anything.
};
/// \{
/// Set union operation of \ref permission.
inline constexpr permission operator|(permission a, permission b)
{
return permission(static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
}
inline constexpr permission& operator|=(permission& self, permission mask)
{
return self = self | mask;
}
/// \}
/// \{
/// Set intersection operation of \ref permission.
inline constexpr permission operator&(permission a, permission b)
{
return permission(static_cast<unsigned int>(a) & static_cast<unsigned int>(b));
}
inline constexpr permission& operator&=(permission& self, permission mask)
{
return self = self & mask;
}
/// \}
/// Set inverse operation of \ref permission. This is not exactly the bitwise complement of \a a, as the returned value
/// will only include bits set that are valid in \ref permission discriminants.
inline constexpr permission operator~(permission a)
{
return permission(~static_cast<unsigned int>(a)) & permission::all;
}
/// Check that \a self allows it \a perform all operations. For example,
/// `allows(permission::read | permission::write, permission::read)` will be \c true, as `read|write` is allowed to
/// \c read.
inline constexpr bool allows(permission self, permission perform)
{
return (self & perform) == perform;
}
std::ostream& operator<<(std::ostream&, const permission&);
std::string to_string(const permission&);
/// An individual rule in an \ref acl. It consists of a \ref scheme and \ref id pair to identify the who and a
/// \ref permission set to determine what they are allowed to do.
///
/// See <a href="https://zookeeper.apache.org/doc/r3.4.10/zookeeperProgrammers.html#sc_ACLPermissions">"Builtin ACL
/// Schemes"</a> in the ZooKeeper Programmer's Guide for more information.
class acl_rule final
{
public:
/// Create an ACL under the given \a scheme and \a id with the given \a permissions.
acl_rule(std::string scheme, std::string id, permission permissions);
acl_rule(const acl_rule&) = default;
acl_rule& operator=(const acl_rule&) = default;
acl_rule(acl_rule&&) = default;
acl_rule& operator=(acl_rule&&) = default;
~acl_rule() noexcept;
/// The authentication scheme this list is used for. The most common scheme is `"auth"`, which allows any
/// authenticated user to perform actions (see \ref acls::creator_all).
///
/// ZooKeeper's authentication system is extensible, but the majority of use cases are covered by the built-in
/// schemes:
///
/// - \c "world" -- This has a single ID \c "anyone" that represents any user of the system. The ACLs
/// \ref acls::open_unsafe and \ref acls::read_unsafe use the \c "world" scheme.
/// - \c "auth" -- This represents any authenticated user. The \c id field is unused. The ACL \ref acls::creator_all
/// uses the \c "auth" scheme.
/// - \c "digest" -- This uses a \c "${username}:${password}" string to generate MD5 hash which is then used as an
/// identity. Authentication is done by sending the string in clear text. When used in the ACL, the expression
/// will be the \c "${username}:${digest}", where \c digest is the base 64 encoded SHA1 digest of \c password.
/// - \c "ip" -- This uses the client host IP as an identity. The \c id expression is an IP address or CIDR netmask,
/// which will be matched against the client identity.
const std::string& scheme() const
{
return _scheme;
}
/// The ID of the user under the \ref scheme. For example, with the \c "ip" \c scheme, this is an IP address or CIDR
/// netmask.
const std::string& id() const
{
return _id;
}
/// The permissions associated with this ACL.
const permission& permissions() const
{
return _permissions;
}
private:
std::string _scheme;
std::string _id;
permission _permissions;
};
/// Compute a hash for the given \a rule.
std::size_t hash(const acl_rule& rule);
[[gnu::pure]] bool operator==(const acl_rule& lhs, const acl_rule& rhs);
[[gnu::pure]] bool operator!=(const acl_rule& lhs, const acl_rule& rhs);
[[gnu::pure]] bool operator< (const acl_rule& lhs, const acl_rule& rhs);
[[gnu::pure]] bool operator<=(const acl_rule& lhs, const acl_rule& rhs);
[[gnu::pure]] bool operator> (const acl_rule& lhs, const acl_rule& rhs);
[[gnu::pure]] bool operator>=(const acl_rule& lhs, const acl_rule& rhs);
std::ostream& operator<<(std::ostream&, const acl_rule&);
std::string to_string(const acl_rule&);
/// An access control list is a wrapper around \ref acl_rule instances. In general, the ACL system is similar to UNIX
/// file access permissions, where znodes act as files. Unlike UNIX, each znode can have any number of ACLs to
/// correspond with the potentially limitless (and pluggable) authentication schemes. A more surprising difference is
/// that ACLs are not recursive: If \c /path is only readable by a single user, but \c /path/sub is world-readable, then
/// anyone will be able to read \c /path/sub.
///
/// See <a href="https://zookeeper.apache.org/doc/trunk/zookeeperProgrammers.html#sc_ZooKeeperAccessControl">ZooKeeper
/// Programmer's Guide</a> for more information.
///
/// \see acls
class acl final
{
public:
using iterator = std::vector<acl_rule>::iterator;
using const_iterator = std::vector<acl_rule>::const_iterator;
using size_type = std::size_t;
public:
/// Create an empty ACL. Keep in mind that an empty ACL is an illegal ACL.
acl() = default;
/// Create an instance with the provided \a rules.
acl(std::vector<acl_rule> rules) noexcept;
/// Create an instance with the provided \a rules.
acl(std::initializer_list<acl_rule> rules) :
acl(std::vector<acl_rule>(rules))
{ }
acl(const acl&) = default;
acl& operator=(const acl&) = default;
acl(acl&&) = default;
acl& operator=(acl&&) = default;
~acl() noexcept;
/// The number of rules in this ACL.
size_type size() const { return _impl.size(); }
/// \{
/// Get the rule at the given \a idx.
const acl_rule& operator[](size_type idx) const { return _impl[idx]; }
acl_rule& operator[](size_type idx) { return _impl[idx]; }
/// \}
/// \{
/// Get the rule at the given \a idx.
///
/// \throws std::out_of_range if the \a idx is larger than \ref size.
const acl_rule& at(size_type idx) const { return _impl.at(idx); }
acl_rule& at(size_type idx) { return _impl.at(idx); }
/// \}
/// \{
/// Get an iterator to the beginning of the rule list.
iterator begin() { return _impl.begin(); }
const_iterator begin() const { return _impl.begin(); }
const_iterator cbegin() const { return _impl.begin(); }
/// \}
/// \{
/// Get an iterator to the end of the rule list.
iterator end() { return _impl.end(); }
const_iterator end() const { return _impl.end(); }
const_iterator cend() const { return _impl.end(); }
/// \}
/// Increase the reserved memory block so it can store at least \a capacity rules without reallocating.
void reserve(size_type capacity) { _impl.reserve(capacity); }
/// Construct a rule emplace on the end of the list using \a args.
///
/// \see push_back
template <typename... TArgs>
void emplace_back(TArgs&&... args)
{
_impl.emplace_back(std::forward<TArgs>(args)...);
}
/// \{
/// Add the rule \a x to the end of this list.
void push_back(acl_rule&& x) { emplace_back(std::move(x)); }
void push_back(const acl_rule& x) { emplace_back(x); }
/// \}
private:
std::vector<acl_rule> _impl;
};
[[gnu::pure]] bool operator==(const acl& lhs, const acl& rhs);
[[gnu::pure]] bool operator!=(const acl& lhs, const acl& rhs);
std::ostream& operator<<(std::ostream&, const acl&);
std::string to_string(const acl& self);
/// Commonly-used ACLs.
class acls
{
public:
/// This ACL gives the creators authentication id's all permissions.
static const acl& creator_all();
/// This is a completely open ACL. It is also the ACL used in operations like \ref client::create if no ACL is
/// specified.
static const acl& open_unsafe();
/// This ACL gives the world the ability to read.
static const acl& read_unsafe();
};
/// \}
}
namespace std
{
template <>
class hash<zk::acl_rule>
{
public:
using argument_type = zk::acl_rule;
using result_type = std::size_t;
result_type operator()(const argument_type& x) const
{
return zk::hash(x);
}
};
}
================================================
FILE: src/zk/acl_tests.cpp
================================================
#include <zk/tests/test.hpp>
#include "acl.hpp"
namespace zk
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// permission //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
GTEST_TEST(permission_tests, all)
{
auto all = permission::read
| permission::write
| permission::create
| permission::erase
| permission::admin;
CHECK_EQ(permission::all, all);
CHECK_EQ("all", to_string(all));
}
GTEST_TEST(permission_tests, stringification)
{
CHECK_EQ("none", to_string(permission::none));
CHECK_EQ("read|create", to_string(permission::read | permission::create));
CHECK_EQ("write|admin", to_string(permission::write | permission::admin));
CHECK_EQ("read|create|erase", to_string(permission::read | permission::create | permission::erase));
CHECK_EQ("admin", to_string(permission::admin));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// acl_rule //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
GTEST_TEST(acl_rule_tests, stringification)
{
CHECK_EQ("(ip:80.23.0.0/16, read|write)",
to_string(acl_rule("ip", "80.23.0.0/16", permission::read | permission::write))
);
}
GTEST_TEST(acl_rule_tests, comparisons)
{
acl_rule creator_all = { "auth", "", permission::all };
acl_rule world_open = { "world", "anyone", permission::all };
CHECK_EQ(creator_all, creator_all);
CHECK_NE(creator_all, world_open);
CHECK_LT(creator_all, world_open);
CHECK_LE(creator_all, world_open);
CHECK_GT(world_open, creator_all);
CHECK_GE(world_open, creator_all);
}
GTEST_TEST(acl_rule_tests, hashing)
{
acl_rule creator_all = { "auth", "", permission::all };
acl_rule world_open = { "world", "anyone", permission::all };
CHECK_EQ(hash(creator_all), hash(creator_all));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// acl //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
GTEST_TEST(acl_tests, stringification)
{
CHECK_EQ("[(auth, all)]", to_string(acls::creator_all()));
CHECK_EQ("[(world:anyone, all)]", to_string(acls::open_unsafe()));
CHECK_EQ("[(world:anyone, read)]", to_string(acls::read_unsafe()));
CHECK_EQ("[(auth, read), (ip:50.40.30.0/24, all)]",
to_string(acl({ { "auth", "", permission::read }, { "ip", "50.40.30.0/24", permission::all } }))
);
}
}
================================================
FILE: src/zk/buffer.hpp
================================================
/// \file
/// Controls the \c buffer type.
#pragma once
#include <zk/config.hpp>
#include <type_traits>
/// \addtogroup Client
/// \{
// \def ZKPP_BUFFER_USE_STD_STRING
// Set this to 1 to use \c std::string as the backing type for zk::buffer.
//
#ifndef ZKPP_BUFFER_USE_STD_STRING
# define ZKPP_BUFFER_USE_STD_STRING 0
#endif
/// \def ZKPP_BUFFER_USE_CUSTOM
/// Set this to 1 to use a custom definitions for \c buffer. If this is set, you must also set
/// \ref ZKPP_BUFFER_INCLUDE and \ref ZKPP_BUFFER_TYPE.
///
/// \def ZKPP_BUFFER_INCLUDE
/// The header file to use to find the \c buffer type. This value is set automatically if using built-in configuration
/// options (such as \ref ZKPP_BUFFER_USE_STD_VECTOR) and must be set manually if using \ref ZKPP_BUFFER_USE_CUSTOM.
///
/// \def ZKPP_BUFFER_TYPE
/// The type name to use for the \c buffer type. This value is set automatically if using built-in configuration
/// options (such as \ref ZKPP_BUFFER_USE_STD_VECTOR) and must be set manually if using \ref ZKPP_BUFFER_USE_CUSTOM.
#ifndef ZKPP_BUFFER_USE_CUSTOM
# define ZKPP_BUFFER_USE_CUSTOM 0
#endif
/// \def ZKPP_BUFFER_USE_STD_VECTOR
/// Set this to 1 to use \c std::vector<char> for the implementation of \ref zk::buffer. This is the default behavior.
#ifndef ZKPP_BUFFER_USE_STD_VECTOR
# if ZKPP_BUFFER_USE_STD_STRING || ZKPP_BUFFER_USE_CUSTOM
# define ZKPP_BUFFER_USE_STD_VECTOR 0
# else
# define ZKPP_BUFFER_USE_STD_VECTOR 1
# endif
#endif
#if ZKPP_BUFFER_USE_STD_VECTOR
# define ZKPP_BUFFER_INCLUDE <vector>
# define ZKPP_BUFFER_TYPE std::vector<char>
#elif ZKPP_BUFFER_USE_STD_STRING
# define ZKPP_BUFFER_INCLUDE <string>
# define ZKPP_BUFFER_TYPE std::string
#elif ZKPP_BUFFER_USE_CUSTOM
# if !defined ZKPP_BUFFER_INCLUDE || !defined ZKPP_BUFFER_TYPE
# error "When ZKPP_BUFFER_USE_CUSTOM is set, you must also define ZKPP_BUFFER_INCLUDE and ZKPP_BUFFER_TYPE"
# endif
#else
# error "Unknown type to use for zk::buffer"
#endif
/// \}
#include ZKPP_BUFFER_INCLUDE
namespace zk
{
/// \addtogroup Client
/// \{
/// The \c buffer type. By default, this is an \c std::vector<char>, but this can be altered by compile-time flags such
/// as \ref ZKPP_BUFFER_USE_CUSTOM. The requirements for a custom buffer are minimal -- the type must fit this criteria:
///
/// | expression | type | description |
/// |:----------------------|:--------------------------|:-------------------------------------------------------------|
/// | `buffer::value_type` | `char` | Buffers must be made of single-byte elements |
/// | `buffer::size_type` | `std::size_t` | |
/// | `buffer(ib, ie)` | `buffer` | Constructs a buffer with the range [`ib`, `ie`) |
/// | `buffer(buffer&&)` | `buffer` | Move constructible (must be `noexcept`). |
/// | `operator=(buffer&&)` | `buffer&` | Move assignable (must be `noexcept`). |
/// | `size()` | `size_type` | Get the length of the buffer |
/// | `data()` | `const value_type*` | Get a pointer to the beginning of the contents |
using buffer = ZKPP_BUFFER_TYPE;
// Check through static_assert:
static_assert(sizeof(buffer::value_type) == 1U, "buffer::value_type must be single-byte elements");
static_assert(std::is_same<std::size_t, buffer::size_type>::value, "buffer::size_type must be std::size_t");
static_assert(std::is_constructible<buffer, ptr<const buffer::value_type>, ptr<const buffer::value_type>>::value,
"buffer must be constructible with two pointers"
);
static_assert(std::is_move_constructible<buffer>::value, "buffer must be move-constructible");
static_assert(std::is_nothrow_move_constructible<buffer>::value, "buffer must be nothrow move-constructible");
static_assert(std::is_nothrow_move_assignable<buffer>::value, "buffer must be nothrow move-assignable");
static_assert(std::is_same<decltype(std::declval<const buffer&>().size()), buffer::size_type>::value,
"buffer::size() must return buffer::size_type"
);
static_assert(std::is_constructible<ptr<const buffer::value_type>,
decltype(std::declval<const buffer&>().data())
>::value,
"buffer::data() must return ptr<const buffer::value_type>"
);
/// \}
}
================================================
FILE: src/zk/client.cpp
================================================
#include "client.hpp"
#include "acl.hpp"
#include "connection.hpp"
#include "multi.hpp"
#include "exceptions.hpp"
#include <sstream>
#include <ostream>
namespace zk
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// client //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
client::client(const connection_params& params) :
client(connection::connect(params))
{ }
client::client(string_view conn_string) :
client(connection::connect(conn_string))
{ }
client::client(std::shared_ptr<connection> conn) noexcept :
_conn(std::move(conn))
{ }
future<client> client::connect(string_view conn_string)
{
return connect(connection_params::parse(conn_string));
}
future<client> client::connect(connection_params conn_params)
{
try
{
auto conn = connection::connect(conn_params);
auto state_change_fut = conn->watch_state();
if (conn->state() == state::connected)
{
promise<client> p;
p.set_value(client(std::move(conn)));
return p.get_future();
}
else
{
// TODO: Test if future::then can be relied on and use that instead of std::async
return zk::async
(
zk::launch::async,
[state_change_fut = std::move(state_change_fut), conn = std::move(conn)] () mutable -> client
{
state s(state_change_fut.get());
if (s == state::connected)
return client(conn);
else
zk::throw_exception(std::runtime_error(std::string("Unexpected state: ") + to_string(s)));
}
);
}
}
catch (...)
{
promise<client> p;
p.set_exception(zk::current_exception());
return p.get_future();
}
}
client::~client() noexcept = default;
void client::close()
{
_conn->close();
}
future<get_result> client::get(string_view path) const
{
return _conn->get(path);
}
future<watch_result> client::watch(string_view path) const
{
return _conn->watch(path);
}
future<get_children_result> client::get_children(string_view path) const
{
return _conn->get_children(path);
}
future<watch_children_result> client::watch_children(string_view path) const
{
return _conn->watch_children(path);
}
future<exists_result> client::exists(string_view path) const
{
return _conn->exists(path);
}
future<watch_exists_result> client::watch_exists(string_view path) const
{
return _conn->watch_exists(path);
}
future<create_result> client::create(string_view path,
const buffer& data,
const acl& rules,
create_mode mode
)
{
return _conn->create(path, data, rules, mode);
}
future<create_result> client::create(string_view path,
const buffer& data,
create_mode mode
)
{
return create(path, data, acls::open_unsafe(), mode);
}
future<set_result> client::set(string_view path, const buffer& data, version check)
{
return _conn->set(path, data, check);
}
future<get_acl_result> client::get_acl(string_view path) const
{
return _conn->get_acl(path);
}
future<void> client::set_acl(string_view path, const acl& rules, acl_version check)
{
return _conn->set_acl(path, rules, check);
}
future<void> client::erase(string_view path, version check)
{
return _conn->erase(path, check);
}
future<void> client::load_fence() const
{
return _conn->load_fence();
}
future<multi_result> client::commit(multi_op txn)
{
return _conn->commit(std::move(txn));
}
}
================================================
FILE: src/zk/client.hpp
================================================
#pragma once
#include <zk/config.hpp>
#include <memory>
#include <utility>
#include "buffer.hpp"
#include "forwards.hpp"
#include "future.hpp"
#include "optional.hpp"
#include "string_view.hpp"
#include "results.hpp"
#include "types.hpp"
namespace zk
{
/// \defgroup Client
/// Interacting with ZooKeeper as a \ref client.
/// \{
/// A ZooKeeper client connection. This is the primary class for interacting with the ZooKeeper cluster. The best way to
/// create a client is with the static \ref connect function.
class client final
{
public:
/// Create a client connected to the cluster specified by \a params.
explicit client(const connection_params& params);
/// Create a client connected to the cluster specified by \a conn_string.
///
/// \param conn_string A ZooKeeper \ref ConnectionStrings "connection string".
explicit client(string_view conn_string);
/// Create a client connected with \a conn.
explicit client(std::shared_ptr<connection> conn) noexcept;
/// \{
/// Create a client connected to the cluster specified by \c conn_string.
///
/// \param conn_string A ZooKeeper \ref ConnectionStrings "connection string".
/// \returns A future which will be filled when the conneciton is established. The future will be filled in error if
/// the client will never be able to connect to the cluster (for example: a bad connection string).
static future<client> connect(string_view conn_string);
static future<client> connect(connection_params conn_params);
/// \}
client(const client&) noexcept = default;
client(client&&) noexcept = default;
client& operator=(const client&) noexcept = default;
client& operator=(client&&) noexcept = default;
~client() noexcept;
/// Close the underlying \ref connection. All outstanding operations will be cancelled and all watches will be
/// delivered with \ref closed. You usually do not need to call this operation, as the destructor handles this
/// automatically.
void close();
/// Return the data and the \ref stat of the entry of the given \a path.
///
/// \throws no_entry If no entry exists at the given \a path, the future will be delievered with \ref no_entry.
future<get_result> get(string_view path) const;
/// Similar to \ref get, but if the call is successful (no error is returned), a watch will be left on the entry
/// with the given \a path. The watch will be triggered by a successful operation that sets data or erases the
/// entry.
///
/// \throws no_entry If no entry exists at the given \a path, the future will be delievered with \ref no_entry. To
/// watch for the creation of an entry, use \ref watch_exists.
future<watch_result> watch(string_view path) const;
/// Return the list of the children of the entry of the given \a path. The returned values are not prefixed with the
/// provided \a path; i.e. if the database contains \c "/path/a" and \c "/path/b", the result of \c get_children for
/// \c "/path" will be `["a", "b"]`. The list of children returned is not sorted and no guarantee is provided as to
/// its natural or lexical order.
///
/// \throws no_entry If no entry exists at the given \a path, the future will be delievered with \ref no_entry.
future<get_children_result> get_children(string_view path) const;
/// Similar to \ref get_children, but if the call is successful (no error is returned), a watch will be left on the
/// entry with the given \a path. The watch will be triggered by a successful operation that erases the entry at the
/// given \a path or creates or erases a child immediately under the path (it is not recursive).
future<watch_children_result> watch_children(string_view path) const;
/// Return the \ref stat of the entry of the given \a path or \c nullopt if it does not exist.
future<exists_result> exists(string_view path) const;
/// Similar to \ref watch, but if the call is successful (no error is returned), a watch will be left on the entry
/// with the given \a path. The watch will be triggered by a successful operation that creates the entry, erases the
/// entry, or sets the data on the entry.
future<watch_exists_result> watch_exists(string_view path) const;
/// \{
/// Create an entry at the given \a path.
///
/// This operation, if successful, will trigger all the watches left on the entry of the given path by \ref watch
/// API calls, and the watches left on the parent entry by \ref watch_children API calls.
///
/// \param path The path or path pattern (if using \ref create_mode::sequential) to create.
/// \param data The data to create for the entry.
/// \param mode Specifies the behavior of the created entry (see \ref create_mode for more information).
/// \param rules The ACL for the created entry. If unspecified, it is equivalent to providing
/// \ref acls::open_unsafe.
/// \returns A future which will be filled with the name of the created entry and its \ref stat.
///
/// \throws entry_exists If an entry with the same actual \a path already exists in the ZooKeeper, the future will
/// be delivered with \ref entry_exists. Note that since a different actual path is used for each invocation of
/// creating sequential entry with the same \a path argument, the call should never error in this manner.
/// \throws no_entry If the parent of the given \a path does not exist, the future will be delievered with
/// \ref no_entry.
/// \throws no_children_for_ephemerals An ephemeral entry cannot have children. If the parent entry of the given
/// \a path is ephemeral, the future will be delivered with \c no_children_for_ephemerals.
/// \throws invalid_acl If the \a acl is invalid or empty, the future will be delivered with \ref invalid_acl.
/// \throws invalid_arguments The maximum allowable size of the data array is 1 MiB (1,048,576 bytes). If \a data
/// is larger than this the future will be delivered with \ref invalid_arguments.
future<create_result> create(string_view path,
const buffer& data,
const acl& rules,
create_mode mode = create_mode::normal
);
future<create_result> create(string_view path,
const buffer& data,
create_mode mode = create_mode::normal
);
/// \}
/// Set the data for the entry of the given \a path if such an entry exists and the given version matches the
/// version of the entry (if the given version is \ref version::any, there is no version check). This operation, if
/// successful, will trigger all the watches on the entry of the given \c path left by \ref watch calls.
///
/// \throws no_entry If no entry exists at the given \a path, the future will be delievered with \ref no_entry.
/// \throws version_mismatch If the given version \a check does not match the entry's version, the future will be
/// delivered with \ref version_mismatch.
/// \throws invalid_arguments The maximum allowable size of the data array is 1 MiB (1,048,576 bytes). If \a data
/// is larger than this the future will be delivered with \ref invalid_arguments.
future<set_result> set(string_view path, const buffer& data, version check = version::any());
/// Return the ACL and \ref stat of the entry of the given path.
///
/// \throws no_entry If no entry exists at the given \a path, the future will be delievered with \ref no_entry.
future<get_acl_result> get_acl(string_view path) const;
/// Set the ACL for the entry of the given \a path if such an entry exists and the given version \a check matches
/// the version of the entry.
///
/// \param check If specified, check that the ACL version matches. Keep in mind this is the \c acl_version, not the
/// data \ref version -- there is no way to have this operation fail on changes to \ref stat::data_version without
/// the use of a \ref multi_op.
///
/// \throws no_entry If no entry exists at the given \a path, the future will be delievered with \ref no_entry.
/// \throws version_mismatch If the given version \a check does not match the entry's version, the future will be
/// delivered with \ref version_mismatch.
future<void> set_acl(string_view path, const acl& rules, acl_version check = acl_version::any());
/// Erase the entry at the given \a path. The call will succeed if such an entry exists, and the given version
/// \a check matches the entry's version (if the given version is \ref version::any, it matches any entry's
/// versions). This operation, if successful, will trigger all the watches on the entry of the given \a path left by
/// \ref watch API calls, watches left by \ref watch_exists API calls, and the watches on the parent entry left by
/// \ref watch_children API calls.
///
/// \throws no_entry If no entry exists at the given \a path, the future will be delievered with \ref no_entry.
/// \throws version_mismatch If the given version \a check does not match the entry's version, the future will be
/// delivered with \ref version_mismatch.
/// \throws not_empty You are only allowed to erase entries with no children. If the entry has children, the future
/// will be delievered with \ref not_empty.
future<void> erase(string_view path, version check = version::any());
/// Ensure that all subsequent reads observe the data at the transaction on the server at or past real-time \e now.
/// If your application communicates only through reads and writes of ZooKeeper, this operation is never needed.
/// However, if your application communicates a change in ZooKeeper state through means outside of ZooKeeper (called
/// a "hidden channel" in ZooKeeper vernacular), then it is possible for a receiver to attempt to react to a change
/// before it can observe it through ZooKeeper state.
///
/// \warning
/// The internal pipeline for this operation is not the same as modifying operations (\ref set, \ref create, etc.).
/// In cases of leader failure, there is a chance that the leader does not have support from the quorum, as it has
/// switched to a new leader. While this is rare, it is still \e possible that not all updates have been processed.
///
/// \note
/// Other APIs call this operation \c sync and allow you to provide a \c path parameter. There are a few issues with
/// this. First: that name conflicts with the commonly-used POSIX \c sync command, leading to confusion that data in
/// ZooKeeper does not have integrity. Secondly, this operation has more in common with \c std::atomic_thread_fence
/// or the x86 \c lfence instruction than \c sync (on the server, "flush" is an appropriate term -- just like fence
/// implementations in CPUs). Finally, the \c path parameter is ignored by the server -- all future fetches are
/// fenced, no matter what path is specified. In the future, ZooKeeper might support partitioning, in which case the
/// \c path parameter might become relevant.
///
/// It is often not necessary to wait for the fence future to be returned, as future reads will be synced without
/// waiting. However, there is no guarantee on the ordering of the read if the future returned from \c load_fence
/// is completed in error.
///
/// \code
/// auto fence_future = client.load_fence();
/// // data_future will be completed with the load_fence, even though we haven't waited to complete
/// auto data_future = client.get("/some/path");
///
/// // Useful to use when_all to concat and error check (C++ Extensions for Concurrency, ISO/IEC TS 19571:2016)
/// auto guaranteed_future = std::when_all(std::move(fence_future), std::move(data_future));
/// \endcode
future<void> load_fence() const;
/// Commit the transaction specified by \a txn. The operations are performed atomically: They will either all
/// succeed or all fail.
///
/// \throws transaction_failed If the transaction does not complete with an error in the transaction itself (any
/// \ref error_code that fits \ref is_api_error), the future will be delivered with \ref transaction_failed. Check
/// the thrown \ref transaction_failed::underlying_cause for more information.
/// \throws system_error For the same reasons any other operation might fail, the future will be delivered with a
/// specific \ref system_error.
future<multi_result> commit(multi_op txn);
private:
std::shared_ptr<connection> _conn;
};
/// \}
}
================================================
FILE: src/zk/client_tests.cpp
================================================
#include <zk/server/server_tests.hpp>
#include <algorithm>
#include <chrono>
#include <cstring>
#include <iostream>
#include <thread>
#include "client.hpp"
#include "error.hpp"
#include "multi.hpp"
#include "string_view.hpp"
namespace zk
{
static buffer buffer_from(string_view str)
{
return buffer(str.data(), str.data() + str.size());
}
class client_tests :
public server::single_server_fixture
{ };
GTEST_TEST_F(client_tests, get_root)
{
client c = get_connected_client();
auto res = c.get("/").get();
std::cerr << res << std::endl;
c.close();
}
GTEST_TEST_F(client_tests, exists)
{
client c = get_connected_client();
CHECK_TRUE(c.exists("/").get());
CHECK_FALSE(c.exists("/some/bogus/path").get());
}
GTEST_TEST_F(client_tests, create)
{
client c = get_connected_client();
const char local_buf[10] = { 0 };
auto f_create = c.create("/test-node", buffer(local_buf, local_buf + sizeof local_buf));
auto name = f_create.get().name();
CHECK_EQ("/test-node", name);
}
GTEST_TEST_F(client_tests, create_seq_and_set)
{
client c = get_connected_client();
auto f_create = c.create("/test-node-", buffer_from("Hello!"), create_mode::sequential);
auto name = f_create.get().name();
auto orig_stat = c.get(name).get().stat();
auto expected_version = orig_stat.data_version;
++expected_version;
c.set(name, buffer_from("WORLD")).get();
auto contents = c.get(name).get();
CHECK_EQ(contents.stat().data_version, expected_version);
CHECK_TRUE(contents.data() == buffer_from("WORLD"));
}
GTEST_TEST_F(client_tests, create_seq_and_erase)
{
client c = get_connected_client();
auto f_create = c.create("/test-node-", buffer_from("Hello!"), create_mode::sequential);
auto name = f_create.get().name();
auto orig_stat = c.get(name).get().stat();
c.erase(name, orig_stat.data_version).get();
CHECK_THROWS(no_entry)
{
c.get(name).get();
};
}
GTEST_TEST_F(client_tests, create_seq_and_get_children)
{
client c = get_connected_client();
auto f_create = c.create("/test-node-", buffer_from("Hello!"), create_mode::sequential);
auto name = f_create.get().name();
auto orig_stat = c.get(name).get().stat();
c.create(name + "/a", buffer()).get();
c.create(name + "/b", buffer()).get();
c.create(name + "/c", buffer()).get();
auto result = c.get_children(name).get();
CHECK_EQ(orig_stat.data_version, result.parent_stat().data_version);
CHECK_LT(orig_stat.child_version, result.parent_stat().child_version);
std::vector<std::string> expected_children = { "a", "b", "c" };
std::sort(result.children().begin(), result.children().end());
CHECK_TRUE(expected_children == result.children());
}
GTEST_TEST_F(client_tests, acl)
{
client c = get_connected_client();
auto name = c.create("/test-node-", buffer_from("Hello!"), create_mode::sequential).get().name();
// set the data of the node a few times to make sure the data_version is different value from acl_version
for (std::size_t changes = 0; changes < 5; ++changes)
c.set(name, buffer_from("data change")).get();
auto orig_result = c.get_acl(name).get();
CHECK_EQ(acls::open_unsafe(), orig_result.acl());
std::cerr << "HEY: " << orig_result << std::endl;
c.set_acl(name, acls::read_unsafe(), orig_result.stat().acl_version).get();
auto new_result = c.get_acl(name).get();
CHECK_EQ(acls::read_unsafe(), new_result.acl());
}
GTEST_TEST_F(client_tests, watch_change)
{
client c = get_connected_client();
auto name = c.create("/test-node-", buffer_from("Hello!"), create_mode::sequential).get().name();
auto watch = c.watch(name).get();
CHECK_TRUE(watch.initial().data() == buffer_from("Hello!"));
c.set(name, buffer_from("world")); // don't wait -- the watch won't trigger until the operation completes
auto ev = watch.next().get();
CHECK_EQ(ev.type(), event_type::changed);
CHECK_EQ(ev.state(), state::connected);
}
GTEST_TEST_F(client_tests, watch_children)
{
client c = get_connected_client();
auto root_name = c.create("/test-node-", buffer_from("Hello!"), create_mode::sequential).get().name();
c.commit({
op::create(root_name + "/a", buffer_from("a")),
op::create(root_name + "/b", buffer_from("b")),
op::create(root_name + "/c", buffer_from("c")),
op::create(root_name + "/d", buffer_from("d")),
}).get();
auto watch_child_creation = c.watch_children(root_name).get();
CHECK_EQ(4U, watch_child_creation.initial().children().size());
c.create(root_name + "/e", buffer_from("e"));
auto ev = watch_child_creation.next().get();
CHECK_EQ(ev.type(), event_type::child);
CHECK_EQ(ev.state(), state::connected);
auto watch_child_erase = c.watch_children(root_name).get();
CHECK_EQ(5U, watch_child_erase.initial().children().size());
c.erase(root_name + "/a");
auto ev2 = watch_child_erase.next().get();
CHECK_EQ(ev2.type(), event_type::child);
CHECK_EQ(ev2.state(), state::connected);
}
GTEST_TEST_F(client_tests, watch_exists)
{
client c = get_connected_client();
auto root_name = c.create("/test-node-", buffer_from("Hello!"), create_mode::sequential).get().name();
auto watch_creation = c.watch_exists(root_name + "/sub").get();
CHECK_FALSE(watch_creation.initial());
c.create(root_name + "/sub", buffer_from("Blah"));
auto ev = watch_creation.next().get();
CHECK_EQ(ev.type(), event_type::created);
CHECK_EQ(ev.state(), state::connected);
auto watch_erase = c.watch_exists(root_name + "/sub").get();
CHECK_TRUE(watch_erase.initial());
c.erase(root_name + "/sub");
auto ev2 = watch_erase.next().get();
CHECK_EQ(ev2.type(), event_type::erased);
CHECK_EQ(ev2.state(), state::connected);
}
GTEST_TEST_F(client_tests, load_fence)
{
client c = get_connected_client();
// There does not appear to be a good way to actually test this -- so just make sure we don't segfault
c.load_fence().get();
}
GTEST_TEST_F(client_tests, watch_close)
{
client c = get_connected_client();
auto watch = c.watch("/").get();
c.close();
// watch should be triggered with session closed
auto ev = watch.next().get();
CHECK_EQ(ev.type(), event_type::session);
CHECK_EQ(ev.state(), state::closed);
}
class stopping_client_tests :
public server::server_fixture
{ };
GTEST_TEST_F(stopping_client_tests, watch_server_stop)
{
client c = get_connected_client();
auto watch = c.watch("/").get();
this->stop_server(true);
auto ev = watch.next().get();
CHECK_EQ(ev.type(), event_type::session);
}
}
================================================
FILE: src/zk/config.hpp
================================================
#pragma once
/// \addtogroup Client
/// \{
/// \def ZKPP_USER_CONFIG
/// A user-defined configuration file to be included before all other ZooKeeper C++ content.
#ifdef ZKPP_USER_CONFIG
# include ZKPP_USER_CONFIG
#endif
#define ZKPP_VERSION_MAJOR 0
#define ZKPP_VERSION_MINOR 2
#define ZKPP_VERSION_PATCH 3
/// \def ZKPP_DEBUG
/// Was ZooKeeper C++ compiled in debug mode? This value must be the same between when the SO was built and when you are
/// compiling. In general, this is not useful outside of library maintainers.
///
/// \warning
/// Keep in mind this value is \e always defined. Use `#if ZKPP_DEBUG`, \e not `#ifdef ZKPP_DEBUG`.
#ifndef ZKPP_DEBUG
# define ZKPP_DEBUG 0
#endif
/// \}
namespace zk
{
/// \addtogroup Client
/// \{
/// A simple, unowned pointer. It operates exactly like using \c *, but removes the question of \c * associativity and
/// is easier to read when \c const qualifiers are involved.
template <typename T>
using ptr = T*;
/// \}
}
================================================
FILE: src/zk/connection.cpp
================================================
#include "connection.hpp"
#include "connection_zk.hpp"
#include "error.hpp"
#include "types.hpp"
#include "exceptions.hpp"
#include <algorithm>
#include <regex>
#include <ostream>
#include <sstream>
#include <stdexcept>
#include <unordered_set>
#include <zookeeper/zookeeper.h>
namespace zk
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// connection //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
connection::~connection() noexcept
{ }
std::shared_ptr<connection> connection::connect(const connection_params& params)
{
return std::make_shared<connection_zk>(params);
}
std::shared_ptr<connection> connection::connect(string_view conn_string)
{
return connect(connection_params::parse(conn_string));
}
future<zk::state> connection::watch_state()
{
std::unique_lock<std::mutex> ax(_state_change_promises_protect);
_state_change_promises.emplace_back();
return _state_change_promises.rbegin()->get_future();
}
void connection::on_session_event(zk::state new_state)
{
std::unique_lock<std::mutex> ax(_state_change_promises_protect);
auto l_state_change_promises = std::move(_state_change_promises);
ax.unlock();
auto ex = new_state == zk::state::expired_session ? get_exception_ptr_of(error_code::session_expired)
: new_state == zk::state::authentication_failed ? get_exception_ptr_of(error_code::authentication_failed)
: zk::exception_ptr();
for (auto& p : l_state_change_promises)
{
if (ex)
p.set_exception(ex);
else
p.set_value(new_state);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// connection_params //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
connection_params::connection_params() noexcept :
_connection_schema("zk"),
_hosts({}),
_chroot("/"),
_randomize_hosts(true),
_read_only(false),
_timeout(default_timeout)
{ }
connection_params::~connection_params() noexcept
{ }
template <typename TMatch>
static string_view sv_from_match(const TMatch& src)
{
return string_view(src.first, std::distance(src.first, src.second));
}
template <typename FAction>
void split_each_substr(string_view src, char delim, const FAction& action)
{
while (!src.empty())
{
// if next_div is src.end, the logic still works
auto next_div = std::find(src.begin(), src.end(), delim);
action(string_view(src.data(), std::distance(src.begin(), next_div)));
src.remove_prefix(std::distance(src.begin(), next_div));
if (!src.empty())
src.remove_prefix(1U);
}
}
static connection_params::host_list extract_host_list(string_view src)
{
connection_params::host_list out;
out.reserve(std::count(src.begin(), src.end(), ','));
split_each_substr(src, ',', [&] (string_view sub) { out.emplace_back(std::string(sub)); });
return out;
}
static bool extract_bool(string_view key, string_view val)
{
if (val.empty())
zk::throw_exception(std::invalid_argument(std::string("Key ") + std::string(key) + " has blank value"));
switch (val[0])
{
case '1':
case 't':
case 'T':
return true;
case '0':
case 'f':
case 'F':
return false;
default:
zk::throw_exception(std::invalid_argument(std::string("Invalid value for ") + std::string(key) + std::string(" \"")
+ std::string(val) + "\" -- expected a boolean"
));
}
}
static std::chrono::milliseconds extract_millis(string_view key, string_view val)
{
if (val.empty())
zk::throw_exception(std::invalid_argument(std::string("Key ") + std::string(key) + " has blank value"));
if (val[0] == 'P')
{
zk::throw_exception(std::invalid_argument("ISO 8601 duration is not supported (yet)."));
}
else
{
double seconds = std::stod(std::string(val));
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::duration<double>(seconds));
}
}
static void extract_advanced_options(string_view src, connection_params& out)
{
if (src.empty() || src.size() == 1U)
return;
else
src.remove_prefix(1U);
std::string invalid_keys_msg;
auto invalid_key = [&] (string_view key)
{
if (invalid_keys_msg.empty())
invalid_keys_msg = "Invalid key in querystring: ";
else
invalid_keys_msg += ", ";
invalid_keys_msg += std::string(key);
};
split_each_substr(src, '&', [&] (string_view qp_part)
{
auto eq_it = std::find(qp_part.begin(), qp_part.end(), '=');
if (eq_it == qp_part.end())
zk::throw_exception(std::invalid_argument("Invalid connection string -- query string must be specified as "
"\"key1=value1&key2=value2...\""
));
auto key = qp_part.substr(0, std::distance(qp_part.begin(), eq_it));
auto val = qp_part.substr(std::distance(qp_part.begin(), eq_it) + 1);
if (key == "randomize_hosts")
out.randomize_hosts() = extract_bool(key, val);
else if (key == "read_only")
out.read_only() = extract_bool(key, val);
else if (key == "timeout")
out.timeout() = extract_millis(key, val);
else
invalid_key(key);
});
if (!invalid_keys_msg.empty())
zk::throw_exception(std::invalid_argument(std::move(invalid_keys_msg)));
}
connection_params connection_params::parse(string_view conn_string)
{
static const std::regex expr(R"(([^:]+)://([^/]+)((/[^\?]*)(\?.*)?)?)",
std::regex_constants::ECMAScript | std::regex_constants::optimize
);
constexpr auto schema_idx = 1U;
constexpr auto hostaddr_idx = 2U;
constexpr auto path_idx = 4U;
constexpr auto querystring_idx = 5U;
std::cmatch match;
if (!std::regex_match(conn_string.begin(), conn_string.end(), match, expr))
zk::throw_exception(std::invalid_argument(std::string("Invalid connection string (") + std::string(conn_string)
+ " -- format is \"schema://[auth@]${host_addrs}/[path][?options]\""
));
connection_params out;
out.connection_schema() = match[schema_idx].str();
out.hosts() = extract_host_list(sv_from_match(match[hostaddr_idx]));
out.chroot() = match[path_idx].str();
if (out.chroot().empty())
out.chroot() = "/";
extract_advanced_options(sv_from_match(match[querystring_idx]), out);
return out;
}
bool operator==(const connection_params& lhs, const connection_params& rhs)
{
return lhs.connection_schema() == rhs.connection_schema()
&& lhs.hosts() == rhs.hosts()
&& lhs.chroot() == rhs.chroot()
&& lhs.randomize_hosts() == rhs.randomize_hosts()
&& lhs.read_only() == rhs.read_only()
&& lhs.timeout() == rhs.timeout();
}
bool operator!=(const connection_params& lhs, const connection_params& rhs)
{
return !(lhs == rhs);
}
std::ostream& operator<<(std::ostream& os, const connection_params& x)
{
os << x.connection_schema() << "://";
bool first = true;
for (const auto& host : x.hosts())
{
if (first)
first = false;
else
os << ',';
os << host;
}
os << x.chroot();
first = true;
auto query_string = [&] (ptr<const char> key, const auto& val)
{
if (first)
{
first = false;
os << '?';
}
else
{
os << '&';
}
os << key << '=' << val;
};
if (!x.randomize_hosts())
query_string("randomize_hosts", "false");
if (x.read_only())
query_string("read_only", "true");
if (x.timeout() != connection_params::default_timeout)
query_string("timeout", std::chrono::duration<double>(x.timeout()).count());
return os;
}
std::string to_string(const connection_params& x)
{
std::ostringstream os;
os << x;
return os.str();
}
}
================================================
FILE: src/zk/connection.hpp
================================================
#pragma once
#include <zk/config.hpp>
#include <chrono>
#include <iosfwd>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "buffer.hpp"
#include "forwards.hpp"
#include "future.hpp"
#include "string_view.hpp"
namespace zk
{
/// \addtogroup Client
/// \{
/// An actual connection to the server. The majority of methods have the same signature and meaning as \ref client.
///
/// \see connection_zk
class connection
{
public:
static std::shared_ptr<connection> connect(const connection_params&);
static std::shared_ptr<connection> connect(string_view conn_string);
virtual ~connection() noexcept;
virtual void close() = 0;
virtual future<get_result> get(string_view path) = 0;
virtual future<watch_result> watch(string_view path) = 0;
virtual future<get_children_result> get_children(string_view path) = 0;
virtual future<watch_children_result> watch_children(string_view path) = 0;
virtual future<exists_result> exists(string_view path) = 0;
virtual future<watch_exists_result> watch_exists(string_view path) = 0;
virtual future<create_result> create(string_view path,
const buffer& data,
const acl& rules,
create_mode mode
) = 0;
virtual future<set_result> set(string_view path, const buffer& data, version check) = 0;
virtual future<void> erase(string_view path, version check) = 0;
virtual future<get_acl_result> get_acl(string_view path) const = 0;
virtual future<void> set_acl(string_view path, const acl& rules, acl_version check) = 0;
virtual future<multi_result> commit(multi_op&& txn) = 0;
virtual future<void> load_fence() = 0;
virtual zk::state state() const = 0;
/// Watch for a state change.
virtual future<zk::state> watch_state();
protected:
/// Call this from derived classes when a session event happens. This triggers the delivery of all promises of state
/// changes (issued through \ref watch_state).
virtual void on_session_event(zk::state new_state);
private:
mutable std::mutex _state_change_promises_protect;
std::vector<promise<zk::state>> _state_change_promises;
};
/// Used to specify parameters for a \c connection. This can either be created manually or through a
/// \ref ConnectionStrings "connection string".
class connection_params final
{
public:
using host_list = std::vector<std::string>;
public:
static constexpr std::chrono::milliseconds default_timeout = std::chrono::seconds(10);
public:
/// Create an instance with default values.
connection_params() noexcept;
~connection_params() noexcept;
/// Create an instance from a connection string.
///
/// \anchor ConnectionStrings
/// \par Connection Strings
/// Connection strings follow the standard format for URLs (`schema://host_address/path?querystring`). The `schema`
/// is the type of connection (usually `"zk"`), `auth` is potential authentication, the `host_address` is the list
/// of servers, the `path` is the chroot to use for this connection and the `querystring` allows for configuring
/// advanced options. For example: `"zk://server-a:2181,server-b:2181,server-c:2181/?timeout=5"`.
///
/// \par
/// - \e schema: \ref connection_params::connection_schema
/// - \e host_address: comma-separated list of \ref connection_params::hosts
/// - \e path: \ref connection_params::chroot
/// - \e querystring: Allows for an arbitrary amount of optional parameters to be specified. These are specified
/// with the conventional HTTP-style (e.g. `"zk://localhost/?timeout=10&read_only=true"` specifies a timeout of 10
/// seconds and sets a read-only client. Boolean values can be specified with \c true, \c t, or \c 1 for \c true
/// or \c false, \c f, or \c 0 for \c false. It is important to note that, unlike regular HTTP URLs, query
/// parameters which are not understood will result in an error.
/// - `randomize_hosts`: \ref connection_params::randomize_hosts
/// - `read_only`: \ref connection_params::read_only
/// - `timeout`: \ref connection_params::timeout
///
/// \throws std::invalid_argument if the string is malformed in some way.
static connection_params parse(string_view conn_string);
/// \{
/// Determines the underlying \ref zk::connection implementation to use. The valid values are \c "zk" and
/// \c "fakezk".
///
/// - `zk`: The standard-issue ZooKeeper connection to a real ZooKeeper cluster. This schema uses
/// \ref zk::connection_zk as the underlying connection.
/// - `fakezk`: Create a client connected to a fake ZooKeeper server (\ref zk::fake::server). Here, the
/// `host_address` refers to the name of the in-memory DB created when the server instance was. This schema uses
/// \ref zk::fake::connection_fake as the underlying connection.
const std::string& connection_schema() const { return _connection_schema; }
std::string& connection_schema() { return _connection_schema; }
/// \}
/// \{
/// Addresses for the ensemble to connect to. This can be IP addresses (IPv4 or IPv6) or hostnames, but IP
/// addresses are the recommended method of specification. If the port is left unspecified, \c 2181 is assumed (as
/// it is the de facto standard for ZooKeeper server). For IPv6 addresses, use the boxed format (e.g. `[::1]:2181`);
/// this is required even when the port is \c 2181 to disambiguate between a host named in hexadecimal and an IPv6
/// address (e.g. `"[fd2d:8413:d6c6::73b]"` or `"[::1]:2181"`).
const host_list& hosts() const { return _hosts; }
host_list& hosts() { return _hosts; }
/// \}
/// \{
/// Specifying a value for \c chroot as something aside from \c "" or \c "/" will run the client commands while
/// interpreting all paths relative to the specified path. For example, specifying `"/app/a"` will make requests for
/// `"/foo/bar"` sent to `"/app/a/foo/bar"` (from the server's perspective). If unspecified, the path will be
/// treated as `"/"`.
const std::string& chroot() const { return _chroot; }
std::string& chroot() { return _chroot; }
/// \}
/// \{
/// Connect to a host at random (as opposed to attempting connections in order)? The default is to randomize (the
/// use cases for sequential connections are usually limited to testing purposes).
bool randomize_hosts() const { return _randomize_hosts; }
bool& randomize_hosts() { return _randomize_hosts; }
/// \}
/// \{
/// Allow connections to read-only servers? The default (\c false) is to disallow. **/
bool read_only() const { return _read_only; }
bool& read_only() { return _read_only; }
/// \}
/// \{
/// The session timeout between this client and the server. The server will attempt to respect this value, but will
/// automatically use a lower timeout value if this value is too large (see the ZooKeeper Programmer's Guide for
/// more information on maximum values). The default is 10 seconds.
///
/// When specified in a query string, this value is specified either with floating-point seconds or as an ISO 8601
/// duration (`PT8.93S` for \c 8.930 seconds).
std::chrono::milliseconds timeout() const { return _timeout; }
std::chrono::milliseconds& timeout() { return _timeout; }
/// \}
private:
std::string _connection_schema;
host_list _hosts;
std::string _chroot;
bool _randomize_hosts;
bool _read_only;
std::chrono::milliseconds _timeout;
};
bool operator==(const connection_params& lhs, const connection_params& rhs);
bool operator!=(const connection_params& lhs, const connection_params& rhs);
std::string to_string(const connection_params&);
std::ostream& operator<<(std::ostream&, const connection_params&);
/// \}
}
================================================
FILE: src/zk/connection_tests.cpp
================================================
#include <zk/tests/test.hpp>
#include "connection.hpp"
namespace zk
{
// This test is mostly to check that we still use the defaults.
GTEST_TEST(connection_params_tests, defaults)
{
const auto res = connection_params::parse("zk://localhost/");
CHECK_EQ("zk", res.connection_schema());
CHECK_EQ(1U, res.hosts().size());
CHECK_EQ("localhost", res.hosts()[0]);
CHECK_EQ("/", res.chroot());
CHECK_TRUE(res.randomize_hosts());
CHECK_FALSE(res.read_only());
CHECK_TRUE(connection_params::default_timeout == res.timeout());
connection_params manual;
manual.hosts() = { "localhost" };
CHECK_EQ(manual, res);
}
GTEST_TEST(connection_params_tests, multi_hosts)
{
const auto res = connection_params::parse("zk://server-a,server-b,server-c/");
connection_params manual;
manual.hosts() = { "server-a", "server-b", "server-c" };
CHECK_EQ(manual, res);
}
GTEST_TEST(connection_params_tests, chroot)
{
const auto res = connection_params::parse("zk://localhost/some/sub/path");
connection_params manual;
manual.hosts() = { "localhost" };
manual.chroot() = "/some/sub/path";
CHECK_EQ(manual, res);
}
GTEST_TEST(connection_params_tests, randomize_hosts)
{
const auto res = connection_params::parse("zk://localhost/?randomize_hosts=false");
connection_params manual;
manual.hosts() = { "localhost" };
manual.randomize_hosts() = false;
CHECK_EQ(manual, res);
}
GTEST_TEST(connection_params_tests, read_only)
{
const auto res = connection_params::parse("zk://localhost/?read_only=true");
connection_params manual;
manual.hosts() = { "localhost" };
manual.read_only() = true;
CHECK_EQ(manual, res);
}
GTEST_TEST(connection_params_tests, randomize_and_read_only)
{
const auto res = connection_params::parse("zk://localhost/?randomize_hosts=false&read_only=1");
connection_params manual;
manual.hosts() = { "localhost" };
manual.randomize_hosts() = false;
manual.read_only() = true;
CHECK_EQ(manual, res);
}
GTEST_TEST(connection_params_tests, timeout)
{
const auto res = connection_params::parse("zk://localhost/?timeout=0.5");
connection_params manual;
manual.hosts() = { "localhost" };
manual.timeout() = std::chrono::milliseconds(500);
CHECK_EQ(manual, res);
}
}
================================================
FILE: src/zk/connection_zk.cpp
================================================
#include "connection_zk.hpp"
#include "exceptions.hpp"
#include <algorithm>
#include <cassert>
#include <cerrno>
#include <cstring>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <system_error>
#include <tuple>
#include <zookeeper/zookeeper.h>
#include "acl.hpp"
#include "error.hpp"
#include "multi.hpp"
#include "results.hpp"
#include "types.hpp"
namespace zk
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Utility Functions //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename FAction>
auto with_str(string_view src, FAction&& action) noexcept(noexcept(std::forward<FAction>(action)(ptr<const char>())))
-> decltype(std::forward<FAction>(action)(ptr<const char>()))
{
char buffer[src.size() + 1];
buffer[src.size()] = '\0';
std::memcpy(buffer, src.data(), src.size());
return std::forward<FAction>(action)(buffer);
}
static ACL encode_acl_part(const acl_rule& src)
{
ACL out;
out.perms = static_cast<int>(src.permissions());
out.id.scheme = const_cast<ptr<char>>(src.scheme().c_str());
out.id.id = const_cast<ptr<char>>(src.id().c_str());
return out;
}
template <typename FAction>
auto with_acl(const acl& rules, FAction&& action) noexcept(noexcept(std::forward<FAction>(action)(ptr<ACL_vector>())))
-> decltype(std::forward<FAction>(action)(ptr<ACL_vector>()))
{
ACL parts[rules.size()];
for (std::size_t idx = 0; idx < rules.size(); ++idx)
parts[idx] = encode_acl_part(rules[idx]);
ACL_vector vec;
vec.count = int(rules.size());
vec.data = parts;
return std::forward<FAction>(action)(&vec);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Native Adaptors //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static error_code error_code_from_raw(int raw)
{
switch (raw)
{
case ZOPERATIONTIMEOUT:
raw = ZCONNECTIONLOSS;
break;
case ZINVALIDCALLBACK:
case ZINVALIDACL:
raw = ZBADARGUMENTS;
break;
case ZSESSIONMOVED:
raw = ZCONNECTIONLOSS;
break;
default:
break;
}
return static_cast<error_code>(raw);
}
static event_type event_from_raw(int raw)
{
return static_cast<event_type>(raw);
}
// ZooKeeper does not have this concept pre-3.5
#if ZOO_MAJOR_VERSION <= 3 && ZOO_MINOR_VERSION <= 4
static const int ZOO_NOTCONNECTED_STATE = 999;
#endif
static state state_from_raw(int raw)
{
// The C client will put us into `ZOO_NOTCONNECTED_STATE` for two reasons:
//
// 1. This is the state of the initial connection (zookeeper_init_internal), which is then replaced when the adaptor
// threads first call the interest function.
// 2. During a reconfiguration, the client disconnects and transitions to this state (update_addrs), which is then
// updated the next time the I/O thread touches interest.
//
// In both cases, the state is still "connecting" from the point of view of a client.
if (raw == ZOO_NOTCONNECTED_STATE)
{
raw = ZOO_CONNECTING_STATE;
}
// `ZOO_ASSOCIATING_STATE` means we have connected to a server, but have not yet authenticated and created the
// session. We still can't perform any operations, so treat it as connecting -- the client does not care about the
// difference between establishing a TCP connection and negotiating credentials.
else if (raw == ZOO_ASSOCIATING_STATE)
{
raw = ZOO_CONNECTING_STATE;
}
return static_cast<state>(raw);
}
static stat stat_from_raw(const struct Stat& raw)
{
stat out;
out.acl_version = acl_version(raw.aversion);
out.child_modified_transaction = transaction_id(raw.pzxid);
out.child_version = child_version(raw.cversion);
out.children_count = raw.numChildren;
out.create_time = stat::time_point() + std::chrono::milliseconds(raw.ctime);
out.create_transaction = transaction_id(raw.czxid);
out.data_size = raw.dataLength;
out.data_version = version(raw.version);
out.ephemeral_owner = raw.ephemeralOwner;
out.modified_time = stat::time_point() + std::chrono::milliseconds(raw.mtime);
out.modified_transaction = transaction_id(raw.mzxid);
return out;
}
static std::vector<std::string> string_vector_from_raw(const struct String_vector& raw)
{
std::vector<std::string> out;
out.reserve(raw.count);
for (std::int32_t idx = 0; idx < raw.count; ++idx)
out.emplace_back(raw.data[idx]);
return out;
}
static acl acl_from_raw(const struct ACL_vector& raw)
{
auto sz = std::size_t(raw.count);
acl out;
out.reserve(sz);
for (std::size_t idx = 0; idx < sz; ++idx)
{
const auto& item = raw.data[idx];
out.emplace_back(item.id.scheme, item.id.id, static_cast<permission>(item.perms));
}
return out;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// connection_zk //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
connection_zk::connection_zk(const connection_params& params) :
_handle(nullptr)
{
if (params.connection_schema() != "zk")
zk::throw_exception(std::invalid_argument(std::string("Invalid connection string \"") + to_string(params) + "\""));
auto conn_string = [&] ()
{
std::ostringstream os;
bool first = true;
for (const auto& host : params.hosts())
{
if (first)
first = false;
else
os << ',';
os << host;
}
return os.str();
}();
_handle = ::zookeeper_init(conn_string.c_str(),
on_session_event_raw,
static_cast<int>(params.timeout().count()),
nullptr,
this,
0
);
if (!_handle)
std::system_error(errno, std::system_category(), "Failed to create ZooKeeper client");
}
connection_zk::~connection_zk() noexcept
{
close();
}
class connection_zk::watcher
{
public:
watcher() :
_event_delivered(false)
{ }
virtual ~watcher() noexcept {}
virtual void deliver_event(event ev)
{
if (!_event_delivered.exchange(true, std::memory_order_relaxed))
{
_event_promise.set_value(std::move(ev));
}
}
future<event> get_event_future()
{
return _event_promise.get_future();
}
protected:
std::atomic<bool> _event_delivered;
promise<event> _event_promise;
};
template <typename TResult>
class connection_zk::basic_watcher :
public connection_zk::watcher
{
public:
basic_watcher() :
_data_delivered(false)
{ }
future<TResult> get_data_future()
{
return _data_promise.get_future();
}
virtual void deliver_event(event ev) override
{
if (!_data_delivered.load(std::memory_order_relaxed))
{
deliver_data(nullopt, get_exception_ptr_of(error_code::closed));
}
watcher::deliver_event(std::move(ev));
}
void deliver_data(optional<TResult> data, zk::exception_ptr ex_ptr)
{
if (!_data_delivered.exchange(true, std::memory_order_relaxed))
{
if (ex_ptr)
{
_data_promise.set_exception(std::move(ex_ptr));
}
else
{
_data_promise.set_value(std::move(*data));
}
}
}
private:
std::atomic<bool> _data_delivered;
promise<TResult> _data_promise;
};
std::shared_ptr<connection_zk::watcher> connection_zk::try_extract_watch(ptr<const void> addr)
{
std::unique_lock<std::mutex> ax(_watches_protect);
auto iter = _watches.find(addr);
if (iter != _watches.end())
return _watches.extract(iter).mapped();
else
return nullptr;
}
static ptr<connection_zk> connection_from_context(ptr<zhandle_t> zh)
{
return (ptr<connection_zk>) zoo_get_context(zh);
}
void connection_zk::deliver_watch(ptr<zhandle_t> zh,
int type_in,
int state_in,
ptr<const char> path [[gnu::unused]],
ptr<void> proms_in
)
{
auto& self = *connection_from_context(zh);
if (auto watcher = self.try_extract_watch(proms_in))
watcher->deliver_event(event(event_from_raw(type_in), state_from_raw(state_in)));
}
void connection_zk::close()
{
if (_handle)
{
auto err = error_code_from_raw(::zookeeper_close(_handle));
if (err != error_code::ok)
throw_error(err);
_handle = nullptr;
// Deliver a session event as if there was a close.
std::unique_lock<std::mutex> ax(_watches_protect);
auto l_watches = std::move(_watches);
ax.unlock();
for (const auto& pair : l_watches)
pair.second->deliver_event(event(event_type::session, zk::state::closed));
}
}
zk::state connection_zk::state() const
{
if (_handle)
return state_from_raw(::zoo_state(_handle));
else
return zk::state::closed;
}
future<get_result> connection_zk::get(string_view path)
{
::data_completion_t callback =
[] (int rc_in, ptr<const char> data, int data_sz, ptr<const struct Stat> pstat, ptr<const void> prom_in) noexcept
{
std::unique_ptr<promise<get_result>> prom((ptr<promise<get_result>>) prom_in);
auto rc = error_code_from_raw(rc_in);
if (rc == error_code::ok)
prom->set_value(get_result(buffer(data, data + data_sz), stat_from_raw(*pstat)));
else
prom->set_exception(get_exception_ptr_of(rc));
};
return with_str(path, [&] (ptr<const char> path) noexcept
{
auto ppromise = std::make_unique<promise<get_result>>();
auto fut = ppromise->get_future();
auto rc = error_code_from_raw(::zoo_aget(_handle, path, 0, callback, ppromise.get()));
if (rc == error_code::ok)
{
ppromise.release();
return fut;
}
else
{
ppromise->set_exception(get_exception_ptr_of(rc));
return fut;
}
});
}
class connection_zk::data_watcher :
public connection_zk::basic_watcher<watch_result>
{
public:
static void deliver_raw(int rc_in,
ptr<const char> data,
int data_sz,
ptr<const struct Stat> pstat,
ptr<const void> self_in
) noexcept
{
auto& self = *static_cast<ptr<data_watcher>>(const_cast<ptr<void>>(self_in));
auto rc = error_code_from_raw(rc_in);
if (rc == error_code::ok)
{
self.deliver_data(watch_result(get_result(buffer(data, data + data_sz), stat_from_raw(*pstat)),
self.get_event_future()
),
zk::exception_ptr()
);
}
else
{
self.deliver_data(nullopt, get_exception_ptr_of(rc));
}
}
};
future<watch_result> connection_zk::watch(string_view path)
{
return with_str(path, [&] (ptr<const char> path) noexcept
{
std::unique_lock<std::mutex> ax(_watches_protect);
auto watcher = std::make_shared<data_watcher>();
auto rc = error_code_from_raw(::zoo_awget(_handle,
path,
deliver_watch,
watcher.get(),
data_watcher::deliver_raw,
watcher.get()
)
);
if (rc == error_code::ok)
_watches.emplace(watcher.get(), watcher);
else
watcher->deliver_data(nullopt, get_exception_ptr_of(rc));
return watcher->get_data_future();
});
}
future<get_children_result> connection_zk::get_children(string_view path)
{
::strings_stat_completion_t callback =
[] (int rc_in,
ptr<const struct String_vector> strings_in,
ptr<const struct Stat> stat_in,
ptr<const void> prom_in
)
{
std::unique_ptr<promise<get_children_result>> prom((ptr<promise<get_children_result>>) prom_in);
auto rc = error_code_from_raw(rc_in);
try
{
if (rc != error_code::ok)
throw_error(rc);
prom->set_value(get_children_result(string_vector_from_raw(*strings_in), stat_from_raw(*stat_in)));
}
catch (...)
{
prom->set_exception(zk::current_exception());
}
};
return with_str(path, [&] (ptr<const char> path) noexcept
{
auto ppromise = std::make_unique<promise<get_children_result>>();
auto fut = ppromise->get_future();
auto rc = error_code_from_raw(::zoo_aget_children2(_handle,
path,
0,
callback,
ppromise.get()
)
);
if (rc == error_code::ok)
{
ppromise.release();
return fut;
}
else
{
ppromise->set_exception(get_exception_ptr_of(rc));
return fut;
}
});
}
class connection_zk::child_watcher :
public connection_zk::basic_watcher<watch_children_result>
{
public:
static void deliver_raw(int rc_in,
ptr<const struct String_vector> strings_in,
ptr<const struct Stat> stat_in,
ptr<const void> prom_in
) noexcept
{
auto& self = *static_cast<ptr<child_watcher>>(const_cast<ptr<void>>(prom_in));
auto rc = error_code_from_raw(rc_in);
try
{
if (rc != error_code::ok)
throw_error(rc);
self.deliver_data(watch_children_result(get_children_result(string_vector_from_raw(*strings_in),
stat_from_raw(*stat_in)
),
self.get_event_future()
),
zk::exception_ptr()
);
}
catch (...)
{
self.deliver_data(nullopt, zk::current_exception());
}
}
};
future<watch_children_result> connection_zk::watch_children(string_view path)
{
return with_str(path, [&] (ptr<const char> path) noexcept
{
std::unique_lock<std::mutex> ax(_watches_protect);
auto watcher = std::make_shared<child_watcher>();
auto rc = error_code_from_raw(::zoo_awget_children2(_handle,
path,
deliver_watch,
watcher.get(),
child_watcher::deliver_raw,
watcher.get()
)
);
if (rc == error_code::ok)
_watches.emplace(watcher.get(), watcher);
else
watcher->deliver_data(nullopt, get_exception_ptr_of(rc));
return watcher->get_data_future();
});
}
future<exists_result> connection_zk::exists(string_view path)
{
::stat_completion_t callback =
[] (int rc_in, ptr<const struct Stat> stat_in, ptr<const void> prom_in)
{
std::unique_ptr<promise<exists_result>> prom((ptr<promise<exists_result>>) prom_in);
auto rc = error_code_from_raw(rc_in);
if (rc == error_code::ok)
prom->set_value(exists_result(stat_from_raw(*stat_in)));
else if (rc == error_code::no_entry)
prom->set_value(exists_result(nullopt));
else
prom->set_exception(get_exception_ptr_of(rc));
};
return with_str(path, [&] (ptr<const char> path) noexcept
{
auto ppromise = std::make_unique<promise<exists_result>>();
auto fut = ppromise->get_future();
auto rc = error_code_from_raw(::zoo_aexists(_handle, path, 0, callback, ppromise.get()));
if (rc == error_code::ok)
{
ppromise.release();
return fut;
}
else
{
ppromise->set_exception(get_exception_ptr_of(rc));
return fut;
}
});
}
class connection_zk::exists_watcher :
public connection_zk::basic_watcher<watch_exists_result>
{
public:
static void deliver_raw(int rc_in, ptr<const struct Stat> stat_in, ptr<const void> self_in)
{
auto& self = *static_cast<ptr<exists_watcher>>(const_cast<ptr<void>>(self_in));
auto rc = error_code_from_raw(rc_in);
if (rc == error_code::ok)
{
self.deliver_data(watch_exists_result(exists_result(stat_from_raw(*stat_in)), self.get_event_future()),
zk::exception_ptr()
);
}
else if (rc == error_code::no_entry)
{
self.deliver_data(watch_exists_result(exists_result(nullopt), self.get_event_future()),
zk::exception_ptr()
);
}
else
{
self.deliver_data(nullopt, get_exception_ptr_of(rc));
}
}
};
future<watch_exists_result> connection_zk::watch_exists(string_view path)
{
return with_str(path, [&] (ptr<const char> path) noexcept
{
std::unique_lock<std::mutex> ax(_watches_protect);
auto watcher = std::make_shared<exists_watcher>();
auto rc = error_code_from_raw(::zoo_awexists(_handle,
path,
deliver_watch,
watcher.get(),
exists_watcher::deliver_raw,
watcher.get()
)
);
if (rc == error_code::ok)
_watches.emplace(watcher.get(), watcher);
else
watcher->deliver_data(nullopt, get_exception_ptr_of(rc));
return watcher->get_data_future();
});
}
future<create_result> connection_zk::create(string_view path,
const buffer& data,
const acl& rules,
create_mode mode
)
{
::string_completion_t callback =
[] (int rc_in, ptr<const char> name_in, ptr<const void> prom_in)
{
std::unique_ptr<promise<create_result>> prom((ptr<promise<create_result>>) prom_in);
auto rc = error_code_from_raw(rc_in);
if (rc == error_code::ok)
prom->set_value(create_result(std::string(name_in)));
else
prom->set_exception(get_exception_ptr_of(rc));
};
return with_str(path, [&] (ptr<const char> path) noexcept
{
auto ppromise = std::make_unique<promise<create_result>>();
auto fut = ppromise->get_future();
auto rc = with_acl(rules, [&] (ptr<const ACL_vector> rules) noexcept
{
return error_code_from_raw(::zoo_acreate(_handle,
path,
data.data(),
int(data.size()),
rules,
static_cast<int>(mode),
callback,
ppromise.get()
)
);
});
if (rc == error_code::ok)
{
ppromise.release();
return fut;
}
else
{
ppromise->set_exception(get_exception_ptr_of(rc));
return fut;
}
});
}
future<set_result> connection_zk::set(string_view path, const buffer& data, version check)
{
::stat_completion_t callback =
[] (int rc_in, ptr<const struct Stat> stat_raw, ptr<const void> prom_in)
{
std::unique_ptr<promise<set_result>> prom((ptr<promise<set_result>>) prom_in);
auto rc = error_code_from_raw(rc_in);
if (rc == error_code::ok)
prom->set_value(set_result(stat_from_raw(*stat_raw)));
else
prom->set_exception(get_exception_ptr_of(rc));
};
return with_str(path, [&] (ptr<const char> path) noexcept
{
auto ppromise = std::make_unique<promise<set_result>>();
auto fut = ppromise->get_future();
auto rc = error_code_from_raw(::zoo_aset(_handle,
path,
data.data(),
int(data.size()),
check.value,
callback,
ppromise.get()
));
if (rc == error_code::ok)
{
ppromise.release();
return fut;
}
else
{
ppromise->set_exception(get_exception_ptr_of(rc));
return fut;
}
});
}
future<void> connection_zk::erase(string_view path, version check)
{
::void_completion_t callback =
[] (int rc_in, ptr<const void> prom_in)
{
std::unique_ptr<promise<void>> prom((ptr<promise<void>>) prom_in);
auto rc = error_code_from_raw(rc_in);
if (rc == error_code::ok)
prom->set_value();
else
prom->set_exception(get_exception_ptr_of(rc));
};
return with_str(path, [&] (ptr<const char> path) noexcept
{
auto ppromise = std::make_unique<promise<void>>();
auto fut = ppromise->get_future();
auto rc = error_code_from_raw(::zoo_adelete(_handle, path, check.value, callback, ppromise.get()));
if (rc == error_code::ok)
{
ppromise.release();
return fut;
}
else
{
ppromise->set_exception(get_exception_ptr_of(rc));
return fut;
}
});
}
future<get_acl_result> connection_zk::get_acl(string_view path) const
{
::acl_completion_t callback =
[] (int rc_in, ptr<struct ACL_vector> acl_raw, ptr<struct Stat> stat_raw, ptr<const void> prom_in) noexcept
{
std::unique_ptr<promise<get_acl_result>> prom((ptr<promise<get_acl_result>>) prom_in);
auto rc = error_code_from_raw(rc_in);
if (rc == error_code::ok)
prom->set_value(get_acl_result(acl_from_raw(*acl_raw), stat_from_raw(*stat_raw)));
else
prom->set_exception(get_exception_ptr_of(rc));
};
return with_str(path, [&] (ptr<const char> path) noexcept
{
auto ppromise = std::make_unique<promise<get_acl_result>>();
auto fut = ppromise->get_future();
auto rc = error_code_from_raw(::zoo_aget_acl(_handle, path, callback, ppromise.get()));
if (rc == error_code::ok)
{
ppromise.release();
return fut;
}
else
{
ppromise->set_exception(get_exception_ptr_of(rc));
return fut;
}
});
}
future<void> connection_zk::set_acl(string_view path, const acl& rules, acl_version check)
{
::void_completion_t callback =
[] (int rc_in, ptr<const void> prom_in)
{
std::unique_ptr<promise<void>> prom((ptr<promise<void>>) prom_in);
auto rc = error_code_from_raw(rc_in);
if (rc == error_code::ok)
prom->set_value();
else
prom->set_exception(get_exception_ptr_of(rc));
};
return with_str(path, [&] (ptr<const char> path) noexcept
{
return with_acl(rules, [&] (ptr<struct ACL_vector> rules) noexcept
{
auto ppromise = std::make_unique<promise<void>>();
auto fut = ppromise->get_future();
auto rc = error_code_from_raw(::zoo_aset_acl(_handle,
path,
check.value,
rules,
callback,
ppromise.get()
)
);
if (rc == error_code::ok)
{
ppromise.release();
return fut;
}
else
{
ppromise->set_exception(get_exception_ptr_of(rc));
return fut;
}
});
});
}
struct connection_zk_commit_completer
{
multi_op source_txn;
promise<multi_result> prom;
std::vector<zoo_op_result_t> raw_results;
std::map<std::size_t, Stat> raw_stats;
std::map<std::size_t, std::vector<char>> path_buffers;
explicit connection_zk_commit_completer(multi_op&& src) :
source_txn(std::move(src)),
raw_results(source_txn.size())
{
for (zoo_op_result_t& x : raw_results)
x.err = -42;
}
ptr<Stat> raw_stat_at(std::size_t idx)
{
return &raw_stats[idx];
}
ptr<std::vector<char>> path_buffer_for(std::size_t idx, const std::string& path, create_mode mode)
{
// If the creation is sequential, append 12 extra characters to store the digits
auto sz = path.size() + (is_set(mode, create_mode::sequential) ? 12 : 1);
path_buffers[idx] = std::vector<char>(sz);
return &path_buffers[idx];
}
void deliver(error_code rc)
{
try
{
if (rc == error_code::ok)
{
multi_result out;
out.reserve(raw_results.size());
for (std::size_t idx = 0; idx < source_txn.size(); ++idx)
{
const auto& raw_res = raw_results[idx];
switch (source_txn[idx].type())
{
case op_type::create:
out.emplace_back(create_result(std::string(raw_res.value)));
break;
case op_type::set:
out.emplace_back(set_result(stat_from_raw(*raw_res.stat)));
break;
default:
out.emplace_back(source_txn[idx].type(), nullptr);
break;
}
}
prom.set_value(std::move(out));
}
else
{
// All results until the failure are 0, equal to rc where we care, and runtime_inconsistency after that.
auto iter = std::partition_point(raw_results.begin(), raw_results.end(),
[] (auto res) { return res.err == 0; }
);
zk::throw_exception(transaction_failed(rc, std::size_t(std::distance(raw_results.begin(), iter))));
}
}
catch (...)
{
prom.set_exception(zk::current_exception());
}
}
};
future<multi_result> connection_zk::commit(multi_op&& txn_in)
{
::void_completion_t callback =
[] (int rc_in, ptr<const void> completer_in)
{
std::unique_ptr<connection_zk_commit_completer>
completer((ptr<connection_zk_commit_completer>) completer_in);
completer->deliver(error_code_from_raw(rc_in));
};
auto pcompleter = std::make_unique<connection_zk_commit_completer>(std::move(txn_in));
auto& txn = pcompleter->source_txn;
try
{
::zoo_op raw_ops[txn.size()];
std::size_t create_op_count = 0;
std::size_t acl_piece_count = 0;
for (const auto& tx : txn)
{
if (tx.type() == op_type::create)
{
++create_op_count;
acl_piece_count += tx.as_create().rules.size();
}
}
ACL_vector encoded_acls[create_op_count];
ACL acl_pieces[acl_piece_count];
ptr<ACL_vector> encoded_acl_iter = encoded_acls;
ptr<ACL> acl_piece_iter = acl_pieces;
for (std::size_t idx = 0; idx < txn.size(); ++idx)
{
auto& raw_op = raw_ops[idx];
auto& src_op = txn[idx];
switch (src_op.type())
{
case op_type::check:
zoo_check_op_init(&raw_op, src_op.as_check().path.c_str(), src_op.as_check().check.value);
break;
case op_type::create:
{
const auto& cdata = src_op.as_create();
encoded_acl_iter->count = int(cdata.rules.size());
encoded_acl_iter->data = acl_piece_iter;
for (const auto& acl : cdata.rules)
{
*acl_piece_iter = encode_acl_part(acl);
++acl_piece_iter;
}
auto path_buf_ref = pcompleter->path_buffer_for(idx, cdata.path, cdata.mode);
zoo_create_op_init(&raw_op,
cdata.path.c_str(),
cdata.data.data(),
int(cdata.data.size()),
encoded_acl_iter,
static_cast<int>(cdata.mode),
path_buf_ref->data(),
int(path_buf_ref->size())
);
++encoded_acl_iter;
break;
}
case op_type::erase:
zoo_delete_op_init(&raw_op, src_op.as_erase().path.c_str(), src_op.as_erase().check.value);
break;
case op_type::set:
{
const auto& setting = src_op.as_set();
zoo_set_op_init(&raw_op,
setting.path.c_str(),
setting.data.data(),
int(setting.data.size()),
setting.check.value,
pcompleter->raw_stat_at(idx)
);
break;
}
default:
{
using std::to_string;
zk::throw_exception(std::invalid_argument("Invalid op_type at index=" + to_string(idx) + ": "
+ to_string(src_op.type())
));
}
}
}
auto fut = pcompleter->prom.get_future();
auto rc = error_code_from_raw(::zoo_amulti(_handle,
int(txn.size()),
raw_ops,
pcompleter->raw_results.data(),
callback,
pcompleter.get()
)
);
if (rc == error_code::ok)
{
pcompleter.release();
return fut;
}
else
{
pcompleter->prom.set_exception(get_exception_ptr_of(rc));
return pcompleter->prom.get_future();
}
}
catch (...)
{
pcompleter->prom.set_exception(zk::current_exception());
return pcompleter->prom.get_future();
}
}
future<void> connection_zk::load_fence()
{
::string_completion_t callback =
[] (int rc_in, ptr<const char>, ptr<const void> prom_in)
{
std::unique_ptr<promise<void>> prom((ptr<promise<void>>) prom_in);
auto rc = error_code_from_raw(rc_in);
if (rc == error_code::ok)
prom->set_value();
else
prom->set_exception(get_exception_ptr_of(rc));
};
auto ppromise = std::make_unique<zk::promise<void>>();
auto rc = error_code_from_raw(::zoo_async(_handle, "/", callback, ppromise.get()));
if (rc == error_code::ok)
{
auto f = ppromise->get_future();
ppromise.release();
return f;
}
else
{
ppromise->set_exception(get_exception_ptr_of(rc));
return ppromise->get_future();
}
}
void connection_zk::on_session_event_raw(ptr<zhandle_t> handle [[gnu::unused]],
int ev_type,
int state,
ptr<const char> path_ptr,
ptr<void> watcher_ctx
) noexcept
{
auto self = static_cast<ptr<connection_zk>>(watcher_ctx);
// Most of the time, self's _handle will be the same thing that ZK provides to us. However, if we connect very
// quickly, a session event will happen trigger *before* we set the _handle. This isn't a problem, just something to
// be aware of.
assert(self->_handle == nullptr || self->_handle == handle);
auto ev = event_from_raw(ev_type);
auto st = state_from_raw(state);
auto path = string_view(path_ptr);
if (ev != event_type::session)
{
// TODO: Remove this usage of std::cerr
std::cerr << "WARNING: Got unexpected event " << ev << " in state=" << st << " with path=" << path << std::endl;
return;
}
self->on_session_event(st);
}
}
================================================
FILE: src/zk/connection_zk.hpp
================================================
#pragma once
#include <zk/config.hpp>
#include <chrono>
#include <memory>
#include <mutex>
#include <unordered_map>
#include "connection.hpp"
#include "string_view.hpp"
typedef struct _zhandle zhandle_t;
namespace zk
{
/// \addtogroup Client
/// \{
class connection_zk final :
public connection
{
public:
explicit connection_zk(const connection_params& params);
virtual ~connection_zk() noexcept;
virtual void close() override;
virtual zk::state state() const override;
virtual future<get_result> get(string_view path) override;
virtual future<watch_result> watch(string_view path) override;
virtual future<get_children_result> get_children(string_view path) override;
virtual future<watch_children_result> watch_children(string_view path) override;
virtual future<exists_result> exists(string_view path) override;
virtual future<watch_exists_result> watch_exists(string_view path) override;
virtual future<create_result> create(string_view path,
const buffer& data,
const acl& rules,
create_mode mode
) override;
virtual future<set_result> set(string_view path, const buffer& data, version check) override;
virtual future<void> erase(string_view path, version check) override;
virtual future<get_acl_result> get_acl(string_view path) const override;
virtual future<void> set_acl(string_view path, const acl& rules, acl_version check) override;
virtual future<multi_result> commit(multi_op&& txn) override;
gitextract_87o4bcld/
├── .github/
│ └── ISSUE_TEMPLATE.md
├── .gitignore
├── .travis.yml
├── AUTHORS
├── CMakeLists.txt
├── CONTRIBUTING.rst
├── COPYING
├── README.md
├── cmake/
│ └── modules/
│ ├── BuildFunctions.cmake
│ ├── CodeCoverage.cmake
│ ├── ConfigurationSetting.cmake
│ ├── ListSplit.cmake
│ └── ZooKeeper.cmake
├── config/
│ ├── dev-env
│ ├── docker/
│ │ ├── debian-10/
│ │ │ └── Dockerfile
│ │ ├── opensuse-15/
│ │ │ └── Dockerfile
│ │ ├── ubuntu-16.04/
│ │ │ └── Dockerfile
│ │ ├── ubuntu-18.04/
│ │ │ └── Dockerfile
│ │ └── ubuntu-20.04/
│ │ └── Dockerfile
│ ├── make-doxygen
│ ├── make-package
│ ├── make-packages
│ ├── publish-doxygen
│ ├── run-tests
│ ├── travisci_rsa.enc
│ └── upload-coverage
├── doc/
│ └── Doxyfile
├── install/
│ └── deb/
│ ├── libzkpp/
│ │ ├── control.in
│ │ ├── postinst.in
│ │ └── shlibs.in
│ ├── libzkpp-dev/
│ │ └── control.in
│ ├── libzkpp-server/
│ │ ├── control.in
│ │ └── postinst.in
│ └── libzkpp-server-dev/
│ └── control.in
└── src/
└── zk/
├── acl.cpp
├── acl.hpp
├── acl_tests.cpp
├── buffer.hpp
├── client.cpp
├── client.hpp
├── client_tests.cpp
├── config.hpp
├── connection.cpp
├── connection.hpp
├── connection_tests.cpp
├── connection_zk.cpp
├── connection_zk.hpp
├── error.cpp
├── error.hpp
├── error_tests.cpp
├── exceptions.cpp
├── exceptions.hpp
├── forwards.hpp
├── future.hpp
├── multi.cpp
├── multi.hpp
├── multi_tests.cpp
├── optional.hpp
├── optional_tests.cpp
├── results.cpp
├── results.hpp
├── server/
│ ├── classpath.cpp
│ ├── classpath.hpp
│ ├── classpath_registration_template.cpp.in
│ ├── classpath_tests.cpp
│ ├── configuration.cpp
│ ├── configuration.hpp
│ ├── configuration_tests.cpp
│ ├── detail/
│ │ ├── close.cpp
│ │ ├── close.hpp
│ │ ├── event_handle.cpp
│ │ ├── event_handle.hpp
│ │ ├── pipe.cpp
│ │ ├── pipe.hpp
│ │ ├── pipe_tests.cpp
│ │ ├── subprocess.cpp
│ │ ├── subprocess.hpp
│ │ └── subprocess_tests.cpp
│ ├── package_registry.cpp
│ ├── package_registry.hpp
│ ├── package_registry_tests.cpp
│ ├── package_registry_tests.hpp
│ ├── server.cpp
│ ├── server.hpp
│ ├── server_group.cpp
│ ├── server_group.hpp
│ ├── server_group_tests.cpp
│ ├── server_tests.cpp
│ └── server_tests.hpp
├── string_view.hpp
├── tests/
│ ├── main.cpp
│ ├── test.cpp
│ └── test.hpp
├── types.cpp
├── types.hpp
└── types_tests.cpp
SYMBOL INDEX (581 symbols across 60 files)
FILE: src/zk/acl.cpp
type zk (line 8) | namespace zk
function to_string (line 33) | std::string to_string(const permission& self)
function hash (line 53) | std::size_t hash(const acl_rule& self)
function to_string (line 101) | std::string to_string(const acl_rule& self)
function to_string (line 144) | std::string to_string(const acl& self)
function acl (line 155) | const acl& acls::creator_all()
function acl (line 161) | const acl& acls::open_unsafe()
function acl (line 167) | const acl& acls::read_unsafe()
FILE: src/zk/acl.hpp
type zk (line 13) | namespace zk
type permission (line 21) | enum class permission : unsigned int
function permission (line 34) | inline constexpr permission operator|(permission a, permission b)
function permission (line 39) | inline constexpr permission& operator|=(permission& self, permission m...
function permission (line 47) | inline constexpr permission operator&(permission a, permission b)
function permission (line 52) | inline constexpr permission& operator&=(permission& self, permission m...
function permission (line 60) | inline constexpr permission operator~(permission a)
function allows (line 68) | inline constexpr bool allows(permission self, permission perform)
class acl_rule (line 82) | class acl_rule final
method acl_rule (line 88) | acl_rule(const acl_rule&) = default;
method acl_rule (line 89) | acl_rule& operator=(const acl_rule&) = default;
method acl_rule (line 91) | acl_rule(acl_rule&&) = default;
method acl_rule (line 92) | acl_rule& operator=(acl_rule&&) = default;
method permission (line 124) | const permission& permissions() const
class acl (line 159) | class acl final
method acl (line 168) | acl() = default;
method acl (line 174) | acl(std::initializer_list<acl_rule> rules) :
method acl (line 178) | acl(const acl&) = default;
method acl (line 179) | acl& operator=(const acl&) = default;
method acl (line 181) | acl(acl&&) = default;
method acl (line 182) | acl& operator=(acl&&) = default;
method size_type (line 187) | size_type size() const { return _impl.size(); }
method acl_rule (line 191) | const acl_rule& operator[](size_type idx) const { return _impl[idx]; }
method acl_rule (line 192) | acl_rule& operator[](size_type idx) { return _impl[idx]; }
method acl_rule (line 199) | const acl_rule& at(size_type idx) const { return _impl.at(idx); }
method acl_rule (line 200) | acl_rule& at(size_type idx) { return _impl.at(idx); }
method iterator (line 205) | iterator begin() { return _impl.begin(); }
method const_iterator (line 206) | const_iterator begin() const { return _impl.begin(); }
method const_iterator (line 207) | const_iterator cbegin() const { return _impl.begin(); }
method iterator (line 212) | iterator end() { return _impl.end(); }
method const_iterator (line 213) | const_iterator end() const { return _impl.end(); }
method const_iterator (line 214) | const_iterator cend() const { return _impl.end(); }
method reserve (line 218) | void reserve(size_type capacity) { _impl.reserve(capacity); }
method emplace_back (line 224) | void emplace_back(TArgs&&... args)
method push_back (line 231) | void push_back(acl_rule&& x) { emplace_back(std::move(x)); }
method push_back (line 232) | void push_back(const acl_rule& x) { emplace_back(x); }
class acls (line 247) | class acls
type std (line 265) | namespace std
class hash<zk::acl_rule> (line 269) | class hash<zk::acl_rule>
method result_type (line 275) | result_type operator()(const argument_type& x) const
FILE: src/zk/acl_tests.cpp
type zk (line 5) | namespace zk
function GTEST_TEST (line 12) | GTEST_TEST(permission_tests, all)
function GTEST_TEST (line 23) | GTEST_TEST(permission_tests, stringification)
function GTEST_TEST (line 36) | GTEST_TEST(acl_rule_tests, stringification)
function GTEST_TEST (line 43) | GTEST_TEST(acl_rule_tests, comparisons)
function GTEST_TEST (line 56) | GTEST_TEST(acl_rule_tests, hashing)
function GTEST_TEST (line 68) | GTEST_TEST(acl_tests, stringification)
FILE: src/zk/buffer.hpp
type zk (line 62) | namespace zk
FILE: src/zk/client.cpp
type zk (line 10) | namespace zk
FILE: src/zk/client.hpp
type zk (line 16) | namespace zk
class client (line 25) | class client final
method client (line 49) | client(const client&) noexcept = default;
method client (line 50) | client(client&&) noexcept = default;
method client (line 52) | client& operator=(const client&) noexcept = default;
method client (line 53) | client& operator=(client&&) noexcept = default;
FILE: src/zk/client_tests.cpp
type zk (line 14) | namespace zk
function buffer (line 17) | static buffer buffer_from(string_view str)
class client_tests (line 22) | class client_tests :
function GTEST_TEST_F (line 26) | GTEST_TEST_F(client_tests, get_root)
function GTEST_TEST_F (line 34) | GTEST_TEST_F(client_tests, exists)
function GTEST_TEST_F (line 41) | GTEST_TEST_F(client_tests, create)
function GTEST_TEST_F (line 50) | GTEST_TEST_F(client_tests, create_seq_and_set)
function GTEST_TEST_F (line 65) | GTEST_TEST_F(client_tests, create_seq_and_erase)
function GTEST_TEST_F (line 78) | GTEST_TEST_F(client_tests, create_seq_and_get_children)
function GTEST_TEST_F (line 97) | GTEST_TEST_F(client_tests, acl)
function GTEST_TEST_F (line 115) | GTEST_TEST_F(client_tests, watch_change)
function GTEST_TEST_F (line 129) | GTEST_TEST_F(client_tests, watch_children)
function GTEST_TEST_F (line 158) | GTEST_TEST_F(client_tests, watch_exists)
function GTEST_TEST_F (line 180) | GTEST_TEST_F(client_tests, load_fence)
function GTEST_TEST_F (line 187) | GTEST_TEST_F(client_tests, watch_close)
class stopping_client_tests (line 200) | class stopping_client_tests :
function GTEST_TEST_F (line 204) | GTEST_TEST_F(stopping_client_tests, watch_server_stop)
FILE: src/zk/config.hpp
type zk (line 28) | namespace zk
FILE: src/zk/connection.cpp
type zk (line 16) | namespace zk
function string_view (line 79) | static string_view sv_from_match(const TMatch& src)
function split_each_substr (line 85) | void split_each_substr(string_view src, char delim, const FAction& act...
function extract_host_list (line 98) | static connection_params::host_list extract_host_list(string_view src)
function extract_bool (line 106) | static bool extract_bool(string_view key, string_view val)
function extract_millis (line 128) | static std::chrono::milliseconds extract_millis(string_view key, strin...
function extract_advanced_options (line 144) | static void extract_advanced_options(string_view src, connection_param...
function connection_params (line 187) | connection_params connection_params::parse(string_view conn_string)
function to_string (line 269) | std::string to_string(const connection_params& x)
FILE: src/zk/connection.hpp
type zk (line 17) | namespace zk
class connection (line 26) | class connection
class connection_params (line 84) | class connection_params final
method host_list (line 142) | const host_list& hosts() const { return _hosts; }
method host_list (line 143) | host_list& hosts() { return _hosts; }
method randomize_hosts (line 158) | bool randomize_hosts() const { return _randomize_hosts; }
method read_only (line 164) | bool read_only() const { return _read_only; }
method timeout (line 175) | std::chrono::milliseconds timeout() const { return _timeout; }
FILE: src/zk/connection_tests.cpp
type zk (line 5) | namespace zk
function GTEST_TEST (line 9) | GTEST_TEST(connection_params_tests, defaults)
function GTEST_TEST (line 25) | GTEST_TEST(connection_params_tests, multi_hosts)
function GTEST_TEST (line 33) | GTEST_TEST(connection_params_tests, chroot)
function GTEST_TEST (line 42) | GTEST_TEST(connection_params_tests, randomize_hosts)
function GTEST_TEST (line 51) | GTEST_TEST(connection_params_tests, read_only)
function GTEST_TEST (line 60) | GTEST_TEST(connection_params_tests, randomize_and_read_only)
function GTEST_TEST (line 70) | GTEST_TEST(connection_params_tests, timeout)
FILE: src/zk/connection_zk.cpp
type zk (line 25) | namespace zk
function with_str (line 33) | auto with_str(string_view src, FAction&& action) noexcept(noexcept(std...
function ACL (line 42) | static ACL encode_acl_part(const acl_rule& src)
function with_acl (line 52) | auto with_acl(const acl& rules, FAction&& action) noexcept(noexcept(st...
function error_code (line 69) | static error_code error_code_from_raw(int raw)
function event_type (line 89) | static event_type event_from_raw(int raw)
function state (line 99) | static state state_from_raw(int raw)
function stat (line 124) | static stat stat_from_raw(const struct Stat& raw)
function string_vector_from_raw (line 141) | static std::vector<std::string> string_vector_from_raw(const struct St...
function acl (line 150) | static acl acl_from_raw(const struct ACL_vector& raw)
class connection_zk::watcher (line 207) | class connection_zk::watcher
method watcher (line 210) | watcher() :
method deliver_event (line 216) | virtual void deliver_event(event ev)
method get_event_future (line 224) | future<event> get_event_future()
class connection_zk::basic_watcher (line 235) | class connection_zk::basic_watcher :
method basic_watcher (line 239) | basic_watcher() :
method get_data_future (line 243) | future<TResult> get_data_future()
method deliver_event (line 248) | virtual void deliver_event(event ev) override
method deliver_data (line 258) | void deliver_data(optional<TResult> data, zk::exception_ptr ex_ptr)
function connection_from_context (line 288) | static ptr<connection_zk> connection_from_context(ptr<zhandle_t> zh)
type Stat (line 335) | struct Stat
class connection_zk::data_watcher (line 363) | class connection_zk::data_watcher :
method deliver_raw (line 367) | static void deliver_raw(int rc_in,
type String_vector (line 419) | struct String_vector
type Stat (line 420) | struct Stat
class connection_zk::child_watcher (line 463) | class connection_zk::child_watcher :
method deliver_raw (line 467) | static void deliver_raw(int rc_in,
type Stat (line 523) | struct Stat
class connection_zk::exists_watcher (line 553) | class connection_zk::exists_watcher :
method deliver_raw (line 557) | static void deliver_raw(int rc_in, ptr<const struct Stat> stat_in, p...
type Stat (line 655) | struct Stat
type ACL_vector (line 725) | struct ACL_vector
type Stat (line 725) | struct Stat
type ACL_vector (line 768) | struct ACL_vector
type connection_zk_commit_completer (line 794) | struct connection_zk_commit_completer
method connection_zk_commit_completer (line 802) | explicit connection_zk_commit_completer(multi_op&& src) :
method raw_stat_at (line 810) | ptr<Stat> raw_stat_at(std::size_t idx)
method path_buffer_for (line 815) | ptr<std::vector<char>> path_buffer_for(std::size_t idx, const std::s...
method deliver (line 823) | void deliver(error_code rc)
FILE: src/zk/connection_zk.hpp
type _zhandle (line 13) | struct _zhandle
type zk (line 15) | namespace zk
class connection_zk (line 21) | class connection_zk final :
class watcher (line 73) | class watcher
class basic_watcher (line 76) | class basic_watcher
class data_watcher (line 78) | class data_watcher
class child_watcher (line 80) | class child_watcher
class exists_watcher (line 82) | class exists_watcher
FILE: src/zk/error.cpp
type zk (line 9) | namespace zk
function to_string (line 25) | std::string to_string(const error_code& code)
function throw_error (line 32) | void throw_error(error_code code)
function get_exception_ptr_of (line 59) | zk::exception_ptr get_exception_ptr_of(error_code code)
class error_category_impl (line 75) | class error_category_impl final : public std::error_category
method name (line 78) | virtual ptr<const char> name() const noexcept override { return "zoo...
function format_error (line 98) | static std::string format_error(error_code code, const std::string& de...
FILE: src/zk/error.hpp
type zk (line 10) | namespace zk
type error_code (line 19) | enum class error_code : int
function is_transport_error (line 44) | inline constexpr bool is_transport_error(error_code code)
function is_invalid_arguments (line 51) | inline constexpr bool is_invalid_arguments(error_code code)
function is_invalid_ensemble_state (line 58) | inline constexpr bool is_invalid_ensemble_state(error_code code)
function is_invalid_connection_state (line 66) | inline constexpr bool is_invalid_connection_state(error_code code)
function is_check_failed (line 76) | inline constexpr bool is_check_failed(error_code code)
class error (line 108) | class error :
method error_code (line 117) | error_code code() const { return _code; }
class transport_error (line 126) | class transport_error :
class connection_loss (line 142) | class connection_loss:
class marshalling_error (line 157) | class marshalling_error:
class not_implemented (line 168) | class not_implemented:
class invalid_arguments (line 179) | class invalid_arguments :
class authentication_failed (line 196) | class authentication_failed:
class invalid_ensemble_state (line 208) | class invalid_ensemble_state :
class new_configuration_no_quorum (line 220) | class new_configuration_no_quorum:
class reconfiguration_in_progress (line 231) | class reconfiguration_in_progress:
class reconfiguration_disabled (line 241) | class reconfiguration_disabled:
class invalid_connection_state (line 251) | class invalid_connection_state :
class session_expired (line 271) | class session_expired:
class not_authorized (line 281) | class not_authorized:
class closed (line 292) | class closed:
class ephemeral_on_local_session (line 304) | class ephemeral_on_local_session:
class read_only_connection (line 316) | class read_only_connection :
class check_failed (line 327) | class check_failed :
class no_entry (line 337) | class no_entry :
class entry_exists (line 347) | class entry_exists :
class not_empty (line 357) | class not_empty :
class version_mismatch (line 368) | class version_mismatch :
class no_children_for_ephemerals (line 378) | class no_children_for_ephemerals :
class transaction_failed (line 389) | class transaction_failed :
method error_code (line 399) | error_code underlying_cause() const { return _underlying_cause; }
method failed_op_index (line 403) | std::size_t failed_op_index() const { return _op_index; }
type std (line 414) | namespace std
type is_error_code_enum<zk::error_code> (line 418) | struct is_error_code_enum<zk::error_code> :
FILE: src/zk/error_tests.cpp
type zk (line 5) | namespace zk
function GTEST_TEST (line 31) | GTEST_TEST(error_code_tests, throwing)
function GTEST_TEST (line 91) | GTEST_TEST(error_code_tests, to_string_bogus_code)
FILE: src/zk/exceptions.cpp
type zk (line 3) | namespace zk
function exception_ptr (line 6) | exception_ptr current_exception() noexcept
FILE: src/zk/exceptions.hpp
type zk (line 16) | namespace zk
function throw_exception (line 29) | [[noreturn]] inline void throw_exception(T const & e)
function throw_exception (line 41) | [[noreturn]] inline void throw_exception(T const & e)
FILE: src/zk/forwards.hpp
type zk (line 5) | namespace zk
class acl (line 8) | class acl
class acl_rule (line 9) | class acl_rule
type acl_version (line 10) | struct acl_version
type child_version (line 11) | struct child_version
class client (line 12) | class client
class connection (line 13) | class connection
class connection_params (line 14) | class connection_params
type create_mode (line 15) | enum class create_mode : unsigned int
class create_result (line 16) | class create_result
class error (line 17) | class error
class event (line 18) | class event
class exists_result (line 19) | class exists_result
type error_code (line 20) | enum class error_code : int
type event_type (line 21) | enum class event_type : int
class get_acl_result (line 22) | class get_acl_result
class get_children_result (line 23) | class get_children_result
class get_result (line 24) | class get_result
class multi_result (line 25) | class multi_result
class multi_op (line 26) | class multi_op
class op (line 27) | class op
type op_type (line 28) | enum class op_type : int
type permission (line 29) | enum class permission : unsigned int
class set_result (line 30) | class set_result
type state (line 31) | enum class state : int
type transaction_id (line 32) | struct transaction_id
type version (line 33) | struct version
class watch_children_result (line 34) | class watch_children_result
class watch_exists_result (line 35) | class watch_exists_result
class watch_result (line 36) | class watch_result
FILE: src/zk/future.hpp
type zk (line 101) | namespace zk
FILE: src/zk/multi.cpp
type zk (line 9) | namespace zk
function to_string_generic (line 13) | static std::string to_string_generic(const T& self)
function to_string (line 36) | std::string to_string(const op_type& self)
function op_type (line 57) | op_type op::type() const
function T (line 63) | const T& op::as(ptr<const char> operation) const
function op (line 93) | op op::check(std::string path, version check_)
function op (line 120) | op op::create(std::string path, buffer data, acl rules, create_mode mode)
function op (line 125) | op op::create(std::string path, buffer data, create_mode mode)
function op (line 149) | op op::erase(std::string path, version check)
function op (line 174) | op op::set(std::string path, buffer data, version check)
function to_string (line 193) | std::string to_string(const op& self)
function T (line 255) | const T& multi_result::part::as(ptr<const char> operation) const
function create_result (line 271) | const create_result& multi_result::part::as_create() const
function set_result (line 276) | const set_result& multi_result::part::as_set() const
function to_string (line 313) | std::string to_string(const multi_result::part& self)
function to_string (line 318) | std::string to_string(const multi_result& self)
FILE: src/zk/multi.hpp
type zk (line 17) | namespace zk
type op_type (line 24) | enum class op_type : int
class op (line 37) | class op final
type check_data (line 41) | struct check_data
method op_type (line 48) | op_type type() const { return op_type::check; }
type create_data (line 55) | struct create_data
method op_type (line 64) | op_type type() const { return op_type::create; }
type erase_data (line 76) | struct erase_data
method op_type (line 83) | op_type type() const { return op_type::erase; }
type set_data (line 92) | struct set_data
method op_type (line 100) | op_type type() const { return op_type::set; }
method op (line 112) | op& operator=(const op&) = delete;
method op (line 113) | op& operator=(op&&) = delete;
class multi_op (line 162) | class multi_op final
method multi_op (line 171) | multi_op() noexcept
method multi_op (line 178) | multi_op(std::initializer_list<op> ops) :
method size_type (line 185) | size_type size() const { return _ops.size(); }
method op (line 189) | const op& operator[](size_type idx) const { return _ops[idx]; }
method op (line 190) | op& operator[](size_type idx) { return _ops[idx]; }
method op (line 197) | const op& at(size_type idx) const { return _ops.at(idx); }
method op (line 198) | op& at(size_type idx) { return _ops.at(idx); }
method iterator (line 203) | iterator begin() { return _ops.begin(); }
method const_iterator (line 204) | const_iterator begin() const { return _ops.begin(); }
method const_iterator (line 205) | const_iterator cbegin() const { return _ops.begin(); }
method iterator (line 210) | iterator end() { return _ops.end(); }
method const_iterator (line 211) | const_iterator end() const { return _ops.end(); }
method const_iterator (line 212) | const_iterator cend() const { return _ops.end(); }
method reserve (line 216) | void reserve(size_type capacity) { _ops.reserve(capacity); }
method emplace_back (line 222) | void emplace_back(TArgs&&... args)
method push_back (line 229) | void push_back(op&& x) { emplace_back(std::move(x)); }
method push_back (line 230) | void push_back(const op& x) { emplace_back(x); }
class multi_result (line 242) | class multi_result final
class part (line 246) | class part final
method part (line 256) | part& operator=(const part&) = delete;
method part (line 257) | part& operator=(part&&) = delete;
method op_type (line 262) | op_type type() const { return _type; }
method multi_result (line 290) | multi_result() noexcept
method multi_result (line 295) | multi_result(multi_result&&) = default;
method multi_result (line 296) | multi_result & operator=(multi_result&&) = default;
method size_type (line 301) | size_type size() const { return _parts.size(); }
method part (line 307) | part& operator[](size_type idx) { return _parts[idx]; }
method part (line 256) | part& operator=(const part&) = delete;
method part (line 257) | part& operator=(part&&) = delete;
method op_type (line 262) | op_type type() const { return _type; }
method part (line 308) | const part& operator[](size_type idx) const { return _parts[idx]; }
method part (line 256) | part& operator=(const part&) = delete;
method part (line 257) | part& operator=(part&&) = delete;
method op_type (line 262) | op_type type() const { return _type; }
method part (line 315) | const part& at(size_type idx) const { return _parts.at(idx); }
method part (line 256) | part& operator=(const part&) = delete;
method part (line 257) | part& operator=(part&&) = delete;
method op_type (line 262) | op_type type() const { return _type; }
method part (line 316) | part& at(size_type idx) { return _parts.at(idx); }
method part (line 256) | part& operator=(const part&) = delete;
method part (line 257) | part& operator=(part&&) = delete;
method op_type (line 262) | op_type type() const { return _type; }
method iterator (line 321) | iterator begin() { return _parts.begin(); }
method const_iterator (line 322) | const_iterator begin() const { return _parts.begin(); }
method const_iterator (line 323) | const_iterator cbegin() const { return _parts.begin(); }
method iterator (line 328) | iterator end() { return _parts.end(); }
method const_iterator (line 329) | const_iterator end() const { return _parts.end(); }
method const_iterator (line 330) | const_iterator cend() const { return _parts.end(); }
method reserve (line 334) | void reserve(size_type capacity) { _parts.reserve(capacity); }
method emplace_back (line 340) | void emplace_back(TArgs&&... args)
method push_back (line 347) | void push_back(part&& x) { emplace_back(std::move(x)); }
method push_back (line 348) | void push_back(const part& x) { emplace_back(x); }
FILE: src/zk/multi_tests.cpp
type zk (line 13) | namespace zk
class multi_tests (line 16) | class multi_tests :
function buffer (line 20) | static buffer buffer_from(string_view str)
function GTEST_TEST_F (line 25) | GTEST_TEST_F(multi_tests, commit_no_fail)
function GTEST_TEST_F (line 53) | GTEST_TEST_F(multi_tests, commit_fail_check)
FILE: src/zk/optional.hpp
type zk (line 10) | namespace zk
function map (line 25) | auto map(FUnary&& transform, const optional<T>&... x) -> optional<decl...
function some (line 34) | optional<T> some(T x)
FILE: src/zk/optional_tests.cpp
type zk (line 5) | namespace zk
function GTEST_TEST (line 8) | GTEST_TEST(optional_test, integer)
FILE: src/zk/results.cpp
type zk (line 7) | namespace zk
type print_buffer_length_tag (line 19) | struct print_buffer_length_tag {}
type print_buffer_content_tag (line 20) | struct print_buffer_content_tag : public print_buffer_length_tag {}
function print_buffer (line 23) | auto print_buffer(std::ostream& os, const TBuffer& buf, struct print_b...
function print_buffer (line 30) | void print_buffer(std::ostream& os, const TBuffer& buf, struct print_b...
function print_range (line 36) | void print_range(std::ostream& os, const TRange& range)
function to_string_generic (line 52) | std::string to_string_generic(const T& x)
function to_string (line 79) | std::string to_string(const get_result& self)
function to_string (line 109) | std::string to_string(const get_children_result& self)
function to_string (line 140) | std::string to_string(const exists_result& self)
function to_string (line 166) | std::string to_string(const create_result& self)
function to_string (line 192) | std::string to_string(const set_result& self)
function to_string (line 219) | std::string to_string(const get_acl_result& self)
function to_string (line 243) | std::string to_string(const event& self)
function to_string (line 270) | std::string to_string(const watch_result& self)
function to_string (line 295) | std::string to_string(const watch_children_result& self)
function to_string (line 320) | std::string to_string(const watch_exists_result& self)
FILE: src/zk/results.hpp
type zk (line 17) | namespace zk
class get_result (line 24) | class get_result final
method get_result (line 29) | get_result(const get_result&) = default;
method get_result (line 30) | get_result& operator=(const get_result&) = default;
method get_result (line 32) | get_result(get_result&&) = default;
method get_result (line 33) | get_result& operator=(get_result&&) = default;
method buffer (line 39) | const buffer& data() const & { return _data; }
method buffer (line 40) | buffer& data() & { return _data; }
method buffer (line 41) | buffer data() && { return std::move(_data); }
class get_children_result (line 61) | class get_children_result final
method get_children_result (line 69) | get_children_result(const get_children_result&) = default;
method get_children_result (line 70) | get_children_result& operator=(const get_children_result&) = default;
method get_children_result (line 72) | get_children_result(get_children_result&&) = default;
method get_children_result (line 73) | get_children_result& operator=(get_children_result&&) = default;
method children_list_type (line 79) | const children_list_type& children() const & { return _children; }
method children_list_type (line 80) | children_list_type& children() & { return _children; }
method children_list_type (line 81) | children_list_type children() && { return std::move(_chi...
method stat (line 86) | const stat& parent_stat() const { return _parent_stat; }
method stat (line 87) | stat& parent_stat() { return _parent_stat; }
class exists_result (line 100) | class exists_result final
method exists_result (line 105) | exists_result(const exists_result&) = default;
method exists_result (line 106) | exists_result& operator=(const exists_result&) = default;
method exists_result (line 108) | exists_result(exists_result&&) = default;
method exists_result (line 109) | exists_result& operator=(exists_result&&) = default;
class create_result (line 134) | class create_result final
method create_result (line 139) | create_result(const create_result&) = default;
method create_result (line 140) | create_result& operator=(const create_result&) = default;
method create_result (line 142) | create_result(create_result&&) = default;
method create_result (line 143) | create_result& operator=(create_result&&) = default;
method name (line 153) | std::string name() && { return std::move(_name); }
class set_result (line 165) | class set_result final
method set_result (line 170) | set_result(const set_result&) = default;
method set_result (line 171) | set_result& operator=(const set_result&) = default;
method set_result (line 173) | set_result(set_result&&) = default;
method set_result (line 174) | set_result& operator=(set_result&&) = default;
class get_acl_result (line 193) | class get_acl_result final
method get_acl_result (line 198) | get_acl_result(const get_acl_result&) = default;
method get_acl_result (line 199) | get_acl_result& operator=(const get_acl_result&) = default;
method get_acl_result (line 201) | get_acl_result(get_acl_result&&) = default;
method get_acl_result (line 202) | get_acl_result& operator=(get_acl_result&&) = default;
method acl (line 210) | zk::acl acl() && { return std::move(_acl); }
class event (line 238) | class event final
method event (line 243) | event(const event&) = default;
method event (line 244) | event& operator=(const event&) = default;
method event (line 246) | event(event&&) = default;
method event (line 247) | event& operator=(event&&) = default;
method event_type (line 250) | const event_type& type() const { return _type; }
class watch_result (line 266) | class watch_result final
method watch_result (line 271) | watch_result(const watch_result&) = delete;
method watch_result (line 272) | watch_result& operator=(const watch_result&) = delete;
method watch_result (line 274) | watch_result(watch_result&&) = default;
method watch_result (line 275) | watch_result& operator=(watch_result&&) = default;
method get_result (line 281) | const get_result& initial() const & { return _initial; }
method get_result (line 282) | get_result& initial() & { return _initial; }
method get_result (line 283) | get_result initial() && { return std::move(_initial); }
method next (line 290) | future<event> next() && { return std::move(_next); }
class watch_children_result (line 303) | class watch_children_result final
method watch_children_result (line 308) | watch_children_result(const watch_children_result&) = del...
method watch_children_result (line 309) | watch_children_result& operator=(const watch_children_result&) = del...
method watch_children_result (line 311) | watch_children_result(watch_children_result&&) = default;
method watch_children_result (line 312) | watch_children_result& operator=(watch_children_result&&) = default;
method get_children_result (line 318) | const get_children_result& initial() const & { return _initial; }
method get_children_result (line 319) | get_children_result& initial() & { return _initial; }
method get_children_result (line 320) | get_children_result initial() && { return std::move(_ini...
method next (line 327) | future<event> next() && { return std::move(_next); }
class watch_exists_result (line 340) | class watch_exists_result final
method watch_exists_result (line 345) | watch_exists_result(const watch_exists_result&) = delete;
method watch_exists_result (line 346) | watch_exists_result& operator=(const watch_exists_result&) = delete;
method watch_exists_result (line 348) | watch_exists_result(watch_exists_result&&) = default;
method watch_exists_result (line 349) | watch_exists_result& operator=(watch_exists_result&&) = default;
method exists_result (line 355) | const exists_result& initial() const & { return _initial; }
method exists_result (line 356) | exists_result& initial() & { return _initial; }
method exists_result (line 357) | exists_result initial() && { return std::move(_initial); }
method next (line 364) | future<event> next() && { return std::move(_next); }
FILE: src/zk/server/classpath.cpp
type zk::server (line 17) | namespace zk::server
function join (line 21) | void join(std::ostream& os, const TContainer& src, TSep sep)
function file_exists (line 32) | static bool file_exists(ptr<const char> path)
function classpath (line 48) | static classpath find_system_default()
function classpath (line 101) | classpath classpath::system_default()
FILE: src/zk/server/classpath.hpp
type zk::server (line 9) | namespace zk::server
class classpath (line 18) | class classpath final
FILE: src/zk/server/classpath_tests.cpp
type zk::server (line 8) | namespace zk::server
function GTEST_TEST (line 11) | GTEST_TEST(classpath_tests, system_default)
FILE: src/zk/server/configuration.cpp
type zk::server (line 12) | namespace zk::server
class zero_copy_streambuf (line 35) | class zero_copy_streambuf final :
method zero_copy_streambuf (line 39) | zero_copy_streambuf(string_view input)
function configuration (line 77) | configuration configuration::make_minimal(std::string data_directory, ...
function parse_whitelist (line 88) | static std::set<std::string> parse_whitelist(string_view source)
function configuration (line 118) | configuration configuration::from_lines(std::vector<std::string> lines)
function configuration (line 185) | configuration configuration::from_stream(std::istream& stream)
function configuration (line 201) | configuration configuration::from_file(std::string filename)
function configuration (line 209) | configuration configuration::from_string(string_view value)
function configuration (line 264) | configuration& configuration::client_port(optional<std::uint16_t> port)
function configuration (line 275) | configuration& configuration::data_directory(optional<std::string> path)
function configuration (line 286) | configuration& configuration::tick_time(optional<duration_type> tick_t...
function configuration (line 297) | configuration& configuration::init_limit(optional<std::size_t> limit)
function configuration (line 308) | configuration& configuration::sync_limit(optional<std::size_t> limit)
function configuration (line 319) | configuration& configuration::leader_serves(optional<bool> serve)
function configuration (line 333) | configuration& configuration::four_letter_word_whitelist(optional<std:...
function configuration (line 368) | configuration& configuration::add_server(server_id id,
function configuration (line 398) | configuration& configuration::add_setting(std::string key, std::string...
FILE: src/zk/server/configuration.hpp
type zk::server (line 16) | namespace zk::server
type server_id (line 29) | struct server_id :
method server_id (line 35) | server_id(std::size_t value) :
class configuration (line 71) | class configuration final
type setting (line 243) | struct setting
type std (line 279) | namespace std
type hash<zk::server::server_id> (line 283) | struct hash<zk::server::server_id>
method result_type (line 288) | result_type operator()(const argument_type& x) const
FILE: src/zk/server/configuration_tests.cpp
type zk::server (line 9) | namespace zk::server
function GTEST_TEST (line 37) | GTEST_TEST(configuration_tests, from_example)
function GTEST_TEST (line 75) | GTEST_TEST(configuration_tests, minimal)
function GTEST_TEST (line 93) | GTEST_TEST(configuration_tests, four_letter_words)
FILE: src/zk/server/detail/close.cpp
type zk::server::detail (line 9) | namespace zk::server::detail
function close (line 12) | void close(int fd)
FILE: src/zk/server/detail/close.hpp
type zk::server::detail (line 5) | namespace zk::server::detail
FILE: src/zk/server/detail/event_handle.cpp
type zk::server::detail (line 11) | namespace zk::server::detail
FILE: src/zk/server/detail/event_handle.hpp
type zk::server::detail (line 5) | namespace zk::server::detail
class event_handle (line 8) | class event_handle final
method event_handle (line 16) | event_handle(const event_handle&) = delete;
method event_handle (line 18) | event_handle& operator=(const event_handle&) = delete;
method native_handle_type (line 36) | native_handle_type native_handle() { return _fd; }
FILE: src/zk/server/detail/pipe.cpp
type zk::server::detail (line 10) | namespace zk::server::detail
function on_exec_flags (line 28) | static int on_exec_flags(on_exec exec)
function close_if_open (line 57) | static void close_if_open(int& fd)
function dup3 (line 153) | static int dup3(int src_fd, int redir_fd, on_exec exec)
FILE: src/zk/server/detail/pipe.hpp
type zk::server::detail (line 9) | namespace zk::server::detail
type on_exec (line 13) | enum class on_exec
class pipe_closed (line 27) | class pipe_closed :
class pipe (line 39) | class pipe final
method pipe (line 52) | pipe(const pipe&) = delete;
method pipe (line 53) | pipe& operator=(const pipe&) = delete;
FILE: src/zk/server/detail/pipe_tests.cpp
type zk::server::detail (line 7) | namespace zk::server::detail
function GTEST_TEST (line 10) | GTEST_TEST(pipe_tests, read_write)
FILE: src/zk/server/detail/subprocess.cpp
type zk::server::detail (line 15) | namespace zk::server::detail
function pid_t (line 18) | static pid_t create_subproc(pipe& stdin_pipe,
function dont_leak (line 62) | static void dont_leak(pipe& p) noexcept
FILE: src/zk/server/detail/subprocess.hpp
type zk::server::detail (line 13) | namespace zk::server::detail
class subprocess (line 17) | class subprocess
method subprocess (line 27) | subprocess(const subprocess&) = delete;
method subprocess (line 28) | subprocess& operator=(const subprocess&) = delete;
method pipe (line 38) | pipe& stdin() noexcept { return _stdin; }
method pipe (line 39) | pipe& stdout() noexcept { return _stdout; }
method pipe (line 40) | pipe& stderr() noexcept { return _stderr; }
FILE: src/zk/server/detail/subprocess_tests.cpp
type zk::server::detail (line 8) | namespace zk::server::detail
function GTEST_TEST (line 11) | GTEST_TEST(subprocess_tests, echo)
function GTEST_TEST (line 23) | GTEST_TEST(subprocess_tests, cat_kill)
FILE: src/zk/server/package_registry.cpp
type zk::server (line 5) | namespace zk::server
type package_registry::registration_info (line 8) | struct package_registry::registration_info final
method registration_info (line 13) | registration_info(std::shared_ptr<package_registry> owner, std::stri...
FILE: src/zk/server/package_registry.hpp
type zk::server (line 14) | namespace zk::server
class package_registry (line 25) | class package_registry final
type registration_info (line 30) | struct registration_info
method empty (line 62) | bool empty() const
FILE: src/zk/server/package_registry_tests.cpp
type zk::server (line 10) | namespace zk::server
function package_registry (line 17) | package_registry& test_package_registry::instance()
function GTEST_TEST (line 27) | GTEST_TEST(package_registry_tests, test_package_registry_global_instance)
function GTEST_TEST (line 32) | GTEST_TEST(package_registry_tests, registration)
FILE: src/zk/server/package_registry_tests.hpp
type zk::server (line 5) | namespace zk::server
class package_registry (line 8) | class package_registry
class test_package_registry (line 11) | class test_package_registry final
FILE: src/zk/server/server.cpp
type zk::server (line 18) | namespace zk::server
function validate_settings (line 21) | static void validate_settings(const configuration& settings)
function wait_for_event (line 63) | static void wait_for_event(int fd1, int fd2, int fd3)
FILE: src/zk/server/server.hpp
type zk::server (line 11) | namespace zk::server
type detail (line 14) | namespace detail
class event_handle (line 17) | class event_handle
class classpath (line 25) | class classpath
class configuration (line 26) | class configuration
class server (line 29) | class server final
method server (line 48) | server(const server&) = delete;
FILE: src/zk/server/server_group.cpp
type zk::server (line 17) | namespace zk::server
type server_group::info (line 20) | struct server_group::info
method info (line 29) | info(const configuration& base) :
function server_group (line 39) | server_group& server_group::operator=(server_group&& src) noexcept
function create_directory (line 46) | static void create_directory(const std::string& path)
function save_id_file (line 54) | static void save_id_file(const std::string& path, const server_id& id)
function server_group (line 63) | server_group server_group::make_ensemble(std::size_t size, const confi...
FILE: src/zk/server/server_group.hpp
type zk::server (line 13) | namespace zk::server
class classpath (line 20) | class classpath
class server (line 21) | class server
class server_group (line 34) | class server_group final
method size (line 60) | std::size_t size() const { return _servers.size(); }
type info (line 63) | struct info
FILE: src/zk/server/server_group_tests.cpp
type zk::server (line 12) | namespace zk::server
function GTEST_TEST (line 17) | GTEST_TEST(server_group_tests, ensemble)
FILE: src/zk/server/server_tests.cpp
type zk::server (line 20) | namespace zk::server
function delete_directory (line 23) | void delete_directory(std::string path)
function client (line 64) | client server_fixture::get_connected_client() const
function client (line 102) | client single_server_fixture::get_connected_client()
function GTEST_TEST (line 111) | GTEST_TEST(server_tests, start_stop)
function GTEST_TEST (line 120) | GTEST_TEST(server_tests, shutdown_and_wait)
FILE: src/zk/server/server_tests.hpp
type zk::server (line 9) | namespace zk::server
class server (line 12) | class server
class server_fixture (line 14) | class server_fixture :
class single_server_fixture (line 36) | class single_server_fixture :
FILE: src/zk/string_view.hpp
type zk (line 9) | namespace zk
FILE: src/zk/tests/main.cpp
function main (line 3) | int main(int argc, char** argv)
FILE: src/zk/tests/test.hpp
type zk::test (line 28) | namespace zk::test
type detail (line 55) | namespace detail
type check_throws_info (line 59) | struct check_throws_info
method check_throws_info (line 64) | explicit check_throws_info(ptr<const char> filename, std::size_t l...
FILE: src/zk/types.cpp
type zk (line 7) | namespace zk
function to_string (line 29) | std::string to_string(const event_type& self)
function to_string (line 52) | std::string to_string(const version& self)
function to_string (line 75) | std::string to_string(const acl_version& self)
function to_string (line 98) | std::string to_string(const child_version& self)
function to_string (line 114) | std::string to_string(const transaction_id& self)
function to_string (line 136) | std::string to_string(const stat& self)
function to_string (line 161) | std::string to_string(const state& self)
function to_string (line 186) | std::string to_string(const create_mode& self)
FILE: src/zk/types.hpp
type zk (line 9) | namespace zk
type strong_id (line 28) | struct strong_id
method strong_id (line 38) | strong_id() noexcept = default;
method strong_id (line 41) | constexpr explicit strong_id(value_type value) noexcept :
method value_type (line 46) | constexpr value_type get() const noexcept { return value; }
method TReal (line 57) | TReal& operator++()
method TReal (line 63) | TReal operator++(int)
method TReal (line 74) | TReal& operator--()
method TReal (line 80) | TReal operator--(int)
function hash (line 127) | inline std::size_t hash(const strong_id<TReal, TId>& x)
type basic_version (line 140) | struct basic_version :
method TReal (line 145) | static constexpr TReal invalid() { return TReal(-42); }
method TReal (line 150) | static constexpr TReal any() { return TReal(-1); }
type version (line 158) | struct version final :
type acl_version (line 171) | struct acl_version final :
type child_version (line 184) | struct child_version final :
type transaction_id (line 199) | struct transaction_id final :
type stat (line 220) | struct stat final
method is_ephemeral (line 271) | bool is_ephemeral() const
type create_mode (line 283) | enum class create_mode : unsigned int
function create_mode (line 302) | constexpr create_mode operator|(create_mode a, create_mode b)
function create_mode (line 308) | constexpr create_mode operator&(create_mode a, create_mode b)
function create_mode (line 315) | constexpr create_mode operator~(create_mode a)
function is_set (line 321) | constexpr bool is_set(create_mode self, create_mode flags)
type event_type (line 331) | enum class event_type : int
type state (line 385) | enum class state : int
type std (line 412) | namespace std
type hash<zk::version> (line 416) | struct hash<zk::version>
method result_type (line 421) | result_type operator()(const argument_type& x) const
type hash<zk::acl_version> (line 428) | struct hash<zk::acl_version>
method result_type (line 433) | result_type operator()(const argument_type& x) const
type hash<zk::child_version> (line 440) | struct hash<zk::child_version>
method result_type (line 445) | result_type operator()(const argument_type& x) const
type hash<zk::transaction_id> (line 452) | struct hash<zk::transaction_id>
method result_type (line 457) | result_type operator()(const argument_type& x) const
FILE: src/zk/types_tests.cpp
type zk (line 5) | namespace zk
function GTEST_TEST (line 12) | GTEST_TEST(event_type_tests, stringification)
function GTEST_TEST (line 28) | GTEST_TEST(state_tests, stringification)
Condensed preview — 96 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (396K chars).
[
{
"path": ".github/ISSUE_TEMPLATE.md",
"chars": 76,
"preview": "Don't forget the label!\n\n - If you have a question, use the \"question\" tag.\n"
},
{
"path": ".gitignore",
"chars": 747,
"preview": "*~\n*.swp\n*.orig\n*.kdev4\nbuild*\n.kdev*\n*.kdev4\npackages/\n.vs\nx64/\n*sdf\n*.pc\n\n# Compiled source #\n###################\n*.co"
},
{
"path": ".travis.yml",
"chars": 2168,
"preview": "language: cpp\nsudo: required\ndist: trusty\nenv:\n matrix:\n - DISTRO=ubuntu-16.04 CONFIG=Debug\n - DISTRO=ubuntu-16.04 CO"
},
{
"path": "AUTHORS",
"chars": 64,
"preview": "Contributors\n============\n\nTravis Gockel <travis@gockelhut.com>\n"
},
{
"path": "CMakeLists.txt",
"chars": 13986,
"preview": "cmake_minimum_required(VERSION 3.5)\n\nfile(READ src/zk/config.hpp CONFIG_HPP_STR)\nstring(REGEX REPLACE \".*# *define +ZKPP"
},
{
"path": "CONTRIBUTING.rst",
"chars": 3956,
"preview": "Contributing Guide\n==================\n\nSo you want to contribute to the ZooKeeper C++ library?\nI'd love your contributio"
},
{
"path": "COPYING",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 12769,
"preview": "# ZooKeeper C++\n\nA ZooKeeper client for C++.\nIt is [hosted on GitHub](https://github.com/tgockel/zookeeper-cpp),\n[docume"
},
{
"path": "cmake/modules/BuildFunctions.cmake",
"chars": 6186,
"preview": "include(CMakeParseArguments)\ninclude(ListSplit)\n\n# build_option\n# Creates a build option, which is configurable via a CM"
},
{
"path": "cmake/modules/CodeCoverage.cmake",
"chars": 6503,
"preview": "# Copyright (c) 2012 - 2015, Lars Bilke\n# All rights reserved.\n#\n# Redistribution and use in source and binary forms, wi"
},
{
"path": "cmake/modules/ConfigurationSetting.cmake",
"chars": 1598,
"preview": "include(CMakeParseArguments)\n\n# configuration_setting\n# Creates a configuration setting, which is configurable via a CMa"
},
{
"path": "cmake/modules/ListSplit.cmake",
"chars": 331,
"preview": "# list_split\n# Split a list into values matching or not matching a given expression.\nmacro(list_split matching non_match"
},
{
"path": "cmake/modules/ZooKeeper.cmake",
"chars": 3091,
"preview": "# Utilities for configuring and running a ZooKeeper Server.\ncmake_minimum_required(VERSION 3.5)\n\ninclude(CMakeParseArgum"
},
{
"path": "config/dev-env",
"chars": 2148,
"preview": "#!/bin/bash -e\n# Create a build environment.\n\nPROJECT_ROOT=$(readlink -f $(dirname $0)/..)\n\nCOMMAND=/bin/bash\nDISTRO=\n\nf"
},
{
"path": "config/docker/debian-10/Dockerfile",
"chars": 434,
"preview": "FROM debian:10\nLABEL maintainer=\"Travis Gockel <travis@gockelhut.com>\"\n\nRUN apt-get update \\\n && apt-get instal"
},
{
"path": "config/docker/opensuse-15/Dockerfile",
"chars": 588,
"preview": "FROM opensuse/leap:15\nLABEL maintainer=\"Travis Gockel <travis@gockelhut.com>\"\n\nRUN zypper addrepo -f \"https://download.o"
},
{
"path": "config/docker/ubuntu-16.04/Dockerfile",
"chars": 1084,
"preview": "FROM ubuntu:16.04\nLABEL maintainer=\"Travis Gockel <travis@gockelhut.com>\"\n\nRUN apt-get update\nRUN apt-get install --yes "
},
{
"path": "config/docker/ubuntu-18.04/Dockerfile",
"chars": 507,
"preview": "FROM ubuntu:18.04\nLABEL maintainer=\"Travis Gockel <travis@gockelhut.com>\"\n\nRUN apt-get update \\\n && apt-get ins"
},
{
"path": "config/docker/ubuntu-20.04/Dockerfile",
"chars": 555,
"preview": "FROM ubuntu:20.04\nLABEL maintainer=\"Travis Gockel <travis@gockelhut.com>\"\n\nRUN apt-get update \\\n && DEB"
},
{
"path": "config/make-doxygen",
"chars": 55,
"preview": "#!/bin/bash -e\nmkdir -p build/doc\ndoxygen doc/Doxyfile\n"
},
{
"path": "config/make-package",
"chars": 3977,
"preview": "#!/bin/bash -e\n\nPROJECT_ROOT=$(readlink -f $(dirname $0)/..)\nBUILD_DIR=\"$BUILD_DIR\"\nCOPY_TO=\"\"\nDISTRO=\"$DISTRO\"\nPACKAGE_"
},
{
"path": "config/make-packages",
"chars": 1573,
"preview": "#!/bin/bash -e\n\nPROJECT_ROOT=$(readlink -f $(dirname $0)/..)\n\nfunction show_usage {\n echo \"$0: Create multiple packages"
},
{
"path": "config/publish-doxygen",
"chars": 809,
"preview": "#!/bin/bash -e\n\n# Settings\nREPO_PATH=git@github.com:tgockel/zookeeper-cpp.git\nHTML_PATH=build/doc/html\nCOMMIT_USER=\"Docu"
},
{
"path": "config/run-tests",
"chars": 643,
"preview": "#!/bin/bash -e\n\nPROJECT_ROOT=$(readlink -f $(dirname $0)/..)\nBUILD_DIR=${PROJECT_ROOT}/build-ci\necho \"${DISTRO} ${PROJEC"
},
{
"path": "config/upload-coverage",
"chars": 654,
"preview": "#!/bin/bash -e\n\n# TODO: 99% sure there is a better way to do this\nGCC_VERSION_MAJOR=$(c++ --version | grep -Po '\\d\\.\\d\\."
},
{
"path": "doc/Doxyfile",
"chars": 10403,
"preview": "#---------------------------------------------------------------------------\n# Project related configuration options\n#--"
},
{
"path": "install/deb/libzkpp/control.in",
"chars": 447,
"preview": "Package: libzkpp@PROJECT_SO_VERSION@\nVersion: @PROJECT_PACKAGE_VERSION@\nPriority: optional\nMaintainer: Travis Gockel <tr"
},
{
"path": "install/deb/libzkpp/postinst.in",
"chars": 152,
"preview": "#!/bin/sh -e\n\nupdate-alternatives --install @CMAKE_INSTALL_PREFIX@/lib/libzkpp.so libzkpp @CMAKE_INSTALL_PREFIX@/lib/lib"
},
{
"path": "install/deb/libzkpp/shlibs.in",
"chars": 34,
"preview": "libzkpp 0.2 libzkpp0.2 (>= 0.2.3)\n"
},
{
"path": "install/deb/libzkpp-dev/control.in",
"chars": 316,
"preview": "Package: libzkpp@PROJECT_SO_VERSION@-dev\nVersion: @PROJECT_PACKAGE_VERSION@\nPriority: optional\nMaintainer: Travis Gockel"
},
{
"path": "install/deb/libzkpp-server/control.in",
"chars": 437,
"preview": "Package: libzkpp-server@PROJECT_SO_VERSION@\nVersion: @PROJECT_PACKAGE_VERSION@\nPriority: optional\nMaintainer: Travis Goc"
},
{
"path": "install/deb/libzkpp-server/postinst.in",
"chars": 173,
"preview": "#!/bin/sh -e\n\nupdate-alternatives --install @CMAKE_INSTALL_PREFIX@/lib/libzkpp-server.so libzkpp-server @CMAKE_INSTALL_P"
},
{
"path": "install/deb/libzkpp-server-dev/control.in",
"chars": 455,
"preview": "Package: libzkpp-server@PROJECT_SO_VERSION@-dev\nVersion: @PROJECT_PACKAGE_VERSION@\nPriority: optional\nMaintainer: Travis"
},
{
"path": "src/zk/acl.cpp",
"chars": 4915,
"preview": "#include \"acl.hpp\"\n\n#include <ostream>\n#include <sstream>\n#include <tuple>\n#include <utility>\n\nnamespace zk\n{\n\n/////////"
},
{
"path": "src/zk/acl.hpp",
"chars": 9576,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\n#include <initializer_list>\n#include <iosfwd>\n#include <string>\n#include <utilit"
},
{
"path": "src/zk/acl_tests.cpp",
"chars": 3130,
"preview": "#include <zk/tests/test.hpp>\n\n#include \"acl.hpp\"\n\nnamespace zk\n{\n\n//////////////////////////////////////////////////////"
},
{
"path": "src/zk/buffer.hpp",
"chars": 4668,
"preview": "/// \\file\n/// Controls the \\c buffer type.\n#pragma once\n\n#include <zk/config.hpp>\n\n#include <type_traits>\n\n/// \\addtogro"
},
{
"path": "src/zk/client.cpp",
"chars": 4135,
"preview": "#include \"client.hpp\"\n#include \"acl.hpp\"\n#include \"connection.hpp\"\n#include \"multi.hpp\"\n#include \"exceptions.hpp\"\n\n#incl"
},
{
"path": "src/zk/client.hpp",
"chars": 12909,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\n#include <memory>\n#include <utility>\n\n#include \"buffer.hpp\"\n#include \"forwards.h"
},
{
"path": "src/zk/client_tests.cpp",
"chars": 6726,
"preview": "#include <zk/server/server_tests.hpp>\n\n#include <algorithm>\n#include <chrono>\n#include <cstring>\n#include <iostream>\n#in"
},
{
"path": "src/zk/config.hpp",
"chars": 984,
"preview": "#pragma once\n\n/// \\addtogroup Client\n/// \\{\n\n/// \\def ZKPP_USER_CONFIG\n/// A user-defined configuration file to be inclu"
},
{
"path": "src/zk/connection.cpp",
"chars": 9171,
"preview": "#include \"connection.hpp\"\n#include \"connection_zk.hpp\"\n#include \"error.hpp\"\n#include \"types.hpp\"\n#include \"exceptions.hp"
},
{
"path": "src/zk/connection.hpp",
"chars": 8209,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\n#include <chrono>\n#include <iosfwd>\n#include <memory>\n#include <mutex>\n#include "
},
{
"path": "src/zk/connection_tests.cpp",
"chars": 2350,
"preview": "#include <zk/tests/test.hpp>\n\n#include \"connection.hpp\"\n\nnamespace zk\n{\n\n// This test is mostly to check that we still u"
},
{
"path": "src/zk/connection_zk.cpp",
"chars": 37273,
"preview": "#include \"connection_zk.hpp\"\n#include \"exceptions.hpp\"\n\n#include <algorithm>\n#include <cassert>\n#include <cerrno>\n#inclu"
},
{
"path": "src/zk/connection_zk.hpp",
"chars": 2988,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\n#include <chrono>\n#include <memory>\n#include <mutex>\n#include <unordered_map>\n\n#"
},
{
"path": "src/zk/error.cpp",
"chars": 9233,
"preview": "#include \"error.hpp\"\n#include \"exceptions.hpp\"\n\n#include <sstream>\n#include <ostream>\n\n#include <zookeeper/zookeeper.h>\n"
},
{
"path": "src/zk/error.hpp",
"chars": 14165,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n#include \"exceptions.hpp\"\n\n#include <iosfwd>\n#include <string>\n#include <system_e"
},
{
"path": "src/zk/error_tests.cpp",
"chars": 2764,
"preview": "#include <zk/tests/test.hpp>\n\n#include \"error.hpp\"\n\nnamespace zk\n{\n\nstatic error_code all_error_codes[] =\n {\n "
},
{
"path": "src/zk/exceptions.cpp",
"chars": 208,
"preview": "#include \"exceptions.hpp\"\n\nnamespace zk\n{\n\nexception_ptr current_exception() noexcept\n{\n#if ZKPP_FUTURE_USE_BOOST\n re"
},
{
"path": "src/zk/exceptions.hpp",
"chars": 904,
"preview": "/** \\file\n * Controls the throwing of exceptions and import of \\c exception_ptr types and \\c current_exception implemen"
},
{
"path": "src/zk/forwards.hpp",
"chars": 684,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\nnamespace zk\n{\n\nclass acl;\nclass acl_rule;\nstruct acl_version;\nstruct child_vers"
},
{
"path": "src/zk/future.hpp",
"chars": 4059,
"preview": "/** \\file\n * Controls the import of \\c future and \\c promise types. These are probably \\c std::future and \\c std::promi"
},
{
"path": "src/zk/multi.cpp",
"chars": 8144,
"preview": "#include \"multi.hpp\"\n#include \"exceptions.hpp\"\n\n#include <new>\n#include <ostream>\n#include <sstream>\n#include <stdexcept"
},
{
"path": "src/zk/multi.hpp",
"chars": 10326,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\n#include <initializer_list>\n#include <iosfwd>\n#include <string>\n#include <varian"
},
{
"path": "src/zk/multi_tests.cpp",
"chars": 1914,
"preview": "#include <zk/server/server_tests.hpp>\n\n#include <algorithm>\n#include <chrono>\n#include <cstring>\n#include <iostream>\n\n#i"
},
{
"path": "src/zk/optional.hpp",
"chars": 867,
"preview": "/// \\file\n/// Imports of \\c optional and \\c nullopt_t types, as well as the \\c nullopt \\c constexpr. These are \\c std::o"
},
{
"path": "src/zk/optional_tests.cpp",
"chars": 222,
"preview": "#include <zk/tests/test.hpp>\n\n#include \"optional.hpp\"\n\nnamespace zk\n{\n\nGTEST_TEST(optional_test, integer)\n{\n optional"
},
{
"path": "src/zk/results.cpp",
"chars": 11885,
"preview": "#include \"results.hpp\"\n\n#include <ostream>\n#include <sstream>\n#include <utility>\n\nnamespace zk\n{\n\n//////////////////////"
},
{
"path": "src/zk/results.hpp",
"chars": 11688,
"preview": "/// \\file\n/// Describes the various result types of \\c client operations.\n#pragma once\n\n#include <zk/config.hpp>\n\n#inclu"
},
{
"path": "src/zk/server/classpath.cpp",
"chars": 2652,
"preview": "#include \"classpath.hpp\"\n\n#include <zk/string_view.hpp>\n\n#include <algorithm>\n#include <array>\n#include <cerrno>\n#includ"
},
{
"path": "src/zk/server/classpath.hpp",
"chars": 1121,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\n#include <iosfwd>\n#include <string>\n#include <vector>\n\nnamespace zk::server\n{\n\n/"
},
{
"path": "src/zk/server/classpath_registration_template.cpp.in",
"chars": 450,
"preview": "#include <zk/config.hpp>\n#include <zk/server/classpath.hpp>\n#include <zk/server/package_registry.hpp>\n#include <zk/serve"
},
{
"path": "src/zk/server/classpath_tests.cpp",
"chars": 476,
"preview": "#include <zk/tests/test.hpp>\n\n#include <iostream>\n#include <stdexcept>\n\n#include \"classpath.hpp\"\n\nnamespace zk::server\n{"
},
{
"path": "src/zk/server/configuration.cpp",
"chars": 13888,
"preview": "#include \"configuration.hpp\"\n\n#include <cctype>\n#include <cstdlib>\n#include <fstream>\n#include <istream>\n#include <ostre"
},
{
"path": "src/zk/server/configuration.hpp",
"chars": 11447,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n#include <zk/optional.hpp>\n#include <zk/string_view.hpp>\n#include <zk/types.hpp>\n"
},
{
"path": "src/zk/server/configuration_tests.cpp",
"chars": 3036,
"preview": "#include <zk/string_view.hpp>\n#include <zk/tests/test.hpp>\n\n#include <iostream>\n#include <sstream>\n\n#include \"configurat"
},
{
"path": "src/zk/server/detail/close.cpp",
"chars": 651,
"preview": "#include \"close.hpp\"\n\n#include <string>\n#include <system_error>\n\n#include <cerrno>\n#include <unistd.h>\n\nnamespace zk::se"
},
{
"path": "src/zk/server/detail/close.hpp",
"chars": 571,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\nnamespace zk::server::detail\n{\n\n/** A safe wrapper around the Linux \\c close cal"
},
{
"path": "src/zk/server/detail/event_handle.cpp",
"chars": 966,
"preview": "#include \"event_handle.hpp\"\n#include \"close.hpp\"\n\n#include <cerrno>\n#include <cstdint>\n#include <system_error>\n\n#include"
},
{
"path": "src/zk/server/detail/event_handle.hpp",
"chars": 1027,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\nnamespace zk::server::detail\n{\n\nclass event_handle final\n{\npublic:\n using nat"
},
{
"path": "src/zk/server/detail/pipe.cpp",
"chars": 4282,
"preview": "#include \"close.hpp\"\n#include \"pipe.hpp\"\n\n#include <algorithm>\n#include <system_error>\n\n#include <fcntl.h>\n#include <uni"
},
{
"path": "src/zk/server/detail/pipe.hpp",
"chars": 3428,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n#include <zk/optional.hpp>\n\n#include <stdexcept>\n#include <string>\n\nnamespace zk:"
},
{
"path": "src/zk/server/detail/pipe_tests.cpp",
"chars": 353,
"preview": "#include <zk/tests/test.hpp>\n\n#include \"pipe.hpp\"\n\n#include <unistd.h>\n\nnamespace zk::server::detail\n{\n\nGTEST_TEST(pipe_"
},
{
"path": "src/zk/server/detail/subprocess.cpp",
"chars": 4116,
"preview": "#include \"subprocess.hpp\"\n\n#include <algorithm>\n#include <iterator>\n#include <numeric>\n#include <ostream>\n#include <syst"
},
{
"path": "src/zk/server/detail/subprocess.hpp",
"chars": 1468,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\n#include <chrono>\n#include <cstddef>\n#include <iosfwd>\n#include <string>\n#includ"
},
{
"path": "src/zk/server/detail/subprocess_tests.cpp",
"chars": 849,
"preview": "#include <zk/tests/test.hpp>\n\n#include <chrono>\n#include <thread>\n\n#include \"subprocess.hpp\"\n\nnamespace zk::server::deta"
},
{
"path": "src/zk/server/package_registry.cpp",
"chars": 1900,
"preview": "#include \"package_registry.hpp\"\n\n#include <stdexcept>\n\nnamespace zk::server\n{\n\nstruct package_registry::registration_inf"
},
{
"path": "src/zk/server/package_registry.hpp",
"chars": 2319,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n#include <zk/optional.hpp>\n\n#include <cstddef>\n#include <map>\n#include <memory>\n#"
},
{
"path": "src/zk/server/package_registry_tests.cpp",
"chars": 1928,
"preview": "#include <zk/tests/test.hpp>\n\n#include \"classpath.hpp\"\n#include \"package_registry.hpp\"\n#include \"package_registry_tests."
},
{
"path": "src/zk/server/package_registry_tests.hpp",
"chars": 235,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\nnamespace zk::server\n{\n\nclass package_registry;\n\n/** Global package registry for"
},
{
"path": "src/zk/server/server.cpp",
"chars": 3809,
"preview": "#include \"server.hpp\"\n\n#include <zk/future.hpp>\n\n#include <cerrno>\n#include <exception>\n#include <iostream>\n#include <sy"
},
{
"path": "src/zk/server/server.hpp",
"chars": 2320,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\n#include <atomic>\n#include <exception>\n#include <memory>\n#include <string>\n#incl"
},
{
"path": "src/zk/server/server_group.cpp",
"chars": 3797,
"preview": "#include \"server_group.hpp\"\n\n#include <algorithm>\n#include <cerrno>\n#include <fstream>\n#include <sstream>\n#include <stde"
},
{
"path": "src/zk/server/server_group.hpp",
"chars": 2148,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\n#include <cstdint>\n#include <map>\n#include <memory>\n#include <string>\n#include <"
},
{
"path": "src/zk/server/server_group_tests.cpp",
"chars": 706,
"preview": "#include <zk/client.hpp>\n#include <zk/tests/test.hpp>\n\n#include <chrono>\n#include <thread>\n\n#include \"configuration.hpp\""
},
{
"path": "src/zk/server/server_tests.cpp",
"chars": 4107,
"preview": "#include <zk/tests/test.hpp>\n#include <zk/client.hpp>\n\n#include <cerrno>\n#include <chrono>\n#include <iostream>\n#include "
},
{
"path": "src/zk/server/server_tests.hpp",
"chars": 990,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n#include <zk/forwards.hpp>\n#include <zk/tests/test.hpp>\n\n#include <memory>\n\nnames"
},
{
"path": "src/zk/string_view.hpp",
"chars": 230,
"preview": "/// \\file\n/// Imports the \\c string_view type as \\c std::string_view.\n#pragma once\n\n#include <zk/config.hpp>\n\n#include <"
},
{
"path": "src/zk/tests/main.cpp",
"chars": 131,
"preview": "#include \"test.hpp\"\n\nint main(int argc, char** argv)\n{\n ::testing::InitGoogleTest(&argc, argv);\n return ::RUN_ALL_"
},
{
"path": "src/zk/tests/test.cpp",
"chars": 20,
"preview": "#include \"test.hpp\"\n"
},
{
"path": "src/zk/tests/test.hpp",
"chars": 3095,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\n#ifdef GTEST_API_\n# error \"GTest was included externally -- you MUST include <"
},
{
"path": "src/zk/types.cpp",
"chars": 7025,
"preview": "#include \"types.hpp\"\n\n#include <ostream>\n#include <sstream>\n#include <utility>\n\nnamespace zk\n{\n\n////////////////////////"
},
{
"path": "src/zk/types.hpp",
"chars": 16580,
"preview": "#pragma once\n\n#include <zk/config.hpp>\n\n#include <chrono>\n#include <iosfwd>\n#include <string>\n\nnamespace zk\n{\n\n/// \\addt"
},
{
"path": "src/zk/types_tests.cpp",
"chars": 1953,
"preview": "#include <zk/tests/test.hpp>\n\n#include \"types.hpp\"\n\nnamespace zk\n{\n\n////////////////////////////////////////////////////"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the tgockel/zookeeper-cpp GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 96 files (367.6 KB), approximately 85.6k tokens, and a symbol index with 581 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.