From 6937920fa145ed77d299254f37c9cc67ee1d6677 Mon Sep 17 00:00:00 2001 From: mriise Date: Fri, 21 Nov 2025 12:28:25 -0800 Subject: [PATCH 1/3] feat: base45 --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + cli/main.rs | 2 ++ src/base.rs | 2 ++ src/error.rs | 6 ++++++ src/impls.rs | 13 +++++++++++++ tests/lib.rs | 1 + 7 files changed, 32 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index c53f517..14fe937 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,6 +115,12 @@ dependencies = [ "match-lookup", ] +[[package]] +name = "base45" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3103f6d6af63726e86b026ac66769fc578ea38aba33f409e5855a4def97a564" + [[package]] name = "bitflags" version = "1.3.2" @@ -479,6 +485,7 @@ version = "0.9.2" dependencies = [ "base-x", "base256emoji", + "base45", "criterion", "data-encoding", "data-encoding-macro", diff --git a/Cargo.toml b/Cargo.toml index 131513a..47cda72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ base-x = { version = "0.2.7", default-features = false } base256emoji = "1.0.2" data-encoding = { version = "2.3.1", default-features = false, features = ["alloc"] } data-encoding-macro = "0.1.9" +base45 = "3.1.0" [dev-dependencies] criterion = "0.7" diff --git a/cli/main.rs b/cli/main.rs index 529b6d0..ec11468 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -84,6 +84,7 @@ impl fmt::Display for StrBase { Base::Base32Z => "base32z", Base::Base36Lower => "base36lower", Base::Base36Upper => "base36upper", + Base::Base45 => "base45", Base::Base58Flickr => "base58flickr", Base::Base58Btc => "base58btc", Base::Base64 => "base64", @@ -118,6 +119,7 @@ impl FromStr for StrBase { "base32z" => Ok(Base::Base32Z), "base36lower" => Ok(Base::Base36Lower), "base36upper" => Ok(Base::Base36Upper), + "base45" => Ok(Base::Base45), "base58flickr" => Ok(Base::Base58Flickr), "base58btc" => Ok(Base::Base58Btc), "base64" => Ok(Base::Base64), diff --git a/src/base.rs b/src/base.rs index 40b55ab..470fc65 100644 --- a/src/base.rs +++ b/src/base.rs @@ -82,6 +82,8 @@ build_base_enum! { 'k' => Base36Lower, /// Base36, [0-9A-Z] no padding (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ). 'K' => Base36Upper, + /// Base45, rfc9285 (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:). + 'R' => Base45, /// Base58 flicker (alphabet: 123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ). 'Z' => Base58Flickr, /// Base58 bitcoin (alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz). diff --git a/src/error.rs b/src/error.rs index 5fb14a6..790296c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -41,3 +41,9 @@ impl From for Error { Self::InvalidBaseString } } + +impl From for Error { + fn from(_: base45::DecodeError) -> Self { + Self::InvalidBaseString + } +} diff --git a/src/impls.rs b/src/impls.rs index fbc915a..36b3d25 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -158,3 +158,16 @@ impl BaseCodec for Base36Upper { Ok(base_x::decode(encoding::BASE36_UPPER, &uppercased)?) } } + +/// Base45, rfc9285 (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:). +pub(crate) struct Base45; + +impl BaseCodec for Base45 { + fn encode>(input: I) -> String { + base45::encode(input.as_ref()) + } + + fn decode>(input: I) -> Result> { + Ok(base45::decode(input.as_ref())?) + } +} diff --git a/tests/lib.rs b/tests/lib.rs index e0a7264..2ff01c5 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -58,6 +58,7 @@ fn test_basic() { (Base32Z, "hxf1zgedpcfzg1ebb"), (Base36Lower, "k2lcpzo5yikidynfl"), (Base36Upper, "K2LCPZO5YIKIDYNFL"), + (Base45, "RRFF.OEB$D5/DZ24"), (Base58Flickr, "Z7Pznk19XTTzBtx"), (Base58Btc, "z7paNL19xttacUY"), (Base64, "meWVzIG1hbmkgIQ"), From f66571a962fb62d1d522cfb2c0b98a97787de18e Mon Sep 17 00:00:00 2001 From: mriise Date: Sun, 23 Nov 2025 02:05:59 -0800 Subject: [PATCH 2/3] fix: add tests for base45 --- src/impls.rs | 3 ++- tests/lib.rs | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/impls.rs b/src/impls.rs index 36b3d25..a8d255e 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -168,6 +168,7 @@ impl BaseCodec for Base45 { } fn decode>(input: I) -> Result> { - Ok(base45::decode(input.as_ref())?) + let uppercased = input.as_ref().to_ascii_uppercase(); + Ok(base45::decode(&uppercased)?) } } diff --git a/tests/lib.rs b/tests/lib.rs index 2ff01c5..b222be0 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -91,6 +91,7 @@ fn preserves_leading_zero() { (Base32Z, "hybhskh3ypiosh4jyrr"), (Base36Lower, "k02lcpzo5yikidynfl"), (Base36Upper, "K02LCPZO5YIKIDYNFL"), + (Base45, "RV206$CL44CEC2DDX0"), (Base58Flickr, "Z17Pznk19XTTzBtx"), (Base58Btc, "z17paNL19xttacUY"), (Base64, "mAHllcyBtYW5pICE"), @@ -123,6 +124,7 @@ fn preserves_two_leading_zeroes() { (Base32Z, "hyyy813murbssn5ujryoo"), (Base36Lower, "k002lcpzo5yikidynfl"), (Base36Upper, "K002LCPZO5YIKIDYNFL"), + (Base45, "R000RFF.OEB$D5/DZ24"), (Base58Flickr, "Z117Pznk19XTTzBtx"), (Base58Btc, "z117paNL19xttacUY"), (Base64, "mAAB5ZXMgbWFuaSAh"), @@ -150,6 +152,7 @@ fn case_insensitivity() { (Base32HexPadUpper, "Td1imor3f41RMUSJCCG======"), (Base36Lower, "kfUvrsIvVnfRbjWaJo"), (Base36Upper, "KfUVrSIVVnFRbJWAJo"), + (Base45, "R+8d vd82ek4f.kea2"), ]; for (base, output) in test_cases { assert_eq!(decode(output).unwrap(), (base, input.to_vec())); From 858f8e72519dcc996614f14e09b84956d4608a16 Mon Sep 17 00:00:00 2001 From: mriise Date: Tue, 16 Dec 2025 14:00:25 -0800 Subject: [PATCH 3/3] chore: bump base45 version for no-std support --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 14fe937..e6ce8b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,9 +117,9 @@ dependencies = [ [[package]] name = "base45" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3103f6d6af63726e86b026ac66769fc578ea38aba33f409e5855a4def97a564" +checksum = "240e56f4d3c453c36faacb695c535a4d5f8c7d23dac175014f32eb0a71012a03" [[package]] name = "bitflags" diff --git a/Cargo.toml b/Cargo.toml index 47cda72..4fcddb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,14 +12,14 @@ keywords = ["ipld", "ipfs", "multihash", "cid", "no_std"] [features] default = ["std"] -std = ["data-encoding/std"] +std = ["data-encoding/std", "base45/std"] [dependencies] base-x = { version = "0.2.7", default-features = false } base256emoji = "1.0.2" data-encoding = { version = "2.3.1", default-features = false, features = ["alloc"] } data-encoding-macro = "0.1.9" -base45 = "3.1.0" +base45 = { version = "3.2.0", default-features = false } [dev-dependencies] criterion = "0.7"