Skip to content

Commit e1e4098

Browse files
tchardingEricson2314
authored andcommitted
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 8abf91b commit e1e4098

File tree

3 files changed

+37
-6
lines changed

3 files changed

+37
-6
lines changed

.github/workflows/rust.yml

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
on: [pull_request]
23

34
name: Continuous Integration
@@ -22,7 +23,11 @@ jobs:
2223
- uses: actions-rs/cargo@v1
2324
with:
2425
command: test
25-
args: --verbose --features strict
26+
args: --verbose --no-default-features --features strict alloc
27+
- uses: actions-rs/cargo@v1
28+
with:
29+
command: test
30+
args: --verbose --no-default-features --features strict std
2631

2732
fmt:
2833
name: Rustfmt

Cargo.toml

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

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

src/lib.rs

+28-4
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,20 @@ assert_eq!(variant, Variant::Bech32);
5454
#![deny(non_camel_case_types)]
5555
#![deny(non_snake_case)]
5656
#![deny(unused_mut)]
57+
5758
#![cfg_attr(feature = "strict", deny(warnings))]
5859
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
5960

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

63-
#[cfg(all(not(feature = "std"), not(test)))]
64+
#[cfg(all(feature = "alloc", not(feature = "std")))]
6465
use alloc::borrow::Cow;
65-
#[cfg(all(not(feature = "std"), not(test)))]
66+
#[cfg(all(feature = "alloc", not(feature = "std")))]
6667
use alloc::{string::String, vec::Vec};
6768
use core::convert::Infallible;
6869
use core::{fmt, mem};
69-
#[cfg(any(feature = "std", test))]
70+
#[cfg(feature = "std")]
7071
use std::borrow::Cow;
7172

7273
/// Integer in the range `0..32`
@@ -213,6 +214,7 @@ pub trait FromBase32: Sized {
213214
fn from_base32(b32: &[u5]) -> Result<Self, Self::Err>;
214215
}
215216

217+
#[cfg(feature = "alloc")]
216218
impl WriteBase32 for Vec<u5> {
217219
type Err = Infallible;
218220

@@ -227,6 +229,7 @@ impl WriteBase32 for Vec<u5> {
227229
}
228230
}
229231

