├── .envrc
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
├── dependabot.yml
└── workflows
│ ├── audit.yml
│ ├── bench.yml
│ ├── coverage.yml
│ ├── release.yml
│ └── tests_and_checks.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .release-please-manifest.json
├── .rustfmt.toml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Cargo.toml
├── LICENSE
├── README.md
├── assets
└── a_logo.png
├── codecov.yml
├── deny.toml
├── deterministic-bloom-benches
├── Cargo.toml
└── benches
│ └── bloom_filter_benchmark.rs
├── deterministic-bloom-wasm
├── Cargo.toml
├── LICENSE
├── README.md
├── src
│ └── lib.rs
└── tests
│ └── web.rs
├── deterministic-bloom
├── .dockerignore
├── Cargo.toml
├── LICENSE
├── README.md
├── proptest-regressions
│ └── lib.txt
└── src
│ ├── common.rs
│ ├── const_size.rs
│ ├── lib.rs
│ ├── runtime_size.rs
│ └── utils.rs
├── examples
├── Cargo.toml
└── counterparts.rs
├── flake.lock
├── flake.nix
├── release-please-config.json
└── rust-toolchain.toml
/.envrc:
--------------------------------------------------------------------------------
1 | use_flake
2 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Default
2 | * @expede
3 | * @matheus23
4 | * @appcypher
5 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: "\U0001F41B bug"
6 | assignees: ''
7 |
8 | ---
9 |
10 | # Summary
11 |
12 | ## Problem
13 |
14 | Describe the immediate problem.
15 |
16 | ### Impact
17 |
18 | What's the impact of this bug?
19 |
20 | ## Solution
21 |
22 | Describe the sort of fix that would solve the issue.
23 |
24 | # Detail
25 |
26 | **Describe the bug**
27 |
28 | A clear and concise description of what the bug is.
29 |
30 | **To Reproduce**
31 |
32 | Steps to reproduce the behavior:
33 | 1. Go to '...'
34 | 2. Click on '....'
35 | 3. Scroll down to '....'
36 | 4. See error
37 |
38 | **Expected behavior**
39 |
40 | A clear and concise description of what you expected to happen.
41 |
42 | **Screenshots**
43 |
44 | If applicable, add screenshots to help explain your problem.
45 |
46 | **Desktop (please complete the following information):**
47 |
48 | - OS: [e.g. iOS]
49 | - Browser [e.g. chrome, safari]
50 | - Version [e.g. 22]
51 |
52 | **Smartphone (please complete the following information):**
53 |
54 | - Device: [e.g. iPhone6]
55 | - OS: [e.g. iOS8.1]
56 | - Browser [e.g. stock browser, safari]
57 | - Version [e.g. 22]
58 |
59 | **Additional context**
60 |
61 | Add any other context about the problem here.
62 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: "\U0001F497 enhancement"
6 | assignees: ''
7 |
8 | ---
9 |
10 | NB: Feature requests will only be considered if they solve a pain or present a useful refactoring of the code.
11 |
12 | # Summary
13 |
14 | ## Problem
15 |
16 | Describe the pain that this feature will solve.
17 |
18 | ### Impact
19 |
20 | Describe the impact of not having this feature.
21 |
22 | ## Solution
23 |
24 | Describe the solution.
25 |
26 | # Detail
27 |
28 | **Is your feature request related to a problem? Please describe.**
29 |
30 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
31 |
32 | **Describe the solution you'd like**
33 |
34 | A clear and concise description of what you want to happen.
35 |
36 | **Describe alternatives you've considered**
37 |
38 | A clear and concise description of any alternative solutions or features you've considered.
39 |
40 | **Additional context**
41 |
42 | Add any other context or screenshots about the feature request here.
43 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # Description
2 |
3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
4 |
5 | ## Link to issue
6 |
7 | Please add a link to any relevant issues/tickets.
8 |
9 | ## Type of change
10 |
11 | - [ ] Bug fix (non-breaking change that fixes an issue)
12 | - [ ] New feature (non-breaking change that adds functionality)
13 | - [ ] Refactor (non-breaking change that updates existing functionality)
14 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
15 | - [ ] This change requires a documentation update
16 | - [ ] Comments have been added/updated
17 |
18 | Please delete options that are not relevant.
19 |
20 | ## Test plan (required)
21 |
22 | Demonstrate the code is solid. Which commands did you test with and what are the expected results?
23 | Which tests have you added or updated? Do the tests cover all of the changes included in this PR?
24 |
25 | ## Screenshots/Screencaps
26 |
27 | Please add previews of any UI Changes.
28 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 |
8 | updates:
9 | - package-ecosystem: "cargo"
10 | directory: "/deterministic-bloom"
11 | commit-message:
12 | prefix: "chore(rust)"
13 | include: "scope"
14 | target-branch: "main"
15 | schedule:
16 | interval: "weekly"
17 |
18 | - package-ecosystem: "npm"
19 | directory: "/deterministic-bloom-wasm"
20 | commit-message:
21 | prefix: "chore(npm)"
22 | include: "scope"
23 | target-branch: "main"
24 | schedule:
25 | interval: "weekly"
26 |
27 | - package-ecosystem: "github-actions"
28 | directory: "/"
29 | commit-message:
30 | prefix: "chore(ci)"
31 | include: "scope"
32 | target-branch: "main"
33 | schedule:
34 | interval: "weekly"
35 |
--------------------------------------------------------------------------------
/.github/workflows/audit.yml:
--------------------------------------------------------------------------------
1 | name: 🛡 Audit-Check
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * *'
6 |
7 | jobs:
8 | security-audit:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - name: Checkout Repository
13 | uses: actions/checkout@v3
14 |
15 | - name: Run Audit-Check
16 | uses: rustsec/audit-check@v1.4.1
17 | with:
18 | token: ${{ secrets.GITHUB_TOKEN }}
19 |
--------------------------------------------------------------------------------
/.github/workflows/bench.yml:
--------------------------------------------------------------------------------
1 | name: 📈 Benchmark
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 |
7 | pull_request:
8 | branches: [ '*' ]
9 |
10 | concurrency:
11 | group: ${{ github.workflow }}-${{ github.ref }}
12 | cancel-in-progress: true
13 |
14 | jobs:
15 | benchmark:
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - name: Checkout Repository
20 | uses: actions/checkout@v3
21 |
22 | - name: Install Rust Toolchain
23 | uses: actions-rs/toolchain@v1
24 | with:
25 | override: true
26 | toolchain: stable
27 |
28 | - name: Cache Project
29 | uses: Swatinem/rust-cache@v2
30 |
31 | - name: Run Benchmark
32 | run: cargo bench -p deterministic-bloom-benches -- --output-format bencher | tee output.txt
33 |
34 | - name: Upload Benchmark Result Artifact
35 | uses: actions/upload-artifact@v3
36 | with:
37 | name: bench_result
38 | path: output.txt
39 |
40 | - name: Create gh-pages Branch
41 | uses: peterjgrainger/action-create-branch@v2.4.0
42 | env:
43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44 | with:
45 | branch: gh-pages
46 |
47 | - name: Store Benchmark Result
48 | uses: benchmark-action/github-action-benchmark@v1
49 | with:
50 | name: Rust Benchmark
51 | tool: 'cargo'
52 | output-file-path: output.txt
53 | github-token: ${{ secrets.GITHUB_TOKEN }}
54 | auto-push: ${{ github.event_name == 'push' && github.repository == 'wnfs-wg/deterministic-bloom' && github.ref == 'refs/heads/main' }}
55 | alert-threshold: '200%'
56 | comment-on-alert: true
57 | fail-on-alert: true
58 | alert-comment-cc-users: '@expede'
59 |
--------------------------------------------------------------------------------
/.github/workflows/coverage.yml:
--------------------------------------------------------------------------------
1 | name: ☂ Code Coverage
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 |
7 | pull_request:
8 | branches: [ '*' ]
9 |
10 | concurrency:
11 | group: ${{ github.workflow }}-${{ github.ref }}
12 | cancel-in-progress: true
13 |
14 | jobs:
15 | coverage:
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - name: Checkout Repository
20 | uses: actions/checkout@v3
21 |
22 | - name: Install Rust Toolchain
23 | uses: actions-rs/toolchain@v1
24 | with:
25 | override: true
26 | toolchain: nightly
27 | components: llvm-tools-preview
28 | profile: minimal
29 |
30 | - name: Cache Project
31 | uses: Swatinem/rust-cache@v2
32 |
33 | - name: Generate Code coverage
34 | env:
35 | CARGO_INCREMENTAL: '0'
36 | LLVM_PROFILE_FILE: "deterministic-bloom-%p-%m.profraw"
37 | RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests'
38 | RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests'
39 | ## currently just runs coverage on rust project
40 | run: cargo test -p deterministic-bloom --all-features
41 |
42 | - name: Install grcov
43 | run: "curl -L https://github.com/mozilla/grcov/releases/download/v0.8.12/grcov-x86_64-unknown-linux-gnu.tar.bz2 | tar jxf -"
44 |
45 | - name: Run grcov
46 | run: "./grcov . --llvm --binary-path target/debug/ -s . -t lcov --branch --ignore-not-existing --ignore '/*' -o lcov.info"
47 |
48 | - name: Install covfix
49 | uses: actions-rs/install@v0.1
50 | with:
51 | crate: rust-covfix
52 | use-tool-cache: true
53 |
54 | - name: Run covfix
55 | run: rust-covfix lcov.info -o lcov.info --verbose
56 |
57 | - name: Upload to codecov.io
58 | uses: codecov/codecov-action@v3
59 | with:
60 | token: ${{ secrets.CODECOV_TOKEN }}
61 | fail_ci_if_error: true
62 | files: lcov.info
63 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: 𝌚 Release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | force-publish:
7 | required: true
8 | type: boolean
9 | description: Publish Releases at Anytime
10 |
11 | workflow_run:
12 | workflows: [ 🧪 Tests and Checks ]
13 | branches: [main]
14 | types: [ completed ]
15 |
16 | concurrency:
17 | group: ${{ github.workflow }}-${{ github.ref }}
18 | cancel-in-progress: true
19 |
20 | jobs:
21 | release-please:
22 | runs-on: ubuntu-latest
23 | if: >
24 | github.ref == 'refs/heads/main' &&
25 | github.repository_owner == 'wnfs-wg' &&
26 | github.event.workflow_run.conclusion == 'success'
27 |
28 | outputs:
29 | releases_created: ${{ steps.release.outputs['deterministic-bloom--release_created'] || steps.release.outputs['deterministic-bloom-wasm--release_created'] }}
30 | wasm_release_created: ${{ steps.release.outputs['deterministic-bloom-wasm--release_created'] }}
31 |
32 | steps:
33 | - name: Run release-please
34 | id: release
35 | uses: google-github-actions/release-please-action@v3
36 | with:
37 | token: ${{ secrets.GITHUB_TOKEN }}
38 | default-branch: main
39 | command: manifest
40 | extra-files: |
41 | README.md
42 |
43 |
44 | publish-release-crates:
45 | if: ${{ needs.release-please.outputs.releases_created || github.event.inputs.force-publish }}
46 |
47 | runs-on: ubuntu-latest
48 | needs: [ release-please ]
49 |
50 | permissions:
51 | contents: write
52 |
53 | steps:
54 | - name: Checkout Repository
55 | uses: actions/checkout@v3
56 |
57 | - name: Cache Project
58 | uses: Swatinem/rust-cache@v2
59 |
60 | - name: Install Rust Toolchain
61 | uses: actions-rs/toolchain@v1
62 | with:
63 | override: true
64 | profile: minimal
65 | toolchain: stable
66 |
67 | - name: Install Cargo Workspaces
68 | env:
69 | RUSTFLAGS: '-Copt-level=1'
70 | uses: actions-rs/cargo@v1
71 | with:
72 | args: --force cargo-workspaces
73 | command: install
74 |
75 | - name: Cargo Publish to crates.io
76 | env:
77 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
78 | run: cargo workspaces publish --from-git
79 |
80 | publish-release-npm:
81 | if: ${{ needs.release-please.outputs.wasm_release_created || github.event.inputs.force-publish }}
82 |
83 | runs-on: ubuntu-latest
84 | needs: [ release-please ]
85 |
86 | permissions:
87 | contents: write
88 |
89 | defaults:
90 | run:
91 | working-directory: ./deterministic-bloom-wasm
92 |
93 | steps:
94 | - name: Checkout Repository
95 | uses: actions/checkout@v3
96 |
97 | - name: Cache Project
98 | uses: Swatinem/rust-cache@v2
99 |
100 | - name: Install Rust Toolchain
101 | uses: actions-rs/toolchain@v1
102 | with:
103 | override: true
104 | profile: minimal
105 | toolchain: stable
106 |
107 | - name: Setup Node
108 | uses: actions/setup-node@v3
109 | with:
110 | node-version: '16.x'
111 | registry-url: 'https://registry.npmjs.org'
112 |
113 | - name: Install wasm-pack
114 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
115 |
116 | # Builds output via wasm-pack and renames package.json name to remove
117 | # `-wasm`.
118 | - name: Build
119 | run: |
120 | wasm-pack build --target web --out-name deterministic-bloom
121 | sed -i -e 's/"name": "deterministic-bloom-wasm"/"name": "deterministic-bloom",\n "type": "module"/g' pkg/package.json
122 |
123 | - name: Publish to npm
124 | run: wasm-pack publish
125 | working-directory: pkg
126 | env:
127 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
128 |
--------------------------------------------------------------------------------
/.github/workflows/tests_and_checks.yml:
--------------------------------------------------------------------------------
1 | name: 🧪 Tests and Checks
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 |
7 | pull_request:
8 | branches: [ '*' ]
9 |
10 | concurrency:
11 | group: ${{ github.workflow }}-${{ github.ref }}
12 | cancel-in-progress: true
13 |
14 | jobs:
15 | run-checks:
16 | runs-on: ubuntu-latest
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | rust-toolchain:
21 | - stable
22 | - nightly
23 | # minimum version
24 | - 1.64
25 | steps:
26 | - name: Checkout Repository
27 | uses: actions/checkout@v3
28 |
29 | # Smarter caching action, speeds up build times compared to regular cache:
30 | # https://github.com/Swatinem/rust-cache
31 | - name: Cache Project
32 | uses: Swatinem/rust-cache@v2
33 |
34 | # Widely adopted suite of Rust-specific boilerplate actions, especially
35 | # toolchain/cargo use: https://actions-rs.github.io/
36 | - name: Install Rust Toolchain
37 | uses: actions-rs/toolchain@v1
38 | with:
39 | override: true
40 | components: rustfmt, clippy
41 | toolchain: ${{ matrix.rust-toolchain }}
42 |
43 | - name: Check Format
44 | uses: actions-rs/cargo@v1
45 | with:
46 | args: --all -- --check
47 | command: fmt
48 | toolchain: ${{ matrix.rust-toolchain }}
49 |
50 | - name: Run Linter
51 | uses: actions-rs/cargo@v1
52 | with:
53 | args: --all -- -D warnings
54 | command: clippy
55 | toolchain: ${{ matrix.rust-toolchain }}
56 |
57 | # Check for security advisories.
58 | - name: Check Advisories
59 | if: ${{ matrix.rust-toolchain == 'stable' }}
60 | uses: EmbarkStudios/cargo-deny-action@v1
61 | with:
62 | command: check advisories
63 | continue-on-error: true
64 |
65 | # Audit licenses, unreleased crates, and unexpected duplicate versions.
66 | - name: Check Bans, Licenses, and Sources
67 | if: ${{ matrix.rust-toolchain == 'stable' }}
68 | uses: EmbarkStudios/cargo-deny-action@v1
69 | with:
70 | command: check bans licenses sources
71 |
72 | - name: Install wasm-pack
73 | if: ${{ matrix.rust-toolchain == 'stable' }} && github.event_name == 'push' }}
74 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
75 |
76 | # Only "test" release build on push event.
77 | - name: Test Release
78 | if: ${{ matrix.rust-toolchain == 'stable' }} && github.event_name == 'push' }}
79 | run: |
80 | cargo build --manifest-path deterministic-bloom/Cargo.toml --release
81 | cd deterministic-bloom-wasm && wasm-pack build --target web --release
82 |
83 | run-cargo-tests:
84 | runs-on: ubuntu-latest
85 | strategy:
86 | fail-fast: false
87 | matrix:
88 | rust-toolchain:
89 | - stable
90 | - nightly
91 |
92 | steps:
93 | - name: Checkout Repository
94 | uses: actions/checkout@v3
95 |
96 | - name: Cache Project
97 | uses: Swatinem/rust-cache@v2
98 |
99 | - name: Install Rust Toolchain
100 | uses: actions-rs/toolchain@v1
101 | with:
102 | override: true
103 | toolchain: ${{ matrix.rust-toolchain }}
104 |
105 | - name: Run Tests
106 | run: cargo test --manifest-path deterministic-bloom/Cargo.toml --all-features
107 |
108 | run-headless-tests:
109 | runs-on: ${{ matrix.os }}
110 | strategy:
111 | fail-fast: false
112 | matrix:
113 | os: [ ubuntu-latest ]
114 | browser: [ firefox, chrome ]
115 |
116 | # include:
117 | # bug w/ wasm-bindgen: https://github.com/rustwasm/wasm-bindgen/issues/3004
118 | # - os: macos-latest
119 | # browser: safari
120 |
121 | defaults:
122 | run:
123 | working-directory: ./deterministic-bloom-wasm
124 |
125 | steps:
126 | - name: Checkout Repository
127 | uses: actions/checkout@v3
128 |
129 | - name: Install wasm-pack
130 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
131 |
132 | - name: Cache Project
133 | uses: Swatinem/rust-cache@v2
134 |
135 | - name: Install Rust Toolchain
136 | uses: actions-rs/toolchain@v1
137 | with:
138 | override: true
139 | toolchain: stable
140 |
141 | - name: Run Rust Headless Browser Tests
142 | run: wasm-pack test --headless --${{ matrix.browser }}
143 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | **/target/
4 | Cargo.lock
5 | # These are backup files generated by rustfmt
6 | **/*.rs.bk
7 |
8 | # Ignore local environment settings
9 | .envrc.custom
10 | .direnv
11 |
12 | **/package-lock.json
13 | **/pkg/
14 | wasm-pack.log
15 | **/node_modules/
16 |
17 | # Other files + dirs
18 | private
19 | *.temp
20 | *.tmp
21 | **/.history
22 | **/.DS_Store
23 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | # See https://pre-commit.com for more information
2 | # pre-commit install
3 | # pre-commit install --hook-type commit-msg
4 | exclude: ^(LICENSE|LICENSE*)
5 | repos:
6 | - repo: local
7 | hooks:
8 | - id: fmt
9 | name: fmt
10 | description: Format rust files.
11 | entry: cargo fmt
12 | language: system
13 | types: [rust]
14 | args: ["--all", "--", "--check"]
15 | - id: cargo-check
16 | name: cargo check
17 | description: Check the package for errors.
18 | entry: cargo check
19 | language: system
20 | types: [rust]
21 | pass_filenames: false
22 | - id: clippy
23 | name: clippy
24 | description: Lint via clippy
25 | entry: cargo clippy
26 | language: system
27 | args: ["--workspace", "--", "-D", "warnings"]
28 | types: [rust]
29 | pass_filenames: false
30 |
31 | - repo: https://github.com/DevinR528/cargo-sort
32 | rev: v1.0.9
33 | hooks:
34 | - id: cargo-sort
35 | args: ["-w"]
36 |
37 | - repo: https://github.com/compilerla/conventional-pre-commit
38 | rev: v2.1.1
39 | hooks:
40 | - id: conventional-pre-commit
41 | stages:
42 | - commit-msg
43 |
44 | - repo: https://github.com/pre-commit/pre-commit-hooks
45 | rev: v4.3.0
46 | hooks:
47 | - id: no-commit-to-branch
48 | args: ["-b", "main"]
49 | - id: check-merge-conflict
50 | - id: trailing-whitespace
51 | - id: end-of-file-fixer
52 | - id: check-yaml
53 | - id: check-json
54 | - id: check-added-large-files
55 | - id: detect-private-key
56 | - id: check-executables-have-shebangs
57 | - id: check-toml
58 |
--------------------------------------------------------------------------------
/.release-please-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "deterministic-bloom": "0.1.0",
3 | "deterministic-bloom-wasm": "0.1.0"
4 | }
5 |
--------------------------------------------------------------------------------
/.rustfmt.toml:
--------------------------------------------------------------------------------
1 | edition = "2021"
2 | imports_granularity = "Crate"
3 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wnfs-wg/deterministic-bloom/62849fa6b3d1731ea4f4cb18b8d81e845404dc10/CHANGELOG.md
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | **TL;DR Be kind, inclusive, and considerate.**
4 |
5 | In the interest of fostering an open, inclusive, and welcoming environment, all
6 | members, contributors, and maintainers interacting within our online community
7 | (including Discord, Discourse, etc.), on affiliated projects and repositories
8 | (including issues, pull requests, and discussions on Github), and/or involved
9 | with associated events pledge to accept and observe the following Code of
10 | Conduct.
11 |
12 | As members, contributors, and maintainers, we pledge to make participation in
13 | our projects and community a harassment-free experience, ensuring a safe
14 | environment for all, regardless of background, gender, gender identity and
15 | expression, age, sexual orientation, disability, physical appearance, body size,
16 | race, ethnicity, religion (or lack thereof), or any other dimension of
17 | diversity.
18 |
19 | Sexual language and imagery will not be accepted in any way. Be kind to others.
20 | Do not insult or put down people within the community. Behave professionally.
21 | Remember that harassment and sexist, racist, or exclusionary jokes are not
22 | appropriate in any form. Participants violating these rules may be sanctioned or
23 | expelled from the community and related projects.
24 |
25 | ## Spelling it out.
26 |
27 | Harassment includes offensive verbal comments or actions related to or involving
28 |
29 | - background
30 | - gender
31 | - gender identity and expression
32 | - age
33 | - sexual orientation
34 | - disability
35 | - physical appearance
36 | - body size
37 | - race
38 | - ethnicity
39 | - religion (or lack thereof)
40 | - economic status
41 | - geographic location
42 | - technology choices
43 | - sexual imagery
44 | - deliberate intimidation
45 | - violence and threats of violence
46 | - stalking
47 | - doxing
48 | - inappropriate or unwelcome physical contact in public spaces
49 | - unwelcomed sexual attention
50 | - influencing unacceptable behavior
51 | - any other dimension of diversity
52 |
53 | ## Our Responsibilities
54 |
55 | Maintainers of the community and associated projects are not only subject to the
56 | anti-harassment policy, but also responsible for executing the policy,
57 | moderating related forums, and for taking appropriate and fair corrective action
58 | in response to any instances of unacceptable behavior that breach the policy.
59 |
60 | Maintainers have the right to remove and reject comments, threads, commits,
61 | code, documentation, pull requests, issues, and contributions not aligned with
62 | this Code of Conduct.
63 |
64 | ## Scope
65 |
66 | This Code of Conduct applies within all project and community spaces, as well as
67 | in any public spaces where an individual representing the community is involved.
68 | This covers
69 |
70 | - Interactions on the Github repository, including discussions, issues, pull
71 | requests, commits, and wikis
72 | - Interactions on any affiliated Discord, Slack, IRC, or related online
73 | communities and forums like Discourse, etc.
74 | - Any official project emails and social media posts
75 | - Individuals representing the community at public events like meetups, talks,
76 | and presentations
77 |
78 | ## Enforcement
79 |
80 | All instances of abusive, harassing, or otherwise unacceptable behavior should
81 | be reported by contacting the project and community maintainers at
82 | [brooklyn@fission.codes][support-email]. All complaints will be reviewed and
83 | investigated and will result in a response that is deemed necessary and
84 | appropriate to the circumstances.
85 |
86 | Maintainers of the community and associated projects are obligated to maintain
87 | confidentiality with regard to the reporter of an incident. Further details of
88 | specific enforcement policies may be posted separately.
89 |
90 | Anyone asked to stop abusive, harassing, or otherwise unacceptable behavior are
91 | expected to comply immediately and accept the response decided on by the
92 | maintainers of the community and associated projects.
93 |
94 | ## Need help?
95 |
96 | If you are experiencing harassment, witness an incident or have concerns about
97 | content please contact us at [brooklyn@fission.codes][support-email].
98 |
99 | ## Attribution
100 |
101 | This Code of Conduct is adapted from the [Contributor Covenant, v2.1][contributor-cov],
102 | among other sources like [!!con’s Code of Conduct][!!con] and
103 | [Mozilla’s Community Participation Guidelines][mozilla].
104 |
105 | [!!con]: https://bangbangcon.com/conduct.html
106 | [contributor-cov]: https://www.contributor-covenant.org/version/2/1/code_of_conduct/
107 | [mozilla]: https://www.mozilla.org/en-US/about/governance/policies/participation/
108 | [support-email]: mailto:brooklyn@fission.codes
109 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to deterministic-bloom
2 |
3 | We welcome everyone to contribute what and where they can. Whether you are brand
4 | new, just want to contribute a little bit, or want to contribute a lot there is
5 | probably something you can help out with. Check out our
6 | [good first issues][good-first-issues] label for in the issues tab to see a list
7 | of issue that good for those new to the project.
8 |
9 | ## Where to Get Help
10 |
11 | The main way to get help is on our [discord server](https://discord.gg/fissioncodes).server][https://discord.gg/fissioncodes].
12 | Though, this guide should help you get started. It may be slightly lengthy, but it's
13 | designed for those who are new so please don't let length intimidate you.
14 |
15 | ## Code of Conduct
16 |
17 | Please be kind, inclusive, and considerate when interacting when interacting
18 | with others and follow our [code of conduct](./CODE_OF_CONDUCT.md).
19 |
20 | ## How to Contribute
21 |
22 | If the code adds a feature that is not already present in an issue, you can
23 | create a new issue for the feature and add the pull request to it. If the code
24 | adds a feature that is not already present in an issue, you can create a new
25 | issue for the feature and add the pull request to it.
26 |
27 | ### Contributing by Adding a Topic for Discussion
28 |
29 | #### Issues
30 |
31 | If you have found a bug and would like to report it or if you have a feature
32 | that you feel we should add, then we'd love it if you opened an issue! ❤️
33 | Before you do, please search the other issues to avoid creating a duplicate
34 | issue.
35 |
36 | To submit a new issue just hit the issue button and a choice between two
37 | templates should appear. Then, follow along with the template you chose. If you
38 | don't know how to fill in all parts of the template go ahead and skip those
39 | parts. You can edit the issue later.
40 |
41 | #### Discussion
42 |
43 | If you have a new discussion you want to start but it isn't a bug or feature
44 | add, then you can start a [GitHub discussion][gh-discussions]. Some examples of
45 | what kinds of things that are good discussion topics can include, but are not
46 | limited to the following:
47 |
48 | - Community announcements and/or asking the community for feedback
49 | - Discussing a new release
50 | - Asking questions, Q&A that isn't for sure a bug report
51 |
52 | ### Contributing through Code
53 |
54 | In order to contribute through code follow the steps below. Note that you don't
55 | need to be the best programmer to contribute. Our discord is open for questions
56 | 1. **Pick a feature** you would like to add or a bug you would like to fix
57 | - If you wish to contribute but what you want to fix/add is not already
58 | covered in an existing issue, please open a new issue.
59 |
60 | 2. **Discuss** the issue with the rest of the community
61 | - Before you write any code, it is recommended that you discuss your
62 | intention to write the code on the issue you are attempting to edit.
63 | - This helps to stop you from wasting your time duplicating the work of
64 | others that maybe working on the same issue; at the same time.
65 | - This step also allows you to get helpful pointers on the community on some
66 | problems they may have encountered on similar issues.
67 |
68 | 3. **Fork** the repository
69 | - A fork creates a copy of the code on your Github, so you can work on it
70 | separately from everyone else.
71 | - You can learn more about forking [here][forking].
72 |
73 | 4. Ensure that you have **commit signing** enabled
74 | - This ensures that the code you submit was committed by you and not someone
75 | else who claims to be you.
76 | - You can learn more about how to setup commit signing [here][commit-signing].
77 | - If you have already made some commits that you wish to put in a pull
78 | request without signing them, then you can follow [this guide][post-signing]
79 | on how to fix that.
80 |
81 | 5. **Clone** the repository to your local computer
82 | - This puts a copy of your fork on your computer so you can edit it
83 | - You can learn more about cloning repositories [here][git-clone].
84 |
85 | 6. **Build** the project
86 | - For a detailed look on how to build deterministic-bloom look at our
87 | [README file](./README.md).
88 |
89 | 7. **Start writing** your code
90 | - Open up your favorite code editor and make the changes that you wanted to
91 | make to the repository.
92 | - Make sure to test your code with the test command(s) found in our
93 | [README file](./README.md).
94 |
95 | 8. **Write tests** for your code
96 | - If you are adding a new feature, you should write tests that ensure that
97 | if someone make changes to the code it cannot break your new feature
98 | without breaking the test.
99 | - If your code adds a new feature, you should also write at least one
100 | documentation test. The documentation test's purpose is to demonstrate and
101 | document how to use the API feature.
102 | - If your code fixes a bug, you should write tests that ensure that if
103 | someone makes code changes in the future the bug does not re-emerge
104 | without breaking test.
105 | - Please create integration tests, if the addition is large enough to
106 | warrant them, and unit tests.
107 | * Unit tests are tests that ensure the functionality of a single
108 | function or small section of code.
109 | * Integration tests test large large sections of code.
110 | * Read more about the differences [here][unit-and-integration].
111 | - For more information on test organization, take a look [here][test-org].
112 |
113 | 9. Ensure that the code that you made follows our Rust **coding guidelines**
114 | - You can find a list of some Rust guidelines [here][rust-style-guide]. This
115 | is a courtesy to the programmers that come after you. The easier your code
116 | is to read, the easier it will be for the next person to make modifications.
117 | - If you find it difficult to follow the guidelines or if the guidelines or
118 | unclear, please reach out to us through our discord linked above, or you
119 | can just continue and leave a comment at the pull request stage.
120 |
121 | 10. **Commit and Push** your code
122 | - This sends your changes to your repository branch.
123 | - You can learn more about committing code [here][commiting-code] and
124 | pushing it to a remote repository [here][push-remote].
125 | - We use conventional commits for the names and description of commits.
126 | You can find out more about them [here][conventional-commits].
127 |
128 | 11. The final step is to create **pull request** to our main branch 🎉
129 | - A pull request is how you merge the code you just worked so hard on with
130 | the code everyone else has access to.
131 | - Once you have submitted your pull request, we will review your code and
132 | check to make sure the code implements the feature or fixes the bug. We
133 | may leave some feedback and suggest edits. You can make the changes we
134 | suggest by committing more code to your fork.
135 | - You can learn more about pull requests [here][prs].
136 |
137 |
138 | [conventional-commits]: https://www.conventionalcommits.org/en/v1.0.0/
139 | [commiting-code]: https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/making-changes-in-a-branch/committing-and-reviewing-changes-to-your-project
140 | [commit-signing]: https://www.freecodecamp.org/news/what-is-commit-signing-in-git/
141 | [forking]: https://docs.github.com/en/get-started/quickstart/fork-a-repo
142 | [gh-discussions]: https://docs.github.com/en/discussions
143 | [git-clone]: https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository
144 | [good-first-issues]: [https://build.prestashop-project.org/news/a-definition-of-the-good-first-issue-label/]
145 | [post-signing]: https://dev.to/jmarhee/signing-existing-commits-with-gpg-5b58
146 | [prs]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests
147 | [push-remote]: https://docs.github.com/en/get-started/using-git/pushing-commits-to-a-remote-repository
148 | [rust-style-guide]: https://rust-lang.github.io/api-guidelines/about.html
149 | [test-org]: https://doc.rust-lang.org/book/ch11-03-test-organization.html
150 | [unit-and-integration]: https://www.geeksforgeeks.org/difference-between-unit-testing-and-integration-testing/
151 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "deterministic-bloom",
4 | "deterministic-bloom-benches",
5 | "deterministic-bloom-wasm",
6 | "examples"
7 | ]
8 |
9 | [profile.release]
10 | # Tell `rustc` to optimize for small code size.
11 | opt-level = "s"
12 |
13 | # Speedup build on macOS
14 | # See https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html#splitting-debug-information
15 | [profile.dev]
16 | split-debuginfo = "unpacked"
17 |
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2022 Expede
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
28 |
29 | :warning: Work in progress :warning:
30 |
31 | ## Outline
32 |
33 | - [Crates](#crates)
34 | - [Usage](#usage)
35 | - [Testing the Project](#testing-the-project)
36 | - [Benchmarking the Project](#benchmarking-the-project)
37 | - [Setting-up deterministic-bloom-wasm](#setting-up-deterministic-bloom-wasm)
38 | - [Contributing](#contributing)
39 | - [Getting Help](#getting-help)
40 | - [External Resources](#external-resources)
41 | - [License](#license)
42 |
43 | ## Crates
44 |
45 | - [deterministic-bloom](https://github.com/wnfs-wg/deterministic-bloom/tree/main/deterministic-bloom)
46 | - [deterministic-bloom-wasm](https://github.com/wnfs-wg/deterministic-bloom/tree/main/deterministic-bloom-wasm)
47 |
48 | ## Usage
49 |
50 | - Add the following to the `[dependencies]` section of your `Cargo.toml` file
51 | for using the rust-only `deterministic-bloom` crate/workspace:
52 |
53 | ```toml
54 | deterministic-bloom = "0.1.0"
55 | ```
56 |
57 | - Add the following to the `[dependencies]` section of your `Cargo.toml` file
58 | for using `deterministic-bloom-wasm` crate/workspace:
59 |
60 | ```toml
61 | deterministic-bloom-wasm = "0.1.0"
62 | ```
63 |
64 | ## Testing the Project
65 |
66 | - Run tests for crate/workspace `deterministic-bloom`:
67 |
68 | ```console
69 | cd deterministic-bloom && cargo test
70 | ```
71 |
72 | - To run tests for crate/workspace `deterministic-bloom-wasm`, follow
73 | the instructions in [deterministic-bloom-wasm](./deterministic-bloom-wasm#testing-the-project),
74 | which leverages [wasm-pack][wasm-pack].
75 |
76 | ## Benchmarking the Project
77 |
78 | For benchmarking and measuring performance, this workspaces provides
79 | a Rust-specific benchmarking package leveraging [criterion][criterion] and a
80 | `test_utils` feature flag for integrating [proptest][proptest] within the
81 | suite for working with [strategies][strategies] and sampling from randomly
82 | generated values.
83 |
84 | - Run benchmarks
85 |
86 | ```console
87 | cargo bench -p deterministic-bloom-benches
88 | ```
89 |
90 | *Note*: Currently, this workspace only supports Rust-native benchmarking, as
91 | `wasm-bindgen` support for criterion is still [an open issue][criterion-bindgen].
92 | However, with some extra work, benchmarks can be compiled to [wasi][wasi] and
93 | run with [wasmer][wasmer]/[wasmtime][wasmtime] or in the brower with
94 | [webassembly.sh][wasmsh]. Please catch-up with wasm support for criterion on their
95 | [user-guide][criterion-user-guide].
96 |
97 | ## Setting-up deterministic-bloom-wasm
98 |
99 | The Wasm targetted version of this project relies on [wasm-pack][wasm-pack]
100 | for building, testing, and publishing artifacts sutiable for
101 | [Node.js][node-js], web broswers, or bundlers like [webpack][webpack].
102 |
103 | Please read more on working with `wasm-pack` directly in
104 | [deterministic-bloom-wasm](./deterministic-bloom-wasm#set-up).
105 |
106 | ## Contributing
107 |
108 | :balloon: We're thankful for any feedback and help in improving our project!
109 | We have a [contributing guide](./CONTRIBUTING.md) to help you get involved. We
110 | also adhere to our [Code of Conduct](./CODE_OF_CONDUCT.md).
111 |
112 | ### Nix
113 | This repository contains a [Nix flake][nix-flake] that initiates both the Rust
114 | toolchain set in [rust-toolchain.toml](./rust-toolchain.toml) and a
115 | [pre-commit hook](#pre-commit-hook). It also installs helpful cargo binaries for
116 | development. Please install [nix][nix] and [direnv][direnv] to get started.
117 |
118 | Run `nix develop` or `direnv allow` to load the `devShell` flake output,
119 | according to your preference.
120 |
121 | ### Formatting
122 |
123 | For formatting Rust in particular, please use `cargo +nightly fmt` as it uses
124 | specific nightly features we recommend by default.
125 |
126 | ### Pre-commit Hook
127 |
128 | This library recommends using [pre-commit][pre-commit] for running pre-commit
129 | hooks. Please run this before every commit and/or push.
130 |
131 | - If you are doing interim commits locally, and for some reason if you _don't_
132 | want pre-commit hooks to fire, you can run
133 | `git commit -a -m "Your message here" --no-verify`.
134 |
135 | ### Recommended Development Flow
136 |
137 | - We recommend leveraging [cargo-watch][cargo-watch],
138 | [cargo-expand][cargo-expand] and [irust][irust] for Rust development.
139 | - We recommend using [cargo-udeps][cargo-udeps] for removing unused dependencies
140 | before commits and pull-requests.
141 |
142 | ### Conventional Commits
143 |
144 | This project *lightly* follows the [Conventional Commits
145 | convention][commit-spec-site] to help explain
146 | commit history and tie in with our release process. The full specification
147 | can be found [here][commit-spec]. We recommend prefixing your commits with
148 | a type of `fix`, `feat`, `docs`, `ci`, `refactor`, etc..., structured like so:
149 |
150 | ```
151 | [optional scope]:
152 |
153 | [optional body]
154 |
155 | [optional footer(s)]
156 | ```
157 |
158 | ## Getting Help
159 |
160 | For usage questions, usecases, or issues reach out to us in our [Discord channel](https://discord.gg/fissioncodes).
161 |
162 | We would be happy to try to answer your question or try opening a new issue on Github.
163 |
164 | ## External Resources
165 |
166 | These are references to specifications, talks and presentations, etc.
167 |
168 | ## License
169 |
170 | This project is licensed under the [Apache License 2.0](./LICENSE), or
171 | [http://www.apache.org/licenses/LICENSE-2.0][apache].
172 |
173 |
174 | [apache]: https://www.apache.org/licenses/LICENSE-2.0
175 | [cargo-expand]: https://github.com/dtolnay/cargo-expand
176 | [cargo-udeps]: https://github.com/est31/cargo-udeps
177 | [cargo-watch]: https://github.com/watchexec/cargo-watch
178 | [commit-spec]: https://www.conventionalcommits.org/en/v1.0.0/#specification
179 | [commit-spec-site]: https://www.conventionalcommits.org/
180 | [criterion]: https://github.com/bheisler/criterion.rs
181 | [criterion-bindgen]: https://github.com/bheisler/criterion.rs/issues/270
182 | [direnv]:https://direnv.net/
183 | [irust]: https://github.com/sigmaSd/IRust
184 | [mit]: http://opensource.org/licenses/MIT
185 | [nix]:https://nixos.org/download.html
186 | [nix-flake]: https://nixos.wiki/wiki/Flakes
187 | [node-js]: https://nodejs.dev/en/
188 | [pre-commit]: https://pre-commit.com/
189 | [proptest]: https://github.com/proptest-rs/proptest
190 | [strategies]: https://docs.rs/proptest/latest/proptest/strategy/trait.Strategy.html
191 | [criterion-user-guide]: https://github.com/bheisler/criterion.rs/blob/version-0.4/book/src/user_guide/wasi.md
192 | [wasi]: https://wasi.dev/
193 | [wasmer]: https://wasmer.io/
194 | [wasmtime]: https://docs.wasmtime.dev/
195 | [wasmsh]: https://webassembly.sh/
196 | [wasm-pack]: https://rustwasm.github.io/docs/wasm-pack/
197 | [webpack]: https://webpack.js.org/
198 |
--------------------------------------------------------------------------------
/assets/a_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wnfs-wg/deterministic-bloom/62849fa6b3d1731ea4f4cb18b8d81e845404dc10/assets/a_logo.png
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | ignore:
2 | - "deterministic-bloom/tests"
3 | - "deterministic-bloom-wasm"
4 | - "deterministic-bloom-benches"
5 | - "examples"
6 |
7 | comment:
8 | layout: "reach, diff, flags, files"
9 | require_changes: true
10 |
11 | github_checks:
12 | annotations: false
13 |
14 | coverage:
15 | status:
16 | project:
17 | default:
18 | threshold: 5%
19 |
--------------------------------------------------------------------------------
/deny.toml:
--------------------------------------------------------------------------------
1 | # This template contains all of the possible sections and their default values
2 |
3 | # Note that all fields that take a lint level have these possible values:
4 | # * deny - An error will be produced and the check will fail
5 | # * warn - A warning will be produced, but the check will not fail
6 | # * allow - No warning or error will be produced, though in some cases a note
7 | # will be
8 |
9 | # The values provided in this template are the default values that will be used
10 | # when any section or field is not specified in your own configuration
11 |
12 | # If 1 or more target triples (and optionally, target_features) are specified,
13 | # only the specified targets will be checked when running `cargo deny check`.
14 | # This means, if a particular package is only ever used as a target specific
15 | # dependency, such as, for example, the `nix` crate only being used via the
16 | # `target_family = "unix"` configuration, that only having windows targets in
17 | # this list would mean the nix crate, as well as any of its exclusive
18 | # dependencies not shared by any other crates, would be ignored, as the target
19 | # list here is effectively saying which targets you are building for.
20 | targets = [
21 | # The triple can be any string, but only the target triples built in to
22 | # rustc (as of 1.40) can be checked against actual config expressions
23 | #{ triple = "x86_64-unknown-linux-musl" },
24 | # You can also specify which target_features you promise are enabled for a
25 | # particular target. target_features are currently not validated against
26 | # the actual valid features supported by the target architecture.
27 | #{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
28 | ]
29 |
30 | # This section is considered when running `cargo deny check advisories`
31 | # More documentation for the advisories section can be found here:
32 | # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
33 | [advisories]
34 | # The path where the advisory database is cloned/fetched into
35 | db-path = "~/.cargo/advisory-db"
36 | # The url(s) of the advisory databases to use
37 | db-urls = ["https://github.com/rustsec/advisory-db"]
38 | # The lint level for security vulnerabilities
39 | vulnerability = "deny"
40 | # The lint level for unmaintained crates
41 | unmaintained = "warn"
42 | # The lint level for crates that have been yanked from their source registry
43 | yanked = "deny"
44 | # The lint level for crates with security notices. Note that as of
45 | # 2019-12-17 there are no security notice advisories in
46 | # https://github.com/rustsec/advisory-db
47 | notice = "warn"
48 | # A list of advisory IDs to ignore. Note that ignored advisories will still
49 | # output a note when they are encountered.
50 | ignore = [
51 | #"RUSTSEC-0000-0000"
52 | ]
53 | # Threshold for security vulnerabilities, any vulnerability with a CVSS score
54 | # lower than the range specified will be ignored. Note that ignored advisories
55 | # will still output a note when they are encountered.
56 | # * None - CVSS Score 0.0
57 | # * Low - CVSS Score 0.1 - 3.9
58 | # * Medium - CVSS Score 4.0 - 6.9
59 | # * High - CVSS Score 7.0 - 8.9
60 | # * Critical - CVSS Score 9.0 - 10.0
61 | #severity-threshold =
62 |
63 | # This section is considered when running `cargo deny check licenses`
64 | # More documentation for the licenses section can be found here:
65 | # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
66 | [licenses]
67 | # The lint level for crates which do not have a detectable license
68 | unlicensed = "deny"
69 | # List of explicitly allowed licenses
70 | # See https://spdx.org/licenses/ for list of possible licenses
71 | # [possible values: any SPDX 3.7 short identifier (+ optional exception)].
72 | allow = [
73 | "Apache-2.0",
74 | "BSD-2-Clause",
75 | "BSD-3-Clause",
76 | "BSL-1.0",
77 | "CC0-1.0",
78 | "MIT"
79 | ]
80 | # List of explicitly disallowed licenses
81 | # See https://spdx.org/licenses/ for list of possible licenses
82 | # [possible values: any SPDX 3.7 short identifier (+ optional exception)].
83 | deny = [
84 | #"Nokia",
85 | ]
86 | # Lint level for licenses considered copyleft
87 | copyleft = "deny"
88 | # Blanket approval or denial for OSI-approved or FSF Free/Libre licenses
89 | # * both - The license will be approved if it is both OSI-approved *AND* FSF
90 | # * either - The license will be approved if it is either OSI-approved *OR* FSF
91 | # * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF
92 | # * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved
93 | # * neither - This predicate is ignored and the default lint level is used
94 | allow-osi-fsf-free = "neither"
95 | # Lint level used when no other predicates are matched
96 | # 1. License isn't in the allow or deny lists
97 | # 2. License isn't copyleft
98 | # 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither"
99 | default = "deny"
100 | # The confidence threshold for detecting a license from license text.
101 | # The higher the value, the more closely the license text must be to the
102 | # canonical license text of a valid SPDX license file.
103 | # [possible values: any between 0.0 and 1.0].
104 | confidence-threshold = 0.8
105 | # Allow 1 or more licenses on a per-crate basis, so that particular licenses
106 | # aren't accepted for every possible crate as with the normal allow list
107 | exceptions = [
108 | # The Unicode-DFS-2016 license is necessary for unicode-ident because they
109 | # use data from the unicode tables to generate the tables which are
110 | # included in the application. We do not distribute those data files so
111 | # this is not a problem for us. See https://github.com/dtolnay/unicode-ident/pull/9/files
112 | { allow = ["Unicode-DFS-2016"], name = "unicode-ident", version = "*"},
113 | ]
114 |
115 | # Some crates don't have (easily) machine readable licensing information,
116 | # adding a clarification entry for it allows you to manually specify the
117 | # licensing information
118 | #[[licenses.clarify]]
119 | # The name of the crate the clarification applies to
120 | #name = "ring"
121 | # The optional version constraint for the crate
122 | #version = "*"
123 | # The SPDX expression for the license requirements of the crate
124 | #expression = "MIT AND ISC AND OpenSSL"
125 | # One or more files in the crate's source used as the "source of truth" for
126 | # the license expression. If the contents match, the clarification will be used
127 | # when running the license check, otherwise the clarification will be ignored
128 | # and the crate will be checked normally, which may produce warnings or errors
129 | # depending on the rest of your configuration
130 | #license-files = [
131 | # Each entry is a crate relative path, and the (opaque) hash of its contents
132 | #{ path = "LICENSE", hash = 0xbd0eed23 }
133 | #]
134 |
135 | [licenses.private]
136 | # If true, ignores workspace crates that aren't published, or are only
137 | # published to private registries
138 | ignore = true
139 | # One or more private registries that you might publish crates to, if a crate
140 | # is only published to private registries, and ignore is true, the crate will
141 | # not have its license(s) checked
142 | registries = [
143 | #"https://sekretz.com/registry
144 | ]
145 |
146 | # This section is considered when running `cargo deny check bans`.
147 | # More documentation about the 'bans' section can be found here:
148 | # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
149 | [bans]
150 | # Lint level for when multiple versions of the same crate are detected
151 | multiple-versions = "warn"
152 | # Lint level for when a crate version requirement is `*`
153 | wildcards = "allow"
154 | # The graph highlighting used when creating dotgraphs for crates
155 | # with multiple versions
156 | # * lowest-version - The path to the lowest versioned duplicate is highlighted
157 | # * simplest-path - The path to the version with the fewest edges is highlighted
158 | # * all - Both lowest-version and simplest-path are used
159 | highlight = "all"
160 | # List of crates to deny
161 | deny = [
162 | # Each entry the name of a crate and a version range. If version is
163 | # not specified, all versions will be matched.
164 | #{ name = "ansi_term", version = "=0.11.0" },
165 | ]
166 | # Certain crates/versions that will be skipped when doing duplicate detection.
167 | skip = [
168 | #{ name = "ansi_term", version = "=0.11.0" },
169 | ]
170 | # Similarly to `skip` allows you to skip certain crates during duplicate
171 | # detection. Unlike skip, it also includes the entire tree of transitive
172 | # dependencies starting at the specified crate, up to a certain depth, which is
173 | # by default infinite
174 | skip-tree = [
175 | #{ name = "ansi_term", version = "=0.11.0", depth = 20 },
176 | ]
177 |
178 | # This section is considered when running `cargo deny check sources`.
179 | # More documentation about the 'sources' section can be found here:
180 | # https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
181 | [sources]
182 | # Lint level for what to happen when a crate from a crate registry that is not
183 | # in the allow list is encountered
184 | unknown-registry = "deny"
185 | # Lint level for what to happen when a crate from a git repository that is not
186 | # in the allow list is encountered
187 | unknown-git = "deny"
188 | # List of URLs for allowed crate registries. Defaults to the crates.io index
189 | # if not specified. If it is specified but empty, no registries are allowed.
190 | allow-registry = ["https://github.com/rust-lang/crates.io-index"]
191 | # List of URLs for allowed Git repositories
192 | allow-git = []
193 |
194 | #[sources.allow-org]
195 | # 1 or more github.com organizations to allow git sources for
196 | #github = [""]
197 | # 1 or more gitlab.com organizations to allow git sources for
198 | #gitlab = [""]
199 | # 1 or more bitbucket.org organizations to allow git sources for
200 | #bitbucket = [""]
201 |
--------------------------------------------------------------------------------
/deterministic-bloom-benches/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "deterministic-bloom-benches"
3 | version = "0.1.0"
4 | publish = false
5 | edition = "2021"
6 | authors = ["Brooklyn Zelenka "]
7 |
8 | [dependencies]
9 | deterministic-bloom = { path = "../deterministic-bloom", version = "0.1" }
10 | rand = "0.8"
11 |
12 | [dev-dependencies]
13 | criterion = { version = "0.4", default-features = false }
14 |
15 | [[bench]]
16 | name = "bloom_filter_benchmark"
17 | harness = false
18 |
--------------------------------------------------------------------------------
/deterministic-bloom-benches/benches/bloom_filter_benchmark.rs:
--------------------------------------------------------------------------------
1 | use criterion::{criterion_group, criterion_main, Criterion};
2 | use deterministic_bloom::const_size::BloomFilter;
3 | use rand::Rng;
4 |
5 | pub fn add_benchmark(crit: &mut Criterion) {
6 | let mut rng = rand::thread_rng();
7 | let mut bloom = BloomFilter::<256, 30>::new();
8 |
9 | crit.bench_function("add", |bench| {
10 | bench.iter(|| {
11 | let new_val: u8 = rng.gen_range(0..10);
12 | bloom.insert(&[new_val; 32]);
13 | })
14 | });
15 | }
16 |
17 | pub fn count_ones_benchmark(crit: &mut Criterion) {
18 | let mut rng = rand::thread_rng();
19 | let mut bloom = BloomFilter::<256, 30>::new();
20 |
21 | for _ in 1..50 {
22 | let new_val: u8 = rng.gen_range(0..10);
23 | bloom.insert(&[new_val; 32]);
24 | }
25 |
26 | crit.bench_function("count_ones", |bench| bench.iter(|| bloom.count_ones()));
27 | }
28 |
29 | criterion_group!(benches, add_benchmark, count_ones_benchmark);
30 | criterion_main!(benches);
31 |
--------------------------------------------------------------------------------
/deterministic-bloom-wasm/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "deterministic-bloom-wasm"
3 | version = "0.1.0"
4 | description = "Wasm bindings for the deterministic-bloom crate."
5 | keywords = []
6 | categories = []
7 | include = ["/src", "README.md", "LICENSE"]
8 | license = "Apache-2.0"
9 | readme = "README.md"
10 | edition = "2021"
11 | rust-version = "1.64"
12 | documentation = "https://docs.rs/deterministic-bloom-wasm"
13 | repository = "https://github.com/wnfs-wg/deterministic-bloom/tree/main/deterministic-bloom-wasm"
14 | authors = ["Brooklyn Zelenka "]
15 |
16 | [lib]
17 | crate-type = ["cdylib", "rlib"]
18 | path = "src/lib.rs"
19 |
20 | [dependencies]
21 | # The `console_error_panic_hook` crate provides better debugging of panics by
22 | # logging them with `console.error`. This is great for development, but requires
23 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
24 | # code size when deploying.
25 | console_error_panic_hook = { version = "0.1", optional = true }
26 | derive_more = "0.99"
27 | deterministic-bloom = { path = "../deterministic-bloom", version = "0.1" }
28 | js-sys = { version = "0.3", optional = true }
29 | once_cell = "1.16"
30 | thiserror = "1.0"
31 | tracing = "0.1"
32 | wasm-bindgen = { version = "0.2", optional = true, features = ["serde-serialize"] }
33 | wasm-bindgen-futures = { version = "0.4", optional = true }
34 | web-sys = { version = "0.3", optional = true }
35 |
36 | [dev-dependencies]
37 | wasm-bindgen-test = "0.3"
38 |
39 | [features]
40 | default = ["js"]
41 | full = ["js", "web"]
42 | js = [
43 | "console_error_panic_hook",
44 | "js-sys",
45 | "wasm-bindgen",
46 | "wasm-bindgen-futures"
47 | ]
48 | web = ["web-sys"]
49 |
50 | [profile.release]
51 | lto = true
52 |
--------------------------------------------------------------------------------
/deterministic-bloom-wasm/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/deterministic-bloom-wasm/README.md:
--------------------------------------------------------------------------------
1 |
32 |
33 | :warning: Work in progress :warning:
34 |
35 | ##
36 |
37 | Description.
38 |
39 | ## Outline
40 |
41 | - [Set-up](#set-up)
42 | - [Build for Javascript](#build-for-javascript)
43 | - [Testing the Project](#testing-the-project)
44 | - [Publishing a Package](#publishing-a-package)
45 | - [License](#license)
46 |
47 | ## Set-up
48 |
49 | We'll use [`wasm-pack`][wasm-pack] for building, testing, and publishing
50 | our Wasm project.
51 |
52 | ### Build for Javascript
53 |
54 | The `wasm-pack build` command will compile the code in this directory into
55 | Wasm and generate a `pkg` folder by default, containing the Wasm binary, a
56 | Javascript-wrapper file, the deterministic-bloom-wasm README (and version), and a
57 | `package.json` file.
58 |
59 | - Targetting node:
60 |
61 | ```console
62 | wasm-pack build --target nodejs
63 | ```
64 |
65 | - Targetting browswers:
66 |
67 | ```console
68 | wasm-pack build --target web
69 | ```
70 |
71 | - Targetting bundlers like [webpack][webpack]:
72 |
73 | ```console
74 | wasm-pack build --target bundler
75 | ```
76 |
77 | ## Testing the Project
78 |
79 | For running tests in the current directory, use one of these commands:
80 |
81 | - Run tests expected to execute in [Node.js][node-js]:
82 |
83 | ```console
84 | wasm-pack test --node
85 | ```
86 |
87 | - Run browser tests in a headless browwer:
88 |
89 | ```console
90 | wasm-pack test --headless --firefox --chrome --safari
91 | ```
92 |
93 | *Note*: Make sure you have the appropriate browser installed when running
94 | locally.
95 |
96 | ## Publishing a Package
97 |
98 | Once you've [built the package](#build-for-javascript), which lives under
99 | `pkg` by default (or a sub-directory of your choosing), you can pack and
100 | publish it to [npm][npm] via (given credentials):
101 |
102 | ```console
103 | wasm-pack publish
104 | ```
105 |
106 | ## License
107 |
108 | This project is licensed under the [Apache License 2.0](./LICENSE), or
109 | [http://www.apache.org/licenses/LICENSE-2.0][apache].
110 |
111 |
112 | [apache]: https://www.apache.org/licenses/LICENSE-2.0
113 | [mit]: http://opensource.org/licenses/MIT
114 | [node-js]: https://nodejs.dev/en/
115 | [npm]: https://www.npmjs.com/
116 | [wasm-pack]: https://rustwasm.github.io/docs/wasm-pack/
117 | [webpack]: https://webpack.js.org/
118 |
--------------------------------------------------------------------------------
/deterministic-bloom-wasm/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![cfg_attr(docsrs, feature(doc_cfg))]
2 | #![warn(missing_debug_implementations, missing_docs, rust_2018_idioms)]
3 | #![deny(unreachable_pub, private_in_public)]
4 |
5 | //! Wasm/JS bindings for [BloomFilter]
6 |
7 | use derive_more::{From, Into};
8 | use deterministic_bloom::const_size::BloomFilter;
9 | use std::boxed::Box;
10 | use wasm_bindgen::prelude::{wasm_bindgen, JsError};
11 |
12 | //------------------------------------------------------------------------------
13 | // Utilities
14 | //------------------------------------------------------------------------------
15 |
16 | /// Panic hook lets us get better error messages if our Rust code ever panics.
17 | ///
18 | /// For more details see
19 | ///
20 | #[wasm_bindgen(js_name = "setPanicHook")]
21 | pub fn set_panic_hook() {
22 | #[cfg(feature = "console_error_panic_hook")]
23 | console_error_panic_hook::set_once();
24 | }
25 |
26 | //------------------------------------------------------------------------------
27 | // Macros
28 | //------------------------------------------------------------------------------
29 |
30 | /// Generate a monomorphic [BloomFilter] newtypes.
31 | macro_rules! gen_bloom {
32 | ($name: ident, $n: expr, $k: expr) => {
33 | #[doc = concat!("A monomorphic wrapper for [BloomFilter]`s with a size of ", stringify!($n), " bytes and ", stringify!($k), " hash functions.")]
34 | #[wasm_bindgen]
35 | #[derive(Debug, Default, From, Into)]
36 | pub struct $name {
37 | pub(crate) boxed: Box>,
38 | }
39 |
40 | #[wasm_bindgen]
41 | impl $name {
42 | #[doc = concat!("Initialize a blank [", stringify!($name), "] (i.e. contains no elements).")]
43 | ///
44 | /// # Examples
45 | ///
46 | /// ```
47 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")]
48 | ///
49 | #[doc = concat!("let blank = ", stringify!($name), "::new();")]
50 | #[doc = concat!("assert_eq!(", stringify!($name), "::count_ones(&blank), 0);")]
51 | /// ```
52 | #[wasm_bindgen(constructor)]
53 | pub fn new() -> Self {
54 | Default::default()
55 | }
56 |
57 | #[doc = concat!("Attempt to initialize a [", stringify!($name), "] with a starting array.")]
58 | #[doc = concat!("Fails with a [JsError] if the [Vec] is not exactly ", stringify!($n), " bytes long.")]
59 | pub fn try_from_vec(vec: Vec) -> Result<$name, JsError> {
60 | $name::try_from(vec).map_err(|e| JsError::new(&e.to_string()))
61 | }
62 |
63 | /// The (constant) size of the underlying [BloomFilter] in bytes.
64 | ///
65 | /// # Examples
66 | ///
67 | /// ```
68 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")]
69 | ///
70 | #[doc = concat!("let size = ", stringify!($name), "::byte_count();")]
71 | #[doc = concat!("assert_eq!(size, ", stringify!($k), ");")]
72 | /// ```
73 | pub fn byte_count() -> usize {
74 | $k
75 | }
76 |
77 | /// The number of hashes used in the underlying [BloomFilter].
78 | ///
79 | /// # Examples
80 | ///
81 | /// ```
82 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")]
83 | ///
84 | #[doc = concat!("assert_eq!(", stringify!($name), "::hash_count(), ", stringify!($n), ");")]
85 | /// ```
86 | pub fn hash_count() -> usize {
87 | $n
88 | }
89 |
90 | /// Insert a new elememt into the underlying [BloomFilter].
91 | /// This [Vec] can be of any length.
92 | ///
93 | /// # Examples
94 | ///
95 | /// ```
96 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")]
97 | ///
98 | #[doc = concat!("let mut bloom = ", stringify!($name), "::new();")]
99 | /// let item = vec![1, 2, 3, 4, 5];
100 | /// bloom.insert_vec(item.clone());
101 | ///
102 | /// assert!(bloom.contains(item.clone()));
103 | /// ```
104 | pub fn insert_vec(&mut self, new_val: Vec) -> () {
105 | self.boxed.insert(&new_val);
106 | }
107 |
108 | /// Check if some [Vec] is in the underlying [BloomFilter].
109 | ///
110 | /// # Examples
111 | ///
112 | /// ```
113 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")]
114 | ///
115 | #[doc = concat!("let mut bloom = ", stringify!($name), "::new();")]
116 | /// let item = vec![1, 2, 3, 4, 5];
117 | /// bloom.insert_vec(item.clone());
118 | ///
119 | /// assert!(bloom.contains(item.clone()));
120 | /// ```
121 | pub fn contains(&self, item: Vec) -> bool {
122 | self.boxed.contains(&item)
123 | }
124 |
125 | /// Count how many bits are set to 1 (sometimes called a `popcount`).
126 | ///
127 | /// # Examples
128 | ///
129 | /// ```
130 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")]
131 | ///
132 | #[doc = concat!("let mut bloom = ", stringify!($name), "::new();")]
133 | /// let item1 = vec![1, 2, 3, 4, 5];
134 | /// bloom.insert_vec(item1.clone());
135 | /// bloom.insert_vec(item1.clone());
136 | /// bloom.insert_vec(item1.clone());
137 | ///
138 | /// let item2 = vec![6, 7];
139 | /// bloom.insert_vec(item2.clone());
140 | ///
141 | /// let item3 = vec![8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
142 | /// bloom.insert_vec(item2.clone());
143 | ///
144 | #[doc = concat!("assert!(bloom.count_ones() >= ", $k, ");")]
145 | #[doc = concat!("assert!(bloom.count_ones() <= 3 * ", $k, ");")]
146 | /// ```
147 | pub fn count_ones(&self) -> usize {
148 | self.boxed.count_ones()
149 | }
150 |
151 | /// Retreive the underlying byte array.
152 | ///
153 | /// # Examples
154 | ///
155 | /// ```
156 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")]
157 | ///
158 | #[doc = concat!("let mut bloom = ", stringify!($name), "::new();")]
159 | /// bloom.insert_vec(vec![1, 2, 3, 4, 5]);
160 | ///
161 | #[doc = concat!("assert_ne!(bloom.as_bytes(), vec![0; ", $n, "]);")]
162 | #[doc = concat!("assert_eq!(bloom.as_bytes().len(), ", $n, ");")]
163 | /// ```
164 | pub fn as_bytes(&self) -> Vec {
165 | self.boxed.as_bytes().to_vec()
166 | }
167 | }
168 |
169 | impl From> for $name {
170 | fn from(bloom: BloomFilter<$n, $k>) -> Self {
171 | $name {
172 | boxed: Box::new(bloom)
173 | }
174 | }
175 | }
176 |
177 | impl TryFrom> for $name {
178 | type Error = deterministic_bloom::common::Error;
179 |
180 | fn try_from(vec: Vec) -> Result {
181 | >::try_from(vec).map($name::from)
182 | }
183 | }
184 | };
185 | }
186 |
187 | gen_bloom!(SmallBloomFilter, 256, 13);
188 | gen_bloom!(MediumBloomFilter, 4096, 17);
189 | gen_bloom!(LargeBloomFilter, 1048576, 23);
190 |
--------------------------------------------------------------------------------
/deterministic-bloom-wasm/tests/web.rs:
--------------------------------------------------------------------------------
1 | //! Test suite for the Web and headless browsers.
2 |
3 | #![cfg(target_arch = "wasm32")]
4 |
5 | // use deterministic_bloom::{test_utils::Rvg, BloomFilter};
6 | use deterministic_bloom_wasm::SmallBloomFilter;
7 | use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
8 | wasm_bindgen_test_configure!(run_in_browser);
9 |
10 | #[wasm_bindgen_test]
11 | fn test_contains() {
12 | let mut bloom = SmallBloomFilter::new();
13 | // let mut rvg = Rvg::new();
14 |
15 | let new_val: Vec = vec![1, 2, 3]; // rvg.sample(&(..255u8))];
16 | bloom.insert_vec(new_val.clone());
17 |
18 | for _ in 1..25 {
19 | bloom.insert_vec(vec![4, 5, 6]); // rvg.sample(&(..255u8))]);
20 | }
21 |
22 | assert!(bloom.contains(new_val.clone()));
23 | }
24 |
--------------------------------------------------------------------------------
/deterministic-bloom/.dockerignore:
--------------------------------------------------------------------------------
1 | *
2 |
3 | !Cargo.lock
4 | !Cargo.toml
5 | !src
6 |
7 | src/bin
8 |
--------------------------------------------------------------------------------
/deterministic-bloom/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "deterministic-bloom"
3 | version = "0.1.0"
4 | description = "A deterministic Bloom filter with support for saturation. Suitable for distributed use cases and as a cryptographic primitive."
5 | keywords = ["bloom"]
6 | categories = []
7 | license = "Apache-2.0"
8 | include = ["/src", "README.md", "LICENSE"]
9 | readme = "README.md"
10 | edition = "2021"
11 | rust-version = "1.64"
12 | documentation = "https://docs.rs/deterministic-bloom"
13 | repository = "https://github.com/wnfs-wg/deterministic-bloom/tree/main/deterministic-bloom"
14 | authors = ["Philipp Krüger ", "Stephen Akinyemi ", "Brooklyn Zelenka "]
15 |
16 | [lib]
17 | path = "src/lib.rs"
18 |
19 | [dependencies]
20 | bitvec = { version = "1.0", features = ["serde"] }
21 | miette = "5.5"
22 | proptest = { version = "1.0", optional = true }
23 | rand_core = "0.6"
24 | serde = { version = "1.0", features = ["rc"] }
25 | thiserror = "1.0"
26 | tracing = "0.1"
27 | xxhash-rust = { version = "0.8", features = ["xxh3"] }
28 |
29 | [dev-dependencies]
30 | libipld = { version = "0.16", features = ["serde-codec"] }
31 | proptest = "1.0"
32 | rand = "0.8"
33 | test-strategy = "0.3"
34 |
35 | [features]
36 | default = []
37 | test_utils = ["proptest"]
38 |
--------------------------------------------------------------------------------
/deterministic-bloom/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/deterministic-bloom/README.md:
--------------------------------------------------------------------------------
1 |
29 |
30 | :warning: Work in progress :warning:
31 |
32 | ##
33 |
34 | Description.
35 |
36 | ## License
37 |
38 | This project is licensed under the [Apache License 2.0](./LICENSE), or
39 | [http://www.apache.org/licenses/LICENSE-2.0][apache].
40 |
41 |
42 | [apache]: https://www.apache.org/licenses/LICENSE-2.0
43 | [mit]: http://opensource.org/licenses/MIT
44 |
--------------------------------------------------------------------------------
/deterministic-bloom/proptest-regressions/lib.txt:
--------------------------------------------------------------------------------
1 | # Seeds for failure cases proptest has generated in the past. It is
2 | # automatically read and these particular cases re-run before any
3 | # novel cases are generated.
4 | #
5 | # It is recommended to check this file in to source control so that
6 | # everyone who runs the test benefits from these saved cases.
7 | cc 291d42539f7fb8127033c08ad7c09dba8c1b74a026ba9af55d7c1cbfb3673e80 # shrinks to input = _BloomParamsFprCalcRoundTripsArgs { bloom_bytes: 1, n_elems: 1, fpr: 0.4123907350873679 }
8 | cc a4e3f11f5e30029214f9bca6d33a195cbeeeac10f7c128da8f2124cc5ac1920b # shrinks to input = _BloomParamsFprCalcRoundTripsArgs { n_elems: 548, fpr: 0.8964377231312348 }
9 |
--------------------------------------------------------------------------------
/deterministic-bloom/src/common.rs:
--------------------------------------------------------------------------------
1 | use std::{f64::consts::LN_2, fmt::Debug};
2 | use xxhash_rust::xxh3;
3 |
4 | /// An iterator that generates indices into some bloom filter based on deterministic hashing of specified item.
5 | ///
6 | /// # Examples
7 | ///
8 | /// ```
9 | /// use deterministic_bloom::const_size::BloomFilter;
10 | ///
11 | /// let filter = BloomFilter::<256, 30>::default();
12 | /// let indices = filter.hash_indices(&[0xF5u8; 32]);
13 | /// let indices = indices.collect::>();
14 | ///
15 | /// assert_eq!(indices.len(), 30);
16 | /// ```
17 | #[derive(Debug, Clone, PartialEq, Eq)]
18 | pub struct HashIndexIterator<'a, T: AsRef<[u8]>> {
19 | item: &'a T,
20 | bit_size: usize,
21 | index: u64,
22 | }
23 |
24 | /// Optimal bloom parameters for some false positive rate at a maximum number of
25 | /// elements added, or for some byte size with target element count, etc.
26 | ///
27 | /// Captures the bloom filter byte size needed as well as the number of hash function
28 | /// evaluations needed per item to insert.
29 | ///
30 | /// To construct this, use
31 | /// - [`BloomParams::new_from_fpr`] for constructing this from a given false positive rate and desired capacity,
32 | /// - similarly [`BloomParams::new_from_fpr_po2`], but with power-of-two sizes,
33 | /// - [`BloomParams::new_from_size`] for constructing from desired size and capacity.
34 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
35 | pub struct BloomParams {
36 | /// size of the bloom filter in bytes, non-zero
37 | pub byte_size: usize,
38 | /// hashing functions used/number of bits set per element, non-zero
39 | pub k_hashes: usize,
40 | }
41 |
42 | /// Errors for [BloomFilter] operations.
43 | #[derive(thiserror::Error, miette::Diagnostic, Debug)]
44 | pub enum Error {
45 | /// Report a size mismatch when importing a Bloom filter from a [Vec].
46 | #[error("Cannot convert vector to BloomFilter: expected {expected}, but got {actual}")]
47 | #[diagnostic(url(docsrs))]
48 | VectorImportSizeMismatch {
49 | /// The expected size in the [BloomFilter].
50 | expected: usize,
51 |
52 | /// The actual size of the [Vec].
53 | actual: usize,
54 | },
55 | }
56 |
57 | //------------------------------------------------------------------------------
58 | // Implementations
59 | //------------------------------------------------------------------------------
60 |
61 | impl<'a, T: AsRef<[u8]>> HashIndexIterator<'a, T> {
62 | /// Creates a new iterator.
63 | pub fn new(item: &'a T, bit_size: usize) -> Self {
64 | Self {
65 | item,
66 | index: 0,
67 | bit_size,
68 | }
69 | }
70 | }
71 |
72 | impl> Iterator for HashIndexIterator<'_, T> {
73 | type Item = usize;
74 |
75 | fn next(&mut self) -> Option {
76 | if self.bit_size == 0 {
77 | // This avoids an infinite loop in rejection sampling.
78 | return None;
79 | }
80 |
81 | let bit_size_po2 = self.bit_size.next_power_of_two();
82 | loop {
83 | let hash = xxh3::xxh3_64_with_seed(self.item.as_ref(), self.index) as usize;
84 | self.index += 1;
85 |
86 | // Rejection sampling for non-power-of-two bit sizes
87 | let value = hash % bit_size_po2;
88 | if value < self.bit_size {
89 | return Some(value);
90 | }
91 | }
92 | }
93 | }
94 |
95 | impl BloomParams {
96 | /// Construct optimal bloom parameters for given number maximum elements
97 | /// that the bloom filter will hold as well as the approximate
98 | /// false positive rate it should have at that capacity.
99 | ///
100 | /// `n_elems` must be non-zero, and `fpr` must be between 0 and 1, exclusive.
101 | ///
102 | /// This will generate non-power-of-two sizes for bloom filters.
103 | /// For a variant that power-of-two (po2) sizes, see [`BloomParams::new_from_fpr_po2`].
104 | ///
105 | /// # Example
106 | ///
107 | /// ```
108 | /// use deterministic_bloom::common::BloomParams;
109 | ///
110 | /// // figure out bloom parameters for 47 elements with a one in a billion false positive rate:
111 | /// let params = BloomParams::new_from_fpr(47, 1.0 / 1_000_000_000.0);
112 | /// assert_eq!(params, BloomParams {
113 | /// byte_size: 254,
114 | /// k_hashes: 30,
115 | /// })
116 | /// ```
117 | pub fn new_from_fpr(n_elems: u64, fpr: f64) -> Self {
118 | let byte_size = Self::optimal_byte_size(n_elems, fpr);
119 | let k_hashes = Self::optimal_k_hashes(byte_size * 8, n_elems);
120 |
121 | Self {
122 | byte_size,
123 | k_hashes,
124 | }
125 | }
126 |
127 | /// Construct optimal bloom parameters for given capacity `n_elems` and false positive rate,
128 | /// where the target size will always be a power-of-two.
129 | ///
130 | /// `n_elems` must be non-zero, and `fpr` must be between 0.0 and 1.0, exclusive.
131 | ///
132 | /// It is often desirable to go for power-of-two sizes, since that simplifies generating
133 | /// bit indices by not requiring rejection sampling.
134 | ///
135 | /// # Example
136 | ///
137 | /// ```
138 | /// use deterministic_bloom::common::BloomParams;
139 | ///
140 | /// // Generate some bloom parameters
141 | /// let params = BloomParams::new_from_fpr_po2(1_000_000, 0.0001);
142 | /// assert_eq!(params.byte_size, params.byte_size.next_power_of_two());
143 | /// ```
144 | pub fn new_from_fpr_po2(n_elems: u64, fpr: f64) -> Self {
145 | let byte_size = Self::optimal_byte_size(n_elems, fpr).next_power_of_two();
146 | let k_hashes = Self::optimal_k_hashes(byte_size * 8, n_elems);
147 |
148 | Self {
149 | byte_size,
150 | k_hashes,
151 | }
152 | }
153 |
154 | /// Construct optimal bloom parameters for given bloom filter `byte_size` and capacity `n_elems`.
155 | pub fn new_from_size(byte_size: usize, n_elems: u64) -> Self {
156 | Self {
157 | byte_size,
158 | k_hashes: Self::optimal_k_hashes(byte_size * 8, n_elems),
159 | }
160 | }
161 |
162 | /// Compute the approximate false positive rate at `n_elems`.
163 | /// `n_elems` must be non-zero.
164 | ///
165 | /// Returns the false positive rate as a number between 0.0 and 1.0.
166 | pub fn false_positive_rate_at(&self, n_elems: u64) -> f64 {
167 | debug_assert!(n_elems != 0);
168 |
169 | let k = self.k_hashes as f64;
170 | let ki = self.k_hashes as i32;
171 | let m = (self.byte_size * 8) as f64;
172 | let n = n_elems as f64;
173 |
174 | // see https://hur.st/bloomfilter/
175 | (1.0 - (-k / (m / n)).exp()).powi(ki)
176 | }
177 |
178 | fn optimal_byte_size(n_elems: u64, fpr: f64) -> usize {
179 | debug_assert!(n_elems != 0);
180 | debug_assert!(fpr > 0.0 && fpr < 1.0);
181 |
182 | let n = n_elems as f64;
183 | let bit_size = n * fpr.ln() / -(LN_2 * LN_2);
184 | (bit_size / 8.0).ceil() as usize
185 | }
186 |
187 | fn optimal_k_hashes(bloom_bits: usize, n_elems: u64) -> usize {
188 | debug_assert!(bloom_bits != 0);
189 | debug_assert!(n_elems != 0);
190 |
191 | let m = bloom_bits as f64;
192 | let n = n_elems as f64;
193 | let k_hashes = ((m / n) * LN_2).ceil() as usize;
194 | std::cmp::max(k_hashes, 1)
195 | }
196 | }
197 |
198 | #[cfg(test)]
199 | mod tests {
200 | use super::HashIndexIterator;
201 |
202 | #[test]
203 | fn test_zero_bit_size() {
204 | let mut iterator = HashIndexIterator::new(&[1, 2, 3], 0);
205 | assert_eq!(iterator.next(), None);
206 | }
207 | }
208 |
209 | #[cfg(test)]
210 | mod proptests {
211 | use super::BloomParams;
212 | use proptest::prop_assert;
213 | use test_strategy::proptest;
214 |
215 | #[proptest(cases = 10_000)]
216 | fn bloom_params_fpr_calc_round_trips(
217 | #[strategy(100u64..1_000_000)] n_elems: u64,
218 | #[strategy(0.0..0.1)] fpr: f64,
219 | ) {
220 | if fpr == 0.0 {
221 | return Ok(());
222 | }
223 |
224 | let params = BloomParams::new_from_fpr(n_elems, fpr);
225 | let fpr_computed = params.false_positive_rate_at(n_elems);
226 |
227 | // The computed FPR can differ from the target FPR due to
228 | // rounding errors and the fact that only multiple-of-8
229 | // bloom sizes are allowed.
230 | let fpr_diff = (fpr_computed - fpr).abs();
231 | // We're fine if it's within 15% of a margin-of-error.
232 | prop_assert!(fpr_diff < fpr * 0.15);
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/deterministic-bloom/src/const_size.rs:
--------------------------------------------------------------------------------
1 | use crate::{
2 | common::{Error, HashIndexIterator},
3 | utils::{ByteArrayVisitor, HexFieldDebug},
4 | };
5 | use bitvec::prelude::BitArray;
6 | use serde::{Deserialize, Serialize};
7 | use std::{fmt::Debug, ops::Index};
8 |
9 | //------------------------------------------------------------------------------
10 | // Type Definitions
11 | //------------------------------------------------------------------------------
12 |
13 | /// The bloom filter is a probabilistic data structure that can be used to store a set of hashes.
14 | ///
15 | /// `N` is the size of the bloom filter in bytes.
16 | ///
17 | /// `K` is the number of bits to be set with each insert operation.
18 | ///
19 | /// # Examples
20 | ///
21 | /// ```
22 | /// use deterministic_bloom::const_size::BloomFilter;
23 | ///
24 | /// let mut filter = BloomFilter::<256, 30>::default();
25 | /// filter.insert(&[0xF5u8; 32]);
26 | ///
27 | /// assert!(filter.contains(&[0xF5u8; 32]));
28 | /// ```
29 | #[derive(Clone, PartialEq, Eq, PartialOrd)]
30 | pub struct BloomFilter {
31 | /// The underlying `BitArray`
32 | pub bits: BitArray<[u8; N]>,
33 | }
34 |
35 | //------------------------------------------------------------------------------
36 | // Implementations
37 | //------------------------------------------------------------------------------
38 |
39 | impl BloomFilter {
40 | /// Creates a new bloom filter with all bits unset.
41 | ///
42 | /// # Examples
43 | ///
44 | /// ```
45 | /// use deterministic_bloom::const_size::BloomFilter;
46 | ///
47 | /// let mut filter = BloomFilter::<256, 30>::new();
48 | /// filter.insert(&[0xF5u8; 32]);
49 | ///
50 | /// assert!(filter.contains(&[0xF5u8; 32]));
51 | /// ```
52 | pub fn new() -> Self {
53 | Self {
54 | bits: Default::default(),
55 | }
56 | }
57 |
58 | /// Inserts an item to the bloom filter.
59 | ///
60 | /// # Examples
61 | ///
62 | /// ```
63 | /// use deterministic_bloom::const_size::BloomFilter;
64 | ///
65 | /// let mut filter = BloomFilter::<256, 30>::default();
66 | /// filter.insert(&[0xF5u8; 32]);
67 | ///
68 | /// assert!(filter.contains(&[0xF5u8; 32]));
69 | /// ```
70 | pub fn insert(&mut self, item: &T)
71 | where
72 | T: AsRef<[u8]>,
73 | {
74 | for i in self.hash_indices(item) {
75 | self.bits.set(i, true);
76 | }
77 | }
78 |
79 | /// Returns the number of hash iterations the bloom filter uses to set bits.
80 | ///
81 | /// # Examples
82 | ///
83 | /// ```
84 | /// use deterministic_bloom::const_size::BloomFilter;
85 | ///
86 | /// let mut filter = BloomFilter::<256, 30>::default();
87 | ///
88 | /// assert_eq!(filter.hash_count(), 30);
89 | /// ```
90 | pub const fn hash_count(&self) -> usize {
91 | K
92 | }
93 |
94 | /// Checks if the item is in the bloom filter.
95 | ///
96 | /// # Examples
97 | ///
98 | /// ```
99 | /// use deterministic_bloom::const_size::BloomFilter;
100 | ///
101 | /// let mut filter = BloomFilter::<256, 30>::default();
102 | /// filter.insert(&[0xF5u8; 32]);
103 | ///
104 | /// assert!(filter.contains(&[0xF5u8; 32]));
105 | /// ```
106 | pub fn contains(&self, item: &T) -> bool
107 | where
108 | T: AsRef<[u8]>,
109 | {
110 | self.hash_indices(item).all(|i| self.bits[i])
111 | }
112 |
113 | /// Counts the number of bits set in the bloom filter.
114 | ///
115 | /// # Examples
116 | ///
117 | /// ```
118 | /// use deterministic_bloom::const_size::BloomFilter;
119 | ///
120 | /// let mut filter = BloomFilter::<256, 30>::default();
121 | /// filter.insert(&[0xF5u8; 32]);
122 | ///
123 | /// assert_eq!(filter.count_ones(), 30);
124 | /// ```
125 | pub fn count_ones(&self) -> usize {
126 | self.bits.count_ones()
127 | }
128 |
129 | /// Returns the indices of the bits that would be set if the item was inserted to the bloom filter.
130 | ///
131 | /// # Examples
132 | ///
133 | /// ```
134 | /// use deterministic_bloom::const_size::BloomFilter;
135 | ///
136 | /// let filter = BloomFilter::<256, 30>::default();
137 | /// let indices = filter.hash_indices(&[0xF5u8; 32]);
138 | /// let indices = indices.collect::>();
139 | ///
140 | /// assert_eq!(indices.len(), 30);
141 | /// ```
142 | #[inline]
143 | pub fn hash_indices<'a, T>(&self, item: &'a T) -> impl Iterator- + 'a
144 | where
145 | T: AsRef<[u8]>,
146 | {
147 | HashIndexIterator::new(item, N * 8).take(self.hash_count())
148 | }
149 |
150 | /// Get the bytes of the bloom filter.
151 | ///
152 | /// # Examples
153 | ///
154 | /// ```
155 | /// use deterministic_bloom::const_size::BloomFilter;
156 | ///
157 | /// let mut filter = BloomFilter::<256, 30>::default();
158 | /// filter.insert(&[0xF5u8; 32]);
159 | ///
160 | /// let bytes = filter.as_bytes();
161 | /// assert_eq!(bytes.len(), 256);
162 | /// ```
163 | #[inline]
164 | pub fn as_bytes(&self) -> &[u8] {
165 | self.bits.as_raw_slice()
166 | }
167 | }
168 |
169 | impl TryFrom> for BloomFilter {
170 | type Error = Error;
171 |
172 | fn try_from(bytes: Vec) -> Result {
173 | let bits = BitArray::<[u8; N]>::new(bytes.try_into().map_err(|vec: Vec| {
174 | Error::VectorImportSizeMismatch {
175 | expected: N,
176 | actual: vec.len(),
177 | }
178 | })?);
179 |
180 | Ok(Self { bits })
181 | }
182 | }
183 |
184 | impl Index for BloomFilter {
185 | type Output = bool;
186 |
187 | fn index(&self, index: usize) -> &Self::Output {
188 | &self.bits[index]
189 | }
190 | }
191 |
192 | impl Default for BloomFilter {
193 | #[inline]
194 | fn default() -> Self {
195 | Self::new()
196 | }
197 | }
198 |
199 | impl Serialize for BloomFilter {
200 | fn serialize
(&self, serializer: S) -> Result
201 | where
202 | S: serde::Serializer,
203 | {
204 | serializer.serialize_bytes(self.bits.as_raw_slice())
205 | }
206 | }
207 |
208 | impl<'de, const N: usize, const K: usize> Deserialize<'de> for BloomFilter {
209 | fn deserialize(deserializer: D) -> Result
210 | where
211 | D: serde::Deserializer<'de>,
212 | {
213 | Ok(BloomFilter:: {
214 | bits: BitArray::<[u8; N]>::new(deserializer.deserialize_bytes(ByteArrayVisitor::)?),
215 | })
216 | }
217 | }
218 |
219 | impl AsRef<[u8]> for &BloomFilter {
220 | fn as_ref(&self) -> &[u8] {
221 | self.as_bytes()
222 | }
223 | }
224 |
225 | impl Debug for BloomFilter {
226 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 | f.debug_tuple("BloomFilter")
228 | .field(&HexFieldDebug(self))
229 | .finish()
230 | }
231 | }
232 |
233 | //------------------------------------------------------------------------------
234 | // Tests
235 | //------------------------------------------------------------------------------
236 |
237 | #[cfg(test)]
238 | mod tests {
239 | use super::*;
240 |
241 | #[test]
242 | fn bloom_filter_can_insert_and_validate_item_existence() {
243 | let mut bloom = BloomFilter::<256, 30>::new();
244 | let items: Vec = vec!["first".into(), "second".into(), "third".into()];
245 | items.iter().for_each(|item| {
246 | bloom.insert(item);
247 | });
248 |
249 | items.iter().for_each(|item| {
250 | assert!(bloom.contains(item));
251 | });
252 |
253 | assert!(!bloom.contains(b"irst"));
254 | assert!(!bloom.contains(b"secnd"));
255 | assert!(!bloom.contains(b"tird"));
256 | }
257 |
258 | #[test]
259 | fn serialized_bloom_filter_can_be_deserialized_correctly() {
260 | let mut bloom = BloomFilter::<256, 30>::new();
261 | let items: Vec = vec!["first".into(), "second".into(), "third".into()];
262 | items.iter().for_each(|item| {
263 | bloom.insert(item);
264 | });
265 |
266 | let ipld = libipld::serde::to_ipld(&bloom).unwrap();
267 | let deserialized: BloomFilter<256, 30> = libipld::serde::from_ipld(ipld).unwrap();
268 |
269 | assert_eq!(deserialized, bloom);
270 | }
271 | }
272 |
273 | #[cfg(test)]
274 | mod proptests {
275 | use super::BloomFilter;
276 | use crate::common::HashIndexIterator;
277 | use proptest::collection::vec;
278 | use test_strategy::proptest;
279 |
280 | #[proptest]
281 | fn iterator_can_give_unbounded_number_of_indices(#[strategy(0usize..500)] count: usize) {
282 | let iter = HashIndexIterator::new(&"hello", 200);
283 |
284 | let indices = (0..20)
285 | .map(|_| (iter.clone().take(count).collect::>(), count))
286 | .collect::>();
287 |
288 | for (indices, count) in indices {
289 | assert_eq!(indices.len(), count);
290 | }
291 | }
292 |
293 | #[proptest(cases = 1000)]
294 | fn test_contains(#[strategy(vec(vec(0..255u8, 0..100), 26))] values: Vec>) {
295 | let mut bloom = BloomFilter::<256, 30>::new();
296 |
297 | for v in values.iter() {
298 | bloom.insert(v);
299 | }
300 |
301 | for v in values.iter() {
302 | assert!(bloom.contains(v));
303 | }
304 | }
305 | }
306 |
--------------------------------------------------------------------------------
/deterministic-bloom/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![cfg_attr(docsrs, feature(doc_cfg))]
2 | #![warn(missing_debug_implementations, missing_docs, rust_2018_idioms)]
3 | #![deny(unreachable_pub, private_in_public)]
4 |
5 | //! Deterministic Bloom filters
6 | //!
7 | //! This Crate is intented as a solid basis for cache reproducability
8 | //! and for underlying certain cryptographic primitives.
9 |
10 | /// Some structs and implementations that multiple bloom implementations can depend on
11 | pub mod common;
12 | /// Bloom filters with compile-time-determinted parameters (size & hash count)
13 | pub mod const_size;
14 | /// Bloom filters with runtime-determined parameters. Their size can be chosen
15 | /// arbitrarily at runtime, but not be modified during use (they're not resizable).
16 | pub mod runtime_size;
17 |
18 | mod utils;
19 |
--------------------------------------------------------------------------------
/deterministic-bloom/src/runtime_size.rs:
--------------------------------------------------------------------------------
1 | use crate::{
2 | common::{BloomParams, HashIndexIterator},
3 | utils::HexFieldDebug,
4 | };
5 | use bitvec::{prelude::Lsb0, view::BitView};
6 | use std::fmt::Debug;
7 |
8 | //------------------------------------------------------------------------------
9 | // Type Definitions
10 | //------------------------------------------------------------------------------
11 |
12 | /// An implementation of a basic [bloom filter].
13 | ///
14 | /// Its size can be chosen (or made optimal for given parameters) at creation time,
15 | /// but its size will have to stay the same during usage. I.e. you need to know
16 | /// what your target capacity and false positive rates should be in advance.
17 | ///
18 | /// Unlike the [`const_size::BloomFilter`](crate::const_size::BloomFilter) however,
19 | /// this implementation doesn't require you to know the parameters at compile time.
20 | ///
21 | /// # Example
22 | ///
23 | /// ```
24 | /// use deterministic_bloom::runtime_size::BloomFilter;
25 | ///
26 | /// let mut filter = BloomFilter::new_from_fpr(1_000, 1.0 / 1_000_000.0);
27 | /// filter.insert(b"Hello, World!");
28 | ///
29 | /// assert!(filter.contains(b"Hello, World!"));
30 | /// assert!(!filter.contains(b"Hello?")); // true in all but 1 in a million cases
31 | /// ```
32 | ///
33 | /// [bloom filter]: https://en.wikipedia.org/wiki/Bloom_filter
34 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
35 | pub struct BloomFilter {
36 | k_hashes: usize,
37 | bytes: Box<[u8]>,
38 | }
39 |
40 | impl BloomFilter {
41 | /// Construct a bloom filter with optimal parameters for given maximum capacity `n_elems`
42 | /// and false positive rate `fpr`.
43 | ///
44 | /// `n_elems` must be non-zero and `fpr` must be a number between 0 and 1 exclusive.
45 | ///
46 | /// # Example
47 | ///
48 | /// ```
49 | /// use deterministic_bloom::runtime_size::BloomFilter;
50 | ///
51 | /// let mut filter = BloomFilter::new_from_fpr(1_000_000, 1.0 / 1_000_000.0);
52 | ///
53 | /// assert_eq!(filter.as_bytes().len(), 3594397);
54 | /// assert_eq!(filter.hash_count(), 20);
55 | /// ```
56 | pub fn new_from_fpr(n_elems: u64, fpr: f64) -> Self {
57 | let params = BloomParams::new_from_fpr(n_elems, fpr);
58 | let bits = Box::from(vec![0u8; params.byte_size].as_ref());
59 | Self {
60 | k_hashes: params.k_hashes,
61 | bytes: bits,
62 | }
63 | }
64 |
65 | /// Construct an optimal power-of-two (po2) sized bloom filter for given maximum capacity
66 | /// `n_elems` and false positive rate `fpr`.
67 | ///
68 | /// `n_elems` must be non-zero and `fpr` must be a number between 0 and 1 exclusive.
69 | ///
70 | /// Using a power-of-two size can be beneficial due to not requiring rejection sampling
71 | /// when generating the hash indicies for items inserted into the filter.
72 | ///
73 | /// # Example
74 | ///
75 | /// ```
76 | /// use deterministic_bloom::runtime_size::BloomFilter;
77 | ///
78 | /// let mut filter = BloomFilter::new_from_fpr_po2(10_000_000, 0.01);
79 | ///
80 | /// assert_eq!(filter.as_bytes().len(), 16_777_216);
81 | /// assert_eq!(filter.as_bytes().len().count_ones(), 1); // size is a power of two
82 | /// assert_eq!(filter.hash_count(), 10);
83 | /// ```
84 | pub fn new_from_fpr_po2(n_elems: u64, fpr: f64) -> Self {
85 | let params = BloomParams::new_from_fpr_po2(n_elems, fpr);
86 | let bits = Box::from(vec![0u8; params.byte_size].as_ref());
87 | Self {
88 | k_hashes: params.k_hashes,
89 | bytes: bits,
90 | }
91 | }
92 |
93 | /// Construct a bloom filter with given target size and target capacity, both must
94 | /// be non-zero.
95 | ///
96 | /// This will compute the optimal number of hash evaluations per item inserted, but the
97 | /// false positive rate completely depends on the given filter size to maximum capacity
98 | /// ratio.
99 | ///
100 | /// # Example
101 | ///
102 | /// ```
103 | /// use deterministic_bloom::runtime_size::BloomFilter;
104 | ///
105 | /// let mut filter = BloomFilter::new_from_size(1000, 1000);
106 | ///
107 | /// // False positive rate isn't controlled though:
108 | /// assert!((filter.false_positive_rate_at(1000) - 0.0215).abs() < 1e-4);
109 | /// ```
110 | pub fn new_from_size(bloom_bytes: usize, n_elems: u64) -> Self {
111 | let params = BloomParams::new_from_size(bloom_bytes, n_elems);
112 | let bits = Box::from(vec![0u8; params.byte_size].as_ref());
113 | Self {
114 | k_hashes: params.k_hashes,
115 | bytes: bits,
116 | }
117 | }
118 |
119 | /// Construct the bloom filter from existing components.
120 | ///
121 | /// This is useful when e.g. deserializing a bloom filter.
122 | ///
123 | /// # Example
124 | ///
125 | /// ```
126 | /// use deterministic_bloom::runtime_size::BloomFilter;
127 | ///
128 | /// let mut filter = BloomFilter::new_from_fpr_po2(100_000, 0.01);
129 | ///
130 | /// filter.insert(b"Hello, World!");
131 | ///
132 | /// // Serialize
133 | /// let k_hashes = filter.hash_count();
134 | /// let bytes = Box::from(filter.as_bytes());
135 | ///
136 | /// // Deserialize
137 | /// let filter2 = BloomFilter::new_with(k_hashes, bytes);
138 | /// assert_eq!(filter, filter2);
139 | /// ```
140 | pub fn new_with(k_hashes: usize, bytes: Box<[u8]>) -> Self {
141 | Self { k_hashes, bytes }
142 | }
143 |
144 | /// Compute the bloom parameters for this bloom filter.
145 | /// This contains information about its size and hash function evaluations per
146 | /// item (`k_hashes`).
147 | pub fn get_bloom_params(&self) -> BloomParams {
148 | BloomParams {
149 | k_hashes: self.k_hashes,
150 | byte_size: self.bytes.len(),
151 | }
152 | }
153 |
154 | /// Get the approximate false positive rate at given capacity for this bloom filter.
155 | /// Returns a number between 0 and 1.
156 | pub fn false_positive_rate_at(&self, n_elems: u64) -> f64 {
157 | self.get_bloom_params().false_positive_rate_at(n_elems)
158 | }
159 |
160 | /// Get the approximate false positive rate at the current capacity of this bloom filter.
161 | /// Returns a number between 0 and 1.
162 | pub fn current_false_positive_rate(&self) -> f64 {
163 | let m = (self.bytes.len() * 8) as f64;
164 | let m_set = self.count_ones() as f64;
165 | let load = m_set / m;
166 | load.powi(self.hash_count() as i32)
167 | }
168 |
169 | /// Counts the amount of bits set in the bloom filter.
170 | pub fn count_ones(&self) -> usize {
171 | self.bytes.view_bits::().count_ones()
172 | }
173 |
174 | /// Insert an element into the bloom filter.
175 | ///
176 | /// The element will be hashed, thus it needs to be representable as bytes.
177 | ///
178 | /// Note: If you're using the bloom filter in a non-trusted
179 | /// environment, so e.g. the items can be chosen by an adversary, please
180 | /// make sure to pre-hash your items with a cryptographic hashing function
181 | /// like SHA-256 or BLAKE3.
182 | /// Otherwise an adversary will be able to generate elements that cause
183 | /// the bloom filter to e.g. be unusually full with an unusually high false
184 | /// positive rate or cheaply generate elements that are false positives.
185 | ///
186 | /// # Example
187 | ///
188 | /// ```
189 | /// use deterministic_bloom::runtime_size::BloomFilter;
190 | ///
191 | /// let mut filter = BloomFilter::new_from_fpr(1000, 0.0001);
192 | ///
193 | /// for i in 0u32..1000 {
194 | /// filter.insert(&i.to_le_bytes());
195 | /// }
196 | ///
197 | /// // Slightly more than half filled with zeros
198 | /// assert_eq!(filter.as_bytes().len() / 2 * 8, filter.count_ones() - 322);
199 | ///
200 | /// assert!(filter.contains(&10u32.to_le_bytes()));
201 | /// assert!(!filter.contains(&1001u32.to_le_bytes())); // Except in 0.01%
202 | /// ```
203 | pub fn insert(&mut self, item: &impl AsRef<[u8]>) {
204 | for i in self.hash_indices(item) {
205 | self.bytes.view_bits_mut::().set(i, true);
206 | }
207 | }
208 |
209 | /// Check whether an element was added into the bloom filter.
210 | ///
211 | /// This will always return true if the element was indeed added before,
212 | /// but it *may* sometimes return true, even if it wasn't.
213 | /// This is called a false positive and the false positive rate
214 | /// at certain capacities can be checked with [`false_positive_rate_at`](BloomFilter::false_positive_rate_at)
215 | /// and a desired false positive rate can be configured on creation with
216 | /// [`new_from_fpr`](BloomFilter::new_from_fpr) or [`new_from_fpr_po2`](BloomFilter::new_from_fpr_po2).
217 | ///
218 | /// # Example
219 | ///
220 | /// ```
221 | /// use deterministic_bloom::runtime_size::BloomFilter;
222 | ///
223 | /// let mut filter = BloomFilter::new_from_fpr(100, 0.1); // very high false-positive rate
224 | ///
225 | /// for i in 0u32..100 {
226 | /// filter.insert(&i.to_le_bytes());
227 | /// }
228 | ///
229 | /// // Inserted items will always return true
230 | /// assert!(filter.contains(&50u32.to_le_bytes()));
231 | /// // Non-inserted items mostly return false, but sometimes true
232 | /// assert!(!filter.contains(&101u32.to_le_bytes()));
233 | /// // But sometimes there exist false positives (in this case 10% of the time)
234 | /// assert!(filter.contains(&106u32.to_le_bytes()));
235 | /// ```
236 | pub fn contains(&self, item: &impl AsRef<[u8]>) -> bool {
237 | for i in self.hash_indices(item) {
238 | if !self.bytes.view_bits::()[i] {
239 | return false;
240 | }
241 | }
242 | true
243 | }
244 |
245 | /// Returns how many hash function invocations are used pre item inserted
246 | pub fn hash_count(&self) -> usize {
247 | self.k_hashes
248 | }
249 |
250 | /// Return the underlying array used to store the bloom bits (always on the heap)
251 | pub fn as_bytes(&self) -> &[u8] {
252 | &self.bytes
253 | }
254 |
255 | /// Return the indices that a given element would set in the filter
256 | pub fn hash_indices<'a>(&self, item: &'a impl AsRef<[u8]>) -> impl Iterator- + 'a {
257 | HashIndexIterator::new(item, self.bytes.len() * 8).take(self.hash_count())
258 | }
259 | }
260 |
261 | impl Debug for BloomFilter {
262 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263 | f.debug_struct("BloomFilter")
264 | .field("k_hashes", &self.k_hashes)
265 | .field("bytes", &HexFieldDebug(&self.bytes))
266 | .finish()
267 | }
268 | }
269 |
270 | #[cfg(test)]
271 | mod tests {
272 | use super::BloomFilter;
273 |
274 | #[test]
275 | fn serialization_round_trip() {
276 | let mut filter = BloomFilter::new_from_fpr(100, 0.001);
277 | filter.insert(b"Hello");
278 | filter.insert(b"World!");
279 | let serialized_bytes = filter.as_bytes();
280 | let serialized_k = filter.hash_count();
281 | let deserialized = BloomFilter::new_with(serialized_k, Box::from(serialized_bytes));
282 | assert!(deserialized.contains(b"Hello"));
283 | assert!(!deserialized.contains(b"abc"));
284 | assert_eq!(deserialized, filter);
285 | }
286 |
287 | #[test]
288 | fn empty_bloom_filter() {
289 | let filter = BloomFilter::new_with(3, Box::new([]));
290 | // Technically an empty bloom "contains" anything, since everything is a false positive.
291 | assert!(filter.contains(&[1, 2, 3]));
292 | }
293 | }
294 |
295 | #[cfg(test)]
296 | mod proptests {
297 | use super::BloomFilter;
298 | use proptest::prop_assert;
299 | use test_strategy::proptest;
300 |
301 | #[proptest]
302 | fn inserted_always_contained(items: Vec, #[strategy(100usize..10_000)] size: usize) {
303 | let capacity = std::cmp::max(items.len() as u64, 1);
304 | let mut filter = BloomFilter::new_from_size(size, capacity);
305 |
306 | for item in items.iter() {
307 | filter.insert(&item.to_le_bytes());
308 | }
309 |
310 | for item in items.iter() {
311 | prop_assert!(filter.contains(&item.to_le_bytes()));
312 | }
313 | }
314 |
315 | #[proptest]
316 | fn false_positive_rate_as_predicted(
317 | #[strategy(100u64..1_000)] n_elems: u64,
318 | #[strategy(100.0..10_000.0)] inv_fpr: f64,
319 | ) {
320 | let fpr = 1.0 / inv_fpr;
321 | let mut filter = BloomFilter::new_from_fpr(n_elems, fpr);
322 |
323 | for i in 0..n_elems {
324 | filter.insert(&i.to_le_bytes());
325 | }
326 |
327 | let measurements = 100_000;
328 | let mut false_positives = 0;
329 |
330 | for i in n_elems..n_elems + measurements {
331 | if filter.contains(&i.to_le_bytes()) {
332 | false_positives += 1;
333 | }
334 | }
335 |
336 | let computed_fpr = false_positives as f64 / measurements as f64;
337 | // The actual FPR should be pretty close
338 | prop_assert!((computed_fpr - fpr).abs() < 1.5e-3);
339 | }
340 | }
341 |
--------------------------------------------------------------------------------
/deterministic-bloom/src/utils.rs:
--------------------------------------------------------------------------------
1 | //! Internally-used Utilities
2 |
3 | use serde::de::Visitor;
4 | use std::fmt::{self, Debug};
5 |
6 | //--------------------------------------------------------------------------------------------------
7 | // Type Definitions
8 | //--------------------------------------------------------------------------------------------------
9 |
10 | pub(crate) struct ByteArrayVisitor;
11 |
12 | /// Helper newtype for rendering given debug field as hex string
13 | pub(crate) struct HexFieldDebug>(pub(crate) A);
14 |
15 | //--------------------------------------------------------------------------------------------------
16 | // Implementations
17 | //--------------------------------------------------------------------------------------------------
18 |
19 | impl<'de, const N: usize> Visitor<'de> for ByteArrayVisitor {
20 | type Value = [u8; N];
21 |
22 | fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
23 | write!(formatter, "a byte array of length {N}")
24 | }
25 |
26 | fn visit_bytes(self, v: &[u8]) -> Result
27 | where
28 | E: serde::de::Error,
29 | {
30 | let bytes: [u8; N] = v.try_into().map_err(E::custom)?;
31 | Ok(bytes)
32 | }
33 | }
34 |
35 | impl> Debug for HexFieldDebug {
36 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 | write!(f, "0x")?;
38 | for byte in self.0.as_ref().iter() {
39 | write!(f, "{byte:02X}")?;
40 | }
41 |
42 | Ok(())
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/examples/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "examples"
3 | version = "0.1.0"
4 | publish = false
5 | edition = "2021"
6 | authors = ["Brooklyn Zelenka "]
7 |
8 | [dev-dependencies]
9 | deterministic-bloom = { path = "../deterministic-bloom", version = "0.1" }
10 |
11 | [[example]]
12 | name = "counterparts"
13 | path = "counterparts.rs"
14 |
--------------------------------------------------------------------------------
/examples/counterparts.rs:
--------------------------------------------------------------------------------
1 | use std::error::Error;
2 |
3 | pub fn main() -> Result<(), Box> {
4 | println!("Alien Shore!");
5 | Ok(())
6 | }
7 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-utils": {
4 | "locked": {
5 | "lastModified": 1667395993,
6 | "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
7 | "owner": "numtide",
8 | "repo": "flake-utils",
9 | "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
10 | "type": "github"
11 | },
12 | "original": {
13 | "owner": "numtide",
14 | "repo": "flake-utils",
15 | "type": "github"
16 | }
17 | },
18 | "nixpkgs": {
19 | "locked": {
20 | "lastModified": 1671417167,
21 | "narHash": "sha256-JkHam6WQOwZN1t2C2sbp1TqMv3TVRjzrdoejqfefwrM=",
22 | "owner": "nixos",
23 | "repo": "nixpkgs",
24 | "rev": "bb31220cca6d044baa6dc2715b07497a2a7c4bc7",
25 | "type": "github"
26 | },
27 | "original": {
28 | "owner": "nixos",
29 | "ref": "nixpkgs-unstable",
30 | "repo": "nixpkgs",
31 | "type": "github"
32 | }
33 | },
34 | "root": {
35 | "inputs": {
36 | "flake-utils": "flake-utils",
37 | "nixpkgs": "nixpkgs",
38 | "rust-overlay": "rust-overlay"
39 | }
40 | },
41 | "rust-overlay": {
42 | "inputs": {
43 | "flake-utils": [
44 | "flake-utils"
45 | ],
46 | "nixpkgs": [
47 | "nixpkgs"
48 | ]
49 | },
50 | "locked": {
51 | "lastModified": 1671416426,
52 | "narHash": "sha256-kpSH1Jrxfk2qd0pRPJn1eQdIOseGv5JuE+YaOrqU9s4=",
53 | "owner": "oxalica",
54 | "repo": "rust-overlay",
55 | "rev": "fbaaff24f375ac25ec64268b0a0d63f91e474b7d",
56 | "type": "github"
57 | },
58 | "original": {
59 | "owner": "oxalica",
60 | "repo": "rust-overlay",
61 | "type": "github"
62 | }
63 | }
64 | },
65 | "root": "root",
66 | "version": 7
67 | }
68 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "deterministic-bloom";
3 |
4 | inputs = {
5 | nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
6 | flake-utils.url = "github:numtide/flake-utils";
7 |
8 | rust-overlay = {
9 | url = "github:oxalica/rust-overlay";
10 | inputs.nixpkgs.follows = "nixpkgs";
11 | inputs.flake-utils.follows = "flake-utils";
12 | };
13 | };
14 |
15 | outputs =
16 | { self
17 | , nixpkgs
18 | , flake-utils
19 | , rust-overlay
20 | } @ inputs:
21 | flake-utils.lib.eachDefaultSystem (system:
22 | let
23 | overlays = [ (import rust-overlay) ];
24 | pkgs = import nixpkgs { inherit system overlays; };
25 |
26 | rust-toolchain =
27 | (pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml).override {
28 | extensions = [ "cargo" "clippy" "rustfmt" "rust-src" "rust-std" ];
29 | targets = [ "wasm32-unknown-unknown" ];
30 | };
31 |
32 | nightly-rustfmt = pkgs.rust-bin.nightly.latest.rustfmt;
33 |
34 | format-pkgs = with pkgs; [
35 | nixpkgs-fmt
36 | ];
37 |
38 | cargo-installs = with pkgs; [
39 | binaryen
40 | cargo-deny
41 | cargo-expand
42 | cargo-sort
43 | cargo-udeps
44 | cargo-watch
45 | wasm-pack
46 | wasm-bindgen-cli
47 | ];
48 |
49 | bin = {
50 | bash = "${pkgs.bash}/bin/bash";
51 | cargo = "${pkgs.cargo}/bin/cargo";
52 | wasm-pack = "${pkgs.wasm-pack}/bin/wasm-pack";
53 | };
54 |
55 | test-all =
56 | pkgs.writeScriptBin "test:all" ''
57 | #!${bin.bash}
58 | echo "⚙️ Running cargo test"
59 | ${bin.cargo} test
60 |
61 | printf %"$COLUMNS"s | tr " " "-"
62 | echo "⚙️ Running headless Wasm tests"
63 | ${bin.wasm-pack} test --headless --chrome deterministic-bloom-wasm
64 | '';
65 | in
66 | rec
67 | {
68 | devShells.default = pkgs.mkShell {
69 | name = "deterministic-bloom";
70 | nativeBuildInputs = with pkgs; [
71 | # The ordering of these two items is important. For nightly rustfmt to be used instead of
72 | # the rustfmt provided by `rust-toolchain`, it must appear first in the list. This is
73 | # because native build inputs are added to $PATH in the order they're listed here.
74 | nightly-rustfmt
75 | rust-toolchain
76 | pre-commit
77 | direnv
78 | chromedriver
79 | geckodriver
80 | test-all
81 | self.packages.${system}.irust
82 | ] ++ format-pkgs ++ cargo-installs;
83 |
84 | shellHook = ''
85 | [ -e .git/hooks/pre-commit ] || pre-commit install --install-hooks && pre-commit install --hook-type commit-msg
86 | '';
87 | };
88 |
89 | packages.irust = pkgs.rustPlatform.buildRustPackage rec {
90 | pname = "irust";
91 | version = "1.65.1";
92 | src = pkgs.fetchFromGitHub {
93 | owner = "sigmaSd";
94 | repo = "IRust";
95 | rev = "v${version}";
96 | sha256 = "sha256-AMOND5q1XzNhN5smVJp+2sGl/OqbxkGPGuPBCE48Hik=";
97 | };
98 |
99 | doCheck = false;
100 | cargoSha256 = "sha256-A24O3p85mCRVZfDyyjQcQosj/4COGNnqiQK2a7nCP6I=";
101 | };
102 | }
103 | );
104 | }
105 |
--------------------------------------------------------------------------------
/release-please-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["cargo-workspace"],
3 | "changelog-path": "CHANGELOG.md",
4 | "release-type": "rust",
5 | "bump-minor-pre-major": true,
6 | "bump-patch-for-minor-pre-major": true,
7 | "packages": {
8 | "deterministic-bloom": {},
9 | "deterministic-bloom-wasm": {}
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/rust-toolchain.toml:
--------------------------------------------------------------------------------
1 | [toolchain]
2 | channel = "stable"
3 |
--------------------------------------------------------------------------------