use super::rangeset::RangeSet;
use super::validation_error::{atom, ErrorCode, ValidationErr};
use crate::gen::flags::COND_CANON_INTS;
use clvmr::allocator::{Allocator, AtomBuf, NodePtr, SExp};
fn count_zeros_impl(buf: &[u8]) -> usize {
let mut ret: usize = 0;
while ret < buf.len() && buf[ret] == 0 {
ret += 1;
}
ret as usize
}
fn count_zeros(a: &Allocator, range_cache: &mut RangeSet, atom_range: &AtomBuf) -> usize {
if atom_range.is_empty() {
return 0;
}
if atom_range.len() < 20 {
return count_zeros_impl(a.buf(atom_range));
}
let range = atom_range.idx_range();
let ranges = range_cache.not_overlapping(range.0, range.1);
if ranges.is_empty() {
return atom_range.len();
}
let full_buffer = a.buf(atom_range);
let mut start = range.0;
let mut ret = 0_usize;
for r in ranges {
debug_assert!(r.0 >= range.0);
debug_assert!(r.1 <= range.1);
debug_assert!(r.0 < r.1);
if r.0 != start {
ret += (r.0 - start) as usize;
}
let buf = &full_buffer[(r.0 - range.0) as usize..(r.1 - range.0) as usize];
let zeros = count_zeros_impl(buf);
ret += zeros;
if zeros < buf.len() {
break;
}
start = r.1;
}
debug_assert!(start <= range.1);
if ret > 0 {
range_cache.add(range.0, range.0 + ret as u32);
}
ret
}
pub fn sanitize_uint<'a>(
a: &'a Allocator,
n: NodePtr,
max_size: usize,
code: ErrorCode,
range_cache: &mut RangeSet,
flags: u32,
) -> Result<&'a [u8], ValidationErr> {
assert!(max_size <= 8);
let buf = atom(a, n, code)?;
if buf.is_empty() {
return Ok(&[]);
}
if (buf[0] & 0x80) != 0 {
return Err(ValidationErr(n, ErrorCode::NegativeAmount));
}
let atom_range = match a.sexp(n) {
SExp::Atom(b) => b,
_ => {
panic!("unreachable");
}
};
let mut i = count_zeros(a, range_cache, &atom_range);
let len = buf.len();
if i > 0 {
let range = atom_range.idx_range();
range_cache.add(range.0, range.0 + i as u32);
}
if len - i > max_size {
return Err(ValidationErr(n, code));
}
if i > 0 && i < len && buf[i] >= 0x80 {
i -= 1;
}
if (flags & COND_CANON_INTS) != 0 && i > 0 {
return Err(ValidationErr(n, code));
}
Ok(&buf[i..len])
}
#[test]
fn test_count_zeros_impl() {
assert_eq!(count_zeros_impl(&[0x80]), 0);
assert_eq!(count_zeros_impl(&[]), 0);
assert_eq!(count_zeros_impl(&[0]), 1);
assert_eq!(count_zeros_impl(&[0, 0x80]), 1);
assert_eq!(count_zeros_impl(&[0, 0, 0, 0, 0, 0, 0x7f]), 6);
assert_eq!(count_zeros_impl(&[0, 0, 0, 0, 0, 0, 0x80]), 6);
assert_eq!(count_zeros_impl(&[0xff, 0, 0, 0, 0, 0]), 0);
assert_eq!(count_zeros_impl(&[0, 0xff, 0, 0, 0, 0, 0]), 1);
}
#[cfg(test)]
fn get_buf(a: &Allocator, n: NodePtr) -> AtomBuf {
match a.sexp(n) {
SExp::Atom(b) => b,
SExp::Pair(_, _) => {
panic!("not an atom");
}
}
}
#[test]
fn test_count_zeros_and_sanitize_int() {
let mut a = Allocator::new();
let atom = {
let mut buf = Vec::<u8>::new();
for _i in 0..1024 {
buf.push(0);
}
buf[0] = 0xff;
buf[100] = 0x7f;
buf[1023] = 0xff;
a.new_atom(&buf)
}
.unwrap();
{
let mut range_cache = RangeSet::new();
let no_leading_zero = a.new_substr(atom, 0, 40).unwrap();
let b = get_buf(&mut a, no_leading_zero);
assert_eq!(count_zeros(&mut a, &mut range_cache, &b), 0);
assert_eq!(range_cache.not_overlapping(1, 41), vec![(1, 41)]);
let just_zeros = a.new_substr(atom, 10, 70).unwrap();
let b = get_buf(&mut a, just_zeros);
assert_eq!(count_zeros(&mut a, &mut range_cache, &b), 60);
assert_eq!(range_cache.not_overlapping(11, 71), vec![]);
assert_eq!(
range_cache.not_overlapping(10, 72),
vec![(10, 11), (71, 72)]
);
let a1 = a.new_substr(atom, 1, 101).unwrap();
let b = get_buf(&mut a, a1);
assert_eq!(count_zeros(&mut a, &mut range_cache, &b), 99);
assert_eq!(range_cache.not_overlapping(1, 101), vec![(1, 2)]);
assert_eq!(
range_cache.not_overlapping(1, 102),
vec![(1, 2), (101, 102)]
);
let a1 = a.new_substr(atom, 1, 101).unwrap();
let b = get_buf(&mut a, a1);
assert_eq!(count_zeros(&mut a, &mut range_cache, &b), 99);
assert_eq!(range_cache.not_overlapping(1, 101), vec![(1, 2)]);
let a1 = a.new_substr(atom, 1000, 1024).unwrap();
let b = get_buf(&mut a, a1);
assert_eq!(count_zeros(&mut a, &mut range_cache, &b), 23);
assert_eq!(range_cache.not_overlapping(1001, 1025), vec![(1024, 1025)]);
}
{
let e = ErrorCode::InvalidCoinAmount;
let mut range_cache = RangeSet::new();
let no_leading_zero = a.new_substr(atom, 0, 8).unwrap();
assert!(sanitize_uint(&a, no_leading_zero, 8, e, &mut range_cache, 0).is_err());
assert_eq!(range_cache.not_overlapping(1, 9), vec![(1, 9)]);
let just_zeros = a.new_substr(atom, 10, 70).unwrap();
assert_eq!(
sanitize_uint(&a, just_zeros, 8, e, &mut range_cache, 0).unwrap(),
&[]
);
assert_eq!(range_cache.not_overlapping(11, 71), vec![]);
assert_eq!(
range_cache.not_overlapping(10, 72),
vec![(10, 11), (71, 72)]
);
let a1 = a.new_substr(atom, 1, 101).unwrap();
assert_eq!(
sanitize_uint(&a, a1, 8, e, &mut range_cache, 0).unwrap(),
&[0x7f]
);
assert_eq!(range_cache.not_overlapping(1, 101), vec![(1, 2)]);
assert_eq!(
range_cache.not_overlapping(1, 102),
vec![(1, 2), (101, 102)]
);
let a1 = a.new_substr(atom, 1, 101).unwrap();
assert_eq!(
sanitize_uint(&a, a1, 8, e, &mut range_cache, 0).unwrap(),
&[0x7f]
);
assert_eq!(range_cache.not_overlapping(1, 101), vec![(1, 2)]);
let a1 = a.new_substr(atom, 1000, 1024).unwrap();
assert_eq!(
sanitize_uint(&a, a1, 8, e, &mut range_cache, 0).unwrap(),
&[0, 0xff]
);
assert_eq!(range_cache.not_overlapping(1001, 1025), vec![(1024, 1025)]);
}
}