Skip to content

Commit cdbaa3e

Browse files
committed
Add "alloc" feature
We would like users to be able to use parts of this library in a `no_std` environment without an allocator. To achieve this add an "alloc" feature and feature gate any code that requires allocation behind "alloc"/"std". Update the CI test job to run the test with each feature on its own.
1 parent 95e2444 commit cdbaa3e

File tree

3 files changed

+36
-6
lines changed

3 files changed

+36
-6
lines changed

.github/workflows/rust.yml

+5-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ jobs:
2222
- uses: actions-rs/cargo@v1
2323
with:
2424
command: test
25-
args: --verbose --features strict
25+
args: --verbose --no-default-features --features strict alloc
26+
- uses: actions-rs/cargo@v1
27+
with:
28+
command: test
29+
args: --verbose --no-default-features --features strict std
2630

2731
fmt:
2832
name: Rustfmt

Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ edition = "2018"
1313

1414
[features]
1515
default = ["std"]
16-
std = []
16+
std = ["alloc"]
17+
alloc = []
18+
1719
# Only for CI to make all warnings errors, do not activate otherwise (may break forward compatibility)
1820
strict = []
1921

src/lib.rs

+28-4
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,20 @@ assert_eq!(variant, Variant::Bech32);
5757
#![cfg_attr(feature = "strict", deny(warnings))]
5858
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
5959

60-
#[cfg(all(not(feature = "std"), not(test)))]
60+
#[cfg(feature = "alloc")]
6161
extern crate alloc;
6262

6363
#[cfg(any(test, feature = "std"))]
6464
extern crate core;
6565

66-
#[cfg(all(not(feature = "std"), not(test)))]
66+
#[cfg(all(feature = "alloc", not(feature = "std")))]
6767
use alloc::borrow::Cow;
68-
#[cfg(all(not(feature = "std"), not(test)))]
68+
#[cfg(all(feature = "alloc", not(feature = "std")))]
6969
use alloc::{string::String, vec::Vec};
70+
#[cfg(feature = "alloc")]
7071
use core::convert::Infallible;
7172
use core::{fmt, mem};
72-
#[cfg(any(feature = "std", test))]
73+
#[cfg(feature = "std")]
7374
use std::borrow::Cow;
7475

7576
/// Integer in the range `0..32`
@@ -216,6 +217,7 @@ pub trait FromBase32: Sized {
216217
fn from_base32(b32: &[u5]) -> Result<Self, Self::Err>;
217218
}
218219

220+
#[cfg(feature = "alloc")]
219221
impl WriteBase32 for Vec<u5> {
220222
type Err = Infallible;
221223

@@ -230,6 +232,7 @@ impl WriteBase32 for Vec<u5> {
230232
}
231233
}
232234

