From fe10ff2eb7c68fd0e092b6c94e59d0de7229935a Mon Sep 17 00:00:00 2001 From: "Jamil Lambert, PhD" Date: Mon, 18 Nov 2024 10:24:20 +0000 Subject: [PATCH 1/2] Mark functions `const` in `units` Mark `checked_` functions const. Replace `map()` and `?` operators, which are not allowed in const context, with match statements. Use descriptive variable names in ceiling division to make it easier to follow. --- units/src/amount/signed.rs | 48 +++++++++++++++++++------ units/src/amount/unsigned.rs | 69 ++++++++++++++++++++++++++++-------- 2 files changed, 91 insertions(+), 26 deletions(-) diff --git a/units/src/amount/signed.rs b/units/src/amount/signed.rs index c98d11898..0bcdb4896 100644 --- a/units/src/amount/signed.rs +++ b/units/src/amount/signed.rs @@ -217,27 +217,45 @@ impl SignedAmount { /// Consider using `unsigned_abs` which is often more practical. /// /// Returns [`None`] if overflow occurred. (`self == MIN`) - pub fn checked_abs(self) -> Option { self.0.checked_abs().map(SignedAmount) } + pub const fn checked_abs(self) -> Option { + // No `map()` in const context. + match self.0.checked_abs() { + Some(res) => Some(SignedAmount(res)), + None => None, + } + } /// Checked addition. /// /// Returns [`None`] if overflow occurred. - pub fn checked_add(self, rhs: SignedAmount) -> Option { - self.0.checked_add(rhs.0).map(SignedAmount) + pub const fn checked_add(self, rhs: SignedAmount) -> Option { + // No `map()` in const context. + match self.0.checked_add(rhs.0) { + Some(res) => Some(SignedAmount(res)), + None => None, + } } /// Checked subtraction. /// /// Returns [`None`] if overflow occurred. - pub fn checked_sub(self, rhs: SignedAmount) -> Option { - self.0.checked_sub(rhs.0).map(SignedAmount) + pub const fn checked_sub(self, rhs: SignedAmount) -> Option { + // No `map()` in const context. + match self.0.checked_sub(rhs.0) { + Some(res) => Some(SignedAmount(res)), + None => None, + } } /// Checked multiplication. /// /// Returns [`None`] if overflow occurred. - pub fn checked_mul(self, rhs: i64) -> Option { - self.0.checked_mul(rhs).map(SignedAmount) + pub const fn checked_mul(self, rhs: i64) -> Option { + // No `map()` in const context. + match self.0.checked_mul(rhs) { + Some(res) => Some(SignedAmount(res)), + None => None, + } } /// Checked integer division. @@ -245,15 +263,23 @@ impl SignedAmount { /// Be aware that integer division loses the remainder if no exact division can be made. /// /// Returns [`None`] if overflow occurred. - pub fn checked_div(self, rhs: i64) -> Option { - self.0.checked_div(rhs).map(SignedAmount) + pub const fn checked_div(self, rhs: i64) -> Option { + // No `map()` in const context. + match self.0.checked_div(rhs) { + Some(res) => Some(SignedAmount(res)), + None => None, + } } /// Checked remainder. /// /// Returns [`None`] if overflow occurred. - pub fn checked_rem(self, rhs: i64) -> Option { - self.0.checked_rem(rhs).map(SignedAmount) + pub const fn checked_rem(self, rhs: i64) -> Option { + // No `map()` in const context. + match self.0.checked_rem(rhs) { + Some(res) => Some(SignedAmount(res)), + None => None, + } } /// Unchecked addition. diff --git a/units/src/amount/unsigned.rs b/units/src/amount/unsigned.rs index cb2f49f08..e5597269f 100644 --- a/units/src/amount/unsigned.rs +++ b/units/src/amount/unsigned.rs @@ -196,28 +196,48 @@ impl Amount { /// Checked addition. /// /// Returns [`None`] if overflow occurred. - pub fn checked_add(self, rhs: Amount) -> Option { - self.0.checked_add(rhs.0).map(Amount) + pub const fn checked_add(self, rhs: Amount) -> Option { + // No `map()` in const context. + match self.0.checked_add(rhs.0) { + Some(res) => Some(Amount(res)), + None => None, + } } /// Checked subtraction. /// /// Returns [`None`] if overflow occurred. - pub fn checked_sub(self, rhs: Amount) -> Option { - self.0.checked_sub(rhs.0).map(Amount) + pub const fn checked_sub(self, rhs: Amount) -> Option { + // No `map()` in const context. + match self.0.checked_sub(rhs.0) { + Some(res) => Some(Amount(res)), + None => None, + } } /// Checked multiplication. /// /// Returns [`None`] if overflow occurred. - pub fn checked_mul(self, rhs: u64) -> Option { self.0.checked_mul(rhs).map(Amount) } + pub const fn checked_mul(self, rhs: u64) -> Option { + // No `map()` in const context. + match self.0.checked_mul(rhs) { + Some(res) => Some(Amount(res)), + None => None, + } + } /// Checked integer division. /// /// Be aware that integer division loses the remainder if no exact division can be made. /// /// Returns [`None`] if overflow occurred. - pub fn checked_div(self, rhs: u64) -> Option { self.0.checked_div(rhs).map(Amount) } + pub const fn checked_div(self, rhs: u64) -> Option { + // No `map()` in const context. + match self.0.checked_div(rhs) { + Some(res) => Some(Amount(res)), + None => None, + } + } /// Checked weight ceiling division. /// @@ -227,12 +247,19 @@ impl Amount { /// /// Returns [`None`] if overflow occurred. #[cfg(feature = "alloc")] - pub fn checked_div_by_weight_ceil(self, rhs: Weight) -> Option { - let sats = self.0.checked_mul(1_000)?; + pub const fn checked_div_by_weight_ceil(self, rhs: Weight) -> Option { let wu = rhs.to_wu(); - - let fee_rate = sats.checked_add(wu.checked_sub(1)?)?.checked_div(wu)?; - Some(FeeRate::from_sat_per_kwu(fee_rate)) + // No `?` operator in const context. + if let Some(sats) = self.0.checked_mul(1_000) { + if let Some(wu_minus_one) = wu.checked_sub(1) { + if let Some(sats_plus_wu_minus_one) = sats.checked_add(wu_minus_one) { + if let Some(fee_rate) = sats_plus_wu_minus_one.checked_div(wu) { + return Some(FeeRate::from_sat_per_kwu(fee_rate)); + } + } + } + } + None } /// Checked weight floor division. @@ -242,15 +269,27 @@ impl Amount { /// /// Returns [`None`] if overflow occurred. #[cfg(feature = "alloc")] - pub fn checked_div_by_weight_floor(self, rhs: Weight) -> Option { - let fee_rate = self.0.checked_mul(1_000)?.checked_div(rhs.to_wu())?; - Some(FeeRate::from_sat_per_kwu(fee_rate)) + pub const fn checked_div_by_weight_floor(self, rhs: Weight) -> Option { + // No `?` operator in const context. + match self.0.checked_mul(1_000) { + Some(res) => match res.checked_div(rhs.to_wu()) { + Some(fee_rate) => Some(FeeRate::from_sat_per_kwu(fee_rate)), + None => None, + }, + None => None, + } } /// Checked remainder. /// /// Returns [`None`] if overflow occurred. - pub fn checked_rem(self, rhs: u64) -> Option { self.0.checked_rem(rhs).map(Amount) } + pub const fn checked_rem(self, rhs: u64) -> Option { + // No `map()` in const context. + match self.0.checked_rem(rhs) { + Some(res) => Some(Amount(res)), + None => None, + } + } /// Unchecked addition. /// From a8379bf0053e66cf5984ce449d19e54e529b6b70 Mon Sep 17 00:00:00 2001 From: "Jamil Lambert, PhD" Date: Mon, 18 Nov 2024 10:50:26 +0000 Subject: [PATCH 2/2] Mark `checked_` functions const in bitcoin. Replace `?` operators, which are not allowed in const context, with match statements. --- bitcoin/src/psbt/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs index c9eac7165..5adc81654 100644 --- a/bitcoin/src/psbt/mod.rs +++ b/bitcoin/src/psbt/mod.rs @@ -627,8 +627,11 @@ impl Psbt { /// Gets the input at `input_index` after checking that it is a valid index. fn checked_input(&self, input_index: usize) -> Result<&Input, IndexOutOfBoundsError> { - self.check_index_is_within_bounds(input_index)?; - Ok(&self.inputs[input_index]) + // No `?` operator in const context. + match self.check_index_is_within_bounds(input_index) { + Ok(_) => Ok(&self.inputs[input_index]), + Err(e) => Err(e), + } } /// Checks `input_index` is within bounds for the PSBT `inputs` array and