Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 47 additions & 10 deletions ff/src/fields/prime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,32 @@ pub trait PrimeField:
/// If the integer represented by `bytes` is larger than the modulus `p`, this method
/// performs the appropriate reduction.
fn from_be_bytes_mod_order(bytes: &[u8]) -> Self {
let mut bytes_copy = bytes.to_vec();
bytes_copy.reverse();
Self::from_le_bytes_mod_order(&bytes_copy)
// Process big-endian input without allocating by accumulating in base 2^64.
let pow256 = Self::from(256u64);
let window64 = pow256.pow(&[8]);

let mut res = Self::from(0u64);

// Handle a possible leading partial chunk (most-significant bytes).
let rem_len = bytes.len() % 8;
if rem_len != 0 {
let mut acc: u64 = 0;
for &b in &bytes[..rem_len] {
acc = (acc << 8) | (b as u64);
}
// Multiply by 256^rem_len and add the partial limb.
let window_small = pow256.pow(&[rem_len as u64]);
res *= window_small;
res += Self::from(acc);
}

// Process remaining bytes in 8-byte big-endian chunks.
for chunk in bytes[rem_len..].chunks(8) {
let limb = u64::from_be_bytes(chunk.try_into().expect("chunk size is 8"));
res *= window64;
res += Self::from(limb);
}
res
}

/// Reads bytes in little-endian, and converts them to a field element.
Expand All @@ -82,14 +105,28 @@ pub trait PrimeField:
// Guaranteed to not be None, as the input is less than the modulus size.
let mut res = Self::from_random_bytes(bytes_to_directly_convert).unwrap();

// Update the result, byte by byte.
// Update the result using 8-byte chunks for better performance.
// We go through existing field arithmetic, which handles the reduction.
// TODO: If we need higher speeds, parse more bytes at once, or implement
// modular multiplication by a u64
let window_size = Self::from(256u64);
for byte in bytes.iter().rev() {
res *= window_size;
res += Self::from(*byte);
// TODO: Already parsing in 8-byte chunks; consider specialized mul-by-u64
// or even wider chunk sizes if profiling shows further benefit.
let pow256 = Self::from(256u64);
let window64 = pow256.pow(&[8]);

let mut iter = bytes.rchunks_exact(8);
for chunk in &mut iter {
let limb = u64::from_le_bytes(chunk.try_into().expect("chunk size is 8"));
res *= window64;
res += Self::from(limb);
}
let rem = iter.remainder();
if !rem.is_empty() {
let window_small = pow256.pow(&[rem.len() as u64]);
res *= window_small;
let mut acc: u64 = 0;
for (i, b) in rem.iter().enumerate() {
acc |= (*b as u64) << (8 * i);
}
res += Self::from(acc);
}
res
}
Expand Down
29 changes: 29 additions & 0 deletions test-templates/src/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ macro_rules! __test_field {
let e1: [u64; 10] = rng.gen();
let e2: [u64; 10] = rng.gen();
assert_eq!(a.pow(&e1).pow(&e2), a.pow(&e2).pow(&e1));
assert_eq!(a.pow(&e1).pow(&e2), a.pow(&e2).pow(&e1));

// Distributivity
let e3: [u64; 10] = rng.gen();
Expand Down Expand Up @@ -498,6 +499,34 @@ macro_rules! __test_field {
},
}
}

#[test]
fn test_from_bytes_mod_order_matches_biguint() {
use $crate::num_bigint::BigUint;
use ark_std::rand::Rng;

let mut rng = test_rng();
let modulus: BigUint = <$field>::MODULUS.into();

let max_len = (2 * <$field>::MODULUS_BIT_SIZE / 8 + 2) as usize;

for _ in 0..ITERATIONS {
let len = rng.gen_range(0..=max_len);
let data: Vec<u8> = (0..len).map(|_| rng.gen()).collect();

let int_be = BigUint::from_bytes_be(&data) % &modulus;
let int_le = BigUint::from_bytes_le(&data) % &modulus;

let expected_be = <$field>::from(int_be);
let expected_le = <$field>::from(int_le);

let actual_be = <$field>::from_be_bytes_mod_order(&data);
let actual_le = <$field>::from_le_bytes_mod_order(&data);

assert_eq!(expected_be, actual_be);
assert_eq!(expected_le, actual_le);
}
}
};
($field: ty; mont_prime_field) => {
$crate::__test_field!($field; prime);
Expand Down
Loading