///|
/// Aggregated semantic instruction usage for one window or complete delta.
pub(all) struct InstructionStatistics {
instruction_count : Int
add_count : Int
run_count : Int
copy_count : Int
add_bytes : Int
run_bytes : Int
copy_bytes : Int
literal_bytes : Int
maximum_instruction_size : Int
tiny_instructions : Int
small_instructions : Int
medium_instructions : Int
large_instructions : Int
copy_mode_counts : Array[Int]
} derive(Eq, Debug)
///|
/// Statistics for one target window, including its encoded section costs.
pub(all) struct WindowStatistics {
window_index : Int
target_size : Int
delta_size : Int
data_size : Int
instruction_size : Int
address_size : Int
operations : InstructionStatistics
} derive(Eq, Debug)
///|
/// Complete statistics report suitable for benchmark and tuning tools.
pub(all) struct DeltaStatistics {
file_size : Int
target_size : Int
window_count : Int
header_bytes : Int
window_header_bytes : Int
data_bytes : Int
instruction_bytes : Int
address_bytes : Int
operations : InstructionStatistics
windows : Array[WindowStatistics]
} derive(Eq, Debug)
///|
/// Stable comparison between two valid delta documents.
pub(all) struct DeltaComparison {
left_size : Int
right_size : Int
size_difference : Int
left_instructions : Int
right_instructions : Int
instruction_difference : Int
same_target_size : Bool
same_window_count : Bool
same_instruction_mix : Bool
} derive(Eq, Debug)
///|
priv struct StatisticsAccumulator {
mut instruction_count : Int
mut add_count : Int
mut run_count : Int
mut copy_count : Int
mut add_bytes : Int
mut run_bytes : Int
mut copy_bytes : Int
mut maximum_instruction_size : Int
mut tiny_instructions : Int
mut small_instructions : Int
mut medium_instructions : Int
mut large_instructions : Int
copy_mode_counts : Array[Int]
}
///|
fn StatisticsAccumulator::new() -> StatisticsAccumulator {
{
instruction_count: 0,
add_count: 0,
run_count: 0,
copy_count: 0,
add_bytes: 0,
run_bytes: 0,
copy_bytes: 0,
maximum_instruction_size: 0,
tiny_instructions: 0,
small_instructions: 0,
medium_instructions: 0,
large_instructions: 0,
copy_mode_counts: Array::make(9, 0),
}
}
///|
fn StatisticsAccumulator::record_size(
self : StatisticsAccumulator,
size : Int,
) -> Unit {
self.maximum_instruction_size = Int::max(self.maximum_instruction_size, size)
if size <= 4 {
self.tiny_instructions += 1
} else if size <= 16 {
self.small_instructions += 1
} else if size <= 64 {
self.medium_instructions += 1
} else {
self.large_instructions += 1
}
}
///|
fn StatisticsAccumulator::record(
self : StatisticsAccumulator,
item : TraceInstruction,
) -> Unit {
self.instruction_count += 1
self.record_size(item.size)
match item.kind {
Add => {
self.add_count += 1
self.add_bytes += item.size
}
Run => {
self.run_count += 1
self.run_bytes += item.size
}
Copy => {
self.copy_count += 1
self.copy_bytes += item.size
if item.mode >= 0 && item.mode < self.copy_mode_counts.length() {
self.copy_mode_counts[item.mode] += 1
}
}
Noop => ()
}
}
///|
fn StatisticsAccumulator::finish(
self : StatisticsAccumulator,
) -> InstructionStatistics {
{
instruction_count: self.instruction_count,
add_count: self.add_count,
run_count: self.run_count,
copy_count: self.copy_count,
add_bytes: self.add_bytes,
run_bytes: self.run_bytes,
copy_bytes: self.copy_bytes,
literal_bytes: self.add_bytes + self.run_count,
maximum_instruction_size: self.maximum_instruction_size,
tiny_instructions: self.tiny_instructions,
small_instructions: self.small_instructions,
medium_instructions: self.medium_instructions,
large_instructions: self.large_instructions,
copy_mode_counts: self.copy_mode_counts,
}
}
///|
fn collect_operations(
instructions : ArrayView[TraceInstruction],
) -> InstructionStatistics {
let accumulator = StatisticsAccumulator::new()
for item in instructions {
accumulator.record(item)
}
accumulator.finish()
}
///|
fn window_instruction_view(
trace_report : DeltaTrace,
window_index : Int,
) -> Array[TraceInstruction] {
let selected : Array[TraceInstruction] = []
for item in trace_report.instructions {
if item.window_index == window_index {
selected.push(item)
}
}
selected
}
///|
fn calculate_window_header_bytes(summary : DeltaSummary) -> Int {
let mut total = 0
for window in summary.windows {
total += window.delta_size -
window.data_size -
window.instruction_size -
window.address_size
}
total
}
///|
/// Computes semantic and encoded-size statistics for a valid delta.
pub fn statistics(delta : Bytes) -> DeltaStatistics raise VcdiffError {
let summary = inspect(delta)
let traced = trace(delta)
let windows : Array[WindowStatistics] = []
for window in summary.windows {
let selected = window_instruction_view(traced, window.index)
windows.push({
window_index: window.index,
target_size: window.target_size,
delta_size: window.delta_size,
data_size: window.data_size,
instruction_size: window.instruction_size,
address_size: window.address_size,
operations: collect_operations(selected),
})
}
{
file_size: summary.file_size,
target_size: summary.target_size,
window_count: summary.window_count,
header_bytes: summary.header_size,
window_header_bytes: calculate_window_header_bytes(summary),
data_bytes: summary.data_size,
instruction_bytes: summary.instruction_size,
address_bytes: summary.address_size,
operations: collect_operations(traced.instructions),
windows,
}
}
///|
/// Compares structural cost without requiring source or target contents.
pub fn compare_deltas(
left : Bytes,
right : Bytes,
) -> DeltaComparison raise VcdiffError {
let left_report = statistics(left)
let right_report = statistics(right)
let left_ops = left_report.operations
let right_ops = right_report.operations
{
left_size: left_report.file_size,
right_size: right_report.file_size,
size_difference: right_report.file_size - left_report.file_size,
left_instructions: left_ops.instruction_count,
right_instructions: right_ops.instruction_count,
instruction_difference: right_ops.instruction_count -
left_ops.instruction_count,
same_target_size: left_report.target_size == right_report.target_size,
same_window_count: left_report.window_count == right_report.window_count,
same_instruction_mix: left_ops.add_count == right_ops.add_count &&
left_ops.run_count == right_ops.run_count &&
left_ops.copy_count == right_ops.copy_count,
}
}
///|
/// Encoded bytes per thousand target bytes, or zero for an empty target.
pub fn DeltaStatistics::encoded_per_mille(self : DeltaStatistics) -> Int {
if self.target_size == 0 {
0
} else {
self.file_size * 1000 / self.target_size
}
}
///|
/// Percentage of target bytes reconstructed by COPY, rounded down.
pub fn InstructionStatistics::copy_percentage(
self : InstructionStatistics,
) -> Int {
let total = self.add_bytes + self.run_bytes + self.copy_bytes
if total == 0 {
0
} else {
self.copy_bytes * 100 / total
}
}
///|
/// Percentage of target bytes reconstructed by RUN, rounded down.
pub fn InstructionStatistics::run_percentage(
self : InstructionStatistics,
) -> Int {
let total = self.add_bytes + self.run_bytes + self.copy_bytes
if total == 0 {
0
} else {
self.run_bytes * 100 / total
}
}
///|
/// Renders stable multi-line statistics for command-line inspection.
pub fn DeltaStatistics::to_text(self : DeltaStatistics) -> String {
let builder = StringBuilder()
let operations = self.operations
builder <+ "MoonVCDIFF instruction statistics\n"
builder <+ "file size: \{self.file_size}\n"
builder <+ "target size: \{self.target_size}\n"
builder <+ "encoded per mille: \{self.encoded_per_mille()}\n"
builder <+ "windows: \{self.window_count}\n"
builder <+ "header bytes: \{self.header_bytes + self.window_header_bytes}\n"
builder <+ "data bytes: \{self.data_bytes}\n"
builder <+ "instruction bytes: \{self.instruction_bytes}\n"
builder <+ "address bytes: \{self.address_bytes}\n"
builder <+ "operations: \{operations.instruction_count}\n"
builder <+
"ADD: \{operations.add_count} operations, \{operations.add_bytes} bytes\n"
builder <+
"RUN: \{operations.run_count} operations, \{operations.run_bytes} bytes\n"
builder <+
"COPY: \{operations.copy_count} operations, \{operations.copy_bytes} bytes\n"
builder <+ "copy coverage: \{operations.copy_percentage()}%\n"
builder <+ "run coverage: \{operations.run_percentage()}%\n"
builder <+ "maximum instruction: \{operations.maximum_instruction_size}\n"
builder.to_string()
}
///|
/// Renders a stable comparison for regression reports.
pub fn DeltaComparison::to_text(self : DeltaComparison) -> String {
let builder = StringBuilder()
builder <+
"delta size: \{self.left_size} -> \{self.right_size} (\{self.size_difference})\n"
builder <+
"instructions: \{self.left_instructions} -> \{self.right_instructions} (\{self.instruction_difference})\n"
builder <+ "same target size: \{self.same_target_size}\n"
builder <+ "same windows: \{self.same_window_count}\n"
builder <+ "same instruction mix: \{self.same_instruction_mix}\n"
builder.to_string()
}