Skip to content

Commit 9327ea8

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 51b030f commit 9327ea8

File tree

3 files changed

+40
-7
lines changed

3 files changed

+40
-7
lines changed

.github/workflows/rust.yml

Lines changed: 6 additions & 1 deletion
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

Lines changed: 3 additions & 1 deletion
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

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,24 @@ 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::{string::String, vec::Vec};
6566

66-
#[cfg(all(not(feature = "std"), not(test)))]
67+
#[cfg(all(feature = "alloc", not(feature = "std")))]
6768
use alloc::borrow::Cow;
68-
#[cfg(any(feature = "std", test))]
69+
#[cfg(feature = "std")]
6970
use std::borrow::Cow;
7071

71-
use core::{convert::Infallible, fmt, mem};
72+
use core::{fmt, mem};
73+
#[cfg(feature = "alloc")]
74+
use core::convert::Infallible;
7275

7376
/// Integer in the range `0..32`
7477
#[derive(PartialEq, Eq, Debug, Copy, Clone, Default, PartialOrd, Ord, Hash)]
@@ -228,6 +231,7 @@ pub trait FromBase32: Sized {
228231
fn from_base32(b32: &[u5]) -> Result<Self, Self::Err>;
229232
}
230233

234+
#[cfg(feature = "alloc")]
231235
impl WriteBase32 for Vec<u5> {
232236
type Err = Infallible;
233237

@@ -242,6 +246,7 @@ impl WriteBase32 for Vec<u5> {
242246
}
243247
}
244248

