diff --git a/src/main.rs b/src/main.rs index b8442c6..2f82089 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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); diff --git a/src/vcd.rs b/src/vcd.rs index 5d228f8..b245601 100644 --- a/src/vcd.rs +++ b/src/vcd.rs @@ -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::*; diff --git a/src/vcd/parse.rs b/src/vcd/parse.rs index e3771d9..5260604 100644 --- a/src/vcd/parse.rs +++ b/src/vcd/parse.rs @@ -39,6 +39,20 @@ pub fn parse_vcd(file: File) -> Result { 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) } diff --git a/src/vcd/parse/events.rs b/src/vcd/parse/events.rs index 9dfb319..053a22b 100644 --- a/src/vcd/parse/events.rs +++ b/src/vcd/parse/events.rs @@ -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 { .. } => { diff --git a/src/vcd/parse/scopes.rs b/src/vcd/parse/scopes.rs index 6d69ee8..c48888c 100644 --- a/src/vcd/parse/scopes.rs +++ b/src/vcd/parse/scopes.rs @@ -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::().expect(parse_err.as_str()); - let no_bits = u16::try_from(no_bits).map_err(|_| { + let num_bits = word.parse::().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) diff --git a/src/vcd/signal.rs b/src/vcd/signal.rs index fd9fc92..5582182 100644 --- a/src/vcd/signal.rs +++ b/src/vcd/signal.rs @@ -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, num_bits: Option, + num_bytes: Option, // 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, 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: 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, - ) -> 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, name: &String) -> Result { - 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 { 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 { - // 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, + ) -> 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, + all_signals: &Vec, + ) -> Result { + 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); } } diff --git a/src/vcd/types.rs b/src/vcd/types.rs index 93ceee7..6a48e4d 100644 --- a/src/vcd/types.rs +++ b/src/vcd/types.rs @@ -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!() + )), + } + } } diff --git a/src/vcd/utilities.rs b/src/vcd/utilities.rs index a8e20aa..a34905c 100644 --- a/src/vcd/utilities.rs +++ b/src/vcd/utilities.rs @@ -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, 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, -// tmstmps_encoded_as_u8s: &Vec, -// //(REMOVE THIS COMMENT)below is from self -// nums_encoded_as_fixed_width_le_u8: &Vec, -// // TODO : should this be usize? -// bytes_per_value: usize, -// desired_time: BigUint, -// ) -> Result { -// // 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); -// }