Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - 2026-07-13

### Changed
- Synced from bettersign workspace (bs-multicodec 0.7.0)
- Renamed crate from `bs-multicodec` to `multi-codec`
- Added `types.rs` module with type-safe codec wrappers
- Added comprehensive test suite (edge cases, error tests, integration, proptest, security)
- Initial published release on crates.io as `multi-codec`
30 changes: 22 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
[package]
name = "multicodec"
version = "1.0.10"
name = "multi-codec"
version = "1.0.2"
edition = "2021"
authors = ["Benjamin Kampmann <ben@gnunicorn.org>", "Dave Grantham <dwg@linuxprogrammer.org>"]
description = "Implementation of the Multicodec specification"
repository = "https://github.com/cryptidtech/rust-multicodec"
license = "MIT OR Apache-2.0"
repository = "https://github.com/cryptidtech/multi-codec.git"
readme = "README.md"
license = "MIT OR Apache-2.0"
keywords = ["multiformats", "multicodec", "serde"]
categories = ["encoding"]

[features]
default = ["serde"]

[dependencies]
multitrait = { version = "1.0", git="https://github.com/cryptidtech/multitrait.git" }
serde = { version = "1.0", default-features = false, optional = true, features = ['serde_derive'] }
thiserror = "1.0"
multi-trait = "1.0"
serde = { version = "1.0", default-features = false, optional = true, features = ["serde_derive"] }
thiserror = { version = "2.0" }

[dev-dependencies]
criterion = { version = "0.8", features = ["html_reports"] }
hex = "0.4"
proptest = "1.4"
serde_cbor = "0.11"
serde_json = "1.0"
serde_test = "1.0"
unsigned-varint = { version = "0.8", features = ["std"] }

[[bench]]
name = "codec_bench"
harness = false
path = "benches/codec_bench.rs"

[[example]]
name = "uvi"
path = "examples/uvi.rs"

[build-dependencies]
convert_case = "0.6"
csv = "1.3"
serde = "1.0"
serde = { version = "1.0", features = ["serde_derive"] }
serde_derive = "1.0"
202 changes: 202 additions & 0 deletions benches/codec_bench.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
// SPDX-License-Identifier: MIT or Apache-2.0
//! Performance benchmarks for multi-codec

use std::hint::black_box;

use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use multi_codec::Codec;
use multi_trait::TryDecodeFrom;

/// Benchmark conversions from u64
fn bench_from_u64(c: &mut Criterion) {
c.bench_function("codec_from_u64_valid", |b| {
b.iter(|| Codec::try_from(black_box(0xEDu64)))
});

c.bench_function("codec_from_u64_invalid", |b| {
b.iter(|| Codec::try_from(black_box(0xDEADBEEFu64)))
});
}

/// Benchmark conversions to u64
fn bench_to_u64(c: &mut Criterion) {
let codec = Codec::Ed25519Pub;

c.bench_function("codec_to_u64", |b| {
b.iter(|| {
let _code: u64 = black_box(codec).into();
})
});

c.bench_function("codec_code_method", |b| b.iter(|| black_box(codec).code()));
}

/// Benchmark conversions from string
fn bench_from_str(c: &mut Criterion) {
c.bench_function("codec_from_str_valid", |b| {
b.iter(|| Codec::try_from(black_box("ed25519-pub")))
});

c.bench_function("codec_from_str_invalid", |b| {
b.iter(|| Codec::try_from(black_box("unknown-codec")))
});
}

/// Benchmark conversions to string
fn bench_to_str(c: &mut Criterion) {
c.bench_function("codec_as_str", |b| {
let codec = Codec::Ed25519Pub;
b.iter(|| {
let s = codec.as_str();
black_box(s.len())
})
});
}

/// Benchmark encoding
fn bench_encoding(c: &mut Criterion) {
let mut group = c.benchmark_group("encoding");

let codecs = vec![
("Identity", Codec::Identity),
("Sha2256", Codec::Sha2256),
("Ed25519Pub", Codec::Ed25519Pub),
];

for (name, codec) in codecs {
group.bench_with_input(BenchmarkId::new("into_vec", name), &codec, |b, &codec| {
b.iter(|| {
let _v: Vec<u8> = black_box(codec).into();
})
});
}

group.finish();
}

/// Benchmark decoding
fn bench_decoding(c: &mut Criterion) {
let mut group = c.benchmark_group("decoding");

let test_cases = vec![
("Identity", vec![0x00]),
("Sha2256", vec![0x12]),
("Ed25519Pub", vec![0xED, 0x01]),
];

for (name, bytes) in test_cases {
group.bench_with_input(
BenchmarkId::new("try_decode_from", name),
&bytes,
|b, bytes| b.iter(|| Codec::try_decode_from(black_box(bytes.as_slice()))),
);
}

group.finish();
}

/// Benchmark hash operations
fn bench_hash(c: &mut Criterion) {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

let codec = Codec::Ed25519Pub;

c.bench_function("codec_hash", |b| {
b.iter(|| {
let mut hasher = DefaultHasher::new();
black_box(codec).hash(&mut hasher);
hasher.finish()
})
});
}

/// Benchmark roundtrip operations
fn bench_roundtrip(c: &mut Criterion) {
let mut group = c.benchmark_group("roundtrip");

group.bench_function("encode_decode", |b| {
let codec = Codec::Ed25519Pub;
b.iter(|| {
let encoded: Vec<u8> = black_box(codec).into();
let (decoded, _) = Codec::try_decode_from(black_box(&encoded)).unwrap();
black_box(decoded);
})
});

group.bench_function("str_codec_str", |b| {
b.iter(|| {
let codec = Codec::try_from(black_box("ed25519-pub")).unwrap();
let len = codec.as_str().len();
black_box(len);
})
});

group.bench_function("u64_codec_u64", |b| {
b.iter(|| {
let codec = Codec::try_from(black_box(0xEDu64)).unwrap();
let _code: u64 = codec.into();
})
});

group.finish();
}

/// Benchmark serde operations
#[cfg(feature = "serde")]
fn bench_serde(c: &mut Criterion) {
use serde_json;

let mut group = c.benchmark_group("serde");

let codec = Codec::Ed25519Pub;

group.bench_function("json_serialize", |b| {
b.iter(|| serde_json::to_string(&black_box(codec)))
});

let json = serde_json::to_string(&codec).unwrap();
group.bench_function("json_deserialize", |b| {
b.iter(|| serde_json::from_str::<Codec>(black_box(&json)))
});

group.bench_function("cbor_serialize", |b| {
b.iter(|| serde_cbor::to_vec(&black_box(codec)))
});

let cbor = serde_cbor::to_vec(&codec).unwrap();
group.bench_function("cbor_deserialize", |b| {
b.iter(|| serde_cbor::from_slice::<Codec>(black_box(&cbor)))
});

group.finish();
}

#[cfg(feature = "serde")]
criterion_group!(
benches,
bench_from_u64,
bench_to_u64,
bench_from_str,
bench_to_str,
bench_encoding,
bench_decoding,
bench_hash,
bench_roundtrip,
bench_serde
);

#[cfg(not(feature = "serde"))]
criterion_group!(
benches,
bench_from_u64,
bench_to_u64,
bench_from_str,
bench_to_str,
bench_encoding,
bench_decoding,
bench_hash,
bench_roundtrip
);

criterion_main!(benches);
Loading
Loading