249+
#[cfg(feature = "alloc")]
245250
impl FromBase32 for Vec<u8> {
246251
type Err = Error;
247252

@@ -253,6 +258,7 @@ impl FromBase32 for Vec<u8> {
253258
}
254259

255260
/// A trait for converting a value to a type `T` that represents a `u5` slice.
261+
#[cfg(feature = "alloc")]
256262
pub trait ToBase32 {
257263
/// Convert `Self` to base32 vector
258264
fn to_base32(&self) -> Vec<u5> {
@@ -267,11 +273,13 @@ pub trait ToBase32 {
267273
}
268274

269275
/// Interface to calculate the length of the base32 representation before actually serializing
276+
#[cfg(feature = "alloc")]
270277
pub trait Base32Len: ToBase32 {
271278
/// Calculate the base32 serialized length
272279
fn base32_len(&self) -> usize;
273280
}
274281

282+
#[cfg(feature = "alloc")]
275283
impl<T: AsRef<[u8]>> ToBase32 for T {
276284
fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
277285
// Amount of bits left over from last round, stored in buffer.
@@ -316,6 +324,7 @@ impl<T: AsRef<[u8]>> ToBase32 for T {
316324
}
317325
}
318326

327+
#[cfg(feature = "alloc")]
319328
impl<T: AsRef<[u8]>> Base32Len for T {
320329
fn base32_len(&self) -> usize {
321330
let bits = self.as_ref().len() * 8;
@@ -337,6 +346,7 @@ pub trait CheckBase32<T: AsRef<[u5]>> {
337346
fn check_base32(self) -> Result<T, Self::Err>;
338347
}
339348

349+
#[cfg(feature = "alloc")]
340350
impl<T: AsRef<[u8]>> CheckBase32<Vec<u5>> for T {
341351
type Err = Error;
342352

@@ -349,6 +359,7 @@ impl<T: AsRef<[u8]>> CheckBase32<Vec<u5>> for T {
349359
}
350360

351361
#[derive(Clone, Copy, PartialEq, Eq)]
362+
#[cfg(feature = "alloc")]
352363
enum Case {
353364
Upper,
354365
Lower,
@@ -361,6 +372,7 @@ enum Case {
361372
/// * **MixedCase**: If the HRP contains both uppercase and lowercase characters.
362373
/// * **InvalidChar**: If the HRP contains any non-ASCII characters (outside 33..=126).
363374
/// * **InvalidLength**: If the HRP is outside 1..83 characters long.
375+
#[cfg(feature = "alloc")]
364376
fn check_hrp(hrp: &str) -> Result<Case, Error> {
365377
if hrp.is_empty() || hrp.len() > 83 {
366378
return Err(Error::InvalidLength);
@@ -400,6 +412,7 @@ fn check_hrp(hrp: &str) -> Result<Case, Error> {
400412
/// * If [check_hrp] returns an error for the given HRP.
401413
/// # Deviations from standard
402414
/// * No length limits are enforced for the data part
415+
#[cfg(feature = "alloc")]
403416
pub fn encode_to_fmt<T: AsRef<[u5]>>(
404417
fmt: &mut fmt::Write,
405418
hrp: &str,
@@ -429,6 +442,7 @@ pub fn encode_to_fmt<T: AsRef<[u5]>>(
429442
/// * If [check_hrp] returns an error for the given HRP.
430443
/// # Deviations from standard
431444
/// * No length limits are enforced for the data part
445+
#[cfg(feature = "alloc")]
432446
pub fn encode_without_checksum_to_fmt<T: AsRef<[u5]>>(
433447
fmt: &mut fmt::Write,
434448
hrp: &str,
@@ -467,6 +481,7 @@ const BECH32M_CONST: u32 = 0x2bc8_30a3;
467481

468482
impl Variant {
469483
// Produce the variant based on the remainder of the polymod operation
484+
#[cfg(feature = "alloc")]
470485
fn from_remainder(c: u32) -> Option<Self> {
471486
match c {
472487
BECH32_CONST => Some(Variant::Bech32),
@@ -489,6 +504,7 @@ impl Variant {
489504
/// * If [check_hrp] returns an error for the given HRP.
490505
/// # Deviations from standard
491506
/// * No length limits are enforced for the data part
507+
#[cfg(feature = "alloc")]
492508
pub fn encode<T: AsRef<[u5]>>(hrp: &str, data: T, variant: Variant) -> Result<String, Error> {
493509
let mut buf = String::new();
494510
encode_to_fmt(&mut buf, hrp, data, variant)?.unwrap();
@@ -501,6 +517,7 @@ pub fn encode<T: AsRef<[u5]>>(hrp: &str, data: T, variant: Variant) -> Result<St
501517
/// * If [check_hrp] returns an error for the given HRP.
502518
/// # Deviations from standard
503519
/// * No length limits are enforced for the data part
520+
#[cfg(feature = "alloc")]
504521
pub fn encode_without_checksum<T: AsRef<[u5]>>(hrp: &str, data: T) -> Result<String, Error> {
505522
let mut buf = String::new();
506523
encode_without_checksum_to_fmt(&mut buf, hrp, data)?.unwrap();
@@ -510,6 +527,7 @@ pub fn encode_without_checksum<T: AsRef<[u5]>>(hrp: &str, data: T) -> Result<Str
510527
/// Decode a bech32 string into the raw HRP and the data bytes.
511528
///
512529
/// Returns the HRP in lowercase, the data with the checksum removed, and the encoding.
530+
#[cfg(feature = "alloc")]
513531
pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
514532
let (hrp_lower, mut data) = split_and_decode(s)?;
515533
if data.len() < CHECKSUM_LENGTH {
@@ -531,11 +549,13 @@ pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
531549
/// Decode a bech32 string into the raw HRP and the data bytes, assuming no checksum.
532550
///
533551
/// Returns the HRP in lowercase and the data.
552+
#[cfg(feature = "alloc")]
534553
pub fn decode_without_checksum(s: &str) -> Result<(String, Vec<u5>), Error> {
535554
split_and_decode(s)
536555
}
537556

538557
/// Decode a bech32 string into the raw HRP and the `u5` data.
558+
#[cfg(feature = "alloc")]
539559
fn split_and_decode(s: &str) -> Result<(String, Vec<u5>), Error> {
540560
// Split at separator and check for two pieces
541561
let (raw_hrp, raw_data) = match s.rfind(SEP) {
@@ -592,12 +612,14 @@ fn split_and_decode(s: &str) -> Result<(String, Vec<u5>), Error> {
592612
Ok((hrp_lower, data))
593613
}
594614

615+
#[cfg(feature = "alloc")]
595616
fn verify_checksum(hrp: &[u8], data: &[u5]) -> Option<Variant> {
596617
let mut exp = hrp_expand(hrp);
597618
exp.extend_from_slice(data);
598619
Variant::from_remainder(polymod(&exp))
599620
}
600621

622+
#[cfg(feature = "alloc")]
601623
fn hrp_expand(hrp: &[u8]) -> Vec<u5> {
602624
let mut v: Vec<u5> = Vec::new();
603625
for b in hrp {
@@ -610,6 +632,7 @@ fn hrp_expand(hrp: &[u8]) -> Vec<u5> {
610632
v
611633
}
612634

635+
#[cfg(feature = "alloc")]
613636
fn polymod(values: &[u5]) -> u32 {
614637
let mut chk: u32 = 1;
615638
let mut b: u8;
@@ -638,6 +661,7 @@ const CHARSET: [char; 32] = [
638661
];
639662

640663
/// Reverse character set. Maps ASCII byte -> CHARSET index on [0,31]
664+
#[cfg(feature = "alloc")]
641665
const CHARSET_REV: [i8; 128] = [
642666
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
643667
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
@@ -718,6 +742,7 @@ impl std::error::Error for Error {
718742
/// let base5 = convert_bits(&[0xff], 8, 5, true);
719743
/// assert_eq!(base5.unwrap(), vec![0x1f, 0x1c]);
720744
/// ```
745+
#[cfg(feature = "alloc")]
721746
pub fn convert_bits<T>(data: &[T], from: u32, to: u32, pad: bool) -> Result<Vec<u8>, Error>
722747
where
723748
T: Into<u8> + Copy,
@@ -753,6 +778,7 @@ where
753778
}
754779

755780
#[cfg(test)]
781+
#[cfg(feature = "alloc")] // Note, all the unit tests currently require an allocator.
756782
mod tests {
757783
use super::*;
758784

0 commit comments

Comments
 (0)