///|
/// Aggregate facts about one 256-entry instruction code table.
pub(all) struct CodeTableMetrics {
entry_count : Int
compound_count : Int
variable_size_count : Int
noop_count : Int
add_count : Int
run_count : Int
copy_count : Int
highest_copy_mode : Int
serialized_size : Int
checksum : Int
} derive(Eq, Debug)
///|
fn instruction_kind_byte(kind : InstructionKind) -> Byte {
match kind {
Noop => b'\x00'
Add => b'\x01'
Run => b'\x02'
Copy => b'\x03'
}
}
///|
fn instruction_kind_from_byte(
byte : Byte,
offset : Int,
) -> InstructionKind raise VcdiffError {
match byte {
b'\x00' => Noop
b'\x01' => Add
b'\x02' => Run
b'\x03' => Copy
_ =>
raise InvalidInstruction(
offset~,
reason="code table contains an unknown instruction type",
)
}
}
///|
fn validate_code_instruction(
instruction : CodeInstruction,
mode_count : Int,
index : Int,
slot : Int,
) -> Unit raise VcdiffError {
if instruction.size < 0 || instruction.size > 255 {
raise InvalidInstruction(
offset=index * 6 + slot,
reason="code-table size is outside the byte range",
)
}
match instruction.kind {
Noop =>
if instruction.size != 0 || instruction.mode != 0 {
raise InvalidInstruction(
offset=index * 6 + slot,
reason="NOOP must have zero size and mode",
)
}
Add | Run =>
if instruction.mode != 0 {
raise InvalidInstruction(
offset=index * 6 + slot,
reason="ADD and RUN must use address mode zero",
)
}
Copy =>
if instruction.mode < 0 || instruction.mode >= mode_count {
raise InvalidInstruction(
offset=index * 6 + slot,
reason="COPY mode exceeds configured address caches",
)
}
}
}
///|
fn validate_code_table(
table : Array[CodeTableEntry],
near_size : Int,
same_size : Int,
) -> Unit raise VcdiffError {
if table.length() != 256 {
raise InvalidOption(
option="code_table",
reason="must contain exactly 256 entries",
)
}
if near_size < 0 || same_size < 0 {
raise InvalidOption(
option="address_cache",
reason="cache sizes must not be negative",
)
}
if near_size > 253 || same_size > 253 - near_size {
raise InvalidOption(
option="address_cache",
reason="cache sizes produce more than 255 address modes",
)
}
let mode_count = 2 + near_size + same_size
for index, entry in table {
validate_code_instruction(entry.first, mode_count, index, 0)
validate_code_instruction(entry.second, mode_count, index, 3)
}
}
///|
fn write_instruction_field(
output : @buffer.Buffer,
table : Array[CodeTableEntry],
selector : Int,
) -> Unit {
for entry in table {
let value = match selector {
0 => instruction_kind_byte(entry.first.kind)
1 => instruction_kind_byte(entry.second.kind)
2 => entry.first.size.to_byte()
3 => entry.second.size.to_byte()
4 => entry.first.mode.to_byte()
_ => entry.second.mode.to_byte()
}
output.write_byte(value)
}
}
///|
/// Serializes a validated table using RFC 3284 section 7 field ordering.
pub fn encode_code_table_data(
table : Array[CodeTableEntry],
near_size : Int,
same_size : Int,
) -> Bytes raise VcdiffError {
validate_code_table(table, near_size, same_size)
let output = @buffer.Buffer(size_hint=1536)
for field = 0; field < 6; field = field + 1 {
write_instruction_field(output, table, field)
}
output.to_bytes()
}
///|
fn table_byte(data : Bytes, field : Int, index : Int) -> Byte {
data[field * 256 + index]
}
///|
/// Parses an uncompressed 1536-byte code-table representation.
///
/// This utility does not enable custom tables in the file decoder; it exists
/// for inspection, generation, and interoperability tooling.
pub fn decode_code_table_data(
data : Bytes,
near_size : Int,
same_size : Int,
) -> Array[CodeTableEntry] raise VcdiffError {
if data.length() != 1536 {
raise LengthMismatch(offset=0, expected=1536, actual=data.length())
}
let table : Array[CodeTableEntry] = []
for index = 0; index < 256; index = index + 1 {
let first : CodeInstruction = {
kind: instruction_kind_from_byte(table_byte(data, 0, index), index),
size: table_byte(data, 2, index).to_int(),
mode: table_byte(data, 4, index).to_int(),
}
let second : CodeInstruction = {
kind: instruction_kind_from_byte(table_byte(data, 1, index), 256 + index),
size: table_byte(data, 3, index).to_int(),
mode: table_byte(data, 5, index).to_int(),
}
table.push({ first, second })
}
validate_code_table(table, near_size, same_size)
table
}
///|
/// Returns the canonical serialized form of the RFC default table.
pub fn default_code_table_data() -> Bytes {
encode_code_table_data(
default_code_table(),
DEFAULT_NEAR_CACHE_SIZE,
DEFAULT_SAME_CACHE_SIZE,
) catch {
_ => abort("internal default code table is invalid")
}
}
///|
fn update_table_checksum(checksum : Int, instruction : CodeInstruction) -> Int {
let kind = instruction_kind_byte(instruction.kind).to_int()
let first = (checksum + kind + 1) % 65521
let second = (first * 3 + instruction.size + 1) % 65521
(second * 3 + instruction.mode + 1) % 65521
}
///|
fn count_instruction_kind(
instruction : CodeInstruction,
metrics : Array[Int],
) -> Unit {
match instruction.kind {
Noop => metrics[0] += 1
Add => metrics[1] += 1
Run => metrics[2] += 1
Copy => metrics[3] += 1
}
if instruction.kind != Noop && instruction.size == 0 {
metrics[4] += 1
}
if instruction.kind == Copy && instruction.mode > metrics[5] {
metrics[5] = instruction.mode
}
}
///|
/// Validates a table and returns stable aggregate metrics.
pub fn inspect_code_table(
table : Array[CodeTableEntry],
near_size : Int,
same_size : Int,
) -> CodeTableMetrics raise VcdiffError {
validate_code_table(table, near_size, same_size)
let counts = Array::make(6, 0)
let mut compound_count = 0
let mut checksum = 1
for entry in table {
count_instruction_kind(entry.first, counts)
count_instruction_kind(entry.second, counts)
if entry.second.kind != Noop {
compound_count += 1
}
checksum = update_table_checksum(checksum, entry.first)
checksum = update_table_checksum(checksum, entry.second)
}
{
entry_count: table.length(),
compound_count,
variable_size_count: counts[4],
noop_count: counts[0],
add_count: counts[1],
run_count: counts[2],
copy_count: counts[3],
highest_copy_mode: counts[5],
serialized_size: table.length() * 6,
checksum,
}
}