Merge rust-bitcoin/rust-bitcoin#4089: Script improvements
5680b4e870
Refer to `Script{Buf}` as `Self` where relevant (Martin Habovstiak)ce55dd5b70
Make `ScriptHash` & `WScriptHash` obey sanity rule (Martin Habovstiak)9ec9adc71d
Add a note about Electrum's script hashes (Martin Habovstiak)82f553aada
Expose `ScriptBuf`'s `capacity` (Martin Habovstiak)6b9d439dc1
Remove stale FIXME comments (Martin Habovstiak)0567e6fe1d
Put `#[inline]` on most `Script{Buf}` methods (Martin Habovstiak)b7e2af1b6b
Implement `Arbitrary` for `&'a Script` (Martin Habovstiak)bca2864084
Move `Deref{Mut}` from common module to `owned` (Martin Habovstiak)3b15e900f0
Add `const` to some `Script` methods (Martin Habovstiak)277223da6a
Make `Script` and `ScriptBuf` obey sanity rules (Martin Habovstiak) Pull request description: This implements various improvements related to `Script`. Please refer to the individual commits for details. This is a part of #4059 ACKs for top commit: tcharding: ACK5680b4e870
apoelstra: ACK 5680b4e870ba3b7340432256c24d37d2b6ead15a; successfully ran local tests Tree-SHA512: 5daa8bf6c0b439a579d31d23944077e4a7fa89e14052003d2b81c745f225147f8f6f693d068e0567830027cefea7dda2516596da632bc817199352fa29af0a9b
This commit is contained in:
commit
6c286e32d4
|
@ -4,6 +4,9 @@ use core::ops::{
|
|||
Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
|
||||
};
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
|
||||
use super::ScriptBuf;
|
||||
use crate::prelude::{Box, ToOwned, Vec};
|
||||
|
||||
|
@ -54,7 +57,7 @@ use crate::prelude::{Box, ToOwned, Vec};
|
|||
///
|
||||
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct Script(pub(in crate::script) [u8]);
|
||||
pub struct Script([u8]);
|
||||
|
||||
impl Default for &Script {
|
||||
#[inline]
|
||||
|
@ -64,39 +67,40 @@ impl Default for &Script {
|
|||
impl ToOwned for Script {
|
||||
type Owned = ScriptBuf;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned { ScriptBuf(self.0.to_owned()) }
|
||||
#[inline]
|
||||
fn to_owned(&self) -> Self::Owned { ScriptBuf::from_bytes(self.to_vec()) }
|
||||
}
|
||||
|
||||
impl Script {
|
||||
/// Constructs a new empty script.
|
||||
#[inline]
|
||||
pub fn new() -> &'static Script { Script::from_bytes(&[]) }
|
||||
pub const fn new() -> &'static Self { Self::from_bytes(&[]) }
|
||||
|
||||
/// Treat byte slice as `Script`
|
||||
#[inline]
|
||||
pub fn from_bytes(bytes: &[u8]) -> &Script {
|
||||
pub const fn from_bytes(bytes: &[u8]) -> &Self {
|
||||
// SAFETY: copied from `std`
|
||||
// The pointer was just created from a reference which is still alive.
|
||||
// Casting slice pointer to a transparent struct wrapping that slice is sound (same
|
||||
// layout).
|
||||
unsafe { &*(bytes as *const [u8] as *const Script) }
|
||||
unsafe { &*(bytes as *const [u8] as *const Self) }
|
||||
}
|
||||
|
||||
/// Treat mutable byte slice as `Script`
|
||||
#[inline]
|
||||
pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut Script {
|
||||
pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut Self {
|
||||
// SAFETY: copied from `std`
|
||||
// The pointer was just created from a reference which is still alive.
|
||||
// Casting slice pointer to a transparent struct wrapping that slice is sound (same
|
||||
// layout).
|
||||
// Function signature prevents callers from accessing `bytes` while the returned reference
|
||||
// is alive.
|
||||
unsafe { &mut *(bytes as *mut [u8] as *mut Script) }
|
||||
unsafe { &mut *(bytes as *mut [u8] as *mut Self) }
|
||||
}
|
||||
|
||||
/// Returns the script data as a byte slice.
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] { &self.0 }
|
||||
pub const fn as_bytes(&self) -> &[u8] { &self.0 }
|
||||
|
||||
/// Returns the script data as a mutable byte slice.
|
||||
#[inline]
|
||||
|
@ -104,7 +108,7 @@ impl Script {
|
|||
|
||||
/// Returns a copy of the script data.
|
||||
#[inline]
|
||||
pub fn to_vec(&self) -> Vec<u8> { self.0.to_owned() }
|
||||
pub fn to_vec(&self) -> Vec<u8> { self.as_bytes().to_owned() }
|
||||
|
||||
/// Returns a copy of the script data.
|
||||
#[inline]
|
||||
|
@ -113,14 +117,15 @@ impl Script {
|
|||
|
||||
/// Returns the length in bytes of the script.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize { self.0.len() }
|
||||
pub const fn len(&self) -> usize { self.as_bytes().len() }
|
||||
|
||||
/// Returns whether the script is the empty script.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool { self.0.is_empty() }
|
||||
pub const fn is_empty(&self) -> bool { self.as_bytes().is_empty() }
|
||||
|
||||
/// Converts a [`Box<Script>`](Box) into a [`ScriptBuf`] without copying or allocating.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn into_script_buf(self: Box<Self>) -> ScriptBuf {
|
||||
let rw = Box::into_raw(self) as *mut [u8];
|
||||
// SAFETY: copied from `std`
|
||||
|
@ -128,7 +133,16 @@ impl Script {
|
|||
// Casting a transparent struct wrapping a slice to the slice pointer is sound (same
|
||||
// layout).
|
||||
let inner = unsafe { Box::from_raw(rw) };
|
||||
ScriptBuf(Vec::from(inner))
|
||||
ScriptBuf::from_bytes(Vec::from(inner))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl<'a> Arbitrary<'a> for &'a Script {
|
||||
#[inline]
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let v = <&'a [u8]>::arbitrary(u)?;
|
||||
Ok(Script::from_bytes(v))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,7 +155,7 @@ macro_rules! delegate_index {
|
|||
|
||||
#[inline]
|
||||
fn index(&self, index: $type) -> &Self::Output {
|
||||
Self::from_bytes(&self.0[index])
|
||||
Self::from_bytes(&self.as_bytes()[index])
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
|
|
@ -2,15 +2,12 @@
|
|||
|
||||
//! Bitcoin scripts.
|
||||
|
||||
/// FIXME: Make this private.
|
||||
mod borrowed;
|
||||
/// FIXME: Make this private.
|
||||
mod owned;
|
||||
|
||||
use core::cmp::Ordering;
|
||||
use core::convert::Infallible;
|
||||
use core::fmt;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
use hashes::{hash160, sha256};
|
||||
use hex::DisplayHex;
|
||||
|
@ -36,9 +33,18 @@ pub const MAX_REDEEM_SCRIPT_SIZE: usize = 520;
|
|||
pub const MAX_WITNESS_SCRIPT_SIZE: usize = 10_000;
|
||||
|
||||
hashes::hash_newtype! {
|
||||
/// A hash of Bitcoin Script bytecode.
|
||||
/// A 160-bit hash of Bitcoin Script bytecode.
|
||||
///
|
||||
/// Note: there is another "script hash" object in bitcoin ecosystem (Electrum protocol) that
|
||||
/// uses 256-bit hash and hashes a semantically different script. Thus, this type cannot
|
||||
/// represent it.
|
||||
pub struct ScriptHash(hash160::Hash);
|
||||
/// SegWit version of a Bitcoin Script bytecode hash.
|
||||
|
||||
/// SegWit (256-bit) version of a Bitcoin Script bytecode hash.
|
||||
///
|
||||
/// Note: there is another "script hash" object in bitcoin ecosystem (Electrum protocol) that
|
||||
/// looks similar to this one also being SHA256, however, they hash semantically different
|
||||
/// scripts and have reversed representations, so this type cannot be used for both.
|
||||
pub struct WScriptHash(sha256::Hash);
|
||||
}
|
||||
|
||||
|
@ -57,12 +63,14 @@ impl ScriptHash {
|
|||
/// > spend a P2SH output if the redemption script it refers to is >520 bytes in length.
|
||||
///
|
||||
/// ref: [BIP-16](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki#user-content-520byte_limitation_on_serialized_script_size)
|
||||
#[inline]
|
||||
pub fn from_script(redeem_script: &Script) -> Result<Self, RedeemScriptSizeError> {
|
||||
if redeem_script.len() > MAX_REDEEM_SCRIPT_SIZE {
|
||||
return Err(RedeemScriptSizeError { size: redeem_script.len() });
|
||||
}
|
||||
|
||||
Ok(ScriptHash(hash160::Hash::hash(redeem_script.as_bytes())))
|
||||
// We've just checked the length
|
||||
Ok(ScriptHash::from_script_unchecked(redeem_script))
|
||||
}
|
||||
|
||||
/// Constructs a new `ScriptHash` from any script irrespective of script size.
|
||||
|
@ -71,6 +79,7 @@ impl ScriptHash {
|
|||
/// then the output will be unspendable (see [BIP-16]).
|
||||
///
|
||||
/// [BIP-16]: <https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki#user-content-520byte_limitation_on_serialized_script_size>
|
||||
#[inline]
|
||||
pub fn from_script_unchecked(script: &Script) -> Self {
|
||||
ScriptHash(hash160::Hash::hash(script.as_bytes()))
|
||||
}
|
||||
|
@ -85,12 +94,14 @@ impl WScriptHash {
|
|||
/// > witnessScript must match the 32-byte witness program.
|
||||
///
|
||||
/// ref: [BIP-141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki)
|
||||
#[inline]
|
||||
pub fn from_script(witness_script: &Script) -> Result<Self, WitnessScriptSizeError> {
|
||||
if witness_script.len() > MAX_WITNESS_SCRIPT_SIZE {
|
||||
return Err(WitnessScriptSizeError { size: witness_script.len() });
|
||||
}
|
||||
|
||||
Ok(WScriptHash(sha256::Hash::hash(witness_script.as_bytes())))
|
||||
// We've just checked the length
|
||||
Ok(WScriptHash::from_script_unchecked(witness_script))
|
||||
}
|
||||
|
||||
/// Constructs a new `WScriptHash` from any script irrespective of script size.
|
||||
|
@ -99,6 +110,7 @@ impl WScriptHash {
|
|||
/// output then the output will be unspendable (see [BIP-141]).
|
||||
///
|
||||
/// ref: [BIP-141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki)
|
||||
#[inline]
|
||||
pub fn from_script_unchecked(script: &Script) -> Self {
|
||||
WScriptHash(sha256::Hash::hash(script.as_bytes()))
|
||||
}
|
||||
|
@ -107,6 +119,7 @@ impl WScriptHash {
|
|||
impl TryFrom<ScriptBuf> for ScriptHash {
|
||||
type Error = RedeemScriptSizeError;
|
||||
|
||||
#[inline]
|
||||
fn try_from(redeem_script: ScriptBuf) -> Result<Self, Self::Error> {
|
||||
Self::from_script(&redeem_script)
|
||||
}
|
||||
|
@ -115,6 +128,7 @@ impl TryFrom<ScriptBuf> for ScriptHash {
|
|||
impl TryFrom<&ScriptBuf> for ScriptHash {
|
||||
type Error = RedeemScriptSizeError;
|
||||
|
||||
#[inline]
|
||||
fn try_from(redeem_script: &ScriptBuf) -> Result<Self, Self::Error> {
|
||||
Self::from_script(redeem_script)
|
||||
}
|
||||
|
@ -123,6 +137,7 @@ impl TryFrom<&ScriptBuf> for ScriptHash {
|
|||
impl TryFrom<&Script> for ScriptHash {
|
||||
type Error = RedeemScriptSizeError;
|
||||
|
||||
#[inline]
|
||||
fn try_from(redeem_script: &Script) -> Result<Self, Self::Error> {
|
||||
Self::from_script(redeem_script)
|
||||
}
|
||||
|
@ -131,6 +146,7 @@ impl TryFrom<&Script> for ScriptHash {
|
|||
impl TryFrom<ScriptBuf> for WScriptHash {
|
||||
type Error = WitnessScriptSizeError;
|
||||
|
||||
#[inline]
|
||||
fn try_from(witness_script: ScriptBuf) -> Result<Self, Self::Error> {
|
||||
Self::from_script(&witness_script)
|
||||
}
|
||||
|
@ -139,6 +155,7 @@ impl TryFrom<ScriptBuf> for WScriptHash {
|
|||
impl TryFrom<&ScriptBuf> for WScriptHash {
|
||||
type Error = WitnessScriptSizeError;
|
||||
|
||||
#[inline]
|
||||
fn try_from(witness_script: &ScriptBuf) -> Result<Self, Self::Error> {
|
||||
Self::from_script(witness_script)
|
||||
}
|
||||
|
@ -147,6 +164,7 @@ impl TryFrom<&ScriptBuf> for WScriptHash {
|
|||
impl TryFrom<&Script> for WScriptHash {
|
||||
type Error = WitnessScriptSizeError;
|
||||
|
||||
#[inline]
|
||||
fn try_from(witness_script: &Script) -> Result<Self, Self::Error> {
|
||||
Self::from_script(witness_script)
|
||||
}
|
||||
|
@ -160,10 +178,12 @@ pub struct RedeemScriptSizeError {
|
|||
}
|
||||
|
||||
impl From<Infallible> for RedeemScriptSizeError {
|
||||
#[inline]
|
||||
fn from(never: Infallible) -> Self { match never {} }
|
||||
}
|
||||
|
||||
impl fmt::Display for RedeemScriptSizeError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "redeem script size exceeds {} bytes: {}", MAX_REDEEM_SCRIPT_SIZE, self.size)
|
||||
}
|
||||
|
@ -180,10 +200,12 @@ pub struct WitnessScriptSizeError {
|
|||
}
|
||||
|
||||
impl From<Infallible> for WitnessScriptSizeError {
|
||||
#[inline]
|
||||
fn from(never: Infallible) -> Self { match never {} }
|
||||
}
|
||||
|
||||
impl fmt::Display for WitnessScriptSizeError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "witness script size exceeds {} bytes: {}", MAX_WITNESS_SCRIPT_SIZE, self.size)
|
||||
}
|
||||
|
@ -195,14 +217,17 @@ impl std::error::Error for WitnessScriptSizeError {}
|
|||
// We keep all the `Script` and `ScriptBuf` impls together since its easier to see side-by-side.
|
||||
|
||||
impl From<ScriptBuf> for Box<Script> {
|
||||
#[inline]
|
||||
fn from(v: ScriptBuf) -> Self { v.into_boxed_script() }
|
||||
}
|
||||
|
||||
impl From<ScriptBuf> for Cow<'_, Script> {
|
||||
#[inline]
|
||||
fn from(value: ScriptBuf) -> Self { Cow::Owned(value) }
|
||||
}
|
||||
|
||||
impl<'a> From<Cow<'a, Script>> for ScriptBuf {
|
||||
#[inline]
|
||||
fn from(value: Cow<'a, Script>) -> Self {
|
||||
match value {
|
||||
Cow::Owned(owned) => owned,
|
||||
|
@ -212,6 +237,7 @@ impl<'a> From<Cow<'a, Script>> for ScriptBuf {
|
|||
}
|
||||
|
||||
impl<'a> From<Cow<'a, Script>> for Box<Script> {
|
||||
#[inline]
|
||||
fn from(value: Cow<'a, Script>) -> Self {
|
||||
match value {
|
||||
Cow::Owned(owned) => owned.into(),
|
||||
|
@ -221,22 +247,26 @@ impl<'a> From<Cow<'a, Script>> for Box<Script> {
|
|||
}
|
||||
|
||||
impl<'a> From<&'a Script> for Box<Script> {
|
||||
#[inline]
|
||||
fn from(value: &'a Script) -> Self { value.to_owned().into() }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Script> for ScriptBuf {
|
||||
#[inline]
|
||||
fn from(value: &'a Script) -> Self { value.to_owned() }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Script> for Cow<'a, Script> {
|
||||
#[inline]
|
||||
fn from(value: &'a Script) -> Self { Cow::Borrowed(value) }
|
||||
}
|
||||
|
||||
/// Note: This will fail to compile on old Rust for targets that don't support atomics
|
||||
#[cfg(target_has_atomic = "ptr")]
|
||||
impl<'a> From<&'a Script> for Arc<Script> {
|
||||
#[inline]
|
||||
fn from(value: &'a Script) -> Self {
|
||||
let rw: *const [u8] = Arc::into_raw(Arc::from(&value.0));
|
||||
let rw: *const [u8] = Arc::into_raw(Arc::from(value.as_bytes()));
|
||||
// SAFETY: copied from `std`
|
||||
// The pointer was just created from an Arc without deallocating
|
||||
// Casting a slice to a transparent struct wrapping that slice is sound (same
|
||||
|
@ -246,8 +276,9 @@ impl<'a> From<&'a Script> for Arc<Script> {
|
|||
}
|
||||
|
||||
impl<'a> From<&'a Script> for Rc<Script> {
|
||||
#[inline]
|
||||
fn from(value: &'a Script) -> Self {
|
||||
let rw: *const [u8] = Rc::into_raw(Rc::from(&value.0));
|
||||
let rw: *const [u8] = Rc::into_raw(Rc::from(value.as_bytes()));
|
||||
// SAFETY: copied from `std`
|
||||
// The pointer was just created from an Rc without deallocating
|
||||
// Casting a slice to a transparent struct wrapping that slice is sound (same
|
||||
|
@ -257,19 +288,22 @@ impl<'a> From<&'a Script> for Rc<Script> {
|
|||
}
|
||||
|
||||
impl From<Vec<u8>> for ScriptBuf {
|
||||
fn from(v: Vec<u8>) -> Self { ScriptBuf(v) }
|
||||
#[inline]
|
||||
fn from(v: Vec<u8>) -> Self { ScriptBuf::from_bytes(v) }
|
||||
}
|
||||
|
||||
impl From<ScriptBuf> for Vec<u8> {
|
||||
fn from(v: ScriptBuf) -> Self { v.0 }
|
||||
#[inline]
|
||||
fn from(v: ScriptBuf) -> Self { v.into_bytes() }
|
||||
}
|
||||
|
||||
impl AsRef<Script> for Script {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Script { self }
|
||||
fn as_ref(&self) -> &Self { self }
|
||||
}
|
||||
|
||||
impl AsRef<Script> for ScriptBuf {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Script { self }
|
||||
}
|
||||
|
||||
|
@ -279,14 +313,17 @@ impl AsRef<[u8]> for Script {
|
|||
}
|
||||
|
||||
impl AsRef<[u8]> for ScriptBuf {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] { self.as_bytes() }
|
||||
}
|
||||
|
||||
impl AsMut<Script> for Script {
|
||||
fn as_mut(&mut self) -> &mut Script { self }
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut Self { self }
|
||||
}
|
||||
|
||||
impl AsMut<Script> for ScriptBuf {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut Script { self }
|
||||
}
|
||||
|
||||
|
@ -296,6 +333,7 @@ impl AsMut<[u8]> for Script {
|
|||
}
|
||||
|
||||
impl AsMut<[u8]> for ScriptBuf {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut [u8] { self.as_mut_bytes() }
|
||||
}
|
||||
|
||||
|
@ -308,6 +346,7 @@ impl fmt::Debug for Script {
|
|||
}
|
||||
|
||||
impl fmt::Debug for ScriptBuf {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self.as_script(), f) }
|
||||
}
|
||||
|
||||
|
@ -390,21 +429,23 @@ impl fmt::Display for ScriptBuf {
|
|||
}
|
||||
|
||||
impl fmt::LowerHex for Script {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::LowerHex::fmt(&self.as_bytes().as_hex(), f)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "alloc")]
|
||||
internals::impl_to_hex_from_lower_hex!(Script, |script: &Script| script.len() * 2);
|
||||
internals::impl_to_hex_from_lower_hex!(Script, |script: &Self| script.len() * 2);
|
||||
|
||||
impl fmt::LowerHex for ScriptBuf {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self.as_script(), f) }
|
||||
}
|
||||
#[cfg(feature = "alloc")]
|
||||
internals::impl_to_hex_from_lower_hex!(ScriptBuf, |script_buf: &ScriptBuf| script_buf.len() * 2);
|
||||
internals::impl_to_hex_from_lower_hex!(ScriptBuf, |script_buf: &Self| script_buf.len() * 2);
|
||||
|
||||
impl fmt::UpperHex for Script {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::UpperHex::fmt(&self.as_bytes().as_hex(), f)
|
||||
}
|
||||
|
@ -415,39 +456,35 @@ impl fmt::UpperHex for ScriptBuf {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(self.as_script(), f) }
|
||||
}
|
||||
|
||||
impl Deref for ScriptBuf {
|
||||
type Target = Script;
|
||||
|
||||
fn deref(&self) -> &Self::Target { Script::from_bytes(&self.0) }
|
||||
}
|
||||
|
||||
impl DerefMut for ScriptBuf {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { Script::from_bytes_mut(&mut self.0) }
|
||||
}
|
||||
|
||||
impl Borrow<Script> for ScriptBuf {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &Script { self }
|
||||
}
|
||||
|
||||
impl BorrowMut<Script> for ScriptBuf {
|
||||
#[inline]
|
||||
fn borrow_mut(&mut self) -> &mut Script { self }
|
||||
}
|
||||
|
||||
impl PartialEq<ScriptBuf> for Script {
|
||||
#[inline]
|
||||
fn eq(&self, other: &ScriptBuf) -> bool { self.eq(other.as_script()) }
|
||||
}
|
||||
|
||||
impl PartialEq<Script> for ScriptBuf {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Script) -> bool { self.as_script().eq(other) }
|
||||
}
|
||||
|
||||
impl PartialOrd<Script> for ScriptBuf {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Script) -> Option<Ordering> {
|
||||
self.as_script().partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<ScriptBuf> for Script {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &ScriptBuf) -> Option<Ordering> {
|
||||
self.partial_cmp(other.as_script())
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
#[cfg(doc)]
|
||||
use core::ops::Deref;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
|
@ -19,27 +18,31 @@ use crate::prelude::{Box, Vec};
|
|||
///
|
||||
/// [deref coercions]: https://doc.rust-lang.org/std/ops/trait.Deref.html#more-on-deref-coercion
|
||||
#[derive(Default, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
pub struct ScriptBuf(pub(in crate::script) Vec<u8>);
|
||||
pub struct ScriptBuf(Vec<u8>);
|
||||
|
||||
impl ScriptBuf {
|
||||
/// Constructs a new empty script.
|
||||
#[inline]
|
||||
pub const fn new() -> Self { ScriptBuf(Vec::new()) }
|
||||
pub const fn new() -> Self { Self::from_bytes(Vec::new()) }
|
||||
|
||||
/// Converts byte vector into script.
|
||||
///
|
||||
/// This method doesn't (re)allocate.
|
||||
pub fn from_bytes(bytes: Vec<u8>) -> Self { ScriptBuf(bytes) }
|
||||
#[inline]
|
||||
pub const fn from_bytes(bytes: Vec<u8>) -> Self { Self(bytes) }
|
||||
|
||||
/// Returns a reference to unsized script.
|
||||
#[inline]
|
||||
pub fn as_script(&self) -> &Script { Script::from_bytes(&self.0) }
|
||||
|
||||
/// Returns a mutable reference to unsized script.
|
||||
#[inline]
|
||||
pub fn as_mut_script(&mut self) -> &mut Script { Script::from_bytes_mut(&mut self.0) }
|
||||
|
||||
/// Converts the script into a byte vector.
|
||||
///
|
||||
/// This method doesn't (re)allocate.
|
||||
#[inline]
|
||||
pub fn into_bytes(self) -> Vec<u8> { self.0 }
|
||||
|
||||
/// Converts this `ScriptBuf` into a [boxed](Box) [`Script`].
|
||||
|
@ -52,12 +55,13 @@ impl ScriptBuf {
|
|||
#[inline]
|
||||
pub fn into_boxed_script(self) -> Box<Script> {
|
||||
// Copied from PathBuf::into_boxed_path
|
||||
let rw = Box::into_raw(self.0.into_boxed_slice()) as *mut Script;
|
||||
let rw = Box::into_raw(self.into_bytes().into_boxed_slice()) as *mut Script;
|
||||
unsafe { Box::from_raw(rw) }
|
||||
}
|
||||
|
||||
/// Constructs a new empty script with pre-allocated capacity.
|
||||
pub fn with_capacity(capacity: usize) -> Self { ScriptBuf(Vec::with_capacity(capacity)) }
|
||||
#[inline]
|
||||
pub fn with_capacity(capacity: usize) -> Self { ScriptBuf::from_bytes(Vec::with_capacity(capacity)) }
|
||||
|
||||
/// Pre-allocates at least `additional_len` bytes if needed.
|
||||
///
|
||||
|
@ -69,6 +73,7 @@ impl ScriptBuf {
|
|||
/// # Panics
|
||||
///
|
||||
/// Panics if the new capacity exceeds `isize::MAX bytes`.
|
||||
#[inline]
|
||||
pub fn reserve(&mut self, additional_len: usize) { self.0.reserve(additional_len); }
|
||||
|
||||
/// Pre-allocates exactly `additional_len` bytes if needed.
|
||||
|
@ -84,14 +89,36 @@ impl ScriptBuf {
|
|||
/// # Panics
|
||||
///
|
||||
/// Panics if the new capacity exceeds `isize::MAX bytes`.
|
||||
#[inline]
|
||||
pub fn reserve_exact(&mut self, additional_len: usize) { self.0.reserve_exact(additional_len); }
|
||||
|
||||
/// Returns the number of **bytes** available for writing without reallocation.
|
||||
///
|
||||
/// It is guaranteed that `script.capacity() >= script.len()` always holds.
|
||||
#[inline]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.0.capacity()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ScriptBuf {
|
||||
type Target = Script;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target { self.as_script() }
|
||||
}
|
||||
|
||||
impl DerefMut for ScriptBuf {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut_script() }
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
impl<'a> Arbitrary<'a> for ScriptBuf {
|
||||
#[inline]
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let v = Vec::<u8>::arbitrary(u)?;
|
||||
Ok(ScriptBuf(v))
|
||||
Ok(ScriptBuf::from_bytes(v))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +130,7 @@ mod tests {
|
|||
fn script_buf_from_bytes() {
|
||||
let bytes = vec![1, 2, 3];
|
||||
let script = ScriptBuf::from_bytes(bytes.clone());
|
||||
assert_eq!(script.0, bytes);
|
||||
assert_eq!(script.as_bytes(), bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -120,7 +147,7 @@ mod tests {
|
|||
let mut script = ScriptBuf::from_bytes(bytes.clone());
|
||||
let script_mut_ref = script.as_mut_script();
|
||||
script_mut_ref.as_mut_bytes()[0] = 4;
|
||||
assert_eq!(script.0, vec![4, 2, 3]);
|
||||
assert_eq!(script.as_mut_bytes(), vec![4, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -142,20 +169,20 @@ mod tests {
|
|||
#[test]
|
||||
fn script_buf_capacity() {
|
||||
let script = ScriptBuf::with_capacity(10);
|
||||
assert!(script.0.capacity() >= 10);
|
||||
assert!(script.capacity() >= 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn script_buf_reserve() {
|
||||
let mut script = ScriptBuf::new();
|
||||
script.reserve(10);
|
||||
assert!(script.0.capacity() >= 10);
|
||||
assert!(script.capacity() >= 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn script_buf_reserve_exact() {
|
||||
let mut script = ScriptBuf::new();
|
||||
script.reserve_exact(10);
|
||||
assert!(script.0.capacity() >= 10);
|
||||
assert!(script.capacity() >= 10);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue