///|
/// One fully expanded instruction from a VCDIFF instruction stream.
pub(all) struct TraceInstruction {
window_index : Int
ordinal : Int
code_index : Int
code_offset : Int
code_slot : Int
kind : InstructionKind
size : Int
mode : Int
target_offset : Int
data_offset : Int
data_size : Int
address_offset : Int
address : Int
} derive(Eq, Debug)
///|
/// Expanded instruction trace for a complete delta file.
pub(all) struct DeltaTrace {
file_size : Int
window_count : Int
target_size : Int
instruction_count : Int
add_count : Int
run_count : Int
copy_count : Int
instructions : Array[TraceInstruction]
} derive(Eq, Debug)
///|
priv struct WindowTraceState {
mut target_offset : Int
mut ordinal : Int
mut add_count : Int
mut run_count : Int
mut copy_count : Int
}
///|
fn WindowTraceState::new() -> WindowTraceState {
{ target_offset: 0, ordinal: 0, add_count: 0, run_count: 0, copy_count: 0 }
}
///|
fn trace_size(
instruction : CodeInstruction,
cursor : ByteCursor,
) -> Int raise VcdiffError {
if instruction.size == 0 {
read_varint(cursor)
} else {
instruction.size
}
}
///|
fn trace_add(
window_index : Int,
code_index : Int,
code_offset : Int,
code_slot : Int,
instruction : CodeInstruction,
size : Int,
state : WindowTraceState,
data_cursor : ByteCursor,
) -> TraceInstruction raise VcdiffError {
ignore(instruction)
let data_offset = data_cursor.offset()
ignore(data_cursor.read_exact(size))
let traced = {
window_index,
ordinal: state.ordinal,
code_index,
code_offset,
code_slot,
kind: Add,
size,
mode: 0,
target_offset: state.target_offset,
data_offset,
data_size: size,
address_offset: -1,
address: -1,
}
state.target_offset += size
state.ordinal += 1
state.add_count += 1
traced
}
///|
fn trace_run(
window_index : Int,
code_index : Int,
code_offset : Int,
code_slot : Int,
instruction : CodeInstruction,
size : Int,
state : WindowTraceState,
data_cursor : ByteCursor,
) -> TraceInstruction raise VcdiffError {
ignore(instruction)
let data_offset = data_cursor.offset()
ignore(data_cursor.read_byte())
let traced = {
window_index,
ordinal: state.ordinal,
code_index,
code_offset,
code_slot,
kind: Run,
size,
mode: 0,
target_offset: state.target_offset,
data_offset,
data_size: 1,
address_offset: -1,
address: -1,
}
state.target_offset += size
state.ordinal += 1
state.run_count += 1
traced
}
///|
fn trace_copy(
window_index : Int,
code_index : Int,
code_offset : Int,
code_slot : Int,
instruction : CodeInstruction,
size : Int,
source_size : Int,
state : WindowTraceState,
address_cursor : ByteCursor,
cache : AddressCache,
) -> TraceInstruction raise VcdiffError {
let address_offset = address_cursor.offset()
let here = source_size + state.target_offset
let address = cache.decode(address_cursor, here, instruction.mode)
let traced = {
window_index,
ordinal: state.ordinal,
code_index,
code_offset,
code_slot,
kind: Copy,
size,
mode: instruction.mode,
target_offset: state.target_offset,
data_offset: -1,
data_size: 0,
address_offset,
address,
}
state.target_offset += size
state.ordinal += 1
state.copy_count += 1
traced
}
///|
fn trace_one_instruction(
window_index : Int,
window : ParsedWindow,
code_index : Int,
code_offset : Int,
code_slot : Int,
instruction : CodeInstruction,
instruction_cursor : ByteCursor,
data_cursor : ByteCursor,
address_cursor : ByteCursor,
cache : AddressCache,
state : WindowTraceState,
) -> TraceInstruction? raise VcdiffError {
match instruction.kind {
Noop => None
Add => {
let size = trace_size(instruction, instruction_cursor)
Some(
trace_add(
window_index, code_index, code_offset, code_slot, instruction, size, state,
data_cursor,
),
)
}
Run => {
let size = trace_size(instruction, instruction_cursor)
Some(
trace_run(
window_index, code_index, code_offset, code_slot, instruction, size, state,
data_cursor,
),
)
}
Copy => {
let size = trace_size(instruction, instruction_cursor)
Some(
trace_copy(
window_index,
code_index,
code_offset,
code_slot,
instruction,
size,
window.source_size,
state,
address_cursor,
cache,
),
)
}
}
}
///|
fn trace_window(
window_index : Int,
window : ParsedWindow,
table : Array[CodeTableEntry],
output : Array[TraceInstruction],
) -> WindowTraceState raise VcdiffError {
let instruction_cursor = ByteCursor::with_base(
window.instructions,
window.instruction_offset,
)
let data_cursor = ByteCursor::with_base(window.data, window.data_offset)
let address_cursor = ByteCursor::with_base(
window.addresses,
window.address_offset,
)
let cache = AddressCache::new()
let state = WindowTraceState::new()
while instruction_cursor.remaining() > 0 {
let code_offset = instruction_cursor.offset()
let code_index = instruction_cursor.read_byte().to_int()
let entry = table[code_index]
let first = trace_one_instruction(
window_index,
window,
code_index,
code_offset,
0,
entry.first,
instruction_cursor,
data_cursor,
address_cursor,
cache,
state,
)
guard first is Some(item) else { continue }
output.push(item)
let second = trace_one_instruction(
window_index,
window,
code_index,
code_offset,
1,
entry.second,
instruction_cursor,
data_cursor,
address_cursor,
cache,
state,
)
if second is Some(item) {
output.push(item)
}
}
if state.target_offset != window.target_size {
raise LengthMismatch(
offset=window.instruction_offset + window.instructions.length(),
expected=window.target_size,
actual=state.target_offset,
)
}
if data_cursor.remaining() != 0 {
raise LengthMismatch(
offset=data_cursor.offset(),
expected=window.data.length() - data_cursor.remaining(),
actual=window.data.length(),
)
}
if address_cursor.remaining() != 0 {
raise LengthMismatch(
offset=address_cursor.offset(),
expected=window.addresses.length() - address_cursor.remaining(),
actual=window.addresses.length(),
)
}
state
}
///|
/// Expands every code byte into semantic ADD, RUN, and COPY operations.
///
/// COPY addresses are resolved through the standard caches. Source contents
/// are not needed because source segment sizes are carried by window headers.
pub fn trace(delta : Bytes) -> DeltaTrace raise VcdiffError {
let parsed = parse_delta(delta)
let table = default_code_table()
let instructions : Array[TraceInstruction] = []
let mut target_size = 0
let mut add_count = 0
let mut run_count = 0
let mut copy_count = 0
for window_index, window in parsed.windows {
let state = trace_window(window_index, window, table, instructions)
target_size = checked_summary_add(
target_size,
state.target_offset,
window.offset,
)
add_count = checked_summary_add(add_count, state.add_count, window.offset)
run_count = checked_summary_add(run_count, state.run_count, window.offset)
copy_count = checked_summary_add(
copy_count,
state.copy_count,
window.offset,
)
}
{
file_size: delta.length(),
window_count: parsed.windows.length(),
target_size,
instruction_count: instructions.length(),
add_count,
run_count,
copy_count,
instructions,
}
}
///|
/// Renders an expanded trace as one operation per line.
pub fn DeltaTrace::to_text(self : DeltaTrace) -> String {
let builder = StringBuilder()
builder <+
"windows=\{self.window_count} target=\{self.target_size} operations=\{self.instruction_count}\n"
for item in self.instructions {
builder <+ "w\{item.window_index} #\{item.ordinal} @\{item.target_offset} "
builder <+ "\{item.kind.name()} size=\{item.size}"
if item.kind == Copy {
builder <+ " mode=\{item.mode} address=\{item.address}"
} else {
builder <+ " data=\{item.data_offset}+\{item.data_size}"
}
builder <+ " code=\{item.code_index}:\{item.code_slot}\n"
}
builder.to_string()
}