Skip to content

Commit 4ff5d68

Browse files
committed
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!
1 parent c81fb93 commit 4ff5d68

File tree

1 file changed

+51
-14
lines changed

1 file changed

+51
-14
lines changed

bitcoin/src/blockdata/script/owned.rs

+51-14
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ impl ScriptBuf {
105105
}
106106

107107
/// Adds a single opcode to the script.
108-
pub fn push_opcode(&mut self, data: Opcode) { self.0.push(data.to_u8()); }
108+
pub fn push_opcode(&mut self, data: Opcode) { self.as_byte_vec().push(data.to_u8()); }
109109

110110
/// Adds instructions to push some arbitrary data onto the stack.
111111
pub fn push_slice<T: AsRef<PushBytes>>(&mut self, data: T) {
@@ -153,33 +153,42 @@ impl ScriptBuf {
153153
}
154154

155155
impl ScriptBuf {
156+
/// Pretends to convert `&mut ScriptBuf` to `&mut Vec<u8>` so that it can be modified.
157+
///
158+
/// Note: if the returned value leaks the original `ScriptBuf` will become empty.
159+
pub(crate) fn as_byte_vec(&mut self) -> ScriptBufAsVec<'_> {
160+
let vec = core::mem::take(self).into_bytes();
161+
ScriptBufAsVec(self, vec)
162+
}
163+
156164
/// Pushes the slice without reserving
157165
pub(crate) fn push_slice_no_opt(&mut self, data: &PushBytes) {
166+
let mut this = self.as_byte_vec();
158167
// Start with a PUSH opcode
159168
match data.len().to_u64() {
160169
n if n < opcodes::Ordinary::OP_PUSHDATA1 as u64 => {
161-
self.0.push(n as u8);
170+
this.push(n as u8);
162171
}
163172
n if n < 0x100 => {
164-
self.0.push(opcodes::Ordinary::OP_PUSHDATA1.to_u8());
165-
self.0.push(n as u8);
173+
this.push(opcodes::Ordinary::OP_PUSHDATA1.to_u8());
174+
this.push(n as u8);
166175
}
167176
n if n < 0x10000 => {
168-
self.0.push(opcodes::Ordinary::OP_PUSHDATA2.to_u8());
169-
self.0.push((n % 0x100) as u8);
170-
self.0.push((n / 0x100) as u8);
177+
this.push(opcodes::Ordinary::OP_PUSHDATA2.to_u8());
178+
this.push((n % 0x100) as u8);
179+
this.push((n / 0x100) as u8);
171180
}
172181
// `PushBytes` enforces len < 0x100000000
173182
n => {
174-
self.0.push(opcodes::Ordinary::OP_PUSHDATA4.to_u8());
175-
self.0.push((n % 0x100) as u8);
176-
self.0.push(((n / 0x100) % 0x100) as u8);
177-
self.0.push(((n / 0x10000) % 0x100) as u8);
178-
self.0.push((n / 0x1000000) as u8);
183+
this.push(opcodes::Ordinary::OP_PUSHDATA4.to_u8());
184+
this.push((n % 0x100) as u8);
185+
this.push(((n / 0x100) % 0x100) as u8);
186+
this.push(((n / 0x10000) % 0x100) as u8);
187+
this.push((n / 0x1000000) as u8);
179188
}
180189
}
181190
// Then push the raw bytes
182-
self.0.extend_from_slice(data.as_bytes());
191+
this.extend_from_slice(data.as_bytes());
183192
}
184193

185194
/// Computes the sum of `len` and the length of an appropriate push opcode.
@@ -200,7 +209,7 @@ impl ScriptBuf {
200209
pub(crate) fn push_verify(&mut self, last_opcode: Option<Opcode>) {
201210
match opcode_to_verify(last_opcode) {
202211
Some(opcode) => {
203-
self.0.pop();
212+
self.as_byte_vec().pop();
204213
self.push_opcode(opcode);
205214
}
206215
None => self.push_opcode(OP_VERIFY),
@@ -254,3 +263,31 @@ impl<'a> Extend<Instruction<'a>> for ScriptBuf {
254263
}
255264
}
256265
}
266+
267+
/// Pretends that this is a mutable reference to [`ScriptBuf`]'s internal buffer.
268+
///
269+
/// In reality the backing `Vec<u8>` is swapped with an empty one and this is holding both the
270+
/// reference and the vec. The vec is put back when this drops so it also covers paics. (But not
271+
/// leaks, which is OK since we never leak.)
272+
pub(crate) struct ScriptBufAsVec<'a>(&'a mut ScriptBuf, Vec<u8>);
273+
274+
impl<'a> core::ops::Deref for ScriptBufAsVec<'a> {
275+
type Target = Vec<u8>;
276+
277+
fn deref(&self) -> &Self::Target {
278+
&self.1
279+
}
280+
}
281+
282+
impl<'a> core::ops::DerefMut for ScriptBufAsVec<'a> {
283+
fn deref_mut(&mut self) -> &mut Self::Target {
284+
&mut self.1
285+
}
286+
}
287+
288+
impl<'a> Drop for ScriptBufAsVec<'a> {
289+
fn drop(&mut self) {
290+
let vec = core::mem::take(&mut self.1);
291+
*(self.0) = ScriptBuf::from_bytes(vec);
292+
}
293+
}

0 commit comments

Comments
 (0)