use crate::prelude::*;
use bytemuck::{try_cast_slice, try_from_bytes};
use bytemuck::{Pod, Zeroable};
use std::cell::Ref;
use superslice::*;
#[zero_copy(unsafe)]
#[derive(Default, Debug)]
#[repr(packed)]
pub struct AggregatorHistoryRow {
pub timestamp: i64,
pub value: SwitchboardDecimal,
}
unsafe impl Pod for AggregatorHistoryRow {}
unsafe impl Zeroable for AggregatorHistoryRow {}
pub struct AggregatorHistoryBuffer<'a> {
pub insertion_idx: usize,
pub rows: Ref<'a, [AggregatorHistoryRow]>,
}
impl<'a> AggregatorHistoryBuffer<'a> {
pub fn new(
history_buffer: &'a AccountInfo,
) -> anchor_lang::Result<AggregatorHistoryBuffer<'a>> {
let data = history_buffer.try_borrow_data()?;
let mut disc_bytes = [0u8; 8];
disc_bytes.copy_from_slice(&data[..8]);
if disc_bytes != AggregatorHistoryBuffer::discriminator() {
return Err(SwitchboardError::AccountDiscriminatorMismatch.into());
}
let insertion_idx: u32 = *try_from_bytes::<u32>(&data[8..12]).unwrap();
return Ok(Self {
insertion_idx: insertion_idx as usize,
rows: Ref::map(data, |data| try_cast_slice(&data[12..]).unwrap()),
});
}
pub fn lower_bound(&self, timestamp: i64) -> Option<AggregatorHistoryRow> {
if self.rows[self.insertion_idx].timestamp == 0 {
return None;
}
let lower = &self.rows[..self.insertion_idx + 1];
let lahr = lower.lower_bound_by(|x| {
let other: i64 = x.timestamp;
other.cmp(×tamp)
});
if lahr < lower.len() && lower[lahr].timestamp == timestamp {
return Some(lower[lahr]);
}
if lahr != 0 {
return Some(lower[lahr - 1]);
}
if self.insertion_idx + 1 < self.rows.len()
&& self.rows[self.insertion_idx + 1].timestamp != 0
{
let upper = &self.rows[self.insertion_idx + 1..];
let uahr = upper.lower_bound_by(|x| {
let other: i64 = x.timestamp;
other.cmp(×tamp)
});
if uahr < upper.len() && upper[uahr].timestamp == timestamp {
return Some(upper[uahr]);
}
if uahr != 0 {
return Some(upper[uahr - 1]);
}
}
None
}
}
impl<'a> Discriminator for AggregatorHistoryBuffer<'a> {
const DISCRIMINATOR: [u8; 8] = [66, 85, 70, 70, 69, 82, 120, 120];
}
impl<'a> Owner for AggregatorHistoryBuffer<'a> {
fn owner() -> Pubkey {
*SWITCHBOARD_PROGRAM_ID
}
}
#[cfg(test)]
mod tests {
use super::*;
const HISTORY_BUFFER_DATA: [u8; 292] = [
66, 85, 70, 70, 69, 82, 120, 120, 1, 0, 0, 0, 212, 199, 31, 98, 0, 0, 0, 0, 69, 210, 158,
59, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 221, 199, 31, 98, 0, 0, 0, 0, 95,
37, 234, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 241, 198, 31, 98, 0, 0, 0, 0,
11, 195, 200, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 24, 199, 31, 98, 0, 0, 0,
0, 183, 43, 255, 101, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 153, 199, 31, 98, 0,
0, 0, 0, 117, 187, 242, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 165, 199, 31,
98, 0, 0, 0, 0, 69, 250, 67, 236, 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 174,
199, 31, 98, 0, 0, 0, 0, 83, 177, 74, 103, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0,
183, 199, 31, 98, 0, 0, 0, 0, 113, 186, 62, 0, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0,
0, 0, 190, 199, 31, 98, 0, 0, 0, 0, 146, 224, 37, 87, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8, 0, 0, 0, 201, 199, 31, 98, 0, 0, 0, 0, 215, 90, 246, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 7, 0, 0, 0,
];
const HISTORY_BUFFER_PUBKEY: Pubkey = Pubkey::new_from_array([
188, 221, 119, 59, 130, 153, 226, 148, 95, 158, 33, 63, 106, 233, 240, 46, 242, 141, 150,
147, 148, 158, 88, 14, 59, 66, 18, 82, 181, 250, 102, 130,
]);
#[test]
fn test_history_buffer() {
let mut history_data = HISTORY_BUFFER_DATA;
let mut lamports = 0;
let history_account_info = AccountInfo::new(
&HISTORY_BUFFER_PUBKEY,
false,
false,
&mut lamports,
&mut history_data,
&SWITCHBOARD_PROGRAM_ID,
false,
0,
);
let history_buffer = AggregatorHistoryBuffer::new(&history_account_info).unwrap();
match history_buffer.lower_bound(1646249940) {
None => panic!("failed to retrieve a value for a valid timestamp"),
Some(row) => {
let correct_value = SwitchboardDecimal {
mantissa: 1006022611525,
scale: 10,
};
if row.value != correct_value {
panic!(
"failed to retrieve correct value at exact timestamp 1646249940. received: {:?}, expected: {:?}",
row.value, correct_value
)
}
}
};
match history_buffer.lower_bound(1646249949) {
None => panic!("failed to retrieve a value for a valid timestamp"),
Some(row) => {
let correct_value = SwitchboardDecimal {
mantissa: 1005200735,
scale: 7,
};
if row.value != correct_value {
panic!(
"failed to retrieve correct value at exact timestamp 1646249940. received: {:?}, expected: {:?}",
row.value, correct_value
)
}
}
};
match history_buffer.lower_bound(1646249911) {
None => panic!("failed to retrieve a value for a valid timestamp"),
Some(row) => {
let correct_value = SwitchboardDecimal {
mantissa: 1005026458225,
scale: 10,
};
if row.value != correct_value {
panic!(
"failed to retrieve correct value at exact timestamp 1646249911. received: {:?}, expected: {:?}",
row.value, correct_value
)
}
}
};
match history_buffer.lower_bound(1646249929) {
None => panic!("failed to retrieve a value for a valid timestamp"),
Some(row) => {
let correct_value = SwitchboardDecimal {
mantissa: 1006000855,
scale: 7,
};
if row.value != correct_value {
panic!(
"failed to retrieve correct value at exact timestamp 1646249911. received: {:?}, expected: {:?}",
row.value, correct_value
)
}
}
};
match history_buffer.lower_bound(1646249912) {
None => panic!("failed to retrieve a value for a valid timestamp"),
Some(row) => {
let correct_value = SwitchboardDecimal {
mantissa: 1005026458225,
scale: 10,
};
if row.value != correct_value {
panic!("failed to retrieve correct value for timestamp 1646249912. received: {:?}, expected: {:?}",row.value, correct_value)
}
}
};
match history_buffer.lower_bound(1646249910) {
None => panic!("failed to retrieve a value for a valid timestamp"),
Some(row) => {
let correct_value = SwitchboardDecimal {
mantissa: 100517196115,
scale: 9,
};
if row.value != correct_value {
panic!(
"failed to retrieve correct value for timestamp 1646249910. received: {:?}, expected: {:?}",
row.value, correct_value
)
}
}
};
match history_buffer.lower_bound(2646249911) {
None => panic!("failed to retrieve a value for a valid timestamp"),
Some(row) => {
let correct_value = SwitchboardDecimal {
mantissa: 1005200735,
scale: 7,
};
if row.value != correct_value {
panic!("failed to retrieve correct value for timestamp 2646249911. received: {:?}, expected: {:?}",row.value, correct_value)
}
}
};
match history_buffer.lower_bound(646249911) {
None => (),
Some(row) => panic!("retrieved row when no value was expected {:?}", row.value),
};
}
}