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:
    ACK 5680b4e870
  apoelstra:
    ACK 5680b4e870ba3b7340432256c24d37d2b6ead15a; successfully ran local tests

Tree-SHA512: 5daa8bf6c0b439a579d31d23944077e4a7fa89e14052003d2b81c745f225147f8f6f693d068e0567830027cefea7dda2516596da632bc817199352fa29af0a9b
This commit is contained in:
merge-script 2025-02-24 21:50:48 +00:00
commit 6c286e32d4
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
3 changed files with 129 additions and 51 deletions

View File

@ -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])
}
}
)*

View File

@ -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())
}

View File

@ -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);
}
}