Avoid allocation in build_scriptint

This commit is contained in:
Steven Roose 2022-06-02 15:36:45 +01:00
parent 09b4198b16
commit c80dbc2169
No known key found for this signature in database
GPG Key ID: 2F2A88D7F8D68E87
1 changed files with 31 additions and 13 deletions

View File

@ -241,30 +241,36 @@ impl From<bitcoinconsensus::Error> for Error {
Error::BitcoinConsensus(err) Error::BitcoinConsensus(err)
} }
} }
/// Helper to encode an integer in script format
fn build_scriptint(n: i64) -> Vec<u8> { /// Helper to encode an integer in script format.
if n == 0 { return vec![] } /// Writes bytes into the buffer and returns the number of bytes written.
fn write_scriptint(out: &mut [u8; 8], n: i64) -> usize {
let mut len = 0;
if n == 0 { return len; }
let neg = n < 0; let neg = n < 0;
let mut abs = if neg { -n } else { n } as usize; let mut abs = if neg { -n } else { n } as usize;
let mut v = vec![];
while abs > 0xFF { while abs > 0xFF {
v.push((abs & 0xFF) as u8); out[len] = (abs & 0xFF) as u8;
len += 1;
abs >>= 8; abs >>= 8;
} }
// If the number's value causes the sign bit to be set, we need an extra // If the number's value causes the sign bit to be set, we need an extra
// byte to get the correct value and correct sign bit // byte to get the correct value and correct sign bit
if abs & 0x80 != 0 { if abs & 0x80 != 0 {
v.push(abs as u8); out[len] = abs as u8;
v.push(if neg { 0x80u8 } else { 0u8 }); len += 1;
out[len] = if neg { 0x80u8 } else { 0u8 };
len += 1;
} }
// Otherwise we just set the sign bit ourselves // Otherwise we just set the sign bit ourselves
else { else {
abs |= if neg { 0x80 } else { 0 }; abs |= if neg { 0x80 } else { 0 };
v.push(abs as u8); out[len] = abs as u8;
len += 1;
} }
v len
} }
/// Helper to decode an integer in script format /// Helper to decode an integer in script format
@ -908,7 +914,9 @@ impl Builder {
/// Adds instructions to push an integer onto the stack, using the explicit /// Adds instructions to push an integer onto the stack, using the explicit
/// encoding regardless of the availability of dedicated opcodes. /// encoding regardless of the availability of dedicated opcodes.
pub fn push_scriptint(self, data: i64) -> Builder { pub fn push_scriptint(self, data: i64) -> Builder {
self.push_slice(&build_scriptint(data)) let mut buf = [0u8; 8];
let len = write_scriptint(&mut buf, data);
self.push_slice(&buf[..len])
} }
/// Adds instructions to push some arbitrary data onto the stack. /// Adds instructions to push some arbitrary data onto the stack.
@ -1106,7 +1114,7 @@ mod test {
use core::str::FromStr; use core::str::FromStr;
use super::*; use super::*;
use super::build_scriptint; use super::write_scriptint;
use crate::hashes::hex::{FromHex, ToHex}; use crate::hashes::hex::{FromHex, ToHex};
use crate::consensus::encode::{deserialize, serialize}; use crate::consensus::encode::{deserialize, serialize};
@ -1287,13 +1295,23 @@ mod test {
#[test] #[test]
fn scriptint_round_trip() { fn scriptint_round_trip() {
fn build_scriptint(n: i64) -> Vec<u8> {
let mut buf = [0u8; 8];
let len = write_scriptint(&mut buf, n);
assert!(len <= 8);
buf[..len].to_vec()
}
assert_eq!(build_scriptint(-1), vec![0x81]); assert_eq!(build_scriptint(-1), vec![0x81]);
assert_eq!(build_scriptint(255), vec![255, 0]); assert_eq!(build_scriptint(255), vec![255, 0]);
assert_eq!(build_scriptint(256), vec![0, 1]); assert_eq!(build_scriptint(256), vec![0, 1]);
assert_eq!(build_scriptint(257), vec![1, 1]); assert_eq!(build_scriptint(257), vec![1, 1]);
assert_eq!(build_scriptint(511), vec![255, 1]); assert_eq!(build_scriptint(511), vec![255, 1]);
for &i in [10, 100, 255, 256, 1000, 10000, 25000, 200000, 5000000, 1000000000, let test_vectors = [
(1 << 31) - 1, -((1 << 31) - 1)].iter() { 10, 100, 255, 256, 1000, 10000, 25000, 200000, 5000000, 1000000000,
(1 << 31) - 1, -((1 << 31) - 1),
];
for &i in test_vectors.iter() {
assert_eq!(Ok(i), read_scriptint(&build_scriptint(i))); assert_eq!(Ok(i), read_scriptint(&build_scriptint(i)));
assert_eq!(Ok(-i), read_scriptint(&build_scriptint(-i))); assert_eq!(Ok(-i), read_scriptint(&build_scriptint(-i)));
} }