///|
priv struct InstructionTotals {
mut output_size : Int
mut add_count : Int
mut run_count : Int
mut copy_count : Int
}
///|
fn InstructionTotals::new() -> InstructionTotals {
{ output_size: 0, add_count: 0, run_count: 0, copy_count: 0 }
}
///|
fn InstructionTotals::record(
self : InstructionTotals,
instruction : CodeInstruction,
cursor : ByteCursor,
) -> Unit raise VcdiffError {
if instruction.kind == Noop {
return
}
let size = if instruction.size == 0 {
read_varint(cursor)
} else {
instruction.size
}
if self.output_size > MAX_PORTABLE_INT - size {
raise IntegerOverflow(offset=cursor.offset())
}
self.output_size += size
match instruction.kind {
Noop => ()
Add => self.add_count += 1
Run => self.run_count += 1
Copy => self.copy_count += 1
}
}
///|
fn inspect_instruction_stream(
window : ParsedWindow,
table : Array[CodeTableEntry],
) -> InstructionTotals raise VcdiffError {
let cursor = ByteCursor::with_base(
window.instructions,
window.instruction_offset,
)
let totals = InstructionTotals::new()
while cursor.remaining() > 0 {
let code = cursor.read_byte().to_int()
let entry = table[code]
totals.record(entry.first, cursor)
totals.record(entry.second, cursor)
}
if totals.output_size != window.target_size {
raise LengthMismatch(
offset=cursor.offset(),
expected=window.target_size,
actual=totals.output_size,
)
}
totals
}
///|
fn window_source_kind(indicator : Int) -> WindowSourceKind {
if indicator == VCD_SOURCE {
SourceDictionary
} else if indicator == VCD_TARGET {
TargetDictionary
} else {
NoDictionary
}
}
///|
fn make_window_summary(
index : Int,
window : ParsedWindow,
totals : InstructionTotals,
) -> WindowSummary {
{
index,
offset: window.offset,
source_kind: window_source_kind(window.indicator),
source_size: window.source_size,
source_position: window.source_position,
target_size: window.target_size,
delta_size: window.delta_length,
data_size: window.data.length(),
instruction_size: window.instructions.length(),
address_size: window.addresses.length(),
add_count: totals.add_count,
run_count: totals.run_count,
copy_count: totals.copy_count,
}
}
///|
fn checked_summary_add(
current : Int,
amount : Int,
offset : Int,
) -> Int raise VcdiffError {
if current > MAX_PORTABLE_INT - amount {
raise IntegerOverflow(offset~)
}
current + amount
}
///|
/// Parses a delta into a stable file-level and per-window summary.
///
/// Inspection validates code bytes and separately encoded instruction sizes,
/// but does not require the external source file.
pub fn inspect(delta : Bytes) -> DeltaSummary raise VcdiffError {
let parsed = parse_delta(delta)
let table = default_code_table()
let windows : Array[WindowSummary] = []
let mut target_size = 0
let mut data_size = 0
let mut instruction_size = 0
let mut address_size = 0
let mut add_count = 0
let mut run_count = 0
let mut copy_count = 0
let mut uses_source = false
let mut uses_target = false
for index, window in parsed.windows {
let totals = inspect_instruction_stream(window, table)
let summary = make_window_summary(index, window, totals)
windows.push(summary)
target_size = checked_summary_add(
target_size,
summary.target_size,
window.offset,
)
data_size = checked_summary_add(data_size, summary.data_size, window.offset)
instruction_size = checked_summary_add(
instruction_size,
summary.instruction_size,
window.offset,
)
address_size = checked_summary_add(
address_size,
summary.address_size,
window.offset,
)
add_count = checked_summary_add(add_count, summary.add_count, window.offset)
run_count = checked_summary_add(run_count, summary.run_count, window.offset)
copy_count = checked_summary_add(
copy_count,
summary.copy_count,
window.offset,
)
if summary.source_kind == SourceDictionary {
uses_source = true
}
if summary.source_kind == TargetDictionary {
uses_target = true
}
}
{
file_size: delta.length(),
header_size: parsed.header.length,
window_count: windows.length(),
target_size,
data_size,
instruction_size,
address_size,
add_count,
run_count,
copy_count,
uses_source,
uses_target,
windows,
}
}
///|
fn first_mismatch(expected : Bytes, actual : Bytes) -> Int {
let shared = if expected.length() < actual.length() {
expected.length()
} else {
actual.length()
}
for index = 0; index < shared; index = index + 1 {
if expected[index] != actual[index] {
return index
}
}
if expected.length() != actual.length() {
shared
} else {
-1
}
}
///|
/// Decodes a delta with standard limits and compares it to expected bytes.
pub fn verify(
source : Bytes,
delta : Bytes,
expected : Bytes,
) -> VerifyReport raise VcdiffError {
let actual = decode(source, delta, default_decode_limits())
let mismatch = first_mismatch(expected, actual)
{
matches: mismatch < 0,
expected_size: expected.length(),
actual_size: actual.length(),
first_mismatch: mismatch,
}
}