pub const XD3_NOOP: u8 = 0;
pub const XD3_ADD: u8 = 1;
pub const XD3_RUN: u8 = 2;
pub const XD3_CPY: u8 = 3;
pub const MIN_MATCH: u8 = 4;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct CodeTableEntry {
pub type1: u8,
pub size1: u8,
pub type2: u8,
pub size2: u8,
}
pub type CodeTable = [CodeTableEntry; 256];
pub fn build_default_code_table() -> CodeTable {
let mut tbl = [CodeTableEntry::default(); 256];
let mut idx: usize = 0;
const ADD_SIZES: u8 = 17;
const NEAR_MODES: usize = 4;
const SAME_MODES: usize = 3;
const CPY_SIZES: u8 = 15;
const ADDCOPY_ADD_MAX: u8 = 4;
const ADDCOPY_NEAR_CPY_MAX: u8 = 6;
const ADDCOPY_SAME_CPY_MAX: u8 = 4;
const COPYADD_ADD_MAX: u8 = 1;
const COPYADD_NEAR_CPY_MAX: u8 = 4;
const COPYADD_SAME_CPY_MAX: u8 = 4;
const CPY_MODES: usize = 2 + NEAR_MODES + SAME_MODES;
tbl[idx] = CodeTableEntry {
type1: XD3_RUN,
size1: 0,
type2: XD3_NOOP,
size2: 0,
};
idx += 1;
tbl[idx] = CodeTableEntry {
type1: XD3_ADD,
size1: 0,
type2: XD3_NOOP,
size2: 0,
};
idx += 1;
for size1 in 1..=ADD_SIZES {
tbl[idx] = CodeTableEntry {
type1: XD3_ADD,
size1,
type2: XD3_NOOP,
size2: 0,
};
idx += 1;
}
for mode in 0..CPY_MODES as u8 {
tbl[idx] = CodeTableEntry {
type1: XD3_CPY + mode,
size1: 0,
type2: XD3_NOOP,
size2: 0,
};
idx += 1;
for size1 in MIN_MATCH..MIN_MATCH + CPY_SIZES {
tbl[idx] = CodeTableEntry {
type1: XD3_CPY + mode,
size1,
type2: XD3_NOOP,
size2: 0,
};
idx += 1;
}
}
for mode in 0..CPY_MODES as u8 {
let near_limit = 2 + NEAR_MODES as u8;
let cpy_max = if mode < near_limit {
ADDCOPY_NEAR_CPY_MAX
} else {
ADDCOPY_SAME_CPY_MAX
};
for add_size in 1..=ADDCOPY_ADD_MAX {
for cpy_size in MIN_MATCH..=cpy_max {
tbl[idx] = CodeTableEntry {
type1: XD3_ADD,
size1: add_size,
type2: XD3_CPY + mode,
size2: cpy_size,
};
idx += 1;
}
}
}
for mode in 0..CPY_MODES as u8 {
let near_limit = 2 + NEAR_MODES as u8;
let cpy_max = if mode < near_limit {
COPYADD_NEAR_CPY_MAX
} else {
COPYADD_SAME_CPY_MAX
};
for cpy_size in MIN_MATCH..=cpy_max {
for add_size in 1..=COPYADD_ADD_MAX {
tbl[idx] = CodeTableEntry {
type1: XD3_CPY + mode,
size1: cpy_size,
type2: XD3_ADD,
size2: add_size,
};
idx += 1;
}
}
}
debug_assert_eq!(idx, 256, "code table must have exactly 256 entries");
tbl
}
pub fn default_code_table() -> &'static CodeTable {
use std::sync::LazyLock;
static TABLE: LazyLock<CodeTable> = LazyLock::new(build_default_code_table);
&TABLE
}
#[derive(Debug, Clone, Copy)]
pub struct ChosenInstruction {
pub code1: u8,
pub code2: Option<u8>,
}
#[derive(Debug, Clone, Copy)]
pub struct InstructionInfo {
pub itype: u8,
pub size: u32,
}
pub fn choose_instruction(
prev: Option<&InstructionInfo>,
inst: &InstructionInfo,
) -> ChosenInstruction {
match inst.itype {
XD3_RUN => ChosenInstruction {
code1: 0,
code2: None,
},
XD3_ADD => {
let mut code1 = 1u8;
let mut code2 = None;
if inst.size <= 17 {
code1 += inst.size as u8;
if inst.size == 1
&& let Some(prev) = prev
&& prev.size == 4
&& prev.itype >= XD3_CPY
{
code2 = Some(247 + (prev.itype - XD3_CPY));
}
}
ChosenInstruction { code1, code2 }
}
_ => {
let mode = inst.itype - XD3_CPY;
let mut code1 = 19 + 16 * mode; let mut code2 = None;
if inst.size >= 4 && inst.size <= 18 {
code1 += (inst.size as u8) - 3;
if let Some(prev) = prev
&& prev.itype == XD3_ADD
&& prev.size <= 4
{
if inst.size <= 6 && mode <= 5 {
code2 = Some(
163 + (mode * 12)
+ (3 * ((prev.size as u8) - 1))
+ ((inst.size as u8) - 4),
);
} else if inst.size == 4 && mode >= 6 {
code2 = Some(235 + ((mode - 6) * 4) + ((prev.size as u8) - 1));
}
}
}
ChosenInstruction { code1, code2 }
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Instruction {
Add { len: u32 },
Copy { len: u32, addr: u64, mode: u8 },
Run { len: u32 },
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn table_has_256_entries() {
let table = build_default_code_table();
assert_eq!(table.len(), 256);
}
#[test]
fn index_0_is_run() {
let t = default_code_table();
assert_eq!(t[0].type1, XD3_RUN);
assert_eq!(t[0].size1, 0);
assert_eq!(t[0].type2, XD3_NOOP);
}
#[test]
fn index_1_is_add_size0() {
let t = default_code_table();
assert_eq!(t[1].type1, XD3_ADD);
assert_eq!(t[1].size1, 0);
assert_eq!(t[1].type2, XD3_NOOP);
}
#[test]
fn indices_2_to_18_are_add() {
let t = default_code_table();
for (i, size) in (2..=18).zip(1..=17u8) {
assert_eq!(t[i].type1, XD3_ADD, "index {i}");
assert_eq!(t[i].size1, size, "index {i}");
assert_eq!(t[i].type2, XD3_NOOP, "index {i}");
}
}
#[test]
fn copy_mode_0_starts_at_19() {
let t = default_code_table();
assert_eq!(t[19].type1, XD3_CPY);
assert_eq!(t[19].size1, 0);
assert_eq!(t[20].type1, XD3_CPY);
assert_eq!(t[20].size1, 4);
assert_eq!(t[34].type1, XD3_CPY);
assert_eq!(t[34].size1, 18);
}
#[test]
fn copy_mode_1_starts_at_35() {
let t = default_code_table();
assert_eq!(t[35].type1, XD3_CPY + 1);
assert_eq!(t[35].size1, 0);
}
#[test]
fn last_copy_mode_8() {
let t = default_code_table();
assert_eq!(t[147].type1, XD3_CPY + 8);
assert_eq!(t[147].size1, 0);
assert_eq!(t[162].type1, XD3_CPY + 8);
assert_eq!(t[162].size1, 18);
}
#[test]
fn add_copy_doubles_start_at_163() {
let t = default_code_table();
assert_eq!(t[163].type1, XD3_ADD);
assert_eq!(t[163].size1, 1);
assert_eq!(t[163].type2, XD3_CPY);
assert_eq!(t[163].size2, 4);
}
#[test]
fn copy_add_doubles_start_at_247() {
let t = default_code_table();
assert_eq!(t[247].type1, XD3_CPY);
assert_eq!(t[247].size1, 4);
assert_eq!(t[247].type2, XD3_ADD);
assert_eq!(t[247].size2, 1);
}
#[test]
fn index_255_is_last() {
let t = default_code_table();
assert_eq!(t[255].type1, XD3_CPY + 8);
assert_eq!(t[255].size1, 4);
assert_eq!(t[255].type2, XD3_ADD);
assert_eq!(t[255].size2, 1);
}
#[test]
fn all_doubles_have_nonzero_sizes() {
let t = default_code_table();
for (i, entry) in t.iter().enumerate() {
if entry.type2 != XD3_NOOP {
assert_ne!(entry.size1, 0, "double at {i} has size1=0");
assert_ne!(entry.size2, 0, "double at {i} has size2=0");
}
}
}
#[test]
fn choose_run() {
let r = choose_instruction(
None,
&InstructionInfo {
itype: XD3_RUN,
size: 10,
},
);
assert_eq!(r.code1, 0);
assert!(r.code2.is_none());
}
#[test]
fn choose_add_small() {
for size in 1..=17u32 {
let r = choose_instruction(
None,
&InstructionInfo {
itype: XD3_ADD,
size,
},
);
assert_eq!(r.code1, 1 + size as u8);
}
}
#[test]
fn choose_add_large() {
let r = choose_instruction(
None,
&InstructionInfo {
itype: XD3_ADD,
size: 18,
},
);
assert_eq!(r.code1, 1); }
#[test]
fn choose_copy_mode0_size4() {
let r = choose_instruction(
None,
&InstructionInfo {
itype: XD3_CPY,
size: 4,
},
);
assert_eq!(r.code1, 20); }
#[test]
fn choose_double_add_copy() {
let prev = InstructionInfo {
itype: XD3_ADD,
size: 1,
};
let inst = InstructionInfo {
itype: XD3_CPY,
size: 4,
};
let r = choose_instruction(Some(&prev), &inst);
assert_eq!(r.code2, Some(163)); }
#[test]
fn choose_double_copy_add() {
let prev = InstructionInfo {
itype: XD3_CPY,
size: 4,
};
let inst = InstructionInfo {
itype: XD3_ADD,
size: 1,
};
let r = choose_instruction(Some(&prev), &inst);
assert_eq!(r.code2, Some(247)); }
#[test]
fn choose_double_add_copy_mode6() {
let prev = InstructionInfo {
itype: XD3_ADD,
size: 2,
};
let inst = InstructionInfo {
itype: XD3_CPY + 6,
size: 4,
};
let r = choose_instruction(Some(&prev), &inst);
assert_eq!(r.code2, Some(236));
}
#[test]
fn code_table_matches_descriptor_offsets() {
let t = default_code_table();
assert_eq!(t[163].type1, XD3_ADD);
assert_eq!(t[163].type2, XD3_CPY);
assert_eq!(t[174].type1, XD3_ADD);
assert_eq!(t[174].type2, XD3_CPY);
assert_eq!(t[175].type1, XD3_ADD);
assert_eq!(t[175].type2, XD3_CPY + 1);
assert_eq!(t[235].type1, XD3_ADD);
assert_eq!(t[235].type2, XD3_CPY + 6);
assert_eq!(t[235].size2, 4);
assert_eq!(t[247].type1, XD3_CPY);
assert_eq!(t[247].type2, XD3_ADD);
}
}