Skip to content

Commit c84d0c7

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 5ce9e66 commit c84d0c7

File tree

3 files changed

+39
-7
lines changed

3 files changed

+39
-7
lines changed

.github/workflows/rust.yml

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

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,24 +54,27 @@ 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

6364
#[cfg(any(test, feature = "std"))]
6465
extern crate core;
6566

66-
#[cfg(all(not(feature = "std"), not(test)))]
67+
#[cfg(all(feature = "alloc", not(feature = "std")))]
6768
use alloc::{string::String, vec::Vec};
6869

69-
#[cfg(all(not(feature = "std"), not(test)))]
70+
#[cfg(all(feature = "alloc", not(feature = "std")))]
7071
use alloc::borrow::Cow;
71-
#[cfg(any(feature = "std", test))]
72+
#[cfg(feature = "std")]
7273
use std::borrow::Cow;
7374

74-
use core::{convert::Infallible, fmt, mem};
75+
use core::{fmt, mem};
76+
#[cfg(feature = "alloc")]
77+
use core::convert::Infallible;
7578

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

237+
#[cfg(feature = "alloc")]
234238
impl WriteBase32 for Vec<u5> {
235239
type Err = Infallible;
236240

@@ -245,6 +249,7 @@ impl WriteBase32 for Vec<u5> {
245249
}
246250
}
247251

