Signal query #5

Merged
ThePerfectComputer merged 9 commits from signal_query into main 2022-09-02 19:08:26 +00:00
8 changed files with 326 additions and 210 deletions

View file

@ -20,7 +20,7 @@ fn main() -> std::io::Result<()> {
let now = Instant::now();
let file = File::open(&args.path)?;
let vcd = parse_vcd(file).unwrap();
parse_vcd(file).unwrap();
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);

View file

@ -2,13 +2,13 @@ mod reader;
use reader::*;
mod types;
pub(super) use types::*;
use types::*;
mod parse;
pub(super) use parse::*;
pub use parse::*;
mod signal;
pub(super) use signal::*;
use signal::*;
mod utilities;
use utilities::*;

View file

@ -39,6 +39,20 @@ pub fn parse_vcd(file: File) -> Result<VCD, String> {
parse_scopes(&mut word_gen, &mut vcd, &mut signal_map)?;
parse_events(&mut word_gen, &mut vcd, &mut signal_map)?;
let signal = vcd.try_dereference_alias(signal_map.get("Q").unwrap())?;
let name = match signal {
Signal::Data { name, .. } => name,
_ => "ERROR",
};
let val = signal
.query_num_val_on_tmln(
BigUint::from(4687u32),
&vcd.tmstmps_encoded_as_u8s,
&vcd.all_signals,
)
.unwrap();
dbg!(format!("{val:#X}"));
dbg!(name);
Ok(vcd)
}

View file

