From 62ddd18cf0b2e1e8128a1cc0d72767975fc62dec Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 11 Feb 2022 18:56:45 -0500 Subject: [PATCH 1/3] Avoid failure with cargo test --all-targets --no-default-features Annotate the parts of the tests that require the std feature. This is an initial (but not complete) cleanup for #2. A proper fix would include new tests for the low level interface as well. --- src/decode_impl.rs | 4 ++++ src/encode_impl.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/decode_impl.rs b/src/decode_impl.rs index 0963a0c..fc4c339 100644 --- a/src/decode_impl.rs +++ b/src/decode_impl.rs @@ -261,9 +261,11 @@ pub fn decode(input: &str, output: &mut Vec, bits: u64) -> Result<(), ZBase3 #[cfg(test)] mod tests { + #[cfg(feature = "std")] use super::decode; use crate::test_data::{TestCase, RANDOM_TEST_DATA, STANDARD_TEST_DATA}; + #[cfg(feature = "std")] fn run_tests(test_cases: &[TestCase]) { let mut buffer = Vec::new(); for test in test_cases { @@ -274,11 +276,13 @@ mod tests { } #[test] + #[cfg(feature = "std")] fn test_decode_standard() { run_tests(STANDARD_TEST_DATA); } #[test] + #[cfg(feature = "std")] fn test_decode_random() { run_tests(RANDOM_TEST_DATA); } diff --git a/src/encode_impl.rs b/src/encode_impl.rs index 49aab2d..d0f9760 100644 --- a/src/encode_impl.rs +++ b/src/encode_impl.rs @@ -250,9 +250,11 @@ pub fn encode(input: &[u8], output: &mut String, bits: u64) -> Result<(), ZBase3 #[cfg(test)] mod tests { + #[cfg(feature = "std")] use super::encode; use crate::test_data::{TestCase, RANDOM_TEST_DATA, STANDARD_TEST_DATA}; + #[cfg(feature = "std")] fn run_tests(test_cases: &[TestCase]) { let mut buffer = String::new(); for test in test_cases { @@ -263,11 +265,13 @@ mod tests { } #[test] + #[cfg(feature = "std")] fn test_encode_standard() { run_tests(STANDARD_TEST_DATA); } #[test] + #[cfg(feature = "std")] fn test_encode_random() { run_tests(RANDOM_TEST_DATA); } From 340f90e09e2e3d3f6acce7feaa532d1054e4dd4d Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 11 Feb 2022 19:56:42 -0500 Subject: [PATCH 2/3] Add high-level encode_full_bytes interface This is a much simpler function for a caller that knows they're always dealing with ranges of full bytes. Closes: #1 --- src/encode_impl.rs | 37 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 15 ++++++++++++--- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/encode_impl.rs b/src/encode_impl.rs index d0f9760..1139ade 100644 --- a/src/encode_impl.rs +++ b/src/encode_impl.rs @@ -248,12 +248,37 @@ pub fn encode(input: &[u8], output: &mut String, bits: u64) -> Result<(), ZBase3 Ok(()) } +/// Encode a whole number of octets (bytes) to a [`String`]. +/// +/// If you need a number of bits that is not a multiple of 8 (that is, +/// not a whole number of bytes), or you need to append to an existing +/// string, use [`encode`] instead. +/// +/// This method is not available in `no_std` mode. +#[cfg(feature = "std")] +pub fn encode_full_bytes(input: &[u8]) -> String { + let mut output = String::from(""); + encode(input, &mut output, input.len() as u64 * 8).unwrap(); + output +} + #[cfg(test)] mod tests { #[cfg(feature = "std")] use super::encode; + #[cfg(feature = "std")] + use super::encode_full_bytes; use crate::test_data::{TestCase, RANDOM_TEST_DATA, STANDARD_TEST_DATA}; + #[cfg(feature = "std")] + fn run_full_bytes_tests(test_cases: &[TestCase]) { + for test in test_cases { + if (test.bits % 8) == 0 { + assert_eq!(String::from(test.encoded), encode_full_bytes(test.unencoded)); + } + } + } + #[cfg(feature = "std")] fn run_tests(test_cases: &[TestCase]) { let mut buffer = String::new(); @@ -275,4 +300,16 @@ mod tests { fn test_encode_random() { run_tests(RANDOM_TEST_DATA); } + + #[test] + #[cfg(feature = "std")] + fn test_encode_full_bytes_standard() { + run_full_bytes_tests(STANDARD_TEST_DATA); + } + + #[test] + #[cfg(feature = "std")] + fn test_encode_full_bytes_random() { + run_full_bytes_tests(RANDOM_TEST_DATA); + } } diff --git a/src/lib.rs b/src/lib.rs index cd5b499..7151450 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,17 +50,26 @@ //! //! ## High-level API //! -//! The high-level API consists of the functions [`encode`] and -//! its reverse, [`decode`]. +//! The high-level API consists of two encoding functions +//! [`encode_full_bytes`] and [`encode`]; and a decoding function +//! [`decode`]. +//! +//! [`encode_full_bytes`] is simple to use when you know that you have +//! a whole number of bytes to encode and you want to produce a new +//! String. [`encode`] can handle numbers of bits that are not +//! divisible by 8, and can also append to an existing String. //! //! Example: //! //! ``` -//! use libzbase32::{ZBase32Error, encode, decode}; +//! use libzbase32::{ZBase32Error, encode, encode_full_bytes, decode}; //! //! # fn main() { //! const DATA: &'static [u8] = &[0, 44, 55, 128]; //! +//! let full_bytes_encoded = encode_full_bytes(DATA); +//! assert_eq!(&full_bytes_encoded, "yysdxyy"); +//! //! let mut encoded = String::new(); //! encode(DATA, &mut encoded, 25).expect("Encoding failed!"); //! From bde7459c8ad3533e5550fd48d3d0f98712fc19cd Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 11 Feb 2022 21:03:40 -0500 Subject: [PATCH 3/3] Add decode_full_bytes by analogy with encode_full_bytes This rounds out the ergonomic very high-level API. --- src/decode_impl.rs | 39 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 16 ++++++++++------ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/decode_impl.rs b/src/decode_impl.rs index fc4c339..abb22b9 100644 --- a/src/decode_impl.rs +++ b/src/decode_impl.rs @@ -259,12 +259,39 @@ pub fn decode(input: &str, output: &mut Vec, bits: u64) -> Result<(), ZBase3 Ok(()) } +/// Decode a slice of characters to a [`Vec`] of octets (bytes). +/// +/// It decodes to the longest full octet string, and fails if any +/// left-over bits from the encoded form are non-zero. +/// +/// This method is not available in `no_std` mode. +#[cfg(feature = "std")] +pub fn decode_full_bytes(input: &str) -> Result, ZBase32Error> { + let bits:u64 = ((input.len() as u64 *5)/8)*8; + let mut output:Vec = Vec::new(); + match decode(input, &mut output, bits) { + Ok(_) => Ok(output), + Err(e) => Err(e), + } +} + #[cfg(test)] mod tests { #[cfg(feature = "std")] use super::decode; + #[cfg(feature = "std")] + use super::decode_full_bytes; use crate::test_data::{TestCase, RANDOM_TEST_DATA, STANDARD_TEST_DATA}; + #[cfg(feature = "std")] + fn run_full_bytes_tests(test_cases: &[TestCase]) { + for test in test_cases { + if (test.bits % 8) == 0 { + assert_eq!(test.unencoded, decode_full_bytes(test.encoded).unwrap()); + } + } + } + #[cfg(feature = "std")] fn run_tests(test_cases: &[TestCase]) { let mut buffer = Vec::new(); @@ -286,4 +313,16 @@ mod tests { fn test_decode_random() { run_tests(RANDOM_TEST_DATA); } + + #[test] + #[cfg(feature = "std")] + fn test_decode_full_bytes_standard() { + run_full_bytes_tests(STANDARD_TEST_DATA); + } + + #[test] + #[cfg(feature = "std")] + fn test_decode_full_bytes_random() { + run_full_bytes_tests(RANDOM_TEST_DATA); + } } diff --git a/src/lib.rs b/src/lib.rs index 7151450..d3d4923 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,13 +51,14 @@ //! ## High-level API //! //! The high-level API consists of two encoding functions -//! [`encode_full_bytes`] and [`encode`]; and a decoding function -//! [`decode`]. +//! [`encode_full_bytes`] and [`encode`]; and two decoding functions +//! [`decode_full_bytes`] and [`decode`]. //! -//! [`encode_full_bytes`] is simple to use when you know that you have -//! a whole number of bytes to encode and you want to produce a new -//! String. [`encode`] can handle numbers of bits that are not -//! divisible by 8, and can also append to an existing String. +//! [`encode_full_bytes`] and [`decode_full_bytes`] are simple to use +//! when you know that the unencoded bytestring is a whole number of +//! bytes. [`encode`] and [`decode`] can also handle numbers of +//! unencoded bits that are not divisible by 8, and can also append +//! their results to existing variables. //! //! Example: //! @@ -70,6 +71,9 @@ //! let full_bytes_encoded = encode_full_bytes(DATA); //! assert_eq!(&full_bytes_encoded, "yysdxyy"); //! +//! let full_bytes_decoded = decode_full_bytes(full_bytes_encoded); +//! assert_eq!(&full_bytes_decoded, DATA); +//! //! let mut encoded = String::new(); //! encode(DATA, &mut encoded, 25).expect("Encoding failed!"); //!