///|
pub(all) enum IntegrityStatus {
IntegrityUnknown
IntegrityVerified
IntegrityMismatch
IntegrityMissing
IntegrityInvalidRange
} derive(Debug, ToJson, Eq)
///|
pub(all) struct IntegrityRange {
resource : Int
offset : Int
length : Int
checksum : UInt
status : IntegrityStatus
} derive(Debug, ToJson, Eq)
///|
pub(all) struct IntegrityRecord {
id : String
resource : Int
expected : UInt
actual : UInt
bytes : Int
status : IntegrityStatus
message : String
} derive(Debug, ToJson, Eq)
///|
pub(all) struct IntegrityReport {
records : Array[IntegrityRecord]
ranges : Array[IntegrityRange]
verified : Int
mismatches : Int
missing : Int
invalid : Int
total_bytes : Int
valid : Bool
} derive(Debug, ToJson, Eq)
///|
pub(all) struct IntegrityPolicy {
require_checksum : Bool
reject_mismatch : Bool
reject_missing : Bool
reject_invalid_range : Bool
max_resource_bytes : Int
} derive(Debug, ToJson, Eq)
///|
pub fn integrity_status_name(status : IntegrityStatus) -> String {
match status {
IntegrityUnknown => "unknown"
IntegrityVerified => "verified"
IntegrityMismatch => "mismatch"
IntegrityMissing => "missing"
IntegrityInvalidRange => "invalid-range"
}
}
///|
pub fn integrity_range(
resource : Int,
offset : Int,
length : Int,
checksum : UInt,
) -> IntegrityRange {
{ resource, offset, length, checksum, status: IntegrityUnknown }
}
///|
pub fn integrity_range_end(value : IntegrityRange) -> Int {
value.offset + value.length
}
///|
pub fn integrity_range_valid(value : IntegrityRange, size : Int) -> Bool {
value.resource >= 0 &&
value.offset >= 0 &&
value.length >= 0 &&
integrity_range_end(value) <= size
}
///|
pub fn integrity_ranges_overlap(
left : IntegrityRange,
right : IntegrityRange,
) -> Bool {
left.resource == right.resource &&
left.offset < integrity_range_end(right) &&
right.offset < integrity_range_end(left)
}
///|
pub fn integrity_ranges_total(ranges : Array[IntegrityRange]) -> Int {
let mut total = 0
for value in ranges {
total = total + value.length
}
total
}
///|
pub fn integrity_checksum(data : Bytes) -> UInt {
binary_checksum(data)
}
///|
pub fn integrity_checksum_range(data : Bytes, range : IntegrityRange) -> UInt? {
if range.offset < 0 ||
range.length < 0 ||
integrity_range_end(range) > data.length() {
return None
}
Some(binary_checksum_range(data, range.offset, range.length) catch { _ => 0 })
}
///|
pub fn integrity_record(
id : String,
resource : Int,
expected : UInt,
actual : UInt,
bytes : Int,
) -> IntegrityRecord {
let status = if bytes < 0 {
IntegrityInvalidRange
} else if expected == 0 {
IntegrityUnknown
} else if expected == actual {
IntegrityVerified
} else {
IntegrityMismatch
}
{
id,
resource,
expected,
actual,
bytes,
status,
message: integrity_status_name(status),
}
}
///|
pub fn integrity_record_missing(id : String, resource : Int) -> IntegrityRecord {
{
id,
resource,
expected: 0,
actual: 0,
bytes: 0,
status: IntegrityMissing,
message: "resource bytes are missing",
}
}
///|
pub fn integrity_records_for_buffers(
buffers : Array[Bytes],
expected : Array[UInt],
) -> Array[IntegrityRecord] {
let records : Array[IntegrityRecord] = []
for index, data in buffers {
let checksum = integrity_checksum(data)
let required = match expected.get(index) {
Some(value) => value
None => 0
}
records.push(
integrity_record(
"buffer[\{index}]",
index,
required,
checksum,
data.length(),
),
)
}
for index in buffers.length().. Array[IntegrityRange] {
ranges.map(fn(range) {
match integrity_checksum_range(data, range) {
Some(value) => { ..range, checksum: value, status: IntegrityVerified }
None => { ..range, status: IntegrityInvalidRange }
}
})
}
///|
pub fn integrity_duplicate_checksum_count(
records : Array[IntegrityRecord],
) -> Int {
let seen : Map[UInt, Int] = Map([])
let mut duplicates = 0
for record in records {
if record.expected == 0 {
continue
}
let count = match seen.get(record.expected) {
Some(value) => value + 1
None => 1
}
seen[record.expected] = count
if count > 1 {
duplicates = duplicates + 1
}
}
duplicates
}
///|
pub fn integrity_status_count(
records : Array[IntegrityRecord],
status : IntegrityStatus,
) -> Int {
records.filter(fn(record) { record.status == status }).length()
}
///|
pub fn default_integrity_policy() -> IntegrityPolicy {
{
require_checksum: false,
reject_mismatch: true,
reject_missing: false,
reject_invalid_range: true,
max_resource_bytes: 2 * 1024 * 1024 * 1024,
}
}
///|
pub fn integrity_report(
records : Array[IntegrityRecord],
ranges : Array[IntegrityRange],
policy : IntegrityPolicy,
) -> IntegrityReport {
let verified = integrity_status_count(records, IntegrityVerified)
let mismatches = integrity_status_count(records, IntegrityMismatch)
let missing = integrity_status_count(records, IntegrityMissing)
let invalid = integrity_status_count(records, IntegrityInvalidRange)
let violations = (policy.reject_mismatch && mismatches > 0) ||
(policy.reject_missing && missing > 0) ||
(policy.reject_invalid_range && invalid > 0) ||
integrity_ranges_total(ranges) > policy.max_resource_bytes
{
records,
ranges,
verified,
mismatches,
missing,
invalid,
total_bytes: integrity_ranges_total(ranges) +
integrity_record_bytes(records),
valid: !violations,
}
}
///|
pub fn integrity_compare_records(
before : Array[IntegrityRecord],
after : Array[IntegrityRecord],
) -> Array[String] {
let changes : Array[String] = []
for previous in before {
let mut current : IntegrityRecord? = None
for candidate in after {
if candidate.id == previous.id {
current = Some(candidate)
break
}
}
match current {
Some(value) =>
if value.status != previous.status {
changes.push(
"\{previous.id}: \{integrity_status_name(previous.status)} -> \{integrity_status_name(value.status)}",
)
}
None => changes.push("\{previous.id}: removed")
}
}
for current in after {
if !before.any(fn(record) { record.id == current.id }) {
changes.push("\{current.id}: added")
}
}
changes
}
///|
fn integrity_record_bytes(records : Array[IntegrityRecord]) -> Int {
let mut total = 0
for record in records {
total = total + record.bytes
}
total
}
///|
pub fn integrity_find_range(
ranges : Array[IntegrityRange],
resource : Int,
offset : Int,
) -> IntegrityRange? {
for range in ranges {
if range.resource == resource && range.offset == offset {
return Some(range)
}
}
None
}
///|
pub fn integrity_overlaps(
ranges : Array[IntegrityRange],
) -> Array[(IntegrityRange, IntegrityRange)] {
let result : Array[(IntegrityRange, IntegrityRange)] = []
for left in 0..
if integrity_ranges_overlap(a, b) {
result.push((a, b))
}
_ => ()
}
}
}
result
}
///|
pub fn integrity_report_lines(report : IntegrityReport) -> Array[String] {
[
"records=\{report.records.length()}",
"ranges=\{report.ranges.length()}",
"verified=\{report.verified}",
"mismatches=\{report.mismatches}",
"missing=\{report.missing}",
"invalid=\{report.invalid}",
"total_bytes=\{report.total_bytes}",
"valid=\{report.valid}",
]
}