multihash-0.19.1/.cargo_vcs_info.json0000644000000001360000000000100131220ustar { "git": { "sha1": "8adf8c0c602d98082d951f5625c29cd24c972b2c" }, "path_in_vcs": "" }multihash-0.19.1/.envrc000064400000000000000000000001471046102023000130320ustar 00000000000000use_flake() { watch_file flake.nix watch_file flake.lock eval "$(nix print-dev-env)" } use flake multihash-0.19.1/.github/codecov.yml000064400000000000000000000000431046102023000154140ustar 00000000000000coverage: status: patch: off multihash-0.19.1/.github/dependabot.yml000064400000000000000000000003161046102023000161020ustar 00000000000000version: 2 updates: - package-ecosystem: "cargo" directory: "/" schedule: interval: "daily" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" multihash-0.19.1/.github/workflows/build.yml000064400000000000000000000070411046102023000171330ustar 00000000000000name: build on: [ push, pull_request ] jobs: build: name: Build strategy: fail-fast: false matrix: platform: [ ubuntu-latest, macos-latest, windows-latest ] toolchain: [ stable ] runs-on: ${{ matrix.platform }} steps: - name: Checkout Sources uses: actions/checkout@v4 - name: Cache Dependencies & Build Outputs uses: actions/cache@v3 with: path: ~/.cargo key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Install Rust Toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.toolchain }} components: rustfmt, clippy - name: Check Code Format run: cargo fmt --all -- --check shell: bash - name: Code Lint run: cargo clippy --all-targets --all-features --workspace -- -D warnings shell: bash - name: Code Lint Without Default Features run: cargo clippy --no-default-features --workspace -- -D warnings shell: bash - name: Test run: cargo test --all-features --workspace shell: bash build-no-std: name: Build no_std runs-on: ubuntu-latest steps: - name: Checkout Sources uses: actions/checkout@v4 - name: Install Rust Toolchain uses: dtolnay/rust-toolchain@stable with: targets: thumbv6m-none-eabi - name: Build run: cargo build --no-default-features --workspace --target thumbv6m-none-eabi shell: bash build-no-std-with-serde: name: Build no_std with `serde` feature enabled runs-on: ubuntu-latest steps: - name: Checkout Sources uses: actions/checkout@v4 - name: Install Rust Toolchain uses: dtolnay/rust-toolchain@stable with: targets: thumbv6m-none-eabi - name: Build run: cargo build --no-default-features --features serde --workspace --target thumbv6m-none-eabi shell: bash msrv: name: MSRV runs-on: ubuntu-latest steps: - name: Checkout Sources uses: actions/checkout@v4 - name: Extract MSRV from Cargo.toml run: | MSRV=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "multihash") | .rust_version') echo "MSRV=$MSRV" >> $GITHUB_ENV - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ env.MSRV }} - uses: Swatinem/rust-cache@v2 - run: cargo +"$MSRV" build --package multihash coverage: name: Code Coverage runs-on: ubuntu-latest container: image: xd009642/tarpaulin:0.25.2 options: --security-opt seccomp=unconfined steps: - name: Checkout repository uses: actions/checkout@v4 - name: Generate code coverage run: cargo tarpaulin --verbose --all-features --workspace --timeout 120 --out Xml - name: Upload Code Coverage uses: codecov/codecov-action@v3 cargo-deny: name: Cargo Deny runs-on: ubuntu-latest steps: - name: Checkout Sources uses: actions/checkout@v4 - name: Install Rust Toolchain uses: dtolnay/rust-toolchain@stable - name: Install cargo-deny run: cargo install cargo-deny shell: bash - name: Cargo Deny - Check run: cargo deny check shell: bash semver-checks: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Check semver uses: obi1kenobi/cargo-semver-checks-action@v2 multihash-0.19.1/.gitignore000064400000000000000000000000351046102023000137000ustar 00000000000000target Cargo.lock *.bk .idea multihash-0.19.1/CHANGELOG.md000064400000000000000000000123521046102023000135260ustar 00000000000000## [](https://github.com/multiformats/rust-multihash/compare/v0.19.0...v0.19.1) (2023-09-06) ### Bug Fixes * make Serde (de)serialization no_std compatible ([#337](https://github.com/multiformats/rust-multihash/issues/337)) ([7ad5161](https://github.com/multiformats/rust-multihash/commit/7ad51614ad347bfa8c6f421986abc517e04091f6)), closes 336[#](https://github.com/multiformats/rust-multihash/issues/336). This was a regression introduced in v0.19.0. ## [](https://github.com/multiformats/rust-multihash/compare/v0.18.0...v0.19.0) (2023-06-06) ### ⚠ BREAKING CHANGES * the Serde serialization format changed * split crates into multiple to isolate breaking changes * `identity` hasher was removed See the migration section below for help on upgrading. ### Features * **codetable:** remove `identity` hasher ([#289](https://github.com/multiformats/rust-multihash/issues/289)) ([8473e2f](https://github.com/multiformats/rust-multihash/commit/8473e2f7ecdc0838a3f35d0ecb1935b4c70797c2)) * Serde serialize Multihash in bytes representation ([#302](https://github.com/multiformats/rust-multihash/issues/302)) ([1023226](https://github.com/multiformats/rust-multihash/commit/10232266c01aa83190af62ad6aeebf63bb7a16c7)) ### Bug Fixes * avoid possible panic in error handling code ([#277](https://github.com/multiformats/rust-multihash/issues/277)) ([5dc1dfa](https://github.com/multiformats/rust-multihash/commit/5dc1dfac0235e63e9ad80572e6b73f8fcd301ec3)) * don't panic on non minimal varints ([#291](https://github.com/multiformats/rust-multihash/issues/291)) ([6ef6040](https://github.com/multiformats/rust-multihash/commit/6ef604012b84d5c15d4f3c66a28ead96afedf158)), closes [#282](https://github.com/multiformats/rust-multihash/issues/282) * expose `MultihashDigest` trait in codetable ([#304](https://github.com/multiformats/rust-multihash/issues/304)) ([50b43cd](https://github.com/multiformats/rust-multihash/commit/50b43cdbba5492923ffb31bb197930d2f3e2cf14)) ### Code Refactoring * split crates into multiple to isolate breaking changes ([#272](https://github.com/multiformats/rust-multihash/issues/272)) ([954e523](https://github.com/multiformats/rust-multihash/commit/954e5233d273a2b7d682fd087178203628d131a4)) ### Migrating When upgrading to `v0.19`, consider the following: - `Code` has moved from `multihash::Code` to `multihash_codetable::Code`. It's strongly recommended to define your own code table using `multihash_derive`. Check the [custom codetable example](codetable/examples/custom_table.rs) on how to use it. For the simplest migration, use the `multihash_codetable::Code`. **Before** ```rust use multihash::{Code, MultihashDigest}; fn main() { let hash = Code::Sha2_256.digest(b"hello, world!"); println!("{:?}", hash); } ``` **After** ```rust use multihash_codetable::{Code, MultihashDigest}; fn main() { let hash = Code::Sha2_256.digest(b"hello, world!"); println!("{:?}", hash); } ``` If you get compile errors, make sure you have the correct features enabled. In this case it would be the `sha2` and `digest` features. - `multihash::Multihash` now requires the size of its internal buffer as a const-generic. You can migrate your existing code by defining the following type-alias: ```rust type Multihash = multihash::Multihash<64>; ``` - The `identity` hasher has been removed completely. **Before** ```rust use multihash::{Code, MultihashDigest}; fn main() { let hash = Code::Identity.digest(b"hello, world!"); println!("{:?}", hash); } ``` **After** ```rust use multihash::Multihash; const IDENTITY_HASH_CODE: u64 = 0x00; fn main() { let hash = Multihash::<64>::wrap(IDENTITY_HASH_CODE, b"hello, world!").unwrap(); println!("{:?}", hash); } ``` Check the [identity example](examples/identity.rs) for more information on how to replicate the functionality. ## [v0.18.1](https://github.com/multiformats/rust-multihash/compare/v0.18.0...v0.18.1) (2023-04-14) ### Bug Fixes * don't panic on non minimal varints ([#293](https://github.com/multiformats/rust-multihash/issues/293)) ([c3445fc](https://github.com/multiformats/rust-multihash/commit/c3445fc5041b0fc573945321ebd4b0cdffe0daa5)), closes [#282](https://github.com/multiformats/rust-multihash/issues/282) ## [0.18.0](https://github.com/multiformats/rust-multihash/compare/v0.17.0...v0.18.0) (2022-12-06) ### ⚠ BREAKING CHANGES * update to Rust edition 2021 * `Multihash::write()` returns bytes written Prior to this change it returned an empty tuple `()`, now it returns the bytes written. ### Features * add `encoded_len` and bytes written ([#252](https://github.com/multiformats/rust-multihash/issues/252)) ([b3cc43e](https://github.com/multiformats/rust-multihash/commit/b3cc43ecb6f9c59da774b094853d6542430d55ad)) ### Bug Fixes * remove Nix support ([#254](https://github.com/multiformats/rust-multihash/issues/254)) ([ebf57dd](https://github.com/multiformats/rust-multihash/commit/ebf57ddb82be2d2fd0a2f00666b0f888d4c78e1b)), closes [#247](https://github.com/multiformats/rust-multihash/issues/247) * update to Rust edition 2021 ([#255](https://github.com/multiformats/rust-multihash/issues/255)) ([da53376](https://github.com/multiformats/rust-multihash/commit/da53376e0d9cf2d82d6c0d10590a77991cb3a6b6)) multihash-0.19.1/Cargo.lock0000644000000221240000000000100110760ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] name = "arbitrary" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" [[package]] name = "arrayvec" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "byte-slice-cast" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "core2" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" dependencies = [ "memchr", ] [[package]] name = "env_logger" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ "log", "regex", ] [[package]] name = "getrandom" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "impl-trait-for-tuples" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "indexmap" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", ] [[package]] name = "itoa" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "libc" version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "log" version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "multihash" version = "0.19.1" dependencies = [ "arbitrary", "core2", "hex", "parity-scale-codec", "quickcheck", "rand", "serde", "serde_json", "serde_test", "unsigned-varint", ] [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "parity-scale-codec" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" dependencies = [ "arrayvec", "byte-slice-cast", "impl-trait-for-tuples", "parity-scale-codec-derive", ] [[package]] name = "parity-scale-codec-derive" version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn", ] [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-crate" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", "toml_edit", ] [[package]] name = "proc-macro2" version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] [[package]] name = "quickcheck" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" dependencies = [ "env_logger", "log", "rand", ] [[package]] name = "quote" version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "regex" version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "ryu" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "serde" version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" [[package]] name = "serde_json" version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_test" version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "100168a8017b89fd4bcbeb8d857d95a8cfcbde829a7147c09cc82d3ab8d8cb41" dependencies = [ "serde", ] [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "toml_datetime" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" [[package]] name = "toml_edit" version = "0.19.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" dependencies = [ "indexmap", "toml_datetime", "winnow", ] [[package]] name = "unicode-ident" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unsigned-varint" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winnow" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" dependencies = [ "memchr", ] multihash-0.19.1/Cargo.toml0000644000000037370000000000100111320ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.64" name = "multihash" version = "0.19.1" authors = [ "dignifiedquire ", "David Craven ", "Volker Mische ", ] description = "Implementation of the multihash format" documentation = "https://docs.rs/multihash/" readme = "README.md" keywords = [ "multihash", "ipfs", ] license = "MIT" repository = "https://github.com/multiformats/rust-multihash" [dependencies.arbitrary] version = "1.1.0" optional = true [dependencies.core2] version = "0.4.0" default-features = false [dependencies.parity-scale-codec] version = "3.0.0" features = ["derive"] optional = true default-features = false [dependencies.quickcheck] version = "1.0.3" optional = true [dependencies.rand] version = "0.8.5" features = ["small_rng"] optional = true [dependencies.serde] version = "1.0.116" optional = true default-features = false [dependencies.unsigned-varint] version = "0.7.1" default-features = false [dev-dependencies.arbitrary] version = "1.1.0" [dev-dependencies.hex] version = "0.4.2" [dev-dependencies.quickcheck] version = "1.0.3" [dev-dependencies.rand] version = "0.8.5" [dev-dependencies.serde_json] version = "1.0.58" [dev-dependencies.serde_test] version = "1.0.160" [features] alloc = [] arb = [ "dep:quickcheck", "dep:rand", "dep:arbitrary", ] default = ["std"] scale-codec = ["dep:parity-scale-codec"] serde = ["dep:serde"] serde-codec = ["serde"] std = [ "unsigned-varint/std", "alloc", ] multihash-0.19.1/Cargo.toml.orig000064400000000000000000000026051046102023000146040ustar 00000000000000[workspace] members = ["derive", "derive-impl", ".", "codetable"] resolver = "2" [package] name = "multihash" description = "Implementation of the multihash format" repository = "https://github.com/multiformats/rust-multihash" keywords = ["multihash", "ipfs"] version = "0.19.1" authors = ["dignifiedquire ", "David Craven ", "Volker Mische "] license = "MIT" readme = "README.md" documentation = "https://docs.rs/multihash/" edition = "2021" rust-version = "1.64" [features] default = ["std"] std = ["unsigned-varint/std", "alloc"] alloc = [] arb = ["dep:quickcheck", "dep:rand", "dep:arbitrary"] scale-codec = ["dep:parity-scale-codec"] serde-codec = ["serde"] # Deprecated, don't use. serde = ["dep:serde"] [dependencies] parity-scale-codec = { version = "3.0.0", default-features = false, features = ["derive"], optional = true } quickcheck = { version = "1.0.3", optional = true } rand = { version = "0.8.5", optional = true, features = ["small_rng"] } serde = { version = "1.0.116", optional = true, default-features = false } unsigned-varint = { version = "0.7.1", default-features = false } arbitrary = { version = "1.1.0", optional = true } core2 = { version = "0.4.0", default-features = false } [dev-dependencies] hex = "0.4.2" serde_json = "1.0.58" quickcheck = "1.0.3" rand = "0.8.5" arbitrary = "1.1.0" serde_test = "1.0.160" multihash-0.19.1/LICENSE000064400000000000000000000021511046102023000127160ustar 00000000000000The MIT License Copyright (C) 2015-2016 Friedel Ziegelmayer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Status API Training Shop Blog About Pricing multihash-0.19.1/README.md000064400000000000000000000073211046102023000131740ustar 00000000000000# rust-multihash [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) [![](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats) [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs) [![](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) [![Build Status](https://github.com/multiformats/rust-multihash/workflows/build/badge.svg)](https://github.com/multiformats/rust-multihash/actions) [![Crates.io](https://img.shields.io/crates/v/multihash?style=flat-square)](https://crates.io/crates/multihash) [![License](https://img.shields.io/crates/l/multihash?style=flat-square)](LICENSE) [![Documentation](https://docs.rs/multihash/badge.svg?style=flat-square)](https://docs.rs/multihash) [![Dependency Status](https://deps.rs/repo/github/multiformats/rust-multihash/status.svg)](https://deps.rs/repo/github/multiformats/rust-multihash) [![Coverage Status]( https://img.shields.io/codecov/c/github/multiformats/rust-multihash?style=flat-square)](https://codecov.io/gh/multiformats/rust-multihash) > [multihash](https://github.com/multiformats/multihash) implementation in Rust. ## Table of Contents - [Install](#install) - [Usage](#usage) - [Supported Hash Types](#supported-hash-types) - [Maintainers](#maintainers) - [Contribute](#contribute) - [License](#license) ## Install First add this to your `Cargo.toml` ```toml [dependencies] multihash = "*" ``` Then run `cargo build`. ## MSRV The minimum supported Rust version for this library is `1.64.0`. This is only guaranteed without additional features activated. ## Usage The `multihash` crate exposes a basic data structure for encoding and decoding multihash. It does not provide any hashing functionality itself. `Multihash` uses const-generics to define the internal buffer size. You should set this to the maximum size of the digest you want to support. ```rust use multihash::Multihash; const SHA2_256: u64 = 0x12; fn main() { let hash = Multihash::<64>::wrap(SHA2_256, b"my digest"); println!("{:?}", hash); } ``` ### Using a custom code table You can derive your own application specific code table using the `multihash-derive` crate. The `multihash-codetable` provides predefined hasher implementations if you don't want to implement your own. ```rust use multihash_derive::MultihashDigest; #[derive(Clone, Copy, Debug, Eq, MultihashDigest, PartialEq)] #[mh(alloc_size = 64)] pub enum Code { #[mh(code = 0x01, hasher = multihash_codetable::Sha2_256)] Foo, #[mh(code = 0x02, hasher = multihash_codetable::Sha2_512)] Bar, } fn main() { let hash = Code::Foo.digest(b"my hash"); println!("{:02x?}", hash); } ``` ## Supported Hash Types * `SHA1` * `SHA2-256` * `SHA2-512` * `SHA3`/`Keccak` * `Blake2b-256`/`Blake2b-512`/`Blake2s-128`/`Blake2s-256` * `Blake3` * `Strobe` ## Maintainers Captain: [@dignifiedquire](https://github.com/dignifiedquire). ## Contribute Contributions welcome. Please check out [the issues](https://github.com/multiformats/rust-multihash/issues). Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. ## License [MIT](LICENSE) multihash-0.19.1/RELEASE.md000064400000000000000000000023451046102023000133200ustar 00000000000000Release process =============== Generating Changelog -------------------- Install dependencies ```sh $ npm install -g conventional-changelog-cli $ cd rust-multihash $ conventional-changelog --preset conventionalcommits ``` Add the output of that to `CHANGELOG.md`. Write a human-centric summary of changes and add migration instructions for breaking changes if needed. Update the linked output to reference the new version, which conventional-changelog doesn't know about: ```md # [](https://github.com/multiformats/rust-multihash/compare/v0.17.0...v) (2022-12-06) ``` becomes: ```md # [v0.18.0](https://github.com/multiformats/rust-multihash/compare/v0.17.0...v0.18.0) (2022-12-06) ``` Create a pull request with the changelog changes and the correct version bumps to the crates. Publishing ---------- Once the PR above is merged, the crate can be published. This is done using [`cargo-release`](https://github.com/crate-ci/cargo-release). This requires the following permissions - on github.com/multiformats/rust-multihash - creating tags - pushing to `master` - on crates.io - publish access to all published crates Dry run ```sh $ cargo release --workspace ``` Actual publishing ```sh $ cargo release --workspace --execute ``` multihash-0.19.1/deny.toml000064400000000000000000000021621046102023000135470ustar 00000000000000 [advisories] ignore = [ # https://rustsec.org/advisories/RUSTSEC-2021-0145 # 1. On windows, `atty` dereferences a potentially unaligned pointer. # 2. Last release of `atty` was almost 3 years ago. "RUSTSEC-2021-0145", # https://rustsec.org/advisories/RUSTSEC-2021-0127 # 1. serde_cbor is unmaintained "RUSTSEC-2021-0127" ] notice = "deny" unmaintained = "warn" vulnerability = "deny" yanked = "deny" [licenses] allow = [ "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "CC0-1.0", "MIT", "Unicode-DFS-2016", ] default = "deny" confidence-threshold = 1.0 unlicensed = "deny" [bans] allow = [] deny = [] # TODO before changing to 'deny': # * Resolve duplicate versions of itoa (used by criterion) # * Remove atty from criterion # * Resolve duplicate versions of constant_time_eq (used by blake2_sid and blake3) multiple-versions = "warn" skip = [] skip-tree = [] wildcards = "warn" [sources] allow-git = [] allow-registry = ["https://github.com/rust-lang/crates.io-index"] unknown-git = "deny" unknown-registry = "deny" [sources.allow-org] github = [] gitlab = [] bitbucket = [] multihash-0.19.1/examples/identity.rs000064400000000000000000000021151046102023000157260ustar 00000000000000//! An example for how to use the "identity" hash of [`Multihash`]. //! //! Identity hashing means we don't actually perform any hashing. //! Instead, we just store data directly in place of the "digest". //! //! [`Multihash::wrap`] returns an error in case the provided digest is too big for the available space. //! Make sure you construct a [`Multihash`] with a large enough buffer for your data. //! //! Typically, the way you want to use the "identity" hash is: //! 1. Check if your data is smaller than whatever buffer size you chose. //! 2. If yes, store the data inline. //! 3. If no, hash it make it fit into the provided buffer. use multihash::Multihash; /// See for reference. const IDENTITY_HASH_CODE: u64 = 0; fn main() { let identity_hash = Multihash::<64>::wrap(IDENTITY_HASH_CODE, b"foobar").unwrap(); let wrap_err = Multihash::<2>::wrap(IDENTITY_HASH_CODE, b"foobar").unwrap_err(); assert_eq!(identity_hash.digest(), b"foobar"); assert_eq!(wrap_err.to_string(), "Invalid multihash size 6."); } multihash-0.19.1/release.toml000064400000000000000000000000341046102023000142240ustar 00000000000000consolidate-commits = false multihash-0.19.1/rustfmt.toml000064400000000000000000000000431046102023000143100ustar 00000000000000format_code_in_doc_comments = true multihash-0.19.1/src/arb.rs000064400000000000000000000053361046102023000136220ustar 00000000000000use quickcheck::Gen; use rand::{ distributions::{weighted::WeightedIndex, Distribution}, Rng, RngCore, SeedableRng, }; use crate::Multihash; use arbitrary::{size_hint, Unstructured}; /// Generates a random valid multihash. impl quickcheck::Arbitrary for Multihash { fn arbitrary(g: &mut Gen) -> Multihash { // In real world lower multihash codes are more likely to happen, hence distribute them // with bias towards smaller values. let weights = [128, 64, 32, 16, 8, 4, 2, 1]; let dist = WeightedIndex::new(weights.iter()).unwrap(); let mut rng = rand::rngs::SmallRng::seed_from_u64(u64::arbitrary(g)); let code = match dist.sample(&mut rng) { 0 => rng.gen_range(0..u64::pow(2, 7)), 1 => rng.gen_range(u64::pow(2, 7)..u64::pow(2, 14)), 2 => rng.gen_range(u64::pow(2, 14)..u64::pow(2, 21)), 3 => rng.gen_range(u64::pow(2, 21)..u64::pow(2, 28)), 4 => rng.gen_range(u64::pow(2, 28)..u64::pow(2, 35)), 5 => rng.gen_range(u64::pow(2, 35)..u64::pow(2, 42)), 6 => rng.gen_range(u64::pow(2, 42)..u64::pow(2, 49)), 7 => rng.gen_range(u64::pow(2, 56)..u64::pow(2, 63)), _ => unreachable!(), }; // Maximum size is S byte due to the generic. let size = rng.gen_range(0..S); let mut data = [0; S]; rng.fill_bytes(&mut data); Multihash::wrap(code, &data[..size]).unwrap() } } impl<'a, const S: usize> arbitrary::Arbitrary<'a> for Multihash { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { let mut code = 0u64; let mut len_choice = u.arbitrary::()? | 1; while len_choice & 1 == 1 { len_choice >>= 1; let x = u.arbitrary::(); let next = code .checked_shl(8) .zip(x.ok()) .map(|(next, x)| next.saturating_add(x as u64)); match next { None => break, Some(next) => code = next, } } let size = u.int_in_range(0..=S)?; let data = u.bytes(size)?; Ok(Multihash::wrap(code, data).unwrap()) } fn size_hint(depth: usize) -> (usize, Option) { size_hint::and(<[u8; 3]>::size_hint(depth), (0, Some(S + 8))) } } #[cfg(test)] mod tests { use crate::Multihash; use arbitrary::{Arbitrary, Unstructured}; #[test] fn arbitrary() { let mut u = Unstructured::new(&[2, 4, 13, 5, 6, 7, 8, 9, 6]); let mh = as Arbitrary>::arbitrary(&mut u).unwrap(); let mh2 = Multihash::<16>::wrap(1037, &[6, 7, 8, 9, 6]).unwrap(); assert_eq!(mh, mh2); } } multihash-0.19.1/src/error.rs000064400000000000000000000046731046102023000142120ustar 00000000000000#[cfg(not(feature = "std"))] use core2::{error::Error as StdError, io}; #[cfg(feature = "std")] use std::{error::Error as StdError, io}; use unsigned_varint::decode; /// Opaque error struct for operations involving a [`Multihash`](crate::Multihash). #[derive(Debug)] pub struct Error { kind: Kind, } impl Error { pub(crate) const fn invalid_size(size: u64) -> Self { Self { kind: Kind::InvalidSize(size), } } #[cfg(not(feature = "std"))] pub(crate) const fn insufficient_varint_bytes() -> Self { Self { kind: Kind::Varint(decode::Error::Insufficient), } } #[cfg(not(feature = "std"))] pub(crate) const fn varint_overflow() -> Self { Self { kind: Kind::Varint(decode::Error::Overflow), } } } impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { self.kind.fmt(f) } } #[derive(Debug)] enum Kind { /// Io error. Io(io::Error), /// Invalid multihash size. InvalidSize(u64), /// Invalid varint. Varint(decode::Error), } #[cfg(feature = "std")] pub(crate) fn unsigned_varint_to_multihash_error(err: unsigned_varint::io::ReadError) -> Error { match err { unsigned_varint::io::ReadError::Io(err) => io_to_multihash_error(err), unsigned_varint::io::ReadError::Decode(err) => Error { kind: Kind::Varint(err), }, other => io_to_multihash_error(io::Error::new(io::ErrorKind::Other, other)), } } #[cfg(not(feature = "std"))] pub(crate) fn unsigned_varint_decode_to_multihash_error( err: unsigned_varint::decode::Error, ) -> Error { Error { kind: Kind::Varint(err), } } pub(crate) fn io_to_multihash_error(err: io::Error) -> Error { Error { kind: Kind::Io(err), } } impl core::fmt::Display for Kind { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match self { Self::Io(err) => write!(f, "{err}"), Self::InvalidSize(size) => write!(f, "Invalid multihash size {size}."), Self::Varint(err) => write!(f, "{err}"), } } } impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match &self.kind { Kind::Io(inner) => Some(inner), Kind::InvalidSize(_) => None, Kind::Varint(_) => None, // FIXME: Does not implement `core2::Error`. } } } multihash-0.19.1/src/lib.rs000064400000000000000000000031071046102023000136160ustar 00000000000000//! Bare-minimum multihash data structure. //! //! This crate defines a `no_std` compatible data structures for representing a `Multihash`. //! //! It does not offer any hashing, instead you are encouraged to either do the hashing yourself. //! Alternatively, you can use an existing code table or make your own code table. //! //! The [`multihash-codetable`] crate defines a set of hashes to get started quickly. //! To make your own codetable, use the [`multihash-derive`] crate. //! //! The `arb` feature flag enables the quickcheck arbitrary implementation for property based //! testing. //! //! For serializing the multihash there is support for [Serde] via the `serde-codec` feature and //! the [SCALE Codec] via the `scale-codec` feature. //! //! [Serde]: https://serde.rs //! [SCALE Codec]: https://github.com/paritytech/parity-scale-codec //! [`multihash-derive`]: https://docs.rs/multihash-derive //! [`multihash-codetable`]: https://docs.rs/multihash-codetable #![deny(missing_docs, unsafe_code)] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(feature = "alloc")] extern crate alloc; #[cfg(any(test, feature = "arb"))] mod arb; mod error; mod multihash; #[cfg(feature = "serde")] mod serde; /// Multihash result. #[deprecated(note = "Use `Result` instead")] pub type Result = core::result::Result; pub use crate::error::Error; pub use crate::multihash::Multihash; /// Deprecated type-alias for the [`Multihash`] type. #[deprecated(since = "0.18.0", note = "Use `multihash::Multihash instead.")] pub type MultihashGeneric = Multihash; multihash-0.19.1/src/multihash.rs000064400000000000000000000266141046102023000150560ustar 00000000000000use crate::Error; #[cfg(feature = "alloc")] use alloc::vec::Vec; use core::convert::TryInto; use core::fmt::Debug; use unsigned_varint::encode as varint_encode; #[cfg(feature = "std")] use std::io; #[cfg(not(feature = "std"))] use core2::io; /// A Multihash instance that only supports the basic functionality and no hashing. /// /// With this Multihash implementation you can operate on Multihashes in a generic way, but /// no hasher implementation is associated with the code. /// /// # Example /// /// ``` /// use multihash::Multihash; /// /// const Sha3_256: u64 = 0x16; /// let digest_bytes = [ /// 0x16, 0x20, 0x64, 0x4b, 0xcc, 0x7e, 0x56, 0x43, 0x73, 0x04, 0x09, 0x99, 0xaa, 0xc8, 0x9e, /// 0x76, 0x22, 0xf3, 0xca, 0x71, 0xfb, 0xa1, 0xd9, 0x72, 0xfd, 0x94, 0xa3, 0x1c, 0x3b, 0xfb, /// 0xf2, 0x4e, 0x39, 0x38, /// ]; /// let mh = Multihash::<32>::from_bytes(&digest_bytes).unwrap(); /// assert_eq!(mh.code(), Sha3_256); /// assert_eq!(mh.size(), 32); /// assert_eq!(mh.digest(), &digest_bytes[2..]); /// ``` #[derive(Clone, Copy, Debug, Eq, Ord, PartialOrd)] pub struct Multihash { /// The code of the Multihash. code: u64, /// The actual size of the digest in bytes (not the allocated size). size: u8, /// The digest. digest: [u8; S], } impl Default for Multihash { fn default() -> Self { Self { code: 0, size: 0, digest: [0; S], } } } impl Multihash { /// Wraps the digest in a multihash. pub const fn wrap(code: u64, input_digest: &[u8]) -> Result { if input_digest.len() > S { return Err(Error::invalid_size(input_digest.len() as _)); } let size = input_digest.len(); let mut digest = [0; S]; let mut i = 0; while i < size { digest[i] = input_digest[i]; i += 1; } Ok(Self { code, size: size as u8, digest, }) } /// Returns the code of the multihash. pub const fn code(&self) -> u64 { self.code } /// Returns the size of the digest. pub const fn size(&self) -> u8 { self.size } /// Returns the digest. pub fn digest(&self) -> &[u8] { &self.digest[..self.size as usize] } /// Reads a multihash from a byte stream. pub fn read(r: R) -> Result where Self: Sized, { let (code, size, digest) = read_multihash(r)?; Ok(Self { code, size, digest }) } /// Parses a multihash from a bytes. /// /// You need to make sure the passed in bytes have the correct length. The digest length /// needs to match the `size` value of the multihash. pub fn from_bytes(mut bytes: &[u8]) -> Result where Self: Sized, { let result = Self::read(&mut bytes)?; // There were more bytes supplied than read if !bytes.is_empty() { return Err(Error::invalid_size(bytes.len().try_into().expect( "Currently the maximum size is 255, therefore always fits into usize", ))); } Ok(result) } /// Writes a multihash to a byte stream, returning the written size. pub fn write(&self, w: W) -> Result { write_multihash(w, self.code(), self.size(), self.digest()) } /// Returns the length in bytes needed to encode this multihash into bytes. pub fn encoded_len(&self) -> usize { let mut code_buf = varint_encode::u64_buffer(); let code = varint_encode::u64(self.code, &mut code_buf); let mut size_buf = varint_encode::u8_buffer(); let size = varint_encode::u8(self.size, &mut size_buf); code.len() + size.len() + usize::from(self.size) } #[cfg(feature = "alloc")] /// Returns the bytes of a multihash. pub fn to_bytes(&self) -> Vec { let mut bytes = Vec::with_capacity(self.size().into()); let written = self .write(&mut bytes) .expect("writing to a vec should never fail"); debug_assert_eq!(written, bytes.len()); bytes } /// Truncates the multihash to the given size. It's up to the caller to ensure that the new size /// is secure (cryptographically) to use. /// /// If the new size is larger than the current size, this method does nothing. pub fn truncate(&self, size: u8) -> Self { let mut mh = *self; mh.size = mh.size.min(size); mh } /// Resizes the backing multihash buffer. /// /// This function fails if the hash digest is larger than the target size. pub fn resize(&self) -> Result, Error> { let size = self.size as usize; if size > R { return Err(Error::invalid_size(self.size as u64)); } let mut mh = Multihash { code: self.code, size: self.size, digest: [0; R], }; mh.digest[..size].copy_from_slice(&self.digest[..size]); Ok(mh) } /// Decomposes struct, useful when needing a `Sized` array or moving all the data into another type /// /// It is recommended to use `digest()` `code()` and `size()` for most cases. pub fn into_inner(self) -> (u64, [u8; S], u8) { let Self { code, digest, size } = self; (code, digest, size) } } // Don't hash the whole allocated space, but just the actual digest #[allow(clippy::derived_hash_with_manual_eq)] impl core::hash::Hash for Multihash { fn hash(&self, state: &mut T) { self.code.hash(state); self.digest().hash(state); } } #[cfg(feature = "alloc")] impl From> for Vec { fn from(multihash: Multihash) -> Self { multihash.to_bytes() } } impl PartialEq> for Multihash { fn eq(&self, other: &Multihash) -> bool { // NOTE: there's no need to explicitly check the sizes, that's implicit in the digest. self.code == other.code && self.digest() == other.digest() } } #[cfg(feature = "scale-codec")] impl parity_scale_codec::Encode for Multihash { fn encode_to(&self, dest: &mut EncOut) { self.code.encode_to(dest); self.size.encode_to(dest); // **NOTE** We write the digest directly to dest, since we have known the size of digest. // // We do not choose to encode &[u8] directly, because it will add extra bytes (the compact length of digest). // For a valid multihash, the length of digest must equal to `size`. // Therefore, we can only read raw bytes whose length is equal to `size` when decoding. dest.write(self.digest()); } } #[cfg(feature = "scale-codec")] impl parity_scale_codec::EncodeLike for Multihash {} #[cfg(feature = "scale-codec")] impl parity_scale_codec::Decode for Multihash { fn decode( input: &mut DecIn, ) -> Result { let mut mh = Multihash { code: parity_scale_codec::Decode::decode(input)?, size: parity_scale_codec::Decode::decode(input)?, digest: [0; S], }; if mh.size as usize > S { return Err(parity_scale_codec::Error::from("invalid size")); } // For a valid multihash, the length of digest must equal to the size. input.read(&mut mh.digest[..mh.size as usize])?; Ok(mh) } } /// Writes the multihash to a byte stream. fn write_multihash(mut w: W, code: u64, size: u8, digest: &[u8]) -> Result where W: io::Write, { let mut code_buf = varint_encode::u64_buffer(); let code = varint_encode::u64(code, &mut code_buf); let mut size_buf = varint_encode::u8_buffer(); let size = varint_encode::u8(size, &mut size_buf); let written = code.len() + size.len() + digest.len(); w.write_all(code) .map_err(crate::error::io_to_multihash_error)?; w.write_all(size) .map_err(crate::error::io_to_multihash_error)?; w.write_all(digest) .map_err(crate::error::io_to_multihash_error)?; Ok(written) } /// Reads a multihash from a byte stream that contains a full multihash (code, size and the digest) /// /// Returns the code, size and the digest. The size is the actual size and not the /// maximum/allocated size of the digest. /// /// Currently the maximum size for a digest is 255 bytes. fn read_multihash(mut r: R) -> Result<(u64, u8, [u8; S]), Error> where R: io::Read, { let code = read_u64(&mut r)?; let size = read_u64(&mut r)?; if size > S as u64 || size > u8::MAX as u64 { return Err(Error::invalid_size(size)); } let mut digest = [0; S]; r.read_exact(&mut digest[..size as usize]) .map_err(crate::error::io_to_multihash_error)?; Ok((code, size as u8, digest)) } #[cfg(feature = "std")] pub(crate) fn read_u64(r: R) -> Result { unsigned_varint::io::read_u64(r).map_err(crate::error::unsigned_varint_to_multihash_error) } /// Reads 64 bits from a byte array into a u64 /// Adapted from unsigned-varint's generated read_u64 function at /// https://github.com/paritytech/unsigned-varint/blob/master/src/io.rs #[cfg(not(feature = "std"))] pub(crate) fn read_u64(mut r: R) -> Result { use unsigned_varint::decode; let mut b = varint_encode::u64_buffer(); for i in 0..b.len() { let n = r .read(&mut (b[i..i + 1])) .map_err(crate::error::io_to_multihash_error)?; if n == 0 { return Err(Error::insufficient_varint_bytes()); } else if decode::is_last(b[i]) { return decode::u64(&b[..=i]) .map(|decoded| decoded.0) .map_err(crate::error::unsigned_varint_decode_to_multihash_error); } } Err(Error::varint_overflow()) } #[cfg(test)] mod tests { use super::*; #[test] #[cfg(feature = "scale-codec")] fn test_scale() { use parity_scale_codec::{Decode, Encode}; let mh1 = Multihash::<32>::wrap(0, b"hello world").unwrap(); // println!("mh1: code = {}, size = {}, digest = {:?}", mh1.code(), mh1.size(), mh1.digest()); let mh1_bytes = mh1.encode(); // println!("Multihash<32>: {}", hex::encode(&mh1_bytes)); let mh2: Multihash<32> = Decode::decode(&mut &mh1_bytes[..]).unwrap(); assert_eq!(mh1, mh2); let mh3 = Multihash::<64>::wrap(0, b"hello world").unwrap(); // println!("mh3: code = {}, size = {}, digest = {:?}", mh3.code(), mh3.size(), mh3.digest()); let mh3_bytes = mh3.encode(); // println!("Multihash<64>: {}", hex::encode(&mh3_bytes)); let mh4: Multihash<64> = Decode::decode(&mut &mh3_bytes[..]).unwrap(); assert_eq!(mh3, mh4); assert_eq!(mh1_bytes, mh3_bytes); } #[test] fn test_eq_sizes() { let mh1 = Multihash::<32>::default(); let mh2 = Multihash::<64>::default(); assert_eq!(mh1, mh2); } #[test] fn decode_non_minimal_error() { // This is a non-minimal varint. let data = [241, 0, 0, 0, 0, 0, 128, 132, 132, 132, 58]; let result = read_u64(&data[..]); assert!(result.is_err()); } } multihash-0.19.1/src/serde.rs000064400000000000000000000170731046102023000141610ustar 00000000000000//! Multihash Serde (de)serialization use core::{fmt, mem, slice}; use serde::{ de::{self, SeqAccess, Visitor}, ser, Deserialize, Deserializer, Serialize, Serializer, }; use crate::Multihash; /// The maximum serialization size of `code` is 9 bytes (a large varint encoded u64) and for `size` /// is 2 bytes (a large varint encoded u8), this makes a total of 11 bytes. const MAXIMUM_PREFIX_SIZE: usize = 11; /// The is currently no way to allocate an array that is some constant size bigger then a given /// const generic. Once `generic_const_exprs` are a thing, this struct will no longer be needed. /// Until then we introduce a hack. We allocate a struct, which contains two independent arrays, /// which can be specified with const generics. We then treat the whole struct as a slice of /// continuous memory. #[repr(C, packed)] struct Buffer { first: [u8; SIZE_FIRST], second: [u8; SIZE_SECOND], } #[allow(unsafe_code)] impl Buffer { fn new() -> Self { Self { first: [0; SIZE_FIRST], second: [0; SIZE_SECOND], } } fn as_slice(&self) -> &[u8] { unsafe { slice::from_raw_parts(self as *const _ as _, mem::size_of::()) } } fn as_mut_slice(&mut self) -> &mut [u8] { unsafe { slice::from_raw_parts_mut(self as *mut _ as _, mem::size_of::()) } } } impl Serialize for Multihash { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut buffer = Buffer::::new(); let bytes_written = self .write(buffer.as_mut_slice()) .map_err(|_| ser::Error::custom("Failed to serialize Multihash"))?; serializer.serialize_bytes(&buffer.as_slice()[..bytes_written]) } } struct BytesVisitor; impl<'de, const SIZE: usize> Visitor<'de> for BytesVisitor { type Value = Multihash; fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "a valid Multihash in bytes") } fn visit_bytes(self, bytes: &[u8]) -> Result where E: de::Error, { Multihash::::from_bytes(bytes) .map_err(|_| de::Error::custom("Failed to deserialize Multihash")) } // Some Serde data formats interpret a byte stream as a sequence of bytes (e.g. `serde_json`). fn visit_seq(self, mut seq: A) -> Result where A: SeqAccess<'de>, { let mut buffer = Buffer::::new(); let bytes = buffer.as_mut_slice(); // Fill the bytes slices with the given sequence let mut pos = 0; while let Some(byte) = seq.next_element()? { bytes[pos] = byte; pos += 1; if pos >= bytes.len() { return Err(de::Error::custom("Failed to deserialize Multihash")); } } Multihash::::from_bytes(&bytes[..pos]) .map_err(|_| de::Error::custom("Failed to deserialize Multihash")) } } impl<'de, const SIZE: usize> Deserialize<'de> for Multihash { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_bytes(BytesVisitor) } } #[cfg(test)] mod tests { use super::*; use std::ptr; use serde_test::{assert_tokens, Token}; const SHA2_256_CODE: u64 = 0x12; const DIGEST: [u8; 32] = [ 159, 228, 204, 198, 222, 22, 114, 79, 58, 48, 199, 232, 242, 84, 243, 198, 71, 25, 134, 172, 177, 248, 216, 207, 142, 150, 206, 42, 215, 219, 231, 251, ]; #[test] fn test_serde_json() { // This is a concatenation of `SHA2_256_CODE + DIGEST_LENGTH + DIGEST`. let expected_json = format!("[{},{},159,228,204,198,222,22,114,79,58,48,199,232,242,84,243,198,71,25,134,172,177,248,216,207,142,150,206,42,215,219,231,251]", SHA2_256_CODE as u8, DIGEST.len() as u8); let mh = Multihash::<32>::wrap(SHA2_256_CODE, &DIGEST).unwrap(); let json = serde_json::to_string(&mh).unwrap(); assert_eq!(json, expected_json); let mh_decoded: Multihash<32> = serde_json::from_str(&json).unwrap(); assert_eq!(mh, mh_decoded); } #[test] fn test_serde_test() { // This is a concatenation of `SHA2_256_CODE + DIGEST_LENGTH + DIGEST`. const ENCODED_MULTIHASH_BYTES: [u8; 34] = [ SHA2_256_CODE as u8, DIGEST.len() as u8, 159, 228, 204, 198, 222, 22, 114, 79, 58, 48, 199, 232, 242, 84, 243, 198, 71, 25, 134, 172, 177, 248, 216, 207, 142, 150, 206, 42, 215, 219, 231, 251, ]; let mh = Multihash::<32>::wrap(SHA2_256_CODE, &DIGEST).unwrap(); // As bytes. assert_tokens(&mh, &[Token::Bytes(&ENCODED_MULTIHASH_BYTES)]); // As sequence. serde_test::assert_de_tokens( &mh, &[ Token::Seq { len: Some(34) }, Token::U8(SHA2_256_CODE as u8), Token::U8(DIGEST.len() as u8), Token::U8(159), Token::U8(228), Token::U8(204), Token::U8(198), Token::U8(222), Token::U8(22), Token::U8(114), Token::U8(79), Token::U8(58), Token::U8(48), Token::U8(199), Token::U8(232), Token::U8(242), Token::U8(84), Token::U8(243), Token::U8(198), Token::U8(71), Token::U8(25), Token::U8(134), Token::U8(172), Token::U8(177), Token::U8(248), Token::U8(216), Token::U8(207), Token::U8(142), Token::U8(150), Token::U8(206), Token::U8(42), Token::U8(215), Token::U8(219), Token::U8(231), Token::U8(251), Token::SeqEnd, ], ); } #[test] fn test_buffer_alignment() { const SIZE_FIRST: usize = 11; const SIZE_SECOND: usize = 13; let buffer = Buffer::::new(); // Make sure that the struct allocated continuous memory, as we exploit that fact with the // `as_slice` and `as_mut_slice()` methods. let start_first = ptr::addr_of!(buffer.first) as *const u8; let start_second = ptr::addr_of!(buffer.second) as *const u8; #[allow(unsafe_code)] unsafe { assert_eq!(start_second.offset_from(start_first), SIZE_FIRST as isize); }; } #[test] fn test_buffer() { const SIZE_FIRST: usize = 3; const SIZE_SECOND: usize = 8; let mut buffer = Buffer::::new(); let data: [u8; SIZE_FIRST + SIZE_SECOND] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; buffer.as_mut_slice().copy_from_slice(&data); assert_eq!(buffer.as_slice(), data); } }