252+
#[cfg(feature = "alloc")]
248253
impl FromBase32 for Vec<u8> {
249254
type Err = Error;
250255

@@ -256,6 +261,7 @@ impl FromBase32 for Vec<u8> {
256261
}
257262

258263
/// A trait for converting a value to a type `T` that represents a `u5` slice.
264+
#[cfg(feature = "alloc")]
259265
pub trait ToBase32 {
260266
/// Convert `Self` to base32 vector
261267
fn to_base32(&self) -> Vec<u5> {
@@ -270,11 +276,13 @@ pub trait ToBase32 {
270276
}
271277

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

285+
#[cfg(feature = "alloc")]
278286
impl<T: AsRef<[u8]>> ToBase32 for T {
279287
fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
280288
// Amount of bits left over from last round, stored in buffer.
@@ -319,6 +327,7 @@ impl<T: AsRef<[u8]>> ToBase32 for T {
319327
}
320328
}
321329

330+
#[cfg(feature = "alloc")]
322331
impl<T: AsRef<[u8]>> Base32Len for T {
323332
fn base32_len(&self) -> usize {
324333
let bits = self.as_ref().len() * 8;
@@ -340,6 +349,7 @@ pub trait CheckBase32<T: AsRef<[u5]>> {
340349
fn check_base32(self) -> Result<T, Self::Err>;
341350
}
342351

352+
#[cfg(feature = "alloc")]
343353
impl<T: AsRef<[u8]>> CheckBase32<Vec<u5>> for T {
344354
type Err = Error;
345355

@@ -352,6 +362,7 @@ impl<T: AsRef<[u8]>> CheckBase32<Vec<u5>> for T {
352362
}
353363

354364
#[derive(Clone, Copy, PartialEq, Eq)]
365+
#[cfg(feature = "alloc")]
355366
enum Case {
356367
Upper,
357368
Lower,
@@ -364,6 +375,7 @@ enum Case {
364375
/// * **MixedCase**: If the HRP contains both uppercase and lowercase characters.
365376
/// * **InvalidChar**: If the HRP contains any non-ASCII characters (outside 33..=126).
366377
/// * **InvalidLength**: If the HRP is outside 1..83 characters long.
378+
#[cfg(feature = "alloc")]
367379
fn check_hrp(hrp: &str) -> Result<Case, Error> {
368380
if hrp.is_empty() || hrp.len() > 83 {
369381
return Err(Error::InvalidLength);
@@ -403,6 +415,7 @@ fn check_hrp(hrp: &str) -> Result<Case, Error> {
403415
/// * If [check_hrp] returns an error for the given HRP.
404416
/// # Deviations from standard
405417
/// * No length limits are enforced for the data part
418+
#[cfg(feature = "alloc")]
406419
pub fn encode_to_fmt<T: AsRef<[u5]>>(
407420
fmt: &mut fmt::Write,
408421
hrp: &str,
@@ -432,6 +445,7 @@ pub fn encode_to_fmt<T: AsRef<[u5]>>(
432445
/// * If [check_hrp] returns an error for the given HRP.
433446
/// # Deviations from standard
434447
/// * No length limits are enforced for the data part
448+
#[cfg(feature = "alloc")]
435449
pub fn encode_without_checksum_to_fmt<T: AsRef<[u5]>>(
436450
fmt: &mut fmt::Write,
437451
hrp: &str,
@@ -470,6 +484,7 @@ const BECH32M_CONST: u32 = 0x2bc8_30a3;
470484

471485
impl Variant {
472486
// Produce the variant based on the remainder of the polymod operation
487+
#[cfg(feature = "alloc")]
473488
fn from_remainder(c: u32) -> Option<Self> {
474489
match c {
475490
BECH32_CONST => Some(Variant::Bech32),
@@ -492,6 +507,7 @@ impl Variant {
492507
/// * If [check_hrp] returns an error for the given HRP.
493508
/// # Deviations from standard
494509
/// * No length limits are enforced for the data part
510+
#[cfg(feature = "alloc")]
495511
pub fn encode<T: AsRef<[u5]>>(hrp: &str, data: T, variant: Variant) -> Result<String, Error> {
496512
let mut buf = String::new();
497513
encode_to_fmt(&mut buf, hrp, data, variant)?.unwrap();
@@ -504,6 +520,7 @@ pub fn encode<T: AsRef<[u5]>>(hrp: &str, data: T, variant: Variant) -> Result<St
504520
/// * If [check_hrp] returns an error for the given HRP.
505521
/// # Deviations from standard
506522
/// * No length limits are enforced for the data part
523+
#[cfg(feature = "alloc")]
507524
pub fn encode_without_checksum<T: AsRef<[u5]>>(hrp: &str, data: T) -> Result<String, Error> {
508525
let mut buf = String::new();
509526
encode_without_checksum_to_fmt(&mut buf, hrp, data)?.unwrap();
@@ -513,6 +530,7 @@ pub fn encode_without_checksum<T: AsRef<[u5]>>(hrp: &str, data: T) -> Result<Str
513530
/// Decode a bech32 string into the raw HRP and the data bytes.
514531
///
515532
/// Returns the HRP in lowercase, the data with the checksum removed, and the encoding.
533+
#[cfg(feature = "alloc")]
516534
pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
517535
let (hrp_lower, mut data) = split_and_decode(s)?;
518536
if data.len() < CHECKSUM_LENGTH {
@@ -534,11 +552,13 @@ pub fn decode(s: &str) -> Result<(String, Vec<u5>, Variant), Error> {
534552
/// Decode a bech32 string into the raw HRP and the data bytes, assuming no checksum.
535553
///
536554
/// Returns the HRP in lowercase and the data.
555+
#[cfg(feature = "alloc")]
537556
pub fn decode_without_checksum(s: &str) -> Result<(String, Vec<u5>), Error> {
538557
split_and_decode(s)
539558
}
540559

541560
/// Decode a bech32 string into the raw HRP and the `u5` data.
561+
#[cfg(feature = "alloc")]
542562
fn split_and_decode(s: &str) -> Result<(String, Vec<u5>), Error> {
543563
// Split at separator and check for two pieces
544564
let (raw_hrp, raw_data) = match s.rfind(SEP) {
@@ -595,12 +615,14 @@ fn split_and_decode(s: &str) -> Result<(String, Vec<u5>), Error> {
595615
Ok((hrp_lower, data))
596616
}
597617

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

625+
#[cfg(feature = "alloc")]
604626
fn hrp_expand(hrp: &[u8]) -> Vec<u5> {
605627
let mut v: Vec<u5> = Vec::new();
606628
for b in hrp {
@@ -613,6 +635,7 @@ fn hrp_expand(hrp: &[u8]) -> Vec<u5> {
613635
v
614636
}
615637

638+
#[cfg(feature = "alloc")]
616639
fn polymod(values: &[u5]) -> u32 {
617640
let mut chk: u32 = 1;
618641
let mut b: u8;
@@ -641,6 +664,7 @@ const CHARSET: [char; 32] = [
641664
];
642665

643666
/// Reverse character set. Maps ASCII byte -> CHARSET index on [0,31]
667+
#[cfg(feature = "alloc")]
644668
const CHARSET_REV: [i8; 128] = [
645669
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
646670
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
@@ -721,6 +745,7 @@ impl std::error::Error for Error {
721745
/// let base5 = convert_bits(&[0xff], 8, 5, true);
722746
/// assert_eq!(base5.unwrap(), vec![0x1f, 0x1c]);
723747
/// ```
748+
#[cfg(feature = "alloc")]
724749
pub fn convert_bits<T>(data: &[T], from: u32, to: u32, pad: bool) -> Result<Vec<u8>, Error>
725750
where
726751
T: Into<u8> + Copy,
@@ -756,6 +781,7 @@ where
756781
}
757782

758783
#[cfg(test)]
784+
#[cfg(feature = "alloc")] // Note, all the unit tests currently require an allocator.
759785
mod tests {
760786
use super::*;
761787

0 commit comments

Comments
 (0)