///|
/// Build a signal-local position index without copying waveform values.
pub fn build_index(trace : Trace) -> TraceIndex {
let ids = trace.signals.map(fn(signal) { signal.id })
let offsets : Array[Int] = []
let counts : Array[Int] = []
let positions : Array[Int] = []
for id in ids {
offsets.push(positions.length())
let before = positions.length()
for i in 0.. Int {
self.signal_ids.length()
}
///|
/// Return the number of changes indexed for a signal.
pub fn TraceIndex::change_count(self : TraceIndex, signal_id : String) -> Int {
match self.signal_ids.search(signal_id) {
Some(index) => self.counts[index]
None => 0
}
}
///|
/// Return the original change positions for a signal.
pub fn TraceIndex::positions(
self : TraceIndex,
signal_id : String,
) -> Array[Int] {
match self.signal_ids.search(signal_id) {
Some(index) => {
let start = self.offsets[index]
let end = start + self.counts[index]
self.change_positions[start:end].to_owned()
}
None => []
}
}
///|
/// Return indexed changes in source order.
pub fn TraceIndex::changes(
self : TraceIndex,
trace : Trace,
signal_id : String,
) -> Array[Change] {
self.positions(signal_id).map(fn(position) { trace.changes[position] })
}
///|
/// Return the first indexed timestamp for a signal.
pub fn TraceIndex::first_time(
self : TraceIndex,
trace : Trace,
signal_id : String,
) -> Int64? {
match self.positions(signal_id).get(0) {
Some(position) => Some(trace.changes[position].time)
None => None
}
}
///|
/// Return the last indexed timestamp for a signal.
pub fn TraceIndex::last_time(
self : TraceIndex,
trace : Trace,
signal_id : String,
) -> Int64? {
match self.positions(signal_id).last() {
Some(position) => Some(trace.changes[position].time)
None => None
}
}
///|
/// Return the indexed value at or before a timestamp.
pub fn TraceIndex::value_at(
self : TraceIndex,
trace : Trace,
signal_id : String,
timestamp : Int64,
) -> Value? {
let mut result : Value? = None
for position in self.positions(signal_id) {
let change = trace.changes[position]
if change.time > timestamp {
break
}
result = Some(change.value)
}
result
}
///|
/// Return indexed changes in an inclusive time window.
pub fn TraceIndex::between(
self : TraceIndex,
trace : Trace,
signal_id : String,
start~ : Int64,
end~ : Int64,
) -> Array[Change] {
self
.changes(trace, signal_id)
.filter(fn(change) { change.time >= start && change.time <= end })
}
///|
/// Return the index position for a signal identifier, if present.
pub fn TraceIndex::lookup(self : TraceIndex, signal_id : String) -> Int? {
self.signal_ids.search(signal_id)
}
///|
/// Verify that every indexed position points to the requested signal.
pub fn TraceIndex::is_consistent(self : TraceIndex, trace : Trace) -> Bool {
for index in 0..= trace.changes.length() {
return false
}
if trace.changes[position].id != id {
return false
}
}
}
true
}
///|
/// Return an index-backed snapshot of every signal's changes.
pub fn indexed_changes(trace : Trace) -> Array[(String, Array[Change])] {
let index = build_index(trace)
trace.signals.map(fn(signal) { (signal.id, index.changes(trace, signal.id)) })
}