use std::fmt::{self, Display, Formatter};
use casper_types::{
bytesrepr::{self, FromBytes, ToBytes},
AvailableBlockRange,
};
use datasize::DataSize;
use itertools::Itertools;
use tracing::trace;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum InsertOutcome {
TooHigh,
ExtendedHigh,
AlreadyInSequence,
ExtendedLow,
TooLow,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, DataSize, Ord, PartialOrd)]
pub(crate) struct Sequence {
high: u64,
low: u64,
}
impl Sequence {
pub(super) fn new(a: u64, b: u64) -> Self {
let (low, high) = if a <= b { (a, b) } else { (b, a) };
Sequence { low, high }
}
fn single(value: u64) -> Self {
Sequence {
high: value,
low: value,
}
}
fn try_insert(&mut self, value: u64) -> InsertOutcome {
if value == self.high + 1 {
self.high = value;
InsertOutcome::ExtendedHigh
} else if value >= self.low && value <= self.high {
InsertOutcome::AlreadyInSequence
} else if value + 1 == self.low {
self.low = value;
InsertOutcome::ExtendedLow
} else if value > self.high {
InsertOutcome::TooHigh
} else {
InsertOutcome::TooLow
}
}
pub(crate) fn high(&self) -> u64 {
self.high
}
pub(crate) fn low(&self) -> u64 {
self.low
}
}
impl From<Sequence> for AvailableBlockRange {
fn from(sequence: Sequence) -> Self {
AvailableBlockRange::new(sequence.low(), sequence.high())
}
}
#[derive(Default, Debug, DataSize)]
#[cfg_attr(test, derive(Clone))]
pub(super) struct DisjointSequences {
sequences: Vec<Sequence>,
}
impl DisjointSequences {
pub(super) fn new(initial_sequence: Sequence) -> Self {
DisjointSequences {
sequences: vec![initial_sequence],
}
}
pub(super) fn insert(&mut self, value: u64) -> bool {
let mut iter_mut = self.sequences.iter_mut().enumerate().peekable();
let mut maybe_insertion_index = Some(0);
let mut maybe_removal_index = None;
let mut added_new_value = true;
while let Some((index, sequence)) = iter_mut.next() {
match sequence.try_insert(value) {
InsertOutcome::ExtendedHigh => {
maybe_insertion_index = None;
break;
}
InsertOutcome::AlreadyInSequence => {
maybe_insertion_index = None;
added_new_value = false;
break;
}
InsertOutcome::TooHigh => {
maybe_insertion_index = Some(index);
break;
}
InsertOutcome::TooLow => {
maybe_insertion_index = Some(index + 1);
}
InsertOutcome::ExtendedLow => {
maybe_insertion_index = None;
if let Some((next_index, next_sequence)) = iter_mut.peek() {
if next_sequence.high + 1 == sequence.low {
sequence.low = next_sequence.low;
maybe_removal_index = Some(*next_index);
}
}
break;
}
};
}
if let Some(index_to_insert) = maybe_insertion_index {
self.sequences
.insert(index_to_insert, Sequence::single(value));
}
if let Some(index_to_remove) = maybe_removal_index {
let _ = self.sequences.remove(index_to_remove);
}
trace!(%self, "current state of disjoint sequences");
added_new_value
}
pub(super) fn highest_sequence(&self) -> Option<&Sequence> {
self.sequences.first()
}
pub(super) fn sequences(&self) -> &Vec<Sequence> {
&self.sequences
}
pub(super) fn truncate(&mut self, max_value: u64) {
self.sequences.retain_mut(|sequence| {
if sequence.high <= max_value {
return true;
}
if sequence.low > max_value {
return false;
}
sequence.high = max_value;
true
})
}
}
#[cfg(test)]
impl DisjointSequences {
fn extend<T: IntoIterator<Item = u64>>(&mut self, iter: T) {
iter.into_iter().for_each(|height| {
self.insert(height);
})
}
fn contains(&self, value: u64) -> bool {
self.sequences
.iter()
.any(|sequence| value >= sequence.low && value <= sequence.high)
}
}
impl FromBytes for Sequence {
#[inline]
fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
let (high, bytes) = u64::from_bytes(bytes)?;
let (low, bytes) = u64::from_bytes(bytes)?;
Ok((Sequence { high, low }, bytes))
}
}
impl ToBytes for Sequence {
#[inline]
fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
let mut buf = Vec::new();
self.write_bytes(&mut buf)?;
Ok(buf)
}
#[inline]
fn serialized_length(&self) -> usize {
self.high.serialized_length() + self.low.serialized_length()
}
#[inline]
fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
self.high.write_bytes(writer)?;
self.low.write_bytes(writer)?;
Ok(())
}
}
impl FromBytes for DisjointSequences {
fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
Vec::<Sequence>::from_bytes(bytes)
.map(|(sequences, remainder)| (DisjointSequences { sequences }, remainder))
}
#[inline]
fn from_vec(bytes: Vec<u8>) -> Result<(Self, Vec<u8>), bytesrepr::Error> {
Vec::<Sequence>::from_vec(bytes)
.map(|(sequences, remainder)| (DisjointSequences { sequences }, remainder))
}
}
impl ToBytes for DisjointSequences {
#[inline]
fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
self.sequences.to_bytes()
}
#[inline]
fn serialized_length(&self) -> usize {
self.sequences.serialized_length()
}
fn into_bytes(self) -> Result<Vec<u8>, bytesrepr::Error>
where
Self: Sized,
{
self.sequences.into_bytes()
}
fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
self.sequences.write_bytes(writer)
}
}
impl From<Vec<u64>> for DisjointSequences {
fn from(mut input: Vec<u64>) -> Self {
input.sort_unstable();
let sequences = input
.drain(..)
.peekable()
.batching(|iter| match iter.next() {
None => None,
Some(low) => {
let mut sequence = Sequence::single(low);
while let Some(i) = iter.peek() {
if *i == sequence.high + 1 {
sequence.high = iter.next().unwrap();
}
}
Some(sequence)
}
})
.collect();
DisjointSequences { sequences }
}
}
impl Display for DisjointSequences {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
let mut iter = self.sequences.iter().peekable();
while let Some(sequence) = iter.next() {
write!(formatter, "[{}, {}]", sequence.high, sequence.low)?;
if iter.peek().is_some() {
write!(formatter, ", ")?;
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeSet;
use rand::{seq::SliceRandom, Rng};
use super::*;
fn new_sequence(a: u64, b: u64) -> Sequence {
let (low, high) = if a <= b { (a, b) } else { (b, a) };
assert!(low <= high);
Sequence { low, high }
}
fn assert_matches(actual: &DisjointSequences, expected: &BTreeSet<u64>) {
let mut actual_set = BTreeSet::new();
for sequence in &actual.sequences {
for i in sequence.low..=sequence.high {
assert!(actual_set.insert(i));
}
}
assert_eq!(&actual_set, expected)
}
#[test]
fn should_insert_all_u8s_including_duplicates() {
let mut rng = crate::new_rng();
let mut disjoint_sequences = DisjointSequences::default();
let mut expected = BTreeSet::new();
while disjoint_sequences.sequences != vec![Sequence { high: 255, low: 0 }] {
let value = rng.gen::<u8>() as u64;
let insertion_result = !disjoint_sequences.contains(value);
assert_eq!(insertion_result, disjoint_sequences.insert(value));
expected.insert(value);
assert_matches(&disjoint_sequences, &expected);
}
}
#[test]
fn should_extend() {
let to_be_inserted = vec![5_u64, 4, 3, 2, 1];
let mut expected = BTreeSet::new();
expected.extend(to_be_inserted.clone());
let mut disjoint_sequences = DisjointSequences::default();
disjoint_sequences.extend(to_be_inserted);
assert_matches(&disjoint_sequences, &expected);
disjoint_sequences.extend(Vec::<u64>::new());
assert_matches(&disjoint_sequences, &expected);
}
#[test]
fn should_insert_with_no_duplicates() {
const MAX: u64 = 1000;
let mut rng = crate::new_rng();
let mut values = (0..=MAX).collect::<Vec<u64>>();
values.shuffle(&mut rng);
let mut disjoint_sequences = DisjointSequences::default();
let mut expected = BTreeSet::new();
for value in values {
assert!(disjoint_sequences.insert(value));
expected.insert(value);
assert_matches(&disjoint_sequences, &expected);
}
assert_eq!(
disjoint_sequences.sequences,
vec![Sequence { high: MAX, low: 0 }]
);
}
#[test]
fn should_construct_from_random_set() {
const MAX: u64 = 2_000_000;
let mut rng = crate::new_rng();
let mut values = (0..=MAX).collect::<Vec<u64>>();
values.shuffle(&mut rng);
let disjoint_sequences = DisjointSequences::from(values);
assert_eq!(
disjoint_sequences.sequences,
vec![Sequence { high: MAX, low: 0 }]
);
}
#[test]
fn should_get_highest_sequence() {
let mut disjoint_sequences = DisjointSequences::default();
assert_eq!(disjoint_sequences.highest_sequence(), None);
disjoint_sequences.extend([1]);
assert_eq!(
disjoint_sequences.highest_sequence(),
Some(&Sequence { low: 1, high: 1 })
);
disjoint_sequences.extend([5, 6]);
assert_eq!(
disjoint_sequences.highest_sequence(),
Some(&Sequence { low: 5, high: 6 })
);
disjoint_sequences.extend([8, 9]);
assert_eq!(
disjoint_sequences.highest_sequence(),
Some(&Sequence { low: 8, high: 9 })
);
}
#[test]
fn should_truncate() {
const SEQ_HIGH: Sequence = Sequence { high: 11, low: 9 };
const SEQ_MID: Sequence = Sequence { high: 6, low: 6 };
const SEQ_LOW: Sequence = Sequence { high: 3, low: 1 };
let initial_sequences = DisjointSequences {
sequences: vec![SEQ_HIGH, SEQ_MID, SEQ_LOW],
};
let mut disjoint_sequences = initial_sequences.clone();
disjoint_sequences.truncate(12);
assert_eq!(disjoint_sequences.sequences, initial_sequences.sequences);
disjoint_sequences.truncate(11);
assert_eq!(disjoint_sequences.sequences, initial_sequences.sequences);
disjoint_sequences = initial_sequences.clone();
disjoint_sequences.truncate(SEQ_HIGH.low - 1);
assert_eq!(disjoint_sequences.sequences, vec![SEQ_MID, SEQ_LOW]);
disjoint_sequences = initial_sequences.clone();
disjoint_sequences.truncate(SEQ_MID.high);
assert_eq!(disjoint_sequences.sequences, vec![SEQ_MID, SEQ_LOW]);
disjoint_sequences = initial_sequences.clone();
disjoint_sequences.truncate(SEQ_MID.low - 1);
assert_eq!(disjoint_sequences.sequences, vec![SEQ_LOW]);
disjoint_sequences = initial_sequences.clone();
disjoint_sequences.truncate(SEQ_LOW.high);
assert_eq!(disjoint_sequences.sequences, vec![SEQ_LOW]);
disjoint_sequences = initial_sequences.clone();
disjoint_sequences.truncate(SEQ_LOW.low - 1);
assert!(disjoint_sequences.sequences.is_empty());
disjoint_sequences = initial_sequences.clone();
let max_value = SEQ_HIGH.high - 1;
disjoint_sequences.truncate(max_value);
assert_eq!(
disjoint_sequences.sequences,
vec![new_sequence(max_value, SEQ_HIGH.low), SEQ_MID, SEQ_LOW]
);
disjoint_sequences = initial_sequences.clone();
let max_value = SEQ_HIGH.low;
disjoint_sequences.truncate(max_value);
assert_eq!(
disjoint_sequences.sequences,
vec![new_sequence(max_value, SEQ_HIGH.low), SEQ_MID, SEQ_LOW]
);
disjoint_sequences = initial_sequences.clone();
let max_value = SEQ_MID.low;
disjoint_sequences.truncate(max_value);
assert_eq!(disjoint_sequences.sequences, vec![SEQ_MID, SEQ_LOW]);
disjoint_sequences = initial_sequences.clone();
let max_value = SEQ_LOW.high - 1;
disjoint_sequences.truncate(max_value);
assert_eq!(
disjoint_sequences.sequences,
vec![new_sequence(max_value, SEQ_LOW.low)]
);
disjoint_sequences = initial_sequences;
let max_value = SEQ_LOW.low;
disjoint_sequences.truncate(max_value);
assert_eq!(
disjoint_sequences.sequences,
vec![new_sequence(max_value, SEQ_LOW.low)]
);
disjoint_sequences = DisjointSequences::default();
assert!(disjoint_sequences.sequences.is_empty());
disjoint_sequences.truncate(100);
assert!(disjoint_sequences.sequences.is_empty());
}
#[test]
fn roundtrip_to_bytes() {
let mut disjoint_sequences = DisjointSequences::default();
disjoint_sequences.extend([4, 5, 6, 7, 8]);
disjoint_sequences.extend([15, 16, 17, 18, 19, 20]);
let expected = [
0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let actual = disjoint_sequences.to_bytes().expect("serialization failed");
assert_eq!(expected.as_slice(), &actual);
let expected_inner_state = disjoint_sequences.sequences;
let (restored, remainder) =
DisjointSequences::from_bytes(&actual).expect("deserialization failed");
assert!(remainder.is_empty());
let (restored2, remainder) =
DisjointSequences::from_vec(actual).expect("deserialization failed");
assert!(remainder.is_empty());
assert_eq!(restored.sequences, expected_inner_state);
assert_eq!(restored2.sequences, expected_inner_state);
}
}