diff --git a/.travis.yml b/.travis.yml index 11ff9939..16446d22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,14 @@ script: jobs: include: + - stage: best practices + rust: stable + install: + - rustup component add rustfmt + - rustup component add clippy + script: + - rustfmt --check src/lib.rs + - cargo clippy -- -D warnings - stage: fuzz before_install: - sudo apt-get -qq update diff --git a/src/lib.rs b/src/lib.rs index ba0777c3..b12fc764 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,18 +18,18 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -//! Encoding and decoding Bech32 format +//! Encoding and decoding of the Bech32 format //! -//! Bech32 is a 5-bit (base-32) encoding scheme that produces strings that comprise -//! a human-readable part, a separator, a data part, and a checksum. The encoding -//! implements a BCH code that guarantees error detection of up to four characters -//! with less than 1 in 1 billion chance of failing to detect more errors. +//! Bech32 is an encoding scheme that is easy to use for humans and efficient to encode in QR codes. //! -//! The Bech32 encoding was originally formulated in [BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) +//! A Bech32 string consists of a human-readable part (HRP), a separator (the character `'1'`), and a data part. +//! A checksum at the end of the string provides error detection to prevent mistakes when the string is written off or read out loud. +//! +//! The original description in [BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) has more details. //! //! # Examples //! -//! ```rust +//! ``` //! use bech32::{self, FromBase32, ToBase32}; //! //! let encoded = bech32::encode("bech32", vec![0x00, 0x01, 0x02].to_base32()).unwrap(); @@ -44,7 +44,6 @@ // Allow trait objects without dyn on nightly and make 1.22 ignore the unknown lint #![allow(unknown_lints)] #![allow(bare_trait_objects)] - #![deny(missing_docs)] #![deny(non_upper_case_globals)] #![deny(non_camel_case_types)] @@ -52,8 +51,8 @@ #![deny(unused_mut)] #![cfg_attr(feature = "strict", deny(warnings))] -use std::{error, fmt}; use std::borrow::Cow; +use std::{error, fmt}; // AsciiExt is needed for Rust 1.14 but not for newer versions #[allow(unused_imports, deprecated)] @@ -75,12 +74,12 @@ impl u5 { } /// Returns a copy of the underlying `u8` value - pub fn to_u8(&self) -> u8 { + pub fn to_u8(self) -> u8 { self.0 } /// Get char representing this 5 bit value as defined in BIP173 - pub fn to_char(&self) -> char { + pub fn to_char(self) -> char { CHARSET[self.to_u8() as usize] } } @@ -149,10 +148,11 @@ impl<'a> Bech32Writer<'a> { fn polymod_step(&mut self, v: u5) { let b = (self.chk >> 25) as u8; - self.chk = (self.chk & 0x1ffffff) << 5 ^ (u32::from(*v.as_ref())); - for i in 0..5 { + self.chk = (self.chk & 0x01ff_ffff) << 5 ^ (u32::from(*v.as_ref())); + + for (i, item) in GEN.iter().enumerate() { if (b >> i) & 1 == 1 { - self.chk ^= GEN[i] + self.chk ^= item; } } } @@ -173,9 +173,8 @@ impl<'a> Bech32Writer<'a> { let plm: u32 = self.chk ^ 1; for p in 0..6 { - self.formatter.write_char( - u5(((plm >> (5 * (5 - p))) & 0x1f) as u8).to_char() - )?; + self.formatter + .write_char(u5(((plm >> (5 * (5 - p))) & 0x1f) as u8).to_char())?; } Ok(()) @@ -193,7 +192,8 @@ impl<'a> WriteBase32 for Bech32Writer<'a> { impl<'a> Drop for Bech32Writer<'a> { fn drop(&mut self) { - self.inner_finalize().expect("Unhandled error writing the checksum on drop.") + self.inner_finalize() + .expect("Unhandled error writing the checksum on drop.") } } @@ -265,8 +265,8 @@ impl> ToBase32 for T { // buffer holds too many bits, so we don't have to combine buffer bits with new bits // from this rounds byte. if buffer_bits >= 5 { - writer.write_u5(u5((buffer & 0b11111000) >> 3))?; - buffer = buffer << 5; + writer.write_u5(u5((buffer & 0b1111_1000) >> 3))?; + buffer <<= 5; buffer_bits -= 5; } @@ -277,13 +277,13 @@ impl> ToBase32 for T { writer.write_u5(u5(from_buffer | from_byte))?; buffer = b << (5 - buffer_bits); - buffer_bits = 3 + buffer_bits; + buffer_bits += 3; } // There can be at most two u5s left in the buffer after processing all bytes, write them. if buffer_bits >= 5 { - writer.write_u5(u5((buffer & 0b11111000) >> 3))?; - buffer = buffer << 5; + writer.write_u5(u5((buffer & 0b1111_1000) >> 3))?; + buffer <<= 5; buffer_bits -= 5; } @@ -511,10 +511,11 @@ fn polymod(values: &[u5]) -> u32 { let mut b: u8; for v in values { b = (chk >> 25) as u8; - chk = (chk & 0x1ffffff) << 5 ^ (u32::from(*v.as_ref())); - for i in 0..5 { + chk = (chk & 0x01ff_ffff) << 5 ^ (u32::from(*v.as_ref())); + + for (i, item) in GEN.iter().enumerate() { if (b >> i) & 1 == 1 { - chk ^= GEN[i] + chk ^= item; } } } @@ -526,26 +527,30 @@ const SEP: char = '1'; /// Encoding character set. Maps data value -> char const CHARSET: [char; 32] = [ - 'q','p','z','r','y','9','x','8', - 'g','f','2','t','v','d','w','0', - 's','3','j','n','5','4','k','h', - 'c','e','6','m','u','a','7','l' + 'q', 'p', 'z', 'r', 'y', '9', 'x', '8', // +0 + 'g', 'f', '2', 't', 'v', 'd', 'w', '0', // +8 + 's', '3', 'j', 'n', '5', '4', 'k', 'h', // +16 + 'c', 'e', '6', 'm', 'u', 'a', '7', 'l', // +24 ]; /// Reverse character set. Maps ASCII byte -> CHARSET index on [0,31] const CHARSET_REV: [i8; 128] = [ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, - -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, - 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, - -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, - 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, + -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, -1, 29, + -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, + -1, -1, -1, -1, ]; /// Generator coefficients -const GEN: [u32; 5] = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]; +const GEN: [u32; 5] = [ + 0x3b6a_57b2, + 0x2650_8e6d, + 0x1ea1_19fa, + 0x3d42_33dd, + 0x2a14_62b3, +]; /// Error types for Bech32 encoding / decoding #[derive(Copy, Clone, PartialEq, Debug)] @@ -669,7 +674,11 @@ mod tests { for s in strings { let decode_result = decode(s); if !decode_result.is_ok() { - panic!("Did not decode: {:?} Reason: {:?}", s, decode_result.unwrap_err()); + panic!( + "Did not decode: {:?} Reason: {:?}", + s, + decode_result.unwrap_err() + ); } assert!(decode_result.is_ok()); let decoded = decode_result.unwrap(); @@ -707,7 +716,12 @@ mod tests { println!("{:?}", dec_result.unwrap()); panic!("Should be invalid: {:?}", s); } - assert_eq!(dec_result.unwrap_err(), expected_error, "testing input '{}'", s); + assert_eq!( + dec_result.unwrap_err(), + expected_error, + "testing input '{}'", + s + ); } } @@ -720,7 +734,13 @@ mod tests { (vec![0x01], 8, 8, true, vec![0x01]), (vec![0x01], 8, 4, true, vec![0x00, 0x01]), (vec![0x01], 8, 2, true, vec![0x00, 0x00, 0x00, 0x01]), - (vec![0x01], 8, 1, true, vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), + ( + vec![0x01], + 8, + 1, + true, + vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], + ), (vec![0xff], 8, 5, true, vec![0x1f, 0x1c]), (vec![0x1f, 0x1c], 5, 8, false, vec![0xff]), ]; @@ -770,7 +790,10 @@ mod tests { assert!([0u8, 1, 2, 30, 31, 255].check_base32().is_err()); assert!([1u8, 2, 3, 4].check_base32().is_ok()); - assert_eq!([30u8, 31, 35, 20].check_base32(), Err(Error::InvalidData(35))); + assert_eq!( + [30u8, 31, 35, 20].check_base32(), + Err(Error::InvalidData(35)) + ); } #[test] @@ -784,7 +807,10 @@ mod tests { #[test] fn from_base32() { use FromBase32; - assert_eq!(Vec::from_base32(&[0x1f, 0x1c].check_base32().unwrap()), Ok(vec![0xff])); + assert_eq!( + Vec::from_base32(&[0x1f, 0x1c].check_base32().unwrap()), + Ok(vec![0xff]) + ); assert_eq!( Vec::from_base32(&[0x1f, 0x1f].check_base32().unwrap()), Err(Error::InvalidPadding)