@ -1,3 +1,5 @@
use num::Zero;
use super::*;
pub(super) fn parse_events<'a>(
@ -7,6 +9,7 @@ pub(super) fn parse_events<'a>(
) -> Result<(), String> {
let mut curr_tmstmp_lsb_idx = 0u32;
let mut curr_tmstmp_len_u8 = 0u8;
let mut curr_time = BigUint::zero();
loop {
let next_word = word_reader.next_word();
@ -35,6 +38,7 @@ pub(super) fn parse_events<'a>(
"Error near {f}:{l}. Failed to parse {value} as BigInt at {cursor:?}"
)
})?;
curr_time = value.clone();
let mut value = value.to_bytes_le();
// TODO : u32 helps with less memory, but should ideally likely be
// configurable.
@ -45,16 +49,15 @@ pub(super) fn parse_events<'a>(
line!()
)
})?;
vcd.tmstmps_encoded_as_u8s.append(&mut value);
curr_tmstmp_lsb_idx =
u32::try_from(vcd.tmstmps_encoded_as_u8s.len()).map_err(|_| {
format!(
"Error near {}:{}. Failed to convert from usize to u8.",
"Error near {}:{}. Failed to convert from usize to u32.",
file!(),
line!()
)
})?;
// curr_tmstmp_lsb_idx = vcd.tmstmps_encoded_as_u8s.len();
vcd.tmstmps_encoded_as_u8s.append(&mut value);
}
// handle the case of an n bit signal whose value must be parsed
@ -111,24 +114,19 @@ pub(super) fn parse_events<'a>(
let signal = vcd.try_dereference_alias_mut(signal_idx)?;
// we may have to dereference a signal if it's pointing at an alias
// let signal = &vcd.all_signals[*signal_idx];
// let signal = signal.try_dereference_alias_mut(&vcd.all_signals)?;
match signal {
Signal::Data {
name,
sig_type,
ref mut signal_error,
num_bits,
self_idx,
num_bytes,
nums_encoded_as_fixed_width_le_u8,
string_vals,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
byte_len_of_num_tmstmp_vals_on_tmln,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
byte_len_of_string_tmstmp_vals_on_tmln,
scope_parent,
..
} => {
// we've already identified in a prior loop iteration that the signal has
// an error
@ -173,6 +171,19 @@ pub(super) fn parse_events<'a>(
string_vals.push(value_string);
Ok(())
} else {
// timestamp stuff
lsb_indxs_of_num_tmstmp_vals_on_tmln
.push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
byte_len_of_num_tmstmp_vals_on_tmln.push(curr_tmstmp_len_u8);
// value stuff
// we may need to zero extend values
// so that we end up storing all values
// of a particular signal in a consistent
// amount of bytes
let bytes_required = num_bytes.ok_or_else(|| {
format!("Error near {}:{}. num_bytes empty.", file!(), line!())
})?;
let mut curr_num_bytes =
u8::try_from(value_u8.len()).map_err(|_| {
format!(
@ -183,20 +194,12 @@ pub(super) fn parse_events<'a>(
line!()
)
})?;
lsb_indxs_of_num_tmstmp_vals_on_tmln
.push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
// we may need to zero extend values
// so that we end up storing all values
// of a particular signal in a consistent
// amount of bytes
let bytes_required = Signal::bytes_required(num_bits, name)?;
nums_encoded_as_fixed_width_le_u8.append(&mut value_u8);
while curr_num_bytes < bytes_required {
nums_encoded_as_fixed_width_le_u8.push(0u8);
curr_num_bytes += 1;
}
byte_len_of_num_tmstmp_vals_on_tmln.push(curr_num_bytes);
Ok(())
}
}
@ -230,14 +233,11 @@ pub(super) fn parse_events<'a>(
sig_type,
ref mut signal_error,
num_bits,
self_idx,
num_bytes,
nums_encoded_as_fixed_width_le_u8,
string_vals,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
byte_len_of_num_tmstmp_vals_on_tmln,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
byte_len_of_string_tmstmp_vals_on_tmln,
scope_parent,
..
} => {
// if this is a bad signal, go ahead and skip it
if signal_error.is_some() {
@ -274,10 +274,25 @@ pub(super) fn parse_events<'a>(
Err(msg)?;
}
};
nums_encoded_as_fixed_width_le_u8.push(0u8);
// timestamp stuff
lsb_indxs_of_num_tmstmp_vals_on_tmln
.push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
byte_len_of_num_tmstmp_vals_on_tmln.push(1u8);
byte_len_of_num_tmstmp_vals_on_tmln.push(curr_tmstmp_len_u8);
// value stuff
// we may need to zero extend values
// so that we end up storing all values
// of a particular signal in a consistent
// amount of bytes
let bytes_required = num_bytes.ok_or_else(|| {
format!("Error near {}:{}. num_bytes empty.", file!(), line!())
})?;
nums_encoded_as_fixed_width_le_u8.push(0u8);
let mut curr_num_bytes = 1;
while curr_num_bytes < bytes_required {
nums_encoded_as_fixed_width_le_u8.push(0u8);
curr_num_bytes += 1;
}
Ok(())
}
Signal::Alias { .. } => {
@ -309,14 +324,11 @@ pub(super) fn parse_events<'a>(
sig_type,
ref mut signal_error,
num_bits,
self_idx,
num_bytes,
nums_encoded_as_fixed_width_le_u8,
string_vals,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
byte_len_of_num_tmstmp_vals_on_tmln,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
byte_len_of_string_tmstmp_vals_on_tmln,
scope_parent,
..
} => {
// if this is a bad signal, go ahead and skip it
if signal_error.is_some() {
@ -353,10 +365,25 @@ pub(super) fn parse_events<'a>(
Err(msg)?;
}
};
nums_encoded_as_fixed_width_le_u8.push(1u8);
// timestamp stuff
lsb_indxs_of_num_tmstmp_vals_on_tmln
.push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
byte_len_of_num_tmstmp_vals_on_tmln.push(1u8);
byte_len_of_num_tmstmp_vals_on_tmln.push(curr_tmstmp_len_u8);
// value stuff
// we may need to zero extend values
// so that we end up storing all values
// of a particular signal in a consistent
// amount of bytes
let bytes_required = num_bytes.ok_or_else(|| {
format!("Error near {}:{}. num_bytes empty.", file!(), line!())
})?;
nums_encoded_as_fixed_width_le_u8.push(1u8);
let mut curr_num_bytes = 1;
while curr_num_bytes < bytes_required {
nums_encoded_as_fixed_width_le_u8.push(0u8);
curr_num_bytes += 1;
}
Ok(())
}
Signal::Alias { .. } => {
@ -369,7 +396,7 @@ pub(super) fn parse_events<'a>(
}?;
}
// other one bit cases
// // other one bit cases
"x" | "X" | "z" | "Z" | "u" | "U" => {
let val = word.to_string();
// lokup signal idx
@ -390,14 +417,11 @@ pub(super) fn parse_events<'a>(
sig_type,
ref mut signal_error,
num_bits,
self_idx,
nums_encoded_as_fixed_width_le_u8,
string_vals,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
byte_len_of_num_tmstmp_vals_on_tmln,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
byte_len_of_string_tmstmp_vals_on_tmln,
scope_parent,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
..
} => {
// if this is a bad signal, go ahead and skip it
if signal_error.is_some() {
@ -434,9 +458,14 @@ pub(super) fn parse_events<'a>(
Err(msg)?;
}
};
string_vals.push(val);
// record timestamp at which this event occurs
lsb_indxs_of_string_tmstmp_vals_on_tmln
.push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
byte_len_of_string_tmstmp_vals_on_tmln.push(curr_tmstmp_len_u8);
// record value
string_vals.push(val);
Ok(())
}
Signal::Alias { .. } => {

View file

@ -48,8 +48,8 @@ pub(super) fn parse_var<'a>(
let parse_err = format!("failed to parse as usize on {cursor:?}");
// $var parameter 3 a IDLE $end
// ^ - no_bits
let no_bits = match var_type {
// ^ - num_bits
let num_bits = match var_type {
SigType::Integer
| SigType::Parameter
| SigType::Real
@ -57,16 +57,16 @@ pub(super) fn parse_var<'a>(
| SigType::Wire
| SigType::Tri1
| SigType::Time => {
let no_bits = word.parse::<usize>().expect(parse_err.as_str());
let no_bits = u16::try_from(no_bits).map_err(|_| {
let num_bits = word.parse::<usize>().expect(parse_err.as_str());
let num_bits = u16::try_from(num_bits).map_err(|_| {
format!(
"Error near {}:{} while parsing vcd file at {cursor:?}. \
This signal has {no_bits} > 2^16 - 1 bits.",
This signal has {num_bits} > 2^16 - 1 bits.",
file!(),
line!()
)
})?;
Some(no_bits)
Some(num_bits)
}
// for strings, we don't really care what the number of bits is
_ => None,
@ -89,6 +89,13 @@ pub(super) fn parse_var<'a>(
}
let full_signal_name = full_signal_name.join(" ");
let num_bytes = if num_bits.is_some() {
let bytes_required = Signal::bytes_required(num_bits.unwrap(), &full_signal_name)?;
Some(bytes_required)
} else {
None
};
// Is the current variable an alias to a signal already encountered?
// if so, handle ref_signal_idx accordingly, if not, add signal to hash
// map
@ -108,14 +115,15 @@ pub(super) fn parse_var<'a>(
name: full_signal_name,
sig_type: var_type,
signal_error: None,
num_bits: no_bits,
num_bits: num_bits,
num_bytes: num_bytes,
self_idx: signal_idx,
nums_encoded_as_fixed_width_le_u8: vec![],
string_vals: vec![],
lsb_indxs_of_num_tmstmp_vals_on_tmln: vec![],
byte_len_of_num_tmstmp_vals_on_tmln: vec![],
lsb_indxs_of_string_tmstmp_vals_on_tmln: vec![],
byte_len_of_string_tmstmp_vals_on_tmln: vec![],
lsb_indxs_of_string_tmstmp_vals_on_tmln: vec![],
scope_parent: parent_scope_idx,
};
(signal, signal_idx)

View file

@ -1,4 +1,3 @@
// use super::utilities::{ordered_binary_lookup_u8, LookupErrors};
use super::{ScopeIdx, SignalIdx};
use num::{BigUint, Zero};
@ -36,6 +35,7 @@ pub(super) enum Signal {
// errors in the following type:
signal_error: Option<String>,
num_bits: Option<u16>,
num_bytes: Option<u8>,
// TODO : may be able to remove self_idx
self_idx: SignalIdx,
// A signal may take on a new value and hold that value
@ -58,8 +58,8 @@ pub(super) enum Signal {
// a particular timestamp is composed of.
lsb_indxs_of_num_tmstmp_vals_on_tmln: Vec<LsbIdxOfTmstmpValOnTmln>,
byte_len_of_num_tmstmp_vals_on_tmln: Vec<u8>,
lsb_indxs_of_string_tmstmp_vals_on_tmln: Vec<LsbIdxOfTmstmpValOnTmln>,
byte_len_of_string_tmstmp_vals_on_tmln: Vec<u8>,
lsb_indxs_of_string_tmstmp_vals_on_tmln: Vec<LsbIdxOfTmstmpValOnTmln>,
scope_parent: ScopeIdx,
},
Alias {
@ -68,33 +68,30 @@ pub(super) enum Signal {
},
}
#[derive(Debug)]
pub(super) enum SignalErrors {
PreTimeline {
desired_time: BigUint,
timeline_start_time: BigUint,
},
EmptyTimeline,
TimelineNotMultiple,
OrderingFailure {
lhs_time: BigUint,
mid_time: BigUint,
rhs_time: BigUint,
},
PointsToAlias,
NoNumBytes,
Other(String),
}
// these are thin type aliases primarily to make code more readable later on
type TimeStamp = BigUint;
type SignalValNum = BigUint;
impl Signal {
pub(super) fn try_dereference_alias<'a>(
&'a self,
signals: &'a Vec<Signal>,
) -> Result<&Signal, String> {
// dereference a signal if we need to and return a signal, else return
// the signal itself
let signal = match self {
Signal::Data { .. } => self,
Signal::Alias { name, signal_alias } => {
let SignalIdx(idx) = signal_alias;
&signals[*idx]
}
};
match signal {
Signal::Data { .. } => Ok(signal),
Signal::Alias { .. } => Err(format!(
"Error near {}:{}. A signal alias shouldn't \
point to a signal alias.",
file!(),
line!()
)),
}
}
pub(super) fn bytes_required(num_bits: &Option<u16>, name: &String) -> Result<u8, String> {
let num_bits = num_bits
.ok_or_else(|| format!("Error near {}:{}. num_bits empty.", file!(), line!()))?;
pub(super) fn bytes_required(num_bits: u16, name: &String) -> Result<u8, String> {
let bytes_required = (num_bits / 8) + if (num_bits % 8) > 0 { 1 } else { 0 };
let bytes_required = u8::try_from(bytes_required).map_err(|_| {
format!(
@ -106,15 +103,175 @@ impl Signal {
})?;
Ok(bytes_required)
}
pub(super) fn query_value(&self, time: BigUint) -> Result<TimelineQueryResults, String> {
// match
// assert
// ordered_binary_lookup_u8(
// &value_sequence_as_bytes_u8,
// 4,
// &timeline_cursors,
// TimelineIdx(scrubbing_cursor),
// );
Ok(TimelineQueryResults::String("".to_string()))
pub(super) fn lookup_time_and_val(
&self,
event_idx: usize,
tmstmps_encoded_as_u8s: &Vec<u8>,
) -> Result<(TimeStamp, SignalValNum), SignalErrors> {
let (
num_bytes,
nums_encoded_as_fixed_width_le_u8,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
byte_len_of_num_tmstmp_vals_on_tmln,
) = match self {
Signal::Data {
num_bytes,
nums_encoded_as_fixed_width_le_u8,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
byte_len_of_num_tmstmp_vals_on_tmln,
..
} => Ok((
num_bytes,
nums_encoded_as_fixed_width_le_u8,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
byte_len_of_num_tmstmp_vals_on_tmln,
)),
Signal::Alias { .. } => Err(SignalErrors::PointsToAlias),
}?;
// get index
let LsbIdxOfTmstmpValOnTmln(timestamp_idx) =
lsb_indxs_of_num_tmstmp_vals_on_tmln[event_idx];
let timestamp_idx = timestamp_idx as usize;
// form timestamp
let byte_len = byte_len_of_num_tmstmp_vals_on_tmln[event_idx] as usize;
let timestamp = &tmstmps_encoded_as_u8s[timestamp_idx..(timestamp_idx + byte_len)];
let timestamp = BigUint::from_bytes_le(timestamp);
// get signal value
let bytes_per_value = num_bytes.ok_or_else(|| SignalErrors::NoNumBytes)?;
let bytes_per_value = bytes_per_value as usize;
let start_idx = event_idx * bytes_per_value;
let end_idx = (event_idx + 1) * bytes_per_value;
let signal_val = &nums_encoded_as_fixed_width_le_u8[start_idx..end_idx];
let signal_val = BigUint::from_bytes_le(signal_val);
Ok((timestamp, signal_val))
}
pub fn query_num_val_on_tmln(
&self,
desired_time: BigUint,
tmstmps_encoded_as_u8s: &Vec<u8>,
all_signals: &Vec<Signal>,
) -> Result<BigUint, SignalErrors> {
let signal_idx = match self {
Self::Data { self_idx, .. } => {
let SignalIdx(idx) = self_idx;
*idx
}
Self::Alias {
name: _,
signal_alias,
} => {
let SignalIdx(idx) = signal_alias;
*idx
}
};
let (nums_encoded_as_fixed_width_le_u8, lsb_indxs_of_num_tmstmp_vals_on_tmln, num_bytes) =
match &all_signals[signal_idx] {
Signal::Data {
num_bytes,
ref nums_encoded_as_fixed_width_le_u8,
ref lsb_indxs_of_num_tmstmp_vals_on_tmln,
..
} => {
if num_bytes.is_none() {
return Err(SignalErrors::NoNumBytes);
}
Ok((
nums_encoded_as_fixed_width_le_u8,
lsb_indxs_of_num_tmstmp_vals_on_tmln,
num_bytes,
))
}
Signal::Alias { .. } => Err(SignalErrors::PointsToAlias),
}?;
// this signal should at least have some events, otherwise, trying to index into
// an empty vector later on would fail
if lsb_indxs_of_num_tmstmp_vals_on_tmln.is_empty() {
return Err(SignalErrors::EmptyTimeline);
}
// assertion that value_sequence is a proper multiple of
// timeline_markers
let bytes_required = num_bytes.ok_or_else(|| {
SignalErrors::Other(format!(
"Error near {}:{}. num_bytes empty.",
file!(),
line!()
))
})?;
if nums_encoded_as_fixed_width_le_u8.len()
!= (lsb_indxs_of_num_tmstmp_vals_on_tmln.len() * (bytes_required as usize))
{
dbg!((
nums_encoded_as_fixed_width_le_u8.len(),
(lsb_indxs_of_num_tmstmp_vals_on_tmln.len() * (bytes_required as usize))
));
return Err(SignalErrors::TimelineNotMultiple);
}
// check if we're requesting a value that occurs before the recorded
// start of the timeline
let (timeline_start_time, _) = self.lookup_time_and_val(0, tmstmps_encoded_as_u8s)?;
if desired_time < timeline_start_time {
return Err(SignalErrors::PreTimeline {
desired_time: desired_time,
timeline_start_time: timeline_start_time,
});
}
let mut lower_idx = 0usize;
let mut upper_idx = lsb_indxs_of_num_tmstmp_vals_on_tmln.len() - 1;
let (timeline_end_time, timeline_end_val) =
self.lookup_time_and_val(upper_idx, tmstmps_encoded_as_u8s)?;
// check if we're requesting a value that occurs beyond the end of the timeline,
// if so, return the last value in this timeline
if desired_time > timeline_end_time {
return Ok(timeline_end_val);
}
// This while loop is the meat of the lookup. Performance is log2(n),
// where n is the number of events on the timeline.
// We can assume that by the time we get here, that the desired_time
// is an event that occurs on the timeline, given that we handle any events
// occuring after or before the recorded tiimeline in the code above.
while lower_idx <= upper_idx {
let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2);
let (curr_time, curr_val) =
self.lookup_time_and_val(mid_idx, tmstmps_encoded_as_u8s)?;
let ordering = curr_time.cmp(&desired_time);
match ordering {
std::cmp::Ordering::Less => {
lower_idx = mid_idx + 1;
}
std::cmp::Ordering::Equal => {
return Ok(curr_val);
}
std::cmp::Ordering::Greater => {
upper_idx = mid_idx - 1;
}
}
}
let (left_time, left_val) =
self.lookup_time_and_val(lower_idx - 1, tmstmps_encoded_as_u8s)?;
let (right_time, _) = self.lookup_time_and_val(lower_idx, tmstmps_encoded_as_u8s)?;
let ordered_left = left_time < desired_time;
let ordered_right = desired_time < right_time;
if !(ordered_left && ordered_right) {
return Err(SignalErrors::OrderingFailure {
lhs_time: left_time,
mid_time: desired_time,
rhs_time: right_time,
});
}
return Ok(left_val);
}
}

View file

@ -75,14 +75,7 @@ impl VCD {
// dereference signal if Signal::Alias, or keep idx if Signal::Data
let signal_idx = match signal {
Signal::Data {
name,
sig_type,
signal_error,
num_bits,
self_idx,
..
} => *self_idx,
Signal::Data { self_idx, .. } => *self_idx,
Signal::Alias { name, signal_alias } => *signal_alias,
};
@ -99,4 +92,31 @@ impl VCD {
)),
}
}
pub(super) fn try_dereference_alias<'a>(
&'a self,
idx: &SignalIdx,
) -> Result<&'a Signal, String> {
// get the signal pointed to be SignalIdx from the arena
let SignalIdx(idx) = idx;
let signal = &self.all_signals[*idx];
// dereference signal if Signal::Alias, or keep idx if Signal::Data
let signal_idx = match signal {
Signal::Data { self_idx, .. } => *self_idx,
Signal::Alias { name, signal_alias } => *signal_alias,
};
// Should now point to Signal::Data variant, or else there's an error
let SignalIdx(idx) = signal_idx;
let signal = self.all_signals.get(idx).unwrap();
match signal {
Signal::Data { .. } => Ok(signal),
Signal::Alias { .. } => Err(format!(
"Error near {}:{}. A signal alias shouldn't \
point to a signal alias.",
file!(),
line!()
)),
}
}
}

View file

@ -1,5 +1,3 @@
use super::*;
#[derive(Debug)]
pub(super) enum BinaryParserErrTypes {
XValue,
@ -74,113 +72,3 @@ pub(super) fn binary_str_to_vec_u8(binary_str: &str) -> Result<Vec<u8>, BinaryPa
}
Ok(vec_u8)
}
use num::{BigUint, Zero};
#[derive(Debug)]
pub(super) enum LookupErrors {
PreTimeline {
desired_time: BigUint,
timeline_start_time: BigUint,
},
EmptyTimeline,
TimelineNotMultiple,
OrderingFailure,
}
// pub(super) fn ordered_binary_lookup_u8(
// //(REMOVE THIS COMMENT)below is from self
// lsb_indxs_of_num_tmstmp_vals_on_tmln: &Vec<LsbIdxOfTmstmpValOnTmln>,
// tmstmps_encoded_as_u8s: &Vec<u8>,
// //(REMOVE THIS COMMENT)below is from self
// nums_encoded_as_fixed_width_le_u8: &Vec<u8>,
// // TODO : should this be usize?
// bytes_per_value: usize,
// desired_time: BigUint,
// ) -> Result<BigUint, LookupErrors> {
// // this signal should at least have some events, otherwise, trying to index into
// // an empty vector later on would fail
// if lsb_indxs_of_num_tmstmp_vals_on_tmln.is_empty() {
// return Err(LookupErrors::EmptyTimeline);
// }
// // assertion that value_sequence is a proper multiple of
// // timeline_markers
// if lsb_indxs_of_num_tmstmp_vals_on_tmln.len()
// != (nums_encoded_as_fixed_width_le_u8.len() * bytes_per_value)
// {
// return Err(LookupErrors::TimelineNotMultiple);
// }
// // let TimelineIdx(desired_time) = desired_time;
// // check if we're requesting a value that occurs before the recorded
// // start of the timeline
// let TimelineIdx(timeline_start_time) = timeline_cursors.first().unwrap();
// if desired_time < *timeline_start_time {
// return Err(LookupErrors::PreTimeline {
// desired_time: TimelineIdx(desired_time),
// timeline_start_time: TimelineIdx(*timeline_start_time),
// });
// }
// let mut lower_idx = 0usize;
// let mut upper_idx = timeline_cursors.len() - 1;
// // check if we're requesting a value that occurs beyond the end of the timeline,
// // if so, return the last value in this timeline
// let TimelineIdx(timeline_end_time) = timeline_cursors.last().unwrap();
// if desired_time > *timeline_end_time {
// let range = (value_sequence_as_bytes.len() - bytes_per_value)..;
// let value_by_bytes = &value_sequence_as_bytes[range];
// let value = BigUint::from_bytes_le(value_by_bytes);
// return Ok(value);
// }
// // This while loop is the meat of the lookup. Performance is log2(n),
// // where n is the number of events on the timeline.
// // We can assume that by the time we get here, that the desired_time
// // is an event that occurs on the timeline, given that we handle any events
// // occuring after or before the recorded tiimeline in the code above.
// while lower_idx <= upper_idx {
// let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2);
// let TimelineIdx(curr_time) = timeline_cursors[mid_idx];
// let ordering = curr_time.cmp(&desired_time);
// match ordering {
// std::cmp::Ordering::Less => {
// lower_idx = mid_idx + 1;
// }
// std::cmp::Ordering::Equal => {
// let u8_timeline_start_idx = mid_idx * bytes_per_value;
// let u8_timeline_end_idx = u8_timeline_start_idx + bytes_per_value;
// let range = u8_timeline_start_idx..u8_timeline_end_idx;
// let value_by_bytes = &value_sequence_as_bytes[range];
// let value = BigUint::from_bytes_le(value_by_bytes);
// return Ok(value);
// }
// std::cmp::Ordering::Greater => {
// upper_idx = mid_idx - 1;
// }
// }
// }
// let idx = lower_idx - 1;
// let TimelineIdx(left_time) = timeline_cursors[idx];
// let TimelineIdx(right_time) = timeline_cursors[idx + 1];
// let ordered_left = left_time < desired_time;
// let ordered_right = desired_time < right_time;
// if !(ordered_left && ordered_right) {
// return Err(LookupErrors::OrderingFailure);
// }
// let u8_timeline_start_idx = idx * bytes_per_value;
// let u8_timeline_end_idx = u8_timeline_start_idx + bytes_per_value;
// let range = u8_timeline_start_idx..u8_timeline_end_idx;
// let value_by_bytes = &value_sequence_as_bytes[range];
// let value = BigUint::from_bytes_le(value_by_bytes);
// return Ok(value);
// }