///|
/// A reproducibility manifest for an imported waveform.
pub struct TraceManifest {
name : String
format : String
timescale : String?
end_time : Int64
signal_count : Int
change_count : Int
shape_digest : Int64
content_digest : Int64
validated : Bool
tags : Array[String]
} derive(Debug)
///|
/// Create a reproducibility manifest from a trace.
pub fn trace_manifest(
name : String,
trace : Trace,
tags : Array[String],
) -> TraceManifest {
{
name,
format: "VCD",
timescale: trace.header.timescale,
end_time: trace.end_time,
signal_count: trace.signals.length(),
change_count: trace.changes.length(),
shape_digest: trace.shape_digest(),
content_digest: manifest_content_digest(trace),
validated: analysis_safe(trace),
tags,
}
}
///|
/// Return whether two manifests identify the same trace content.
pub fn manifests_equal(left : TraceManifest, right : TraceManifest) -> Bool {
left.shape_digest == right.shape_digest &&
left.content_digest == right.content_digest &&
left.end_time == right.end_time
}
///|
/// Return a stable manifest line for logs.
pub fn TraceManifest::to_text(self : TraceManifest) -> String {
self.name +
" format=" +
self.format +
" signals=" +
self.signal_count.to_string() +
" changes=" +
self.change_count.to_string() +
" shape=" +
self.shape_digest.to_string() +
" content=" +
self.content_digest.to_string() +
" validated=" +
self.validated.to_string()
}
///|
/// Export a manifest as JSON.
pub fn TraceManifest::to_json(self : TraceManifest) -> String {
"{\"name\":\"" +
escape_text(self.name) +
"\",\"format\":\"" +
self.format +
"\",\"timescale\":" +
manifest_optional_text(self.timescale) +
",\"end_time\":" +
self.end_time.to_string() +
",\"signal_count\":" +
self.signal_count.to_string() +
",\"change_count\":" +
self.change_count.to_string() +
",\"shape_digest\":" +
self.shape_digest.to_string() +
",\"content_digest\":" +
self.content_digest.to_string() +
",\"validated\":" +
self.validated.to_string() +
",\"tags\":[" +
self.tags.map(fn(tag) { "\"" + escape_text(tag) + "\"" }).join(",") +
"]}"
}
///|
/// Export manifests as CSV.
pub fn trace_manifests_csv(items : Array[TraceManifest]) -> String {
let out = StringBuilder()
out.write_string(
"name,format,timescale,end_time,signals,changes,shape_digest,content_digest,validated,tags\n",
)
for item in items {
out.write_string(
escape_text(item.name) +
"," +
item.format +
"," +
manifest_optional_text(item.timescale) +
"," +
item.end_time.to_string() +
"," +
item.signal_count.to_string() +
"," +
item.change_count.to_string() +
"," +
item.shape_digest.to_string() +
"," +
item.content_digest.to_string() +
"," +
item.validated.to_string() +
"," +
item.tags.join("|") +
"\n",
)
}
out.to_string()
}
///|
/// Calculate a content digest independent of source formatting.
fn manifest_content_digest(trace : Trace) -> Int64 {
trace.changes.fold(init=23L, fn(hash, change) {
hash * 37L +
change.time +
change.id.hash().to_int64() +
change.value.display().hash().to_int64()
})
}
///|
/// Encode an optional string as JSON.
fn manifest_optional_text(value : String?) -> String {
match value {
Some(item) => "\"" + escape_text(item) + "\""
None => "null"
}
}