Files
cubelinux-2/cubedbt/src/lib.rs
T
CUBELinux-2andHermes Agent ec25347af9 fix(cubedbt): add Default impl for CodeCache to satisfy clippy -D warnings
clippy flagged "you should consider adding a `Default` implementation for
`CodeCache`". Add the impl delegating to `new()`.

Also: `patch` method had been accidentally pulled out of `impl CodeCache`
into module scope during a prior edit; move it back inside the impl block
so it compiles.

Verification: all 4 cubedbt tests pass, clippy clean.

Co-Authored-By: Hermes Agent
2026-08-20 17:36:17 -04:00

422 lines
15 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! CUBELinux-2 DBT (dynamic binary translation) runtime.
//!
//! Per the PDF (Package 4, §549): *"cubedbt: a runtime that reads CZYX‑stored
//! translation rules and code fragments and patches them into a code cache to
//! execute mimicked behavior."*
//!
//! This crate is the substrate for the PDF's "watch a binary, archive its
//! behavior structurally, replay or transform it as if it were its own program"
//! story (§554). It is deliberately dependency-free and operates on the real
//! `CubeStore` / `CodeCell` / `Vm` types from the rest of the workspace, so it
//! is exercisable today on `HashBackend` and slots into `ConcurrentStore`
//! later without an API change.
//!
//! Model
//! -----
//! * A **translation rule** is a CZYX record (`Kind::Variant`) whose body is a
//! serialized [`TranslationRule`]. It names an *op kind* it can replace and
//! carries a *replacement fragment* (a `Vec<Op>`). The rule record links to
//! the original code cell it may substitute (the association edge).
//! * A **code cache** is the patched working set: `patch()` rewrites an
//! original `CodeCell`'s bytecode under the eligible rules and writes the
//! result back as a new record (preserving the original header's kind/title
//! and behavior descriptors, so it runs through the same `Vm`).
//! * A **DBT runtime** discovers rules in a store and can `mimic` a target
//! coordinate: fetch original → apply rules → run the patched version in the
//! `Vm`, returning the `RunResult`.
use cubecode::{opcode::Op, CodeCell, Kind, Vm};
use cubecoords::{CubeHeader, Czyx};
use cubestore::{CubeBackend, CubeStore, HashBackend};
/// `C` axis band where DBT translation rules are stored, kept distinct from the
/// `c210` OS-kernel band (`cubecode::C_OS_KERNEL`) and the `c200` snapshot data.
pub const C_DBT_RULE: u8 = 220;
/// `C` axis band where patched/mimicked code-cache entries are written.
pub const C_DBT_CACHE: u8 = 221;
/// The `Op` kinds a translation rule can target.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum OpClass {
Const,
Add,
Sub,
Mul,
Div,
Mod,
And,
Or,
Xor,
Shl,
Shr,
Eq,
Ne,
Lt,
Gt,
Le,
Ge,
Load,
Store,
CallLink,
Any,
}
impl OpClass {
/// Classify a live [`Op`].
pub fn of(op: &Op) -> OpClass {
match op {
Op::Const(_) => OpClass::Const,
Op::Add => OpClass::Add,
Op::Sub => OpClass::Sub,
Op::Mul => OpClass::Mul,
Op::Div => OpClass::Div,
Op::Mod => OpClass::Mod,
Op::And => OpClass::And,
Op::Or => OpClass::Or,
Op::Xor => OpClass::Xor,
Op::Shl => OpClass::Shl,
Op::Shr => OpClass::Shr,
Op::Eq => OpClass::Eq,
Op::Ne => OpClass::Ne,
Op::Lt => OpClass::Lt,
Op::Gt => OpClass::Gt,
Op::Le => OpClass::Le,
Op::Ge => OpClass::Ge,
Op::Load(_) => OpClass::Load,
Op::Store(_) => OpClass::Store,
Op::CallLink(_) => OpClass::CallLink,
_ => OpClass::Any,
}
}
}
/// A translation rule: a named, CZYX-addressed mapping from an op class to a
/// replacement bytecode fragment. Serialized into a rule record's body.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TranslationRule {
/// Human name (also the rule record's title).
pub name: String,
/// Which op class this rule can substitute.
pub target: OpClass,
/// Replacement bytecode fragment (must itself be valid; it replaces every
/// matched op in the original, in place, preserving program length-agnostic
/// semantics the caller is responsible for).
pub fragment: Vec<Op>,
}
impl TranslationRule {
/// Serialize to a stable byte form (name length-prefixed, target byte,
/// fragment as opcode codec). No external deps.
pub fn to_bytes(&self) -> Vec<u8> {
let mut out = Vec::new();
let nb = self.name.as_bytes();
out.push(nb.len() as u8);
out.extend_from_slice(nb);
out.push(self.target as u8);
out.push(self.fragment.len() as u8);
for op in &self.fragment {
out.extend_from_slice(&cubecode::opcode::encode(&[*op]));
}
out
}
/// Inverse of [`to_bytes`]. Returns `None` on any malformed input.
pub fn from_bytes(b: &[u8]) -> Option<TranslationRule> {
let mut i = 0;
let nlen = *b.get(i)? as usize;
i += 1;
if i + nlen > b.len() {
return None;
}
let name = String::from_utf8(b[i..i + nlen].to_vec()).ok()?;
i += nlen;
let target = *b.get(i)?;
i += 1;
let target = match target {
0 => OpClass::Const,
1 => OpClass::Add,
2 => OpClass::Sub,
3 => OpClass::Mul,
4 => OpClass::Div,
5 => OpClass::Mod,
6 => OpClass::And,
7 => OpClass::Or,
8 => OpClass::Xor,
9 => OpClass::Shl,
10 => OpClass::Shr,
11 => OpClass::Eq,
12 => OpClass::Ne,
13 => OpClass::Lt,
14 => OpClass::Gt,
15 => OpClass::Le,
16 => OpClass::Ge,
17 => OpClass::Load,
18 => OpClass::Store,
19 => OpClass::CallLink,
_ => OpClass::Any,
};
let flen = *b.get(i)? as usize;
i += 1;
let mut fragment = Vec::new();
for _ in 0..flen {
// Each fragment op was encoded individually; decode one at a time,
// advancing `i` by its encoded byte length.
let one = decode_one(&b[i..])?;
i += one.len;
fragment.push(one.op);
}
Some(TranslationRule {
name,
target,
fragment,
})
}
}
/// One decoded opcode plus its encoded length, used by `from_bytes`.
struct One {
op: Op,
len: usize,
}
fn decode_one(b: &[u8]) -> Option<One> {
let op = cubecode::opcode::decode(b).ok()?;
let op = op.into_iter().next()?;
let len = cubecode::opcode::encode(&[op]).len();
Some(One { op, len })
}
/// A patched working set ("code cache") inside a [`CubeStore`].
///
/// `patch` rewrites an original [`CodeCell`]'s bytecode under the eligible
/// rules and writes the result back as a new record (preserving the original
/// header's kind/title and behavior descriptors, so it runs through the same
/// `Vm`). This is the literal "patches them into a code cache to execute
/// mimicked behavior" step from the PDF §549.
pub struct CodeCache {
store: CubeStore<HashBackend>,
/// Coordinate of the next cache slot to allocate.
next_x: u8,
}
impl CodeCache {
/// Build an empty cache over a fresh in-memory backend.
pub fn new() -> Self {
CodeCache {
store: CubeStore::new(HashBackend::new()),
next_x: 1,
}
}
/// Patch `original` under `rules`: every op whose class matches a rule's
/// target (or whose rule target is `Any`) is replaced by that rule's
/// fragment. The rewritten cell is stored at a fresh cache coordinate and
/// returned (with its new label). The original store is untouched.
pub fn patch(&mut self, original: &CodeCell, rules: &[TranslationRule]) -> CodeCell {
let mut patched: Vec<Op> = Vec::with_capacity(original.code.len());
for op in &original.code {
let class = OpClass::of(op);
let mut applied = false;
for r in rules {
if r.target == class || r.target == OpClass::Any {
patched.extend_from_slice(&r.fragment);
applied = true;
break;
}
}
if !applied {
patched.push(*op);
}
}
let label = Czyx::new(C_DBT_CACHE, 1, 1, self.next_x);
self.next_x = self.next_x.wrapping_add(1).max(1);
let (h, body) = cache_header_for(original, &cubecode::opcode::encode(&patched));
self.store.put_record(label, &h, &body);
CodeCell::from_record(label, &h, &body).expect("patched cell is always valid bytecode")
}
/// Run a patched cache entry in the VM, returning its result.
pub fn run(&self, cell: &CodeCell) -> cubecode::RunResult {
let mut vm = Vm::new(self.store.clone());
vm.run(cell.label)
}
/// Borrow the backing store (e.g. to persist or inspect).
pub fn store(&self) -> &CubeStore<HashBackend> {
&self.store
}
}
impl Default for CodeCache {
fn default() -> Self {
Self::new()
}
}
/// Build a cache record header that preserves the original's kind/title and
/// behavior descriptors, but marks it a `Variant` (a mimicked implementation).
fn cache_header_for(original: &CodeCell, body: &[u8]) -> (CubeHeader, Vec<u8>) {
let mut h = CubeHeader::new();
h.title = original.name().map(|s| format!("mimic:{}", s));
h.doc_type = Some(Kind::Variant.as_str().into());
h.size_bytes = Some(body.len() as u64);
// Carry the original's behavior descriptors forward (spec: descriptors
// travel with the mimicked behavior).
h.flags.0 |= original.header.flags.0 & cubecode::HEADER_FLAG_BEHAVIOR;
h.refresh_flags();
(h, body.to_vec())
}
/// The DBT runtime: discovers CZYX-stored translation rules and can `mimic` a
/// target code cell by patching it under those rules and running the result.
pub struct DbRuntime<B: CubeBackend> {
store: CubeStore<B>,
}
impl<B: CubeBackend> DbRuntime<B> {
/// Wrap a store that already contains rule records (or will, via
/// [`store_rule`]). The runtime is read-only over this store.
pub fn new(store: CubeStore<B>) -> Self {
DbRuntime { store }
}
/// Collect every translation rule currently in the `c220` rule band.
pub fn discover_rules(&self) -> Vec<TranslationRule> {
let mut rules = Vec::new();
for k in self.store.keys() {
if k.c != C_DBT_RULE {
continue;
}
if let Some((_, body)) = self.store.get_record(&k) {
if let Some(r) = TranslationRule::from_bytes(&body) {
rules.push(r);
}
}
}
rules
}
/// Fetch a code cell by coordinate from the wrapped store.
pub fn fetch(&self, label: Czyx) -> Option<CodeCell> {
let (h, b) = self.store.get_record(&label)?;
CodeCell::from_record(label, &h, &b)
}
/// Mimic `target`: fetch it, apply all discovered rules, patch into a fresh
/// cache, and run the patched version — returning the `RunResult`. This is
/// the end-to-end "re-run or modify behavior without the original binary"
/// path (PDF §554).
pub fn mimic(&self, target: Czyx) -> Option<cubecode::RunResult> {
let original = self.fetch(target)?;
let rules = self.discover_rules();
let mut cache = CodeCache::new();
let patched = cache.patch(&original, &rules);
Some(cache.run(&patched))
}
}
/// Store a translation rule into the `c220` rule band at a fresh coordinate.
/// Returns the coordinate it was written to.
pub fn store_rule<B: CubeBackend>(store: &mut CubeStore<B>, rule: &TranslationRule, x: u8) -> Czyx {
let label = Czyx::new(C_DBT_RULE, 1, 1, x);
let mut h = CubeHeader::new();
h.title = Some(rule.name.clone());
h.doc_type = Some(Kind::Variant.as_str().into());
h.size_bytes = Some(rule.to_bytes().len() as u64);
h.refresh_flags();
store.put_record(label, &h, &rule.to_bytes());
label
}
#[cfg(test)]
mod tests {
use super::*;
use cubecode::opcode::Op;
use cubecode::RunResult;
use cubecoords::Czyx;
fn sample_cell(coord: Czyx, code: Vec<Op>) -> CodeCell {
let mut h = CubeHeader::new();
h.title = Some("sample".into());
h.doc_type = Some(Kind::Fn.as_str().into());
h.refresh_flags();
CodeCell::from_record(coord, &h, &cubecode::opcode::encode(&code))
.expect("sample is valid bytecode")
}
#[test]
fn rule_round_trips_through_bytes() {
let r = TranslationRule {
name: "double-add".into(),
target: OpClass::Add,
fragment: vec![Op::Const(2), Op::Mul],
};
let bytes = r.to_bytes();
let back = TranslationRule::from_bytes(&bytes).expect("rule decodes");
assert_eq!(r, back);
}
#[test]
fn cache_patches_and_runs() {
// Original: Const 3, Const 4, Add, Halt => 3+4 = 7.
let orig = sample_cell(
Czyx::new(1, 1, 1, 1),
vec![Op::Const(3), Op::Const(4), Op::Add, Op::Halt],
);
// Rule: replace `Add` with `Const 2, Mul` => (a)*(2). With a=3,b=4:
// naive in-place substitution yields Const3, Const4, Const2, Mul =>
// 4*2 = 8. This proves the patched fragment is what runs.
let rule = TranslationRule {
name: "mul-by-2".into(),
target: OpClass::Add,
fragment: vec![Op::Const(2), Op::Mul],
};
let mut cache = CodeCache::new();
let patched = cache.patch(&orig, &[rule]);
assert_eq!(patched.kind(), Kind::Variant, "cache entry is a Variant");
match cache.run(&patched) {
RunResult::Halted { top: Some(v) } => assert_eq!(v, 8, "patched semantics differ"),
other => panic!("patched run failed: {:?}", other),
}
}
#[test]
fn runtime_mimic_end_to_end() {
let mut store = CubeStore::new(HashBackend::new());
// Original target at c1.
let target = Czyx::new(1, 1, 1, 1);
let orig = sample_cell(target, vec![Op::Const(10), Op::Const(5), Op::Sub, Op::Halt]);
store.put_record(orig.label, &orig.header, &orig.body());
// Rule in the c220 band: replace Sub with Add (10+5=15 instead of 5).
let rule = TranslationRule {
name: "sub->add".into(),
target: OpClass::Sub,
fragment: vec![Op::Add],
};
store_rule(&mut store, &rule, 1);
let rt = DbRuntime::new(store);
let rules = rt.discover_rules();
assert_eq!(rules.len(), 1, "rule discovered in c220 band");
match rt.mimic(target) {
Some(RunResult::Halted { top: Some(v) }) => assert_eq!(v, 15, "mimic applied rule"),
other => panic!("mimic failed: {:?}", other),
}
}
#[test]
fn mimic_without_rules_matches_original() {
let mut store = CubeStore::new(HashBackend::new());
let target = Czyx::new(1, 1, 1, 2);
let orig = sample_cell(target, vec![Op::Const(7), Op::Const(2), Op::Mul, Op::Halt]);
store.put_record(orig.label, &orig.header, &orig.body());
let rt = DbRuntime::new(store);
match rt.mimic(target) {
Some(RunResult::Halted { top: Some(v) }) => assert_eq!(v, 14),
other => panic!("mimic without rules should run original: {:?}", other),
}
}
}