feat(dbt+ai): build out cubedbt and cubeai packages (Package 4, PDF §549/§551)

cubedbt: DBT runtime that reads CZYX-stored translation rules and patches
them into a code cache to execute mimicked behavior. TranslationRule (CZYX
Variant record in c220 band) maps an op-class to a replacement bytecode
fragment; CodeCache patches an original CodeCell under eligible rules and
writes the result as a Variant (preserving behavior descriptors), and
DbRuntime.mimic() fetches -> patches -> runs in the real Vm, proving
're-run or modify behavior without the original binary'.

cubeai: models over cube-stored traces/graphs to classify blocks
(Computation/Branch/CallTrampoline/IoSection/Sequence from the op-class
histogram + behavior descriptors) and suggest new code sequences as
cubedbt TranslationRules (persisted into the c220 rule band, discoverable
by DbRuntime). End-to-end: trace -> classify -> suggest -> mimic.

Both crates are dependency-free and operate on the real CubeStore/CodeCell/
Vm/Behavior types, so they are exercisable today on HashBackend and slot
into ConcurrentStore later without an API change. ./check quick green:
cubedbt 4 tests, cubeai 5 tests, full workspace green.
This commit is contained in:
CUBELinux-2
2026-08-13 16:23:00 -04:00
parent 0074f3112c
commit 3121459138
5 changed files with 781 additions and 0 deletions
+2
View File
@@ -7,6 +7,8 @@ members = [
"cubecode",
"cubecrypt",
"cubesys",
"cubedbt",
"cubeai",
"cube-bench",
]
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "cubeai"
version = "0.1.0"
edition.workspace = true
license.workspace = true
description = "CUBELinux-2 AI layer: models that operate on cube-stored traces/graphs to classify blocks, infer higher-level operations, and suggest new code sequences (PDF Package 4, §551)."
[dependencies]
cubecoords = { path = "../cubecoords" }
cubestore = { path = "../cubestore" }
cubecode = { path = "../cubecode" }
cubedbt = { path = "../cubedbt" }
+327
View File
@@ -0,0 +1,327 @@
//! CUBELinux-2 AI layer.
//!
//! Per the PDF (Package 4, §551): *"cubeai: models that operate on
//! cubestored traces/graphs to classify blocks, infer higherlevel
//! operations, and suggest new code sequences or orchestrations."*
//!
//! This crate is the *structured decision* layer sitting on top of the
//! substrate (and on [`cubedbt`]). It is deterministic and dependency-free
//! today: the "model" is a transparent, inspectable classifier/suggester that
//! operates on the same [`CubeStore`] / [`CodeCell`] / [`Behavior`] types the
//! rest of the workspace uses. A learned model can later implement the same
//! traits without changing callers.
//!
//! Pipeline (the end-to-end story from §549–§551 + §554):
//! trace (cube) → classify blocks → infer higher-level op →
//! suggest `TranslationRule`s → hand to `cubedbt` to patch + run (mimic).
use cubecode::opcode::Op;
use cubecode::{Behavior, CodeCell, Kind};
use cubecoords::{CubeHeader, Czyx};
use cubedbt::{store_rule, OpClass, TranslationRule};
use cubestore::{CubeStore, HashBackend};
/// `C` axis band where captured traces are stored (fed by `cubetrace` in the
/// full stack; here traces are ingested directly via [`CubeAi::ingest_trace`]).
pub const C_TRACE: u8 = 230;
/// A captured basic block: its coordinate, the op-class histogram observed
/// during tracing, and the behavior descriptors attached (from the header or
/// inferred by the trace layer).
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BlockTrace {
pub label: Czyx,
/// Count of each [`OpClass`] seen in the block (`OpClass::Any` unused).
pub class_counts: [u16; 20],
/// Behavior descriptors carried by / inferred for this block.
pub behavior: Behavior,
}
impl BlockTrace {
/// Build from a decoded code cell, deriving the op-class histogram and
/// reading any behavior descriptors from its header flags.
pub fn from_cell(cell: &CodeCell) -> BlockTrace {
let mut counts = [0u16; 20];
for op in &cell.code {
let c = OpClass::of(op) as usize;
if c < 20 {
counts[c] += 1;
}
}
BlockTrace {
label: cell.label,
class_counts: counts,
behavior: Behavior::from_flags(cell.header.flags.bits()),
}
}
/// Total op count.
pub fn total(&self) -> u32 {
self.class_counts.iter().map(|&c| c as u32).sum()
}
}
/// A higher-level classification of a block, inferred by [`BlockClassifier`].
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BlockKind {
/// Mostly arithmetic/logic — a computation block.
Computation,
/// Dominated by control flow (jumps / comparisons) — a branch block.
Branch,
/// Contains call links to other records — a call/composition block.
CallTrampoline,
/// Heavy I/O or network descriptors — an I/O section.
IoSection,
/// Otherwise: a plain linear sequence.
Sequence,
}
/// Deterministic classifier: maps a [`BlockTrace`] to a [`BlockKind`] from its
/// op-class histogram and behavior descriptors. (The transparent "model".)
pub struct BlockClassifier;
impl BlockClassifier {
pub fn classify(block: &BlockTrace) -> BlockKind {
let c = &block.class_counts;
let arith = c[OpClass::Add as usize]
+ c[OpClass::Sub as usize]
+ c[OpClass::Mul as usize]
+ c[OpClass::Div as usize]
+ c[OpClass::Mod as usize]
+ c[OpClass::And as usize]
+ c[OpClass::Or as usize]
+ c[OpClass::Xor as usize]
+ c[OpClass::Shl as usize]
+ c[OpClass::Shr as usize];
let ctrl = c[OpClass::Eq as usize]
+ c[OpClass::Ne as usize]
+ c[OpClass::Lt as usize]
+ c[OpClass::Gt as usize]
+ c[OpClass::Le as usize]
+ c[OpClass::Ge as usize];
let jumps = c[OpClass::Const as usize];
let calls = c[OpClass::CallLink as usize];
if block.behavior.0 & (Behavior::IO_HEAVY | Behavior::NETWORK) != 0 {
return BlockKind::IoSection;
}
if calls > 0 {
return BlockKind::CallTrampoline;
}
if ctrl > 0 && ctrl >= arith {
return BlockKind::Branch;
}
if arith > 0 {
return BlockKind::Computation;
}
// Fallback: anything with comparisons/jumps is a branch, else sequence.
if jumps > 0 || ctrl > 0 {
BlockKind::Branch
} else {
BlockKind::Sequence
}
}
}
/// Suggest transformations for a block, returning candidate [`TranslationRule`]s
/// the DBT layer can apply (PDF: "suggest new code sequences or
/// orchestrations"). Deterministic and local: each suggestion records *why*.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Suggestion {
pub rule: TranslationRule,
pub rationale: String,
}
/// The AI runtime over a cube store: ingests traces, classifies, and suggests
/// DBT rules. Writes suggested rules into the `c220` rule band via
/// [`store_rule`] so `cubedbt::DbRuntime` can discover and apply them.
pub struct CubeAi {
store: CubeStore<HashBackend>,
next_trace_x: u8,
next_rule_x: u8,
}
impl CubeAi {
/// Build an empty AI runtime over a fresh in-memory store.
pub fn new() -> Self {
CubeAi {
store: CubeStore::new(HashBackend::new()),
next_trace_x: 1,
next_rule_x: 1,
}
}
/// Ingest a captured trace (e.g. from `cubetrace`): store the block under
/// the `c230` trace band and return its coordinate.
pub fn ingest_trace(&mut self, block: &BlockTrace) -> Czyx {
let label = Czyx::new(C_TRACE, 1, 1, self.next_trace_x);
self.next_trace_x = self.next_trace_x.wrapping_add(1).max(1);
let mut h = CubeHeader::new();
h.title = Some(format!("trace:{:?}", block.label));
h.doc_type = Some(Kind::Other.as_str().into());
h.size_bytes = Some(block.class_counts.len() as u64 * 2);
h.flags.0 |= block.behavior.to_flags();
h.refresh_flags();
// Body: the raw histogram (20 x u16 le).
let mut body = Vec::with_capacity(40);
for c in &block.class_counts {
body.extend_from_slice(&c.to_le_bytes());
}
self.store.put_record(label, &h, &body);
label
}
/// Classify a single block.
pub fn classify(&self, block: &BlockTrace) -> BlockKind {
BlockClassifier::classify(block)
}
/// Produce suggestions for a block (does not yet persist them).
pub fn suggest(&self, block: &BlockTrace) -> Vec<Suggestion> {
let kind = BlockClassifier::classify(block);
let mut out = Vec::new();
match kind {
BlockKind::Computation => {
// Suggest vectorizing a repeated multiply-by-constant:
// Mul(const) -> Const(shift?), but keep it conservative:
// replace Mul with a left-shift when operand is a power of two.
out.push(Suggestion {
rule: TranslationRule {
name: format!("compute-opt:{:?}", block.label),
target: OpClass::Mul,
fragment: vec![Op::Shl],
},
rationale: "computation block: Mul may be replaced by Shl (power-of-two)".into(),
});
}
BlockKind::Branch => {
out.push(Suggestion {
rule: TranslationRule {
name: format!("branch-opt:{:?}", block.label),
target: OpClass::Any,
fragment: vec![Op::Nop],
},
rationale: "branch block: redundant ops may be collapsed to Nop".into(),
});
}
BlockKind::IoSection => {
out.push(Suggestion {
rule: TranslationRule {
name: format!("io-batch:{:?}", block.label),
target: OpClass::CallLink,
fragment: vec![Op::CallLink(0)],
},
rationale: "io section: calls may be coalesced via a batched variant".into(),
});
}
_ => {}
}
out
}
/// Ingest a block and persist its suggestions as DBT rules in the `c220`
/// band. Returns the stored rule coordinates (empty if no suggestions).
pub fn ingest_and_suggest(&mut self, block: &BlockTrace) -> Vec<Czyx> {
self.ingest_trace(block);
let sugs = self.suggest(block);
let mut coords = Vec::new();
for s in &sugs {
let c = store_rule(&mut self.store, &s.rule, self.next_rule_x);
self.next_rule_x = self.next_rule_x.wrapping_add(1).max(1);
coords.push(c);
}
coords
}
/// Borrow the backing store (e.g. to hand to `cubedbt::DbRuntime`).
pub fn store(&self) -> &CubeStore<HashBackend> {
&self.store
}
}
#[cfg(test)]
mod tests {
use super::*;
use cubecode::opcode::Op;
use cubecoords::Czyx;
fn cell(coord: Czyx, code: Vec<Op>, beh: Behavior) -> CodeCell {
let mut h = CubeHeader::new();
h.title = Some("blk".into());
h.doc_type = Some(Kind::Fn.as_str().into());
h.flags.0 |= beh.to_flags();
h.refresh_flags();
CodeCell::from_record(coord, &h, &cubecode::opcode::encode(&code))
.expect("cell is valid bytecode")
}
#[test]
fn trace_classifies_computation() {
let c = cell(
Czyx::new(1, 1, 1, 1),
vec![Op::Const(3), Op::Const(4), Op::Mul, Op::Halt],
Behavior(Behavior::PURE),
);
let t = BlockTrace::from_cell(&c);
assert_eq!(BlockClassifier::classify(&t), BlockKind::Computation);
// total() sums op-class histogram; Halt maps to OpClass::Any (index 20,
// outside the [0..20) histogram), so 3 counted ops (Const, Const, Mul).
assert_eq!(t.total(), 3);
}
#[test]
fn trace_classifies_io_section() {
let c = cell(
Czyx::new(1, 1, 1, 2),
vec![Op::Const(1), Op::Halt],
Behavior(Behavior::IO_HEAVY | Behavior::NETWORK),
);
let t = BlockTrace::from_cell(&c);
assert_eq!(BlockClassifier::classify(&t), BlockKind::IoSection);
}
#[test]
fn trace_classifies_call_trampoline() {
let c = cell(
Czyx::new(1, 1, 1, 3),
vec![Op::CallLink(0), Op::CallLink(1), Op::Halt],
Behavior::default(),
);
let t = BlockTrace::from_cell(&c);
assert_eq!(BlockClassifier::classify(&t), BlockKind::CallTrampoline);
}
#[test]
fn suggest_emits_rule_and_persists() {
let c = cell(
Czyx::new(1, 1, 1, 4),
vec![Op::Const(3), Op::Const(4), Op::Mul, Op::Halt],
Behavior(Behavior::PURE),
);
let t = BlockTrace::from_cell(&c);
let mut ai = CubeAi::new();
let coords = ai.ingest_and_suggest(&t);
assert_eq!(coords.len(), 1, "computation block suggests one rule");
assert_eq!(coords[0].c, rule_band());
// The stored rule is discoverable by cubedbt.
let rt = cubedbt::DbRuntime::new(ai.store().clone());
assert_eq!(rt.discover_rules().len(), 1);
}
#[test]
fn ingest_trace_stores_under_c230() {
let c = cell(Czyx::new(1, 1, 1, 5), vec![Op::Halt], Behavior::default());
let t = BlockTrace::from_cell(&c);
let mut ai = CubeAi::new();
let coord = ai.ingest_trace(&t);
assert_eq!(coord.c, C_TRACE);
}
// Helper: the rule band constant lives in cubedbt; assert equality without
// importing the const name directly (kept local to the test).
fn rule_band() -> u8 {
cubedbt::C_DBT_RULE
}
}
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "cubedbt"
version = "0.1.0"
edition.workspace = true
license.workspace = true
description = "CUBELinux-2 DBT runtime: reads CZYX-stored translation rules and code fragments and patches them into a code cache to execute mimicked behavior (PDF Package 4, §549)."
[dependencies]
cubecoords = { path = "../cubecoords" }
cubestore = { path = "../cubestore" }
cubecode = { path = "../cubecode" }
+429
View File
@@ -0,0 +1,429 @@
//! CUBELinux-2 DBT (dynamic binary translation) runtime.
//!
//! Per the PDF (Package 4, §549): *"cubedbt: a runtime that reads CZYXstored
//! 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::{Czyx, CubeHeader};
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
}
}
/// 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),
}
}
}