use ff::PrimeField as _;
use pasta_curves::Fp;
#[inline]
pub fn write_fp(buf: &mut [u8], fp: Fp) {
buf[..32].copy_from_slice(&fp.to_repr());
}
#[inline]
pub fn read_fp(buf: &[u8]) -> Fp {
let mut arr = [0u8; 32];
arr.copy_from_slice(&buf[..32]);
Fp::from_repr(arr).expect("read_fp: non-canonical Fp (caller must validate first)")
}
#[inline]
pub fn validate_fp_bytes(buf: &[u8]) -> anyhow::Result<()> {
anyhow::ensure!(
buf.len() == 32,
"invalid field element byte length: got {}, expected 32",
buf.len()
);
let mut arr = [0u8; 32];
arr.copy_from_slice(buf);
let fp = Fp::from_repr(arr);
anyhow::ensure!(
bool::from(fp.is_some()),
"non-canonical field element encoding"
);
Ok(())
}
pub fn validate_all_fp_chunks(data: &[u8], tier_label: &str) -> anyhow::Result<()> {
for (i, chunk) in data.chunks_exact(32).enumerate() {
validate_fp_bytes(chunk).map_err(|e| {
anyhow::anyhow!("{} invalid field element at 32-byte chunk {}: {}", tier_label, i, e)
})?;
}
Ok(())
}
pub fn binary_search_records(
data: &[u8],
base: usize,
count: usize,
record_size: usize,
key_offset: usize,
value: Fp,
) -> Option<usize> {
let mut lo = 0usize;
let mut hi = count;
while lo < hi {
let mid = lo + (hi - lo) / 2;
let k_off = base + mid * record_size + key_offset;
let k = read_fp(&data[k_off..k_off + 32]);
if k <= value {
lo = mid + 1;
} else {
hi = mid;
}
}
if lo == 0 { None } else { Some(lo - 1) }
}