use crate::util::{Error, Result};
use num_bigint::BigInt;
use num_traits::Zero;
const MAX_BOOL_LEN: usize = 4; const MAX_NUM_LEN: usize = 4; const NUM_RANGE: i64 = 1i64 << 31; #[inline]
pub fn pop_bool(stack: &mut Vec<Vec<u8>>) -> Result<bool> {
let top = stack
.pop()
.ok_or(Error::ScriptError("Empty stack for bool".to_string()))?;
if top.len() > MAX_BOOL_LEN {
return Err(Error::ScriptError(format!(
"Bool too long: {} bytes",
top.len()
)));
}
Ok(decode_bool(&top))
}
#[inline]
pub fn pop_num(stack: &mut Vec<Vec<u8>>) -> Result<i32> {
let top = stack
.pop()
.ok_or(Error::ScriptError("Empty stack for num".to_string()))?;
if top.len() > MAX_NUM_LEN {
return Err(Error::ScriptError(format!(
"Num too long: {} bytes",
top.len()
)));
}
decode_num(&top).map(|n| n as i32)
}
#[inline]
pub fn pop_bigint(stack: &mut Vec<Vec<u8>>) -> Result<BigInt> {
let top = stack
.pop()
.ok_or(Error::ScriptError("Empty stack for bigint".to_string()))?;
Ok(decode_bigint(&top))
}
#[inline]
pub fn decode_bool(s: &[u8]) -> bool {
if s.is_empty() {
return false;
}
for &byte in &s[..s.len().saturating_sub(1)] {
if byte != 0 {
return true;
}
}
(s[s.len() - 1] & 127) != 0
}
#[inline]
pub fn decode_num(s: &[u8]) -> Result<i64> {
let high = if s.is_empty() { 0u8 } else { s[s.len() - 1] };
let sign = (high & 0x80) != 0;
let mut extended: Vec<u8> = s.to_vec();
while extended.len() < 8 {
extended.push(if sign { 0xffu8 } else { 0u8 });
}
let n = i64::from_le_bytes(
extended
.try_into()
.map_err(|_| Error::ScriptError("Invalid extension".to_string()))?,
);
if n.abs() >= NUM_RANGE {
return Err(Error::ScriptError("Number out of range".to_string()));
}
Ok(n)
}
#[inline]
pub fn encode_num(val: i64) -> Result<Vec<u8>> {
if val.abs() >= NUM_RANGE {
return Err(Error::ScriptError("Number out of range".to_string()));
}
if val == 0 {
return Ok(vec![]);
}
let full = (val as i32).to_le_bytes();
for l in 1..=4usize {
let test = full[0..l].to_vec();
let high_byte = test[l - 1];
let is_neg = (high_byte & 0x80) != 0;
let mut extended = test.clone();
for _ in l..4 {
extended.push(if is_neg { 0xffu8 } else { 0u8 });
}
let decoded = i32::from_le_bytes(
extended
.try_into()
.map_err(|_| Error::ScriptError("Invalid slice".to_string()))?,
) as i64;
if decoded == val {
return Ok(test);
}
}
unreachable!("Value out of 32-bit range")
}
#[inline]
pub fn decode_bigint(s: &[u8]) -> BigInt {
BigInt::from_signed_bytes_le(s)
}
#[inline]
pub fn encode_bigint(bi: &BigInt) -> Vec<u8> {
if *bi == BigInt::zero() {
return vec![];
}
bi.to_signed_bytes_le()
}
#[cfg(test)]
mod tests {
use super::*;
use num_bigint::BigInt;
use pretty_assertions::assert_eq;
#[test]
fn decode_bool_tests() {
assert_eq!(decode_bool(&[1]), true);
assert_eq!(decode_bool(&[255, 0, 0, 0]), true);
assert_eq!(decode_bool(&[0, 0, 0, 129]), true);
assert_eq!(decode_bool(&[0]), false);
assert_eq!(decode_bool(&[0, 0, 0, 0]), false);
assert_eq!(decode_bool(&[0, 0, 0, 128]), false);
assert_eq!(decode_bool(&[]), false);
}
#[test]
fn pop_bool_tests() {
let mut stack = vec![vec![1]];
assert_eq!(pop_bool(&mut stack).unwrap(), true);
let mut stack = vec![vec![0, 0, 0, 127]];
assert_eq!(pop_bool(&mut stack).unwrap(), true);
let mut stack = vec![];
assert_eq!(
pop_bool(&mut stack).unwrap_err().to_string(),
"Script error: Empty stack for bool"
);
let mut stack = vec![vec![0; 5]];
assert_eq!(
pop_bool(&mut stack).unwrap_err().to_string(),
"Script error: Bool too long: 5 bytes"
);
let mut stack = vec![vec![]];
assert_eq!(pop_bool(&mut stack).unwrap(), false);
let mut stack = vec![vec![0]];
assert_eq!(pop_bool(&mut stack).unwrap(), false);
let mut stack = vec![vec![0, 0, 0, 0]];
assert_eq!(pop_bool(&mut stack).unwrap(), false);
let mut stack = vec![vec![0, 0, 0, 128]];
assert_eq!(pop_bool(&mut stack).unwrap(), false);
}
#[test]
fn encode_decode_num_tests() {
assert!(encode_num(2_147_483_647).is_ok());
assert!(encode_num(-2_147_483_647).is_ok());
assert_eq!(
encode_num(2_147_483_648).unwrap_err().to_string(),
"Script error: Number out of range"
);
assert_eq!(
encode_num(-2_147_483_648).unwrap_err().to_string(),
"Script error: Number out of range"
);
assert_eq!(decode_num(&encode_num(0).unwrap()).unwrap(), 0);
assert_eq!(decode_num(&encode_num(1).unwrap()).unwrap(), 1);
assert_eq!(decode_num(&encode_num(-1).unwrap()).unwrap(), -1);
assert_eq!(decode_num(&encode_num(1_111).unwrap()).unwrap(), 1_111);
assert_eq!(decode_num(&encode_num(-1_111).unwrap()).unwrap(), -1_111);
assert_eq!(decode_num(&encode_num(111_111).unwrap()).unwrap(), 111_111);
assert_eq!(
decode_num(&encode_num(-111_111).unwrap()).unwrap(),
-111_111
);
assert_eq!(
decode_num(&encode_num(2_147_483_647).unwrap()).unwrap(),
2_147_483_647
);
assert_eq!(
decode_num(&encode_num(-2_147_483_647).unwrap()).unwrap(),
-2_147_483_647
);
}
#[test]
fn pop_num_tests() {
let mut stack = vec![vec![]];
assert_eq!(pop_num(&mut stack).unwrap(), 0i32);
let mut stack = vec![vec![1]];
assert_eq!(pop_num(&mut stack).unwrap(), 1i32);
let mut stack = vec![vec![255]]; assert_eq!(pop_num(&mut stack).unwrap(), -1i32);
let mut stack = vec![vec![0, 0, 0, 0]];
assert_eq!(pop_num(&mut stack).unwrap(), 0i32);
let mut stack = vec![];
let err = pop_num(&mut stack).unwrap_err();
assert_eq!(err.to_string(), "Script error: Empty stack for num");
let mut stack = vec![vec![0; 5]];
let err = pop_num(&mut stack).unwrap_err();
assert_eq!(err.to_string(), "Script error: Num too long: 5 bytes");
}
#[test]
fn bigint_tests() {
let bi_zero = BigInt::zero();
assert_eq!(encode_bigint(&bi_zero), Vec::<u8>::new());
let bi_1234 = BigInt::from(1234u32);
let bytes_1234 = encode_bigint(&bi_1234);
assert_eq!(decode_bigint(&bytes_1234), bi_1234);
let bi_neg1234 = -bi_1234.clone();
let bytes_neg = encode_bigint(&bi_neg1234);
assert!(bytes_neg.last().unwrap() & 0x80 != 0); assert_eq!(decode_bigint(&bytes_neg), bi_neg1234);
}
}