├── .editorconfig ├── .github └── workflows │ ├── ci.yml │ ├── docs.yml │ └── release.yml ├── .gitignore ├── .jenkins ├── .jenkins.d ├── 00-deps.sh ├── 01-ndn-cxx.sh ├── 09-cleanup.sh ├── 10-build.sh ├── 20-tests.sh ├── 30-coverage.sh ├── 40-headers-check.sh └── README.md ├── .waf-tools ├── boost.py ├── coverage.py ├── default-compiler-flags.py ├── doxygen.py ├── sanitizers.py └── sphinx.py ├── AUTHORS.md ├── COPYING.lesser ├── COPYING.md ├── PSync.pc.in ├── PSync ├── common.hpp ├── consumer.cpp ├── consumer.hpp ├── detail │ ├── access-specifiers.hpp │ ├── bloom-filter.cpp │ ├── bloom-filter.hpp │ ├── iblt.cpp │ ├── iblt.hpp │ ├── state.cpp │ ├── state.hpp │ ├── util.cpp │ └── util.hpp ├── full-producer.cpp ├── full-producer.hpp ├── partial-producer.cpp ├── partial-producer.hpp ├── producer-base.cpp ├── producer-base.hpp ├── segment-publisher.cpp └── segment-publisher.hpp ├── README.md ├── docs ├── conf.py ├── doxygen.conf.in ├── examples.rst ├── index.rst ├── install.rst ├── named_data_theme │ ├── layout.html │ ├── named_data_footer-with-analytics.html.in │ ├── named_data_footer.html │ ├── named_data_header.html │ ├── static │ │ ├── bar-top.png │ │ ├── base.css │ │ ├── base.css_t │ │ ├── bc_s.png │ │ ├── default.css_t │ │ ├── doxygen.css │ │ ├── foundation.css │ │ ├── named_data_doxygen.css │ │ ├── named_data_style.css_t │ │ ├── nav_f.png │ │ └── tab_b.png │ └── theme.conf ├── release-notes.rst ├── release-notes │ ├── release-notes-0.1.0.rst │ ├── release-notes-0.2.0.rst │ ├── release-notes-0.3.0.rst │ ├── release-notes-0.4.0.rst │ └── release-notes-0.5.0.rst ├── releases.rst └── requirements.txt ├── examples ├── README.md ├── consumer.cpp ├── full-sync.cpp ├── producer.cpp └── wscript ├── tests ├── boost-test.hpp ├── clock-fixture.cpp ├── clock-fixture.hpp ├── io-fixture.hpp ├── key-chain-fixture.hpp ├── main.cpp ├── test-bloom-filter.cpp ├── test-consumer.cpp ├── test-full-producer.cpp ├── test-full-sync.cpp ├── test-iblt.cpp ├── test-partial-producer.cpp ├── test-partial-sync.cpp ├── test-producer-base.cpp ├── test-segment-publisher.cpp ├── test-state.cpp ├── test-util.cpp └── wscript ├── waf └── wscript /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | 8 | [*.{cpp,hpp}] 9 | indent_style = space 10 | indent_size = 2 11 | trim_trailing_whitespace = true 12 | 13 | [{*.{py,sh},.jenkins,wscript}] 14 | indent_style = space 15 | indent_size = 4 16 | trim_trailing_whitespace = true 17 | 18 | [*.md] 19 | indent_style = space 20 | trim_trailing_whitespace = false 21 | 22 | [*.rst] 23 | indent_style = space 24 | trim_trailing_whitespace = true 25 | 26 | [*.{yaml,yml}] 27 | indent_style = space 28 | indent_size = 2 29 | trim_trailing_whitespace = true 30 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - '**' 6 | workflow_dispatch: 7 | 8 | permissions: {} 9 | 10 | jobs: 11 | Ubuntu: 12 | uses: named-data/actions/.github/workflows/jenkins-script-ubuntu.yml@v1 13 | macOS: 14 | uses: named-data/actions/.github/workflows/jenkins-script-macos.yml@v1 15 | PPA: 16 | uses: named-data/actions/.github/workflows/ppa.yml@v1 17 | with: 18 | extra-deps: libboost-iostreams-dev 19 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | on: 3 | push: 4 | workflow_dispatch: 5 | inputs: 6 | skip-deploy: 7 | description: 'Skip deployment?' 8 | required: true 9 | type: boolean 10 | 11 | permissions: {} 12 | 13 | jobs: 14 | html: 15 | uses: named-data/actions/.github/workflows/docs-html.yml@v1 16 | with: 17 | # Deploy only the master branch and release tags 18 | deploy: ${{ !inputs.skip-deploy && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/0.')) }} 19 | secrets: inherit 20 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: 5 | - '[0-9]+.[0-9]+*' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | attestations: write 10 | contents: write 11 | id-token: write 12 | 13 | jobs: 14 | release: 15 | uses: named-data/actions/.github/workflows/release.yml@v1 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Backup files 2 | *~ 3 | *.bak 4 | *.orig 5 | *.rej 6 | 7 | # Waf build system 8 | /build/ 9 | .waf-*-*/ 10 | .waf3-*-*/ 11 | .lock-waf* 12 | 13 | # Compiled python code 14 | __pycache__/ 15 | *.py[cod] 16 | 17 | # Emacs 18 | \#*\# 19 | /.emacs.desktop 20 | /.emacs.desktop.lock 21 | *.elc 22 | .\#* 23 | 24 | # Visual Studio Code 25 | .vscode/ 26 | 27 | # macOS 28 | .DS_Store 29 | .AppleDouble 30 | .LSOverride 31 | ._* 32 | 33 | # Other 34 | /VERSION.info 35 | -------------------------------------------------------------------------------- /.jenkins: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | case $(uname) in 5 | Linux) 6 | if [[ -e /etc/os-release ]]; then 7 | source /etc/os-release 8 | else 9 | source /usr/lib/os-release 10 | fi 11 | export ID VERSION_ID 12 | export ID_LIKE="${ID} ${ID_LIKE} linux" 13 | if [[ -z $GITHUB_ACTIONS ]]; then 14 | export PATH="${HOME}/.local/bin${PATH:+:}${PATH}" 15 | fi 16 | ;; 17 | Darwin) 18 | # Emulate a subset of os-release(5) 19 | export ID=macos 20 | export VERSION_ID=$(sw_vers -productVersion) 21 | if [[ -z $GITHUB_ACTIONS ]]; then 22 | export PATH="/usr/local/bin${PATH:+:}${PATH}" 23 | fi 24 | if [[ -x /opt/homebrew/bin/brew ]]; then 25 | eval "$(/opt/homebrew/bin/brew shellenv)" 26 | elif [[ -x /usr/local/bin/brew ]]; then 27 | eval "$(/usr/local/bin/brew shellenv)" 28 | fi 29 | ;; 30 | esac 31 | 32 | export CACHE_DIR=${CACHE_DIR:-/tmp} 33 | 34 | if [[ $JOB_NAME == *code-coverage ]]; then 35 | export DISABLE_ASAN=1 36 | export DISABLE_HEADERS_CHECK=1 37 | fi 38 | 39 | # https://reproducible-builds.org/docs/source-date-epoch/ 40 | export SOURCE_DATE_EPOCH=$(git log -1 --format=%ct) 41 | 42 | for file in .jenkins.d/*; do 43 | [[ -f $file && -x $file ]] || continue 44 | 45 | if [[ -n $GITHUB_ACTIONS ]]; then 46 | label=$(basename "$file" | sed -E 's/[[:digit:]]+-(.*)\..*/\1/') 47 | echo "::group::${label}" 48 | fi 49 | 50 | echo "\$ $file" 51 | "$file" 52 | 53 | if [[ -n $GITHUB_ACTIONS ]]; then 54 | echo "::endgroup::" 55 | fi 56 | done 57 | -------------------------------------------------------------------------------- /.jenkins.d/00-deps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | APT_PKGS=( 5 | dpkg-dev 6 | g++ 7 | libboost-chrono-dev 8 | libboost-date-time-dev 9 | libboost-dev 10 | libboost-iostreams-dev 11 | libboost-log-dev 12 | libboost-program-options-dev 13 | libboost-stacktrace-dev 14 | libboost-test-dev 15 | libboost-thread-dev 16 | libsqlite3-dev 17 | libssl-dev 18 | pkgconf 19 | python3 20 | ) 21 | DNF_PKGS=( 22 | boost-devel 23 | gcc-c++ 24 | libasan 25 | lld 26 | openssl-devel 27 | pkgconf 28 | python3 29 | sqlite-devel 30 | ) 31 | FORMULAE=(boost openssl pkgconf) 32 | case $JOB_NAME in 33 | *code-coverage) 34 | APT_PKGS+=(lcov libjson-xs-perl) 35 | ;; 36 | *Docs) 37 | APT_PKGS+=(doxygen graphviz) 38 | FORMULAE+=(doxygen graphviz) 39 | ;; 40 | esac 41 | 42 | install_uv() { 43 | if [[ -z $GITHUB_ACTIONS && $ID_LIKE == *debian* ]]; then 44 | sudo apt-get install -qy --no-install-recommends pipx 45 | pipx upgrade uv || pipx install uv 46 | fi 47 | } 48 | 49 | set -x 50 | 51 | if [[ $ID == macos ]]; then 52 | export HOMEBREW_COLOR=1 53 | export HOMEBREW_NO_ENV_HINTS=1 54 | if [[ -n $GITHUB_ACTIONS ]]; then 55 | export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 56 | fi 57 | brew update 58 | brew install --formula "${FORMULAE[@]}" 59 | elif [[ $ID_LIKE == *debian* ]]; then 60 | sudo apt-get update -qq 61 | sudo apt-get install -qy --no-install-recommends "${APT_PKGS[@]}" 62 | elif [[ $ID_LIKE == *fedora* ]]; then 63 | sudo dnf install -y "${DNF_PKGS[@]}" 64 | fi 65 | 66 | case $JOB_NAME in 67 | *code-coverage) 68 | install_uv 69 | ;; 70 | *Docs) 71 | install_uv 72 | export FORCE_COLOR=1 73 | export UV_NO_MANAGED_PYTHON=1 74 | uv tool install sphinx --upgrade --with-requirements docs/requirements.txt 75 | ;; 76 | esac 77 | -------------------------------------------------------------------------------- /.jenkins.d/01-ndn-cxx.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -exo pipefail 3 | 4 | pushd "$CACHE_DIR" >/dev/null 5 | 6 | INSTALLED_VERSION= 7 | if [[ $ID == macos ]]; then 8 | BOOST=$(brew list --formula --versions boost) 9 | OLD_BOOST=$(cat boost.txt || :) 10 | if [[ $OLD_BOOST != $BOOST ]]; then 11 | echo "$BOOST" > boost.txt 12 | INSTALLED_VERSION=NONE 13 | fi 14 | fi 15 | 16 | if [[ -z $INSTALLED_VERSION ]]; then 17 | INSTALLED_VERSION=$(git -C ndn-cxx rev-parse HEAD 2>/dev/null || echo NONE) 18 | fi 19 | 20 | sudo rm -rf ndn-cxx-latest 21 | git clone --depth 1 https://github.com/named-data/ndn-cxx.git ndn-cxx-latest 22 | LATEST_VERSION=$(git -C ndn-cxx-latest rev-parse HEAD 2>/dev/null || echo UNKNOWN) 23 | 24 | if [[ $INSTALLED_VERSION != $LATEST_VERSION ]]; then 25 | sudo rm -rf ndn-cxx 26 | mv ndn-cxx-latest ndn-cxx 27 | else 28 | sudo rm -rf ndn-cxx-latest 29 | fi 30 | 31 | sudo rm -f /usr/local/bin/ndnsec* 32 | sudo rm -fr /usr/local/include/ndn-cxx 33 | sudo rm -f /usr/local/lib{,64}/libndn-cxx* 34 | sudo rm -f /usr/local/lib{,64}/pkgconfig/libndn-cxx.pc 35 | 36 | pushd ndn-cxx >/dev/null 37 | 38 | ./waf --color=yes configure 39 | ./waf --color=yes build 40 | sudo ./waf --color=yes install 41 | 42 | popd >/dev/null 43 | popd >/dev/null 44 | 45 | if [[ $ID_LIKE == *linux* ]]; then 46 | if [[ $(uname -m) == x86_64 && -d /usr/lib64 ]]; then 47 | sudo tee /etc/ld.so.conf.d/ndn.conf >/dev/null <<< /usr/local/lib64 48 | fi 49 | sudo ldconfig 50 | fi 51 | -------------------------------------------------------------------------------- /.jenkins.d/09-cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -exo pipefail 3 | 4 | PROJ=PSync 5 | 6 | sudo rm -fr /usr/local/include/"$PROJ" 7 | sudo rm -f /usr/local/lib{,64}/lib"$PROJ"* 8 | sudo rm -f /usr/local/lib{,64}/pkgconfig/{,lib}"$PROJ".pc 9 | -------------------------------------------------------------------------------- /.jenkins.d/10-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | if [[ -z $DISABLE_ASAN ]]; then 5 | ASAN="--with-sanitizer=address" 6 | fi 7 | if [[ $JOB_NAME == *code-coverage ]]; then 8 | COVERAGE="--with-coverage" 9 | fi 10 | if [[ $ID == debian && ${VERSION_ID%%.*} -eq 11 ]]; then 11 | LZMA="--without-lzma" 12 | fi 13 | 14 | set -x 15 | 16 | if [[ $JOB_NAME != *code-coverage && $JOB_NAME != *limited-build ]]; then 17 | # Build in release mode with tests 18 | ./waf --color=yes configure --with-tests 19 | ./waf --color=yes build 20 | 21 | # Cleanup 22 | ./waf --color=yes distclean 23 | 24 | # Build in release mode with examples 25 | ./waf --color=yes configure --with-examples 26 | ./waf --color=yes build 27 | 28 | # Cleanup 29 | ./waf --color=yes distclean 30 | fi 31 | 32 | # Build in debug mode with tests 33 | ./waf --color=yes configure --debug --with-tests $ASAN $COVERAGE $LZMA 34 | ./waf --color=yes build 35 | 36 | # (tests will be run against the debug version) 37 | 38 | # Install 39 | sudo ./waf --color=yes install 40 | 41 | if [[ $ID_LIKE == *linux* ]]; then 42 | if [[ $(uname -m) == x86_64 && -d /usr/lib64 ]]; then 43 | sudo tee /etc/ld.so.conf.d/ndn.conf >/dev/null <<< /usr/local/lib64 44 | fi 45 | sudo ldconfig 46 | fi 47 | -------------------------------------------------------------------------------- /.jenkins.d/20-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | # https://github.com/google/sanitizers/wiki/SanitizerCommonFlags 5 | # https://github.com/google/sanitizers/wiki/AddressSanitizerFlags 6 | ASAN_OPTIONS="color=always" 7 | ASAN_OPTIONS+=":strip_path_prefix=${PWD}/" 8 | ASAN_OPTIONS+=":check_initialization_order=1" 9 | ASAN_OPTIONS+=":detect_stack_use_after_return=1" 10 | ASAN_OPTIONS+=":strict_init_order=1" 11 | ASAN_OPTIONS+=":strict_string_checks=1" 12 | ASAN_OPTIONS+=":detect_invalid_pointer_pairs=2" 13 | export ASAN_OPTIONS 14 | 15 | # https://www.boost.org/doc/libs/release/libs/test/doc/html/boost_test/runtime_config/summary.html 16 | export BOOST_TEST_BUILD_INFO=1 17 | export BOOST_TEST_COLOR_OUTPUT=1 18 | export BOOST_TEST_DETECT_MEMORY_LEAK=0 19 | export BOOST_TEST_LOGGER=HRF,test_suite,stdout:XML,all,build/xunit-log.xml 20 | 21 | set -x 22 | 23 | # Prepare environment 24 | rm -rf ~/.ndn 25 | 26 | # Run unit tests 27 | ./build/unit-tests 28 | -------------------------------------------------------------------------------- /.jenkins.d/30-coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | [[ $JOB_NAME == *code-coverage ]] || exit 0 5 | 6 | export FORCE_COLOR=1 7 | export UV_NO_MANAGED_PYTHON=1 8 | 9 | set -x 10 | 11 | # Generate a detailed HTML report and an XML report in Cobertura format using gcovr 12 | # Note: trailing slashes are important in the paths below. Do not remove them! 13 | uvx --from 'git+https://github.com/gcovr/gcovr@99b82e7' gcovr \ 14 | --decisions \ 15 | --filter PSync/ \ 16 | --exclude-throw-branches \ 17 | --exclude-unreachable-branches \ 18 | --cobertura build/coverage.xml \ 19 | --html-details build/gcovr/ \ 20 | --txt-summary \ 21 | build 22 | 23 | # Generate a detailed HTML report using lcov 24 | lcov \ 25 | --quiet \ 26 | --capture \ 27 | --directory . \ 28 | --include "$PWD/PSync/*" \ 29 | --branch-coverage \ 30 | --rc no_exception_branch=1 \ 31 | --ignore-errors inconsistent,mismatch,mismatch \ 32 | --output-file build/coverage.info 33 | 34 | genhtml \ 35 | --quiet \ 36 | --branch-coverage \ 37 | --demangle-cpp \ 38 | --flat \ 39 | --legend \ 40 | --missed \ 41 | --show-proportion \ 42 | --title "PSync $(cat VERSION.info)" \ 43 | --ignore-errors inconsistent,inconsistent \ 44 | --output-directory build/lcov \ 45 | build/coverage.info 46 | -------------------------------------------------------------------------------- /.jenkins.d/40-headers-check.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | # It's intentional not to use `set -x`, because this script explicitly prints useful information 4 | # and should not run in trace mode. 5 | 6 | PROJ=PSync 7 | PCFILE=PSync 8 | 9 | if [[ -n $DISABLE_HEADERS_CHECK ]]; then 10 | echo 'Skipping headers check.' 11 | exit 0 12 | fi 13 | 14 | if [[ $ID_LIKE == *linux* && -d /usr/local/lib64/pkgconfig ]]; then 15 | export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig 16 | fi 17 | 18 | CXX=${CXX:-g++} 19 | STD=-std=c++17 20 | CXXFLAGS="-O2 -Wall -Wno-unknown-warning-option -Wno-enum-constexpr-conversion -Wno-unused-const-variable $(pkgconf --cflags libndn-cxx $PCFILE)" 21 | INCLUDEDIR="$(pkgconf --variable=includedir $PCFILE)"/$PROJ 22 | 23 | echo "Using: $CXX $STD $CXXFLAGS" 24 | 25 | NCHECKED=0 26 | NERRORS=0 27 | while IFS= read -r -d '' H; do 28 | echo "Checking header ${H#${INCLUDEDIR}/}" 29 | "$CXX" -xc++ $STD $CXXFLAGS -c -o /dev/null "$H" || : $((NERRORS++)) 30 | : $((NCHECKED++)) 31 | done < <(find "$INCLUDEDIR" -name '*.hpp' -type f -print0 2>/dev/null) 32 | 33 | if [[ $NCHECKED -eq 0 ]]; then 34 | echo "No headers found. Is $PROJ installed?" 35 | exit 1 36 | else 37 | echo "$NCHECKED headers checked." 38 | fi 39 | 40 | if [[ $NERRORS -gt 0 ]]; then 41 | echo "$NERRORS headers could not be compiled." 42 | exit 1 43 | fi 44 | -------------------------------------------------------------------------------- /.jenkins.d/README.md: -------------------------------------------------------------------------------- 1 | # Continuous Integration Scripts 2 | 3 | ## Environment Variables 4 | 5 | - `ID`: lower-case string that identifies the operating system, for example: `ID=ubuntu`, 6 | `ID=centos`. See [os-release(5)] for more information. On macOS, where `os-release` is 7 | not available, we emulate it by setting `ID=macos`. 8 | 9 | - `ID_LIKE`: space-separated list of operating system identifiers that are closely related 10 | to the running OS. See [os-release(5)] for more information. The listed values are used 11 | by the CI scripts to select the proper behavior for different platforms and OS flavors. 12 | 13 | Examples: 14 | 15 | - On CentOS, `ID_LIKE="centos rhel fedora linux"` 16 | - On Ubuntu, `ID_LIKE="ubuntu debian linux"` 17 | 18 | - `VERSION_ID`: identifies the operating system version, excluding any release code names. 19 | See [os-release(5)] for more information. Examples: `VERSION_ID=42`, `VERSION_ID=22.04`. 20 | 21 | - `JOB_NAME`: defines the type of the current CI job. Depending on the job type, the CI 22 | scripts can perform different tasks. 23 | 24 | Supported values: 25 | 26 | - empty: default build task 27 | - `code-coverage`: debug build with tests and code coverage analysis 28 | - `limited-build`: only a single debug build with tests 29 | 30 | - `CACHE_DIR`: directory containing cached files from previous builds, e.g., a compiled 31 | version of ndn-cxx. If not set, `/tmp` is used. 32 | 33 | - `DISABLE_ASAN`: disable building with AddressSanitizer. This is automatically set for 34 | the `code-coverage` job type. 35 | 36 | [os-release(5)]: https://www.freedesktop.org/software/systemd/man/os-release.html 37 | -------------------------------------------------------------------------------- /.waf-tools/coverage.py: -------------------------------------------------------------------------------- 1 | from waflib import TaskGen 2 | 3 | def options(opt): 4 | opt.add_option('--with-coverage', action='store_true', default=False, 5 | help='Add compiler flags to enable code coverage information') 6 | 7 | def configure(conf): 8 | if conf.options.with_coverage: 9 | if not conf.options.debug: 10 | conf.fatal('Code coverage flags require debug mode compilation (add --debug)') 11 | conf.check_cxx(cxxflags=['-fprofile-arcs', '-ftest-coverage', '-fPIC'], 12 | linkflags=['-fprofile-arcs'], uselib_store='GCOV', mandatory=True) 13 | 14 | @TaskGen.feature('cxx','cc') 15 | @TaskGen.after('process_source') 16 | def add_coverage(self): 17 | if getattr(self, 'use', ''): 18 | self.use += ' GCOV' 19 | else: 20 | self.use = 'GCOV' 21 | -------------------------------------------------------------------------------- /.waf-tools/doxygen.py: -------------------------------------------------------------------------------- 1 | # Thomas Nagy 2008-2010 (ita) 2 | 3 | """ 4 | Doxygen support 5 | 6 | Variables passed to bld(): 7 | * doxyfile -- the Doxyfile to use 8 | 9 | When using this tool, the wscript will look like: 10 | 11 | def options(opt): 12 | opt.load('doxygen') 13 | 14 | def configure(conf): 15 | conf.load('doxygen') 16 | # check conf.env.DOXYGEN, if it is mandatory 17 | 18 | def build(bld): 19 | if bld.env.DOXYGEN: 20 | bld(features="doxygen", doxyfile='Doxyfile', ...) 21 | 22 | def doxygen(bld): 23 | if bld.env.DOXYGEN: 24 | bld(features="doxygen", doxyfile='Doxyfile', ...) 25 | """ 26 | 27 | import os, os.path, re 28 | from waflib import Task, Utils, Node 29 | from waflib.TaskGen import feature 30 | 31 | DOXY_STR = '"${DOXYGEN}" - ' 32 | DOXY_FMTS = 'html latex man rft xml'.split() 33 | DOXY_FILE_PATTERNS = '*.' + ' *.'.join(''' 34 | c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3 35 | inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx 36 | '''.split()) 37 | 38 | re_rl = re.compile('\\\\\r*\n', re.MULTILINE) 39 | re_nl = re.compile('\r*\n', re.M) 40 | def parse_doxy(txt): 41 | tbl = {} 42 | txt = re_rl.sub('', txt) 43 | lines = re_nl.split(txt) 44 | for x in lines: 45 | x = x.strip() 46 | if not x or x.startswith('#') or x.find('=') < 0: 47 | continue 48 | if x.find('+=') >= 0: 49 | tmp = x.split('+=') 50 | key = tmp[0].strip() 51 | if key in tbl: 52 | tbl[key] += ' ' + '+='.join(tmp[1:]).strip() 53 | else: 54 | tbl[key] = '+='.join(tmp[1:]).strip() 55 | else: 56 | tmp = x.split('=') 57 | tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip() 58 | return tbl 59 | 60 | class doxygen(Task.Task): 61 | vars = ['DOXYGEN', 'DOXYFLAGS'] 62 | color = 'BLUE' 63 | 64 | def runnable_status(self): 65 | ''' 66 | self.pars are populated in runnable_status - because this function is being 67 | run *before* both self.pars "consumers" - scan() and run() 68 | 69 | set output_dir (node) for the output 70 | ''' 71 | 72 | for x in self.run_after: 73 | if not x.hasrun: 74 | return Task.ASK_LATER 75 | 76 | if not getattr(self, 'pars', None): 77 | txt = self.inputs[0].read() 78 | self.pars = parse_doxy(txt) 79 | if not self.pars.get('OUTPUT_DIRECTORY'): 80 | self.pars['OUTPUT_DIRECTORY'] = self.inputs[0].parent.get_bld().abspath() 81 | 82 | # Override with any parameters passed to the task generator 83 | if getattr(self.generator, 'pars', None): 84 | for k, v in self.generator.pars.items(): 85 | self.pars[k] = v 86 | 87 | self.doxy_inputs = getattr(self, 'doxy_inputs', []) 88 | if not self.pars.get('INPUT'): 89 | self.doxy_inputs.append(self.inputs[0].parent) 90 | else: 91 | for i in self.pars.get('INPUT').split(): 92 | if os.path.isabs(i): 93 | node = self.generator.bld.root.find_node(i) 94 | else: 95 | node = self.generator.path.find_node(i) 96 | if not node: 97 | self.generator.bld.fatal('Could not find the doxygen input %r' % i) 98 | self.doxy_inputs.append(node) 99 | 100 | if not getattr(self, 'output_dir', None): 101 | bld = self.generator.bld 102 | # First try to find an absolute path, then find or declare a relative path 103 | self.output_dir = bld.root.find_dir(self.pars['OUTPUT_DIRECTORY']) 104 | if not self.output_dir: 105 | self.output_dir = bld.path.find_or_declare(self.pars['OUTPUT_DIRECTORY']) 106 | 107 | self.signature() 108 | return Task.Task.runnable_status(self) 109 | 110 | def scan(self): 111 | exclude_patterns = self.pars.get('EXCLUDE_PATTERNS','').split() 112 | file_patterns = self.pars.get('FILE_PATTERNS','').split() 113 | if not file_patterns: 114 | file_patterns = DOXY_FILE_PATTERNS 115 | if self.pars.get('RECURSIVE') == 'YES': 116 | file_patterns = ["**/%s" % pattern for pattern in file_patterns] 117 | nodes = [] 118 | names = [] 119 | for node in self.doxy_inputs: 120 | if os.path.isdir(node.abspath()): 121 | for m in node.ant_glob(incl=file_patterns, excl=exclude_patterns): 122 | nodes.append(m) 123 | else: 124 | nodes.append(node) 125 | return (nodes, names) 126 | 127 | def run(self): 128 | dct = self.pars.copy() 129 | dct['INPUT'] = ' '.join(['"%s"' % x.abspath() for x in self.doxy_inputs]) 130 | code = '\n'.join(['%s = %s' % (x, dct[x]) for x in self.pars]) 131 | code = code.encode() # for python 3 132 | #fmt = DOXY_STR % (self.inputs[0].parent.abspath()) 133 | cmd = Utils.subst_vars(DOXY_STR, self.env) 134 | env = self.env.env or None 135 | proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE, env=env, cwd=self.generator.bld.path.get_bld().abspath()) 136 | proc.communicate(code) 137 | return proc.returncode 138 | 139 | def post_run(self): 140 | nodes = self.output_dir.ant_glob('**/*', quiet=True) 141 | for x in nodes: 142 | x.sig = Utils.h_file(x.abspath()) 143 | self.outputs += nodes 144 | return Task.Task.post_run(self) 145 | 146 | class tar(Task.Task): 147 | "quick tar creation" 148 | run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}' 149 | color = 'RED' 150 | after = ['doxygen'] 151 | def runnable_status(self): 152 | for x in getattr(self, 'input_tasks', []): 153 | if not x.hasrun: 154 | return Task.ASK_LATER 155 | 156 | if not getattr(self, 'tar_done_adding', None): 157 | # execute this only once 158 | self.tar_done_adding = True 159 | for x in getattr(self, 'input_tasks', []): 160 | self.set_inputs(x.outputs) 161 | if not self.inputs: 162 | return Task.SKIP_ME 163 | return Task.Task.runnable_status(self) 164 | 165 | def __str__(self): 166 | tgt_str = ' '.join([a.nice_path(self.env) for a in self.outputs]) 167 | return '%s: %s\n' % (self.__class__.__name__, tgt_str) 168 | 169 | @feature('doxygen') 170 | def process_doxy(self): 171 | if not getattr(self, 'doxyfile', None): 172 | self.generator.bld.fatal('no doxyfile??') 173 | 174 | node = self.doxyfile 175 | if not isinstance(node, Node.Node): 176 | node = self.path.find_resource(node) 177 | if not node: 178 | raise ValueError('doxygen file not found') 179 | 180 | # the task instance 181 | dsk = self.create_task('doxygen', node) 182 | 183 | if getattr(self, 'doxy_tar', None): 184 | tsk = self.create_task('tar') 185 | tsk.input_tasks = [dsk] 186 | tsk.set_outputs(self.path.find_or_declare(self.doxy_tar)) 187 | if self.doxy_tar.endswith('bz2'): 188 | tsk.env['TAROPTS'] = ['cjf'] 189 | elif self.doxy_tar.endswith('gz'): 190 | tsk.env['TAROPTS'] = ['czf'] 191 | else: 192 | tsk.env['TAROPTS'] = ['cf'] 193 | 194 | def configure(conf): 195 | ''' 196 | Check if doxygen and tar commands are present in the system 197 | 198 | If the commands are present, then conf.env.DOXYGEN and conf.env.TAR 199 | variables will be set. Detection can be controlled by setting DOXYGEN and 200 | TAR environmental variables. 201 | ''' 202 | 203 | conf.find_program('doxygen', var='DOXYGEN', mandatory=False) 204 | conf.find_program('tar', var='TAR', mandatory=False) 205 | 206 | # doxygen command 207 | from waflib.Build import BuildContext 208 | class doxy(BuildContext): 209 | cmd = 'doxygen' 210 | fun = 'doxygen' 211 | -------------------------------------------------------------------------------- /.waf-tools/sanitizers.py: -------------------------------------------------------------------------------- 1 | # Davide Pesavento (LIP6), 2016 2 | 3 | def options(opt): 4 | opt.add_option('--with-sanitizer', action='store', default='', dest='sanitizers', 5 | help='Comma-separated list of compiler sanitizers to enable [default=none]') 6 | 7 | def configure(conf): 8 | for san in conf.options.sanitizers.split(','): 9 | if not san: 10 | continue 11 | 12 | sanflag = '-fsanitize=%s' % san 13 | conf.start_msg('Checking if compiler supports %s' % sanflag) 14 | 15 | if conf.check_cxx(cxxflags=['-Werror', sanflag, '-fno-omit-frame-pointer'], 16 | linkflags=[sanflag], mandatory=False): 17 | conf.end_msg('yes') 18 | conf.env.append_unique('CXXFLAGS', [sanflag, '-fno-omit-frame-pointer']) 19 | conf.env.append_unique('LINKFLAGS', [sanflag]) 20 | else: 21 | conf.end_msg('no', color='RED') 22 | conf.fatal('%s sanitizer is not supported by the current compiler' % san) 23 | -------------------------------------------------------------------------------- /.waf-tools/sphinx.py: -------------------------------------------------------------------------------- 1 | # inspired by code by Hans-Martin von Gaudecker, 2012 2 | 3 | """Support for Sphinx documentation""" 4 | 5 | import os 6 | from waflib import Task, TaskGen 7 | 8 | 9 | class sphinx_build(Task.Task): 10 | color = 'BLUE' 11 | run_str = '${SPHINX_BUILD} -q -b ${BUILDERNAME} -D ${VERSION} -D ${RELEASE} -d ${DOCTREEDIR} ${SRCDIR} ${OUTDIR}' 12 | 13 | def keyword(self): 14 | return f'Processing ({self.env.BUILDERNAME})' 15 | 16 | 17 | # from https://docs.python.org/3.12/whatsnew/3.12.html#imp 18 | def load_source(modname, filename): 19 | import importlib.util 20 | from importlib.machinery import SourceFileLoader 21 | loader = SourceFileLoader(modname, filename) 22 | spec = importlib.util.spec_from_file_location(modname, filename, loader=loader) 23 | module = importlib.util.module_from_spec(spec) 24 | loader.exec_module(module) 25 | return module 26 | 27 | 28 | @TaskGen.feature('sphinx') 29 | @TaskGen.before_method('process_source') 30 | def process_sphinx(self): 31 | """Set up the task generator with a Sphinx instance and create a task.""" 32 | 33 | conf = self.path.find_node(self.config) 34 | if not conf: 35 | self.bld.fatal(f'Sphinx configuration file {repr(self.config)} not found') 36 | 37 | inputs = [conf] + self.to_nodes(self.source) 38 | task = self.create_task('sphinx_build', inputs, always_run=getattr(self, 'always', False)) 39 | 40 | confdir = conf.parent.abspath() 41 | buildername = getattr(self, 'builder', 'html') 42 | srcdir = getattr(self, 'srcdir', confdir) 43 | outdir = self.path.find_or_declare(getattr(self, 'outdir', buildername)).get_bld() 44 | doctreedir = getattr(self, 'doctreedir', os.path.join(outdir.abspath(), '.doctrees')) 45 | release = getattr(self, 'release', self.version) 46 | 47 | task.env['BUILDERNAME'] = buildername 48 | task.env['SRCDIR'] = srcdir 49 | task.env['OUTDIR'] = outdir.abspath() 50 | task.env['DOCTREEDIR'] = doctreedir 51 | task.env['VERSION'] = f'version={self.version}' 52 | task.env['RELEASE'] = f'release={release}' 53 | 54 | if buildername == 'man': 55 | confdata = load_source('sphinx_conf', conf.abspath()) 56 | for i in confdata.man_pages: 57 | target = outdir.find_or_declare(f'{i[1]}.{i[4]}') 58 | task.outputs.append(target) 59 | if self.install_path: 60 | self.bld.install_files(f'{self.install_path}/man{i[4]}/', target) 61 | else: 62 | task.outputs.append(outdir) 63 | 64 | # prevent process_source from complaining that there is no extension mapping for .rst files 65 | self.source = [] 66 | 67 | 68 | def configure(conf): 69 | """Check if sphinx-build program is available.""" 70 | conf.find_program('sphinx-build', var='SPHINX_BUILD', mandatory=False) 71 | 72 | 73 | # sphinx command 74 | from waflib.Build import BuildContext 75 | class sphinx(BuildContext): 76 | cmd = 'sphinx' 77 | fun = 'sphinx' 78 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # PSync Authors 2 | 3 | The following lists maintainers, primary developers, and all much-appreciated contributors to PSync in alphabetical order. 4 | The specific contributions of individual authors can be obtained from the git history of the [official PSync repository](https://github.com/named-data/PSync). 5 | If you would like to become a contributor to the official repository, please follow the recommendations in . 6 | 7 | * Alexander Afanasyev 8 | * ***(Former Maintainer)*** Saurab Dulal 9 | * ***(Former Maintainer)*** Ashlesh Gawande 10 | * Dylan Hensley 11 | * Alexander Lane 12 | * Eric Newberry 13 | * Davide Pesavento 14 | * Junxiao Shi 15 | * Minsheng Zhang 16 | 17 | ## Technical Advisors 18 | 19 | * Lan Wang 20 | -------------------------------------------------------------------------------- /COPYING.lesser: -------------------------------------------------------------------------------- 1 | # GNU LESSER GENERAL PUBLIC LICENSE 2 | 3 | Version 3, 29 June 2007 4 | 5 | Copyright (C) 2007 Free Software Foundation, Inc. 6 | 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this 9 | license document, but changing it is not allowed. 10 | 11 | This version of the GNU Lesser General Public License incorporates the 12 | terms and conditions of version 3 of the GNU General Public License, 13 | supplemented by the additional permissions listed below. 14 | 15 | ## 0. Additional Definitions. 16 | 17 | As used herein, "this License" refers to version 3 of the GNU Lesser 18 | General Public License, and the "GNU GPL" refers to version 3 of the 19 | GNU General Public License. 20 | 21 | "The Library" refers to a covered work governed by this License, other 22 | than an Application or a Combined Work as defined below. 23 | 24 | An "Application" is any work that makes use of an interface provided 25 | by the Library, but which is not otherwise based on the Library. 26 | Defining a subclass of a class defined by the Library is deemed a mode 27 | of using an interface provided by the Library. 28 | 29 | A "Combined Work" is a work produced by combining or linking an 30 | Application with the Library. The particular version of the Library 31 | with which the Combined Work was made is also called the "Linked 32 | Version". 33 | 34 | The "Minimal Corresponding Source" for a Combined Work means the 35 | Corresponding Source for the Combined Work, excluding any source code 36 | for portions of the Combined Work that, considered in isolation, are 37 | based on the Application, and not on the Linked Version. 38 | 39 | The "Corresponding Application Code" for a Combined Work means the 40 | object code and/or source code for the Application, including any data 41 | and utility programs needed for reproducing the Combined Work from the 42 | Application, but excluding the System Libraries of the Combined Work. 43 | 44 | ## 1. Exception to Section 3 of the GNU GPL. 45 | 46 | You may convey a covered work under sections 3 and 4 of this License 47 | without being bound by section 3 of the GNU GPL. 48 | 49 | ## 2. Conveying Modified Versions. 50 | 51 | If you modify a copy of the Library, and, in your modifications, a 52 | facility refers to a function or data to be supplied by an Application 53 | that uses the facility (other than as an argument passed when the 54 | facility is invoked), then you may convey a copy of the modified 55 | version: 56 | 57 | - a) under this License, provided that you make a good faith effort 58 | to ensure that, in the event an Application does not supply the 59 | function or data, the facility still operates, and performs 60 | whatever part of its purpose remains meaningful, or 61 | - b) under the GNU GPL, with none of the additional permissions of 62 | this License applicable to that copy. 63 | 64 | ## 3. Object Code Incorporating Material from Library Header Files. 65 | 66 | The object code form of an Application may incorporate material from a 67 | header file that is part of the Library. You may convey such object 68 | code under terms of your choice, provided that, if the incorporated 69 | material is not limited to numerical parameters, data structure 70 | layouts and accessors, or small macros, inline functions and templates 71 | (ten or fewer lines in length), you do both of the following: 72 | 73 | - a) Give prominent notice with each copy of the object code that 74 | the Library is used in it and that the Library and its use are 75 | covered by this License. 76 | - b) Accompany the object code with a copy of the GNU GPL and this 77 | license document. 78 | 79 | ## 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, taken 82 | together, effectively do not restrict modification of the portions of 83 | the Library contained in the Combined Work and reverse engineering for 84 | debugging such modifications, if you also do each of the following: 85 | 86 | - a) Give prominent notice with each copy of the Combined Work that 87 | the Library is used in it and that the Library and its use are 88 | covered by this License. 89 | - b) Accompany the Combined Work with a copy of the GNU GPL and this 90 | license document. 91 | - c) For a Combined Work that displays copyright notices during 92 | execution, include the copyright notice for the Library among 93 | these notices, as well as a reference directing the user to the 94 | copies of the GNU GPL and this license document. 95 | - d) Do one of the following: 96 | - 0) Convey the Minimal Corresponding Source under the terms of 97 | this License, and the Corresponding Application Code in a form 98 | suitable for, and under terms that permit, the user to 99 | recombine or relink the Application with a modified version of 100 | the Linked Version to produce a modified Combined Work, in the 101 | manner specified by section 6 of the GNU GPL for conveying 102 | Corresponding Source. 103 | - 1) Use a suitable shared library mechanism for linking with 104 | the Library. A suitable mechanism is one that (a) uses at run 105 | time a copy of the Library already present on the user's 106 | computer system, and (b) will operate properly with a modified 107 | version of the Library that is interface-compatible with the 108 | Linked Version. 109 | - e) Provide Installation Information, but only if you would 110 | otherwise be required to provide such information under section 6 111 | of the GNU GPL, and only to the extent that such information is 112 | necessary to install and execute a modified version of the 113 | Combined Work produced by recombining or relinking the Application 114 | with a modified version of the Linked Version. (If you use option 115 | 4d0, the Installation Information must accompany the Minimal 116 | Corresponding Source and Corresponding Application Code. If you 117 | use option 4d1, you must provide the Installation Information in 118 | the manner specified by section 6 of the GNU GPL for conveying 119 | Corresponding Source.) 120 | 121 | ## 5. Combined Libraries. 122 | 123 | You may place library facilities that are a work based on the Library 124 | side by side in a single library together with other library 125 | facilities that are not Applications and are not covered by this 126 | License, and convey such a combined library under terms of your 127 | choice, if you do both of the following: 128 | 129 | - a) Accompany the combined library with a copy of the same work 130 | based on the Library, uncombined with any other library 131 | facilities, conveyed under the terms of this License. 132 | - b) Give prominent notice with the combined library that part of it 133 | is a work based on the Library, and explaining where to find the 134 | accompanying uncombined form of the same work. 135 | 136 | ## 6. Revised Versions of the GNU Lesser General Public License. 137 | 138 | The Free Software Foundation may publish revised and/or new versions 139 | of the GNU Lesser General Public License from time to time. Such new 140 | versions will be similar in spirit to the present version, but may 141 | differ in detail to address new problems or concerns. 142 | 143 | Each version is given a distinguishing version number. If the Library 144 | as you received it specifies that a certain numbered version of the 145 | GNU Lesser General Public License "or any later version" applies to 146 | it, you have the option of following the terms and conditions either 147 | of that published version or of any later version published by the 148 | Free Software Foundation. If the Library as you received it does not 149 | specify a version number of the GNU Lesser General Public License, you 150 | may choose any version of the GNU Lesser General Public License ever 151 | published by the Free Software Foundation. 152 | 153 | If the Library as you received it specifies that a proxy can decide 154 | whether future versions of the GNU Lesser General Public License shall 155 | apply, that proxy's public statement of acceptance of any version is 156 | permanent authorization for you to choose that version for the 157 | Library. 158 | -------------------------------------------------------------------------------- /PSync.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@PREFIX@ 2 | libdir=@LIBDIR@ 3 | includedir=@INCLUDEDIR@ 4 | 5 | Name: PSync 6 | Description: NDN PSync library 7 | Version: @VERSION@ 8 | Libs: -L${libdir} -lPSync 9 | Cflags: -I${includedir} 10 | -------------------------------------------------------------------------------- /PSync/common.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #ifndef PSYNC_COMMON_HPP 21 | #define PSYNC_COMMON_HPP 22 | 23 | #include "PSync/detail/config.hpp" 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | namespace psync { 35 | 36 | using namespace ndn::time_literals; 37 | 38 | inline constexpr ndn::time::milliseconds HELLO_INTEREST_LIFETIME = 1_s; 39 | inline constexpr ndn::time::milliseconds HELLO_REPLY_FRESHNESS = 1_s; 40 | inline constexpr ndn::time::milliseconds SYNC_INTEREST_LIFETIME = 1_s; 41 | inline constexpr ndn::time::milliseconds SYNC_REPLY_FRESHNESS = 1_s; 42 | 43 | enum class CompressionScheme { 44 | NONE, 45 | ZLIB, 46 | GZIP, 47 | BZIP2, 48 | LZMA, 49 | ZSTD, 50 | #ifdef PSYNC_HAVE_ZLIB 51 | DEFAULT = ZLIB 52 | #else 53 | DEFAULT = NONE 54 | #endif 55 | }; 56 | 57 | class CompressionError : public std::runtime_error 58 | { 59 | public: 60 | using std::runtime_error::runtime_error; 61 | }; 62 | 63 | struct MissingDataInfo 64 | { 65 | ndn::Name prefix; 66 | uint64_t lowSeq; 67 | uint64_t highSeq; 68 | uint64_t incomingFace; 69 | }; 70 | 71 | using UpdateCallback = std::function&)>; 72 | 73 | } // namespace psync 74 | 75 | #endif // PSYNC_COMMON_HPP 76 | -------------------------------------------------------------------------------- /PSync/consumer.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #ifndef PSYNC_CONSUMER_HPP 21 | #define PSYNC_CONSUMER_HPP 22 | 23 | #include "PSync/common.hpp" 24 | #include "PSync/detail/access-specifiers.hpp" 25 | #include "PSync/detail/bloom-filter.hpp" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | 34 | namespace psync { 35 | 36 | using ReceiveHelloCallback = std::function&)>; 37 | 38 | /** 39 | * @brief Consumer logic to subscribe to producer's data 40 | * 41 | * Application needs to call sendHelloInterest to get the subscription list 42 | * in psync::ReceiveHelloCallback. It can then add the desired names using addSubscription. 43 | * Finally application will call sendSyncInterest. If the application adds something 44 | * later to the subscription list then it may call sendSyncInterest again for 45 | * sending the next sync interest with updated IBF immediately to reduce any delay in sync data. 46 | * Whenever there is new data UpdateCallback will be called to notify the application. 47 | * 48 | * If consumer wakes up after a long time to sync, producer may not decode the differences 49 | * with its old IBF successfully and send an application nack. Upon receiving the nack, 50 | * consumer will send a hello again and inform the application via psync::ReceiveHelloCallback 51 | * and psync::UpdateCallback. 52 | * 53 | * Currently, fetching of the data needs to be handled by the application. 54 | */ 55 | class Consumer 56 | { 57 | public: 58 | /** 59 | * @brief Constructor options. 60 | */ 61 | struct Options 62 | { 63 | /// Callback to give hello data back to application. 64 | ReceiveHelloCallback onHelloData = [] (const auto&) {}; 65 | /// Callback to give sync data back to application. 66 | UpdateCallback onUpdate = [] (const auto&) {}; 67 | /// Number of expected elements (subscriptions) in Bloom filter. 68 | uint32_t bfCount = 6; 69 | /// Bloom filter false positive probability. 70 | double bfFalsePositive = 0.001; 71 | /// Lifetime of hello Interest. 72 | ndn::time::milliseconds helloInterestLifetime = HELLO_INTEREST_LIFETIME; 73 | /// Lifetime of sync Interest. 74 | ndn::time::milliseconds syncInterestLifetime = SYNC_INTEREST_LIFETIME; 75 | }; 76 | 77 | /** 78 | * @brief Constructor. 79 | * 80 | * @param face Application face. 81 | * @param syncPrefix Prefix to send hello and sync Interests to producer. 82 | * @param opts Options. 83 | */ 84 | Consumer(ndn::Face& face, const ndn::Name& syncPrefix, const Options& opts); 85 | 86 | [[deprecated]] 87 | Consumer(const ndn::Name& syncPrefix, 88 | ndn::Face& face, 89 | const ReceiveHelloCallback& onReceiveHelloData, 90 | const UpdateCallback& onUpdate, 91 | unsigned int count, 92 | double falsePositive = 0.001, 93 | ndn::time::milliseconds helloInterestLifetime = HELLO_INTEREST_LIFETIME, 94 | ndn::time::milliseconds syncInterestLifetime = SYNC_INTEREST_LIFETIME); 95 | 96 | /** 97 | * @brief send hello interest //hello/ 98 | * 99 | * Should be called by the application whenever it wants to send a hello 100 | */ 101 | void 102 | sendHelloInterest(); 103 | 104 | /** 105 | * @brief send sync interest //sync/\/\ 106 | * 107 | * Should be called after subscription list is set or updated 108 | */ 109 | void 110 | sendSyncInterest(); 111 | 112 | /** 113 | * @brief Add prefix to subscription list 114 | * 115 | * @param prefix prefix to be added to the list 116 | * @param seqNo the latest sequence number for the prefix received in HelloData callback 117 | * @param callSyncDataCb true by default to let app know that a new sequence number is available. 118 | * Usually sequence number is zero in hello data, but when it is not Consumer can 119 | * notify the app. Since the app is aware of the latest sequence number by 120 | * ReceiveHelloCallback, app may choose to not let Consumer call UpdateCallback 121 | * by setting this to false. 122 | * @return true if prefix is added, false if it is already present 123 | */ 124 | bool 125 | addSubscription(const ndn::Name& prefix, uint64_t seqNo, bool callSyncDataCb = true); 126 | 127 | /** 128 | * @brief Remove prefix from subscription list 129 | * 130 | * @param prefix prefix to be removed from the list 131 | * @return true if prefix is removed, false if it is not present 132 | */ 133 | bool 134 | removeSubscription(const ndn::Name& prefix); 135 | 136 | std::set 137 | getSubscriptionList() const 138 | { 139 | return m_subscriptionList; 140 | } 141 | 142 | bool 143 | isSubscribed(const ndn::Name& prefix) const 144 | { 145 | return m_subscriptionList.find(prefix) != m_subscriptionList.end(); 146 | } 147 | 148 | std::optional 149 | getSeqNo(const ndn::Name& prefix) const 150 | { 151 | auto it = m_prefixes.find(prefix); 152 | if (it == m_prefixes.end()) { 153 | return std::nullopt; 154 | } 155 | return it->second; 156 | } 157 | 158 | /** 159 | * @brief Stop segment fetcher to stop the sync and free resources 160 | */ 161 | void 162 | stop(); 163 | 164 | private: 165 | /** 166 | * @brief Get hello data from the producer 167 | * 168 | * Format: //hello/\/\ 169 | * Data content is all the prefixes the producer has. 170 | * We store the producer's IBF to be used in sending sync interest 171 | * 172 | * m_onReceiveHelloData is called to let the application know 173 | * so that it can set the subscription list using addSubscription 174 | * 175 | * @param bufferPtr hello data content 176 | */ 177 | void 178 | onHelloData(const ndn::ConstBufferPtr& bufferPtr); 179 | 180 | /** 181 | * @brief Get hello data from the producer 182 | * 183 | * Format: /sync/\/\/\ 184 | * Data content is all the prefixes the producer thinks the consumer doesn't have 185 | * have the latest update for. We update our copy of producer's IBF with the latest one. 186 | * Then we send another sync interest after a random jitter. 187 | * 188 | * @param bufferPtr sync data content 189 | */ 190 | void 191 | onSyncData(const ndn::ConstBufferPtr& bufferPtr); 192 | 193 | PSYNC_PUBLIC_WITH_TESTS_ELSE_PRIVATE: 194 | ndn::Face& m_face; 195 | ndn::Scheduler m_scheduler; 196 | 197 | ndn::Name m_syncPrefix; 198 | ndn::Name m_helloInterestPrefix; 199 | ndn::Name m_syncInterestPrefix; 200 | ndn::Name m_iblt; 201 | ndn::Name m_helloDataName; 202 | ndn::Name m_syncDataName; 203 | uint32_t m_syncDataContentType; 204 | 205 | ReceiveHelloCallback m_onReceiveHelloData; 206 | 207 | // Called when new sync update is received from producer. 208 | UpdateCallback m_onUpdate; 209 | 210 | // Bloom filter is used to store application/user's subscription list. 211 | detail::BloomFilter m_bloomFilter; 212 | 213 | ndn::time::milliseconds m_helloInterestLifetime; 214 | ndn::time::milliseconds m_syncInterestLifetime; 215 | 216 | // Store sequence number for the prefix. 217 | std::map m_prefixes; 218 | std::set m_subscriptionList; 219 | 220 | ndn::random::RandomNumberEngine& m_rng; 221 | std::uniform_int_distribution<> m_rangeUniformRandom; 222 | std::shared_ptr m_helloFetcher; 223 | std::shared_ptr m_syncFetcher; 224 | }; 225 | 226 | } // namespace psync 227 | 228 | #endif // PSYNC_CONSUMER_HPP 229 | -------------------------------------------------------------------------------- /PSync/detail/access-specifiers.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2020, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see 18 | * 19 | **/ 20 | 21 | #ifndef PSYNC_DETAIL_ACCESS_SPECIFIERS_HPP 22 | #define PSYNC_DETAIL_ACCESS_SPECIFIERS_HPP 23 | 24 | #include "PSync/detail/config.hpp" 25 | 26 | #ifdef PSYNC_WITH_TESTS 27 | #define PSYNC_VIRTUAL_WITH_TESTS virtual 28 | #define PSYNC_PUBLIC_WITH_TESTS_ELSE_PROTECTED public 29 | #define PSYNC_PUBLIC_WITH_TESTS_ELSE_PRIVATE public 30 | #define PSYNC_PROTECTED_WITH_TESTS_ELSE_PRIVATE protected 31 | #else 32 | #define PSYNC_VIRTUAL_WITH_TESTS 33 | #define PSYNC_PUBLIC_WITH_TESTS_ELSE_PROTECTED protected 34 | #define PSYNC_PUBLIC_WITH_TESTS_ELSE_PRIVATE private 35 | #define PSYNC_PROTECTED_WITH_TESTS_ELSE_PRIVATE private 36 | #endif 37 | 38 | #endif // PSYNC_DETAIL_ACCESS_SPECIFIERS_HPP 39 | -------------------------------------------------------------------------------- /PSync/detail/bloom-filter.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | * 19 | 20 | * This file incorporates work covered by the following copyright and 21 | * permission notice: 22 | 23 | * The MIT License (MIT) 24 | 25 | * Copyright (c) 2000 Arash Partow 26 | 27 | * Permission is hereby granted, free of charge, to any person obtaining a copy 28 | * of this software and associated documentation files (the "Software"), to deal 29 | * in the Software without restriction, including without limitation the rights 30 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 31 | * copies of the Software, and to permit persons to whom the Software is 32 | * furnished to do so, subject to the following conditions: 33 | 34 | * The above copyright notice and this permission notice shall be included in all 35 | * copies or substantial portions of the Software. 36 | 37 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 39 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 43 | * SOFTWARE. 44 | */ 45 | 46 | #ifndef PSYNC_DETAIL_BLOOM_FILTER_HPP 47 | #define PSYNC_DETAIL_BLOOM_FILTER_HPP 48 | 49 | #include 50 | #include 51 | 52 | #include 53 | #include 54 | 55 | namespace psync::detail { 56 | 57 | class bloom_parameters; 58 | 59 | class BloomFilter 60 | { 61 | public: 62 | class Error : public std::runtime_error 63 | { 64 | public: 65 | using std::runtime_error::runtime_error; 66 | }; 67 | 68 | BloomFilter() = default; 69 | 70 | BloomFilter(unsigned int projected_element_count, 71 | double false_positive_probability); 72 | 73 | BloomFilter(unsigned int projected_element_count, 74 | double false_positive_probability, 75 | const ndn::name::Component& bfName); 76 | 77 | /** 78 | * @brief Append our bloom filter to the given name 79 | * 80 | * Append the count and false positive probability 81 | * along with the bloom filter so that producer (PartialProducer) can construct a copy. 82 | * 83 | * @param name append bloom filter to this name 84 | */ 85 | void 86 | appendToName(ndn::Name& name) const; 87 | 88 | void 89 | clear(); 90 | 91 | void 92 | insert(const ndn::Name& key); 93 | 94 | bool 95 | contains(const ndn::Name& key) const; 96 | 97 | private: 98 | typedef uint32_t bloom_type; 99 | typedef uint8_t cell_type; 100 | 101 | explicit 102 | BloomFilter(const bloom_parameters& p); 103 | 104 | void 105 | compute_indices(const bloom_type& hash, std::size_t& bit_index, std::size_t& bit) const; 106 | 107 | void 108 | generate_unique_salt(); 109 | 110 | private: // non-member operators 111 | // NOTE: the following "hidden friend" operators are available via 112 | // argument-dependent lookup only and must be defined inline. 113 | 114 | friend bool 115 | operator==(const BloomFilter& lhs, const BloomFilter& rhs) 116 | { 117 | return lhs.bit_table_ == rhs.bit_table_; 118 | } 119 | 120 | friend bool 121 | operator!=(const BloomFilter& lhs, const BloomFilter& rhs) 122 | { 123 | return lhs.bit_table_ != rhs.bit_table_; 124 | } 125 | 126 | friend std::ostream& 127 | operator<<(std::ostream& os, const BloomFilter& bf) 128 | { 129 | ndn::printHex(os, bf.bit_table_, false); 130 | return os; 131 | } 132 | 133 | private: 134 | std::vector salt_; 135 | std::vector bit_table_; 136 | unsigned int salt_count_ = 0; 137 | unsigned int table_size_ = 0; 138 | unsigned int projected_element_count_ = 0; 139 | unsigned int inserted_element_count_ = 0; 140 | unsigned long long int random_seed_ = 0; 141 | double desired_false_positive_probability_ = 0.0; 142 | }; 143 | 144 | } // namespace psync::detail 145 | 146 | #endif // PSYNC_DETAIL_BLOOM_FILTER_HPP 147 | -------------------------------------------------------------------------------- /PSync/detail/iblt.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | * 19 | 20 | * This file incorporates work covered by the following copyright and 21 | * permission notice: 22 | 23 | * The MIT License (MIT) 24 | 25 | * Copyright (c) 2014 Gavin Andresen 26 | 27 | * Permission is hereby granted, free of charge, to any person obtaining a copy 28 | * of this software and associated documentation files (the "Software"), to deal 29 | * in the Software without restriction, including without limitation the rights 30 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 31 | * copies of the Software, and to permit persons to whom the Software is 32 | * furnished to do so, subject to the following conditions: 33 | 34 | * The above copyright notice and this permission notice shall be included in all 35 | * copies or substantial portions of the Software. 36 | 37 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 39 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 43 | * SOFTWARE. 44 | */ 45 | 46 | #include "PSync/detail/iblt.hpp" 47 | #include "PSync/detail/util.hpp" 48 | 49 | #include 50 | 51 | namespace psync::detail { 52 | 53 | namespace be = boost::endian; 54 | 55 | constexpr size_t ENTRY_SIZE = sizeof(HashTableEntry::count) + sizeof(HashTableEntry::keySum) + 56 | sizeof(HashTableEntry::keyCheck); 57 | 58 | bool 59 | HashTableEntry::isPure() const 60 | { 61 | if (count == 1 || count == -1) { 62 | uint32_t check = murmurHash3(N_HASHCHECK, keySum); 63 | return keyCheck == check; 64 | } 65 | 66 | return false; 67 | } 68 | 69 | bool 70 | HashTableEntry::isEmpty() const 71 | { 72 | return count == 0 && keySum == 0 && keyCheck == 0; 73 | } 74 | 75 | IBLT::IBLT(size_t expectedNumEntries, CompressionScheme scheme) 76 | : m_compressionScheme(scheme) 77 | { 78 | // 1.5x expectedNumEntries gives very low probability of decoding failure 79 | size_t nEntries = expectedNumEntries + expectedNumEntries / 2; 80 | // make nEntries exactly divisible by N_HASH 81 | size_t remainder = nEntries % N_HASH; 82 | if (remainder != 0) { 83 | nEntries += (N_HASH - remainder); 84 | } 85 | 86 | m_hashTable.resize(nEntries); 87 | } 88 | 89 | void 90 | IBLT::initialize(const ndn::name::Component& ibltName) 91 | { 92 | auto decompressed = decompress(m_compressionScheme, ibltName.value_bytes()); 93 | if (decompressed->size() != ENTRY_SIZE * m_hashTable.size()) { 94 | NDN_THROW(Error("Received IBF cannot be decoded!")); 95 | } 96 | 97 | const uint8_t* input = decompressed->data(); 98 | for (auto& entry : m_hashTable) { 99 | entry.count = be::endian_load(input); 100 | input += sizeof(entry.count); 101 | 102 | entry.keySum = be::endian_load(input); 103 | input += sizeof(entry.keySum); 104 | 105 | entry.keyCheck = be::endian_load(input); 106 | input += sizeof(entry.keyCheck); 107 | } 108 | } 109 | 110 | static inline void 111 | ibltUpdate(std::vector& ht, int32_t plusOrMinus, uint32_t key) 112 | { 113 | size_t bucketsPerHash = ht.size() / N_HASH; 114 | 115 | for (size_t i = 0; i < N_HASH; i++) { 116 | size_t startEntry = i * bucketsPerHash; 117 | uint32_t h = murmurHash3(i, key); 118 | HashTableEntry& entry = ht.at(startEntry + (h % bucketsPerHash)); 119 | entry.count += plusOrMinus; 120 | entry.keySum ^= key; 121 | entry.keyCheck ^= murmurHash3(N_HASHCHECK, key); 122 | } 123 | } 124 | 125 | void 126 | IBLT::insert(uint32_t key) 127 | { 128 | ibltUpdate(m_hashTable, 1, key); 129 | } 130 | 131 | void 132 | IBLT::erase(uint32_t key) 133 | { 134 | ibltUpdate(m_hashTable, -1, key); 135 | } 136 | 137 | void 138 | IBLT::appendToName(ndn::Name& name) const 139 | { 140 | std::vector buffer(ENTRY_SIZE * m_hashTable.size()); 141 | uint8_t* output = buffer.data(); 142 | for (const auto& entry : m_hashTable) { 143 | be::endian_store(output, entry.count); 144 | output += sizeof(entry.count); 145 | 146 | be::endian_store(output, entry.keySum); 147 | output += sizeof(entry.keySum); 148 | 149 | be::endian_store(output, entry.keyCheck); 150 | output += sizeof(entry.keyCheck); 151 | } 152 | 153 | auto compressed = compress(m_compressionScheme, buffer); 154 | name.append(ndn::name::Component(std::move(compressed))); 155 | } 156 | 157 | std::ostream& 158 | operator<<(std::ostream& os, const IBLT& iblt) 159 | { 160 | os << "count keySum keyCheckMatch\n"; 161 | for (const auto& entry : iblt.getHashTable()) { 162 | os << entry.count << " " << entry.keySum << " " 163 | << ((entry.isEmpty() || murmurHash3(N_HASHCHECK, entry.keySum) == entry.keyCheck) ? "true" : "false") 164 | << "\n"; 165 | } 166 | return os; 167 | } 168 | 169 | IBLTDiff 170 | operator-(const IBLT& lhs, const IBLT& rhs) 171 | { 172 | const auto& lht = lhs.getHashTable(); 173 | const auto& rht = rhs.getHashTable(); 174 | BOOST_ASSERT(lht.size() == rht.size()); 175 | 176 | std::vector peeled(lht.size()); 177 | std::transform(lht.begin(), lht.end(), rht.begin(), peeled.begin(), 178 | [] (const HashTableEntry& lhe, const HashTableEntry& rhe) { 179 | HashTableEntry diff; 180 | diff.count = lhe.count - rhe.count; 181 | diff.keySum = lhe.keySum ^ rhe.keySum; 182 | diff.keyCheck = lhe.keyCheck ^ rhe.keyCheck; 183 | return diff; 184 | } 185 | ); 186 | 187 | std::set positive; 188 | std::set negative; 189 | 190 | size_t nErased = 0; 191 | do { 192 | nErased = 0; 193 | for (const auto& entry : peeled) { 194 | if (entry.isPure()) { 195 | if (entry.count == 1) { 196 | positive.insert(entry.keySum); 197 | } 198 | else { 199 | negative.insert(entry.keySum); 200 | } 201 | ibltUpdate(peeled, -entry.count, entry.keySum); 202 | ++nErased; 203 | } 204 | } 205 | } while (nErased > 0); 206 | 207 | // If any buckets for one of the hash functions is not empty, 208 | // then we didn't peel them all: 209 | bool canDecode = std::all_of(peeled.begin(), peeled.end(), 210 | [] (const HashTableEntry& entry) { return entry.isEmpty(); }); 211 | return {canDecode, std::move(positive), std::move(negative)}; 212 | } 213 | 214 | } // namespace psync::detail 215 | -------------------------------------------------------------------------------- /PSync/detail/iblt.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | * 19 | 20 | * This file incorporates work covered by the following copyright and 21 | * permission notice: 22 | 23 | * The MIT License (MIT) 24 | 25 | * Copyright (c) 2014 Gavin Andresen 26 | 27 | * Permission is hereby granted, free of charge, to any person obtaining a copy 28 | * of this software and associated documentation files (the "Software"), to deal 29 | * in the Software without restriction, including without limitation the rights 30 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 31 | * copies of the Software, and to permit persons to whom the Software is 32 | * furnished to do so, subject to the following conditions: 33 | 34 | * The above copyright notice and this permission notice shall be included in all 35 | * copies or substantial portions of the Software. 36 | 37 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 39 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 43 | * SOFTWARE. 44 | */ 45 | 46 | #ifndef PSYNC_DETAIL_IBLT_HPP 47 | #define PSYNC_DETAIL_IBLT_HPP 48 | 49 | #include "PSync/common.hpp" 50 | 51 | #include 52 | 53 | #include 54 | 55 | #include 56 | #include 57 | 58 | namespace psync::detail { 59 | 60 | class HashTableEntry : private boost::equality_comparable 61 | { 62 | public: 63 | bool 64 | isPure() const; 65 | 66 | bool 67 | isEmpty() const; 68 | 69 | private: // non-member operators 70 | // NOTE: the following "hidden friend" operators are available via 71 | // argument-dependent lookup only and must be defined inline. 72 | // boost::equality_comparable provides != operator. 73 | 74 | friend bool 75 | operator==(const HashTableEntry& lhs, const HashTableEntry& rhs) noexcept 76 | { 77 | return lhs.count == rhs.count && lhs.keySum == rhs.keySum && lhs.keyCheck == rhs.keyCheck; 78 | } 79 | 80 | public: 81 | int32_t count = 0; 82 | uint32_t keySum = 0; 83 | uint32_t keyCheck = 0; 84 | }; 85 | 86 | inline constexpr size_t N_HASH = 3; 87 | inline constexpr size_t N_HASHCHECK = 11; 88 | 89 | /** 90 | * @brief Invertible Bloom Lookup Table (Invertible Bloom Filter) 91 | * 92 | * Used by Partial Sync (PartialProducer) and Full Sync (Full Producer) 93 | */ 94 | class IBLT : private boost::equality_comparable 95 | { 96 | public: 97 | class Error : public std::runtime_error 98 | { 99 | public: 100 | using std::runtime_error::runtime_error; 101 | }; 102 | 103 | /** 104 | * @brief constructor 105 | * 106 | * @param expectedNumEntries the expected number of entries in the IBLT 107 | * @param scheme compression scheme to be used for the IBLT 108 | */ 109 | explicit 110 | IBLT(size_t expectedNumEntries, CompressionScheme scheme); 111 | 112 | /** 113 | * @brief Populate the hash table using the vector representation of IBLT 114 | * 115 | * @param ibltName the Component representation of IBLT 116 | * @throws Error if size of values is not compatible with this IBF 117 | */ 118 | void 119 | initialize(const ndn::name::Component& ibltName); 120 | 121 | void 122 | insert(uint32_t key); 123 | 124 | void 125 | erase(uint32_t key); 126 | 127 | const std::vector& 128 | getHashTable() const 129 | { 130 | return m_hashTable; 131 | } 132 | 133 | /** 134 | * @brief Appends self to @p name 135 | * 136 | * Encodes our hash table from uint32_t vector to uint8_t vector 137 | * We create a uin8_t vector 12 times the size of uint32_t vector 138 | * We put the first count in first 4 cells, keySum in next 4, and keyCheck in next 4. 139 | * Repeat for all the other cells of the hash table. 140 | * Then we append this uint8_t vector to the name. 141 | */ 142 | void 143 | appendToName(ndn::Name& name) const; 144 | 145 | private: // non-member operators 146 | // NOTE: the following "hidden friend" operators are available via 147 | // argument-dependent lookup only and must be defined inline. 148 | // boost::equality_comparable provides != operator. 149 | 150 | friend bool 151 | operator==(const IBLT& lhs, const IBLT& rhs) 152 | { 153 | return lhs.m_hashTable == rhs.m_hashTable; 154 | } 155 | 156 | private: 157 | std::vector m_hashTable; 158 | CompressionScheme m_compressionScheme; 159 | }; 160 | 161 | std::ostream& 162 | operator<<(std::ostream& os, const IBLT& iblt); 163 | 164 | /** @brief Represent the difference between two IBLTs, */ 165 | struct IBLTDiff 166 | { 167 | /** @brief Whether decoding completed successfully. */ 168 | bool canDecode = false; 169 | 170 | /** @brief Entries in lhs but not rhs. */ 171 | std::set positive; 172 | 173 | /** @brief Entries in rhs but not lhs. */ 174 | std::set negative; 175 | }; 176 | 177 | /** 178 | * @brief Compute the difference between two IBLTs. 179 | * @param lhs own IBLT. 180 | * @param rhs received IBLT. It must have same hashtable size @p lhs hashtable. 181 | * @return decoding result. 182 | */ 183 | IBLTDiff 184 | operator-(const IBLT& lhs, const IBLT& rhs); 185 | 186 | } // namespace psync::detail 187 | 188 | #endif // PSYNC_DETAIL_IBLT_HPP 189 | -------------------------------------------------------------------------------- /PSync/detail/state.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #include "PSync/detail/state.hpp" 21 | 22 | #include 23 | #include 24 | 25 | namespace psync::detail { 26 | 27 | State::State(const ndn::Block& block) 28 | { 29 | wireDecode(block); 30 | } 31 | 32 | void 33 | State::addContent(const ndn::Name& prefix) 34 | { 35 | m_wire.reset(); 36 | m_content.emplace_back(prefix); 37 | } 38 | 39 | const ndn::Block& 40 | State::wireEncode() const 41 | { 42 | if (m_wire.hasWire()) { 43 | return m_wire; 44 | } 45 | 46 | ndn::EncodingEstimator estimator; 47 | size_t estimatedSize = wireEncode(estimator); 48 | 49 | ndn::EncodingBuffer buffer(estimatedSize, 0); 50 | wireEncode(buffer); 51 | 52 | m_wire = buffer.block(); 53 | return m_wire; 54 | } 55 | 56 | template 57 | size_t 58 | State::wireEncode(ndn::EncodingImpl& block) const 59 | { 60 | size_t totalLength = 0; 61 | 62 | for (auto it = m_content.rbegin(); it != m_content.rend(); ++it) { 63 | totalLength += it->wireEncode(block); 64 | } 65 | 66 | totalLength += block.prependVarNumber(totalLength); 67 | totalLength += block.prependVarNumber(tlv::PSyncContent); 68 | 69 | return totalLength; 70 | } 71 | 72 | NDN_CXX_DEFINE_WIRE_ENCODE_INSTANTIATIONS(State); 73 | 74 | void 75 | State::wireDecode(const ndn::Block& wire) 76 | { 77 | if (wire.type() != tlv::PSyncContent) { 78 | NDN_THROW(ndn::tlv::Error("PSyncContent", wire.type())); 79 | } 80 | 81 | m_content.clear(); 82 | 83 | wire.parse(); 84 | m_wire = wire; 85 | 86 | for (auto it = m_wire.elements_begin(); it != m_wire.elements_end(); ++it) { 87 | if (it->type() == ndn::tlv::Name) { 88 | m_content.emplace_back(*it); 89 | } 90 | else { 91 | NDN_THROW(ndn::tlv::Error("Name", it->type())); 92 | } 93 | } 94 | } 95 | 96 | std::ostream& 97 | operator<<(std::ostream& os, const State& state) 98 | { 99 | auto content = state.getContent(); 100 | 101 | os << "["; 102 | std::copy(content.begin(), content.end(), ndn::make_ostream_joiner(os, ", ")); 103 | os << "]"; 104 | 105 | return os; 106 | } 107 | 108 | } // namespace psync::detail 109 | -------------------------------------------------------------------------------- /PSync/detail/state.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #ifndef PSYNC_DETAIL_STATE_HPP 21 | #define PSYNC_DETAIL_STATE_HPP 22 | 23 | #include 24 | 25 | namespace psync::tlv { 26 | 27 | enum { 28 | PSyncContent = 128 29 | }; 30 | 31 | } // namespace psync::tlv 32 | 33 | namespace psync::detail { 34 | 35 | class State 36 | { 37 | public: 38 | State() = default; 39 | 40 | explicit 41 | State(const ndn::Block& block); 42 | 43 | void 44 | addContent(const ndn::Name& prefix); 45 | 46 | const std::vector& 47 | getContent() const 48 | { 49 | return m_content; 50 | } 51 | 52 | const ndn::Block& 53 | wireEncode() const; 54 | 55 | template 56 | size_t 57 | wireEncode(ndn::EncodingImpl& block) const; 58 | 59 | void 60 | wireDecode(const ndn::Block& wire); 61 | 62 | std::vector::const_iterator 63 | begin() const 64 | { 65 | return m_content.cbegin(); 66 | } 67 | 68 | std::vector::const_iterator 69 | end() const 70 | { 71 | return m_content.cend(); 72 | } 73 | 74 | private: 75 | std::vector m_content; 76 | mutable ndn::Block m_wire; 77 | }; 78 | 79 | NDN_CXX_DECLARE_WIRE_ENCODE_INSTANTIATIONS(State); 80 | 81 | std::ostream& 82 | operator<<(std::ostream& os, const State& State); 83 | 84 | } // namespace psync::detail 85 | 86 | #endif // PSYNC_DETAIL_STATE_HPP 87 | -------------------------------------------------------------------------------- /PSync/detail/util.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | * 19 | * murmurHash3 was written by Austin Appleby, and is placed in the public 20 | * domain. The author hereby disclaims copyright to this source code. 21 | * https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp 22 | */ 23 | 24 | #include "PSync/detail/util.hpp" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #ifdef PSYNC_HAVE_ZLIB 34 | #include 35 | #endif 36 | #ifdef PSYNC_HAVE_GZIP 37 | #include 38 | #endif 39 | #ifdef PSYNC_HAVE_BZIP2 40 | #include 41 | #endif 42 | #ifdef PSYNC_HAVE_LZMA 43 | #include 44 | #endif 45 | #ifdef PSYNC_HAVE_ZSTD 46 | #include 47 | #endif 48 | 49 | namespace psync::detail { 50 | 51 | static inline uint32_t 52 | ROTL32(uint32_t x, int8_t r) 53 | { 54 | return (x << r) | (x >> (32 - r)); 55 | } 56 | 57 | uint32_t 58 | murmurHash3(const void* key, size_t len, uint32_t seed) 59 | { 60 | const uint8_t * data = (const uint8_t*)key; 61 | const int nblocks = len / 4; 62 | 63 | uint32_t h1 = seed; 64 | 65 | const uint32_t c1 = 0xcc9e2d51; 66 | const uint32_t c2 = 0x1b873593; 67 | 68 | //---------- 69 | // body 70 | 71 | const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); 72 | 73 | for (int i = -nblocks; i; i++) 74 | { 75 | uint32_t k1 = blocks[i]; 76 | 77 | k1 *= c1; 78 | k1 = ROTL32(k1,15); 79 | k1 *= c2; 80 | 81 | h1 ^= k1; 82 | h1 = ROTL32(h1,13); 83 | h1 = h1*5+0xe6546b64; 84 | } 85 | 86 | //---------- 87 | // tail 88 | 89 | const uint8_t * tail = (const uint8_t*)(data + nblocks*4); 90 | 91 | uint32_t k1 = 0; 92 | 93 | switch (len & 3) 94 | { 95 | case 3: k1 ^= tail[2] << 16; 96 | [[fallthrough]]; 97 | case 2: k1 ^= tail[1] << 8; 98 | [[fallthrough]]; 99 | case 1: k1 ^= tail[0]; 100 | k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; 101 | } 102 | 103 | //---------- 104 | // finalization 105 | 106 | h1 ^= len; 107 | h1 ^= h1 >> 16; 108 | h1 *= 0x85ebca6b; 109 | h1 ^= h1 >> 13; 110 | h1 *= 0xc2b2ae35; 111 | h1 ^= h1 >> 16; 112 | 113 | return h1; 114 | } 115 | 116 | uint32_t 117 | murmurHash3(uint32_t seed, const ndn::Name& name) 118 | { 119 | const auto& wire = name.wireEncode(); 120 | return murmurHash3(wire.value(), wire.value_size(), seed); 121 | } 122 | 123 | std::shared_ptr 124 | compress(CompressionScheme scheme, ndn::span buffer) 125 | { 126 | namespace bio = boost::iostreams; 127 | 128 | bio::filtering_istreambuf in; 129 | 130 | switch (scheme) { 131 | case CompressionScheme::NONE: 132 | break; 133 | 134 | case CompressionScheme::ZLIB: 135 | #ifdef PSYNC_HAVE_ZLIB 136 | in.push(bio::zlib_compressor(bio::zlib::best_compression)); 137 | break; 138 | #else 139 | NDN_THROW(CompressionError("ZLIB compression not supported!")); 140 | #endif 141 | 142 | case CompressionScheme::GZIP: 143 | #ifdef PSYNC_HAVE_GZIP 144 | in.push(bio::gzip_compressor(bio::gzip::best_compression)); 145 | break; 146 | #else 147 | NDN_THROW(CompressionError("GZIP compression not supported!")); 148 | #endif 149 | 150 | case CompressionScheme::BZIP2: 151 | #ifdef PSYNC_HAVE_BZIP2 152 | in.push(bio::bzip2_compressor()); 153 | break; 154 | #else 155 | NDN_THROW(CompressionError("BZIP2 compression not supported!")); 156 | #endif 157 | 158 | case CompressionScheme::LZMA: 159 | #ifdef PSYNC_HAVE_LZMA 160 | in.push(bio::lzma_compressor(bio::lzma::best_compression)); 161 | break; 162 | #else 163 | NDN_THROW(CompressionError("LZMA compression not supported!")); 164 | #endif 165 | 166 | case CompressionScheme::ZSTD: 167 | #ifdef PSYNC_HAVE_ZSTD 168 | in.push(bio::zstd_compressor(bio::zstd::best_compression)); 169 | break; 170 | #else 171 | NDN_THROW(CompressionError("ZSTD compression not supported!")); 172 | #endif 173 | } 174 | 175 | in.push(bio::array_source(reinterpret_cast(buffer.data()), buffer.size())); 176 | ndn::OBufferStream out; 177 | bio::copy(in, out); 178 | 179 | return out.buf(); 180 | } 181 | 182 | std::shared_ptr 183 | decompress(CompressionScheme scheme, ndn::span buffer) 184 | { 185 | namespace bio = boost::iostreams; 186 | 187 | bio::filtering_istreambuf in; 188 | 189 | switch (scheme) { 190 | case CompressionScheme::NONE: 191 | break; 192 | 193 | case CompressionScheme::ZLIB: 194 | #ifdef PSYNC_HAVE_ZLIB 195 | in.push(bio::zlib_decompressor()); 196 | break; 197 | #else 198 | NDN_THROW(CompressionError("ZLIB decompression not supported!")); 199 | #endif 200 | 201 | case CompressionScheme::GZIP: 202 | #ifdef PSYNC_HAVE_GZIP 203 | in.push(bio::gzip_decompressor()); 204 | break; 205 | #else 206 | NDN_THROW(CompressionError("GZIP compression not supported!")); 207 | #endif 208 | 209 | case CompressionScheme::BZIP2: 210 | #ifdef PSYNC_HAVE_BZIP2 211 | in.push(bio::bzip2_decompressor()); 212 | break; 213 | #else 214 | NDN_THROW(CompressionError("BZIP2 compression not supported!")); 215 | #endif 216 | 217 | case CompressionScheme::LZMA: 218 | #ifdef PSYNC_HAVE_LZMA 219 | in.push(bio::lzma_decompressor()); 220 | break; 221 | #else 222 | NDN_THROW(CompressionError("LZMA compression not supported!")); 223 | #endif 224 | 225 | case CompressionScheme::ZSTD: 226 | #ifdef PSYNC_HAVE_ZSTD 227 | in.push(bio::zstd_decompressor()); 228 | break; 229 | #else 230 | NDN_THROW(CompressionError("ZSTD compression not supported!")); 231 | #endif 232 | } 233 | 234 | in.push(bio::array_source(reinterpret_cast(buffer.data()), buffer.size())); 235 | ndn::OBufferStream out; 236 | bio::copy(in, out); 237 | 238 | return out.buf(); 239 | } 240 | 241 | } // namespace psync::detail 242 | -------------------------------------------------------------------------------- /PSync/detail/util.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #ifndef PSYNC_DETAIL_UTIL_HPP 21 | #define PSYNC_DETAIL_UTIL_HPP 22 | 23 | #include "PSync/common.hpp" 24 | 25 | #include 26 | #include 27 | 28 | namespace psync::detail { 29 | 30 | uint32_t 31 | murmurHash3(const void* key, size_t len, uint32_t seed); 32 | 33 | /** 34 | * @brief Compute 32-bit MurmurHash3 of Name TLV-VALUE. 35 | */ 36 | uint32_t 37 | murmurHash3(uint32_t seed, const ndn::Name& name); 38 | 39 | inline uint32_t 40 | murmurHash3(uint32_t seed, uint32_t value) 41 | { 42 | return murmurHash3(&value, sizeof(value), seed); 43 | } 44 | 45 | std::shared_ptr 46 | compress(CompressionScheme scheme, ndn::span buffer); 47 | 48 | std::shared_ptr 49 | decompress(CompressionScheme scheme, ndn::span buffer); 50 | 51 | } // namespace psync::detail 52 | 53 | #endif // PSYNC_DETAIL_UTIL_HPP 54 | -------------------------------------------------------------------------------- /PSync/full-producer.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #ifndef PSYNC_FULL_PRODUCER_HPP 21 | #define PSYNC_FULL_PRODUCER_HPP 22 | 23 | #include "PSync/producer-base.hpp" 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | namespace psync { 31 | 32 | /** 33 | * @brief Full sync logic to synchronize with other nodes 34 | * where all nodes wants to get all names prefixes synced. 35 | * 36 | * Application should call publishName whenever it wants to 37 | * let consumers know that new data is available for the userPrefix. 38 | * Multiple userPrefixes can be added by using addUserNode. 39 | * Currently, fetching and publishing of data needs to be handled by the application. 40 | */ 41 | class FullProducer : public ProducerBase 42 | { 43 | public: 44 | /** 45 | * @brief Constructor options. 46 | */ 47 | struct Options 48 | { 49 | /// Callback to be invoked when there is new data. 50 | UpdateCallback onUpdate = [] (const auto&) {}; 51 | /// Expected number of entries in IBF. 52 | uint32_t ibfCount = 80; 53 | /// Compression scheme to use for IBF. 54 | CompressionScheme ibfCompression = CompressionScheme::DEFAULT; 55 | /// Lifetime of sync Interest. 56 | ndn::time::milliseconds syncInterestLifetime = SYNC_INTEREST_LIFETIME; 57 | /// FreshnessPeriod of sync Data. 58 | ndn::time::milliseconds syncDataFreshness = SYNC_REPLY_FRESHNESS; 59 | /// Compression scheme to use for Data content. 60 | CompressionScheme contentCompression = CompressionScheme::DEFAULT; 61 | }; 62 | 63 | /** 64 | * @brief Constructor. 65 | * 66 | * @param face Application face. 67 | * @param keyChain KeyChain instance to use for signing. 68 | * @param syncPrefix The prefix of the sync group. 69 | * @param opts Options. 70 | */ 71 | FullProducer(ndn::Face& face, 72 | ndn::KeyChain& keyChain, 73 | const ndn::Name& syncPrefix, 74 | const Options& opts); 75 | 76 | [[deprecated]] 77 | FullProducer(ndn::Face& face, 78 | ndn::KeyChain& keyChain, 79 | size_t expectedNumEntries, 80 | const ndn::Name& syncPrefix, 81 | const ndn::Name& userPrefix, 82 | UpdateCallback onUpdateCallBack, 83 | ndn::time::milliseconds syncInterestLifetime = SYNC_INTEREST_LIFETIME, 84 | ndn::time::milliseconds syncReplyFreshness = SYNC_REPLY_FRESHNESS, 85 | CompressionScheme ibltCompression = CompressionScheme::DEFAULT, 86 | CompressionScheme contentCompression = CompressionScheme::DEFAULT); 87 | 88 | ~FullProducer(); 89 | 90 | /** 91 | * @brief Publish name to let others know 92 | * 93 | * addUserNode needs to be called before this to add the prefix 94 | * if not already added via the constructor. 95 | * If seq is null then the seq of prefix is incremented by 1 else 96 | * the supplied sequence is set in the IBF. 97 | * 98 | * @param prefix the prefix to be updated 99 | * @param seq the sequence number of the prefix 100 | */ 101 | void 102 | publishName(const ndn::Name& prefix, std::optional seq = std::nullopt); 103 | 104 | PSYNC_PUBLIC_WITH_TESTS_ELSE_PRIVATE: 105 | /** 106 | * @brief Send sync interest for full synchronization 107 | * 108 | * Forms the interest name: // 109 | * Cancels any pending sync interest we sent earlier on the face 110 | * Sends the sync interest 111 | */ 112 | void 113 | sendSyncInterest(); 114 | 115 | void 116 | processWaitingInterests(); 117 | 118 | void 119 | scheduleProcessWaitingInterests(); 120 | 121 | /** 122 | * @brief Process sync interest from other parties 123 | * 124 | * Get differences b/w our IBF and IBF in the sync interest. 125 | * If we cannot get the differences successfully then send an application nack. 126 | * 127 | * If we have some things in our IBF that the other side does not have, reply with the content or 128 | * If no. of different items is greater than threshold or equals zero then send a nack. 129 | * Otherwise add the sync interest into a map with interest name as key and PendingEntryInfoFull 130 | * as value. 131 | * 132 | * @param prefixName prefix for sync group which we registered 133 | * @param interest the interest we got 134 | * @param isTimedProcessing is this interest from the waiting interests list 135 | */ 136 | void 137 | onSyncInterest(const ndn::Name& prefixName, const ndn::Interest& interest, 138 | bool isTimedProcessing = false); 139 | 140 | /** 141 | * @brief Send sync data 142 | * 143 | * Check if the data will satisfy our own pending interest, 144 | * remove it first if it does, and then renew the sync interest 145 | * Otherwise just send the data 146 | * 147 | * @param name name to be set as data name 148 | * @param block the content of the data 149 | * @param syncReplyFreshness the freshness to use for the sync data; defaults to @p SYNC_REPLY_FRESHNESS 150 | */ 151 | void 152 | sendSyncData(const ndn::Name& name, const ndn::Block& block, 153 | ndn::time::milliseconds syncReplyFreshness); 154 | 155 | /** 156 | * @brief Process sync data 157 | * 158 | * Call deletePendingInterests to delete any pending sync interest with 159 | * interest name would have been satisfied once NFD got the data. 160 | * 161 | * For each prefix/seq in data content check that we don't already have the 162 | * prefix/seq and updateSeq(prefix, seq) 163 | * 164 | * Notify the application about the updates 165 | * sendSyncInterest because the last one was satisfied by the incoming data 166 | * 167 | * @param interest interest for which we got the data 168 | * @param bufferPtr sync data content 169 | */ 170 | void 171 | onSyncData(const ndn::Interest& interest, const ndn::ConstBufferPtr& bufferPtr); 172 | 173 | private: 174 | /** 175 | * @brief Satisfy pending sync interests 176 | * 177 | * For pending sync interests do a difference with current IBF to find out missing prefixes. 178 | * Send [Missing Prefixes] union @p updatedPrefixWithSeq 179 | * 180 | * This is because it is called from publish, so the @p updatedPrefixWithSeq must be missing 181 | * from other nodes regardless of IBF difference failure. 182 | */ 183 | void 184 | satisfyPendingInterests(const ndn::Name& updatedPrefixWithSeq); 185 | 186 | /** 187 | * @brief Delete pending sync interests that match given name 188 | */ 189 | void 190 | deletePendingInterests(const ndn::Name& interestName); 191 | 192 | /** 193 | * @brief Check if hash(prefix + 1) is in negative 194 | * 195 | * Sometimes what happens is that interest from other side 196 | * gets to us before the data 197 | */ 198 | bool 199 | isFutureHash(const ndn::Name& prefix, const std::set& negative); 200 | 201 | #ifdef PSYNC_WITH_TESTS 202 | public: 203 | size_t nIbfDecodeFailuresAboveThreshold = 0; 204 | size_t nIbfDecodeFailuresBelowThreshold = 0; 205 | #endif // PSYNC_WITH_TESTS 206 | 207 | private: 208 | struct PendingEntryInfo 209 | { 210 | detail::IBLT iblt; 211 | ndn::scheduler::ScopedEventId expirationEvent; 212 | }; 213 | 214 | struct WaitingEntryInfo 215 | { 216 | uint16_t numTries = 0; 217 | ndn::Interest::Nonce nonce; 218 | }; 219 | 220 | ndn::time::milliseconds m_syncInterestLifetime; 221 | UpdateCallback m_onUpdate; 222 | ndn::scheduler::ScopedEventId m_scheduledSyncInterestId; 223 | static constexpr int MIN_JITTER = 100; 224 | static constexpr int MAX_JITTER = 500; 225 | std::uniform_int_distribution<> m_jitter{MIN_JITTER, MAX_JITTER}; 226 | ndn::time::system_clock::time_point m_lastInterestSentTime; 227 | ndn::Name m_outstandingInterestName; 228 | ndn::ScopedRegisteredPrefixHandle m_registeredPrefix; 229 | std::shared_ptr m_fetcher; 230 | uint64_t m_incomingFace = 0; 231 | std::map m_waitingForProcessing; 232 | bool m_inNoNewDataWaitOutPeriod = false; 233 | ndn::scheduler::ScopedEventId m_interestDelayTimerId; 234 | 235 | PSYNC_PUBLIC_WITH_TESTS_ELSE_PRIVATE: 236 | std::map m_pendingEntries; 237 | }; 238 | 239 | } // namespace psync 240 | 241 | #endif // PSYNC_FULL_PRODUCER_HPP 242 | -------------------------------------------------------------------------------- /PSync/partial-producer.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2023, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | **/ 19 | 20 | #ifndef PSYNC_PARTIAL_PRODUCER_HPP 21 | #define PSYNC_PARTIAL_PRODUCER_HPP 22 | 23 | #include "PSync/producer-base.hpp" 24 | #include "PSync/detail/bloom-filter.hpp" 25 | 26 | namespace psync { 27 | 28 | /** 29 | * @brief Partial sync logic to publish data names 30 | * 31 | * Application should call publishName whenever it wants to 32 | * let consumers know that new data is available. 33 | * Additional userPrefix should be added via addUserNode before calling publishName 34 | * Currently, publishing of data needs to be handled by the application. 35 | */ 36 | class PartialProducer : public ProducerBase 37 | { 38 | public: 39 | /** 40 | * @brief Constructor options. 41 | */ 42 | struct Options 43 | { 44 | /// Expected number of entries in IBF. 45 | uint32_t ibfCount = 40; 46 | /// Compression scheme to use for IBF. 47 | CompressionScheme ibfCompression = CompressionScheme::NONE; 48 | /// FreshnessPeriod of hello Data. 49 | ndn::time::milliseconds helloDataFreshness = HELLO_REPLY_FRESHNESS; 50 | /// FreshnessPeriod of sync Data. 51 | ndn::time::milliseconds syncDataFreshness = SYNC_REPLY_FRESHNESS; 52 | }; 53 | 54 | /** 55 | * @brief Constructor. 56 | * 57 | * @param face Application face. 58 | * @param keyChain KeyChain instance to use for signing. 59 | * @param syncPrefix The prefix of the sync group. 60 | * @param opts Options. 61 | */ 62 | PartialProducer(ndn::Face& face, 63 | ndn::KeyChain& keyChain, 64 | const ndn::Name& syncPrefix, 65 | const Options& opts); 66 | 67 | [[deprecated]] 68 | PartialProducer(ndn::Face& face, 69 | ndn::KeyChain& keyChain, 70 | size_t expectedNumEntries, 71 | const ndn::Name& syncPrefix, 72 | const ndn::Name& userPrefix, 73 | ndn::time::milliseconds helloReplyFreshness = HELLO_REPLY_FRESHNESS, 74 | ndn::time::milliseconds syncReplyFreshness = SYNC_REPLY_FRESHNESS, 75 | CompressionScheme ibltCompression = CompressionScheme::NONE); 76 | 77 | /** 78 | * @brief Publish name to let subscribed consumers know 79 | * 80 | * If seq is null then the seq of prefix is incremented by 1 else 81 | * the supplied sequence is set in the IBF. 82 | * Upon updating the sequence in the IBF satisfyPendingSyncInterests 83 | * is called to let subscribed consumers know. 84 | * 85 | * @param prefix the prefix to be updated 86 | * @param seq the sequence number of the prefix 87 | */ 88 | void 89 | publishName(const ndn::Name& prefix, std::optional seq = std::nullopt); 90 | 91 | private: 92 | /** 93 | * @brief Satisfy any pending interest that have subscription for prefix 94 | * 95 | * @param prefix the prefix that was updated in publishName 96 | */ 97 | void 98 | satisfyPendingSyncInterests(const ndn::Name& prefix); 99 | 100 | PSYNC_PUBLIC_WITH_TESTS_ELSE_PRIVATE: 101 | /** 102 | * @brief Receive hello interest from consumer and respond with hello data 103 | * 104 | * Hello data's name format is: /\/hello/\ 105 | * 106 | * @param prefix the hello interest prefix 107 | * @param interest the hello interest received 108 | */ 109 | void 110 | onHelloInterest(const ndn::Name& prefix, const ndn::Interest& interest); 111 | 112 | /** 113 | * @brief Receive sync interest from consumer 114 | * 115 | * Either respond with sync data if consumer is behind or 116 | * store sync interest in m_pendingEntries 117 | * 118 | * Sync data's name format is: /\/sync/\/\/\ 119 | * (BF has 3 components). 120 | */ 121 | void 122 | onSyncInterest(const ndn::Name& prefix, const ndn::Interest& interest); 123 | 124 | PSYNC_PUBLIC_WITH_TESTS_ELSE_PRIVATE: 125 | struct PendingEntryInfo 126 | { 127 | detail::BloomFilter bf; 128 | detail::IBLT iblt; 129 | ndn::scheduler::ScopedEventId expirationEvent; 130 | }; 131 | 132 | std::map m_pendingEntries; 133 | ndn::ScopedRegisteredPrefixHandle m_registeredPrefix; 134 | ndn::time::milliseconds m_helloReplyFreshness; 135 | }; 136 | 137 | } // namespace psync 138 | 139 | #endif // PSYNC_PARTIAL_PRODUCER_HPP 140 | -------------------------------------------------------------------------------- /PSync/producer-base.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #include "PSync/producer-base.hpp" 21 | #include "PSync/detail/util.hpp" 22 | 23 | #include 24 | #include 25 | 26 | namespace psync { 27 | 28 | NDN_LOG_INIT(psync.ProducerBase); 29 | 30 | ProducerBase::ProducerBase(ndn::Face& face, 31 | ndn::KeyChain& keyChain, 32 | size_t expectedNumEntries, 33 | const ndn::Name& syncPrefix, 34 | ndn::time::milliseconds syncReplyFreshness, 35 | CompressionScheme ibltCompression, 36 | CompressionScheme contentCompression) 37 | : m_face(face) 38 | , m_keyChain(keyChain) 39 | , m_scheduler(m_face.getIoContext()) 40 | , m_rng(ndn::random::getRandomNumberEngine()) 41 | , m_iblt(expectedNumEntries, ibltCompression) 42 | , m_segmentPublisher(m_face, m_keyChain) 43 | , m_expectedNumEntries(expectedNumEntries) 44 | , m_threshold(expectedNumEntries / 2) 45 | , m_syncPrefix(syncPrefix) 46 | , m_syncReplyFreshness(syncReplyFreshness) 47 | , m_ibltCompression(ibltCompression) 48 | , m_contentCompression(contentCompression) 49 | { 50 | } 51 | 52 | bool 53 | ProducerBase::addUserNode(const ndn::Name& prefix) 54 | { 55 | if (m_prefixes.find(prefix) == m_prefixes.end()) { 56 | m_prefixes[prefix] = 0; 57 | return true; 58 | } 59 | else { 60 | return false; 61 | } 62 | } 63 | 64 | void 65 | ProducerBase::removeUserNode(const ndn::Name& prefix) 66 | { 67 | auto it = m_prefixes.find(prefix); 68 | if (it != m_prefixes.end()) { 69 | uint64_t seqNo = it->second; 70 | m_prefixes.erase(it); 71 | 72 | ndn::Name prefixWithSeq = ndn::Name(prefix).appendNumber(seqNo); 73 | auto hashIt = m_biMap.right.find(prefixWithSeq); 74 | if (hashIt != m_biMap.right.end()) { 75 | m_iblt.erase(hashIt->second); 76 | m_biMap.right.erase(hashIt); 77 | } 78 | } 79 | } 80 | 81 | void 82 | ProducerBase::updateSeqNo(const ndn::Name& prefix, uint64_t seq) 83 | { 84 | NDN_LOG_DEBUG("UpdateSeq: " << prefix << " " << seq); 85 | 86 | uint64_t oldSeq; 87 | auto it = m_prefixes.find(prefix); 88 | if (it != m_prefixes.end()) { 89 | oldSeq = it->second; 90 | } 91 | else { 92 | NDN_LOG_WARN("Prefix not found in m_prefixes"); 93 | return; 94 | } 95 | 96 | if (oldSeq >= seq) { 97 | NDN_LOG_WARN("Update has lower/equal seq no for prefix, doing nothing!"); 98 | return; 99 | } 100 | 101 | // Delete the last sequence prefix from the iblt 102 | // Because we don't insert zeroth prefix in IBF so no need to delete that 103 | if (oldSeq != 0) { 104 | ndn::Name prefixWithSeq = ndn::Name(prefix).appendNumber(oldSeq); 105 | auto hashIt = m_biMap.right.find(prefixWithSeq); 106 | if (hashIt != m_biMap.right.end()) { 107 | m_iblt.erase(hashIt->second); 108 | m_biMap.right.erase(hashIt); 109 | } 110 | } 111 | 112 | // Insert the new seq no in m_prefixes, m_biMap, and m_iblt 113 | it->second = seq; 114 | ndn::Name prefixWithSeq = ndn::Name(prefix).appendNumber(seq); 115 | auto newHash = detail::murmurHash3(detail::N_HASHCHECK, prefixWithSeq); 116 | m_biMap.insert({newHash, prefixWithSeq}); 117 | m_iblt.insert(newHash); 118 | 119 | m_numOwnElements += (seq - oldSeq); 120 | } 121 | 122 | void 123 | ProducerBase::sendApplicationNack(const ndn::Name& name) 124 | { 125 | NDN_LOG_DEBUG("Sending application nack"); 126 | 127 | ndn::Name dataName(name); 128 | m_iblt.appendToName(dataName); 129 | dataName.appendSegment(0); 130 | ndn::Data data(dataName); 131 | data.setContentType(ndn::tlv::ContentType_Nack) 132 | .setFreshnessPeriod(m_syncReplyFreshness) 133 | .setFinalBlock(dataName[-1]); 134 | 135 | m_keyChain.sign(data); 136 | m_face.put(data); 137 | } 138 | 139 | void 140 | ProducerBase::onRegisterFailed(const ndn::Name& prefix, const std::string& msg) 141 | { 142 | NDN_LOG_ERROR("onRegisterFailed(" << prefix << "): " << msg); 143 | NDN_THROW(Error(msg)); 144 | } 145 | 146 | } // namespace psync 147 | -------------------------------------------------------------------------------- /PSync/producer-base.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #ifndef PSYNC_PRODUCER_BASE_HPP 21 | #define PSYNC_PRODUCER_BASE_HPP 22 | 23 | #include "PSync/common.hpp" 24 | #include "PSync/detail/access-specifiers.hpp" 25 | #include "PSync/detail/iblt.hpp" 26 | #include "PSync/segment-publisher.hpp" 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | 36 | #include 37 | 38 | namespace psync { 39 | 40 | namespace bm = boost::bimaps; 41 | 42 | /** 43 | * @brief Base class for PartialProducer and FullProducer 44 | * 45 | * Contains code common to both. 46 | */ 47 | class ProducerBase 48 | { 49 | public: 50 | class Error : public std::runtime_error 51 | { 52 | public: 53 | using std::runtime_error::runtime_error; 54 | }; 55 | 56 | PSYNC_PUBLIC_WITH_TESTS_ELSE_PROTECTED: 57 | /** 58 | * @brief Constructor 59 | * 60 | * @param face Application's face 61 | * @param keyChain KeyChain instance to use for signing 62 | * @param expectedNumEntries Expected number of entries in IBF 63 | * @param syncPrefix The prefix of the sync group 64 | * @param syncReplyFreshness FreshnessPeriod of sync data 65 | * @param ibltCompression Compression scheme to use for IBF 66 | * @param contentCompression Compression scheme to use for Data content 67 | */ 68 | ProducerBase(ndn::Face& face, 69 | ndn::KeyChain& keyChain, 70 | size_t expectedNumEntries, 71 | const ndn::Name& syncPrefix, 72 | ndn::time::milliseconds syncReplyFreshness = SYNC_REPLY_FRESHNESS, 73 | CompressionScheme ibltCompression = CompressionScheme::NONE, 74 | CompressionScheme contentCompression = CompressionScheme::NONE); 75 | 76 | public: 77 | /** 78 | * @brief Returns the current sequence number of the given prefix 79 | * 80 | * @param prefix prefix to get the sequence number of 81 | */ 82 | std::optional 83 | getSeqNo(const ndn::Name& prefix) const 84 | { 85 | auto it = m_prefixes.find(prefix); 86 | if (it == m_prefixes.end()) { 87 | return std::nullopt; 88 | } 89 | return it->second; 90 | } 91 | 92 | /** 93 | * @brief Adds a user node for synchronization 94 | * 95 | * Initializes m_prefixes[prefix] to zero 96 | * Does not add zero-th sequence number to IBF 97 | * because if a large number of user nodes are added 98 | * then decoding of the difference between own IBF and 99 | * other IBF will not be possible 100 | * 101 | * @param prefix the user node to be added 102 | */ 103 | bool 104 | addUserNode(const ndn::Name& prefix); 105 | 106 | /** 107 | * @brief Remove the user node from synchronization 108 | * 109 | * Erases prefix from IBF and other maps 110 | * 111 | * @param prefix the user node to be removed 112 | */ 113 | void 114 | removeUserNode(const ndn::Name& prefix); 115 | 116 | PSYNC_PUBLIC_WITH_TESTS_ELSE_PROTECTED: 117 | /** 118 | * @brief Update m_prefixes and IBF with the given prefix and seq 119 | * 120 | * Whoever calls this needs to make sure that prefix is in m_prefixes 121 | * We remove already existing prefix/seq from IBF 122 | * (unless seq is zero because we don't insert zero seq into IBF) 123 | * Then we update m_prefixes, m_biMap, and IBF 124 | * 125 | * @param prefix prefix of the update 126 | * @param seq sequence number of the update 127 | */ 128 | void 129 | updateSeqNo(const ndn::Name& prefix, uint64_t seq); 130 | 131 | bool 132 | isUserNode(const ndn::Name& prefix) const 133 | { 134 | return m_prefixes.find(prefix) != m_prefixes.end(); 135 | } 136 | 137 | /** 138 | * @brief Sends a data packet with content type nack 139 | * 140 | * Producer sends a nack to consumer if consumer has very old IBF 141 | * whose differences with latest IBF can't be decoded successfully 142 | * 143 | * @param name send application nack with this name 144 | */ 145 | void 146 | sendApplicationNack(const ndn::Name& name); 147 | 148 | /** 149 | * @brief Logs a message and throws if setting an interest filter fails 150 | */ 151 | [[noreturn]] static void 152 | onRegisterFailed(const ndn::Name& prefix, const std::string& msg); 153 | 154 | PSYNC_PUBLIC_WITH_TESTS_ELSE_PROTECTED: 155 | ndn::Face& m_face; 156 | ndn::KeyChain& m_keyChain; 157 | ndn::Scheduler m_scheduler; 158 | ndn::random::RandomNumberEngine& m_rng; 159 | 160 | detail::IBLT m_iblt; 161 | 162 | // prefix and sequence number 163 | std::map m_prefixes; 164 | 165 | using HashNameBiMap = bm::bimap, 166 | bm::unordered_set_of>>; 167 | HashNameBiMap m_biMap; 168 | 169 | SegmentPublisher m_segmentPublisher; 170 | 171 | const size_t m_expectedNumEntries; 172 | // Threshold is used check if the differences are greater 173 | // than it and whether we need to update the other side. 174 | const size_t m_threshold; 175 | const ndn::Name m_syncPrefix; 176 | const ndn::time::milliseconds m_syncReplyFreshness; 177 | const CompressionScheme m_ibltCompression; 178 | const CompressionScheme m_contentCompression; 179 | uint64_t m_numOwnElements = 0; 180 | }; 181 | 182 | } // namespace psync 183 | 184 | #endif // PSYNC_PRODUCER_BASE_HPP 185 | -------------------------------------------------------------------------------- /PSync/segment-publisher.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2023, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | **/ 19 | 20 | #include "PSync/segment-publisher.hpp" 21 | 22 | namespace psync { 23 | 24 | SegmentPublisher::SegmentPublisher(ndn::Face& face, ndn::KeyChain& keyChain, 25 | const ndn::security::SigningInfo& signingInfo, size_t imsLimit) 26 | : m_face(face) 27 | , m_scheduler(m_face.getIoContext()) 28 | , m_segmenter(keyChain, signingInfo) 29 | , m_ims(imsLimit) 30 | { 31 | } 32 | 33 | void 34 | SegmentPublisher::publish(const ndn::Name& interestName, const ndn::Name& dataName, 35 | ndn::span buffer, ndn::time::milliseconds freshness) 36 | { 37 | auto segments = m_segmenter.segment(buffer, ndn::Name(dataName).appendVersion(), 38 | ndn::MAX_NDN_PACKET_SIZE >> 1, freshness); 39 | for (const auto& data : segments) { 40 | m_ims.insert(*data, freshness); 41 | m_scheduler.schedule(freshness, [this, name = data->getName()] { m_ims.erase(name); }); 42 | } 43 | 44 | // Put on face only the segment which has a pending interest, 45 | // otherwise the segment is unsolicited 46 | uint64_t interestSegment = 0; 47 | if (interestName[-1].isSegment()) { 48 | interestSegment = interestName[-1].toSegment(); 49 | } 50 | if (interestSegment < segments.size()) { 51 | m_face.put(*segments[interestSegment]); 52 | } 53 | } 54 | 55 | bool 56 | SegmentPublisher::replyFromStore(const ndn::Name& interestName) 57 | { 58 | auto it = m_ims.find(interestName); 59 | if (it != nullptr) { 60 | m_face.put(*it); 61 | return true; 62 | } 63 | return false; 64 | } 65 | 66 | } // namespace psync 67 | -------------------------------------------------------------------------------- /PSync/segment-publisher.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2023, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #ifndef PSYNC_SEGMENT_PUBLISHER_HPP 21 | #define PSYNC_SEGMENT_PUBLISHER_HPP 22 | 23 | #include "PSync/detail/access-specifiers.hpp" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | namespace psync { 31 | 32 | /** 33 | * @brief Helper class to publish segmented data. 34 | */ 35 | class SegmentPublisher 36 | { 37 | public: 38 | SegmentPublisher(ndn::Face& face, ndn::KeyChain& keyChain, 39 | const ndn::security::SigningInfo& signingInfo = ndn::security::SigningInfo(), 40 | size_t imsLimit = 100); 41 | 42 | /** 43 | * @brief Put all the segments in memory. 44 | * 45 | * @param interestName the interest name, to determine the sequence to be answered immediately 46 | * @param dataName the data name, has components after interest name 47 | * @param buffer the content of the data 48 | * @param freshness freshness period of the segments 49 | */ 50 | void 51 | publish(const ndn::Name& interestName, const ndn::Name& dataName, 52 | ndn::span buffer, ndn::time::milliseconds freshness); 53 | 54 | /** 55 | * @brief Try to reply from memory, return false if we cannot find the segment. 56 | * 57 | * The caller is then expected to use publish() if this returns false. 58 | */ 59 | bool 60 | replyFromStore(const ndn::Name& interestName); 61 | 62 | private: 63 | ndn::Face& m_face; 64 | ndn::Scheduler m_scheduler; 65 | ndn::Segmenter m_segmenter; 66 | 67 | PSYNC_PUBLIC_WITH_TESTS_ELSE_PRIVATE: 68 | ndn::InMemoryStorageFifo m_ims; 69 | }; 70 | 71 | } // namespace psync 72 | 73 | #endif // PSYNC_SEGMENT_PUBLISHER_HPP 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSync: Partial and Full Synchronization Library for NDN 2 | 3 | ![Latest version](https://img.shields.io/github/v/tag/named-data/PSync?label=Latest%20version) 4 | ![Language](https://img.shields.io/badge/C%2B%2B-17-blue) 5 | [![CI](https://github.com/named-data/PSync/actions/workflows/ci.yml/badge.svg)](https://github.com/named-data/PSync/actions/workflows/ci.yml) 6 | [![Docs](https://github.com/named-data/PSync/actions/workflows/docs.yml/badge.svg)](https://github.com/named-data/PSync/actions/workflows/docs.yml) 7 | 8 | The PSync library implements the 9 | [PSync protocol](https://named-data.net/wp-content/uploads/2017/05/scalable_name-based_data_synchronization.pdf). 10 | It uses Invertible Bloom Lookup Table (IBLT), also known as Invertible Bloom Filter (IBF), 11 | to represent the state of a producer in partial sync mode and the state of a node in full 12 | sync mode. An IBF is a compact data structure where difference of two IBFs can be computed 13 | efficiently. In partial sync, PSync uses a Bloom Filter to represent the subscription list 14 | of the consumer. 15 | 16 | PSync uses the [ndn-cxx](https://github.com/named-data/ndn-cxx) library. 17 | 18 | ## Installation 19 | 20 | ### Prerequisites 21 | 22 | * [ndn-cxx and its dependencies](https://docs.named-data.net/ndn-cxx/current/INSTALL.html) 23 | 24 | ### Build 25 | 26 | To build PSync from source: 27 | 28 | ```shell 29 | ./waf configure 30 | ./waf 31 | sudo ./waf install 32 | ``` 33 | 34 | To build on memory constrained systems, please use `./waf -j1` instead of `./waf`. This 35 | will disable parallel compilation. 36 | 37 | If configured with tests (`./waf configure --with-tests`), the above commands will also 38 | build a suite of unit tests that can be run with `./build/unit-tests`. 39 | 40 | ## Reporting bugs 41 | 42 | Please submit any bug reports or feature requests to the 43 | [PSync issue tracker](https://redmine.named-data.net/projects/psync/issues). 44 | 45 | ## Contributing 46 | 47 | Contributions to PSync are greatly appreciated and can be made through our 48 | [Gerrit code review site](https://gerrit.named-data.net/). 49 | If you are new to the NDN software community, please read our [Contributor's Guide]( 50 | https://github.com/named-data/.github/blob/main/CONTRIBUTING.md) to get started. 51 | 52 | ## License 53 | 54 | PSync is free software distributed under the GNU Lesser General Public License version 3. 55 | See [`COPYING.md`](COPYING.md) and [`COPYING.lesser`](COPYING.lesser) for details. 56 | 57 | PSync contains third-party software, licensed under the following licenses: 58 | 59 | * The *C++ Bloom Filter Library* is licensed under the 60 | [MIT license](https://www.partow.net/programming/bloomfilter/index.html) 61 | * *IBLT_Cplusplus* is licensed under the 62 | [MIT license](https://github.com/gavinandresen/IBLT_Cplusplus/blob/master/LICENSE) 63 | * The *waf* build system is licensed under the [3-clause BSD license](waf) 64 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # For the full list of built-in configuration values, see the documentation: 4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 5 | 6 | import importlib.util 7 | import sys 8 | 9 | # -- Project information ----------------------------------------------------- 10 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 11 | 12 | project = 'PSync: Partial/Full Synchronization Protocol for NDN' 13 | copyright = 'Copyright © 2018-2024 Named Data Networking Project.' 14 | author = 'Named Data Networking Project' 15 | 16 | # The short X.Y version. 17 | #version = '' 18 | 19 | # The full version, including alpha/beta/rc tags. 20 | #release = '' 21 | 22 | # There are two options for replacing |today|: either, you set today to some 23 | # non-false value, then it is used: 24 | #today = '' 25 | # Else, today_fmt is used as the format for a strftime call. 26 | today_fmt = '%Y-%m-%d' 27 | 28 | 29 | # -- General configuration --------------------------------------------------- 30 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 31 | 32 | needs_sphinx = '4.0' 33 | extensions = [ 34 | 'sphinx.ext.extlinks', 35 | 'sphinx.ext.todo', 36 | ] 37 | 38 | def addExtensionIfExists(extension: str): 39 | try: 40 | if importlib.util.find_spec(extension) is None: 41 | raise ModuleNotFoundError(extension) 42 | except (ImportError, ValueError): 43 | sys.stderr.write(f'WARNING: Extension {extension!r} not found. ' 44 | 'Some documentation may not build correctly.\n') 45 | else: 46 | extensions.append(extension) 47 | 48 | addExtensionIfExists('sphinxcontrib.doxylink') 49 | 50 | templates_path = ['_templates'] 51 | exclude_patterns = ['Thumbs.db', '.DS_Store'] 52 | 53 | 54 | # -- Options for HTML output ------------------------------------------------- 55 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 56 | 57 | html_theme = 'named_data_theme' 58 | html_theme_path = ['.'] 59 | 60 | # Add any paths that contain custom static files (such as style sheets) here, 61 | # relative to this directory. They are copied after the builtin static files, 62 | # so a file named "default.css" will overwrite the builtin "default.css". 63 | html_static_path = ['_static'] 64 | 65 | html_copy_source = False 66 | html_show_sourcelink = False 67 | 68 | # Disable syntax highlighting of code blocks by default. 69 | highlight_language = 'none' 70 | 71 | 72 | # -- Misc options ------------------------------------------------------------ 73 | 74 | doxylink = { 75 | 'psync': ('PSync.tag', 'doxygen/'), 76 | } 77 | 78 | extlinks = { 79 | 'issue': ('https://redmine.named-data.net/issues/%s', 'issue #%s'), 80 | } 81 | -------------------------------------------------------------------------------- /docs/examples.rst: -------------------------------------------------------------------------------- 1 | Examples 2 | ======== 3 | 4 | The example programs in the ``examples/`` subdirectory are not built by default. 5 | To enable them, use the ``--with-examples`` configure option: 6 | 7 | .. code-block:: bash 8 | 9 | ./waf configure --with-examples 10 | ./waf 11 | 12 | Once built, the example binaries can be found under ``build/examples/``: 13 | 14 | * Full sync: ``psync-full-sync`` 15 | * Partial sync: ``psync-producer`` and ``psync-consumer`` 16 | 17 | If the library is installed to the system using ``./waf install``, then the examples 18 | will also be installed and can be executed directly. 19 | 20 | Partial Sync Example 21 | -------------------- 22 | 23 | The partial sync example of PSync has two parts: producer and consumer. 24 | These can be run on a machine after starting NFD. 25 | 26 | Producer 27 | ^^^^^^^^ 28 | 29 | * Enable logging for the producer. 30 | 31 | .. code-block:: bash 32 | 33 | export NDN_LOG="examples.PartialSyncProducerApp=INFO" 34 | 35 | * Start the producer that will listen on ``/sync`` and publish one update for each 36 | of ``/a-0`` ... ``/a-9``. 37 | 38 | .. code-block:: bash 39 | 40 | psync-producer /sync /a 10 1 41 | 42 | * Sample output:: 43 | 44 | 1546280442.096296 INFO: [examples.PartialSyncProducerApp] Publish: /a-1/1 45 | 1546280456.053138 INFO: [examples.PartialSyncProducerApp] Publish: /a-6/1 46 | 1546280458.210415 INFO: [examples.PartialSyncProducerApp] Publish: /a-8/1 47 | 1546280469.954134 INFO: [examples.PartialSyncProducerApp] Publish: /a-5/1 48 | 1546280472.487425 INFO: [examples.PartialSyncProducerApp] Publish: /a-2/1 49 | 1546280473.466515 INFO: [examples.PartialSyncProducerApp] Publish: /a-7/1 50 | 1546280481.882258 INFO: [examples.PartialSyncProducerApp] Publish: /a-0/1 51 | 1546280484.201229 INFO: [examples.PartialSyncProducerApp] Publish: /a-4/1 52 | 1546280489.348968 INFO: [examples.PartialSyncProducerApp] Publish: /a-9/1 53 | 1546280491.420391 INFO: [examples.PartialSyncProducerApp] Publish: /a-3/1 54 | 55 | Consumer 56 | ^^^^^^^^ 57 | 58 | * In a new terminal, enable logging for the consumer. 59 | 60 | .. code-block:: bash 61 | 62 | export NDN_LOG="examples.PartialSyncConsumerApp=INFO" 63 | 64 | * Run the consumer to subscribe to 5 random prefixes from the publisher on ``/sync``. 65 | 66 | .. code-block:: bash 67 | 68 | psync-consumer /sync 5 69 | 70 | * Sample output from the consumer shows that it received updates only 71 | for the subscribed prefixes:: 72 | 73 | 1546280436.502769 INFO: [examples.PartialSyncConsumerApp] Subscribing to: /a-7 74 | 1546280436.502888 INFO: [examples.PartialSyncConsumerApp] Subscribing to: /a-9 75 | 1546280436.502911 INFO: [examples.PartialSyncConsumerApp] Subscribing to: /a-8 76 | 1546280436.502934 INFO: [examples.PartialSyncConsumerApp] Subscribing to: /a-4 77 | 1546280436.502956 INFO: [examples.PartialSyncConsumerApp] Subscribing to: /a-5 78 | 1546280458.211188 INFO: [examples.PartialSyncConsumerApp] Update: /a-8/1 79 | 1546280469.954886 INFO: [examples.PartialSyncConsumerApp] Update: /a-5/1 80 | 1546280473.467116 INFO: [examples.PartialSyncConsumerApp] Update: /a-7/1 81 | 1546280484.256181 INFO: [examples.PartialSyncConsumerApp] Update: /a-4/1 82 | 1546280489.349793 INFO: [examples.PartialSyncConsumerApp] Update: /a-9/1 83 | 84 | Full Sync Example 85 | ----------------- 86 | 87 | To demonstrate the full sync mode of PSync, ``psync-full-sync`` can be run on a 88 | machine after starting NFD. 89 | 90 | * Enable logging for the full sync demo app. 91 | 92 | .. code-block:: bash 93 | 94 | export NDN_LOG="examples.FullSyncApp=INFO" 95 | 96 | * Run the full sync example with sync prefix ``/sync``, user prefix ``/a``, 97 | and publish three updates for each user prefix: ``/a-0`` and ``/a-1``. 98 | This will simulate node *a*. 99 | 100 | .. code-block:: bash 101 | 102 | psync-full-sync /sync /a 2 3 103 | 104 | * Repeat for another user prefix, to simulate node *b*. 105 | 106 | .. code-block:: bash 107 | 108 | psync-full-sync /sync /b 2 3 109 | 110 | We should see that node *a* and node *b* have received each other's updates. 111 | 112 | * Sample output from node *a* shows that it received all updates from node *b* 113 | successfully:: 114 | 115 | 1546282730.759387 INFO: [examples.FullSyncApp] Update /b-1/1 116 | 1546282741.143225 INFO: [examples.FullSyncApp] Publish: /a-1/1 117 | 1546282749.375854 INFO: [examples.FullSyncApp] Publish: /a-0/1 118 | 1546282750.263246 INFO: [examples.FullSyncApp] Update /b-0/1 119 | 1546282765.875118 INFO: [examples.FullSyncApp] Update /b-1/2 120 | 1546282783.777807 INFO: [examples.FullSyncApp] Publish: /a-0/2 121 | 1546282794.565507 INFO: [examples.FullSyncApp] Publish: /a-0/3 122 | 1546282794.896895 INFO: [examples.FullSyncApp] Publish: /a-1/2 123 | 1546282803.839416 INFO: [examples.FullSyncApp] Update /b-0/2 124 | 1546282804.785867 INFO: [examples.FullSyncApp] Update /b-1/3 125 | 1546282845.273772 INFO: [examples.FullSyncApp] Publish: /a-1/3 126 | 1546282855.102790 INFO: [examples.FullSyncApp] Update /b-0/3 127 | 128 | * Sample output from node *b*:: 129 | 130 | 1546282730.758296 INFO: [examples.FullSyncApp] Publish: /b-1/1 131 | 1546282741.144027 INFO: [examples.FullSyncApp] Update /a-1/1 132 | 1546282749.376543 INFO: [examples.FullSyncApp] Update /a-0/1 133 | 1546282750.262244 INFO: [examples.FullSyncApp] Publish: /b-0/1 134 | 1546282765.296005 INFO: [examples.FullSyncApp] Publish: /b-1/2 135 | 1546282783.778769 INFO: [examples.FullSyncApp] Update /a-0/2 136 | 1546282794.566485 INFO: [examples.FullSyncApp] Update /a-0/3 137 | 1546282795.374339 INFO: [examples.FullSyncApp] Update /a-1/2 138 | 1546282803.838394 INFO: [examples.FullSyncApp] Publish: /b-0/2 139 | 1546282804.033214 INFO: [examples.FullSyncApp] Publish: /b-1/3 140 | 1546282845.274680 INFO: [examples.FullSyncApp] Update /a-1/3 141 | 1546282855.101780 INFO: [examples.FullSyncApp] Publish: /b-0/3 142 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | PSync: Partial/Full Sync Library based on BF and IBF 2 | ==================================================== 3 | 4 | .. toctree:: 5 | :hidden: 6 | :maxdepth: 2 7 | 8 | install 9 | examples 10 | release-notes 11 | releases 12 | 13 | PSync is a C++ library for name synchronization that implements the `PSync protocol 14 | `__. 15 | It uses Invertible Bloom Lookup Table (IBLT), also known as Invertible Bloom Filter (IBF), 16 | to represent the state of a producer in partial sync mode and the state of a node in full 17 | sync mode. An IBF is a compact data structure where difference of two IBFs can be computed 18 | efficiently. In partial sync, PSync uses a Bloom Filter to represent the subscription of 19 | list of the consumer. 20 | 21 | PSync uses the `ndn-cxx `__ library. 22 | 23 | Documentation 24 | ------------- 25 | 26 | - :doc:`install` 27 | - :doc:`examples` 28 | - :doc:`release-notes` 29 | - :doc:`releases` 30 | 31 | Issues 32 | ------ 33 | 34 | Please submit any bug reports or feature requests to the 35 | `PSync issue tracker `__. 36 | 37 | Contributing 38 | ------------ 39 | 40 | Contributions to PSync are greatly appreciated and can be made through our 41 | `Gerrit code review site `__. 42 | If you are new to the NDN software community, please read our `Contributor's Guide 43 | `__ to get started. 44 | 45 | License 46 | ------- 47 | 48 | PSync is free software: you can redistribute it and/or modify it under the terms of 49 | the GNU Lesser General Public License version 3 or later. For more information, see 50 | `COPYING.md `__ and 51 | `COPYING.lesser `__. 52 | -------------------------------------------------------------------------------- /docs/install.rst: -------------------------------------------------------------------------------- 1 | Installation Instructions 2 | ========================= 3 | 4 | Prerequisites 5 | ------------- 6 | 7 | - `ndn-cxx `__ and its dependencies 8 | 9 | Build 10 | ----- 11 | 12 | Run the following commands to build PSync: 13 | 14 | .. code-block:: sh 15 | 16 | ./waf configure 17 | ./waf 18 | sudo ./waf install 19 | -------------------------------------------------------------------------------- /docs/named_data_theme/layout.html: -------------------------------------------------------------------------------- 1 | {# 2 | named_data_theme/layout.html 3 | ~~~~~~~~~~~~~~~~~ 4 | #} 5 | {% extends "basic/layout.html" %} 6 | 7 | {% block header %} 8 | 9 |
10 | 11 | 12 |
13 |
14 | 17 |
18 | 19 | 20 | 23 |
24 |
25 | 26 | {% endblock %} 27 | 28 | {% block content %} 29 |
30 |
31 |
32 | {%- block document %} 33 | {{ super() }} 34 | {%- endblock %} 35 |
36 | 60 |
61 |
62 |
63 | {% endblock %} 64 | 65 | {% block footer %} 66 | 71 | 72 | 82 | {% endblock %} 83 | 84 | {% block relbar1 %}{% endblock %} 85 | {% block relbar2 %}{% endblock %} 86 | -------------------------------------------------------------------------------- /docs/named_data_theme/named_data_footer-with-analytics.html.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /docs/named_data_theme/named_data_footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/named_data_theme/named_data_header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | $projectname: $title 7 | $title 8 | 9 | 10 | 11 | $treeview 12 | $search 13 | $mathjax 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 |
24 | 25 | 26 |
27 |
28 | 31 |
32 | 33 | 34 | 37 |
38 |
39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/named_data_theme/static/bar-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/PSync/410520d9d5910a10272ea7c034756ea754d0dea1/docs/named_data_theme/static/bar-top.png -------------------------------------------------------------------------------- /docs/named_data_theme/static/base.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0px; 3 | padding: 0px; 4 | } 5 | 6 | html { font-size: 62.5%; } 7 | 8 | body { 9 | font-family: "Verdana", Arial, sans-serif; 10 | background-color: #eeeeec; 11 | color: #777; 12 | border-top: 4px solid #fd7800; 13 | } 14 | 15 | body { background: white; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1; color: #222222; position: relative; -webkit-font-smoothing: antialiased; } 16 | 17 | .clearer { 18 | clear: both; 19 | } 20 | 21 | .left { 22 | float: left; 23 | } 24 | 25 | .right { 26 | float: right; 27 | } 28 | 29 | .line-block { 30 | display: block; 31 | margin-top: 1em; 32 | margin-bottom: 1em; 33 | } 34 | 35 | .line-block .line-block { 36 | margin-top: 0; 37 | margin-bottom: 0; 38 | margin-left: 1.5em; 39 | } 40 | 41 | h1, h2, h3, h4 { 42 | font-family: "Georgia", "Times New Roman", serif; 43 | font-weight: normal; 44 | color: #3465a4; 45 | margin-bottom: .8em; 46 | } 47 | 48 | h1 { 49 | color: #204a87; 50 | } 51 | 52 | h2 { 53 | padding-bottom: .5em; 54 | border-bottom: 1px solid #3465a4; 55 | } 56 | 57 | a.headerlink { 58 | visibility: hidden; 59 | color: #dddddd; 60 | padding-left: .3em; 61 | } 62 | 63 | h1:hover > a.headerlink, 64 | h2:hover > a.headerlink, 65 | h3:hover > a.headerlink, 66 | h4:hover > a.headerlink, 67 | h5:hover > a.headerlink, 68 | h6:hover > a.headerlink, 69 | dt:hover > a.headerlink { 70 | visibility: visible; 71 | } 72 | -------------------------------------------------------------------------------- /docs/named_data_theme/static/bc_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/PSync/410520d9d5910a10272ea7c034756ea754d0dea1/docs/named_data_theme/static/bc_s.png -------------------------------------------------------------------------------- /docs/named_data_theme/static/default.css_t: -------------------------------------------------------------------------------- 1 | @import url("agogo.css"); 2 | 3 | pre { 4 | padding: 10px; 5 | background-color: #fafafa; 6 | color: #222; 7 | line-height: 1.2em; 8 | border: 2px solid #C6C9CB; 9 | font-size: 1.1em; 10 | /* margin: 1.5em 0 1.5em 0; */ 11 | margin: 0; 12 | border-right-style: none; 13 | border-left-style: none; 14 | } 15 | -------------------------------------------------------------------------------- /docs/named_data_theme/static/nav_f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/PSync/410520d9d5910a10272ea7c034756ea754d0dea1/docs/named_data_theme/static/nav_f.png -------------------------------------------------------------------------------- /docs/named_data_theme/static/tab_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/PSync/410520d9d5910a10272ea7c034756ea754d0dea1/docs/named_data_theme/static/tab_b.png -------------------------------------------------------------------------------- /docs/named_data_theme/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = agogo 3 | stylesheet = named_data_style.css 4 | # pygments_style = sphinx 5 | 6 | theme_bodyfont = "normal 12px Verdana, sans-serif" 7 | theme_bgcolor = "#ccc" 8 | 9 | theme_documentwidth = "100%" 10 | theme_textalign = "left" 11 | 12 | [options] 13 | 14 | stickysidebar = true 15 | collapsiblesidebar = true 16 | -------------------------------------------------------------------------------- /docs/release-notes.rst: -------------------------------------------------------------------------------- 1 | Release Notes 2 | ============= 3 | 4 | .. include:: release-notes/release-notes-0.5.0.rst 5 | -------------------------------------------------------------------------------- /docs/release-notes/release-notes-0.1.0.rst: -------------------------------------------------------------------------------- 1 | PSync version 0.1.0 2 | ------------------- 3 | 4 | *Release date: January 25, 2019* 5 | 6 | Version 0.1.0 is the initial release of PSync. The PSync library implements the `PSync protocol 7 | `__. 8 | It uses Invertible Bloom Lookup Table (IBLT), also known as Invertible Bloom Filter (IBF), to represent 9 | the state of a producer in partial sync mode and the state of a node in full sync mode. An IBF is a 10 | compact data structure where difference of two IBFs can be computed efficiently. 11 | In partial sync, PSync uses a Bloom Filter to represent the subscription of list of the consumer. 12 | PSync uses the `ndn-cxx `__ library as NDN development 13 | library. 14 | 15 | PSync is an open source project licensed under LGPL 3.0. We highly welcome all contributions to the PSync code base, provided that they can be licensed under LGPL 3.0+ or other compatible license. 16 | 17 | PSync supports the following features: 18 | 19 | - **Partial Synchronization** 20 | 21 | + Allows consumers to subscribe to a subset of a producer's offered prefixes. 22 | + Consumer can sync with any replicated producer in the same sync group. 23 | + A PartialProducer class to publish prefixes and updates. 24 | + A Consumer class to subscribe to the producer (hello) and listen for updates (sync). 25 | 26 | - **Full Synchronization** 27 | 28 | + Similar to ChronoSync, allows full synchronization of names amongst all the nodes in the sync group. 29 | + FullProducer class can be used start syncing with a sync group. 30 | 31 | - **Miscellaneous** 32 | 33 | + Uses ndn-cxx ``SegmentFetcher`` to segment hello and sync data. 34 | + Provide a SegmentPublisher that can be used by other NDN applications. 35 | + Examples are provided in ``examples`` folder to show how to use the library. 36 | -------------------------------------------------------------------------------- /docs/release-notes/release-notes-0.2.0.rst: -------------------------------------------------------------------------------- 1 | PSync version 0.2.0 2 | ------------------- 3 | 4 | *Release date: February 2, 2020* 5 | 6 | New features 7 | ^^^^^^^^^^^^ 8 | 9 | - *(breaking change)* Compress Sync Data in Full Sync by default (:issue:`5061`, :issue:`4917`) 10 | - Support various compression schemes for compressing IBF and Sync Data (:issue:`5061`) 11 | 12 | Improvements and bug fixes 13 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 14 | 15 | - Use constant interest timeout for (discovery) Sync Interest in ``SegmentFetcher`` (:issue:`4945`) 16 | - Make default jitter shorter, react to NACK after jitter 17 | - Use ``boost::bimap`` instead of two ``std::map`` variables to store hash to prefix matching 18 | -------------------------------------------------------------------------------- /docs/release-notes/release-notes-0.3.0.rst: -------------------------------------------------------------------------------- 1 | PSync version 0.3.0 2 | ------------------- 3 | 4 | *Release date: January 3, 2021* 5 | 6 | The build requirements have been increased to require Clang >= 4.0, Xcode >= 9.0, and Python >= 3.6. 7 | Meanwhile, it is *recommended* to use GCC >= 7.4.0 and Boost >= 1.65.1. 8 | This effectively drops official support for Ubuntu 16.04 when using distribution-provided Boost 9 | packages -- PSync may still work on this platform, but we provide no official support for it. 10 | 11 | New features 12 | ^^^^^^^^^^^^ 13 | 14 | - *(breaking change)* Consumer: change hello data callback to include sequence number (:issue:`5122`) 15 | 16 | Improvements and bug fixes 17 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 18 | 19 | - *(breaking change)* IBLT: make encoding endian safe (:issue:`5076`) 20 | - Reset cached wire encoding after adding names (:issue:`5083`) 21 | - Consumer reacts faster on Sync Interest timeout (:issue:`5124`) 22 | - Move private classes and functions to ``psync::detail`` namespace 23 | - Improved unit tests 24 | 25 | Known issues 26 | ^^^^^^^^^^^^ 27 | 28 | - We have taken some steps to be endian safe but PSync is not completely endian safe yet 29 | (:issue:`4818`) 30 | -------------------------------------------------------------------------------- /docs/release-notes/release-notes-0.4.0.rst: -------------------------------------------------------------------------------- 1 | PSync version 0.4.0 2 | ------------------- 3 | 4 | *Release date: January 18, 2023* 5 | 6 | Important changes and new features 7 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 8 | 9 | - The minimum build requirements have been increased as follows: 10 | 11 | - Either GCC >= 7.4.0 or Clang >= 6.0 is required on Linux 12 | - On macOS, Xcode 11.3 or later is recommended; older versions may still work but are not 13 | officially supported 14 | - Boost >= 1.65.1 and ndn-cxx >= 0.8.1 are required on all platforms 15 | - Sphinx 4.0 or later is required to build the documentation 16 | 17 | - *(breaking change)* Switch to C++17 18 | - *(breaking change)* The Name TLV value is now hashed directly instead of being converted 19 | to URI format first (:issue:`4838`) 20 | - Add ``incomingFace`` field to missing data notifications (:issue:`3626`) 21 | - *(breaking change)* Add ``ndn::KeyChain`` parameter to the producer API 22 | - Provide API to remove a subscription in partial sync :psync:`Consumer` (:issue:`5242`) 23 | 24 | Improvements and bug fixes 25 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 26 | 27 | - Use ndn-cxx's ``ndn::util::Segmenter`` class in :psync:`SegmentPublisher` 28 | - Fix compilation against the latest version of ndn-cxx 29 | - Stop using the ``gold`` linker on Linux; prefer instead linking with ``lld`` if installed 30 | - Update waf build system to version 2.0.24 31 | - Various documentation improvements 32 | 33 | Known issues 34 | ^^^^^^^^^^^^ 35 | 36 | - We have taken some steps to be endian safe but PSync is not completely endian safe yet 37 | (:issue:`4818`) 38 | -------------------------------------------------------------------------------- /docs/release-notes/release-notes-0.5.0.rst: -------------------------------------------------------------------------------- 1 | PSync version 0.5.0 2 | ------------------- 3 | 4 | *Release date: August 9, 2024* 5 | 6 | Important changes and new features 7 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 8 | 9 | - The build dependencies have been increased as follows: 10 | 11 | - GCC >= 9.3 or Clang >= 7.0 are strongly *recommended* on Linux; GCC 8.x is also known 12 | to work but is not officially supported 13 | - Xcode 13 or later is *recommended* on macOS; older versions may still work but are not 14 | officially supported 15 | - Boost >= 1.71.0 and ndn-cxx >= 0.9.0 are *required* on all platforms 16 | 17 | - We have moved to a modified FullSync algorithm originally designed by Ashlesh Gawande 18 | as part of `his thesis work 19 | `__. 20 | These changes are intended to lower delay and overhead when using FullSync 21 | 22 | Improvements and bug fixes 23 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 24 | 25 | - Constructor options are now passed in as a single ``Options`` object; the old constructor 26 | API is considered deprecated (:issue:`5069`) 27 | - :psync:`FullProducer` no longer appends the hash of the IBF to the data name; this had no 28 | functional purpose (:issue:`5066`) 29 | - Refactoring of IBLT implementation (:issue:`4825`) 30 | - Various adjustments to match ndn-cxx namespace changes 31 | - Fix building the documentation with Python 3.12 (:issue:`5298`) 32 | - Update waf build system to version 2.0.27 33 | - Miscellanous CI and build improvements 34 | -------------------------------------------------------------------------------- /docs/releases.rst: -------------------------------------------------------------------------------- 1 | Release History 2 | =============== 3 | 4 | .. toctree:: 5 | :glob: 6 | :hidden: 7 | :maxdepth: 1 8 | :reversed: 9 | 10 | release-notes/* 11 | 12 | * **PSync version 0.5.0** 13 | \| :doc:`Release Notes ` 14 | \| `GitHub `__ 15 | \| `Source download `__ 16 | (`checksum `__) 17 | \| `Documentation `__ 18 | 19 | * **PSync version 0.4.0** 20 | \| :doc:`Release Notes ` 21 | \| `GitHub `__ 22 | \| `Documentation `__ 23 | 24 | * **PSync version 0.3.0** 25 | \| :doc:`Release Notes ` 26 | \| `GitHub `__ 27 | \| `Documentation `__ 28 | 29 | * **PSync version 0.2.0** 30 | \| :doc:`Release Notes ` 31 | \| `GitHub `__ 32 | \| `Documentation `__ 33 | 34 | * **PSync version 0.1.0** 35 | \| :doc:`Release Notes ` 36 | \| `GitHub `__ 37 | \| `Documentation `__ 38 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | docutils>=0.20 2 | sphinx>=7.0.1,<9 3 | sphinxcontrib-doxylink~=1.13 4 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # PSync examples 2 | 3 | By default, the examples in this folder are not built. To enable them, use the 4 | `--with-examples` configure option. For example: 5 | 6 | ```bash 7 | ./waf configure --with-examples 8 | ./waf 9 | ``` 10 | 11 | The example binaries can be found in `build/examples`: 12 | 13 | - Full sync: `psync-full-sync` 14 | - Partial sync: `psync-producer` and `psync-consumer` 15 | 16 | If the library is installed to the system using `./waf install` then the examples 17 | are also installed and can be executed directly. 18 | 19 | ## Partial Sync example 20 | 21 | Partial sync example of PSync has two parts: producer and consumer. 22 | These can be run on a machine after starting NFD. 23 | 24 | ### Producer 25 | 26 | - Enable the logs for producer: 27 | 28 | ```bash 29 | export NDN_LOG=examples.PartialSyncProducerApp=INFO 30 | ``` 31 | 32 | - Start the producer that will listen on `/sync` and publish 1 update for 33 | `/a-0` ... `/a-9` each. 34 | 35 | ```bash 36 | psync-producer /sync /a 10 1 37 | ``` 38 | 39 | - Sample output: 40 | 41 | ``` 42 | 1546280442.096296 INFO: [examples.PartialSyncProducerApp] Publish: /a-1/1 43 | 1546280456.053138 INFO: [examples.PartialSyncProducerApp] Publish: /a-6/1 44 | 1546280458.210415 INFO: [examples.PartialSyncProducerApp] Publish: /a-8/1 45 | 1546280469.954134 INFO: [examples.PartialSyncProducerApp] Publish: /a-5/1 46 | 1546280472.487425 INFO: [examples.PartialSyncProducerApp] Publish: /a-2/1 47 | 1546280473.466515 INFO: [examples.PartialSyncProducerApp] Publish: /a-7/1 48 | 1546280481.882258 INFO: [examples.PartialSyncProducerApp] Publish: /a-0/1 49 | 1546280484.201229 INFO: [examples.PartialSyncProducerApp] Publish: /a-4/1 50 | 1546280489.348968 INFO: [examples.PartialSyncProducerApp] Publish: /a-9/1 51 | 1546280491.420391 INFO: [examples.PartialSyncProducerApp] Publish: /a-3/1 52 | ``` 53 | 54 | ### Consumer 55 | 56 | - In a new terminal, enable the logs for consumer: 57 | 58 | ```bash 59 | export NDN_LOG=examples.PartialSyncConsumerApp=INFO 60 | ``` 61 | 62 | - Run the consumer to subscribe to 5 random prefixes from the publisher on `/sync` 63 | 64 | ```bash 65 | psync-consumer /sync 5 66 | ``` 67 | 68 | - Sample output from the consumer shows that it received updates only 69 | for the subscribed prefixes: 70 | 71 | ``` 72 | 1546280436.502769 INFO: [examples.PartialSyncConsumerApp] Subscribing to: /a-7 73 | 1546280436.502888 INFO: [examples.PartialSyncConsumerApp] Subscribing to: /a-9 74 | 1546280436.502911 INFO: [examples.PartialSyncConsumerApp] Subscribing to: /a-8 75 | 1546280436.502934 INFO: [examples.PartialSyncConsumerApp] Subscribing to: /a-4 76 | 1546280436.502956 INFO: [examples.PartialSyncConsumerApp] Subscribing to: /a-5 77 | 1546280458.211188 INFO: [examples.PartialSyncConsumerApp] Update: /a-8/1 78 | 1546280469.954886 INFO: [examples.PartialSyncConsumerApp] Update: /a-5/1 79 | 1546280473.467116 INFO: [examples.PartialSyncConsumerApp] Update: /a-7/1 80 | 1546280484.256181 INFO: [examples.PartialSyncConsumerApp] Update: /a-4/1 81 | 1546280489.349793 INFO: [examples.PartialSyncConsumerApp] Update: /a-9/1 82 | ``` 83 | 84 | ## Full Sync example 85 | 86 | To demonstrate full sync mode of PSync, `psync-full-sync` 87 | can be run on a machine after starting NFD: 88 | 89 | - Enable the logs for full sync: 90 | 91 | ```bash 92 | export NDN_LOG=examples.FullSyncApp=INFO 93 | ``` 94 | 95 | - Run the full sync example with sync prefix `/sync`, user prefix `/a`, 96 | and publish three updates for each user prefix: `/a-0` and `/a-1`. This will simulate node a. 97 | 98 | ```bash 99 | psync-full-sync /sync /a 2 3 100 | ``` 101 | 102 | - Repeat for another user prefix, to simulate node b: 103 | 104 | ```bash 105 | psync-full-sync /sync /b 2 3 106 | ``` 107 | 108 | We should see that node a and node b have received each other's updates. 109 | 110 | - Sample output from node a shows that it received all updates 111 | from node b successfully: 112 | 113 | ``` 114 | 1546282730.759387 INFO: [examples.FullSyncApp] Update /b-1/1 115 | 1546282741.143225 INFO: [examples.FullSyncApp] Publish: /a-1/1 116 | 1546282749.375854 INFO: [examples.FullSyncApp] Publish: /a-0/1 117 | 1546282750.263246 INFO: [examples.FullSyncApp] Update /b-0/1 118 | 1546282765.875118 INFO: [examples.FullSyncApp] Update /b-1/2 119 | 1546282783.777807 INFO: [examples.FullSyncApp] Publish: /a-0/2 120 | 1546282794.565507 INFO: [examples.FullSyncApp] Publish: /a-0/3 121 | 1546282794.896895 INFO: [examples.FullSyncApp] Publish: /a-1/2 122 | 1546282803.839416 INFO: [examples.FullSyncApp] Update /b-0/2 123 | 1546282804.785867 INFO: [examples.FullSyncApp] Update /b-1/3 124 | 1546282845.273772 INFO: [examples.FullSyncApp] Publish: /a-1/3 125 | 1546282855.102790 INFO: [examples.FullSyncApp] Update /b-0/3 126 | ``` 127 | 128 | - Sample output from node b: 129 | 130 | ``` 131 | 1546282730.758296 INFO: [examples.FullSyncApp] Publish: /b-1/1 132 | 1546282741.144027 INFO: [examples.FullSyncApp] Update /a-1/1 133 | 1546282749.376543 INFO: [examples.FullSyncApp] Update /a-0/1 134 | 1546282750.262244 INFO: [examples.FullSyncApp] Publish: /b-0/1 135 | 1546282765.296005 INFO: [examples.FullSyncApp] Publish: /b-1/2 136 | 1546282783.778769 INFO: [examples.FullSyncApp] Update /a-0/2 137 | 1546282794.566485 INFO: [examples.FullSyncApp] Update /a-0/3 138 | 1546282795.374339 INFO: [examples.FullSyncApp] Update /a-1/2 139 | 1546282803.838394 INFO: [examples.FullSyncApp] Publish: /b-0/2 140 | 1546282804.033214 INFO: [examples.FullSyncApp] Publish: /b-1/3 141 | 1546282845.274680 INFO: [examples.FullSyncApp] Update /a-1/3 142 | 1546282855.101780 INFO: [examples.FullSyncApp] Publish: /b-0/3 143 | ``` 144 | -------------------------------------------------------------------------------- /examples/consumer.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2023, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | NDN_LOG_INIT(examples.PartialSyncConsumerApp); 29 | 30 | class PSyncConsumer 31 | { 32 | public: 33 | /** 34 | * @brief Initialize consumer and start hello process 35 | * @param syncPrefix should be the same as producer 36 | * @param nSub number of subscriptions is used for the bloom filter (subscription list) size 37 | */ 38 | PSyncConsumer(const ndn::Name& syncPrefix, int nSub) 39 | : m_nSub(nSub) 40 | , m_consumer(m_face, syncPrefix, [this] { 41 | psync::Consumer::Options opts; 42 | opts.onHelloData = std::bind(&PSyncConsumer::afterReceiveHelloData, this, _1); 43 | opts.onUpdate = std::bind(&PSyncConsumer::processSyncUpdate, this, _1); 44 | opts.bfCount = m_nSub; 45 | return opts; 46 | } ()) 47 | { 48 | // This starts the consumer side by sending a hello interest to the producer 49 | // When the producer responds with hello data, afterReceiveHelloData is called 50 | m_consumer.sendHelloInterest(); 51 | } 52 | 53 | void 54 | run() 55 | { 56 | m_face.processEvents(); 57 | } 58 | 59 | private: 60 | void 61 | afterReceiveHelloData(const std::map& availSubs) 62 | { 63 | std::vector sensors; 64 | sensors.reserve(availSubs.size()); 65 | for (const auto& it : availSubs) { 66 | sensors.insert(sensors.end(), it.first); 67 | } 68 | 69 | std::shuffle(sensors.begin(), sensors.end(), m_rng); 70 | 71 | // Randomly subscribe to m_nSub prefixes 72 | for (int i = 0; i < m_nSub; i++) { 73 | ndn::Name prefix = sensors[i]; 74 | NDN_LOG_INFO("Subscribing to: " << prefix); 75 | auto it = availSubs.find(prefix); 76 | m_consumer.addSubscription(prefix, it->second); 77 | } 78 | 79 | // After setting the subscription list, send the sync interest 80 | // The sync interest contains the subscription list 81 | // When new data is received for any subscribed prefix, processSyncUpdate is called 82 | m_consumer.sendSyncInterest(); 83 | } 84 | 85 | void 86 | processSyncUpdate(const std::vector& updates) 87 | { 88 | for (const auto& update : updates) { 89 | for (uint64_t i = update.lowSeq; i <= update.highSeq; i++) { 90 | // Data can now be fetched using the prefix and sequence 91 | NDN_LOG_INFO("Update: " << update.prefix << "/" << i); 92 | } 93 | } 94 | } 95 | 96 | private: 97 | ndn::Face m_face; 98 | 99 | int m_nSub; 100 | psync::Consumer m_consumer; 101 | 102 | ndn::random::RandomNumberEngine& m_rng{ndn::random::getRandomNumberEngine()}; 103 | }; 104 | 105 | int 106 | main(int argc, char* argv[]) 107 | { 108 | if (argc != 3) { 109 | std::cerr << "Usage: " << argv[0] << " \n"; 110 | return 1; 111 | } 112 | 113 | try { 114 | PSyncConsumer consumer(argv[1], std::stoi(argv[2])); 115 | consumer.run(); 116 | } 117 | catch (const std::exception& e) { 118 | NDN_LOG_ERROR(e.what()); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /examples/full-sync.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | NDN_LOG_INIT(examples.FullSyncApp); 31 | 32 | using namespace ndn::time_literals; 33 | 34 | class Producer 35 | { 36 | public: 37 | /** 38 | * @brief Initialize producer and schedule updates. 39 | * 40 | * Use default IBF size of 6 as we're expecting 6 updates to IBF in a sync cycle. 41 | * Set syncInterestLifetime and syncDataFreshness to 1.6 seconds. 42 | * userPrefix is the prefix string of user node prefixes. 43 | */ 44 | Producer(const ndn::Name& syncPrefix, const std::string& userPrefix, 45 | int numDataStreams, int maxNumPublish) 46 | : m_producer(m_face, m_keyChain, syncPrefix, [this] { 47 | psync::FullProducer::Options opts; 48 | opts.onUpdate = std::bind(&Producer::processSyncUpdate, this, _1); 49 | opts.syncInterestLifetime = 1600_ms; 50 | opts.syncDataFreshness = 1600_ms; 51 | return opts; 52 | } ()) 53 | , m_maxNumPublish(maxNumPublish) 54 | { 55 | // Add user prefixes and schedule updates for them in specified interval. 56 | for (int i = 0; i < numDataStreams; i++) { 57 | ndn::Name prefix(userPrefix + "-" + std::to_string(i)); 58 | m_producer.addUserNode(prefix); 59 | m_scheduler.schedule(ndn::time::milliseconds(m_uniformRand(m_rng)), 60 | [this, prefix] { doUpdate(prefix); }); 61 | } 62 | } 63 | 64 | void 65 | run() 66 | { 67 | m_face.processEvents(); 68 | } 69 | 70 | private: 71 | void 72 | doUpdate(const ndn::Name& prefix) 73 | { 74 | m_producer.publishName(prefix); 75 | 76 | uint64_t seqNo = m_producer.getSeqNo(prefix).value(); 77 | NDN_LOG_INFO("Publish: " << prefix << "/" << seqNo); 78 | 79 | if (seqNo < m_maxNumPublish) { 80 | m_scheduler.schedule(ndn::time::milliseconds(m_uniformRand(m_rng)), 81 | [this, prefix] { doUpdate(prefix); }); 82 | } 83 | } 84 | 85 | void 86 | processSyncUpdate(const std::vector& updates) 87 | { 88 | for (const auto& update : updates) { 89 | for (uint64_t i = update.lowSeq; i <= update.highSeq; i++) { 90 | NDN_LOG_INFO("Update " << update.prefix << "/" << i); 91 | } 92 | } 93 | } 94 | 95 | private: 96 | ndn::Face m_face; 97 | ndn::KeyChain m_keyChain; 98 | ndn::Scheduler m_scheduler{m_face.getIoContext()}; 99 | 100 | psync::FullProducer m_producer; 101 | uint64_t m_maxNumPublish; 102 | 103 | ndn::random::RandomNumberEngine& m_rng{ndn::random::getRandomNumberEngine()}; 104 | std::uniform_int_distribution<> m_uniformRand{0, 60000}; 105 | }; 106 | 107 | int 108 | main(int argc, char* argv[]) 109 | { 110 | if (argc != 5) { 111 | std::cerr << "Usage: " << argv[0] << " " 112 | << " \n"; 113 | return 1; 114 | } 115 | 116 | try { 117 | Producer producer(argv[1], argv[2], std::stoi(argv[3]), std::stoi(argv[4])); 118 | producer.run(); 119 | } 120 | catch (const std::exception& e) { 121 | NDN_LOG_ERROR(e.what()); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /examples/producer.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2023, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | **/ 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | NDN_LOG_INIT(examples.PartialSyncProducerApp); 31 | 32 | class PSyncPartialProducer 33 | { 34 | public: 35 | /** 36 | * @brief Initialize producer and schedule updates 37 | * 38 | * IBF size is set to 40 in m_producer as the expected number of update to IBF in a sync cycle 39 | */ 40 | PSyncPartialProducer(const ndn::Name& syncPrefix, const std::string& userPrefix, 41 | int numDataStreams, int maxNumPublish) 42 | : m_producer(m_face, m_keyChain, syncPrefix, {}) 43 | , m_maxNumPublish(maxNumPublish) 44 | { 45 | // Add user prefixes and schedule updates for them 46 | for (int i = 0; i < numDataStreams; i++) { 47 | ndn::Name updateName(userPrefix + "-" + std::to_string(i)); 48 | 49 | // Add the user prefix to the producer 50 | m_producer.addUserNode(updateName); 51 | 52 | // Each user prefix is updated at a random interval between 0 and 60 seconds 53 | m_scheduler.schedule(ndn::time::milliseconds(m_uniformRand(m_rng)), 54 | [this, updateName] { doUpdate(updateName); }); 55 | } 56 | } 57 | 58 | void 59 | run() 60 | { 61 | m_face.processEvents(); 62 | } 63 | 64 | private: 65 | void 66 | doUpdate(const ndn::Name& updateName) 67 | { 68 | // Publish an update to this user prefix 69 | m_producer.publishName(updateName); 70 | 71 | uint64_t seqNo = m_producer.getSeqNo(updateName).value(); 72 | NDN_LOG_INFO("Publish: " << updateName << "/" << seqNo); 73 | 74 | if (seqNo < m_maxNumPublish) { 75 | // Schedule the next update for this user prefix between 0 and 60 seconds 76 | m_scheduler.schedule(ndn::time::milliseconds(m_uniformRand(m_rng)), 77 | [this, updateName] { doUpdate(updateName); }); 78 | } 79 | } 80 | 81 | private: 82 | ndn::Face m_face; 83 | ndn::KeyChain m_keyChain; 84 | ndn::Scheduler m_scheduler{m_face.getIoContext()}; 85 | 86 | psync::PartialProducer m_producer; 87 | uint64_t m_maxNumPublish; 88 | 89 | ndn::random::RandomNumberEngine& m_rng{ndn::random::getRandomNumberEngine()}; 90 | std::uniform_int_distribution<> m_uniformRand{0, 60000}; 91 | }; 92 | 93 | int 94 | main(int argc, char* argv[]) 95 | { 96 | if (argc != 5) { 97 | std::cerr << "Usage: " << argv[0] << " " 98 | << " \n"; 99 | return 1; 100 | } 101 | 102 | try { 103 | PSyncPartialProducer producer(argv[1], argv[2], std::stoi(argv[3]), std::stoi(argv[4])); 104 | producer.run(); 105 | } 106 | catch (const std::exception& e) { 107 | NDN_LOG_ERROR(e.what()); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /examples/wscript: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- 2 | 3 | top = '..' 4 | 5 | def build(bld): 6 | # List all .cpp files (whole example in one .cpp) 7 | for ex in bld.path.ant_glob('*.cpp'): 8 | name = ex.change_ext('').path_from(bld.path.get_bld()) 9 | bld.program(name=f'example-{name}', 10 | target=f'psync-{name}', 11 | source=[ex], 12 | use='PSync') 13 | -------------------------------------------------------------------------------- /tests/boost-test.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2020, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | * 19 | * \author Yingdi Yu 20 | */ 21 | 22 | #ifndef PSYNC_TESTS_BOOST_TEST_HPP 23 | #define PSYNC_TESTS_BOOST_TEST_HPP 24 | 25 | // suppress warnings from Boost.Test 26 | #pragma GCC system_header 27 | #pragma clang system_header 28 | 29 | #define BOOST_TEST_DYN_LINK 30 | #include 31 | 32 | #endif // PSYNC_TESTS_BOOST_TEST_HPP 33 | -------------------------------------------------------------------------------- /tests/clock-fixture.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */ 2 | /* 3 | * Copyright (c) 2013-2022 Regents of the University of California 4 | * The University of Memphis 5 | * 6 | * This file is part of PSync. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 10 | * version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #include "tests/clock-fixture.hpp" 21 | 22 | namespace psync::tests { 23 | 24 | ClockFixture::ClockFixture() 25 | : m_steadyClock(std::make_shared()) 26 | , m_systemClock(std::make_shared()) 27 | { 28 | time::setCustomClocks(m_steadyClock, m_systemClock); 29 | } 30 | 31 | ClockFixture::~ClockFixture() 32 | { 33 | time::setCustomClocks(nullptr, nullptr); 34 | } 35 | 36 | void 37 | ClockFixture::advanceClocks(time::nanoseconds tick, time::nanoseconds total) 38 | { 39 | BOOST_ASSERT(tick > time::nanoseconds::zero()); 40 | BOOST_ASSERT(total >= time::nanoseconds::zero()); 41 | 42 | while (total > time::nanoseconds::zero()) { 43 | auto t = std::min(tick, total); 44 | m_steadyClock->advance(t); 45 | m_systemClock->advance(t); 46 | total -= t; 47 | 48 | afterTick(); 49 | } 50 | } 51 | 52 | } // namespace psync::tests 53 | -------------------------------------------------------------------------------- /tests/clock-fixture.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */ 2 | /* 3 | * Copyright (c) 2013-2022 Regents of the University of California 4 | * The University of Memphis 5 | * 6 | * This file is part of PSync. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 10 | * version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #ifndef PSYNC_TESTS_CLOCK_FIXTURE_HPP 21 | #define PSYNC_TESTS_CLOCK_FIXTURE_HPP 22 | 23 | #include 24 | 25 | namespace psync::tests { 26 | 27 | namespace time = ndn::time; 28 | 29 | /** \brief A test fixture that overrides steady clock and system clock. 30 | */ 31 | class ClockFixture 32 | { 33 | public: 34 | virtual 35 | ~ClockFixture(); 36 | 37 | /** \brief Advance steady and system clocks. 38 | * 39 | * Clocks are advanced in increments of \p tick for \p nTicks ticks. 40 | * afterTick() is called after each tick. 41 | * 42 | * Exceptions thrown during I/O events are propagated to the caller. 43 | * Clock advancement will stop in the event of an exception. 44 | */ 45 | void 46 | advanceClocks(time::nanoseconds tick, size_t nTicks = 1) 47 | { 48 | advanceClocks(tick, tick * nTicks); 49 | } 50 | 51 | /** \brief Advance steady and system clocks. 52 | * 53 | * Clocks are advanced in increments of \p tick for \p total time. 54 | * The last increment might be shorter than \p tick. 55 | * afterTick() is called after each tick. 56 | * 57 | * Exceptions thrown during I/O events are propagated to the caller. 58 | * Clock advancement will stop in the event of an exception. 59 | */ 60 | void 61 | advanceClocks(time::nanoseconds tick, time::nanoseconds total); 62 | 63 | protected: 64 | ClockFixture(); 65 | 66 | private: 67 | /** \brief Called by advanceClocks() after each clock advancement (tick). 68 | * 69 | * The base class implementation is a no-op. 70 | */ 71 | virtual void 72 | afterTick() 73 | { 74 | } 75 | 76 | protected: 77 | std::shared_ptr m_steadyClock; 78 | std::shared_ptr m_systemClock; 79 | }; 80 | 81 | } // namespace psync::tests 82 | 83 | #endif // PSYNC_TESTS_CLOCK_FIXTURE_HPP 84 | -------------------------------------------------------------------------------- /tests/io-fixture.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */ 2 | /* 3 | * Copyright (c) 2013-2023, Regents of the University of California, 4 | * The University of Memphis. 5 | * 6 | * This file is part of PSync. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 10 | * version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #ifndef PSYNC_TESTS_IO_FIXTURE_HPP 21 | #define PSYNC_TESTS_IO_FIXTURE_HPP 22 | 23 | #include "tests/clock-fixture.hpp" 24 | 25 | #include 26 | 27 | namespace psync::tests { 28 | 29 | class IoFixture : public ClockFixture 30 | { 31 | private: 32 | void 33 | afterTick() final 34 | { 35 | if (m_io.stopped()) { 36 | m_io.restart(); 37 | } 38 | m_io.poll(); 39 | } 40 | 41 | protected: 42 | boost::asio::io_context m_io; 43 | }; 44 | 45 | } // namespace psync::tests 46 | 47 | #endif // PSYNC_TESTS_IO_FIXTURE_HPP 48 | -------------------------------------------------------------------------------- /tests/key-chain-fixture.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2013-2022 Regents of the University of California. 4 | * 5 | * This file is part of PSync. 6 | * 7 | * PSync is free software: you can redistribute it and/or modify it under the terms 8 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 12 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 13 | * PURPOSE. See the GNU Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License along with 16 | * PSync, e.g., in COPYING.md file. If not, see . 17 | */ 18 | 19 | #ifndef PSYNC_TESTS_KEY_CHAIN_FIXTURE_HPP 20 | #define PSYNC_TESTS_KEY_CHAIN_FIXTURE_HPP 21 | 22 | #include 23 | 24 | namespace psync::tests { 25 | 26 | /** 27 | * @brief A fixture providing an in-memory KeyChain. 28 | */ 29 | class KeyChainFixture 30 | { 31 | protected: 32 | ndn::KeyChain m_keyChain{"pib-memory:", "tpm-memory:"}; 33 | }; 34 | 35 | } // namespace psync::tests 36 | 37 | #endif // PSYNC_TESTS_KEY_CHAIN_FIXTURE_HPP 38 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2020, Regents of the University of California, 4 | * Arizona Board of Regents, 5 | * Colorado State University, 6 | * University Pierre & Marie Curie, Sorbonne University, 7 | * Washington University in St. Louis, 8 | * Beijing Institute of Technology, 9 | * The University of Memphis. 10 | * 11 | * This file is part of PSync. 12 | * See AUTHORS.md for complete list of PSync authors and contributors. 13 | * 14 | * PSync is free software: you can redistribute it and/or modify it under the terms 15 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 16 | * either version 3 of the License, or (at your option) any later version. 17 | * 18 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 19 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 20 | * PURPOSE. See the GNU Lesser General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Lesser General Public License along with 23 | * PSync, e.g., in COPYING.md file. If not, see . 24 | */ 25 | 26 | #define BOOST_TEST_MODULE PSync 27 | #include "tests/boost-test.hpp" 28 | -------------------------------------------------------------------------------- /tests/test-bloom-filter.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | **/ 19 | 20 | #include "PSync/detail/bloom-filter.hpp" 21 | 22 | #include "tests/boost-test.hpp" 23 | 24 | #include 25 | 26 | namespace psync::tests { 27 | 28 | using detail::BloomFilter; 29 | 30 | BOOST_AUTO_TEST_SUITE(TestBloomFilter) 31 | 32 | BOOST_AUTO_TEST_CASE(Basic) 33 | { 34 | BloomFilter bf(100, 0.001); 35 | 36 | std::string insertName("/memphis"); 37 | bf.insert(insertName); 38 | BOOST_CHECK(bf.contains(insertName)); 39 | } 40 | 41 | BOOST_AUTO_TEST_CASE(NameAppendAndExtract) 42 | { 43 | ndn::Name bfName("/test"); 44 | BloomFilter bf(100, 0.001); 45 | bf.insert("/memphis"); 46 | bf.appendToName(bfName); 47 | 48 | BloomFilter bfFromName(100, 0.001, bfName.at(-1)); 49 | 50 | BOOST_CHECK_EQUAL(bfName.at(1).toNumber(), 100); 51 | BOOST_CHECK_EQUAL(bfName.at(2).toNumber(), 1); 52 | BOOST_CHECK_EQUAL(bf, bfFromName); 53 | 54 | BOOST_CHECK_THROW(BloomFilter(200, 0.001, bfName.at(-1)), std::runtime_error); 55 | } 56 | 57 | BOOST_AUTO_TEST_SUITE_END() 58 | 59 | } // namespace psync::tests 60 | -------------------------------------------------------------------------------- /tests/test-consumer.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #include "PSync/consumer.hpp" 21 | 22 | #include "tests/boost-test.hpp" 23 | #include "tests/io-fixture.hpp" 24 | 25 | #include 26 | 27 | namespace psync::tests { 28 | 29 | using ndn::Name; 30 | 31 | BOOST_AUTO_TEST_SUITE(TestConsumer) 32 | 33 | BOOST_AUTO_TEST_CASE(AddSubscription) 34 | { 35 | ndn::DummyClientFace face; 36 | Consumer::Options opts; 37 | opts.bfCount = 40; 38 | Consumer consumer(face, "/psync", opts); 39 | 40 | Name subscription("test"); 41 | 42 | BOOST_CHECK(!consumer.isSubscribed(subscription)); 43 | BOOST_CHECK(consumer.addSubscription(subscription, 0)); 44 | BOOST_CHECK(!consumer.addSubscription(subscription, 0)); 45 | } 46 | 47 | BOOST_AUTO_TEST_CASE(RemoveSubscription) 48 | { 49 | ndn::DummyClientFace face; 50 | Consumer::Options opts; 51 | opts.bfCount = 40; 52 | Consumer consumer(face, "/psync", opts); 53 | 54 | Name subscription("test"); 55 | consumer.addSubscription(subscription, 0); 56 | 57 | BOOST_CHECK(consumer.isSubscribed(subscription)); 58 | BOOST_CHECK(consumer.removeSubscription(subscription)); 59 | BOOST_CHECK(!consumer.removeSubscription(subscription)); 60 | BOOST_CHECK(!consumer.isSubscribed(subscription)); 61 | } 62 | 63 | BOOST_FIXTURE_TEST_CASE(ConstantTimeoutForFirstSegment, IoFixture) 64 | { 65 | ndn::DummyClientFace face(m_io); 66 | Consumer::Options opts; 67 | opts.bfCount = 40; 68 | opts.helloInterestLifetime = 4_s; 69 | opts.syncInterestLifetime = 4_s; 70 | Consumer consumer(face, "/psync", opts); 71 | 72 | consumer.sendHelloInterest(); 73 | advanceClocks(4_s); 74 | BOOST_CHECK_EQUAL(face.sentInterests.size(), 1); 75 | face.sentInterests.clear(); 76 | consumer.stop(); 77 | 78 | consumer.m_iblt = Name("test"); 79 | consumer.sendSyncInterest(); 80 | advanceClocks(3999_ms); 81 | BOOST_CHECK_EQUAL(face.sentInterests.size(), 1); 82 | consumer.stop(); 83 | } 84 | 85 | BOOST_AUTO_TEST_SUITE_END() 86 | 87 | } // namespace psync::tests 88 | -------------------------------------------------------------------------------- /tests/test-full-producer.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #include "PSync/full-producer.hpp" 21 | #include "PSync/detail/util.hpp" 22 | 23 | 24 | #include "tests/boost-test.hpp" 25 | #include "tests/io-fixture.hpp" 26 | #include "tests/key-chain-fixture.hpp" 27 | 28 | #include 29 | 30 | namespace psync::tests { 31 | 32 | using ndn::Interest; 33 | using ndn::Name; 34 | 35 | class FullProducerFixture : public IoFixture, public KeyChainFixture 36 | { 37 | protected: 38 | ndn::DummyClientFace m_face{m_io, m_keyChain, {true, true}}; 39 | }; 40 | 41 | BOOST_FIXTURE_TEST_SUITE(TestFullProducer, FullProducerFixture) 42 | 43 | BOOST_AUTO_TEST_CASE(OnInterest) 44 | { 45 | Name syncPrefix("/psync"); 46 | FullProducer::Options opts; 47 | opts.ibfCount = 40; 48 | FullProducer node(m_face, m_keyChain, syncPrefix, opts); 49 | 50 | Name syncInterestName(syncPrefix); 51 | syncInterestName.append("malicious-IBF"); 52 | 53 | BOOST_CHECK_NO_THROW(node.onSyncInterest(syncPrefix, Interest(syncInterestName))); 54 | } 55 | 56 | BOOST_AUTO_TEST_CASE(ConstantTimeoutForFirstSegment) 57 | { 58 | Name syncPrefix("/psync"); 59 | FullProducer::Options opts; 60 | opts.ibfCount = 40; 61 | opts.syncInterestLifetime = 8_s; 62 | FullProducer node(m_face, m_keyChain, syncPrefix, opts); 63 | 64 | advanceClocks(10_ms); 65 | m_face.sentInterests.clear(); 66 | 67 | // full sync sends the next one in interest lifetime / 2 +- jitter 68 | advanceClocks(6_s); 69 | BOOST_CHECK_EQUAL(m_face.sentInterests.size(), 1); 70 | } 71 | 72 | BOOST_AUTO_TEST_CASE(OnSyncDataDecodeFailure) 73 | { 74 | Name syncPrefix("/psync"); 75 | FullProducer::Options opts; 76 | opts.ibfCount = 40; 77 | FullProducer node(m_face, m_keyChain, syncPrefix, opts); 78 | 79 | Name syncInterestName(syncPrefix); 80 | node.m_iblt.appendToName(syncInterestName); 81 | Interest syncInterest(syncInterestName); 82 | 83 | auto badCompress = std::make_shared(5); 84 | BOOST_CHECK_NO_THROW(node.onSyncData(syncInterest, badCompress)); 85 | 86 | const uint8_t test[] = {'t', 'e', 's', 't'}; 87 | auto goodCompressBadBlock = detail::compress(node.m_contentCompression, test); 88 | BOOST_CHECK_NO_THROW(node.onSyncData(syncInterest, goodCompressBadBlock)); 89 | } 90 | 91 | BOOST_AUTO_TEST_CASE(SatisfyPendingInterestsBehavior) 92 | { 93 | Name syncPrefix("/psync"); 94 | FullProducer::Options opts; 95 | opts.ibfCount = 6; 96 | FullProducer node(m_face, m_keyChain, syncPrefix, opts); 97 | 98 | Name syncInterestName(syncPrefix); 99 | node.m_iblt.appendToName(syncInterestName); 100 | syncInterestName.appendNumber(1); 101 | Interest syncInterest(syncInterestName); 102 | 103 | node.addUserNode(syncPrefix); 104 | 105 | node.onSyncInterest(syncPrefix, syncInterest); 106 | 107 | BOOST_CHECK_EQUAL(node.m_pendingEntries.size(), 1); 108 | 109 | // Test whether data is still sent if IBF diff is greater than default threshhold. 110 | auto prefix1 = Name("/test/alice").appendNumber(1); 111 | uint32_t newHash1 = psync::detail::murmurHash3(42, prefix1); 112 | node.m_iblt.insert(newHash1); 113 | 114 | auto prefix2 = Name("/test/bob").appendNumber(1); 115 | uint32_t newHash2 = psync::detail::murmurHash3(42, prefix2); 116 | node.m_iblt.insert(newHash2); 117 | 118 | auto prefix3 = Name("/test/carol").appendNumber(1); 119 | uint32_t newHash3 = psync::detail::murmurHash3(42, prefix3); 120 | node.m_iblt.insert(newHash3); 121 | 122 | auto prefix4 = Name("/test/david").appendNumber(1); 123 | uint32_t newHash4 = psync::detail::murmurHash3(42, prefix4); 124 | node.m_iblt.insert(newHash4); 125 | 126 | auto prefix5 = Name("/test/erin").appendNumber(1); 127 | uint32_t newHash5 = psync::detail::murmurHash3(42, prefix5); 128 | node.m_iblt.insert(newHash5); 129 | 130 | node.publishName(syncPrefix); 131 | 132 | advanceClocks(10_ms); 133 | 134 | BOOST_CHECK_EQUAL(m_face.sentData.size(), 1); 135 | 136 | BOOST_CHECK_EQUAL(node.m_pendingEntries.empty(), true); 137 | } 138 | 139 | BOOST_AUTO_TEST_SUITE_END() 140 | 141 | } // namespace psync::tests 142 | -------------------------------------------------------------------------------- /tests/test-iblt.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | **/ 19 | 20 | #include "PSync/detail/iblt.hpp" 21 | #include "PSync/detail/util.hpp" 22 | 23 | #include "tests/boost-test.hpp" 24 | 25 | namespace psync::tests { 26 | 27 | using namespace psync::detail; 28 | using ndn::Name; 29 | 30 | BOOST_AUTO_TEST_SUITE(TestIBLT) 31 | 32 | BOOST_AUTO_TEST_CASE(Equal) 33 | { 34 | const size_t size = 10; 35 | 36 | IBLT iblt1(size, CompressionScheme::DEFAULT); 37 | IBLT iblt2(size, CompressionScheme::DEFAULT); 38 | BOOST_CHECK_EQUAL(iblt1, iblt2); 39 | 40 | auto prefix = Name("/test/memphis").appendNumber(1); 41 | uint32_t newHash = murmurHash3(11, prefix); 42 | iblt1.insert(newHash); 43 | iblt2.insert(newHash); 44 | 45 | BOOST_CHECK_EQUAL(iblt1, iblt2); 46 | 47 | Name ibfName1("sync"), ibfName2("sync"); 48 | iblt1.appendToName(ibfName1); 49 | iblt2.appendToName(ibfName2); 50 | BOOST_CHECK_EQUAL(ibfName1, ibfName2); 51 | } 52 | 53 | static const uint8_t IBF[] = { 54 | // Header 55 | 0x08, 0xB4, 56 | // Uncompressed IBF 57 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 58 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x5C, 0x5B, 0xF2, 0x67, 59 | 0x42, 0x24, 0xEE, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 60 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 61 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x5C, 0x5B, 0xF2, 0x67, 62 | 0x42, 0x24, 0xEE, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 63 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 64 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 65 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 66 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 67 | 0x5C, 0x5B, 0xF2, 0x67, 0x42, 0x24, 0xEE, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 68 | 0x00, 0x00, 0x00, 0x00 69 | }; 70 | 71 | BOOST_AUTO_TEST_CASE(NameAppendAndExtract) 72 | { 73 | const int size = 10; 74 | 75 | IBLT iblt(size, CompressionScheme::DEFAULT); 76 | auto prefix = Name("/test/memphis").appendNumber(1); 77 | auto hash = murmurHash3(11, prefix); 78 | iblt.insert(hash); 79 | 80 | Name ibltName("/sync"); 81 | iblt.appendToName(ibltName); 82 | 83 | IBLT rcvd(size, CompressionScheme::DEFAULT); 84 | rcvd.initialize(ibltName.at(-1)); 85 | BOOST_CHECK_EQUAL(iblt, rcvd); 86 | 87 | IBLT rcvdDiffSize(20, CompressionScheme::DEFAULT); 88 | BOOST_CHECK_THROW(rcvdDiffSize.initialize(ibltName.at(-1)), IBLT::Error); 89 | 90 | IBLT iblt2(size, CompressionScheme::NONE); 91 | iblt2.insert(hash); 92 | Name ibltName2("/sync"); 93 | iblt2.appendToName(ibltName2); 94 | BOOST_TEST(ibltName2.at(-1).wireEncode() == IBF, boost::test_tools::per_element()); 95 | 96 | Name malformedName("/test"); 97 | auto compressed = compress(CompressionScheme::DEFAULT, 98 | malformedName.wireEncode().value_bytes()); 99 | malformedName.append(Name::Component(std::move(compressed))); 100 | IBLT rcvd2(size, CompressionScheme::DEFAULT); 101 | BOOST_CHECK_THROW(rcvd2.initialize(malformedName.at(-1)), IBLT::Error); 102 | } 103 | 104 | BOOST_AUTO_TEST_CASE(CopyInsertErase) 105 | { 106 | const size_t size = 10; 107 | 108 | IBLT iblt1(size, CompressionScheme::DEFAULT); 109 | 110 | auto prefix = Name("/test/memphis").appendNumber(1); 111 | uint32_t hash1 = murmurHash3(11, prefix); 112 | iblt1.insert(hash1); 113 | 114 | IBLT iblt2(iblt1); 115 | iblt2.erase(hash1); 116 | prefix = Name("/test/memphis").appendNumber(2); 117 | uint32_t hash3 = murmurHash3(11, prefix); 118 | iblt2.insert(hash3); 119 | 120 | iblt1.erase(hash1); 121 | prefix = Name("/test/memphis").appendNumber(5); 122 | uint32_t hash5 = murmurHash3(11, prefix); 123 | iblt1.insert(hash5); 124 | 125 | iblt2.erase(hash3); 126 | iblt2.insert(hash5); 127 | 128 | BOOST_CHECK_EQUAL(iblt1, iblt2); 129 | } 130 | 131 | BOOST_AUTO_TEST_CASE(HigherSeqTest) 132 | { 133 | // The case where we can't recognize if the rcvd IBF has higher sequence number 134 | // Relevant to full sync case 135 | 136 | const size_t size = 10; 137 | 138 | IBLT ownIBF(size, CompressionScheme::DEFAULT); 139 | IBLT rcvdIBF(size, CompressionScheme::DEFAULT); 140 | 141 | auto prefix = Name("/test/memphis").appendNumber(3); 142 | uint32_t hash1 = murmurHash3(11, prefix); 143 | ownIBF.insert(hash1); 144 | 145 | auto prefix2 = Name("/test/memphis").appendNumber(4); 146 | uint32_t hash2 = murmurHash3(11, prefix2); 147 | rcvdIBF.insert(hash2); 148 | 149 | auto diff = ownIBF - rcvdIBF; 150 | BOOST_CHECK(diff.canDecode); 151 | BOOST_CHECK(*diff.positive.begin() == hash1); 152 | BOOST_CHECK(*diff.negative.begin() == hash2); 153 | } 154 | 155 | BOOST_AUTO_TEST_CASE(Difference) 156 | { 157 | const size_t size = 10; 158 | 159 | IBLT ownIBF(size, CompressionScheme::DEFAULT); 160 | IBLT rcvdIBF = ownIBF; 161 | 162 | auto diff = ownIBF - rcvdIBF; 163 | // non-empty Positive means we have some elements that the others don't 164 | 165 | BOOST_CHECK(diff.canDecode); 166 | BOOST_CHECK_EQUAL(diff.positive.size(), 0); 167 | BOOST_CHECK_EQUAL(diff.negative.size(), 0); 168 | 169 | auto prefix = Name("/test/memphis").appendNumber(1); 170 | uint32_t newHash = murmurHash3(11, prefix); 171 | ownIBF.insert(newHash); 172 | 173 | diff = ownIBF - rcvdIBF; 174 | BOOST_CHECK(diff.canDecode); 175 | BOOST_CHECK_EQUAL(diff.positive.size(), 1); 176 | BOOST_CHECK_EQUAL(diff.negative.size(), 0); 177 | 178 | prefix = Name("/test/csu").appendNumber(1); 179 | newHash = murmurHash3(11, prefix); 180 | rcvdIBF.insert(newHash); 181 | 182 | diff = ownIBF - rcvdIBF; 183 | BOOST_CHECK(diff.canDecode); 184 | BOOST_CHECK_EQUAL(diff.positive.size(), 1); 185 | BOOST_CHECK_EQUAL(diff.negative.size(), 1); 186 | } 187 | 188 | BOOST_AUTO_TEST_CASE(DifferenceBwOversizedIBFs) 189 | { 190 | // Insert 50 elements into IBF of size 10 191 | // Check that we can still list the difference 192 | // even though we can't list the IBFs itself 193 | 194 | const size_t size = 10; 195 | 196 | IBLT ownIBF(size, CompressionScheme::DEFAULT); 197 | 198 | for (int i = 0; i < 50; i++) { 199 | auto prefix = Name("/test/memphis" + std::to_string(i)).appendNumber(1); 200 | uint32_t newHash = murmurHash3(11, prefix); 201 | ownIBF.insert(newHash); 202 | } 203 | 204 | IBLT rcvdIBF = ownIBF; 205 | 206 | auto prefix = Name("/test/ucla").appendNumber(1); 207 | uint32_t newHash = murmurHash3(11, prefix); 208 | ownIBF.insert(newHash); 209 | 210 | auto diff = ownIBF - rcvdIBF; 211 | BOOST_CHECK(diff.canDecode); 212 | BOOST_CHECK_EQUAL(diff.positive.size(), 1); 213 | BOOST_CHECK_EQUAL(*diff.positive.begin(), newHash); 214 | BOOST_CHECK_EQUAL(diff.negative.size(), 0); 215 | 216 | IBLT emptyIBF(size, CompressionScheme::DEFAULT); 217 | BOOST_CHECK(!(ownIBF - emptyIBF).canDecode); 218 | BOOST_CHECK(!(rcvdIBF - emptyIBF).canDecode); 219 | } 220 | 221 | BOOST_AUTO_TEST_SUITE_END() 222 | 223 | } // namespace psync::tests 224 | -------------------------------------------------------------------------------- /tests/test-partial-producer.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | **/ 19 | 20 | #include "PSync/partial-producer.hpp" 21 | 22 | #include "tests/boost-test.hpp" 23 | #include "tests/key-chain-fixture.hpp" 24 | 25 | #include 26 | #include 27 | 28 | namespace psync::tests { 29 | 30 | using ndn::Interest; 31 | using ndn::Name; 32 | 33 | class PartialProducerFixture : public KeyChainFixture 34 | { 35 | protected: 36 | ndn::DummyClientFace m_face{m_keyChain, {true, true}}; 37 | }; 38 | 39 | BOOST_FIXTURE_TEST_SUITE(TestPartialProducer, PartialProducerFixture) 40 | 41 | BOOST_AUTO_TEST_CASE(RegisterPrefix) 42 | { 43 | Name syncPrefix("/psync"), userNode("/testUser"); 44 | PartialProducer producer(m_face, m_keyChain, syncPrefix, {}); 45 | producer.addUserNode(userNode); 46 | 47 | m_face.processEvents(-1_ms); 48 | 49 | BOOST_REQUIRE_EQUAL(m_face.sentInterests.size(), 1); 50 | auto interest = m_face.sentInterests.front(); 51 | BOOST_CHECK_EQUAL(interest.getName().at(3), Name::Component("register")); 52 | ndn::nfd::ControlParameters params(interest.getName().at(4).blockFromValue()); 53 | BOOST_CHECK_EQUAL(params.getName(), syncPrefix); 54 | } 55 | 56 | BOOST_AUTO_TEST_CASE(PublishName) 57 | { 58 | Name syncPrefix("/psync"), userNode("/testUser"), nonUser("/testUser2"); 59 | PartialProducer producer(m_face, m_keyChain, syncPrefix, {}); 60 | producer.addUserNode(userNode); 61 | 62 | BOOST_CHECK_EQUAL(producer.getSeqNo(userNode).value_or(-1), 0); 63 | producer.publishName(userNode); 64 | BOOST_CHECK_EQUAL(producer.getSeqNo(userNode).value_or(-1), 1); 65 | 66 | producer.publishName(userNode); 67 | BOOST_CHECK_EQUAL(producer.getSeqNo(userNode).value_or(-1), 2); 68 | 69 | producer.publishName(userNode, 10); 70 | BOOST_CHECK_EQUAL(producer.getSeqNo(userNode).value_or(-1), 10); 71 | 72 | producer.publishName(nonUser); 73 | BOOST_CHECK_EQUAL(producer.getSeqNo(nonUser).value_or(-1), -1); 74 | } 75 | 76 | BOOST_AUTO_TEST_CASE(SameSyncInterest) 77 | { 78 | Name syncPrefix("/psync"), userNode("/testUser"); 79 | PartialProducer producer(m_face, m_keyChain, syncPrefix, {}); 80 | producer.addUserNode(userNode); 81 | 82 | Name syncInterestName(syncPrefix); 83 | syncInterestName.append("sync"); 84 | Name syncInterestPrefix = syncInterestName; 85 | 86 | detail::BloomFilter bf(20, 0.001); 87 | bf.appendToName(syncInterestName); 88 | 89 | producer.m_iblt.appendToName(syncInterestName); 90 | 91 | Interest syncInterest(syncInterestName); 92 | syncInterest.setInterestLifetime(1_s); 93 | syncInterest.setNonce(1); 94 | BOOST_CHECK_NO_THROW(producer.onSyncInterest(syncInterestPrefix, syncInterest)); 95 | m_face.processEvents(10_ms); 96 | BOOST_CHECK_EQUAL(producer.m_pendingEntries.size(), 1); 97 | 98 | m_face.processEvents(500_ms); 99 | 100 | // Same interest again - size of pending interest should remain same, but expirationEvent should change 101 | syncInterest.setNonce(2); 102 | BOOST_CHECK_NO_THROW(producer.onSyncInterest(syncInterestPrefix, syncInterest)); 103 | m_face.processEvents(10_ms); 104 | BOOST_CHECK_EQUAL(producer.m_pendingEntries.size(), 1); 105 | 106 | m_face.processEvents(500_ms); 107 | BOOST_CHECK_EQUAL(producer.m_pendingEntries.size(), 1); 108 | 109 | m_face.processEvents(500_ms); 110 | BOOST_CHECK_EQUAL(producer.m_pendingEntries.size(), 0); 111 | } 112 | 113 | BOOST_AUTO_TEST_CASE(OnSyncInterest) 114 | { 115 | Name syncPrefix("/psync"), userNode("/testUser"); 116 | PartialProducer producer(m_face, m_keyChain, syncPrefix, {}); 117 | producer.addUserNode(userNode); 118 | 119 | // Sync interest with no bloom filter attached 120 | Name syncInterestName(syncPrefix); 121 | syncInterestName.append("sync"); 122 | producer.m_iblt.appendToName(syncInterestName); 123 | BOOST_CHECK_NO_THROW(producer.onSyncInterest(syncInterestName, Interest(syncInterestName))); 124 | 125 | // Sync interest with malicious bloom filter 126 | syncInterestName = syncPrefix; 127 | syncInterestName.append("sync"); 128 | syncInterestName.appendNumber(20); // count of bloom filter 129 | syncInterestName.appendNumber(1); // false positive probability * 1000 of bloom filter 130 | syncInterestName.append("fake-name"); 131 | producer.m_iblt.appendToName(syncInterestName); 132 | BOOST_CHECK_NO_THROW(producer.onSyncInterest(syncInterestName, Interest(syncInterestName))); 133 | 134 | // Sync interest with malicious IBF 135 | syncInterestName = syncPrefix; 136 | syncInterestName.append("sync"); 137 | detail::BloomFilter bf(20, 0.001); 138 | bf.appendToName(syncInterestName); 139 | syncInterestName.append("fake-name"); 140 | BOOST_CHECK_NO_THROW(producer.onSyncInterest(syncInterestName, Interest(syncInterestName))); 141 | } 142 | 143 | BOOST_AUTO_TEST_SUITE_END() 144 | 145 | } // namespace psync::tests 146 | -------------------------------------------------------------------------------- /tests/test-producer-base.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | **/ 19 | 20 | #include "PSync/producer-base.hpp" 21 | 22 | #include "tests/boost-test.hpp" 23 | #include "tests/key-chain-fixture.hpp" 24 | 25 | #include 26 | 27 | namespace psync::tests { 28 | 29 | using ndn::Name; 30 | 31 | class ProducerBaseFixture : public KeyChainFixture 32 | { 33 | protected: 34 | ndn::DummyClientFace m_face; 35 | }; 36 | 37 | BOOST_FIXTURE_TEST_SUITE(TestProducerBase, ProducerBaseFixture) 38 | 39 | BOOST_AUTO_TEST_CASE(Basic) 40 | { 41 | Name userNode("/testUser"); 42 | ProducerBase producerBase(m_face, m_keyChain, 40, Name("/psync")); 43 | producerBase.addUserNode(userNode); 44 | 45 | // Hash table size should be 40 + 40/2 = 60 (which is perfectly divisible by N_HASH = 3) 46 | BOOST_CHECK_EQUAL(producerBase.m_iblt.getHashTable().size(), 60); 47 | BOOST_CHECK_EQUAL(producerBase.getSeqNo(userNode).value(), 0); 48 | 49 | producerBase.updateSeqNo(userNode, 1); 50 | BOOST_CHECK(producerBase.getSeqNo(userNode).value() == 1); 51 | 52 | auto prefixWithSeq = Name(userNode).appendNumber(1); 53 | uint32_t hash = producerBase.m_biMap.right.find(prefixWithSeq)->second; 54 | Name prefix(producerBase.m_biMap.left.find(hash)->second); 55 | BOOST_CHECK_EQUAL(prefix.getPrefix(-1), userNode); 56 | 57 | producerBase.removeUserNode(userNode); 58 | BOOST_CHECK(producerBase.getSeqNo(userNode) == std::nullopt); 59 | BOOST_CHECK(producerBase.m_biMap.right.find(prefixWithSeq) == producerBase.m_biMap.right.end()); 60 | BOOST_CHECK(producerBase.m_biMap.left.find(hash) == producerBase.m_biMap.left.end()); 61 | 62 | Name nonExistentUserNode("/notAUser"); 63 | producerBase.updateSeqNo(nonExistentUserNode, 1); 64 | BOOST_CHECK(producerBase.m_biMap.right.find(Name(nonExistentUserNode).appendNumber(1)) == 65 | producerBase.m_biMap.right.end()); 66 | } 67 | 68 | BOOST_AUTO_TEST_CASE(ApplicationNack) 69 | { 70 | ProducerBase producerBase(m_face, m_keyChain, 40, Name("/psync")); 71 | producerBase.addUserNode("/testUser"); 72 | 73 | BOOST_CHECK_EQUAL(m_face.sentData.size(), 0); 74 | producerBase.sendApplicationNack(Name("test")); 75 | m_face.processEvents(10_ms); 76 | 77 | BOOST_REQUIRE_EQUAL(m_face.sentData.size(), 1); 78 | BOOST_CHECK_EQUAL(m_face.sentData.front().getContentType(), ndn::tlv::ContentType_Nack); 79 | } 80 | 81 | BOOST_AUTO_TEST_SUITE_END() 82 | 83 | } // namespace psync::tests 84 | -------------------------------------------------------------------------------- /tests/test-segment-publisher.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | **/ 19 | 20 | #include "PSync/segment-publisher.hpp" 21 | #include "PSync/detail/state.hpp" 22 | 23 | #include "tests/boost-test.hpp" 24 | #include "tests/io-fixture.hpp" 25 | #include "tests/key-chain-fixture.hpp" 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | namespace psync::tests { 32 | 33 | using namespace ndn::time_literals; 34 | using ndn::Interest; 35 | using ndn::Name; 36 | 37 | class SegmentPublisherFixture : public IoFixture, public KeyChainFixture 38 | { 39 | protected: 40 | SegmentPublisherFixture() 41 | { 42 | m_face.setInterestFilter(ndn::InterestFilter("/hello/world"), 43 | bind(&SegmentPublisherFixture::onInterest, this, _2), 44 | [] (auto&&...) { BOOST_CHECK(false); }); 45 | advanceClocks(10_ms); 46 | 47 | for (int i = 0; i < 1000; ++i) { 48 | state.addContent(Name("/test").appendNumber(i)); 49 | } 50 | } 51 | 52 | ~SegmentPublisherFixture() override 53 | { 54 | fetcher->stop(); 55 | } 56 | 57 | void 58 | expressInterest(const Interest& interest) 59 | { 60 | fetcher = ndn::SegmentFetcher::start(m_face, interest, ndn::security::getAcceptAllValidator()); 61 | fetcher->onComplete.connect([this] (auto&&...) { numComplete++; }); 62 | fetcher->onError.connect([] (auto&&...) { BOOST_CHECK(false); }); 63 | 64 | advanceClocks(10_ms); 65 | } 66 | 67 | void 68 | onInterest(const Interest& interest) 69 | { 70 | if (publisher.replyFromStore(interest.getName())) { 71 | numRepliesFromStore++; 72 | return; 73 | } 74 | 75 | // If dataName is same as interest name 76 | if (dataName.empty()) { 77 | publisher.publish(interest.getName(), interest.getName(), state.wireEncode(), freshness); 78 | } 79 | else { 80 | publisher.publish(interest.getName(), dataName, state.wireEncode(), freshness); 81 | } 82 | } 83 | 84 | protected: 85 | ndn::DummyClientFace m_face{m_io, m_keyChain, {true, true}}; 86 | SegmentPublisher publisher{m_face, m_keyChain}; 87 | std::shared_ptr fetcher; 88 | Name dataName; 89 | detail::State state; 90 | 91 | int numComplete = 0; 92 | int numRepliesFromStore = 0; 93 | 94 | static constexpr ndn::time::milliseconds freshness = 1_s; 95 | }; 96 | 97 | BOOST_FIXTURE_TEST_SUITE(TestSegmentPublisher, SegmentPublisherFixture) 98 | 99 | BOOST_AUTO_TEST_CASE(Basic) 100 | { 101 | BOOST_CHECK_EQUAL(publisher.m_ims.size(), 0); 102 | 103 | expressInterest(Interest("/hello/world")); 104 | BOOST_CHECK_EQUAL(numComplete, 1); 105 | // First segment is answered directly in publish, 106 | // Rest two are satisfied by the store 107 | BOOST_CHECK_EQUAL(numRepliesFromStore, 2); 108 | BOOST_CHECK_EQUAL(publisher.m_ims.size(), 3); 109 | 110 | for (const auto& data : publisher.m_ims) { 111 | BOOST_TEST_CONTEXT(data.getName()) { 112 | BOOST_REQUIRE_EQUAL(data.getName().size(), 4); 113 | BOOST_CHECK(data.getName()[-1].isSegment()); 114 | BOOST_CHECK(data.getName()[-2].isVersion()); 115 | } 116 | } 117 | 118 | numRepliesFromStore = 0; 119 | expressInterest(Interest("/hello/world")); 120 | BOOST_CHECK_EQUAL(numComplete, 2); 121 | BOOST_CHECK_EQUAL(numRepliesFromStore, 3); 122 | 123 | advanceClocks(freshness); 124 | BOOST_CHECK_EQUAL(publisher.m_ims.size(), 0); 125 | 126 | numRepliesFromStore = 0; 127 | expressInterest(Interest("/hello/world")); 128 | BOOST_CHECK_EQUAL(numComplete, 3); 129 | BOOST_CHECK_EQUAL(numRepliesFromStore, 2); 130 | 131 | numRepliesFromStore = 0; 132 | m_face.expressInterest(Interest("/hello/world").setCanBePrefix(true), 133 | [this] (auto&&...) { this->numComplete++; }, 134 | [] (auto&&...) { BOOST_CHECK(false); }, 135 | [] (auto&&...) { BOOST_CHECK(false); }); 136 | advanceClocks(10_ms); 137 | BOOST_CHECK_EQUAL(numComplete, 4); 138 | BOOST_CHECK_EQUAL(numRepliesFromStore, 1); 139 | } 140 | 141 | BOOST_AUTO_TEST_CASE(LongerDataName) 142 | { 143 | dataName = Name("/hello/world/IBF"); 144 | BOOST_CHECK_EQUAL(publisher.m_ims.size(), 0); 145 | 146 | expressInterest(Interest("/hello/world")); 147 | BOOST_CHECK_EQUAL(numComplete, 1); 148 | // First segment is answered directly in publish, 149 | // Rest two are satisfied by the store 150 | BOOST_CHECK_EQUAL(numRepliesFromStore, 2); 151 | BOOST_CHECK_EQUAL(publisher.m_ims.size(), 3); 152 | 153 | for (const auto& data : publisher.m_ims) { 154 | BOOST_TEST_CONTEXT(data.getName()) { 155 | BOOST_REQUIRE_EQUAL(data.getName().size(), 5); 156 | BOOST_CHECK(data.getName()[-1].isSegment()); 157 | BOOST_CHECK(data.getName()[-2].isVersion()); 158 | } 159 | } 160 | 161 | advanceClocks(freshness); 162 | BOOST_CHECK_EQUAL(publisher.m_ims.size(), 0); 163 | } 164 | 165 | BOOST_AUTO_TEST_SUITE_END() 166 | 167 | } // namespace psync::tests 168 | -------------------------------------------------------------------------------- /tests/test-state.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | **/ 19 | 20 | #include "PSync/detail/state.hpp" 21 | 22 | #include "tests/boost-test.hpp" 23 | 24 | #include 25 | 26 | namespace psync::tests { 27 | 28 | using detail::State; 29 | 30 | BOOST_AUTO_TEST_SUITE(TestState) 31 | 32 | BOOST_AUTO_TEST_CASE(EncodeDecode) 33 | { 34 | State state; 35 | state.addContent(ndn::Name("test1")); 36 | state.addContent(ndn::Name("test2")); 37 | 38 | // Simulate getting buffer content from segment fetcher 39 | ndn::Data data; 40 | data.setContent(state.wireEncode()); 41 | ndn::Buffer buffer(data.getContent().value_size()); 42 | std::copy(data.getContent().value_begin(), 43 | data.getContent().value_end(), 44 | buffer.begin()); 45 | 46 | ndn::Block block(std::make_shared(buffer)); 47 | State rcvdState; 48 | rcvdState.wireDecode(block); 49 | 50 | BOOST_CHECK(state.getContent() == rcvdState.getContent()); 51 | } 52 | 53 | BOOST_AUTO_TEST_CASE(EmptyContent) 54 | { 55 | State state; 56 | 57 | // Simulate getting buffer content from segment fetcher 58 | ndn::Data data; 59 | data.setContent(state.wireEncode()); 60 | ndn::Buffer buffer(data.getContent().value_size()); 61 | std::copy(data.getContent().value_begin(), 62 | data.getContent().value_end(), 63 | buffer.begin()); 64 | 65 | ndn::Block block(std::make_shared(buffer)); 66 | BOOST_CHECK_NO_THROW(State state2(block)); 67 | 68 | State state2(block); 69 | BOOST_CHECK_EQUAL(state2.getContent().size(), 0); 70 | } 71 | 72 | BOOST_AUTO_TEST_CASE(ReEncode) 73 | { 74 | State state; 75 | state.addContent(ndn::Name("test1")); 76 | state.addContent(ndn::Name("test2")); 77 | 78 | state.wireEncode(); 79 | 80 | state.addContent(ndn::Name("test3")); 81 | 82 | State state2(state.wireEncode()); 83 | BOOST_CHECK_EQUAL(state2.getContent().size(), 3); 84 | } 85 | 86 | BOOST_AUTO_TEST_SUITE_END() 87 | 88 | } // namespace psync::tests 89 | -------------------------------------------------------------------------------- /tests/test-util.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, The University of Memphis 4 | * 5 | * This file is part of PSync. 6 | * See AUTHORS.md for complete list of PSync authors and contributors. 7 | * 8 | * PSync is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU Lesser General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License along with 17 | * PSync, e.g., in COPYING.md file. If not, see . 18 | **/ 19 | 20 | #include "PSync/detail/util.hpp" 21 | 22 | #include "tests/boost-test.hpp" 23 | 24 | namespace psync::tests { 25 | 26 | using namespace psync::detail; 27 | 28 | BOOST_AUTO_TEST_SUITE(TestUtil) 29 | 30 | BOOST_AUTO_TEST_CASE(Compression) 31 | { 32 | const std::vector available{ 33 | CompressionScheme::ZLIB, 34 | CompressionScheme::GZIP, 35 | CompressionScheme::BZIP2, 36 | CompressionScheme::LZMA, 37 | CompressionScheme::ZSTD 38 | }; 39 | std::vector supported; 40 | std::vector notSupported; 41 | 42 | #ifdef PSYNC_HAVE_ZLIB 43 | supported.push_back(CompressionScheme::ZLIB); 44 | #endif 45 | #ifdef PSYNC_HAVE_GZIP 46 | supported.push_back(CompressionScheme::GZIP); 47 | #endif 48 | #ifdef PSYNC_HAVE_BZIP2 49 | supported.push_back(CompressionScheme::BZIP2); 50 | #endif 51 | #ifdef PSYNC_HAVE_LZMA 52 | supported.push_back(CompressionScheme::LZMA); 53 | #endif 54 | #ifdef PSYNC_HAVE_ZSTD 55 | supported.push_back(CompressionScheme::ZSTD); 56 | #endif 57 | 58 | std::set_difference(available.begin(), available.end(), supported.begin(), supported.end(), 59 | std::inserter(notSupported, notSupported.begin())); 60 | 61 | const uint8_t uncompressed[] = {'t', 'e', 's', 't'}; 62 | 63 | for (const auto& s : supported) { 64 | BOOST_CHECK_NO_THROW(compress(s, uncompressed)); 65 | auto compressed = compress(s, uncompressed); 66 | BOOST_CHECK_NO_THROW(decompress(s, *compressed)); 67 | } 68 | 69 | for (const auto& s : notSupported) { 70 | BOOST_CHECK_THROW(compress(s, uncompressed), CompressionError); 71 | BOOST_CHECK_THROW(decompress(s, uncompressed), CompressionError); 72 | } 73 | } 74 | 75 | BOOST_AUTO_TEST_SUITE_END() 76 | 77 | } // namespace psync::tests 78 | -------------------------------------------------------------------------------- /tests/wscript: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- 2 | 3 | top = '..' 4 | 5 | def build(bld): 6 | bld.program( 7 | target=f'{top}/unit-tests', 8 | name='unit-tests', 9 | source=bld.path.ant_glob('**/*.cpp'), 10 | use='BOOST_TESTS PSync', 11 | install_path=None) 12 | -------------------------------------------------------------------------------- /waf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/PSync/410520d9d5910a10272ea7c034756ea754d0dea1/waf -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- 2 | 3 | import os 4 | import subprocess 5 | from waflib import Context, Logs 6 | 7 | VERSION = '0.5.0' 8 | APPNAME = 'psync' 9 | GIT_TAG_PREFIX = '' 10 | 11 | BOOST_COMPRESSION_CODE = ''' 12 | #include 13 | int main() {{ boost::iostreams::{0}_compressor test; }} 14 | ''' 15 | COMPRESSION_SCHEMES = ['zlib', 'gzip', 'bzip2', 'lzma', 'zstd'] 16 | 17 | def options(opt): 18 | opt.load(['compiler_cxx', 'gnu_dirs']) 19 | opt.load(['default-compiler-flags', 20 | 'coverage', 'sanitizers', 'boost', 21 | 'doxygen', 'sphinx'], 22 | tooldir=['.waf-tools']) 23 | 24 | optgrp = opt.add_option_group('PSync Options') 25 | optgrp.add_option('--with-examples', action='store_true', default=False, 26 | help='Build examples') 27 | optgrp.add_option('--with-tests', action='store_true', default=False, 28 | help='Build unit tests') 29 | 30 | for scheme in COMPRESSION_SCHEMES: 31 | optgrp.add_option(f'--without-{scheme}', action='store_true', default=False, 32 | help=f'Disable support for {scheme} (de)compression') 33 | 34 | def configure(conf): 35 | conf.load(['compiler_cxx', 'gnu_dirs', 36 | 'default-compiler-flags', 'boost', 37 | 'doxygen', 'sphinx']) 38 | 39 | conf.env.WITH_EXAMPLES = conf.options.with_examples 40 | conf.env.WITH_TESTS = conf.options.with_tests 41 | 42 | conf.find_program('dot', mandatory=False) 43 | 44 | # Prefer pkgconf if it's installed, because it gives more correct results 45 | # on Fedora/CentOS/RHEL/etc. See https://bugzilla.redhat.com/show_bug.cgi?id=1953348 46 | # Store the result in env.PKGCONFIG, which is the variable used inside check_cfg() 47 | conf.find_program(['pkgconf', 'pkg-config'], var='PKGCONFIG') 48 | 49 | pkg_config_path = os.environ.get('PKG_CONFIG_PATH', f'{conf.env.LIBDIR}/pkgconfig') 50 | conf.check_cfg(package='libndn-cxx', args=['libndn-cxx >= 0.9.0', '--cflags', '--libs'], 51 | uselib_store='NDN_CXX', pkg_config_path=pkg_config_path) 52 | 53 | conf.check_boost(lib='iostreams', mt=True) 54 | if conf.env.BOOST_VERSION_NUMBER < 107100: 55 | conf.fatal('The minimum supported version of Boost is 1.71.0.\n' 56 | 'Please upgrade your distribution or manually install a newer version of Boost.\n' 57 | 'For more information, see https://redmine.named-data.net/projects/nfd/wiki/Boost') 58 | 59 | for scheme in COMPRESSION_SCHEMES: 60 | if getattr(conf.options, f'without_{scheme}'): 61 | continue 62 | conf.check_cxx(fragment=BOOST_COMPRESSION_CODE.format(scheme), 63 | use='BOOST', execute=False, mandatory=False, 64 | msg=f'Checking for {scheme} support in boost iostreams', 65 | define_name=f'HAVE_{scheme.upper()}') 66 | 67 | if conf.env.WITH_TESTS: 68 | conf.check_boost(lib='unit_test_framework', mt=True, uselib_store='BOOST_TESTS') 69 | 70 | conf.check_compiler_flags() 71 | 72 | # Loading "late" to prevent tests from being compiled with profiling flags 73 | conf.load('coverage') 74 | conf.load('sanitizers') 75 | 76 | # If there happens to be a static library, waf will put the corresponding -L flags 77 | # before dynamic library flags. This can result in compilation failure when the 78 | # system has a different version of the PSync library installed. 79 | conf.env.prepend_value('STLIBPATH', ['.']) 80 | 81 | conf.define_cond('WITH_TESTS', conf.env.WITH_TESTS) 82 | # The config header will contain all defines that were added using conf.define() 83 | # or conf.define_cond(). Everything that was added directly to conf.env.DEFINES 84 | # will not appear in the config header, but will instead be passed directly to the 85 | # compiler on the command line. 86 | conf.write_config_header('PSync/detail/config.hpp', define_prefix='PSYNC_') 87 | 88 | def build(bld): 89 | version(bld) 90 | 91 | bld.shlib( 92 | target='PSync', 93 | vnum=VERSION_BASE, 94 | cnum=VERSION_BASE, 95 | source=bld.path.ant_glob('PSync/**/*.cpp'), 96 | use='BOOST NDN_CXX', 97 | includes='.', 98 | export_includes='.') 99 | 100 | if bld.env.WITH_TESTS: 101 | bld.recurse('tests') 102 | 103 | if bld.env.WITH_EXAMPLES: 104 | bld.recurse('examples') 105 | 106 | # Install header files 107 | headers = bld.path.ant_glob('PSync/**/*.hpp') 108 | bld.install_files('${INCLUDEDIR}', headers, relative_trick=True) 109 | bld.install_files('${INCLUDEDIR}/PSync/detail', 'PSync/detail/config.hpp') 110 | 111 | bld(features='subst', 112 | source='PSync.pc.in', 113 | target='PSync.pc', 114 | install_path='${LIBDIR}/pkgconfig', 115 | VERSION=VERSION_BASE) 116 | 117 | def docs(bld): 118 | from waflib import Options 119 | Options.commands = ['doxygen', 'sphinx'] + Options.commands 120 | 121 | def doxygen(bld): 122 | version(bld) 123 | 124 | if not bld.env.DOXYGEN: 125 | bld.fatal('Cannot build documentation ("doxygen" not found in PATH)') 126 | 127 | bld(features='subst', 128 | name='doxygen.conf', 129 | source=['docs/doxygen.conf.in', 130 | 'docs/named_data_theme/named_data_footer-with-analytics.html.in'], 131 | target=['docs/doxygen.conf', 132 | 'docs/named_data_theme/named_data_footer-with-analytics.html'], 133 | VERSION=VERSION, 134 | HAVE_DOT='YES' if bld.env.DOT else 'NO', 135 | HTML_FOOTER='../build/docs/named_data_theme/named_data_footer-with-analytics.html' \ 136 | if os.getenv('GOOGLE_ANALYTICS', None) \ 137 | else '../docs/named_data_theme/named_data_footer.html', 138 | GOOGLE_ANALYTICS=os.getenv('GOOGLE_ANALYTICS', '')) 139 | 140 | bld(features='doxygen', 141 | doxyfile='docs/doxygen.conf', 142 | use='doxygen.conf') 143 | 144 | def sphinx(bld): 145 | version(bld) 146 | 147 | if not bld.env.SPHINX_BUILD: 148 | bld.fatal('Cannot build documentation ("sphinx-build" not found in PATH)') 149 | 150 | bld(features='sphinx', 151 | config='docs/conf.py', 152 | outdir='docs', 153 | source=bld.path.ant_glob('docs/**/*.rst'), 154 | version=VERSION_BASE, 155 | release=VERSION) 156 | 157 | def version(ctx): 158 | # don't execute more than once 159 | if getattr(Context.g_module, 'VERSION_BASE', None): 160 | return 161 | 162 | Context.g_module.VERSION_BASE = Context.g_module.VERSION 163 | Context.g_module.VERSION_SPLIT = VERSION_BASE.split('.') 164 | 165 | # first, try to get a version string from git 166 | version_from_git = '' 167 | try: 168 | cmd = ['git', 'describe', '--abbrev=8', '--always', '--match', f'{GIT_TAG_PREFIX}*'] 169 | version_from_git = subprocess.run(cmd, capture_output=True, check=True, text=True).stdout.strip() 170 | if version_from_git: 171 | if GIT_TAG_PREFIX and version_from_git.startswith(GIT_TAG_PREFIX): 172 | Context.g_module.VERSION = version_from_git[len(GIT_TAG_PREFIX):] 173 | elif not GIT_TAG_PREFIX and ('.' in version_from_git or '-' in version_from_git): 174 | Context.g_module.VERSION = version_from_git 175 | else: 176 | # no tags matched (or we are in a shallow clone) 177 | Context.g_module.VERSION = f'{VERSION_BASE}+git.{version_from_git}' 178 | except (OSError, subprocess.SubprocessError): 179 | pass 180 | 181 | # fallback to the VERSION.info file, if it exists and is not empty 182 | version_from_file = '' 183 | version_file = ctx.path.find_node('VERSION.info') 184 | if version_file is not None: 185 | try: 186 | version_from_file = version_file.read().strip() 187 | except OSError as e: 188 | Logs.warn(f'{e.filename} exists but is not readable ({e.strerror})') 189 | if version_from_file and not version_from_git: 190 | Context.g_module.VERSION = version_from_file 191 | return 192 | 193 | # update VERSION.info if necessary 194 | if version_from_file == Context.g_module.VERSION: 195 | # already up-to-date 196 | return 197 | if version_file is None: 198 | version_file = ctx.path.make_node('VERSION.info') 199 | try: 200 | version_file.write(Context.g_module.VERSION) 201 | except OSError as e: 202 | Logs.warn(f'{e.filename} is not writable ({e.strerror})') 203 | 204 | def dist(ctx): 205 | ctx.algo = 'tar.xz' 206 | version(ctx) 207 | 208 | def distcheck(ctx): 209 | ctx.algo = 'tar.xz' 210 | version(ctx) 211 | --------------------------------------------------------------------------------