///|
pub struct RecordCategoryStatistics {
name_value : String
record_count_value : Int
data_bytes_value : Int
} derive(Eq, Debug)
///|
pub struct DocumentStatistics {
format_value : FirmwareFormat
total_records_value : Int
data_records_value : Int
metadata_records_value : Int
data_bytes_value : Int
largest_data_record_value : Int
entry_point_value : UInt64?
has_header_value : Bool
declared_data_records_value : Int?
category_values : Array[RecordCategoryStatistics]
} derive(Eq, Debug)
///|
fn build_categories(
names : Array[String],
counts : Array[Int],
bytes : Array[Int],
) -> Array[RecordCategoryStatistics] {
let result : Array[RecordCategoryStatistics] = []
for index, name in names {
if counts[index] > 0 {
result.push({
name_value: name,
record_count_value: counts[index],
data_bytes_value: bytes[index],
})
}
}
result
}
///|
pub fn analyze_intel_document(
document : IntelHexDocument,
) -> DocumentStatistics {
let names = [
"data", "end_of_file", "extended_segment_address", "start_segment_address", "extended_linear_address",
"start_linear_address",
]
let counts = Array::make(6, 0)
let bytes = Array::make(6, 0)
let records = document.records()
let mut data_records = 0
let mut data_bytes = 0
let mut largest = 0
for record in records {
let index = match record.record_type() {
IntelData => 0
IntelEndOfFile => 1
IntelExtendedSegmentAddress => 2
IntelStartSegmentAddress => 3
IntelExtendedLinearAddress => 4
IntelStartLinearAddress => 5
}
counts[index] = counts[index] + 1
bytes[index] = bytes[index] + record.byte_count()
if record.record_type() == IntelData {
data_records = data_records + 1
data_bytes = data_bytes + record.byte_count()
if record.byte_count() > largest {
largest = record.byte_count()
}
}
}
{
format_value: IntelHexFormat,
total_records_value: records.length(),
data_records_value: data_records,
metadata_records_value: records.length() - data_records,
data_bytes_value: data_bytes,
largest_data_record_value: largest,
entry_point_value: document.entry_point(),
has_header_value: false,
declared_data_records_value: None,
category_values: build_categories(names, counts, bytes),
}
}
///|
pub fn analyze_srecord_document(
document : SRecordDocument,
) -> DocumentStatistics {
let names = [
"header", "data_16", "data_24", "data_32", "count_16", "count_24", "terminate_32",
"terminate_24", "terminate_16",
]
let counts = Array::make(9, 0)
let bytes = Array::make(9, 0)
let records = document.records()
let mut data_records = 0
let mut data_bytes = 0
let mut largest = 0
for record in records {
let index = match record.record_type() {
SHeader => 0
SData16 => 1
SData24 => 2
SData32 => 3
SCount16 => 4
SCount24 => 5
STerminate32 => 6
STerminate24 => 7
STerminate16 => 8
}
counts[index] = counts[index] + 1
bytes[index] = bytes[index] + record.data().length()
if record.record_type().is_data() {
data_records = data_records + 1
data_bytes = data_bytes + record.data().length()
if record.data().length() > largest {
largest = record.data().length()
}
}
}
{
format_value: SRecordFormat,
total_records_value: records.length(),
data_records_value: data_records,
metadata_records_value: records.length() - data_records,
data_bytes_value: data_bytes,
largest_data_record_value: largest,
entry_point_value: document.entry_point(),
has_header_value: document.header() is Some(_),
declared_data_records_value: document.declared_data_count(),
category_values: build_categories(names, counts, bytes),
}
}
///|
pub fn RecordCategoryStatistics::name(
self : RecordCategoryStatistics,
) -> String {
self.name_value
}
///|
pub fn RecordCategoryStatistics::record_count(
self : RecordCategoryStatistics,
) -> Int {
self.record_count_value
}
///|
pub fn RecordCategoryStatistics::data_bytes(
self : RecordCategoryStatistics,
) -> Int {
self.data_bytes_value
}
///|
pub fn DocumentStatistics::format(self : DocumentStatistics) -> FirmwareFormat {
self.format_value
}
///|
pub fn DocumentStatistics::total_records(self : DocumentStatistics) -> Int {
self.total_records_value
}
///|
pub fn DocumentStatistics::data_records(self : DocumentStatistics) -> Int {
self.data_records_value
}
///|
pub fn DocumentStatistics::metadata_records(self : DocumentStatistics) -> Int {
self.metadata_records_value
}
///|
pub fn DocumentStatistics::data_bytes(self : DocumentStatistics) -> Int {
self.data_bytes_value
}
///|
pub fn DocumentStatistics::largest_data_record(
self : DocumentStatistics,
) -> Int {
self.largest_data_record_value
}
///|
pub fn DocumentStatistics::entry_point(self : DocumentStatistics) -> UInt64? {
self.entry_point_value
}
///|
pub fn DocumentStatistics::has_header(self : DocumentStatistics) -> Bool {
self.has_header_value
}
///|
pub fn DocumentStatistics::declared_data_records(
self : DocumentStatistics,
) -> Int? {
self.declared_data_records_value
}
///|
pub fn DocumentStatistics::categories(
self : DocumentStatistics,
) -> Array[RecordCategoryStatistics] {
self.category_values.copy()
}
///|
fn firmware_format_name(format : FirmwareFormat) -> String {
match format {
IntelHexFormat => "intel_hex"
SRecordFormat => "s_record"
}
}
///|
pub fn render_document_statistics_text(
statistics : DocumentStatistics,
) -> String {
let mut result = "firmware-document format=" +
firmware_format_name(statistics.format()) +
" records=" +
statistics.total_records().to_string() +
" data-records=" +
statistics.data_records().to_string() +
" metadata-records=" +
statistics.metadata_records().to_string() +
" data-bytes=" +
statistics.data_bytes().to_string() +
" largest-data-record=" +
statistics.largest_data_record().to_string()
for category in statistics.categories() {
result = result +
"\n" +
category.name() +
" records=" +
category.record_count().to_string() +
" data-bytes=" +
category.data_bytes().to_string()
}
result
}
///|
pub fn render_document_statistics_json(
statistics : DocumentStatistics,
) -> String {
let categories : Array[Json] = []
for category in statistics.categories() {
categories.push(
Json::object({
"name": Json::string(category.name()),
"record_count": Json::number(category.record_count().to_double()),
"data_bytes": Json::number(category.data_bytes().to_double()),
}),
)
}
Json::object({
"type": Json::string("firmware_document_statistics"),
"format": Json::string(firmware_format_name(statistics.format())),
"total_records": Json::number(statistics.total_records().to_double()),
"data_records": Json::number(statistics.data_records().to_double()),
"metadata_records": Json::number(statistics.metadata_records().to_double()),
"data_bytes": Json::number(statistics.data_bytes().to_double()),
"largest_data_record": Json::number(
statistics.largest_data_record().to_double(),
),
"entry_point": optional_address_json(statistics.entry_point()),
"has_header": Json::boolean(statistics.has_header()),
"declared_data_records": match statistics.declared_data_records() {
Some(value) => Json::number(value.to_double())
None => Json::null()
},
"categories": Json::array(categories),
}).stringify()
}