// ===== 控制消息解码面(dop 分派)=====
// 帧形(规格书 §4):u32 len(BE) | 0x83 | ['D' atom-cache 段头(可省)] | ETF 项流
// 控制项流首项 = Int tag → dop 词表分派(dop_gen 封闭查表);后续项 = 参数流。
// 六边形:ETF 项解码藏 EtfPort 注入后(moonbit-eetf 组合根 impl)——
// beam-dist 核心零 eetf 依赖;字节级项解码=组合根役(真件深化候)。
// 'D' 段头(A8 全规格真件):wire='D'|no_atoms u8|位图|指令段——无 hdr_flags 字节
// (真源 external.c:987);no_atoms=0 空头整块无位图;非空段解析 attab
// (旧引用诚实拒——我方无 out 表,槽名不可知=协议违约侧)。
///|
pub(all) suberror ControlError {
ShortFrame(String) // 帧截断/起头非 83
UnknownDop(Int32) // tag 不在词表(封闭拒)
BadTag(String) // 首项非整数(控制项流首项=tag 契约)
CacheSegment(String) // 'D' 非空段(v0 诚实拒——A8 真件役候)
Codec(String) // ETF 项解码错(EtfPort 转译)
}
///|
/// ETF 项值(控制面所需子集——beam-dist 自持,eetf 组合根映射;
/// 不 import eetf——六边形归位)
pub(all) enum EtfItem {
IAtom(String)
IInt(Int32)
IPid(String, Int32, Int32, Int32) // node, id, serial, creation
IRef(String, Int32, Int32) // node, id 序列长, creation
ITuple(Array[EtfItem])
IList(Array[EtfItem])
INil
IBinary(Bytes)
IOther // 其余项型(骨架占位——float/map/big 等直通)
}
///|
/// ETF 项解码注入位(组合根=moonbit-eetf impl;decode 一项推进一位;
/// attab='D' 段头解析出的 atom cache 表——'R' ATOM_CACHE_REF 查表语境)
pub(open) trait EtfPort {
fn decode_term(self : Self, bytes : Bytes, pos : Int32, attab : Array[String]) -> (
EtfItem,
Int32,
) raise ControlError
}
///|
/// 控制消息(tag+词表名+语义动词+参数项流)
pub(all) struct ControlMsg {
tag : Int32
name : String
semantics : String
items : Array[EtfItem]
next_pos : Int32 // ctl 段终止 pos(遇 VERSION_MAGIC 停)——msg 段起点(含 131),
// 上层解 msg 用(dist.c 两段线形:131|ctl|131|msg)
}
///|
/// latin1 字节列→String(段内 atom 名实形 latin1——dist 头 atom 无 utf8 形;
/// 值域 0-255,>127 兑 '?' 兜底——handshake latin 同款;ControlError 域专用
/// 故不与 handshake.DistError 域 latin 同名[4051])
fn latin1_name(
frame : Bytes,
pos : Int32,
len : Int32,
) -> String raise ControlError {
if pos + len > frame.length() {
raise ControlError::ShortFrame(
"latin1[\{len}] at \{pos} over \{frame.length()}",
)
}
let sb = StringBuilder()
for i in 0.. sb.write_char(c)
None => sb.write_char('?')
}
}
sb.to_string()
}
///|
/// 'D' atom-cache 段头全规格解析(A8 真件——moonactor 底本移植;
/// 真源 external.c:987 `ep+=2; no_atoms=get_int8(ep)`):
/// wire = 'D' | no_atoms u8 | 位图 | 指令段——无 hdr_flags 字节。
/// no_atoms=0 → 空头('D' 00 直过,整块无位图——FLAG_BYTES(0) 死区,
/// 金罐 44 00 68 实证);位图 fb=(no_atoms>>1)+1 字节,byte 内高 nibble 先;
/// LONG_ATOMS=位图尾 nibble bit0;指令段顺序紧排:每 atom [cix_lo u8]
/// (nibble 高 3 位=cix 高位;我方无表,槽号仅记账)+(新? [len u8|u16][name]
/// 入 attab / 旧引用诚实拒——声明 ATOM_CACHE 位却无 out 表=协议违约侧)。
/// 非 'D' 原位直过(段头可省)。返回 (attab, ETF 项流起始 pos)。
fn decode_cache_segment(
frame : Bytes,
pos : Int32,
) -> (Array[String], Int32) raise ControlError {
let attab : Array[String] = []
if pos >= frame.length() || frame[pos].to_int() != 68 {
// 非 'D'——无段头,原位直过
return (attab, pos)
}
if pos + 1 >= frame.length() {
raise ControlError::ShortFrame("'D' 段头截断")
}
let no_atoms = frame[pos + 1].to_int()
if no_atoms == 0 {
// 空头:'D' 00——整块无位图
return (attab, pos + 2)
}
let fb = (no_atoms >> 1) + 1
let map_pos = pos + 2
if map_pos + fb > frame.length() {
raise ControlError::ShortFrame(
"cache 位图[\{fb}] at \{map_pos} over \{frame.length()}",
)
}
// LONG_ATOMS=位图尾 nibble 的 bit0(真源 external.c:1012-1014)
let la_byte = frame[map_pos + (no_atoms >> 1)].to_int()
let la_bit = (no_atoms << 2) & 7
let long_atoms = ((la_byte >> la_bit) & 1) != 0
let mut cur = map_pos + fb // 指令段游标(顺序紧排)
let mut tix = 0
while tix < no_atoms {
if cur >= frame.length() {
raise ControlError::ShortFrame(
"cache 指令段 at \{cur} over \{frame.length()}",
)
}
let b = frame[map_pos + (tix >> 1)].to_int()
let n = if tix % 2 == 0 { (b >> 4) & 15 } else { b & 15 }
let new_atom = (n & 8) != 0
// 第一步:1B cix 低 8 位(槽号仅记账)
let _ = ((n & 7) << 8) | frame[cur].to_int()
cur = cur + 1
if new_atom {
// 新 atom:名字 len(long_atoms?u16:u8)+latin1 bytes——入 attab
let mut ln = 0
if long_atoms {
if cur + 2 > frame.length() {
raise ControlError::ShortFrame("cache 长 atom 名长 at \{cur}")
}
ln = (frame[cur].to_int() << 8) | frame[cur + 1].to_int()
cur = cur + 2
} else {
ln = frame[cur].to_int()
cur = cur + 1
}
let name = latin1_name(frame, cur, ln)
cur = cur + ln
attab.push(name)
} else {
// 旧 atom:对端 cache 槽已有名——我方无 out 表,名字不可知→诚实拒
raise ControlError::CacheSegment(
"cache 旧引用(我方无 out 表,槽名不可知)",
)
}
tix += 1
}
(attab, cur)
}
///|
/// 控制帧解码:83 起头核+['D' 段头跳]+首项 tag 分派+参数项流收集。
/// frame = 裸 ETF 流(传输面剥 u32 len 后交付——线形守恒)。
pub fn decode_control(
frame : Bytes,
etf : &EtfPort,
) -> ControlMsg raise ControlError {
if frame.length() < 2 || frame[0].to_int() != 131 {
raise ControlError::ShortFrame("起头非 VERSION_MAGIC(131)")
}
let (attab, stream_pos) = decode_cache_segment(frame, 1)
let (first, next_pos) = etf.decode_term(frame, stream_pos, attab)
// ctl 两形(dist.c 发侧 TUPLE 元组形/老线裸整数形——真 Erlang 节点发元组形
// {tag, params...},裸形=tag 整数后参数流;decode 两形兼容)
let (tag, items, ctl_end) : (Int32, Array[EtfItem], Int32) = match first {
IInt(v) => {
// 裸形:tag 整数打头,参数项流紧随(遇 VERSION_MAGIC 停——msg 段起)
let ps : Array[EtfItem] = []
let mut pos = next_pos
while pos < frame.length() && frame[pos].to_int() != 131 {
let (item, next) = etf.decode_term(frame, pos, attab)
ps.push(item)
pos = next
}
(v, ps, pos)
}
ITuple(arr) => {
// 元组形:{tag, params...}——tag=arr[0],params=arr[1..];msg=元组外续项
let v = match arr[0] {
IInt(t) => t
_ => raise ControlError::BadTag("元组形 ctl 首元须整数 tag")
}
let ps : Array[EtfItem] = []
for i in 1..
raise ControlError::BadTag(
"控制项流首项须整数 tag 或 ctl 元组",
)
}
let name = match dop_name_of(tag) {
Some(found) => found
None => raise ControlError::UnknownDop(tag)
}
let semantics = match dop_semantics_of(tag) {
Some(found) => found
// 词表封闭守恒:name 命中而 semantics 缺=词表违(镜像罐防)
None => raise ControlError::UnknownDop(tag)
}
ControlMsg::{ tag, name, semantics, items, next_pos: ctl_end, }
}
///|
/// 控制错误文本面(show 手写——零 derive 守恒)
pub fn show_control_error(e : ControlError) -> String {
match e {
ControlError::ShortFrame(m) => "ShortFrame(\{m})"
ControlError::UnknownDop(tag) => "UnknownDop(\{tag})"
ControlError::BadTag(m) => "BadTag(\{m})"
ControlError::CacheSegment(m) => "CacheSegment(\{m})"
ControlError::Codec(m) => "Codec(\{m})"
}
}