Skip to content

Commit

Permalink
Improved docs, fixed clippy warnings, fixed cargo fmt (#42)
Browse files Browse the repository at this point in the history
* Documentation

* why is Bech32 used?
* mention "1" as separator
* refer explicitly to original description *for more details*

* cargo clippy

* rustfmt

* removed rustfmt::skip

* add automatic travis checks for formatting/clippy

Co-authored-by: Sebastian <geisler.sebastian@googlemail.com>
  • Loading branch information
2 people authored and clarkmoody committed Jan 12, 2020
1 parent ccbb003 commit a565895
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 44 deletions.
8 changes: 8 additions & 0 deletions .travis.yml
Expand Up @@ -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
Expand Down
114 changes: 70 additions & 44 deletions src/lib.rs
Expand Up @@ -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();
Expand All @@ -44,16 +44,15 @@
// 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)]
#![deny(non_snake_case)]
#![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)]
Expand All @@ -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]
}
}
Expand Down Expand Up @@ -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;
}
}
}
Expand All @@ -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(())
Expand All @@ -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.")
}
}

Expand Down Expand Up @@ -265,8 +265,8 @@ impl<T: AsRef<[u8]>> 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;
}

Expand All @@ -277,13 +277,13 @@ impl<T: AsRef<[u8]>> 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;
}

Expand Down Expand Up @@ -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;
}
}
}
Expand All @@ -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)]
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
);
}
}

Expand All @@ -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]),
];
Expand Down Expand Up @@ -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]
Expand All @@ -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)
Expand Down

0 comments on commit a565895

Please sign in to comment.