235+
#[cfg(feature = "alloc")]
233236
impl FromBase32 for Vec<u8> {
234237
type Err = Error;
235238

@@ -239,6 +242,7 @@ impl FromBase32 for Vec<u8> {
239242
}
240243

241244
/// A trait for converting a value to a type `T` that represents a `u5` slice.
245+
#[cfg(feature = "alloc")]
242246
pub trait ToBase32 {
243247
/// Convert `Self` to base32 vector
244248
fn to_base32(&self) -> Vec<u5> {
@@ -253,11 +257,13 @@ pub trait ToBase32 {
253257
}
254258

255259
/// Interface to calculate the length of the base32 representation before actually serializing
260+
#[cfg(feature = "alloc")]
256261
pub trait Base32Len: ToBase32 {
257262
/// Calculate the base32 serialized length
258263
fn base32_len(&self) -> usize;
259264
}
260265

266+
#[cfg(feature = "alloc")]
261267
impl<T: AsRef<[u8]>> ToBase32 for T {
262268
fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
263269
// Amount of bits left over from last round, stored in buffer.
@@ -302,6 +308,7 @@ impl<T: AsRef<[u8]>> ToBase32 for T {
302308
}
303309
}
304310

311+
#[cfg(feature = "alloc")]
305312
impl<T: AsRef<[u8]>> Base32Len for T {
306313
fn base32_len(&self) -> usize {
307314
let bits = self.as_ref().len() * 8;
@@ -323,6 +330,7 @@ pub trait CheckBase32<T: AsRef<[u5]>> {
323330
fn check_base32(self) -> Result<T, Self::Err>;
324331
}
325332

333+
#[cfg(feature = "alloc")]
326334
impl<T: AsRef<[u8]>> CheckBase32<Vec<u5>> for T {
327335
type Err = Error;
328336

@@ -332,6 +340,7 @@ impl<T: AsRef<[u8]>> CheckBase32<Vec<u5>> for T {
332340
}
333341

334342
#[derive(Clone, Copy, PartialEq, Eq)]
343+
#[cfg(feature = "alloc")]
335344
enum Case {
336345
Upper,
337346
Lower,
@@ -344,6 +353,7 @@ enum Case {
344353
/// * **MixedCase**: If the HRP contains both uppercase and lowercase characters.
345354
/// * **InvalidChar**: If the HRP contains any non-ASCII characters (outside 33..=126).
346355
/// * **InvalidLength**: If the HRP is outside 1..83 characters long.
356+
#[cfg(feature = "alloc")]
347357
fn check_hrp(hrp: &str) -> Result<Case, Error> {
348358
if hrp.is_empty() || hrp.len() > 83 {
349359
return Err(Error::InvalidLength);
@@ -383,6 +393,7 @@ fn check_hrp(hrp: &str) -> Result<Case, Error> {
383393
/// * If [check_hrp] returns an error for the given HRP.
384394
/// # Deviations from standard
385395
/// * No length limits are enforced for the data part
396+
#[cfg(feature = "alloc")]
386397
pub fn encode_to_fmt<T: AsRef<[u5]>>(
387398
fmt: &mut fmt::Write,
388399
hrp: &str,
@@ -412,6 +423,7 @@ pub fn encode_to_fmt<T: AsRef<[u5]>>(
412423
/// * If [check_hrp] returns an error for the given HRP.
413424
/// # Deviations from standard
414425
/// * No length limits are enforced for the data part
426+
#[cfg(feature = "alloc")]
415427
pub fn encode_without_checksum_to_fmt<T: AsRef<[u5]>>(
416428
fmt: &mut fmt::Write,
417429
hrp: &str,
@@ -450,6 +462,7 @@ const BECH32M_CONST: u32 = 0x2bc8_30a3;
450462

451463
impl Variant {
452464
// Produce the variant based on the remainder of the polymod operation
465+
#[cfg(feature = "alloc")]
453466
fn from_remainder(c: u32) -> Option<Self> {
454467
match c {
455468
BECH32_CONST => Some(Variant::Bech32),
@@ -472,6 +485,7 @@ impl Variant {
472485
/// * If [check_hrp] returns an error for the given HRP.
473486
/// # Deviations from standard
474487
/// * No length limits are enforced for the data part
488+
#[cfg(feature = "alloc")]
475489
pub fn encode<T: AsRef<[u5]>>(hrp: &str, data: T, variant: Variant) -> Result<String, Error> {
476490
let mut buf = String::new();
477491
encode_to_fmt(&mut buf, hrp, data, variant)?.unwrap();
@@ -484,6 +498,7 @@ pub fn encode<T: AsRef<[u5]>>(hrp: &str, data: T, variant: Variant) -> Result<St
484498
/// * If [check_hrp] returns an error for the given HRP.
485499
/// # Deviations from standard
486500
/// * No length limits are enforced for the data part
501+
#[cfg(feature = "alloc")]
487502
pub fn encode_without_checksum<T: AsRef<[u5]>>(hrp: &str, data: T) -> Result<String, Error> {
488503
let mut buf = String::new();
489504
encode_without_checksum_to_fmt(&mut buf, hrp, data)?.unwrap();
@@ -493,6 +508,7 @@ pub fn encode_without_checksum<T: AsRef<[u5]>>(hrp: &str, data: T) -> Result<Str
493508
/// Decode a bech32 string into the raw HRP and the data bytes.
494509
///
495510
/// Returns the HRP in lowercase, the data with the checksum removed, and the encoding.
511+
#[cfg(feature = "alloc")]
496512
pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
497513
let (hrp_lower, mut data) = split_and_decode(s)?;
498514
if data.len() < CHECKSUM_LENGTH {
@@ -514,9 +530,11 @@ pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
514530
/// Decode a bech32 string into the raw HRP and the data bytes, assuming no checksum.
515531
///
516532
/// Returns the HRP in lowercase and the data.
533+
#[cfg(feature = "alloc")]
517534
pub fn decode_without_checksum(s: &str) -> Result<(String, Vec<u5>), Error> { split_and_decode(s) }
518535

519536
/// Decode a bech32 string into the raw HRP and the `u5` data.
537+
#[cfg(feature = "alloc")]
520538
fn split_and_decode(s: &str) -> Result<(String, Vec<u5>), Error> {
521539
// Split at separator and check for two pieces
522540
let (raw_hrp, raw_data) = match s.rfind(SEP) {
@@ -573,12 +591,14 @@ fn split_and_decode(s: &str) -> Result<(String, Vec<u5>), Error> {
573591
Ok((hrp_lower, data))
574592
}
575593

594+
#[cfg(feature = "alloc")]
576595
fn verify_checksum(hrp: &[u8], data: &[u5]) -> Option<Variant> {
577596
let mut exp = hrp_expand(hrp);
578597
exp.extend_from_slice(data);
579598
Variant::from_remainder(polymod(&exp))
580599
}
581600

601+
#[cfg(feature = "alloc")]
582602
fn hrp_expand(hrp: &[u8]) -> Vec<u5> {
583603
let mut v: Vec<u5> = Vec::new();
584604
for b in hrp {
@@ -591,6 +611,7 @@ fn hrp_expand(hrp: &[u8]) -> Vec<u5> {
591611
v
592612
}
593613

614+
#[cfg(feature = "alloc")]
594615
fn polymod(values: &[u5]) -> u32 {
595616
let mut chk: u32 = 1;
596617
let mut b: u8;
@@ -619,6 +640,7 @@ const CHARSET: [char; 32] = [
619640
];
620641

621642
/// Reverse character set. Maps ASCII byte -> CHARSET index on [0,31]
643+
#[cfg(feature = "alloc")]
622644
const CHARSET_REV: [i8; 128] = [
623645
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
624646
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
@@ -693,6 +715,7 @@ impl std::error::Error for Error {
693715
/// let base5 = convert_bits(&[0xff], 8, 5, true);
694716
/// assert_eq!(base5.unwrap(), vec![0x1f, 0x1c]);
695717
/// ```
718+
#[cfg(feature = "alloc")]
696719
pub fn convert_bits<T>(data: &[T], from: u32, to: u32, pad: bool) -> Result<Vec<u8>, Error>
697720
where
698721
T: Into<u8> + Copy,
@@ -728,6 +751,7 @@ where
728751
}
729752

730753
#[cfg(test)]
754+
#[cfg(feature = "alloc")] // Note, all the unit tests currently require an allocator.
731755
mod tests {
732756
use super::*;
733757

0 commit comments

Comments
 (0)