Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved docs, fixed clippy warnings, fixed cargo fmt #42

Merged
merged 5 commits into from Jan 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a better way to do this? Apparently cargo fmt doesn't support the --check flag, so I have to use rustfmt directly and specify the files to check.

- 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