use crate::dispatch_buffers::{ensure_input_slots, write_u32_slice_le_bytes, write_zero_u32_words};
use crate::optimizer::dispatcher::DispatchError;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct U32SliceFingerprint {
words: usize,
lo: u64,
hi: u64,
}
#[must_use]
pub(crate) fn fingerprint_u32_slice(values: &[u32]) -> U32SliceFingerprint {
let mut lo = 0x9E37_79B9_7F4A_7C15_u64 ^ values.len() as u64;
let mut hi = 0xC2B2_AE3D_27D4_EB4F_u64 ^ (values.len() as u64).rotate_left(32);
for &value in values {
let word = u64::from(value);
lo ^= word
.wrapping_add(0x9E37_79B9_7F4A_7C15)
.wrapping_add(lo << 6)
.wrapping_add(lo >> 2);
lo = lo.rotate_left(27).wrapping_mul(0x94D0_49BB_1331_11EB);
hi ^= word
.rotate_left(17)
.wrapping_add(lo)
.wrapping_add(hi << 7)
.wrapping_add(hi >> 3);
hi = hi.rotate_left(31).wrapping_mul(0xD6E8_FD9D_DA37_3C91);
}
U32SliceFingerprint {
words: values.len(),
lo,
hi,
}
}
#[derive(Clone, Copy)]
pub(crate) enum DispatchInput<'a> {
U32Slice(&'a [u32]),
U32SliceOrZeroWords {
values: &'a [u32],
words: usize,
context: &'static str,
},
ZeroU32Words {
words: usize,
context: &'static str,
},
}
impl<'a> DispatchInput<'a> {
pub(crate) fn u32_slice(values: &'a [u32]) -> Self {
Self::U32Slice(values)
}
pub(crate) fn u32_slice_or_zero_words(
values: &'a [u32],
words: usize,
context: &'static str,
) -> Self {
Self::U32SliceOrZeroWords {
values,
words,
context,
}
}
pub(crate) fn zero_u32_words(words: usize, context: &'static str) -> Self {
Self::ZeroU32Words { words, context }
}
}
pub(crate) fn prepare_dispatch_inputs(
scratch_inputs: &mut Vec<Vec<u8>>,
inputs: &[DispatchInput<'_>],
) -> Result<(), DispatchError> {
write_dispatch_inputs(scratch_inputs, inputs)
}
pub(crate) fn refresh_keyed_dispatch_inputs<K: Copy + Eq>(
scratch_inputs: &mut Vec<Vec<u8>>,
current_key: &mut Option<K>,
next_key: K,
all_inputs: &[DispatchInput<'_>],
mutable_inputs: &[(usize, DispatchInput<'_>)],
) -> Result<(), DispatchError> {
if *current_key == Some(next_key) && scratch_inputs.len() == all_inputs.len() {
for &(slot_index, input) in mutable_inputs {
let Some(slot) = scratch_inputs.get_mut(slot_index) else {
return Err(DispatchError::BadInputs(format!(
"Fix: keyed graph dispatch mutable input slot {slot_index} exceeds prepared input count {}.",
scratch_inputs.len()
)));
};
write_dispatch_input(slot, input)?;
}
return Ok(());
}
prepare_dispatch_inputs(scratch_inputs, all_inputs)?;
*current_key = Some(next_key);
Ok(())
}
pub(super) fn write_dispatch_inputs(
scratch_inputs: &mut Vec<Vec<u8>>,
inputs: &[DispatchInput<'_>],
) -> Result<(), DispatchError> {
ensure_input_slots(scratch_inputs, inputs.len());
for (slot, input) in scratch_inputs.iter_mut().zip(inputs.iter().copied()) {
write_dispatch_input(slot, input)?;
}
Ok(())
}
pub(crate) fn write_dispatch_input(
slot: &mut Vec<u8>,
input: DispatchInput<'_>,
) -> Result<(), DispatchError> {
match input {
DispatchInput::U32Slice(values) => write_u32_slice_le_bytes(slot, values),
DispatchInput::U32SliceOrZeroWords {
values,
words,
context,
} => {
if values.is_empty() {
write_zero_u32_words(slot, words, context)?;
} else {
write_u32_slice_le_bytes(slot, values);
}
}
DispatchInput::ZeroU32Words { words, context } => {
write_zero_u32_words(slot, words, context)?;
}
}
Ok(())
}