Skip to content

Commit

Permalink
Merge #121: primitives: Inline public functions
Browse files Browse the repository at this point in the history
847e1c9 primitives: Inline public functions (Tobin C. Harding)

Pull request description:

  Audit the `primitives` module and all submodules; add inline to all public methods that are small enough (excl. error impls).

ACKs for top commit:
  apoelstra:
    ACK 847e1c9
  clarkmoody:
    ACK 847e1c9

Tree-SHA512: 0590cef81d1d909cd1523cc5bffd9b15c6b28450e3d40c2a5028246a1ed15086c39304c3de7de37733dd80b3e6da7ebc4f54bfa74af3d27134876164cff49445
  • Loading branch information
apoelstra committed Aug 15, 2023
2 parents 7c800fc + 847e1c9 commit 72e7dd8
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/primitives/checksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,11 @@ impl<Ck: Checksum> Default for Engine<Ck> {

impl<Ck: Checksum> Engine<Ck> {
/// Constructs a new checksum engine with no data input.
#[inline]
pub fn new() -> Self { Engine { residue: Ck::MidstateRepr::ONE } }

/// Feeds `hrp` into the checksum engine.
#[inline]
pub fn input_hrp(&mut self, hrp: &Hrp) {
for fe in HrpFe32Iter::new(hrp) {
self.input_fe(fe)
Expand All @@ -105,6 +107,7 @@ impl<Ck: Checksum> Engine<Ck> {
/// Adds a single gf32 element to the checksum engine.
///
/// This is where the actual checksum computation magic happens.
#[inline]
pub fn input_fe(&mut self, e: Fe32) {
let xn = self.residue.mul_by_x_then_add(Ck::CHECKSUM_LENGTH, e.into());
for i in 0..5 {
Expand All @@ -120,13 +123,15 @@ impl<Ck: Checksum> Engine<Ck> {
/// string, then computing the actual residue, and then replacing the
/// target with the actual. This method lets us compute the actual residue
/// without doing any string concatenations.
#[inline]
pub fn input_target_residue(&mut self) {
for i in 0..Ck::CHECKSUM_LENGTH {
self.input_fe(Fe32(Ck::TARGET_RESIDUE.unpack(Ck::CHECKSUM_LENGTH - i - 1)));
}
}

/// Returns for the current checksum residue.
#[inline]
pub fn residue(&self) -> &Ck::MidstateRepr { &self.residue }
}

Expand Down Expand Up @@ -161,12 +166,15 @@ pub struct PackedNull;

impl ops::BitXor<PackedNull> for PackedNull {
type Output = PackedNull;
#[inline]
fn bitxor(self, _: PackedNull) -> PackedNull { PackedNull }
}

impl PackedFe32 for PackedNull {
const ONE: Self = PackedNull;
#[inline]
fn unpack(&self, _: usize) -> u8 { 0 }
#[inline]
fn mul_by_x_then_add(&mut self, _: usize, _: u8) -> u8 { 0 }
}

Expand All @@ -175,11 +183,13 @@ macro_rules! impl_packed_fe32 {
impl PackedFe32 for $ty {
const ONE: Self = 1;

#[inline]
fn unpack(&self, n: usize) -> u8 {
debug_assert!(n < Self::WIDTH);
(*self >> (n * 5)) as u8 & 0x1f
}

#[inline]
fn mul_by_x_then_add(&mut self, degree: usize, add: u8) -> u8 {
debug_assert!(degree > 0);
debug_assert!(degree <= Self::WIDTH);
Expand Down Expand Up @@ -208,6 +218,7 @@ pub struct HrpFe32Iter<'hrp> {
impl<'hrp> HrpFe32Iter<'hrp> {
/// Creates an iterator that yields the field elements of `hrp` as they are input into the
/// checksum algorithm.
#[inline]
pub fn new(hrp: &'hrp Hrp) -> Self {
let high_iter = hrp.lowercase_byte_iter();
let low_iter = hrp.lowercase_byte_iter();
Expand All @@ -218,6 +229,7 @@ impl<'hrp> HrpFe32Iter<'hrp> {

impl<'hrp> Iterator for HrpFe32Iter<'hrp> {
type Item = Fe32;
#[inline]
fn next(&mut self) -> Option<Fe32> {
if let Some(ref mut high_iter) = &mut self.high_iter {
match high_iter.next() {
Expand All @@ -237,6 +249,7 @@ impl<'hrp> Iterator for HrpFe32Iter<'hrp> {
None
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let high = match &self.high_iter {
Some(high_iter) => {
Expand Down
24 changes: 24 additions & 0 deletions src/primitives/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ impl<'s> UncheckedHrpstring<'s> {
/// Parses an bech32 encode string and constructs a [`UncheckedHrpstring`] object.
///
/// Checks for valid ASCII values, does not validate the checksum.
#[inline]
pub fn new(s: &'s str) -> Result<Self, UncheckedHrpstringError> {
let sep_pos = check_characters(s)?;
let (hrp, data) = s.split_at(sep_pos);
Expand All @@ -135,9 +136,11 @@ impl<'s> UncheckedHrpstring<'s> {
}

/// Returns the human-readable part.
#[inline]
pub fn hrp(&self) -> Hrp { self.hrp }

/// Validates that data has a valid checksum for the `Ck` algorithm and returns a [`CheckedHrpstring`].
#[inline]
pub fn validate_and_remove_checksum<Ck: Checksum>(
self,
) -> Result<CheckedHrpstring<'s>, ChecksumError> {
Expand All @@ -151,12 +154,14 @@ impl<'s> UncheckedHrpstring<'s> {
/// This is useful if you do not know which checksum algorithm was used and wish to validate
/// against multiple algorithms consecutively. If this function returns `true` then call
/// `remove_checksum` to get a [`CheckedHrpstring`].
#[inline]
pub fn has_valid_checksum<Ck: Checksum>(&self) -> bool {
self.validate_checksum::<Ck>().is_ok()
}

/// Validates that data has a valid checksum for the `Ck` algorithm (this may mean an empty
/// checksum if `NoChecksum` is used).
#[inline]
pub fn validate_checksum<Ck: Checksum>(&self) -> Result<(), ChecksumError> {
use ChecksumError::*;

Expand Down Expand Up @@ -193,6 +198,7 @@ impl<'s> UncheckedHrpstring<'s> {
/// # Panics
///
/// May panic if data is not valid.
#[inline]
pub fn remove_checksum<Ck: Checksum>(self) -> CheckedHrpstring<'s> {
let data_len = self.data.len() - Ck::CHECKSUM_LENGTH;

Expand Down Expand Up @@ -237,24 +243,28 @@ impl<'s> CheckedHrpstring<'s> {
/// If you are validating the checksum multiple times consider using [`UncheckedHrpstring`].
///
/// This is equivalent to `UncheckedHrpstring::new().validate_and_remove_checksum::<CK>()`.
#[inline]
pub fn new<Ck: Checksum>(s: &'s str) -> Result<Self, CheckedHrpstringError> {
let unchecked = UncheckedHrpstring::new(s)?;
let checked = unchecked.validate_and_remove_checksum::<Ck>()?;
Ok(checked)
}

/// Returns the human-readable part.
#[inline]
pub fn hrp(&self) -> Hrp { self.hrp }

/// Returns an iterator that yields the data part of the parsed bech32 encoded string.
///
/// Converts the ASCII bytes representing field elements to the respective field elements, then
/// converts the stream of field elements to a stream of bytes.
#[inline]
pub fn byte_iter(&self) -> ByteIter {
ByteIter { iter: AsciiToFe32Iter { iter: self.data.iter().copied() }.fes_to_bytes() }
}

/// Converts this type to a [`SegwitHrpstring`] after validating the witness and HRP.
#[inline]
pub fn validate_segwit(mut self) -> Result<SegwitHrpstring<'s>, SegwitHrpstringError> {
if self.data.is_empty() {
return Err(SegwitHrpstringError::MissingWitnessVersion);
Expand Down Expand Up @@ -362,6 +372,7 @@ impl<'s> SegwitHrpstring<'s> {
///
/// NOTE: We do not enforce any restrictions on the HRP, use [`SegwitHrpstring::has_valid_hrp`]
/// to get strict BIP conformance (also [`Hrp::is_valid_on_mainnet`] and friends).
#[inline]
pub fn new(s: &'s str) -> Result<Self, SegwitHrpstringError> {
let unchecked = UncheckedHrpstring::new(s)?;

Expand Down Expand Up @@ -391,6 +402,7 @@ impl<'s> SegwitHrpstring<'s> {
///
/// [BIP-173]: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
/// [BIP-350]: https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki
#[inline]
pub fn new_bech32(s: &'s str) -> Result<Self, SegwitHrpstringError> {
let unchecked = UncheckedHrpstring::new(s)?;

Expand All @@ -409,12 +421,15 @@ impl<'s> SegwitHrpstring<'s> {
/// BIP-173 requires that the HRP is "bc" or "tb" but software in the Bitcoin ecosystem uses
/// other HRPs, specifically "bcrt" for regtest addresses. We provide this function in order to
/// be BIP-173 compliant but their are no restrictions on the HRP of [`SegwitHrpstring`].
#[inline]
pub fn has_valid_hrp(&self) -> bool { self.hrp().is_valid_segwit() }

/// Returns the human-readable part.
#[inline]
pub fn hrp(&self) -> Hrp { self.hrp }

/// Returns the witness version.
#[inline]
pub fn witness_version(&self) -> Fe32 { self.witness_version }

/// Returns an iterator that yields the data part, excluding the witness version, of the parsed
Expand All @@ -424,6 +439,7 @@ impl<'s> SegwitHrpstring<'s> {
/// converts the stream of field elements to a stream of bytes.
///
/// Use `self.witness_version()` to get the witness version.
#[inline]
pub fn byte_iter(&self) -> ByteIter {
ByteIter { iter: AsciiToFe32Iter { iter: self.data.iter().copied() }.fes_to_bytes() }
}
Expand Down Expand Up @@ -472,11 +488,14 @@ pub struct ByteIter<'s> {

impl<'s> Iterator for ByteIter<'s> {
type Item = u8;
#[inline]
fn next(&mut self) -> Option<u8> { self.iter.next() }
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
}

impl<'s> ExactSizeIterator for ByteIter<'s> {
#[inline]
fn len(&self) -> usize { self.iter.len() }
}

Expand All @@ -487,7 +506,9 @@ pub struct Fe32Iter<'s> {

impl<'s> Iterator for Fe32Iter<'s> {
type Item = Fe32;
#[inline]
fn next(&mut self) -> Option<Fe32> { self.iter.next() }
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
}

Expand All @@ -507,7 +528,9 @@ where
I: Iterator<Item = u8>,
{
type Item = Fe32;
#[inline]
fn next(&mut self) -> Option<Fe32> { self.iter.next().map(Fe32::from_char_unchecked) }
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
// Each ASCII character is an fe32 so iterators are the same size.
self.iter.size_hint()
Expand All @@ -518,6 +541,7 @@ impl<I> ExactSizeIterator for AsciiToFe32Iter<I>
where
I: Iterator<Item = u8> + ExactSizeIterator,
{
#[inline]
fn len(&self) -> usize { self.iter.len() }
}

Expand Down
13 changes: 13 additions & 0 deletions src/primitives/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,19 +90,22 @@ where
Ck: Checksum,
{
/// Constructs a new bech32 encoder.
#[inline]
pub fn new(data: I, hrp: &'hrp Hrp) -> Self {
Self { data, hrp, witness_version: None, marker: PhantomData::<Ck> }
}

/// Adds `witness_version` to the encoder (as first byte of encoded data).
///
/// Note, caller to guarantee that witness version is within valid range (0-16).
#[inline]
pub fn with_witness_version(mut self, witness_version: Fe32) -> Self {
self.witness_version = Some(witness_version);
self
}

/// Returns an iterator that yields the bech32 encoded address as field ASCII characters.
#[inline]
pub fn chars(self) -> CharIter<'hrp, I, Ck> {
let witver_iter = WitnessVersionIter::new(self.witness_version, self.data);
CharIter::new(self.hrp, witver_iter)
Expand All @@ -111,6 +114,7 @@ where
/// Returns an iterator that yields the field elements that go into the checksum, as well as the checksum at the end.
///
/// Each field element yielded has been input into the checksum algorithm (including the HRP as it is fed into the algorithm).
#[inline]
pub fn fes(self) -> Fe32Iter<'hrp, I, Ck> {
let witver_iter = WitnessVersionIter::new(self.witness_version, self.data);
Fe32Iter::new(self.hrp, witver_iter)
Expand All @@ -133,6 +137,7 @@ where
I: Iterator<Item = Fe32>,
{
/// Creates a [`WitnessVersionIter`].
#[inline]
pub fn new(witness_version: Option<Fe32>, iter: I) -> Self { Self { witness_version, iter } }
}

Expand All @@ -142,8 +147,10 @@ where
{
type Item = Fe32;

#[inline]
fn next(&mut self) -> Option<Fe32> { self.witness_version.take().or_else(|| self.iter.next()) }

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (min, max) = self.iter.size_hint();
match self.witness_version {
Expand Down Expand Up @@ -174,6 +181,7 @@ where
Ck: Checksum,
{
/// Adapts the `Fe32Iter` iterator to yield characters representing the bech32 encoding.
#[inline]
pub fn new(hrp: &'hrp Hrp, data: WitnessVersionIter<I>) -> Self {
let checksummed = Checksummed::new_hrp(hrp, data);
Self { hrp_iter: Some(hrp.lowercase_char_iter()), checksummed }
Expand All @@ -187,6 +195,7 @@ where
{
type Item = char;

#[inline]
fn next(&mut self) -> Option<char> {
if let Some(ref mut hrp_iter) = self.hrp_iter {
match hrp_iter.next() {
Expand All @@ -201,6 +210,7 @@ where
self.checksummed.next().map(|fe| fe.to_char())
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
match &self.hrp_iter {
// We have yielded the hrp and separator already.
Expand Down Expand Up @@ -245,6 +255,7 @@ where
Ck: Checksum,
{
/// Creates a [`Fe32Iter`] which yields all the field elements which go into the checksum algorithm.
#[inline]
pub fn new(hrp: &'hrp Hrp, data: WitnessVersionIter<I>) -> Self {
let hrp_iter = HrpFe32Iter::new(hrp);
let checksummed = Checksummed::new_hrp(hrp, data);
Expand All @@ -258,6 +269,7 @@ where
Ck: Checksum,
{
type Item = Fe32;
#[inline]
fn next(&mut self) -> Option<Fe32> {
if let Some(ref mut hrp_iter) = &mut self.hrp_iter {
match hrp_iter.next() {
Expand All @@ -268,6 +280,7 @@ where
self.checksummed.next()
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let hrp = match &self.hrp_iter {
Some(hrp_iter) => hrp_iter.size_hint(),
Expand Down
Loading

0 comments on commit 72e7dd8

Please sign in to comment.