Refactor the double-sided-query to make it less error-prone

This commit is contained in:
TheZoq2 2023-12-05 16:44:19 +01:00
parent 7d414f36dd
commit e2e3541e3f

View file

@ -40,6 +40,11 @@ pub enum SignalValue {
String(String),
}
pub struct QueryResult<T> {
pub current: Option<(TimeStamp, T)>,
pub next: Option<TimeStamp>,
}
pub struct Signal<'a>(pub(super) &'a SignalEnum);
impl<'a> Signal<'a> {
@ -82,6 +87,9 @@ impl<'a> Signal<'a> {
signal_enum.bits_required()
}
// NOTE: (zoq) I am removing thse because they aren't used in Surfer so I can't test them
// properly
/*
pub fn query_string_val_on_tmln(
&self,
desired_time: &BigUint,
@ -90,32 +98,33 @@ impl<'a> Signal<'a> {
let Signal(signal_enum) = &self;
signal_enum
.query_string_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals)
.map(|(val, _, _)| val)
.map(|QueryResult{current, next: _}| current.map(|c| c.1))
}
pub fn query_num_val_on_tmln(
&self,
desired_time: &BigUint,
vcd: &types::VCD,
) -> Result<BigUint, SignalErrors> {
) -> Result<Option<BigUint>, SignalErrors> {
let Signal(signal_enum) = &self;
signal_enum
.query_num_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals)
.map(|(val, _, _)| val)
.map(|QueryResult{current, next: _}| current.map(|c| c.1))
}
*/
pub fn query_val_on_tmln(
&self,
desired_time: &BigUint,
vcd: &types::VCD,
) -> Result<(TimeStamp, SignalValue, Option<TimeStamp>), SignalErrors> {
) -> Result<QueryResult<SignalValue>, SignalErrors> {
let Signal(signal_enum) = &self;
let num_val = signal_enum.query_num_val_on_tmln(
let num_query_out = signal_enum.query_num_val_on_tmln(
desired_time,
&vcd.tmstmps_encoded_as_u8s,
&vcd.all_signals,
);
let str_val = signal_enum.query_string_val_on_tmln(
let str_query_out = signal_enum.query_string_val_on_tmln(
desired_time,
&vcd.tmstmps_encoded_as_u8s,
&vcd.all_signals,
@ -124,22 +133,44 @@ impl<'a> Signal<'a> {
// Both num and str will return the newest value that is closest to
// the desired time. If both have valid values, select the most recent
// one
match (num_val, str_val) {
(Ok((num_val, num_time, num_next)), Ok((str_val, str_time, str_next))) => {
let next = match (num_next, str_next) {
match (num_query_out, str_query_out) {
(Ok(num_result), Ok(str_result)) => {
let next = match (num_result.next, str_result.next) {
(Some(n), Some(s)) => Some(n.min(s)),
(Some(n), None) => Some(n),
(None, Some(s)) => Some(s),
(None, None) => None
(None, None) => None,
};
match (num_result.current, str_result.current) {
(Some((num_time, num_value)), Some((str_time, str_value))) => {
if num_time > str_time {
Ok((num_time, SignalValue::BigUint(num_val), next))
Ok(QueryResult {
current: Some((num_time, SignalValue::BigUint(num_value))),
next,
})
} else {
Ok((str_time, SignalValue::String(str_val), next))
Ok(QueryResult {
current: Some((str_time, SignalValue::String(str_value))),
next,
})
}
}
(Ok((num_val, time, next)), Err(_)) => Ok((time, SignalValue::BigUint(num_val), next)),
(Err(_), Ok((str_val, time, next))) => Ok((time, SignalValue::String(str_val), next)),
(Some((num_time, num_val)), None) => Ok(QueryResult {
current: Some((num_time, SignalValue::BigUint(num_val))),
next,
}),
(None, Some((str_time, str_value))) => Ok(QueryResult {
current: Some((str_time, SignalValue::String(str_value))),
next,
}),
(None, None) => Ok(QueryResult {
current: None,
next,
}),
}
}
(_e, Err(e)) => Err(e),
(Err(e), _e) => Err(e),
}
}
@ -198,10 +229,6 @@ pub(super) enum SignalEnum {
#[derive(Debug)]
pub enum SignalErrors {
PreTimeline {
desired_time: BigUint,
timeline_start_time: BigUint,
},
EmptyTimeline,
TimelineNotMultiple,
StrTmlnLenMismatch,
@ -402,7 +429,7 @@ impl SignalEnum {
desired_time: &BigUint,
tmstmps_encoded_as_u8s: &Vec<u8>,
all_signals: &Vec<SignalEnum>,
) -> Result<(String, TimeStamp, Option<TimeStamp>), SignalErrors> {
) -> Result<QueryResult<String>, SignalErrors> {
let signal_idx = match self {
Self::Data { self_idx, .. } => {
let SignalIdx(idx) = self_idx;
@ -436,7 +463,10 @@ impl SignalEnum {
// this signal should at least have some events, otherwise, trying to index into
// an empty vector later on would fail
if lsb_indxs_of_string_tmstmp_vals_on_tmln.is_empty() {
return Err(SignalErrors::EmptyTimeline);
return Ok(QueryResult {
current: None,
next: None
});
}
// the vector of string timeline lsb indices should have the same
@ -450,9 +480,9 @@ impl SignalEnum {
let (timeline_start_time, _) =
self.time_and_str_val_at_event_idx(0, tmstmps_encoded_as_u8s)?;
if *desired_time < timeline_start_time {
return Err(SignalErrors::PreTimeline {
desired_time: desired_time.clone(),
timeline_start_time,
return Ok(QueryResult {
current: None,
next: Some(timeline_start_time),
});
}
@ -464,7 +494,10 @@ impl SignalEnum {
// 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.to_string(), timeline_end_time, None));
return Ok(QueryResult {
current: Some((timeline_end_time, timeline_end_val.to_string())),
next: None,
});
}
// This while loop is the meat of the lookup. Performance is log2(n),
@ -483,13 +516,21 @@ impl SignalEnum {
lower_idx = mid_idx + 1;
}
std::cmp::Ordering::Equal => {
let next_time = if mid_idx < lsb_indxs_of_string_tmstmp_vals_on_tmln.len()-1 {
Some(self.time_and_str_val_at_event_idx(mid_idx+1, tmstmps_encoded_as_u8s)?.0)
}
else {
let next_time = if mid_idx < lsb_indxs_of_string_tmstmp_vals_on_tmln.len() - 1 {
Some(
self.time_and_str_val_at_event_idx(
mid_idx + 1,
tmstmps_encoded_as_u8s,
)?
.0,
)
} else {
None
};
return Ok((curr_val.to_string(), curr_time, next_time));
return Ok(QueryResult {
current: Some((curr_time, curr_val.to_string())),
next: next_time,
});
}
std::cmp::Ordering::Greater => {
upper_idx = mid_idx - 1;
@ -512,14 +553,17 @@ impl SignalEnum {
});
}
Ok((left_val.to_string(), left_time, Some(right_time)))
Ok(QueryResult {
current: Some((left_time, left_val.to_string())),
next: Some(right_time),
})
}
pub fn query_num_val_on_tmln(
&self,
desired_time: &BigUint,
tmstmps_encoded_as_u8s: &Vec<u8>,
all_signals: &Vec<SignalEnum>,
) -> Result<(BigUint, TimeStamp, Option<TimeStamp>), SignalErrors> {
) -> Result<QueryResult<BigUint>, SignalErrors> {
let signal_idx = match self {
Self::Data { self_idx, .. } => {
let SignalIdx(idx) = self_idx;
@ -564,7 +608,10 @@ impl SignalEnum {
// 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);
return Ok(QueryResult {
current: None,
next: None
});
}
// assertion that value_sequence is a proper multiple of
@ -587,9 +634,9 @@ impl SignalEnum {
let (timeline_start_time, _) =
self.time_and_num_val_at_event_idx(0, tmstmps_encoded_as_u8s)?;
if *desired_time < timeline_start_time {
return Err(SignalErrors::PreTimeline {
desired_time: desired_time.clone(),
timeline_start_time,
return Ok(QueryResult {
current: None,
next: Some(timeline_start_time),
});
}
@ -601,7 +648,10 @@ impl SignalEnum {
// 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, timeline_end_time, None));
return Ok(QueryResult {
current: Some((timeline_end_time, timeline_end_val)),
next: None,
});
}
// This while loop is the meat of the lookup. Performance is log2(n),
@ -621,12 +671,20 @@ impl SignalEnum {
}
std::cmp::Ordering::Equal => {
let next_time = if mid_idx < lsb_indxs_of_num_tmstmp_vals_on_tmln.len() - 1 {
Some(self.time_and_num_val_at_event_idx(mid_idx+1, tmstmps_encoded_as_u8s)?.0)
}
else {
Some(
self.time_and_num_val_at_event_idx(
mid_idx + 1,
tmstmps_encoded_as_u8s,
)?
.0,
)
} else {
None
};
return Ok((curr_val, curr_time, next_time));
return Ok(QueryResult {
current: Some((curr_time, curr_val)),
next: next_time,
});
}
std::cmp::Ordering::Greater => {
upper_idx = mid_idx - 1;
@ -649,6 +707,9 @@ impl SignalEnum {
});
}
Ok((left_val, left_time, Some(right_time)))
return Ok(QueryResult {
current: Some((left_time, left_val)),
next: Some(right_time),
});
}
}