Skip to content

Commit

Permalink
Merge pull request #439 from Chia-Network/node_from_number
Browse files Browse the repository at this point in the history
move node_from_number into the Allocator
  • Loading branch information
arvidn authored Jul 26, 2024
2 parents 3f224c6 + 66a17f9 commit 03730ea
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 82 deletions.
41 changes: 39 additions & 2 deletions src/allocator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::err_utils::err;
use crate::number::{node_from_number, number_from_u8, Number};
use crate::number::{number_from_u8, Number};
use crate::reduction::EvalErr;
use chia_bls::{G1Element, G2Element};
use std::hash::Hash;
Expand Down Expand Up @@ -289,7 +289,17 @@ impl Allocator {
return self.new_small_number(val);
}
}
node_from_number(self, &v)
let bytes: Vec<u8> = v.to_signed_bytes_be();
let mut slice = bytes.as_slice();

// make number minimal by removing leading zeros
while (!slice.is_empty()) && (slice[0] == 0) {
if slice.len() > 1 && (slice[1] & 0x80 == 0x80) {
break;
}
slice = &slice[1..];
}
self.new_atom(slice)
}

pub fn new_g1(&mut self, g1: G1Element) -> Result<NodePtr, EvalErr> {
Expand Down Expand Up @@ -1831,3 +1841,30 @@ fn test_auto_small_number_from_buf(#[case] buf: &[u8], #[case] expect_small: boo
fn test_fits_in_small_atom(#[case] buf: &[u8], #[case] expected: Option<u32>) {
assert_eq!(fits_in_small_atom(buf), expected);
}

#[cfg(test)]
#[rstest]
// 0 is encoded as an empty string
#[case(&[0], "0", &[])]
#[case(&[1], "1", &[1])]
// leading zeroes are redundant
#[case(&[0,0,0,1], "1", &[1])]
#[case(&[0,0,0x80], "128", &[0, 0x80])]
// A leading zero is necessary to encode a positive number with the
// penultimate byte's most significant bit set
#[case(&[0,0xff], "255", &[0, 0xff])]
#[case(&[0x7f,0xff], "32767", &[0x7f, 0xff])]
// the first byte is redundant, it's still -1
#[case(&[0xff,0xff], "-1", &[0xff])]
#[case(&[0xff], "-1", &[0xff])]
#[case(&[0,0,0x80,0], "32768", &[0,0x80,0])]
#[case(&[0,0,0x40,0], "16384", &[0x40,0])]
fn test_number_to_atom(#[case] bytes: &[u8], #[case] text: &str, #[case] buf: &[u8]) {
let mut a = Allocator::new();

// 0 is encoded as an empty string
let num = number_from_u8(bytes);
assert_eq!(format!("{}", num), text);
let ptr = a.new_number(num).unwrap();
assert_eq!(a.atom(ptr).as_ref(), buf);
}
80 changes: 0 additions & 80 deletions src/number.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,6 @@
use crate::allocator::{Allocator, NodePtr};
use crate::reduction::EvalErr;

use num_bigint::BigInt;
pub type Number = BigInt;

// This low-level conversion function is meant to be used by the Allocator, for
// logic interacting with the CLVM heap/allocator, use new_number() and number()
// instead.
pub fn node_from_number(allocator: &mut Allocator, item: &Number) -> Result<NodePtr, EvalErr> {
let bytes: Vec<u8> = item.to_signed_bytes_be();
let mut slice = bytes.as_slice();

// make number minimal by removing leading zeros
while (!slice.is_empty()) && (slice[0] == 0) {
if slice.len() > 1 && (slice[1] & 0x80 == 0x80) {
break;
}
slice = &slice[1..];
}
allocator.new_atom(slice)
}

// This low-level conversion function is meant to be used by the Allocator, for
// logic interacting with the CLVM heap/allocator, use new_number() and number()
// instead.
Expand All @@ -33,66 +13,6 @@ pub fn number_from_u8(v: &[u8]) -> Number {
}
}

#[test]
fn test_node_from_number() {
let mut a = Allocator::new();

// 0 is encoded as an empty string
let num = number_from_u8(&[0]);
let ptr = node_from_number(&mut a, &num).unwrap();
assert_eq!(format!("{}", num), "0");
assert_eq!(a.atom(ptr).as_ref().len(), 0);

let num = number_from_u8(&[1]);
let ptr = node_from_number(&mut a, &num).unwrap();
assert_eq!(format!("{}", num), "1");
assert_eq!(&[1], &a.atom(ptr).as_ref());

// leading zeroes are redundant
let num = number_from_u8(&[0, 0, 0, 1]);
let ptr = node_from_number(&mut a, &num).unwrap();
assert_eq!(format!("{}", num), "1");
assert_eq!(&[1], &a.atom(ptr).as_ref());

let num = number_from_u8(&[0x00, 0x00, 0x80]);
let ptr = node_from_number(&mut a, &num).unwrap();
assert_eq!(format!("{}", num), "128");
assert_eq!(&[0x00, 0x80], &a.atom(ptr).as_ref());

// A leading zero is necessary to encode a positive number with the
// penultimate byte's most significant bit set
let num = number_from_u8(&[0x00, 0xff]);
let ptr = node_from_number(&mut a, &num).unwrap();
assert_eq!(format!("{}", num), "255");
assert_eq!(&[0x00, 0xff], &a.atom(ptr).as_ref());

let num = number_from_u8(&[0x7f, 0xff]);
let ptr = node_from_number(&mut a, &num).unwrap();
assert_eq!(format!("{}", num), "32767");
assert_eq!(&[0x7f, 0xff], &a.atom(ptr).as_ref());

// the first byte is redundant, it's still -1
let num = number_from_u8(&[0xff, 0xff]);
let ptr = node_from_number(&mut a, &num).unwrap();
assert_eq!(format!("{}", num), "-1");
assert_eq!(&[0xff], &a.atom(ptr).as_ref());

let num = number_from_u8(&[0xff]);
let ptr = node_from_number(&mut a, &num).unwrap();
assert_eq!(format!("{}", num), "-1");
assert_eq!(&[0xff], &a.atom(ptr).as_ref());

let num = number_from_u8(&[0x00, 0x80, 0x00]);
assert_eq!(format!("{}", num), "32768");
let ptr = node_from_number(&mut a, &num).unwrap();
assert_eq!(&[0x00, 0x80, 0x00], &a.atom(ptr).as_ref());

let num = number_from_u8(&[0x00, 0x40, 0x00]);
assert_eq!(format!("{}", num), "16384");
let ptr = node_from_number(&mut a, &num).unwrap();
assert_eq!(&[0x40, 0x00], &a.atom(ptr).as_ref());
}

#[cfg(test)]
use num_bigint::{BigUint, Sign};

Expand Down

0 comments on commit 03730ea

Please sign in to comment.