Add private ScriptBufAsVec type

Add a private type that allows us to mutate the inner vector of a
`ScriptBuf` only using public functions and never touching the inner
field.

Done in preparation for moving the `ScriptBuf` to `primitives`.

Mad hackery by Kix!
This commit is contained in:
Tobin C. Harding 2024-08-18 07:45:49 +10:00
parent c81fb93359
commit 4ff5d6886b
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
1 changed files with 51 additions and 14 deletions

View File

@ -105,7 +105,7 @@ impl ScriptBuf {
}
/// Adds a single opcode to the script.
pub fn push_opcode(&mut self, data: Opcode) { self.0.push(data.to_u8()); }
pub fn push_opcode(&mut self, data: Opcode) { self.as_byte_vec().push(data.to_u8()); }
/// Adds instructions to push some arbitrary data onto the stack.
pub fn push_slice<T: AsRef<PushBytes>>(&mut self, data: T) {
@ -153,33 +153,42 @@ impl ScriptBuf {
}
impl ScriptBuf {
/// Pretends to convert `&mut ScriptBuf` to `&mut Vec<u8>` so that it can be modified.
///
/// Note: if the returned value leaks the original `ScriptBuf` will become empty.
pub(crate) fn as_byte_vec(&mut self) -> ScriptBufAsVec<'_> {
let vec = core::mem::take(self).into_bytes();
ScriptBufAsVec(self, vec)
}
/// Pushes the slice without reserving
pub(crate) fn push_slice_no_opt(&mut self, data: &PushBytes) {
let mut this = self.as_byte_vec();
// Start with a PUSH opcode
match data.len().to_u64() {
n if n < opcodes::Ordinary::OP_PUSHDATA1 as u64 => {
self.0.push(n as u8);
this.push(n as u8);
}
n if n < 0x100 => {
self.0.push(opcodes::Ordinary::OP_PUSHDATA1.to_u8());
self.0.push(n as u8);
this.push(opcodes::Ordinary::OP_PUSHDATA1.to_u8());
this.push(n as u8);
}
n if n < 0x10000 => {
self.0.push(opcodes::Ordinary::OP_PUSHDATA2.to_u8());
self.0.push((n % 0x100) as u8);
self.0.push((n / 0x100) as u8);
this.push(opcodes::Ordinary::OP_PUSHDATA2.to_u8());
this.push((n % 0x100) as u8);
this.push((n / 0x100) as u8);
}
// `PushBytes` enforces len < 0x100000000
n => {
self.0.push(opcodes::Ordinary::OP_PUSHDATA4.to_u8());
self.0.push((n % 0x100) as u8);
self.0.push(((n / 0x100) % 0x100) as u8);
self.0.push(((n / 0x10000) % 0x100) as u8);
self.0.push((n / 0x1000000) as u8);
this.push(opcodes::Ordinary::OP_PUSHDATA4.to_u8());
this.push((n % 0x100) as u8);
this.push(((n / 0x100) % 0x100) as u8);
this.push(((n / 0x10000) % 0x100) as u8);
this.push((n / 0x1000000) as u8);
}
}
// Then push the raw bytes
self.0.extend_from_slice(data.as_bytes());
this.extend_from_slice(data.as_bytes());
}
/// Computes the sum of `len` and the length of an appropriate push opcode.
@ -200,7 +209,7 @@ impl ScriptBuf {
pub(crate) fn push_verify(&mut self, last_opcode: Option<Opcode>) {
match opcode_to_verify(last_opcode) {
Some(opcode) => {
self.0.pop();
self.as_byte_vec().pop();
self.push_opcode(opcode);
}
None => self.push_opcode(OP_VERIFY),
@ -254,3 +263,31 @@ impl<'a> Extend<Instruction<'a>> for ScriptBuf {
}
}
}
/// Pretends that this is a mutable reference to [`ScriptBuf`]'s internal buffer.
///
/// In reality the backing `Vec<u8>` is swapped with an empty one and this is holding both the
/// reference and the vec. The vec is put back when this drops so it also covers paics. (But not
/// leaks, which is OK since we never leak.)
pub(crate) struct ScriptBufAsVec<'a>(&'a mut ScriptBuf, Vec<u8>);
impl<'a> core::ops::Deref for ScriptBufAsVec<'a> {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl<'a> core::ops::DerefMut for ScriptBufAsVec<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.1
}
}
impl<'a> Drop for ScriptBufAsVec<'a> {
fn drop(&mut self) {
let vec = core::mem::take(&mut self.1);
*(self.0) = ScriptBuf::from_bytes(vec);
}
}