use crate::types::{ArchivedQueueDiff, QueueDiff};
fn find_chunk_aligned(haystack: &[u8], needle: &[u8], item_ssz_size: usize) -> Option<usize> {
if needle.len() != item_ssz_size {
return None;
}
haystack
.chunks_exact(item_ssz_size)
.position(|chunk| chunk == needle)
.map(|idx| idx * item_ssz_size)
}
pub fn diff_queue(base_ssz: &[u8], target_ssz: &[u8], item_ssz_size: usize) -> QueueDiff {
if target_ssz.is_empty() {
return QueueDiff::Fifo {
consumed_count: base_ssz.len() as u32 / item_ssz_size as u32,
appended_items: Vec::new(),
};
}
if base_ssz.is_empty() {
return QueueDiff::Fifo {
consumed_count: 0,
appended_items: target_ssz.to_vec(),
};
}
let target_head = &target_ssz[..item_ssz_size];
match find_chunk_aligned(base_ssz, target_head, item_ssz_size) {
Some(byte_offset) => {
let remaining_base_bytes = &base_ssz[byte_offset..];
let expected_target_prefix_len = remaining_base_bytes.len();
if expected_target_prefix_len <= target_ssz.len()
&& &target_ssz[..expected_target_prefix_len] == remaining_base_bytes
{
let consumed_count = (byte_offset / item_ssz_size) as u32;
let appended_items = target_ssz[expected_target_prefix_len..].to_vec();
QueueDiff::Fifo {
consumed_count,
appended_items,
}
} else {
QueueDiff::FullReplacement(target_ssz.to_vec())
}
}
None => {
QueueDiff::FullReplacement(target_ssz.to_vec())
}
}
}
pub fn apply_queue(base: &mut Vec<u8>, delta: &ArchivedQueueDiff, item_ssz_size: usize) {
let delta: QueueDiff = rkyv::deserialize::<QueueDiff, rkyv::rancor::Error>(delta)
.expect("Failed to deserialize PendingDepositsDiff");
match delta {
QueueDiff::Fifo {
consumed_count,
appended_items,
} => {
let bytes_to_drain = consumed_count as usize * item_ssz_size;
if bytes_to_drain > base.len() {
base.clear();
} else {
base.drain(..bytes_to_drain);
}
if !appended_items.is_empty() {
base.extend_from_slice(&appended_items);
}
}
QueueDiff::FullReplacement(replacement) => {
base.clear();
if !replacement.is_empty() {
base.extend_from_slice(&replacement);
}
}
}
}