///|
pub struct RecordTypeCount {
type_value : String
count_value : Int
} derive(Eq, Debug)
///|
pub struct ZoneStatistics {
record_count_value : Int
owner_count_value : Int
type_count_values : Array[RecordTypeCount]
minimum_ttl_value : Ttl?
maximum_ttl_value : Ttl?
average_ttl_seconds_value : UInt64?
apex_record_count_value : Int
delegation_owner_count_value : Int
explicit_ttl_count_value : Int
} derive(Eq, Debug)
///|
fn statistics_contains_owner(
owners : Array[DomainName],
candidate : DomainName,
) -> Bool {
for owner in owners {
if owner == candidate {
return true
}
}
false
}
///|
fn increment_type_count(
counts : Array[RecordTypeCount],
record_type : String,
) -> Unit {
for index, item in counts {
if item.type_value == record_type {
counts[index] = {
type_value: item.type_value,
count_value: item.count_value + 1,
}
return
}
}
counts.push({ type_value: record_type, count_value: 1 })
}
///|
fn statistics_text_after(left : String, right : String) -> Bool {
let limit = if left.length() < right.length() {
left.length()
} else {
right.length()
}
for index = 0; index < limit; index = index + 1 {
let left_code = left[index].to_int()
let right_code = right[index].to_int()
if left_code > right_code {
return true
}
if left_code < right_code {
return false
}
}
left.length() > right.length()
}
///|
fn sort_type_counts(counts : Array[RecordTypeCount]) -> Unit {
for index = 1; index < counts.length(); index = index + 1 {
let current = counts[index]
let mut position = index
while position > 0 &&
statistics_text_after(
counts[position - 1].type_value,
current.type_value,
) {
counts[position] = counts[position - 1]
position = position - 1
}
counts[position] = current
}
}
///|
/// Calculate deterministic aggregate evidence for a parsed zone.
pub fn calculate_zone_statistics(zone : ZoneDocument) -> ZoneStatistics {
let records = zone.records()
let owners : Array[DomainName] = []
let delegation_owners : Array[DomainName] = []
let type_counts : Array[RecordTypeCount] = []
let mut minimum_ttl : Ttl? = None
let mut maximum_ttl : Ttl? = None
let mut ttl_total = 0UL
let mut apex_records = 0
let mut explicit_ttls = 0
let origin = zone.origin()
for record in records {
let owner = record.owner()
if !statistics_contains_owner(owners, owner) {
owners.push(owner)
}
increment_type_count(type_counts, record.record_type())
let seconds = record.ttl().seconds()
ttl_total = ttl_total + seconds
match minimum_ttl {
None => minimum_ttl = Some(record.ttl())
Some(value) if seconds < value.seconds() =>
minimum_ttl = Some(record.ttl())
_ => ()
}
match maximum_ttl {
None => maximum_ttl = Some(record.ttl())
Some(value) if seconds > value.seconds() =>
maximum_ttl = Some(record.ttl())
_ => ()
}
if origin == Some(owner) {
apex_records = apex_records + 1
}
if record.ttl_was_explicit() {
explicit_ttls = explicit_ttls + 1
}
if record.record_type() == "NS" &&
origin != Some(owner) &&
!statistics_contains_owner(delegation_owners, owner) {
delegation_owners.push(owner)
}
}
sort_type_counts(type_counts)
let average = if records.length() == 0 {
None
} else {
Some(ttl_total / records.length().to_uint64())
}
{
record_count_value: records.length(),
owner_count_value: owners.length(),
type_count_values: type_counts,
minimum_ttl_value: minimum_ttl,
maximum_ttl_value: maximum_ttl,
average_ttl_seconds_value: average,
apex_record_count_value: apex_records,
delegation_owner_count_value: delegation_owners.length(),
explicit_ttl_count_value: explicit_ttls,
}
}
///|
pub fn RecordTypeCount::record_type(self : RecordTypeCount) -> String {
self.type_value
}
///|
pub fn RecordTypeCount::count(self : RecordTypeCount) -> Int {
self.count_value
}
///|
pub fn ZoneStatistics::record_count(self : ZoneStatistics) -> Int {
self.record_count_value
}
///|
pub fn ZoneStatistics::owner_count(self : ZoneStatistics) -> Int {
self.owner_count_value
}
///|
pub fn ZoneStatistics::type_counts(
self : ZoneStatistics,
) -> Array[RecordTypeCount] {
self.type_count_values.copy()
}
///|
pub fn ZoneStatistics::count_for_type(
self : ZoneStatistics,
record_type : String,
) -> Int {
let expected = uppercase_ascii(record_type)
for item in self.type_count_values {
if item.record_type() == expected {
return item.count()
}
}
0
}
///|
pub fn ZoneStatistics::minimum_ttl(self : ZoneStatistics) -> Ttl? {
self.minimum_ttl_value
}
///|
pub fn ZoneStatistics::maximum_ttl(self : ZoneStatistics) -> Ttl? {
self.maximum_ttl_value
}
///|
pub fn ZoneStatistics::average_ttl_seconds(self : ZoneStatistics) -> UInt64? {
self.average_ttl_seconds_value
}
///|
pub fn ZoneStatistics::apex_record_count(self : ZoneStatistics) -> Int {
self.apex_record_count_value
}
///|
pub fn ZoneStatistics::delegation_owner_count(self : ZoneStatistics) -> Int {
self.delegation_owner_count_value
}
///|
pub fn ZoneStatistics::explicit_ttl_count(self : ZoneStatistics) -> Int {
self.explicit_ttl_count_value
}