num-bigint
num-bigint copied to clipboard
Feature request: zero-copy constructor?
Hi. I have input data in a raw buffer. I am doing this:
let data : &Vec< u8 >;
let data_big = BigUint::from_bytes_le( &data );
I would like to achieve zero-copy solution. Is that possible/planned with the wonderful library?
Seems that's something the library can't do at the moment. What about implementing it? I can open a PR. In theory not that hard. If data is little-ended and it is aligned with usize
then it's enough to reinterpret the original buffer without copying it. Implementing zero-copy constructor would make num
complying with Rust's: "Fast, Reliable, Productive . Pick Three ".
It's not possible to borrow from an existing buffer because there's no lifetime parameter in the type (like BigUint<'a>
).
In theory not that hard. If data is little-ended and it is aligned with
usize
then it's enough to reinterpret the original buffer without copying it.
If we instead consumed a Vec<u8>
buffer, one could imagine reinterpreting that as Vec<BigDigit>
, but the problem there is that the layout must also match how the buffer was allocated when it is later passed to realloc
or dealloc
.
Implementing zero-copy constructor would make
num
complying with Rust's: "Fast, Reliable, Productive . Pick Three ".
Performance is a goal, but that doesn't mean it must come at all costs.
Hi, @cuviper Hm.. Are you saying it's not possible? What about borrowing data from Vec
? BigUint
does not have to own its data. Also, it's possible to introduce alternative BigUint
which does not own its data, if it's assumed BigUint
does own. Another alternative is the functional approach: instead of creating an object, static functions could be exposed accepting vector as the first argument.
I didn't think a lot about alternatives. Please treat them as a product of brainstorm activity rather than well thought out plan. Nevertheless, a theoretical solution definitely exists.
It's fundamental to Rust that a type can't borrow data without expressing that borrowed lifetime. We could add something like BigUintRef<'a>
perhaps, but that's a lot of API we'd need to mirror to the new type. Frankly, I think it's probably rare that the constructor is a performance bottleneck in the first place.
Another alternative is the functional approach: instead of creating an object, static functions could be exposed accepting vector as the first argument.
I'm not sure what you're suggesting here -- could you sketch out that API?
I was looking for an API like this today. What I had in mind was something like this where T can be anything that can be borrowed as a slice of digits:
use std::borrow::BorrowMut;
struct BigUint<T> {
data: T,
}
impl <T: BorrowMut<[u64]>> BigUint<T> {
pub fn new(data: T) -> Self {
Self { data }
}
/// Returns the number of least-significant bits that are zero,
/// or `None` if the entire number is zero.
pub fn trailing_zeros(&self) -> Option<u64> {
let data = self.data.borrow();
let i = data.iter().position(|&digit| digit != 0)?;
let zeros: u64 = data[i].trailing_zeros().into();
Some((i as u64) * 64 + zeros)
}
}
fn main() {
let mut digits: Vec<u64> = vec![0, 1, 0];
// First operate on a mut ref
let bigint = BigUint::new(&mut *digits);
println!("{:?}", bigint.trailing_zeros());
// Then consume the whole thing
let bigint = BigUint::new(digits);
println!("{:?}", bigint.trailing_zeros());
}
By the way
let vec1 = vec![ 0; 15 ];
println!( "{:p}", &vec1[ .. ] );
let mut buff = Cursor::new( vec1 );
let vec2 = &buff.into_inner();
println!( "{:p}", &vec2[ .. ] );
That code does not allocate unnecessary memory.
Maybe num-bigint
can borrow the logic and apply it?
Might be useful: https://github.com/sdroege/byte-slice-cast/blob/master/src/lib.rs