232+
#[cfg(feature = "alloc")]
230233
impl FromBase32 for Vec<u8> {
231234
type Err = Error;
232235

@@ -236,6 +239,7 @@ impl FromBase32 for Vec<u8> {
236239
}
237240

238241
/// A trait for converting a value to a type `T` that represents a `u5` slice.
242+
#[cfg(feature = "alloc")]
239243
pub trait ToBase32 {
240244
/// Convert `Self` to base32 vector
241245
fn to_base32(&self) -> Vec<u5> {
@@ -250,11 +254,13 @@ pub trait ToBase32 {
250254
}
251255

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

263+
#[cfg(feature = "alloc")]
258264
impl<T: AsRef<[u8]>> ToBase32 for T {
259265
fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
260266
// Amount of bits left over from last round, stored in buffer.
@@ -299,6 +305,7 @@ impl<T: AsRef<[u8]>> ToBase32 for T {
299305
}
300306
}
301307

308+
#[cfg(feature = "alloc")]
302309
impl<T: AsRef<[u8]>> Base32Len for T {
303310
fn base32_len(&self) -> usize {
304311
let bits = self.as_ref().len() * 8;
@@ -320,6 +327,7 @@ pub trait CheckBase32<T: AsRef<[u5]>> {
320327
fn check_base32(self) -> Result<T, Self::Err>;
321328
}
322329

330+
#[cfg(feature = "alloc")]
323331
impl<T: AsRef<[u8]>> CheckBase32<Vec<u5>> for T {
324332
type Err = Error;
325333

@@ -329,6 +337,7 @@ impl<T: AsRef<[u8]>> CheckBase32<Vec<u5>> for T {
329337
}
330338

331339
#[derive(Clone, Copy, PartialEq, Eq)]
340+
#[cfg(feature = "alloc")]
332341
enum Case {
333342
Upper,
334343
Lower,
@@ -341,6 +350,7 @@ enum Case {
341350
/// * **MixedCase**: If the HRP contains both uppercase and lowercase characters.
342351
/// * **InvalidChar**: If the HRP contains any non-ASCII characters (outside 33..=126).
343352
/// * **InvalidLength**: If the HRP is outside 1..83 characters long.
353+
#[cfg(feature = "alloc")]
344354
fn check_hrp(hrp: &str) -> Result<Case, Error> {
345355
if hrp.is_empty() || hrp.len() > 83 {
346356
return Err(Error::InvalidLength);
@@ -380,6 +390,7 @@ fn check_hrp(hrp: &str) -> Result<Case, Error> {
380390
/// * If [check_hrp] returns an error for the given HRP.
381391
/// # Deviations from standard
382392
/// * No length limits are enforced for the data part
393+
#[cfg(feature = "alloc")]
383394
pub fn encode_to_fmt<T: AsRef<[u5]>>(
384395
fmt: &mut fmt::Write,
385396
hrp: &str,
@@ -409,6 +420,7 @@ pub fn encode_to_fmt<T: AsRef<[u5]>>(
409420
/// * If [check_hrp] returns an error for the given HRP.
410421
/// # Deviations from standard
411422
/// * No length limits are enforced for the data part
423+
#[cfg(feature = "alloc")]
412424
pub fn encode_without_checksum_to_fmt<T: AsRef<[u5]>>(
413425
fmt: &mut fmt::Write,
414426
hrp: &str,
@@ -447,6 +459,7 @@ const BECH32M_CONST: u32 = 0x2bc8_30a3;
447459

448460
impl Variant {
449461
// Produce the variant based on the remainder of the polymod operation
462+
#[cfg(feature = "alloc")]
450463
fn from_remainder(c: u32) -> Option<Self> {
451464
match c {
452465
BECH32_CONST => Some(Variant::Bech32),
@@ -469,6 +482,7 @@ impl Variant {
469482
/// * If [check_hrp] returns an error for the given HRP.
470483
/// # Deviations from standard
471484
/// * No length limits are enforced for the data part
485+
#[cfg(feature = "alloc")]
472486
pub fn encode<T: AsRef<[u5]>>(hrp: &str, data: T, variant: Variant) -> Result<String, Error> {
473487
let mut buf = String::new();
474488
encode_to_fmt(&mut buf, hrp, data, variant)?.unwrap();
@@ -481,6 +495,7 @@ pub fn encode<T: AsRef<[u5]>>(hrp: &str, data: T, variant: Variant) -> Result<St
481495
/// * If [check_hrp] returns an error for the given HRP.
482496
/// # Deviations from standard
483497
/// * No length limits are enforced for the data part
498+
#[cfg(feature = "alloc")]
484499
pub fn encode_without_checksum<T: AsRef<[u5]>>(hrp: &str, data: T) -> Result<String, Error> {
485500
let mut buf = String::new();
486501
encode_without_checksum_to_fmt(&mut buf, hrp, data)?.unwrap();
@@ -490,6 +505,7 @@ pub fn encode_without_checksum<T: AsRef<[u5]>>(hrp: &str, data: T) -> Result<Str
490505
/// Decode a bech32 string into the raw HRP and the data bytes.
491506
///
492507
/// Returns the HRP in lowercase, the data with the checksum removed, and the encoding.
508+
#[cfg(feature = "alloc")]
493509
pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
494510
let (hrp_lower, mut data) = split_and_decode(s)?;
495511
if data.len() < CHECKSUM_LENGTH {
@@ -511,9 +527,11 @@ pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
511527
/// Decode a bech32 string into the raw HRP and the data bytes, assuming no checksum.
512528
///
513529
/// Returns the HRP in lowercase and the data.
530+
#[cfg(feature = "alloc")]
514531
pub fn decode_without_checksum(s: &str) -> Result<(String, Vec<u5>), Error> { split_and_decode(s) }
515532

516533
/// Decode a bech32 string into the raw HRP and the `u5` data.
534+
#[cfg(feature = "alloc")]
517535
fn split_and_decode(s: &str) -> Result<(String, Vec<u5>), Error> {
518536
// Split at separator and check for two pieces
519537
let (raw_hrp, raw_data) = match s.rfind(SEP) {
@@ -570,12 +588,14 @@ fn split_and_decode(s: &str) -> Result<(String, Vec<u5>), Error> {
570588
Ok((hrp_lower, data))
571589
}
572590

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

598+
#[cfg(feature = "alloc")]
579599
fn hrp_expand(hrp: &[u8]) -> Vec<u5> {
580600
let mut v: Vec<u5> = Vec::new();
581601
for b in hrp {
@@ -588,6 +608,7 @@ fn hrp_expand(hrp: &[u8]) -> Vec<u5> {
588608
v
589609
}
590610

611+
#[cfg(feature = "alloc")]
591612
fn polymod(values: &[u5]) -> u32 {
592613
let mut chk: u32 = 1;
593614
let mut b: u8;
@@ -616,6 +637,7 @@ const CHARSET: [char; 32] = [
616637
];
617638

618639
/// Reverse character set. Maps ASCII byte -> CHARSET index on [0,31]
640+
#[cfg(feature = "alloc")]
619641
const CHARSET_REV: [i8; 128] = [
620642
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
621643
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
@@ -690,6 +712,7 @@ impl std::error::Error for Error {
690712
/// let base5 = convert_bits(&[0xff], 8, 5, true);
691713
/// assert_eq!(base5.unwrap(), vec![0x1f, 0x1c]);
692714
/// ```
715+
#[cfg(feature = "alloc")]
693716
pub fn convert_bits<T>(data: &[T], from: u32, to: u32, pad: bool) -> Result<Vec<u8>, Error>
694717
where
695718
T: Into<u8> + Copy,
@@ -725,6 +748,7 @@ where
725748
}
726749

727750
#[cfg(test)]
751+
#[cfg(feature = "alloc")] // Note, all the unit tests currently require an allocator.
728752
mod tests {
729753
use super::*;
730754

0 commit comments

Comments
 (0)