///|
/// The instruction types stored in an RFC 3284 code-table entry.
pub(all) enum InstructionKind {
Noop
Add
Run
Copy
} derive(Eq, Debug)
///|
/// One instruction triple from a code-table entry.
///
/// A zero size means that the actual size follows the code byte as an RFC
/// unsigned integer. The mode is meaningful only for COPY.
pub(all) struct CodeInstruction {
kind : InstructionKind
size : Int
mode : Int
} derive(Eq, Debug)
///|
/// A code-table entry containing one required instruction and an optional
/// second instruction represented by NOOP.
pub(all) struct CodeTableEntry {
first : CodeInstruction
second : CodeInstruction
} derive(Eq, Debug)
///|
/// Number of recent addresses in the standard near cache.
pub const DEFAULT_NEAR_CACHE_SIZE : Int = 4
///|
/// Number of 256-entry banks in the standard same cache.
pub const DEFAULT_SAME_CACHE_SIZE : Int = 3
///|
/// Number of legal COPY address modes in the standard table.
pub const DEFAULT_ADDRESS_MODE_COUNT : Int = 9
///|
fn code_instruction(
kind : InstructionKind,
size : Int,
mode : Int,
) -> CodeInstruction {
{ kind, size, mode }
}
///|
fn noop_instruction() -> CodeInstruction {
code_instruction(Noop, 0, 0)
}
///|
fn single_entry(
kind : InstructionKind,
size : Int,
mode : Int,
) -> CodeTableEntry {
{ first: code_instruction(kind, size, mode), second: noop_instruction() }
}
///|
fn paired_entry(
first_kind : InstructionKind,
first_size : Int,
first_mode : Int,
second_kind : InstructionKind,
second_size : Int,
second_mode : Int,
) -> CodeTableEntry {
{
first: code_instruction(first_kind, first_size, first_mode),
second: code_instruction(second_kind, second_size, second_mode),
}
}
///|
fn append_single_instructions(table : Array[CodeTableEntry]) -> Unit {
table.push(single_entry(Run, 0, 0))
table.push(single_entry(Add, 0, 0))
for size = 1; size <= 17; size = size + 1 {
table.push(single_entry(Add, size, 0))
}
for mode = 0; mode < DEFAULT_ADDRESS_MODE_COUNT; mode = mode + 1 {
table.push(single_entry(Copy, 0, mode))
for size = 4; size <= 18; size = size + 1 {
table.push(single_entry(Copy, size, mode))
}
}
}
///|
fn append_add_copy_pairs(table : Array[CodeTableEntry]) -> Unit {
for mode = 0; mode <= 5; mode = mode + 1 {
for add_size = 1; add_size <= 4; add_size = add_size + 1 {
for copy_size = 4; copy_size <= 6; copy_size = copy_size + 1 {
table.push(paired_entry(Add, add_size, 0, Copy, copy_size, mode))
}
}
}
for mode = 6; mode < DEFAULT_ADDRESS_MODE_COUNT; mode = mode + 1 {
for add_size = 1; add_size <= 4; add_size = add_size + 1 {
table.push(paired_entry(Add, add_size, 0, Copy, 4, mode))
}
}
}
///|
fn append_copy_add_pairs(table : Array[CodeTableEntry]) -> Unit {
for mode = 0; mode < DEFAULT_ADDRESS_MODE_COUNT; mode = mode + 1 {
table.push(paired_entry(Copy, 4, mode, Add, 1, 0))
}
}
///|
/// Builds the RFC 3284 default 256-entry instruction code table.
///
/// A fresh array is returned on every call so callers may inspect or transform
/// it without mutating decoder state.
pub fn default_code_table() -> Array[CodeTableEntry] {
let table : Array[CodeTableEntry] = []
append_single_instructions(table)
append_add_copy_pairs(table)
append_copy_add_pairs(table)
table
}
///|
/// Looks up a standard code byte while validating the byte-sized index.
pub fn default_code_entry(index : Int) -> CodeTableEntry raise VcdiffError {
if index < 0 || index >= 256 {
raise InvalidInstruction(
offset=-1,
reason="default code index must be in the range 0..255",
)
}
default_code_table()[index]
}
///|
/// Returns whether a code triple is internally valid for the default format.
pub fn CodeInstruction::is_valid(self : CodeInstruction) -> Bool {
if self.size < 0 || self.size > 255 {
return false
}
match self.kind {
Noop => self.size == 0 && self.mode == 0
Add | Run => self.mode == 0
Copy => self.mode >= 0 && self.mode < DEFAULT_ADDRESS_MODE_COUNT
}
}
///|
/// Returns one stable uppercase instruction name for diagnostics and reports.
pub fn InstructionKind::name(self : InstructionKind) -> String {
match self {
Noop => "NOOP"
Add => "ADD"
Run => "RUN"
Copy => "COPY"
}
}