diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45c38f88..cc70af0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,5 +23,5 @@ jobs: make runtime-rs sudo make install - name: 测试 (${{ matrix.backend.name }}) - env: { KVSPACE: "${{ matrix.backend.dsn }}" } + env: { KVSPACE: "${{ matrix.backend.dsn }}", KV_CASE_TIMEOUT: "600" } run: python3 tutorial/test.py diff --git a/deps.json b/deps.json index e7ca0172..08b7f853 100644 --- a/deps.json +++ b/deps.json @@ -1,7 +1,7 @@ { - "kvspace": "v0.2.18", - "kvspace-c": "v0.2.18", - "kvspace-durable": "v0.2.18", + "kvspace": "v0.3.0", + "kvspace-c": "v0.3.0", + "kvspace-durable": "v0.3.0", "blockmalloc": "v0.1.4", "slotsboxmalloc": "v0.1.5" } diff --git a/layout/src/builtin.rs b/layout/src/builtin.rs index ec2dc68a..519b26c6 100644 --- a/layout/src/builtin.rs +++ b/layout/src/builtin.rs @@ -152,7 +152,7 @@ pub fn num_op(opcode: &str) -> bool { // ── 字面量解析 ─────────────────────────────────────────────────────── -/// 将数字字面量解析为 int64/uint64/float64 的 TLV 字节。 +/// Encode a numeric literal. pub fn try_parse_number(s: &str) -> Option> { if s.is_empty() { return None; diff --git a/layout/src/capi.rs b/layout/src/capi.rs index 9632ba0b..0c4c9fe0 100644 --- a/layout/src/capi.rs +++ b/layout/src/capi.rs @@ -231,7 +231,7 @@ pub struct kvlangKindexpr { pub ndim: i32, // 维数(0=标量) pub dims: [i32; 8], // 各维大小(前 ndim 项有效) pub array_len: i32, // 元素总数(标量=1,多维=各维乘积) - pub kind: [u8; 64], // base kind,NUL 终止(如 "float64"、"char/utf8"、"rwir|rwfunc") + pub kind: [u8; 64], // NUL-terminated type. } /// 解析 XValue head 的 langtype 内容(NUL 终止串,含 */@ 前缀与 [dims])。 diff --git a/layout/src/code.rs b/layout/src/code.rs index 9ec53372..f8c87032 100644 --- a/layout/src/code.rs +++ b/layout/src/code.rs @@ -11,13 +11,13 @@ //! printlib: 反向——严格读 /lib/ 子树重建 AST(签名读参数定义键 .[0,±k]、体读线性槽+‥labels), //! 不读 .src、不依赖签名行 [0,x] 静态槽。printstack: /vthread/ 活动栈渲染(只读)。 -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use super::ast::{ self, Expr, Func, FuncSig, Instruction, Param, RwirDecl, ScopeStmt, Stmt, StructDecl, }; use super::ffi::Kv; -use super::{builtin, ffi, keytree, kvkind, langtype, lower, parser}; +use super::{builtin, ffi, keytree, kvkind, lower, parser}; /// 创建基础目录 /lib/ 与 /vthread/(layout 前必须存在)。 pub fn init_dirs(kv: &mut Kv) -> Result<(), String> { @@ -45,6 +45,22 @@ pub fn compile(kv: &mut Kv, src: &str) -> Result, String> { let mut any_code = false; let mut inits: Vec = Vec::new(); + let local_funcs: HashSet = file + .funcs + .iter() + .map(|f| { + let pkg = if f.pkg.is_empty() { + &file.package + } else { + &f.pkg + }; + if pkg.is_empty() { + f.sig.name.clone() + } else { + format!("{pkg}{}{name}", keytree::MEMBER_SEP, name = f.sig.name) + } + }) + .collect(); for decl in &file.structs { write_struct_decl(kv, decl); any_code = true; @@ -56,7 +72,7 @@ pub fn compile(kv: &mut Kv, src: &str) -> Result, String> { func.pkg.clone() }; let mut lowered = lower::lower_func(func); - write_func(kv, &pkg, &mut lowered); + write_func(kv, &pkg, &mut lowered, &local_funcs); any_code = true; if func.sig.name == "init" { inits.push(init_fn_name(&pkg)); @@ -82,7 +98,7 @@ pub fn compile(kv: &mut Kv, src: &str) -> Result, String> { pkg: String::new(), }; let mut lowered = lower::lower_func(&init_fn); - write_func(kv, "", &mut lowered); + write_func(kv, "", &mut lowered, &local_funcs); any_code = true; inits.push(init_fn_name("")); } @@ -520,7 +536,7 @@ fn read_insts(kv: &mut Kv, dir: &str) -> Vec { /// 槽值 → Operand:rwir 族为引用/opcode 名,char 为字符串字面量,其余为明文字面量。 fn decode_operand(data: &[u8]) -> Operand { let k = kvkind::kind(data); - if matches!(k.as_str(), "rwir" | "rwir|rwfunc" | "rwfunc" | "def rwir") { + if matches!(k.as_str(), "rwir" | "rwfunc" | "def rwir") { Operand::Ref(kvkind::rwir_sig(data)) } else if kvkind::is_char_kind(&k) { Operand::Str(kvkind::plain(data)) @@ -618,8 +634,8 @@ fn build_inst(inst: &RawInst, by_irseq: &HashMap) -> Instruction { } /// 写函数到 /lib/:签名(rwfunc)、源码、参数 Ptr、指令体。 -pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { - let mut type_map = lower::infer_types(fn_); +pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func, local_funcs: &HashSet) { + let type_map = lower::infer_types(fn_); lower::specialize(fn_, &type_map); let func_dir = keytree::lib_func(pkg, &fn_.sig.name); @@ -627,24 +643,12 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { let mut labels: HashMap = HashMap::new(); collect_insts(&fn_.body, &mut seq, &mut labels); - // 形参引用 → 帧坐标(编译期替换,运行时无命名参数角色)。带 `*` 的形参按**地址传递**(槽存实参地址,体内显式解引用 `*[0,-k]`); - // 不带 `*` 的按**值传递**(槽存值本体,体内裸坐标 `[0,-k]` 直读)。见 spec [[函数]]。 let mut param_coord: HashMap = HashMap::new(); for (i, p) in fn_.sig.params.iter().enumerate() { - let d = if langtype::is_addr_param(&p.ty) { - "*" - } else { - "" - }; - param_coord.insert(p.name.clone(), format!("{d}[0,-{}]", i + 1)); + param_coord.insert(p.name.clone(), format!("*[0,-{}]", i + 1)); } for (i, r) in fn_.sig.returns.iter().enumerate() { - let d = if langtype::is_addr_param(&r.ty) { - "*" - } else { - "" - }; - param_coord.insert(r.name.clone(), format!("{d}[0,{}]", i + 1)); + param_coord.insert(r.name.clone(), format!("*[0,{}]", i + 1)); } // 按函数覆盖(文件夹复制式合并):只 del_tree 本函数子树,不动 /lib 下其它函数。 @@ -666,9 +670,10 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { } let mut pairs: Vec<(String, Vec)> = Vec::new(); + pairs.push((format!("{func_dir}/"), kvkind::new_rwfunc_dir())); pairs.push(( format!("{func_dir}/[0,0]"), - kvkind::new_rwfunc(seq.len() as i32, nr, nw, fn_.sig.dynamic()), + kvkind::new_rwfunc_anchor(nr, nw, fn_.sig.dynamic()), )); pairs.push(( keytree::lib_src(pkg, &fn_.sig.name), @@ -698,9 +703,11 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { (i as i32) + 1, inst, &labels, - &mut type_map, + &type_map, ¶m_coord, ¶m_langtype, + pkg, + local_funcs, ); } if !labels.is_empty() { @@ -718,8 +725,7 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { } } -/// 写 struct 原型到 /lib/:基节点(kind=struct) + memindex(/lib/Name·) + 各字段默认值。 -/// 运行时 struct·new 以此为原型 cplist 克隆,故字段默认值即实例初值。 +/// Store field types under the prototype and defaults under member keys. pub fn write_struct_decl(kv: &mut Kv, decl: &StructDecl) { let mut name = decl.name.clone(); if !decl.pkg.is_empty() { @@ -727,16 +733,13 @@ pub fn write_struct_decl(kv: &mut Kv, decl: &StructDecl) { } let base = keytree::rwir(&name); let _ = kv.del_tree(&base); - let fnames: Vec = decl.fields.iter().map(|f| f.name.clone()).collect(); - let ftypes: Vec<(String, String)> = decl - .fields - .iter() - .map(|f| (f.name.clone(), f.ty.clone())) - .collect(); let mut pairs: Vec<(String, Vec)> = Vec::new(); - pairs.push((base.clone(), kvkind::new_struct(&ftypes))); - pairs.push((keytree::member(&base, ""), kvkind::new_memindex(&fnames))); + pairs.push((base.clone(), kvkind::new_struct())); for fld in &decl.fields { + pairs.push(( + format!("{base}/{}", fld.name), + kvkind::new_def_langtype(&fld.ty), + )); // *T 字段的默认值是空指针 None——不落键。None 在 kvspace 即"无值"(键不存在), // 故原型不带该字段、实例读回 None(见 [[ptr]]);不造"空 Ptr"这第二种空值表示。 if fld.ty.starts_with('*') { @@ -844,15 +847,12 @@ fn write_linear_inst( n: i32, s: &Instruction, labels: &HashMap, - type_map: &mut HashMap, + type_map: &HashMap, params: &HashMap, param_langtype: &HashMap, + pkg: &str, + local_funcs: &HashSet, ) { - for (j, w) in s.writes.iter().enumerate() { - if j < s.write_types.len() && !s.write_types[j].is_empty() { - type_map.insert(w.clone(), s.write_types[j].clone()); - } - } let (opcode, mut reads) = s.flat(); match opcode.as_str() { "goto" => { @@ -881,16 +881,31 @@ fn write_linear_inst( let mut pairs: Vec<(String, Vec)> = Vec::with_capacity(1 + reads.len() + s.writes.len()); if !opcode.is_empty() { - pairs.push((format!("{prefix}/[{n},0]"), opcode_value(&opcode))); + pairs.push(( + format!("{prefix}/[{n},0]"), + opcode_value( + kv, + &opcode, + reads.len() as i32, + s.writes.len() as i32, + pkg, + local_funcs, + ), + )); } for (j, r) in reads.iter().enumerate() { let rv = params .get(r.as_str()) .map(String::as_str) .unwrap_or(r.as_str()); + let ty = param_langtype + .get(r) + .or_else(|| type_map.get(r)) + .map(String::as_str) + .unwrap_or(""); pairs.push(( format!("{prefix}/[{n},-{}]", j + 1), - slot_value(rv, target_char), + typed_slot_value(rv, ty, target_char), )); } for (j, w) in s.writes.iter().enumerate() { @@ -902,10 +917,11 @@ fn write_linear_inst( .map(String::as_str) .filter(|t| !t.is_empty()) .or_else(|| param_langtype.get(orig).map(String::as_str)) + .or_else(|| type_map.get(orig).map(String::as_str)) .unwrap_or(""); pairs.push(( format!("{prefix}/[{n},{}]", j + 1), - write_slot_value(wv, ty), + typed_slot_value(wv, ty, ""), )); } if !pairs.is_empty() { @@ -928,28 +944,42 @@ fn resolve_label(labels: &HashMap, name: &str, opcode: &str) -> i32 } } -/// 调用目标 opcode 槽值:函数调用/运算符 → `rwir|rwfunc` 并列; -/// 控制/拷贝 opcode(return/goto/br/call/=)原样 `rwir`(非调用目标)。 -fn opcode_value(opcode: &str) -> Vec { - if matches!(opcode, "return" | "goto" | "br" | "call" | "=") { - kvkind::new_rwir(0, 0, opcode) +fn opcode_value( + kv: &mut Kv, + opcode: &str, + nr: i32, + nw: i32, + pkg: &str, + local_funcs: &HashSet, +) -> Vec { + let name = opcode.strip_prefix("/lib/").unwrap_or(opcode); + let qualified = if pkg.is_empty() || name.contains(keytree::MEMBER_SEP) || name.contains('/') { + name.to_string() + } else { + format!("{pkg}{}{name}", keytree::MEMBER_SEP) + }; + let is_func = local_funcs.contains(name) + || local_funcs.contains(&qualified) + || [name, qualified.as_str()].iter().any(|candidate| { + let dir = format!("/lib/{candidate}/"); + kvkind::kind(&kv.get_one(&dir)) == kvkind::KIND_RWFUNC + }); + if is_func { + kvkind::new_rwfunc_call(nr, nw, opcode) } else { - kvkind::new_rwir_union(opcode) + kvkind::new_rwir(nr, nw, opcode) } } -/// 写槽值:写目标带 **map langtype** 标注时(`x:{keylt}·{valt} = {}`),槽的 langtype 即该 map -/// langtype、body 即变量名——runtime 据此把容器值按 [[map容器]] 落成「langtype=map langtype、 -/// storetype=index、body 空」,成员索引落兄弟槽 `x·`。其余写槽仍是 rwir 引用(body 带计数头)。 -/// 参数替换过的写槽(rwfunc 写参轴)不承载类型:声明类型已落 `/lib/.[0,k]` 参数定义键。 -fn write_slot_value(name: &str, ty: &str) -> Vec { - if ty.contains(keytree::MEMBER_SEP) { - return ffi::tlv_encode(ty, name.as_bytes(), 1); +/// Preserve the target type. +fn typed_slot_value(name: &str, ty: &str, target_char: &str) -> Vec { + if !ty.is_empty() && (name.starts_with('/') || !is_literal(name)) { + return kvkind::new_typed_rwir(name, ty); } - slot_value(name, "") + slot_value(name, target_char) } -/// 将字面量/引用字符串编码为 XValue TLV(rwir 槽值)。 +/// Encode an instruction operand. fn slot_value(val: &str, target_char: &str) -> Vec { if !is_literal(val) { return kvkind::new_rwir(0, 0, val); @@ -1111,11 +1141,26 @@ pub fn printstack(kv: &mut Kv, vid: &str) -> String { if !(is_arg || is_local) { continue; } - let v = kvkind::display(&kv.get_one(&format!("{f}/{n}"))); + let mut v = kv.get_one(&format!("{f}/{n}")); + if is_arg { + for _ in 0..2 { + if !kvkind::is_ptr(&v) { + break; + } + v = kv.get_one(&kvkind::ptr_target(&v)); + } + } + let v = kvkind::display(&v); out.push_str(&format!(" {n} = {}\n", clip(v))); } if cur.as_deref() == Some(f.as_str()) && !pc.is_empty() { - let v = kvkind::display(&kv.get_one(&pc)); + let opcode = kv.get_one(&pc); + let kind = kvkind::kind(&opcode); + let v = if kind == "rwir" || kind == "rwfunc" { + format!("{kind}:{}", kvkind::rwir_sig(&opcode)) + } else { + kvkind::display(&opcode) + }; out.push_str(&format!(" cur = {}\n", clip(v))); } } @@ -1161,7 +1206,11 @@ mod tests { "lib a {\nlib b {\nrwfunc f() -> (r:int64) {\n1 -> r\n}\n}\n}\n", ) .unwrap(); + let dir = kv.get_one("/lib/a/b·f/"); + assert_eq!(kvkind::kind(&dir), "rwfunc"); + assert_eq!(kvkind::head(&dir).storetype, 0); assert_eq!(kvkind::kind(&kv.get_one("/lib/a/b·f/[0,0]")), "rwfunc"); + assert_eq!(kvkind::head(&kv.get_one("/lib/a/b·f/[0,0]")).storetype, 1); // 同 lib a 下再 layout 另一嵌套 lib c,验证 b·f 未被整库删除(增量合并) compile( @@ -1177,6 +1226,64 @@ mod tests { assert_eq!(kvkind::kind(&kv.get_one("/lib/a/c·g/[0,0]")), "rwfunc"); } + #[test] + fn call_slots_use_target_type() { + let mut kv = Kv::conn(&format!( + "fs:///tmp/kvlanglayout_call_kind_{}", + std::process::id() + )); + init_dirs(&mut kv).unwrap(); + compile(&mut kv, "rwfunc caller() -> () {\ncallee(1) -> y\nprintln(y)\n}\nrwfunc callee(x:int64) -> (r:int64) {\nx -> r\n}\n").unwrap(); + assert_eq!(kvkind::kind(&kv.get_one("/lib/caller/[1,0]")), "rwfunc"); + assert_eq!( + kvkind::counts(&kv.get_one("/lib/caller/[1,0]")), + (1, 1, false) + ); + assert_eq!(kvkind::kind(&kv.get_one("/lib/caller/[2,0]")), "rwir"); + assert_eq!( + kvkind::counts(&kv.get_one("/lib/caller/[2,0]")), + (1, 0, false) + ); + compile(&mut kv, "rwfunc later() -> () {\ncallee(1) -> y\n}\n").unwrap(); + assert_eq!(kvkind::kind(&kv.get_one("/lib/later/[1,0]")), "rwfunc"); + compile( + &mut kv, + "rwfunc absolute() -> () {\n/lib/callee(1) -> y\n}\n", + ) + .unwrap(); + assert_eq!(kvkind::kind(&kv.get_one("/lib/absolute/[1,0]")), "rwfunc"); + compile( + &mut kv, + "lib p {\nrwfunc caller() -> () {\ntarget()\n}\nrwfunc target() -> () {}\n}\n", + ) + .unwrap(); + assert_eq!(kvkind::kind(&kv.get_one("/lib/p·caller/[1,0]")), "rwfunc"); + } + + #[test] + fn struct_definition_uses_child_keys() { + let mut kv = Kv::conn(&format!( + "fs:///tmp/kvlanglayout_struct_{}", + std::process::id() + )); + init_dirs(&mut kv).unwrap(); + compile( + &mut kv, + "struct Point {\n\tx:int64=3\n\tnext:*Point=None\n}\n", + ) + .unwrap(); + let proto = kv.get_one("/lib/Point"); + assert_eq!(kvkind::kind(&proto), "def struct"); + assert!(kvkind::body(&proto, &kvkind::head(&proto)).is_empty()); + assert_eq!(kvkind::value_string(&kv.get_one("/lib/Point/x")), "int64"); + assert_eq!( + kvkind::value_string(&kv.get_one("/lib/Point/next")), + "*/lib/Point" + ); + assert_eq!(kvkind::kind(&kv.get_one("/lib/Point·x")), "int64"); + assert!(kv.get_one("/lib/Point·next").is_empty()); + } + #[test] fn dot_is_ordinary_char_contiguous_coords() { // '.' 是普通字符(小数点/后缀等),foo.bar 视作单一 opcode,不再被切成 foo bar 两 token diff --git a/layout/src/ffi.rs b/layout/src/ffi.rs index 40e4b211..5e796351 100644 --- a/layout/src/ffi.rs +++ b/layout/src/ffi.rs @@ -1,7 +1,4 @@ -//! kvspace-durable 的 C ABI 绑定 + 安全封装。 -//! -//! 布局侧不依赖 kvspace-durable 的 Rust 类型,只通过 `extern "C"` 符号表调用。 -//! 所有 XValue 以 TLV 字节(`Vec`)跨边界;空字节 = None。 +//! KVSpace C ABI bindings for layout. use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_int, c_void}; @@ -27,17 +24,20 @@ extern "C" { out: *mut *mut u8, out_len: *mut u32, ) -> c_int; - /// 新位置写:按 (ref, storetype, ro, vid, langtype, body_len) 分配新 box、写 head,返回 body 偏移指针。 - fn kvspaceWriteNewPlace( + fn kvspaceSetValue( h: Handle, key: *const c_char, - r#ref: u8, - storetype: u8, + value: *const u8, + value_len: u32, ro: u8, vid: u32, - langtype: *const c_char, - body_len: u32, - body: *mut *mut u8, + err: *mut c_char, + err_cap: u32, + ) -> c_int; + fn kvspaceDel( + h: Handle, + keys: *const *const c_char, + nkeys: u32, err: *mut c_char, err_cap: u32, ) -> c_int; @@ -99,6 +99,7 @@ pub struct kvspaceHead_t { pub langtype: [u8; 256], pub langtype_len: i32, pub body_offset: i32, + pub body_cap: u64, } // ── 内部助手 ───────────────────────────────────────────────────────── @@ -155,47 +156,32 @@ impl Kv { Kv { h } } - /// 写:pairs 的值为预编码 TLV;逐条解 head 取 (langtype, body),经 WriteNewPlace - /// 向 kvspace 要 body 偏移指针后直接写入 body 字节(新建/换 kind/换尺寸唯一原语)。 pub fn set(&mut self, pairs: &[(String, Vec)]) -> Result<(), String> { for (key, tlv) in pairs { - self.write_new_place(key, tlv)?; - } - Ok(()) - } - - fn write_new_place(&mut self, key: &str, tlv: &[u8]) -> Result<(), String> { - let h = decode_head(tlv); - let llen = h.langtype.iter().position(|&b| b == 0).unwrap_or(0); - let langtype = CString::new(&h.langtype[..llen]).expect("no NUL in langtype"); - let ck = CString::new(key).expect("no NUL in key"); - let body_off = h.body_offset as usize; - let body_len = h.body_len.max(0) as usize; - let mut body: *mut u8 = std::ptr::null_mut(); - let mut err: [c_char; 256] = [0; 256]; - let ret = unsafe { - kvspaceWriteNewPlace( - self.h, - ck.as_ptr(), - h.r#ref, - h.storetype, - h.ro, - h.vid, - langtype.as_ptr(), - body_len as u32, - &mut body, - err.as_mut_ptr(), - err.len() as u32, - ) - }; - err_ret(&mut err, ret)?; - if body_len > 0 { - if body.is_null() { - return Err(format!("kvspace: WriteNewPlace null body at {key}")); - } - unsafe { - std::ptr::copy_nonoverlapping(tlv[body_off..].as_ptr(), body, body_len); - } + let ck = CString::new(key.as_str()).expect("no NUL in key"); + let mut err: [c_char; 256] = [0; 256]; + let mut head: kvspaceHead_t = unsafe { std::mem::zeroed() }; + let is_none = tlv.is_empty() + || unsafe { kvspaceDecodeHead(tlv.as_ptr(), tlv.len() as u32, &mut head) == 0 } + && head.langtype_len == 0; + let ret = if is_none { + let key_ptr = ck.as_ptr(); + unsafe { kvspaceDel(self.h, &key_ptr, 1, err.as_mut_ptr(), err.len() as u32) } + } else { + unsafe { + kvspaceSetValue( + self.h, + ck.as_ptr(), + tlv.as_ptr(), + tlv.len() as u32, + 0, + 0, + err.as_mut_ptr(), + err.len() as u32, + ) + } + }; + err_ret(&mut err, ret)?; } Ok(()) } @@ -267,7 +253,7 @@ impl Drop for Kv { } } -// ── XValue TLV 编解码(供 kvkind 使用) ───────────────────────────── +// XValue codec. /// array_len → dims:char/* 恒一维(含空串/单字符);其余标量(≤1)=0 维、多元素=1 维。 fn al_to_dims(kind: &str, array_len: i32) -> Vec { @@ -280,7 +266,7 @@ fn al_to_dims(kind: &str, array_len: i32) -> Vec { } } -/// 通用 TLV 编码(内联,ref=0)。 +/// Encode an inline XValue. pub fn tlv_encode(kind: &str, raw: &[u8], array_len: i32) -> Vec { let ck = CString::new(kind).expect("no NUL in kind"); let dims = al_to_dims(kind, array_len); @@ -311,6 +297,7 @@ pub fn decode_head(data: &[u8]) -> kvspaceHead_t { langtype: [0u8; 256], langtype_len: 0, body_offset: 0, + body_cap: 0, }; unsafe { kvspaceDecodeHead(data.as_ptr(), data.len() as u32, &mut h); diff --git a/layout/src/kvkind.rs b/layout/src/kvkind.rs index c8aff94e..1629b0c1 100644 --- a/layout/src/kvkind.rs +++ b/layout/src/kvkind.rs @@ -1,23 +1,17 @@ -//! kvlang 自有 kind(rwir/rwfunc/scope)+ XValue TLV 字节访问器。 -//! -//! 标准 kind(char/utf8、int64、bool、index…)由 kvspace-durable 编解码; -//! rwir/rwfunc/scope 是 kvlang 自己的 kind,body 格式由本模块定义, -//! head 复用 kvspace-durable 暴露的 TLV head(见 [`super::ffi`])。 +//! kvlang code values over the shared headlenpow XValue codec. use super::ffi; // ── kind 常量 ───────────────────────────────────────────────────────── pub const KIND_CHAR: &str = "char/utf32"; -pub const KIND_INDEX: &str = "index"; -pub const KIND_STRUCT: &str = "struct"; +pub const KIND_DEF_STRUCT: &str = "def struct"; // kvlang 自有 kind pub const KIND_RWIR: &str = "rwir"; pub const KIND_RWFUNC: &str = "rwfunc"; pub const KIND_DEF_RWIR: &str = "def rwir"; pub const KIND_DEF_LANGTYPE: &str = "def langtype"; -pub const KIND_RWIR_OR_RWFUNC: &str = "rwir|rwfunc"; // ── 通用 XValue 字节访问器 ─────────────────────────────────────────── @@ -123,7 +117,7 @@ pub fn value_string(data: &[u8]) -> String { String::from_utf8_lossy(body(data, &h)).into_owned() } -/// 把 XValue TLV 解码成可读文本,形如 "kind:value"(指针 "→target:kind"、空 "None")。 +/// Format an XValue for inspection. /// 对齐 kvspace CLI 的 format_value/plain,供 dump 审查 lower 后的 /lib。 pub fn display(data: &[u8]) -> String { if data.is_empty() { @@ -161,13 +155,6 @@ fn fmt_float(v: f64) -> String { format!("{s}.0") } } -fn count_names(b: &[u8]) -> usize { - if b.len() < 4 { - 0 - } else { - le_u32(b) as usize - } -} fn arr(b: &[u8]) -> [u8; N] { let mut a = [0u8; N]; let n = b.len().min(N); @@ -193,9 +180,8 @@ fn plain_value(k: &str, b: &[u8]) -> String { .chunks(4) .map(|c| char::from_u32(le_u32(c)).unwrap_or('\u{FFFD}')) .collect(), - "index" => format!("({})", count_names(b)), - // kvlang 自有 kind:body = [2B nr][2B nw][1B dynamic][sig];槽值/调用目标 nr=nw=0,取 sig 即可。 - "rwir" | "rwir|rwfunc" | "rwfunc" | "def rwir" => { + // Code bodies store counts before the name. + "rwir" | "rwfunc" | "def rwir" => { let (nr, nw, dynamic) = if b.len() >= 5 { ( u16::from_le_bytes([b[0], b[1]]), @@ -232,29 +218,32 @@ pub fn is_char_kind(k: &str) -> bool { /// 计数头 [nr:u16 LE][nw:u16 LE][dynamic:u8](定义体,无参数载荷)。 fn counts_body(nr: i32, nw: i32, dynamic: bool) -> Vec { let mut raw = Vec::with_capacity(5); - raw.extend_from_slice(&(nr as u16).to_le_bytes()); - raw.extend_from_slice(&(nw as u16).to_le_bytes()); + let nr = u16::try_from(nr).expect("read count exceeds u16"); + let nw = u16::try_from(nw).expect("write count exceeds u16"); + raw.extend_from_slice(&nr.to_le_bytes()); + raw.extend_from_slice(&nw.to_le_bytes()); raw.push(dynamic as u8); raw } -/// 指令槽值:计数头 + 单个引用串载荷(opcode/操作数名,每坐标一个值)。 -fn rwir_slot_body(sig: &str) -> Vec { - let mut raw = counts_body(0, 0, false); +pub fn new_rwir(nr: i32, nw: i32, sig: &str) -> Vec { + let mut raw = counts_body(nr, nw, false); raw.extend_from_slice(sig.as_bytes()); - raw + ffi::tlv_encode(KIND_RWIR, &raw, 1) } -pub fn new_rwir(nr: i32, nw: i32, sig: &str) -> Vec { +pub fn new_rwfunc_call(nr: i32, nw: i32, sig: &str) -> Vec { let mut raw = counts_body(nr, nw, false); raw.extend_from_slice(sig.as_bytes()); - ffi::tlv_encode(KIND_RWIR, &raw, 1) + ffi::tlv_encode(KIND_RWFUNC, &raw, 1) } -/// 调用目标(看起来像函数调用的 opcode)→ langtype `rwir|rwfunc` 并列。 -/// 静态无法判定是扩展 rwir 还是用户 rwfunc,交 runtime 查 /lib/ 的 XValue kind 分派。 -pub fn new_rwir_union(sig: &str) -> Vec { - ffi::tlv_encode(KIND_RWIR_OR_RWFUNC, &rwir_slot_body(sig), 1) +pub fn new_typed_rwir(name: &str, langtype: &str) -> Vec { + let mut raw = counts_body(0, 0, false); + raw.extend_from_slice(name.as_bytes()); + raw.push(0); + raw.extend_from_slice(langtype.as_bytes()); + ffi::tlv_encode(KIND_RWIR, &raw, 1) } /// def rwir 路由头:仅计数头,无参数载荷。各参数落 [0,x] 签名行槽(def langtype)。 @@ -288,33 +277,18 @@ pub fn def_param_parts(data: &[u8]) -> Option<(String, String)> { Some((name, ty)) } -// ── struct 原型(对齐 runtime kvlangBuiltinMemindex)───────────────── -// -// /lib/Name kind=struct,body="name:langtype\n..."(字段声明类型,供实例化类型校验) -// /lib/Name· kind=index,body=[4B count LE][name\n...](字段名唯一权威) - -pub fn new_struct(fields: &[(String, String)]) -> Vec { - let body = fields - .iter() - .map(|(n, t)| format!("{n}:{t}")) - .collect::>() - .join("\n"); - ffi::tlv_encode(KIND_STRUCT, body.as_bytes(), 1) -} - -pub fn new_memindex(names: &[String]) -> Vec { - let mut raw = (names.len() as u32).to_le_bytes().to_vec(); - raw.extend_from_slice(names.join("\n").as_bytes()); - ffi::tlv_encode(KIND_INDEX, &raw, 1) +pub fn new_struct() -> Vec { + ffi::tlv_encode(KIND_DEF_STRUCT, &[], 1) } // ── kvlang 自有 kind:rwfunc ──────────────────────────────────────── // -// body = 计数头 [2B nr LE][2B nw LE][1B dynamic],array_len=num_insts。 -// 各参数类型落签名行 [0,x] 槽(def langtype),不入 body(见上「铁律」)。 +pub fn new_rwfunc_dir() -> Vec { + ffi::tlv_encode(KIND_RWFUNC, &[], 1) +} -pub fn new_rwfunc(num_insts: i32, nr: i32, nw: i32, dynamic: bool) -> Vec { - ffi::tlv_encode(KIND_RWFUNC, &counts_body(nr, nw, dynamic), num_insts) +pub fn new_rwfunc_anchor(nr: i32, nw: i32, dynamic: bool) -> Vec { + ffi::tlv_encode(KIND_RWFUNC, &counts_body(nr, nw, dynamic), 1) } /// rwfunc body 访问器(layout 读回签名时用)。 @@ -368,9 +342,14 @@ pub fn write_slot_name(data: &[u8]) -> (String, String) { if data.is_empty() { return (String::new(), String::new()); } - let k = kind(data); - if k.contains(super::keytree::MEMBER_SEP) { - return (value_string(data), k); + let h = ffi::decode_head(data); + let b = body(data, &h); + if b.len() > 5 { + if let Some(split) = b[5..].iter().position(|&x| x == 0) { + let name = String::from_utf8_lossy(&b[5..5 + split]).into_owned(); + let ty = String::from_utf8_lossy(&b[6 + split..]).into_owned(); + return (name, ty); + } } (rwir_sig(data), String::new()) } @@ -381,5 +360,7 @@ pub fn rwir_sig(data: &[u8]) -> String { } let h = ffi::decode_head(data); let b = body(data, &h); - String::from_utf8_lossy(&b[5.min(b.len())..]).into_owned() + let text = &b[5.min(b.len())..]; + let end = text.iter().position(|&x| x == 0).unwrap_or(text.len()); + String::from_utf8_lossy(&text[..end]).into_owned() } diff --git a/layout/src/langtype.rs b/layout/src/langtype.rs index 8147d7eb..85c07610 100644 --- a/layout/src/langtype.rs +++ b/layout/src/langtype.rs @@ -39,8 +39,6 @@ fn known_kind(k: &str) -> bool { | "char/utf8" | "char/ascii" | "stringkeymap" - | "index" - | "extindex" | "rwir" | "rwfunc" | "scope" @@ -185,14 +183,6 @@ fn valid_atom(s: &str) -> bool { valid_shape(s) } -/// 形参是否按**地址传递**:声明带 `*`(指针)或 `@`(扩展句柄)前缀。两者都不是可拷的值本体 -/// (`*` 指别处的 key、`@` 指 kvspace 之外的位置),故槽里放间接值、体内解引用;不带前缀则按值传 -/// (槽存值本体)。见 spec [[函数]]。 -pub fn is_addr_param(ty: &str) -> bool { - let t = ty.trim_start(); - t.starts_with('*') || t.starts_with('@') -} - /// 类型表达式语法校验(装载期)。变参 `...` 是签名层 arity、不入 langtype 串,此处永不见。 pub fn valid_langtype(expr: &str) -> bool { !expr.is_empty() && expr.split('|').all(valid_atom) @@ -351,7 +341,6 @@ mod tests { "char/utf32", "char/ascii", "stringkeymap", - "index", "[]float32", "[2]float32", "[2,3]float32", @@ -368,7 +357,7 @@ mod tests { "[2,3]float32|float32", "[]float32|[]float64", "bool|char/utf8", - "index|stringkeymap", + "int64|stringkeymap", "[]char/utf8·int64", "[]char/utf32·[]char/utf8", "[]char/utf8·[]char/utf8·int64", @@ -494,7 +483,7 @@ mod tests { ("[2,3]float32|float32", "float64", 0, &[], false), ("[]float32|[]float64", "float64", 1, &[10], true), ("bool|char/utf8", "char/utf8", 0, &[], true), - ("index|stringkeymap", "index", 0, &[], true), + ("int64|stringkeymap", "int64", 0, &[], true), ]; for (expr, kind, ndim, dims, want) in cases { let got = match_langtype(expr, kind, ndim, dims); diff --git a/layout/src/lower.rs b/layout/src/lower.rs index e77612e1..b72bf0dd 100644 --- a/layout/src/lower.rs +++ b/layout/src/lower.rs @@ -4,7 +4,7 @@ use std::collections::{HashMap, HashSet}; use super::ast::{self, Expr, Func, Instruction, LitKind, Stmt}; use super::scanner::{Diagnostic, Pos}; -use super::{builtin, keytree, langtype, symbol}; +use super::{builtin, keytree, symbol}; /// 容器类型(stringkeymap / mapexpr)不可用 `[]` 下标访问成员—— /// `[]` 仅限 compact array(shaped langtype,含字符串 `[]char/*`)。 @@ -195,14 +195,11 @@ fn check_map_inst(s: &Instruction, defined: &HashSet, diags: &mut Vec Vec { let tm = infer_types(fn_); - // 地址传参/返回的 `*` 是**传递方式**而非指针值:layout 体内已把它解引用(名字指称实参本体), - // 故 `p[i]`/`p·x` 是对实操本体的下标/成员,不是「指针当数组」。 let addr: Vec<&str> = fn_ .sig .params .iter() .chain(fn_.sig.returns.iter()) - .filter(|p| langtype::is_addr_param(&p.ty)) .map(|p| p.name.as_str()) .collect(); let mut diags = Vec::new(); diff --git a/layout/src/parser.rs b/layout/src/parser.rs index 7b34bb19..1aaa226f 100644 --- a/layout/src/parser.rs +++ b/layout/src/parser.rs @@ -6,7 +6,6 @@ use super::ast::{ self, Expr, Field, Func, FuncSig, Instruction, Param, RwirDecl, Stmt, StructDecl, }; use super::keytree; -use super::langtype; use super::scanner::{scan, Diagnostic, Kind, Pos, Token}; use super::symbol; @@ -137,7 +136,7 @@ impl Parser { fn parse_file(&mut self) -> ast::File { let mut f = ast::File::default(); loop { - let mut comments = self.collect_leading_comments(); + let comments = self.collect_leading_comments(); if self.peek().kind == Kind::EOF { break; } @@ -338,7 +337,6 @@ impl Parser { .params .iter() .chain(sig.returns.iter()) - .filter(|p| langtype::is_addr_param(&p.ty)) .map(|p| p.name.clone()) .collect(); self.skip_newlines_and_comments(); @@ -744,13 +742,9 @@ impl Parser { } fn check_read_only_params(&mut self, func: &Func) { - // 只读只对**地址读参**(声明带 `*`/`@`)生效:它的槽是调用方对象的地址,写它就是写 - // 调用方的对象。**值读参**(不带前缀)的槽是自己的副本,体内可自由读写,不进本检查。 let mut ro = std::collections::HashSet::new(); for p in func.sig.params.iter() { - if super::langtype::is_addr_param(&p.ty) { - ro.insert(p.name.clone()); - } + ro.insert(p.name.clone()); } if ro.is_empty() { return; diff --git a/layout/tests/pipeline_test.rs b/layout/tests/pipeline_test.rs index 87502f22..35b47c3d 100644 --- a/layout/tests/pipeline_test.rs +++ b/layout/tests/pipeline_test.rs @@ -26,7 +26,7 @@ fn compile_simple_func() { // 函数签名 let sig_val = kv.get_one("/lib/sum/[0,0]"); assert_eq!(kvkind::kind(&sig_val), "rwfunc"); - assert_eq!(kvkind::array_len(&sig_val), 2); // add + return + assert_eq!(body(&sig_val).len(), 5); let b = body(&sig_val); assert_eq!(kvkind::rwfunc_num_reads(b), 2); assert_eq!(kvkind::rwfunc_num_writes(b), 1); @@ -41,21 +41,13 @@ fn compile_simple_func() { .collect(); assert_eq!(types, vec!["int64", "int64", "int64"]); - // 参数 Ptr - let a = kv.get_one("/lib/sum/A"); - assert!(kvkind::is_ptr(&a)); - assert_eq!(kvkind::ptr_target(&a), "[0,-1]"); - let c = kv.get_one("/lib/sum/C"); - assert!(kvkind::is_ptr(&c)); - assert_eq!(kvkind::ptr_target(&c), "[0,1]"); - // 指令(特化后 opcode = int64·add) let op = kv.get_one("/lib/sum/[1,0]"); - assert_eq!(kvkind::kind(&op), "rwir|rwfunc"); + assert_eq!(kvkind::kind(&op), "rwir"); assert_eq!(sig(&op), "int64·add"); - assert_eq!(sig(&kv.get_one("/lib/sum/[1,-1]")), "A"); - assert_eq!(sig(&kv.get_one("/lib/sum/[1,-2]")), "B"); - assert_eq!(sig(&kv.get_one("/lib/sum/[1,1]")), "C"); + assert_eq!(sig(&kv.get_one("/lib/sum/[1,-1]")), "*[0,-1]\0int64"); + assert_eq!(sig(&kv.get_one("/lib/sum/[1,-2]")), "*[0,-2]\0int64"); + assert_eq!(sig(&kv.get_one("/lib/sum/[1,1]")), "*[0,1]\0int64"); assert_eq!(sig(&kv.get_one("/lib/sum/[2,0]")), "return"); // 源码副本 @@ -87,7 +79,7 @@ fn compile_string_literal_and_lib() { #[test] fn compile_multiline_string_literal() { let mut kv = fresh_kv(); - let src = "lib m { rwfunc ml() -> () {\n \"\"\"hello\nworld\"\"\" -> _\n} }\n"; + let src = "lib m { rwfunc ml() -> () {\n r#\"hello\nworld\"# -> _\n} }\n"; compile(&mut kv, src).unwrap(); let r = kv.get_one("/lib/m·ml/[1,-1]"); diff --git a/layout/tests/redis_roundtrip_test.rs b/layout/tests/redis_roundtrip_test.rs index da7cae82..32ee07b4 100644 --- a/layout/tests/redis_roundtrip_test.rs +++ b/layout/tests/redis_roundtrip_test.rs @@ -5,10 +5,14 @@ fn body(data: &[u8]) -> &[u8] { kvkind::body(data, &h) } -/// 需要 redis 运行在 127.0.0.1:6379。 +fn redis_dsn() -> String { + std::env::var("KVLANG_TEST_REDIS_DSN").unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string()) +} + +/// Use a dedicated Redis instance for this test. #[test] fn redis_roundtrip() { - let mut kv = Kv::conn("redis://127.0.0.1:6379"); + let mut kv = Kv::conn(&redis_dsn()); init_dirs(&mut kv).unwrap(); // 写一个 rwir(Opaque kind)并读回(无 `.` 的 plain key,避免 dict 拆分) @@ -16,7 +20,7 @@ fn redis_roundtrip() { kv.set(&[("/lib/testop".to_string(), v)]).unwrap(); let got = kv.get_one("/lib/testop"); assert_eq!(kvkind::kind(&got), "rwir"); - assert_eq!(String::from_utf8_lossy(&body(&got)[4..]), "testop"); + assert_eq!(String::from_utf8_lossy(&body(&got)[5..]), "testop"); // 目录索引可见 let children = kv.list("/lib/", false, false); @@ -28,7 +32,7 @@ fn redis_roundtrip() { #[test] fn redis_compile() { - let mut kv = Kv::conn("redis://127.0.0.1:6379"); + let mut kv = Kv::conn(&redis_dsn()); init_dirs(&mut kv).unwrap(); let src = "rwfunc sum(A:int64, B:int64) -> (C:int64) {\n A + B -> C\n}\n"; @@ -41,6 +45,6 @@ fn redis_compile() { assert_eq!(kvkind::rwfunc_num_writes(b), 1); let op = kv.get_one("/lib/sum/[1,0]"); - assert_eq!(kvkind::kind(&op), "rwir|rwfunc"); - assert_eq!(String::from_utf8_lossy(&body(&op)[4..]), "int64·add"); + assert_eq!(kvkind::kind(&op), "rwir"); + assert_eq!(String::from_utf8_lossy(&body(&op)[5..]), "int64·add"); } diff --git a/runtime-rs/src/engine.rs b/runtime-rs/src/engine.rs index 1b2cfcf2..3128a19e 100644 --- a/runtime-rs/src/engine.rs +++ b/runtime-rs/src/engine.rs @@ -1,5 +1,4 @@ -//! Engine —— kvlang runtime 核心原语:kvspace 读写、TLV 编解码、rwir 读写槽解析、结构操作。 -//! 模式2 驱动循环在二进制 main.rs(含 print 批处理 + 外部 rwir handoff);具体纯净 rwir 见 `crate::rwir`。 +//! KVSpace access and rwir primitives. use std::ffi::{c_char, c_void}; use std::ptr::null_mut; @@ -7,40 +6,6 @@ use std::ptr::null_mut; use crate::ffi::*; use crate::rwir; -const KVSPACE_REF_EXT: u8 = 2; -const STORETYPE_ATOM: u8 = 1; -const STORETYPE_ARRAYND: u8 = 2; -const STORETYPE_INDEX: u8 = 3; -const STORETYPE_EXTINDEX: u8 = 4; - -/// 由完整 langtype langtype 串推 storetype(镜像 kvspace-durable::storetype_from_langtype): -/// extindex / index(含 rwfunc/defrwir) / 对象(·)·路径(/) → INDEX 系;带 [dims] → ARRAYND;否则 ATOM。 -fn storetype_from_langtype(kx: &str) -> u8 { - if kx.is_empty() { - return 0; - } - let (has_dims, base) = match kx.strip_prefix('[') { - Some(_) => match kx.find(']') { - Some(e) => (true, &kx[e + 1..]), - None => (false, kx), - }, - None => (false, kx), - }; - if base == "extindex" { - return STORETYPE_EXTINDEX; - } - if matches!(base, "index" | "rwfunc" | "def rwir") - || base.starts_with('/') - || base.contains('\u{b7}') - { - return STORETYPE_INDEX; - } - if has_dims { - return STORETYPE_ARRAYND; - } - STORETYPE_ATOM -} - pub struct Engine { pub rt: *mut c_void, // kvlang runtime 句柄 pub kv: *mut c_void, // kvspace 句柄(自持,同时传给 rwirext) @@ -51,56 +16,12 @@ pub struct Engine { impl Engine { // ── kvspace 读写(绝对路径,char/utf8 与 char/utf32 编解码)───────── - /// 写即构造:按三正交轴 (ref, storetype, ro, vid, langtype) + body 向 kvspace 要偏移指针后直接写 body 字节—— - /// key 已存在且同 body_len → WriteInPlace(原 box 就地);否则 WriteNewPlace(新 box)。 - /// 两分支各调唯一原语、无预 encode 整条 TLV、无中转 buffer、无 free。 - fn write_construct( - &self, - key: &str, - r#ref: u8, - storetype: u8, - ro: u8, - vid: u32, - langtype: &str, - body: &[u8], - ) { - unsafe { - let ck = cs(key); - let mut bp: *mut u8 = null_mut(); - let mut err = [0u8; 256]; - let rc = kvspaceWriteInPlace( - self.kv, - ck.as_ptr(), - 1, - body.len() as u32, - &mut bp, - err.as_mut_ptr() as *mut c_char, - 256, - ); - if rc != 0 { - kvspaceWriteNewPlace( - self.kv, - ck.as_ptr(), - r#ref, - storetype, - ro, - vid, - cs(langtype).as_ptr(), - body.len() as u32, - &mut bp, - err.as_mut_ptr() as *mut c_char, - 256, - ); - } - if !body.is_empty() && !bp.is_null() { - std::ptr::copy_nonoverlapping(body.as_ptr(), bp, body.len()); - } - } - } /// 通用类型化写入:任意 kind + body 字节 + dims(空=标量 ndim0)。 /// set_kv 与各 rwir 的类型化输出共用,避免每种 kind 一个专用写函数。 pub fn set_tlv_encoded(&self, key: &str, kind: &str, raw: &[u8], dims: &[i32]) { - self.set_tlv(key, &tlv_encode(kind, raw, dims)); + let value = tlv_encode(kind, raw, dims); + assert!(!value.is_empty(), "invalid XValue for {key}"); + self.set_tlv(key, &value); } pub fn set_kv(&self, key: &str, val: &str) { let utf32: Vec = val.chars().map(|c| c as u32).collect(); @@ -134,19 +55,27 @@ impl Engine { } } - /// 扩展世界(@ ref=2)句柄编码写入:kind=目标完整 langtype(如 "[]uint8"),body=定位串。 - /// 读取该 key 时由 read_at 按 body 前缀路由给对应 /lib/networld/* 兑现器还原真实字节。 pub fn set_ext_handle(&self, key: &str, target_langtype: &str, locator: &str) { - let st = storetype_from_langtype(target_langtype); - self.write_construct( - key, - KVSPACE_REF_EXT, - st, - 0, - 0, - target_langtype, - locator.as_bytes(), - ); + unsafe { + let mut out = null_mut(); + let mut len = 0u32; + let rc = kvspaceTlvEncodeMode( + cs(target_langtype).as_ptr(), + locator.as_ptr(), + locator.len() as u32, + std::ptr::null(), + 0, + 2, + 0, + 0, + &mut out, + &mut len, + ); + assert_eq!(rc, 0, "invalid ext locator for {key}"); + let value = std::slice::from_raw_parts(out, len as usize).to_vec(); + libc::free(out as *mut c_void); + self.set_tlv(key, &value); + } } /// 读 key 的 head,返回 (ref, body 串)。仅 ref==2 时 body 有意义(扩展句柄定位串)。 @@ -211,9 +140,7 @@ impl Engine { pub fn write0(&self, pc: &str) -> String { self.write_at(pc, 0) } - /// @-aware 读参整块原始字节(read_at 对数组只返首元素,取字节须走容器路径 + TLV body): - /// 取读参 idx 容器路径 → 读 body 字节;@ 句柄按前缀兑现真实字节;无路径退化为 read_at 的 utf8。 - /// 供 fs·write/append 等需要 []uint8 整块的 rwir 用。 + /// Read the complete operand body, resolving external handles. pub fn read_bytes(&self, pc: &str, idx: i32) -> Vec { let p = take(unsafe { kvlangRwirextResolveReadPath(self.kv, cs(pc).as_ptr(), idx) }); if p.is_empty() { @@ -245,21 +172,8 @@ impl Engine { } } - /// 写一个字符串列表容器(供 fs·list 等返回可 for-in 的字符串列表,对齐 C kvspace·list 表示): - /// dst = 容器值:langtype=`[int64]·[]char/utf32`(见 [[map容器]]),body 空,dims=[n] - /// dst· = memindex:kind=index,body=[4B count LE]["[0]\n[1]\n..."](成员坐标名唯一权威) - /// dst·[i] = 各成员字符串(char/utf32) pub fn set_str_list(&self, dst: &str, items: &[String]) { - let n = items.len(); - self.set_tlv_encoded(dst, "[int64]·[]char/utf32", &[], &[n as i32]); - let mut body = (n as u32).to_le_bytes().to_vec(); - for i in 0..n { - if i > 0 { - body.push(b'\n'); - } - body.extend_from_slice(format!("[{i}]").as_bytes()); - } - self.set_tlv_encoded(&format!("{dst}\u{b7}"), "index", &body, &[]); + self.set_tlv_encoded(dst, "[int64]·[]char/utf32", &[], &[]); for (i, it) in items.iter().enumerate() { self.set_kv(&format!("{dst}\u{b7}[{i}]"), it); } @@ -333,29 +247,24 @@ impl Engine { std::slice::from_raw_parts(out, olen as usize).to_vec() } } - /// 写预编码 TLV:解 head 取 (langtype, body) 后走写即构造(新建/换 kind/换尺寸唯一原语)。 pub fn set_tlv(&self, key: &str, tlv: &[u8]) { if tlv.is_empty() { + self.del(key); return; } unsafe { - let mut head = KvspaceHead::default(); - if kvspaceDecodeHead(tlv.as_ptr(), tlv.len() as u32, &mut head) != 0 { - return; - } - let kx = String::from_utf8_lossy(&head.langtype) - .trim_end_matches('\0') - .to_string(); - let (bo, bl) = (head.body_offset as usize, head.body_len.max(0) as usize); - self.write_construct( - key, - head.r#ref, - head.storetype, - head.ro, - head.vid, - &kx, - &tlv[bo..bo + bl], + let mut err = [0u8; 256]; + let rc = kvspaceSetValue( + self.kv, + cs(key).as_ptr(), + tlv.as_ptr(), + tlv.len() as u32, + 0, + 0, + err.as_mut_ptr() as *mut c_char, + err.len() as u32, ); + assert_eq!(rc, 0, "kvspace set {key}: {}", cbuf(&err)); } } diff --git a/runtime-rs/src/ffi.rs b/runtime-rs/src/ffi.rs index 6361c418..1b806686 100644 --- a/runtime-rs/src/ffi.rs +++ b/runtime-rs/src/ffi.rs @@ -1,12 +1,8 @@ -//! C ABI —— 直连 stock 三方 .so(camelCase,符合 deepx-design/doc/abi-naming-standard.md): -//! kvspace-durable / dispatch : 地址空间 + KV 存取 + TLV 编解码 -//! kvlang runtime : 模式2 主导执行 + rwirext 宿主 ABI(含 IsExt/Handoff 供外部 rwir 移交) -//! kvlang layout : .kv 编译入库(文件 / 内存源码 / 只校验 / 格式化) +//! C ABI bindings for kvspace, kvlang runtime, and layout. use std::ffi::{c_char, c_int, c_void, CStr, CString}; -/// kvspaceDecodeHead 输出(逐字段对齐 kvspace/include/kvspace/kvspace.h 的 kvspaceHead_t)。 -/// 三正交轴 ref×storetype×langtype;langtype 即该 ABI 的 langtype 槽(本 runtime 内部沿用 langtype 命名)。 +/// Matches kvspaceHead_t. #[repr(C)] pub struct KvspaceHead { pub headlen: u16, @@ -20,6 +16,7 @@ pub struct KvspaceHead { pub langtype: [u8; 256], pub langtype_len: i32, pub body_offset: i32, + pub body_cap: u64, } impl Default for KvspaceHead { @@ -36,12 +33,12 @@ impl Default for KvspaceHead { langtype: [0u8; 256], langtype_len: 0, body_offset: 0, + body_cap: 0, } } } -/// 类型化 TLV 编码——唯一编码入口,直委托 kvspace 正典 codec kvspaceTlvEncode(不在 Rust -/// 侧复刻 langtype 构造)。dims 空=标量。返回 frontend malloc 的 TLV 拷贝,随即 free。 +/// Encode through the kvspace codec. pub fn tlv_encode(kind: &str, raw: &[u8], dims: &[i32]) -> Vec { unsafe { let (mut out, mut olen) = (std::ptr::null_mut(), 0u32); @@ -68,7 +65,7 @@ pub fn tlv_encode(kind: &str, raw: &[u8], dims: &[i32]) -> Vec { } unsafe extern "C" { - // ── kvspace:KV 存取 + TLV ──────────────────────────────────────── + // kvspace ABI. pub fn kvspaceConnect(dsn: *const c_char) -> *mut c_void; pub fn kvspaceClear(h: *mut c_void, err: *mut c_char, err_cap: u32) -> c_int; pub fn kvspaceDelTree( @@ -92,27 +89,13 @@ unsafe extern "C" { out: *mut *mut u8, out_len: *mut u32, ) -> c_int; - /// 就地写:key 已存在、body_len==原 body_len → 返回原 box body 偏移指针;否则非 0。 - pub fn kvspaceWriteInPlace( + pub fn kvspaceSetValue( h: *mut c_void, key: *const c_char, - resolve: c_int, - body_len: u32, - body: *mut *mut u8, - err: *mut c_char, - err_cap: u32, - ) -> c_int; - /// 新位置写:按 (ref, storetype, ro, vid, langtype, body_len) 分配新 box、写 head,返回 body 偏移指针。 - pub fn kvspaceWriteNewPlace( - h: *mut c_void, - key: *const c_char, - r#ref: u8, - storetype: u8, + value: *const u8, + value_len: u32, ro: u8, vid: u32, - langtype: *const c_char, - body_len: u32, - body: *mut *mut u8, err: *mut c_char, err_cap: u32, ) -> c_int; @@ -144,6 +127,18 @@ unsafe extern "C" { out: *mut *mut u8, out_len: *mut u32, ) -> c_int; + pub fn kvspaceTlvEncodeMode( + kind: *const c_char, + raw: *const u8, + raw_len: u32, + dims: *const i32, + ndim: i32, + r#ref: i32, + ro: u8, + vid: u32, + out: *mut *mut u8, + out_len: *mut u32, + ) -> c_int; // ── kvlang runtime:模式2 执行(kvlang_vthread.h)─────────────── pub fn kvlangRuntimeConnect(dsn: *const c_char) -> *mut c_void; diff --git a/runtime-rs/src/lib.rs b/runtime-rs/src/lib.rs index 782140ce..3c6c7214 100644 --- a/runtime-rs/src/lib.rs +++ b/runtime-rs/src/lib.rs @@ -1,9 +1,4 @@ -//! kvlang-rs —— 功能完整的 kvlang runtime 库。 -//! ffi : C ABI 绑定(kvspace / kvlang runtime / kvlang layout) -//! engine : Engine,kvspace 读写 / TLV / 读写槽解析等原语 -//! rwir : 纯净 rwir stdlib(term/json/http/kvlanglayout) -//! 二进制 `kvlang`(src/main.rs)在此之上加 CLI + 模式2 驱动循环; -//! byteseek 等外部 crate 可直接依赖本库复用 Engine/ffi/纯 rwir,再叠加自有 rwir。 +//! kvlang runtime library. #![allow(non_snake_case, non_camel_case_types)] pub mod engine; diff --git a/runtime-rs/src/rwir/json.rs b/runtime-rs/src/rwir/json.rs index 1699a6c7..5aa3c28b 100644 --- a/runtime-rs/src/rwir/json.rs +++ b/runtime-rs/src/rwir/json.rs @@ -1,22 +1,14 @@ -//! rwir `json·to` / `json·from`:KV 子树 ↔ JSON 文本(对齐 kvlang go/json 的 · 成员形态)。 -//! json·to(root) -> str root 整棵子树读成 JSON:stringkeymap 容器(p + memindex p·) -//! 按成员名形态分派——`[i]` 坐标段 → JSON 数组,命名键 → JSON 对象; -//! / 目录树(kind=index)→ 嵌套对象;compact ndarray → 数组。 -//! json·from(json) -> root 反序列化 JSON 写回 root 子树(覆盖语义,先 del_tree)。 -//! 容器值在 p(无后缀):stringkeymap,命名字典 dims=[0]、坐标数组 dims=[n];memindex p· -//! (kind=index,body=[4B count LE][names])是成员列表唯一权威。对象数组递归支持。 -//! 编码走权威 kvspace ABI:DecodeHead 读头、TlvEncode/NewCharByte 编码。 +//! JSON conversion over physical KVSpace members. use serde_json::{Map, Value}; use crate::engine::Engine; use crate::ffi::*; -// 常量与 kvlang go/json 的 cconst 对齐(直连 kvspace,无 kvspaceConst ABI)。 const SEP: &str = "·"; const DIR_SUF: &str = "/"; -const KIND_MAP: &str = "stringkeymap"; -const KIND_INDEX: &str = "index"; +const OBJECT_TYPE: &str = "[]char/utf32·any"; +const ARRAY_TYPE: &str = "[int64]·any"; pub fn to(eng: &Engine, pc: &str) { let names = params(eng, pc); @@ -48,15 +40,12 @@ fn params(eng: &Engine, pc: &str) -> Vec { s.lines().map(str::to_string).collect() } -// ── KV 子树 → JSON ──────────────────────────────────────────────── - fn read_value(eng: &Engine, path: &str) -> Value { let (kind, raw, arr_len) = parse_tlv(&eng.get_tlv(path)); - if kind == KIND_MAP { - return read_container(eng, path); + if kind.contains(SEP) { + return read_container(eng, path, &kind); } - let dkind = parse_tlv(&eng.get_tlv(&format!("{path}{DIR_SUF}"))).0; - if dkind == KIND_INDEX { + if !eng.list_kv(&format!("{path}{DIR_SUF}")).is_empty() { return read_dir(eng, path); } if kind.is_empty() { @@ -65,7 +54,6 @@ fn read_value(eng: &Engine, path: &str) -> Value { tlv_to_json(&kind, &raw, arr_len) } -// read_dir:/ 目录树(kind=index)→ JSON object,子名带尾 / 先 strip。 fn read_dir(eng: &Engine, path: &str) -> Value { let mut map = Map::new(); for name in eng.list_kv(&format!("{path}{DIR_SUF}")) { @@ -78,25 +66,21 @@ fn read_dir(eng: &Engine, path: &str) -> Value { Value::Object(map) } -// read_container:stringkeymap 容器 p → 按 memindex 成员名形态分派。 -// 成员全为 `[i]` 坐标段 → JSON 数组;否则(命名键)→ JSON 对象。 -fn read_container(eng: &Engine, path: &str) -> Value { +fn read_container(eng: &Engine, path: &str, langtype: &str) -> Value { let names = eng.list_kv(&format!("{path}{SEP}")); - let all_idx = !names.is_empty() + if langtype.starts_with("[int64]·") && names.iter().all(|n| { n.strip_prefix('[') .and_then(|r| r.strip_suffix(']')) - .map(|i| i.parse::().is_ok()) - .unwrap_or(false) - }); - if all_idx { + .is_some_and(|i| i.parse::().is_ok()) + }) + { read_arr(eng, path) } else { read_obj(eng, path) } } -// read_obj:命名字典 stringkeymap p → 遍历 memindex p· 成员。 fn read_obj(eng: &Engine, path: &str) -> Value { let mut map = Map::new(); for name in eng.list_kv(&format!("{path}{SEP}")) { @@ -105,7 +89,6 @@ fn read_obj(eng: &Engine, path: &str) -> Value { Value::Object(map) } -// read_arr:stringkeymap 容器值 p → 遍历 memindex p· 坐标段 [i],按数值升序。 fn read_arr(eng: &Engine, path: &str) -> Value { let mut idxs: Vec = Vec::new(); for n in eng.list_kv(&format!("{path}{SEP}")) { @@ -218,32 +201,24 @@ fn utf32_to_string(raw: &[u8]) -> String { .collect() } -// ── JSON → KV 子树 ──────────────────────────────────────────────── - fn write_value(eng: &Engine, path: &str, v: &Value) { match v { Value::Object(m) => write_obj(eng, path, m), Value::Array(arr) => write_arr(eng, path, arr), - Value::Null => eng.set_tlv(path, &[]), + Value::Null => eng.set_tlv(path, &tlv_encode("None", &[], &[])), _ => eng.set_tlv(path, &value_to_tlv(v)), } } fn write_obj(eng: &Engine, path: &str, m: &Map) { - let mut keys: Vec<&String> = m.keys().collect(); - keys.sort(); eng.set_tlv(path, &mk_obj_value()); - let names: Vec = keys.iter().map(|k| k.to_string()).collect(); - eng.set_tlv(&format!("{path}{SEP}"), &mk_mem_index(&names)); - for k in keys { + for k in m.keys() { write_value(eng, &format!("{path}{SEP}{k}"), &m[k]); } } fn write_arr(eng: &Engine, path: &str, arr: &[Value]) { - let names: Vec = (0..arr.len()).map(|i| format!("[{i}]")).collect(); - eng.set_tlv(path, &mk_map_value(arr.len())); - eng.set_tlv(&format!("{path}{SEP}"), &mk_mem_index(&names)); + eng.set_tlv(path, &mk_map_value()); for (i, v) in arr.iter().enumerate() { write_value(eng, &format!("{path}{SEP}[{i}]"), v); } @@ -264,28 +239,18 @@ fn value_to_tlv(v: &Value) -> Vec { } } -// ── 容器值 / memindex 编码 ───────────────────────────────────────── - -// memindex p·:kind=index,body=[4B count LE][names]。 -fn mk_mem_index(names: &[String]) -> Vec { - let mut body = (names.len() as u32).to_le_bytes().to_vec(); - body.extend_from_slice(names.join("\n").as_bytes()); - tlv_encode(KIND_INDEX, &body, &[]) -} - -// 命名字典容器值 p:kind=stringkeymap,dims=[0](无形状,成员在 memindex)。 fn mk_obj_value() -> Vec { - tlv_encode(KIND_MAP, &[], &[0]) + tlv_encode(OBJECT_TYPE, &[], &[]) } -// stringkeymap 容器值 p:body 空,dims=[n](恒一维坐标段)。 -fn mk_map_value(n: usize) -> Vec { - tlv_encode(KIND_MAP, &[], &[n as i32]) +fn mk_map_value() -> Vec { + tlv_encode(ARRAY_TYPE, &[], &[]) } -// ── langtype 串解析(反序列化按类型/形状分发用;kvspace 未导出串解析器)───────── - fn parse_langtype(kx: &str) -> (Vec, String) { + if kx.contains(SEP) { + return (Vec::new(), kx.to_string()); + } if kx.starts_with('[') { match kx.find(']') { Some(end) => ( @@ -328,15 +293,17 @@ mod tests { } fn test_engine() -> Engine { - let dsn = "redis://127.0.0.1:6379"; - let kv = unsafe { kvspaceConnect(cs(dsn).as_ptr()) }; + static NEXT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); + let n = NEXT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let dsn = format!("fs:///tmp/kvlang-json-{}-{n}", std::process::id()); + let kv = unsafe { kvspaceConnect(cs(&dsn).as_ptr()) }; assert!(!kv.is_null()); let mut err = [0u8; 256]; unsafe { kvspaceClear(kv, err.as_mut_ptr() as *mut c_char, 256) }; Engine { rt: std::ptr::null_mut(), kv, - dsn: dsn.to_string(), + dsn, ext: None, } } @@ -362,19 +329,10 @@ mod tests { #[test] fn native_data_to() { let eng = test_engine(); - // 用户 kv 代码写 · 成员(object 容器 + memindex),json·to 读回。 eng.set_tlv("/data", &mk_obj_value()); - eng.set_tlv( - "/data·", - &mk_mem_index(&["age".into(), "name".into(), "scat".into()]), - ); eng.set_tlv("/data·age", &i64_tlv(&[42])); eng.set_tlv("/data·name", &new_char_byte(b"alice")); - eng.set_tlv("/data·scat", &mk_map_value(3)); - eng.set_tlv( - "/data·scat·", - &mk_mem_index(&["[0]".into(), "[1]".into(), "[2]".into()]), - ); + eng.set_tlv("/data·scat", &mk_map_value()); eng.set_tlv("/data·scat·[0]", &i64_tlv(&[10])); eng.set_tlv("/data·scat·[1]", &i64_tlv(&[20])); eng.set_tlv("/data·scat·[2]", &i64_tlv(&[30])); diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 51bd0458..63499368 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -22,10 +22,9 @@ target_link_directories(kvlang_runtime PRIVATE ${KVSPACE_LIB_DIR}) if(APPLE) # macOS:数学/线程符号在 libSystem 内(无 -lm/-lpthread);ld64 无 --disable-new-dtags。 - # weak_import 的 ResolveRef/GetByRef 在旧 libkvspace 上不存在,需 dynamic_lookup。 target_link_libraries(kvlang_runtime PRIVATE kvspace) set_target_properties(kvlang_runtime PROPERTIES - LINK_FLAGS "-Wl,-export_dynamic -Wl,-rpath,${KVSPACE_LIB_DIR} -Wl,-undefined,dynamic_lookup") + LINK_FLAGS "-Wl,-export_dynamic -Wl,-rpath,${KVSPACE_LIB_DIR}") else() target_link_libraries(kvlang_runtime PRIVATE kvspace m pthread) # --export-dynamic 供 kvlang(term 扩展)找符号;--disable-new-dtags 使 rpath 转 DT_RPATH(传递)。 @@ -34,17 +33,33 @@ else() endif() if(NOT APPLE) - add_custom_command( - OUTPUT "${BIN_DIR}/test_getmember_siblings" - COMMAND ${CMAKE_C_COMPILER} -O2 -D_GNU_SOURCE - -I${CMAKE_SOURCE_DIR}/include -I${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/tests/test_getmember_siblings.c - -o ${BIN_DIR}/test_getmember_siblings - -L${BIN_DIR} -L${KVSPACE_LIB_DIR} - -Wl,--disable-new-dtags - -Wl,-rpath,${BIN_DIR} -Wl,-rpath,${KVSPACE_LIB_DIR} - -Wl,--no-as-needed -lkvspace -Wl,--as-needed -lkvlang_runtime - DEPENDS kvlang_runtime "${CMAKE_SOURCE_DIR}/tests/test_getmember_siblings.c" - VERBATIM) - add_custom_target(test_getmember_siblings ALL DEPENDS "${BIN_DIR}/test_getmember_siblings") + enable_testing() + foreach(test_name test_getmember_siblings test_kvset_invalid_head test_pc_kvspace_source test_crash_resume test_param_address) + add_custom_command( + OUTPUT "${BIN_DIR}/${test_name}" + COMMAND ${CMAKE_C_COMPILER} -O2 -D_GNU_SOURCE + -I${CMAKE_SOURCE_DIR}/include -I${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/tests/${test_name}.c + -o ${BIN_DIR}/${test_name} + -L${BIN_DIR} -L${KVSPACE_LIB_DIR} + -Wl,--disable-new-dtags + -Wl,-rpath,${BIN_DIR} -Wl,-rpath,${KVSPACE_LIB_DIR} + -Wl,--no-as-needed -lkvspace -Wl,--as-needed -lkvlang_runtime + DEPENDS kvlang_runtime "${CMAKE_SOURCE_DIR}/tests/${test_name}.c" + VERBATIM) + add_custom_target(${test_name} ALL DEPENDS "${BIN_DIR}/${test_name}") + endforeach() + add_test(NAME getmember_siblings COMMAND "${BIN_DIR}/test_getmember_siblings") + add_test(NAME kvset_invalid_head_shm COMMAND "${BIN_DIR}/test_kvset_invalid_head") + add_test(NAME kvset_invalid_head_fs COMMAND "${BIN_DIR}/test_kvset_invalid_head") + add_test(NAME pc_kvspace_source_shm COMMAND "${BIN_DIR}/test_pc_kvspace_source") + add_test(NAME pc_kvspace_source_fs COMMAND "${BIN_DIR}/test_pc_kvspace_source") + add_test(NAME crash_resume_shm COMMAND "${BIN_DIR}/test_crash_resume") + add_test(NAME crash_resume_fs COMMAND "${BIN_DIR}/test_crash_resume") + add_test(NAME param_address_shm COMMAND "${BIN_DIR}/test_param_address") + add_test(NAME param_address_fs COMMAND "${BIN_DIR}/test_param_address") + set_tests_properties(getmember_siblings kvset_invalid_head_shm pc_kvspace_source_shm crash_resume_shm param_address_shm + PROPERTIES ENVIRONMENT "KVSPACE_BACKEND_PATH=${KVSPACE_LIB_DIR};KVLANG_TEST_SCHEME=shm") + set_tests_properties(kvset_invalid_head_fs pc_kvspace_source_fs crash_resume_fs param_address_fs + PROPERTIES ENVIRONMENT "KVSPACE_BACKEND_PATH=${KVSPACE_LIB_DIR};KVLANG_TEST_SCHEME=fs") endif() diff --git a/runtime/src/const.h b/runtime/src/const.h index 078e148f..66951341 100644 --- a/runtime/src/const.h +++ b/runtime/src/const.h @@ -25,15 +25,12 @@ #define KVSPACE_KIND_CHAR_UTF8 "char/utf8" #define KVSPACE_KIND_CHAR_ASCII "char/ascii" #define KVSPACE_KIND_MAP "stringkeymap" -#define KVSPACE_KIND_INDEX "index" -#define KVSPACE_KIND_EXT_INDEX "extindex" #define KVSPACE_KIND_RWIR "rwir" #define KVSPACE_KIND_RWFUNC "rwfunc" #define KVSPACE_KIND_DEF_RWIR "def rwir" #define KVSPACE_KIND_DEF_LANGTYPE "def langtype" -#define KVSPACE_KIND_RWIR_OR_RWFUNC "rwir|rwfunc" #define KVSPACE_KIND_SCOPE "scope" -#define KVSPACE_KIND_STRUCT "struct" +#define KVSPACE_KIND_DEF_STRUCT "def struct" #define KVSPACE_KIND_TIME "time" #define KVSPACE_KIND_DURATION "duration" diff --git a/runtime/src/keytree.c b/runtime/src/keytree.c index 27837b6f..695d6963 100644 --- a/runtime/src/keytree.c +++ b/runtime/src/keytree.c @@ -42,13 +42,6 @@ char *kvlangKeytreeFrameRoot(const char *pc) { return r; } -/* 帧根长度(末个 "/[" 之前),无分配:循环每步据之判断帧是否变化,免一次 malloc。 */ -size_t kvlangKeytreeFrameRootLen(const char *pc) { - const char *last = NULL; - for (const char *p = pc; (p = strstr(p, "/[")) != NULL; p += 2) last = p; - return last ? (size_t)(last - pc) : 0; -} - static char *trim_right_join(const char *root, const char *suffix) { size_t n = strlen(root); while (n > 0 && root[n - 1] == '/') n--; diff --git a/runtime/src/kvcpu.c b/runtime/src/kvcpu.c index b7e3426b..a289e05a 100644 --- a/runtime/src/kvcpu.c +++ b/runtime/src/kvcpu.c @@ -1,104 +1,8 @@ #include "runtime_internal.h" -/* decode-once 进程内缓存(IV-0:进程私有,绝不入 kvspace)。指令 XValue layout 后冻结, - * 一条冻结指令由 (funcdir, addr0) 唯一标识(funcdir=/lib/pkg·fn,不同函数不同 funcdir, - * 递归/多帧同函数共享同一条),故缓存永不失效。命中即跳过 code 槽读 + decode + 分类。 - * runtime C 单线程驱动(全库无锁直改 kvspace),此缓存同样无锁。永不淘汰(≈代码大小上界)。 */ -typedef struct rwir_cache_ent { - char *funcdir; - int addr0; - kvlangRwirInst_t *inst; - struct rwir_cache_ent *next; -} rwir_cache_ent_t; -#define RWIR_CACHE_BUCKETS 4096 -static rwir_cache_ent_t *g_rwir_cache[RWIR_CACHE_BUCKETS]; - -/* 直接对 (funcdir, addr0) 做 FNV——不拼 "funcdir#addr0" 串(那要每步 malloc+格式化+free, - * 在 shm 后端 decode 本已近乎免费的深递归热路径上纯属净开销)。 */ -static size_t rwir_hash(const char *funcdir, int addr0) { - size_t h = 1469598103934665603ULL; - for (const char *s = funcdir; *s; s++) { - h ^= (unsigned char)*s; - h *= 1099511628211ULL; - } - h ^= (size_t)(unsigned)addr0; - h *= 1099511628211ULL; - return h & (RWIR_CACHE_BUCKETS - 1); -} -static kvlangRwirInst_t *rwir_cache_get(const char *funcdir, int addr0) { - for (rwir_cache_ent_t *e = g_rwir_cache[rwir_hash(funcdir, addr0)]; e; - e = e->next) - if (e->addr0 == addr0 && strcmp(e->funcdir, funcdir) == 0) - return e->inst; - return NULL; -} -static void rwir_cache_put(const char *funcdir, int addr0, - kvlangRwirInst_t *inst) { - size_t b = rwir_hash(funcdir, addr0); - rwir_cache_ent_t *e = malloc(sizeof *e); - e->funcdir = strdup(funcdir); - e->addr0 = addr0; - e->inst = inst; - e->next = g_rwir_cache[b]; - g_rwir_cache[b] = e; -} - -/* /lib 元信息 intern(P2-c):opcode → {是否 notinmyrwircaps(须经 def rwir 路由到其它 - * runtime)、读参 langtype 签名}。/lib 布局后冻结,同 decode 缓存同理永不失效。命中即免去 - * OPID_notinmyrwircaps 分支每步两次元信息往返(notinmyrwircaps + load_def_reads)与签名 langtype 重解析。 - * langtype 签名串在此按 opcode 驻留一次(IV-0:进程私有,绝不入 kvspace)。 */ -typedef struct opmeta_ent { - char *opcode; - int notinmyrwircaps; /* 1 = 不在本 runtime myrwircaps、须经 def rwir 路由;0 = 用户 rwfunc */ - char - *def_sig; /* notinmyrwircaps 时的读参 langtype 签名(owned,可 NULL) */ - int def_nr; - int def_dyn; /* 末读参变参(主槽 body 的 dynamic 字节) */ - struct opmeta_ent *next; -} opmeta_ent_t; -static opmeta_ent_t *g_opmeta_cache[RWIR_CACHE_BUCKETS]; - static char *load_def_reads(kvlangKv_t *kv, const char *key, int *out_nr, int *out_dyn); -static opmeta_ent_t *opmeta_get(kvlangKv_t *kv, const char *opcode) { - size_t b = rwir_hash(opcode, 0); - for (opmeta_ent_t *e = g_opmeta_cache[b]; e; e = e->next) - if (strcmp(e->opcode, opcode) == 0) - return e; - opmeta_ent_t *e = malloc(sizeof *e); - e->opcode = strdup(opcode); - e->notinmyrwircaps = notinmyrwircaps(kv, opcode) ? 1 : 0; - e->def_sig = NULL; - e->def_nr = 0; - e->def_dyn = 0; - if (e->notinmyrwircaps) { - char *rk = kvlangKeytreeRwir(opcode); - e->def_sig = load_def_reads(kv, rk, &e->def_nr, &e->def_dyn); - free(rk); - } - e->next = g_opmeta_cache[b]; - g_opmeta_cache[b] = e; - return e; -} - -/* 读帧的 ‥lib 槽 → funcdir(/lib/pkg·fn),供缓存键用;无则 NULL(不缓存该指令)。 */ -static char *read_seglib(kvlangKv_t *kv, const char *fr) { - char *stk = kvlangKeytreeStack(fr); - kvlangStrbuf_t k; - kvlangStrbufInit(&k); - kvlangStrbufPuts(&k, stk); - kvlangStrbufPuts(&k, SEG_LIB); - free(stk); - kvlangXvalue_t v; - kvlangXvalueZero(&v); - kvlangKvGetOne(kv, k.p, &v); - kvlangStrbufFree(&k); - char *r = kvlangXvalueNone(&v) ? NULL : kvlangXvalueValueString(&v); - kvlangXvalueFree(&v); - return r; -} - /* 找末个成员分隔符(·,多字节),对齐 strrchr('.') 的单字节旧语义。 */ static const char *rfind_sep(const char *s) { const char *found = NULL, *p = s; @@ -132,7 +36,7 @@ static int jump_to(kvlangFrame_t *f, const kvlangParam_t *target, const char *op kvlangVthreadSetError(f->kv, f->vtid, f->pc, msg); return -1; } - /* 帧根优先用主循环缓存的(br/goto 帧不变,借它免每次 malloc)。 */ + /* Reuse this instruction's frame root. */ char *owned = NULL; const char *fr = f->frame_root; if (!fr) { owned = kvlangKeytreeFrameRoot(f->pc); fr = owned; } @@ -147,9 +51,10 @@ static int jump_to(kvlangFrame_t *f, const kvlangParam_t *target, const char *op static bool is_literal(const char *s) { if (!s || !s[0]) return false; - return s[0] == '"' || s[0] == '/' || strcmp(s, "true") == 0 || + return s[0] == '"' || strcmp(s, "true") == 0 || strcmp(s, "false") == 0 || strcmp(s, "null") == 0 || - (s[0] >= '0' && s[0] <= '9') || (s[0] == '-' && s[1]); + (s[0] >= '0' && s[0] <= '9') || + (s[0] == '-' && s[1] >= '0' && s[1] <= '9'); } /* 派发期读参类型校验(runtime篇-07 第八节):把每个实参的 kind 逐一匹配 @@ -309,29 +214,16 @@ static char *frame_slot_key(const char *frame_root, const char *slot) { return kvlangStrbufDetach(&b); } -/* 实参名 → 其存储键(写入被调帧 [0,-k]/[0,k])。字面量返回 NULL。 - * *[0,±k] 显式解引用:读本帧 [0,±k](ref=1 Ptr,body=实参地址),取 target 即实参存储键。 - * 普通名/绝对路径走 frame_slot_key(绝对路径直通、·成员返回 NULL)。 */ +/* Resolve a caller operand to its KVSpace key. */ static char *resolve_read_path(kvlangKv_t *kv, const char *frame_root, const char *name) { if (is_literal(name)) return NULL; - if (name[0] == '*') { - char *stk = kvlangKeytreeStack(frame_root); - kvlangXvalue_t pv; - kvlangXvalueZero(&pv); - kvlangKvGetMember(kv, stk, name + 1, &pv); - char *result = - kvlangXvalueIsPtr(&pv) ? kvlangXvaluePtrTarget(&pv) : NULL; - kvlangXvalueFree(&pv); - free(stk); - return result; - } + if (name[0] == '*') + return kvlangBuiltinResolveWriteSlot(kv, frame_root, name); return frame_slot_key(frame_root, name); } -/* return:弹出当前帧 [d]。d==1 → 顶层结束(*out_next=NULL);否则 *out_next=‥returnpc。 - * 返回链断裂(帧无 ‥returnpc)→ RuntimeError(#109),保留该帧供排查,返回 -1。 */ static int handle_return(kvlangKv_t *kv, const char *vtid, const char *pc, char **out_next) { *out_next = NULL; @@ -361,52 +253,6 @@ static int handle_return(kvlangKv_t *kv, const char *vtid, const char *pc, return -1; } } - /* 值写参 copy-out:‥wdst 第 i 行给出第 i 个写参的调用方目标 key,把本帧 `[0,+i]` 的 - * 值复制回去(地址写参留空行,不在此列)。表不存在 = 本帧无值写参,一次读即跳过。 */ - if (d > 1) { - kvlangStrbuf_t dk; - kvlangStrbufInit(&dk); - kvlangStrbufPrintf(&dk, "%s/", fr); - kvlangStrbufPuts(&dk, RUNTIME_MEMBER_SEP "wdst"); - kvlangXvalue_t dv; - kvlangXvalueZero(&dv); - kvlangKvGetOne(kv, dk.p, &dv); - kvlangStrbufFree(&dk); - if (!kvlangXvalueNone(&dv)) { - char *table = kvlangXvalueValueString(&dv); - char *cur = table; - int i = 1; - while (cur) { - char *nl = strchr(cur, '\n'); - if (nl) - *nl = 0; - if (cur[0]) { - kvlangStrbuf_t sk; - kvlangStrbufInit(&sk); - kvlangStrbufPrintf(&sk, "%s/[0,%d]", fr, i); - kvlangXvalue_t sv; - kvlangXvalueZero(&sv); - kvlangKvGetOne(kv, sk.p, &sv); - kvlangStrbufFree(&sk); - if (!kvlangXvalueNone(&sv)) { - kvlangKvPair_t wp = {cur, sv}; - char werr[256]; - kvlangKvSet(kv, &wp, 1, werr, sizeof werr); - } - kvlangXvalueFree(&sv); - } - i++; - cur = nl ? nl + 1 : NULL; - } - free(table); - } - kvlangXvalueFree(&dv); - } - char *stk = kvlangKeytreeStack(fr); - char err[256]; - kvlangKvDelExtIndex(kv, stk, err, sizeof err); - kvlangKvDelTree(kv, fr, err, sizeof err); - free(stk); free(fr); *out_next = next; return 0; @@ -415,7 +261,41 @@ static int handle_return(kvlangKv_t *kv, const char *vtid, const char *pc, static void param_decl_type(kvlangKv_t *kv, const char *func_key, int x, char *lt, size_t cap); -/* HandleCall:创建子帧。返回 EntryPC(frameRoot),失败 NULL */ +#define MAX_BIND_PAIRS (MAX_PARAMS * 5) + +static int add_param_binding(kvlangKvPair_t *pairs, int *np, + const char *frame_root, int x, const char *type, + const char *value_key) { + if (!type[0] || !value_key || *np > MAX_BIND_PAIRS - 2) + return -1; + while (*type == '*' || *type == '@') + type++; + if (!type[0]) + return -1; + kvlangStrbuf_t arg; + kvlangStrbufInit(&arg); + kvlangStrbufPrintf(&arg, "%s/%sarg[0,%d]", frame_root, RUNTIME_MEMBER_SEP, x); + char *arg_key = kvlangStrbufDetach(&arg); + kvlangStrbuf_t slot; + kvlangStrbufInit(&slot); + kvlangStrbufPrintf(&slot, "%s/[0,%d]", frame_root, x); + char *slot_key = kvlangStrbufDetach(&slot); + kvlangXvalue_t arg_ptr, frame_ptr; + kvlangXvalueNewPtr(&arg_ptr, type, value_key); + kvlangXvalueNewPtr(&frame_ptr, type, arg_key); + if (kvlangXvalueNone(&arg_ptr) || kvlangXvalueNone(&frame_ptr)) { + kvlangXvalueFree(&arg_ptr); + kvlangXvalueFree(&frame_ptr); + free(arg_key); + free(slot_key); + return -1; + } + pairs[(*np)++] = (kvlangKvPair_t){arg_key, arg_ptr}; + pairs[(*np)++] = (kvlangKvPair_t){slot_key, frame_ptr}; + return 0; +} + +/* Create the callee frame and return its entry PC. */ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) { kvlangStrbuf_t vtid_b; @@ -535,6 +415,10 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, const uint8_t *sbody = sig.data + h.body_offset; int nr = sbody[0] | (sbody[1] << 8); int nw = sbody[2] | (sbody[3] << 8); + if (nr > MAX_PARAMS || nw > MAX_PARAMS) { + kvlangVthreadSetError(kv, vtid, pc, "TypeError: too many parameters"); + goto fail; + } /* rwfunc 不做派发期位置化读参校验:类型随命名参数键 funcDir/(Ptr 的 * target_langtype)承载,函数体在帧内按名解析、各 native 算子在使用点自校验操作数 @@ -547,8 +431,14 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, char err[256]; kvlangKvDelTree(kv, frame_root, err, sizeof err); char *stack_fr = kvlangKeytreeStack(frame_root); - kvlangKvMkindex(kv, stack_fr, 0, err, sizeof err); - kvlangKvExtIndex(kv, stack_fr, func_dir.p, err, sizeof err); + if (kvlangKvMkindex(kv, stack_fr, 0, err, sizeof err) != 0 || + kvlangKvExtIndex(kv, stack_fr, func_dir.p, err, sizeof err) != 0) { + kvlangVthreadSetError(kv, vtid, pc, err); + free(caller_fr); + free(stack_fr); + free(frame_root); + goto fail; + } /* 系统变量 */ kvlangStrbuf_t npc; @@ -574,155 +464,85 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangXvalueNewCharUtf8(&v_fn, func_key); kvlangKvPair_t sys[3] = { {retpc.p, v_npc}, {callpc.p, v_ep}, {seglib.p, v_fn}}; - kvlangKvSet(kv, sys, 3, err, sizeof err); + int sys_rc = kvlangXvalueNone(&v_npc) || kvlangXvalueNone(&v_ep) || + kvlangXvalueNone(&v_fn) ? -1 : + kvlangKvSet(kv, sys, 3, err, sizeof err); kvlangXvalueFree(&v_npc); kvlangXvalueFree(&v_ep); kvlangXvalueFree(&v_fn); + if (sys_rc != 0) { + kvlangVthreadSetError(kv, vtid, pc, "RuntimeError: cannot persist call frame"); + goto fail_frame; + } - /* 读参 + 写参 */ - kvlangKvPair_t pairs[512]; + kvlangKvPair_t pairs[MAX_BIND_PAIRS]; int np = 0; - int lit_seq = 0; - for (int i = 0; i < nr; i++) { - kvlangStrbuf_t slot; - kvlangStrbufInit(&slot); - kvlangStrbufPrintf(&slot, "%s/[0,-%d]", frame_root, i + 1); - if (i + 1 < inst->nr) { - kvlangParam_t *arg = &inst->reads[i + 1]; - bool concrete = !kvlangXvalueNone(&arg->val) && - !kvlangXvalueKindIs(&arg->val, KVSPACE_KIND_RWIR) && - !kvlangXvalueKindIs(&arg->val, KVSPACE_KIND_RWFUNC); - char lt[256] = {0}; - param_decl_type(kv, func_key, -(i + 1), lt, sizeof lt); - /* 传递方式由形参声明里的 `*` 定(见 spec [[函数]]):带 `*` 按地址(槽存实参地址 - * Ptr),不带按值(槽存实参值本体)。值传递直接落槽——字面量不必再造 ._litN 临时槽。 */ - if (lt[0] != '*') { - kvlangXvalue_t av; - kvlangXvalueZero(&av); - kvlangBuiltinResolveReadValue(kv, caller_fr, arg->name, &arg->val, &av); - if (!kvlangXvalueNone(&av)) { - pairs[np].key = kvlangStrbufDetach(&slot); - pairs[np].val = av; - np++; - } else { - kvlangStrbufFree(&slot); - } - continue; - } - char *rk = resolve_read_path(kv, caller_fr, arg->name); + int bind_error = 0; + for (int i = 0; i < nr && !bind_error; i++) { + if (i + 1 >= inst->nr) { + snprintf(err, sizeof err, "TypeError: missing read argument %d", i + 1); + bind_error = 1; + break; + } + kvlangParam_t *arg = &inst->reads[i + 1]; + char lt[256] = {0}; + param_decl_type(kv, func_key, -(i + 1), lt, sizeof lt); + bool concrete = !arg->address && !kvlangXvalueNone(&arg->val) && + !kvlangXvalueKindIs(&arg->val, KVSPACE_KIND_RWIR) && + !kvlangXvalueKindIs(&arg->val, KVSPACE_KIND_RWFUNC); + bool literal = concrete || is_literal(arg->name); + char *rk = literal ? NULL : resolve_read_path(kv, caller_fr, arg->name); + if (literal) { + kvlangStrbuf_t lit_path; + kvlangStrbufInit(&lit_path); + kvlangStrbufPrintf(&lit_path, "%s/._lit%d", frame_root, i); + rk = kvlangStrbufDetach(&lit_path); if (concrete) { - /* 字面量无变量槽,一律写 ._litN;勿沿用 resolve_read_path 的返回值—— - * 否则字面量内容(如 "https://x" 里的 //)会被当路径段,二次读回即丢。 */ - if (rk) + if (np >= MAX_BIND_PAIRS) { free(rk); - kvlangStrbuf_t lk; - kvlangStrbufInit(&lk); - kvlangStrbufPrintf(&lk, "%s/._lit%d", caller_fr, lit_seq++); - rk = kvlangStrbufDetach(&lk); - /* 写字面量到 rk(拷贝,避免 double-free) */ - kvspaceHead_t ah; - kvspaceDecodeHead(arg->val.data, arg->val.len, &ah); - int32_t abl; - const uint8_t *ab = kvlangXvalueBody(&arg->val, &ah, &abl); - kvlangLangtype akx; - kvlangLangtypeParse(ah.langtype, &akx); - pairs[np].key = strdup(rk); - kvspaceTlvEncode(kvlangXvalueKind(&arg->val), ab, (uint32_t)abl, - akx.dims, akx.ndim, &pairs[np].val.data, - &pairs[np].val.len); - np++; - } - if (rk) { - kvlangXvalue_t rv; - kvlangXvalueNewPtr(&rv, lt + 1, rk); - pairs[np].key = kvlangStrbufDetach(&slot); - pairs[np].val = rv; - np++; - free(rk); + snprintf(err, sizeof err, "TypeError: too many arguments"); + bind_error = 1; + break; + } + kvlangXvalue_t copy; + kvlangXvalueZero(©); + copy.data = malloc(arg->val.len); + if (!copy.data) { + free(rk); + snprintf(err, sizeof err, "OutOfMemory: argument %d", i + 1); + bind_error = 1; + break; + } + memcpy(copy.data, arg->val.data, arg->val.len); + copy.len = arg->val.len; + pairs[np++] = (kvlangKvPair_t){strdup(rk), copy}; } } - kvlangStrbufFree(&slot); + if (add_param_binding(pairs, &np, frame_root, -(i + 1), lt, rk) != 0) { + snprintf(err, sizeof err, "TypeError: cannot bind read argument %d", i + 1); + bind_error = 1; + } + free(rk); } - char *wds = NULL; /* 值写参目标表:第 i 行 = 第 i 个写参的目标 key(地址写参留空行) */ - for (int i = 0; i < nw; i++) { - kvlangStrbuf_t slot; - kvlangStrbufInit(&slot); - kvlangStrbufPrintf(&slot, "%s/[0,%d]", frame_root, i + 1); - char *wk = NULL; - if (i < inst->nw) - wk = resolve_read_path(kv, caller_fr, inst->writes[i].name); + for (int i = 0; i < nw && !bind_error; i++) { char lt[256] = {0}; param_decl_type(kv, func_key, i + 1, lt, sizeof lt); - const char *dst = NULL; - if (wk) { - if (lt[0] == '*' || lt[0] == '@') { - /* 地址写参:槽存调用方目标的地址 Ptr,体内 `*[0,+k]` 直写(现状) */ - kvlangXvalue_t wv; - kvlangXvalueNewPtr(&wv, lt + 1, wk); - pairs[np].key = kvlangStrbufDetach(&slot); - pairs[np].val = wv; - np++; - } else { - /* 值写参:copy-in —— 把调用方该位置当前值拷进槽(累加器据此拿初值), - * 目标 key 记进 ‥wdst,返回时 copy-out 拷回。 */ - kvlangXvalue_t cur; - kvlangXvalueZero(&cur); - kvlangKvGetOne(kv, wk, &cur); - if (!kvlangXvalueNone(&cur)) { - pairs[np].key = kvlangStrbufDetach(&slot); - pairs[np].val = cur; - np++; - } else { - kvlangStrbufFree(&slot); - } - dst = wk; - } - } else { - kvlangStrbufFree(&slot); - } - /* 逐写参追加一行(空行为地址写参),行序即槽序 */ - { - kvlangStrbuf_t wd; - kvlangStrbufInit(&wd); - if (wds) - kvlangStrbufPuts(&wd, wds); - if (i) - kvlangStrbufPutc(&wd, '\n'); - if (dst) - kvlangStrbufPuts(&wd, dst); - free(wds); - wds = kvlangStrbufDetach(&wd); + char *wk = i < inst->nw ? + resolve_read_path(kv, caller_fr, inst->writes[i].name) : NULL; + if (add_param_binding(pairs, &np, frame_root, i + 1, lt, wk) != 0) { + snprintf(err, sizeof err, "TypeError: cannot bind write argument %d", i + 1); + bind_error = 1; } free(wk); } - if (wds) { - /* 至少有一个值写参才落表;全空表不必落(省掉返回期的一次读) */ - if (strspn(wds, "\n") != strlen(wds)) { - kvlangStrbuf_t wk; - kvlangStrbufInit(&wk); - kvlangStrbufPrintf(&wk, "%s/", frame_root); - kvlangStrbufPuts(&wk, RUNTIME_MEMBER_SEP "wdst"); - char *wks = kvlangStrbufDetach(&wk); - kvlangXvalue_t wv; - kvlangXvalueNewCharUtf8(&wv, wds); - kvlangKvPair_t wp = { wks, wv }; - char werr[256]; - kvlangKvSet(kv, &wp, 1, werr, sizeof werr); - kvlangXvalueFree(&wv); - free(wks); - } - free(wds); - } - /* 实参绑定写失败必须报错——绝不静默:丢的是一整个参数,症状会漂到很远的地方 - * (曾表现为 fs 后端下函数读到空参数)。 */ - int wrc = np > 0 ? kvlangKvSet(kv, pairs, np, err, sizeof err) : 0; + int wrc = bind_error ? -1 : np > 0 ? kvlangKvSet(kv, pairs, np, err, sizeof err) : 0; for (int i = 0; i < np; i++) { free(pairs[i].key); kvlangXvalueFree(&pairs[i].val); } if (wrc != 0) { kvlangVthreadSetError(kv, vtid, pc, err); - goto fail; + goto fail_frame; } free(caller_fr); @@ -741,6 +561,16 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, free(frame_root); return ep; +fail_frame: + kvlangKvDelTree(kv, frame_root, err, sizeof err); + free(caller_fr); + free(stack_fr); + kvlangStrbufFree(&npc); + kvlangStrbufFree(&retpc); + kvlangStrbufFree(&callpc); + kvlangStrbufFree(&seglib); + free(ep); + free(frame_root); fail: kvlangStrbufFree(&func_dir); kvlangStrbufFree(&sig_key); @@ -752,9 +582,7 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, return NULL; } -/* 帧槽实参 Ptr 的类型来自**形参声明**:读参数定义键 funcDir.[0,±x] 的 body - * (名字\x00类型串),取出类型串。绝不从实参值的 head 反推——实参可以是 None - * (如递归传空子树),那时无值可取,Ptr 会因空 langtype 构造失败而整个参数丢失。 */ +/* Read the declared target type; the argument value may be None. */ static void param_decl_type(kvlangKv_t *kv, const char *func_key, int x, char *lt, size_t cap) { kvlangStrbuf_t pk; @@ -772,9 +600,6 @@ static void param_decl_type(kvlangKv_t *kv, const char *func_key, int x, for (int bi = 0; ab && bi < al; bi++) { if (ab[bi] != 0) continue; - /* 返回**原始声明串**(含 `*`/`@` 前缀):调用点据此判定传递方式——带 `*` - * 按地址(写实参地址 Ptr),不带按值(写值本体)。前缀只在构造 Ptr 时剥掉 - * (wire langtype 不含它,ref 归 head.ref)。 */ int tl = al - (bi + 1); if (tl > 0 && tl < (int)cap) { memcpy(lt, ab + bi + 1, (size_t)tl); @@ -801,11 +626,20 @@ int kvlangCtlReturn(kvlangFrame_t *f) { if (handle_return(f->kv, f->vtid, f->pc, &parent) != 0) return -1; if (!parent) { - kvlangVthreadSetDone(f->kv, f->vtid, "ok"); - return 0; + f->persist_failed = kvlangVthreadSetDone(f->kv, f->vtid, "ok") != 0; + } else { + kvlangVthreadAdvance(f, parent, "running"); } - kvlangVthreadAdvance(f, parent, "running"); free(parent); + if (f->persist_failed) + return -1; + char *fr = kvlangKeytreeFrameRoot(f->pc); + char *stk = kvlangKeytreeStack(fr); + char err[256]; + kvlangKvDelExtIndex(f->kv, stk, err, sizeof err); + kvlangKvDelTree(f->kv, fr, err, sizeof err); + free(stk); + free(fr); return 0; } @@ -866,12 +700,16 @@ int kvlangKvcpuDynCall(kvlangKv_t *kv, const char *vtid, const char *pc, ci.op_id = 0; ci.reads = malloc(sizeof(kvlangParam_t)); ci.reads[0].name = strdup(funckey); + ci.reads[0].type = NULL; + ci.reads[0].address = 0; kvlangXvalueZero(&ci.reads[0].val); ci.nr = 1; ci.writes = NULL; ci.nw = 0; kvlangFrame_t f = {kv, vtid, pc, &ci, NULL}; int rc = kvlangCtlCall(&f); + if (f.persist_failed) + rc = -1; free(ci.opcode); free(ci.reads[0].name); free(ci.reads); @@ -891,8 +729,13 @@ int handoff_external_rwir(kvlangKv_t *kv, const char *vtid, const char *pc, kvlangXvalueNewCharUtf8(&pv, pc); kvlangKvPair_t p = {vids.p, pv}; char err[256]; - kvlangKvSet(kv, &p, 1, err, sizeof err); + int set_rc = kvlangKvSet(kv, &p, 1, err, sizeof err); kvlangXvalueFree(&pv); + if (set_rc != 0) { + kvlangStrbufFree(&vids); + free(base); + return -1; + } kvlangXvalue_t none; kvlangXvalueZero(&none); /* 目标 None:等条目被删除 */ @@ -958,8 +801,19 @@ char *kvlangKvcpuBootstrap(kvlangKv_t *kv, const char *vtid, char *frame_root = kvlangKeytreeFrameAt(vtid, 1); char *stack_fr = kvlangKeytreeStack(frame_root); char err[256]; - kvlangKvMkindex(kv, stack_fr, 0, err, sizeof err); - kvlangKvExtIndex(kv, stack_fr, func_dir.p, err, sizeof err); + if (kvlangKvMkindex(kv, stack_fr, 0, err, sizeof err) != 0 || + kvlangKvExtIndex(kv, stack_fr, func_dir.p, err, sizeof err) != 0) { + kvlangVthreadSetError(kv, vtid, "", err); + kvlangXvalueFree(&sig); + kvlangStrbufFree(&sig_key); + kvlangStrbufFree(&func_dir); + free(stack_fr); + free(frame_root); + free(func_key); + free(pkg); + free(name); + return NULL; + } char *ep = kvlangKeytreeEntryPc(frame_root); kvlangStrbuf_t callpc; @@ -975,31 +829,56 @@ char *kvlangKvcpuBootstrap(kvlangKv_t *kv, const char *vtid, kvlangXvalueNewCharUtf8(&v_ep, ep); kvlangXvalueNewCharUtf8(&v_fn, func_key); kvlangKvPair_t sys[2] = {{callpc.p, v_ep}, {seglib.p, v_fn}}; - kvlangKvSet(kv, sys, 2, err, sizeof err); + int sys_rc = kvlangXvalueNone(&v_ep) || kvlangXvalueNone(&v_fn) ? -1 : + kvlangKvSet(kv, sys, 2, err, sizeof err); + if (sys_rc != 0) + snprintf(err, sizeof err, "RuntimeError: cannot persist entry frame"); kvlangXvalueFree(&v_ep); kvlangXvalueFree(&v_fn); - if (nargs > 0) { - kvlangKvPair_t pairs[128]; + int bind_error = sys_rc != 0; + if (!bind_error && (nargs < 0 || nargs > MAX_PARAMS || nr > MAX_PARAMS)) { + snprintf(err, sizeof err, "TypeError: too many entry parameters"); + bind_error = 1; + } + if (nargs > 0 && !bind_error) { + kvlangKvPair_t pairs[MAX_BIND_PAIRS]; int np = 0; for (int i = 0; i < nr && i < nargs; i++) { - kvlangStrbuf_t slot; - kvlangStrbufInit(&slot); - kvlangStrbufPrintf(&slot, "%s/[0,-%d]", frame_root, i + 1); + kvlangStrbuf_t literal; + kvlangStrbufInit(&literal); + kvlangStrbufPrintf(&literal, "%s/._arglit%d", frame_root, i); + char *value_key = kvlangStrbufDetach(&literal); kvlangXvalue_t av; kvlangXvalueZero(&av); kvlangBuiltinResolveReadValue(kv, "", args[i], NULL, &av); - pairs[np].key = kvlangStrbufDetach(&slot); - pairs[np].val = av; - np++; + if (!kvlangXvalueNone(&av)) + pairs[np++] = (kvlangKvPair_t){strdup(value_key), av}; + else + kvlangXvalueFree(&av); + char lt[256] = {0}; + param_decl_type(kv, func_key, -(i + 1), lt, sizeof lt); + if (add_param_binding(pairs, &np, frame_root, -(i + 1), lt, value_key) != 0) { + snprintf(err, sizeof err, "TypeError: cannot bind entry argument %d", i + 1); + bind_error = 1; + free(value_key); + break; + } + free(value_key); } - if (np > 0) - kvlangKvSet(kv, pairs, np, err, sizeof err); + if (!bind_error && np > 0 && kvlangKvSet(kv, pairs, np, err, sizeof err) != 0) + bind_error = 1; for (int i = 0; i < np; i++) { free(pairs[i].key); kvlangXvalueFree(&pairs[i].val); } } + if (bind_error) { + kvlangVthreadSetError(kv, vtid, "", err); + kvlangKvDelTree(kv, frame_root, err, sizeof err); + free(ep); + ep = NULL; + } kvlangXvalueFree(&sig); kvlangStrbufFree(&sig_key); @@ -1026,21 +905,22 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, return -1; } - char *cur = strdup(pc); - char *cur_frame = NULL, - *cur_funcdir = NULL; /* 帧不变时 funcdir 只读一次,供缓存键 */ - int cur_depth = 0; /* 帧深度,随 cur_frame 一起缓存 */ + char *cur = NULL; + char *cur_frame = NULL; int rc = 0; - /* status:每步从 kvspace 回读的**源值**,不当缓存用(下轮开头即重读)。 - * 同时借给本步的 kvlangFrame_t.status_known,供 Advance 判断是否真要写 status。 */ + /* Reload status each step. */ char *status = NULL; for (;;) { - /* 指令边界:回收上条指令执行期借出的读池(cache 指令的读参已 Materialize 自持,不受影响)。 - * durable 惰性写不再清池,全靠此处回收;shm 常驻映射侧为 no-op。 */ + /* Release borrowed values from the previous instruction. */ kvlangKvReadReset(kv); - /* 状态门(spec [[执行模型]] 步骤 1):每指令边界从 kvspace 回读 ‥status,只读 1 个 key - * (走 vtid key 缓存)。不得改用进程内副本——那是状态的第二份事实源:外部把 ‥status - * 置 paused/error(暂停、接管、终止)必须在下一步就被看见,且 kvspace 始终等于真实状态。 */ + free(cur); + cur = NULL; + kvlangVthreadPcGet(kv, vtid, &cur); + if (!cur || !cur[0]) { + rc = -1; + break; + } + /* Observe external status changes on every step. */ free(status); status = NULL; kvlangVthreadStatusGet(kv, vtid, &status); @@ -1050,19 +930,8 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, break; } - /* 帧变(call/return/goto 跨帧)才重算根/深度/funcdir;帧内每步只比较前缀,免分配。 */ - size_t frlen = kvlangKeytreeFrameRootLen(cur); - if (!cur_frame || strlen(cur_frame) != frlen || - strncmp(cur_frame, cur, frlen) != 0) { - free(cur_frame); - cur_frame = malloc(frlen + 1); - memcpy(cur_frame, cur, frlen); - cur_frame[frlen] = 0; - free(cur_funcdir); - cur_funcdir = read_seglib(kv, cur_frame); - cur_depth = kvlangKeytreeFrameNum(cur); - } - int depth = cur_depth; + cur_frame = kvlangKeytreeFrameRoot(cur); + int depth = kvlangKeytreeFrameNum(cur); if (depth > MAX_STACK_DEPTH) { char msg[256]; snprintf(msg, sizeof msg, @@ -1073,42 +942,20 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, break; } - const char *fr = cur_frame; /* 借用(随 cur_frame 缓存同步),循环内无需 free */ - const char *lastc = NULL; - for (const char *p = cur; (p = strstr(p, "/[")) != NULL; p += 2) - lastc = p; - int addr0 = lastc ? kvlangRwirExtractAddr0(lastc + 1) : 0; - + const char *fr = cur_frame; kvlangRwirInst_t tmp; - kvlangRwirInst_t *inst = NULL; - bool tmp_owned = false; - if (cur_funcdir) - inst = rwir_cache_get(cur_funcdir, addr0); - - if (!inst) { - char *link_base = kvlangKeytreeStack(fr); - char err[256]; - if (kvlangRwirDecode(kv, link_base, cur, &tmp, err, sizeof err) != - 0) { - char msg[256]; - snprintf(msg, sizeof msg, "decode: %s", err); - kvlangVthreadSetError(kv, vtid, cur, msg); - free(link_base); - rc = -1; - break; - } + char *link_base = kvlangKeytreeStack(fr); + char err[256]; + if (kvlangRwirDecode(kv, link_base, cur, &tmp, err, sizeof err) != 0) { + char msg[256]; + snprintf(msg, sizeof msg, "decode: %.247s", err); + kvlangVthreadSetError(kv, vtid, cur, msg); free(link_base); - if (cur_funcdir && tmp.opcode && tmp.opcode[0]) { - /* 转移所有权入缓存:冻结指令永不失效、永不 free */ - kvlangRwirInst_t *persist = malloc(sizeof *persist); - *persist = tmp; - rwir_cache_put(cur_funcdir, addr0, persist); - inst = persist; - } else { - inst = &tmp; - tmp_owned = true; - } + rc = -1; + break; } + free(link_base); + kvlangRwirInst_t *inst = &tmp; kvlangLogDebug("[%s] PC=%s OP=%s R=%d W=%d", vtid, cur, inst->opcode ? inst->opcode : "(empty)", inst->nr, @@ -1121,21 +968,21 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, snprintf(msg, sizeof msg, "RuntimeError: no instruction at %s", cur); kvlangVthreadSetError(kv, vtid, cur, msg); - if (tmp_owned) - kvlangRwirInstFree(&tmp); + kvlangRwirInstFree(&tmp); rc = -1; break; } int exec_err = 0; char *yield = NULL; - char *fb_pc = NULL; /* 本指令的 PC 回传槽(见 kvlangFrame_t),免写后回读 */ if (inst->op_id >= 0) { /* 单表派发:native 算子与 control/copy 同居 myrwircaps,op_id 直查一跳到底。 */ - kvlangFrame_t f = {kv, vtid, cur, inst, &yield, &fb_pc}; - f.frame_root = cur_frame; /* 复用循环缓存的帧根,免每步重算 */ + kvlangFrame_t f = {kv, vtid, cur, inst, &yield}; + f.frame_root = cur_frame; f.status_known = status; /* 本步开始前读到的 ‥status(源值,借用) */ exec_err = kvlangBuiltinNative(&f); + if (f.persist_failed) + exec_err = -1; if (exec_err == 0 && yield) { /* native(vthread·run return 模式)冒泡一个子 vthread 的 rwir pc 给上层驱动。 * 本 vthread(主)pc 未推进,驱动派发子 rwir 并推进子 pc 后重入即续跑。 */ @@ -1143,31 +990,29 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, *out_pc = yield; else free(yield); - if (tmp_owned) - kvlangRwirInstFree(&tmp); - free(fb_pc); + kvlangRwirInstFree(&tmp); free(cur); free(cur_frame); - free(cur_funcdir); free(status); kvlangStrbufFree(&vtid_b); return 1; } - } else if (opmeta_get(kv, inst->opcode)->notinmyrwircaps) { - opmeta_ent_t *m = opmeta_get(kv, inst->opcode); - if (m->def_sig) + } else if (notinmyrwircaps(kv, inst->opcode)) { + int def_nr = 0, def_dyn = 0; + char *rk = kvlangKeytreeRwir(inst->opcode); + char *def_sig = load_def_reads(kv, rk, &def_nr, &def_dyn); + free(rk); + if (def_sig) exec_err = check_read_types(kv, vtid, cur, inst->opcode, - m->def_sig, m->def_nr, m->def_dyn, + def_sig, def_nr, def_dyn, inst->reads, inst->nr); + free(def_sig); if (exec_err == 0 && mode == KVMODE_RETURN) { if (out_pc) *out_pc = strdup(cur); - if (tmp_owned) - kvlangRwirInstFree(&tmp); - free(fb_pc); + kvlangRwirInstFree(&tmp); free(cur); free(cur_frame); - free(cur_funcdir); free(status); kvlangStrbufFree(&vtid_b); return 1; @@ -1183,6 +1028,8 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, ci.nw = inst->nw; ci.reads = malloc(sizeof(kvlangParam_t) * (size_t)ci.nr); ci.reads[0].name = strdup(inst->opcode); + ci.reads[0].type = NULL; + ci.reads[0].address = 0; ci.reads[0].val.data = NULL; ci.reads[0].val.len = 0; for (int i = 0; i < inst->nr; i++) { @@ -1192,42 +1039,28 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, kvlangFrame_t cf = {kv, vtid, cur, &ci, NULL}; cf.status_known = status; exec_err = kvlangCtlCall(&cf); + if (cf.persist_failed) + exec_err = -1; free(ci.opcode); free(ci.reads[0].name); free(ci.reads); } if (exec_err != 0) { - if (tmp_owned) - kvlangRwirInstFree(&tmp); - free(fb_pc); + kvlangRwirInstFree(&tmp); rc = -1; break; } - char *newpc = NULL; - if (fb_pc) { - /* 本指令经帧回传了新 PC:直接用(它就是本指令刚写进 kvspace 的值)。 - * status 不回传——下一轮状态门照旧从 kvspace 回读。 */ - newpc = fb_pc; - fb_pc = NULL; - } else { - /* 无回传(ext rwir / 用户 call / done 等):从 kvspace 回读 PC。 */ - kvlangVthreadPcGet(kv, vtid, &newpc); - } - if (tmp_owned) - kvlangRwirInstFree(&tmp); - if (!newpc || !newpc[0]) { - free(newpc); - break; - } - free(cur); - cur = newpc; + kvlangRwirInstFree(&tmp); + free(cur_frame); + cur_frame = NULL; + free(status); + status = NULL; } free(cur); free(cur_frame); - free(cur_funcdir); free(status); kvlangStrbufFree(&vtid_b); return rc; diff --git a/runtime/src/kvspace.c b/runtime/src/kvspace.c index d67938ab..74c11f9d 100644 --- a/runtime/src/kvspace.c +++ b/runtime/src/kvspace.c @@ -1,346 +1,20 @@ #include "runtime_internal.h" -/* KV access via the kvspace C ABI. Backend is chosen at runtime from the DSN. */ - -static kvlangRefEnt_t *ref_find(kvlangKv_t *k, const char *key) { - for (int i = 0; i < k->nref; i++) { - if (k->ref[i].key && strcmp(k->ref[i].key, key) == 0) - return &k->ref[i]; - } - return NULL; -} - -static void ref_put(kvlangKv_t *k, const char *key, const kvspaceRef_t *r) { - kvlangRefEnt_t *e = ref_find(k, key); - if (!e) { - if (k->nref < KVLANG_REF_CAP) e = &k->ref[k->nref++]; - else { e = &k->ref[0]; free(e->key); } - e->key = strdup(key); - } - e->block_id = r->block_id; - e->gen = r->gen; -} - -static int ref_ok(kvlangKv_t *k) { - return k->ref_on && kvspaceResolveRef && kvspaceGetByRef; -} - -static int parent_ok(kvlangKv_t *k) { - return ref_ok(k) && k->parent_on; -} - -static kvlangRefEnt_t *pref_find(kvlangKv_t *k, const char *dir, size_t dl) { - for (int i = 0; i < k->npref; i++) { - const char *pk = k->pref[i].key; - if (pk && k->pref[i].gen && k->pref[i].klen == dl && memcmp(pk, dir, dl) == 0) - return &k->pref[i]; - } - return NULL; -} - -static void hot_clear(kvlangKv_t *k) { - for (int i = 0; i < k->nhot; i++) { - free(k->hot[i].name); - free(k->hot[i].key); - k->hot[i].name = k->hot[i].key = NULL; - k->hot[i].block_id = k->hot[i].gen = k->hot[i].dlen = 0; - } - k->nhot = 0; -} - -static int hot_name_ok(const char *name) { - char c; - if (!name || name[1] != 0) - return 0; - c = name[0]; - return c == 'a' || c == 'i' || c == 'n'; -} - -static inline int hot_get(kvlangKv_t *k, const char *dir, const char *name, - uint8_t **d, uint32_t *len) { - if (!dir || !hot_name_ok(name)) - return 0; - for (int i = 0; i < k->nhot; i++) { - uint32_t dl; - if (!k->hot[i].name || k->hot[i].name[0] != name[0]) - continue; - dl = k->hot[i].dlen; - if (!k->hot[i].key || strncmp(dir, k->hot[i].key, dl) != 0 || dir[dl] != 0) - continue; - kvspaceRef_t r = { k->hot[i].block_id, k->hot[i].gen, 0, 0 }; - if (kvspaceGetByRef(k->h, &r, k->hot[i].key, d, len) == 0 && *d && *len > 0) { - k->hot[i].block_id = r.block_id; - k->hot[i].gen = r.gen; - return 1; - } - } - return 0; -} - -/* Leaf refs only (gen==0). Skip /lib/. */ -static void hot_put(kvlangKv_t *k, const char *name, const char *key, - uint32_t block_id, uint32_t gen) { - kvlangHotEnt_t *e; - size_t kl; - if (!hot_name_ok(name) || !key || !block_id || gen != 0) - return; - if (strncmp(key, "/lib/", 5) == 0) - return; - kl = strlen(key); - if (kl < 1) - return; - for (int i = 0; i < k->nhot; i++) { - if (k->hot[i].name && k->hot[i].name[0] == name[0] && k->hot[i].name[1] == 0) { - if (k->hot[i].key && strcmp(k->hot[i].key, key) == 0) { - k->hot[i].block_id = block_id; - k->hot[i].gen = gen; - return; - } - /* Same name, other frame: keep the first key; `n` may use a second slot. */ - if (name[0] != 'n' || k->nhot >= KVLANG_HOT_CAP) - return; - break; - } - } - if (k->nhot < KVLANG_HOT_CAP) - e = &k->hot[k->nhot++]; - else { - e = &k->hot[KVLANG_HOT_CAP - 1]; - free(e->name); - free(e->key); - } - e->name = strdup(name); - e->key = strdup(key); - if (!e->name || !e->key) { - free(e->name); - free(e->key); - e->name = e->key = NULL; - if (k->nhot > 0 && e == &k->hot[k->nhot - 1]) - k->nhot--; - return; - } - e->block_id = block_id; - e->gen = gen; - e->dlen = (uint32_t)(kl - 1); -} - -static inline kvlangHotEnt_t *hot_find_key(kvlangKv_t *k, const char *key) { - if (!key) - return NULL; - for (int i = 0; i < k->nhot; i++) { - if (k->hot[i].key && k->hot[i].gen == 0 && - strcmp(k->hot[i].key, key) == 0) - return &k->hot[i]; - } - return NULL; -} - -static void parent_clear(kvlangKv_t *k) { - for (int i = 0; i < k->npref; i++) - free(k->pref[i].key); - k->npref = 0; - k->pref_i = 0; - free(k->fpar.key); - k->fpar.key = NULL; - k->fpar.block_id = k->fpar.gen = k->fpar.klen = 0; - hot_clear(k); -} - -static int dir_is_member(const char *dir, size_t dl) { - return dl >= MEMBER_SEP_LEN && - (unsigned char)dir[dl - 2] == 0xC2 && - (unsigned char)dir[dl - 1] == 0xB7; -} - -static void parent_put(kvlangKv_t *k, const char *dir, size_t dl, const kvspaceRef_t *rr) { - kvlangRefEnt_t *e; - if (!parent_ok(k) || !dir || !dl || !rr->parent_id || !rr->depth) - return; - if (dl >= 5 && memcmp(dir, "/lib/", 5) == 0) - return; - if (!dir_is_member(dir, dl)) { - if (k->fpar.key && (k->fpar.klen != dl || memcmp(k->fpar.key, dir, dl) != 0)) { - free(k->fpar.key); - k->fpar.key = NULL; - } - if (!k->fpar.key) { - k->fpar.key = malloc(dl + 1); - if (!k->fpar.key) - return; - memcpy(k->fpar.key, dir, dl); - k->fpar.key[dl] = 0; - } - k->fpar.block_id = rr->parent_id; - k->fpar.gen = rr->depth; - k->fpar.klen = (uint32_t)dl; - return; - } - e = pref_find(k, dir, dl); - if (!e) { - if (k->npref < KVLANG_PREF_CAP) - e = &k->pref[k->npref++]; - else { - e = &k->pref[k->pref_i]; - free(e->key); - k->pref_i = (k->pref_i + 1) % KVLANG_PREF_CAP; - } - e->key = malloc(dl + 1); - if (!e->key) - return; - memcpy(e->key, dir, dl); - e->key[dl] = 0; - } - e->block_id = rr->parent_id; - e->gen = rr->depth; - e->klen = (uint32_t)dl; -} - -static int last_dir_sep(const char *key, size_t *seplen) { - const char *slash = strrchr(key, '/'); - const char *start = slash ? slash + 1 : key; - const char *mid = NULL; - for (const char *p = start; *p; p++) { - if ((unsigned char)p[0] == 0xC2 && (unsigned char)p[1] == 0xB7) - mid = p; - } - if (mid) { - *seplen = MEMBER_SEP_LEN; - return (int)(mid - key); - } - if (slash && slash > key) { - *seplen = 1; - return (int)(slash - key); - } - *seplen = 0; - return -1; -} - -/* Nested key + parent_id==0 → backend only wrote leaf fields; disable parent cache. */ -static void parent_note(kvlangKv_t *k, const char *key, const kvspaceRef_t *rr) { - size_t seplen = 0; - if (k->parent_probed || !key) - return; - if (last_dir_sep(key, &seplen) < 0) - return; - k->parent_probed = 1; - if (!rr->parent_id) - k->parent_on = 0; -} - -static int parent_prefix_ok(const kvlangRefEnt_t *e, const char *key) { - size_t dl; - const char *p; - if (!e || !e->key || !e->gen || !key) - return 0; - dl = e->klen; - if (memcmp(key, e->key, dl) != 0 || !key[dl]) - return 0; - for (p = key + dl; *p; p++) { - if (*p == '/' || - ((unsigned char)p[0] == 0xC2 && (unsigned char)p[1] == 0xB7)) - return 0; - } - return 1; -} - -static kvlangRefEnt_t *pref_cover(kvlangKv_t *k, const char *key) { - if (!key) - return NULL; - for (int i = 0; i < k->npref; i++) { - if (parent_prefix_ok(&k->pref[i], key)) { - if (i != 0) { - kvlangRefEnt_t tmp = k->pref[0]; - k->pref[0] = k->pref[i]; - k->pref[i] = tmp; - } - return &k->pref[0]; - } - } - return NULL; -} - -static int parent_hit_ent(kvlangKv_t *k, const kvlangRefEnt_t *e, const char *key, - uint8_t **d, uint32_t *len) { - if (!parent_prefix_ok(e, key)) - return 0; - kvspaceRef_t r = { e->block_id, e->gen, 0, 0 }; - return kvspaceGetByRef(k->h, &r, key, d, len) == 0 && *d && *len > 0; -} - -static int parent_hit(kvlangKv_t *k, const char *key, uint8_t **d, uint32_t *len) { - return parent_hit_ent(k, pref_cover(k, key), key, d, len); -} - -void kvlangKvInvalidateFrame(kvlangKv_t *k, const char *fr) { - if (!k || !k->ref_on || !fr || !fr[0]) return; - size_t n = strlen(fr); - int w = 0; - for (int i = 0; i < k->nref; i++) { - char *key = k->ref[i].key; - if (key && strncmp(key, fr, n) == 0 && (key[n] == 0 || key[n] == '/')) { - free(key); - continue; - } - if (w != i) k->ref[w] = k->ref[i]; - w++; - } - k->nref = w; - { - int pw = 0; - for (int i = 0; i < k->npref; i++) { - char *key = k->pref[i].key; - if (key && strncmp(key, fr, n) == 0 && (key[n] == 0 || key[n] == '/')) { - free(key); - continue; - } - if (pw != i) - k->pref[pw] = k->pref[i]; - pw++; - } - k->npref = pw; - if (k->pref_i >= k->npref) - k->pref_i = 0; - } - if (k->fpar.key && strncmp(k->fpar.key, fr, n) == 0 && - (k->fpar.key[n] == 0 || k->fpar.key[n] == '/')) { - free(k->fpar.key); - k->fpar.key = NULL; - k->fpar.block_id = k->fpar.gen = k->fpar.klen = 0; - } - { - int hw = 0; - for (int i = 0; i < k->nhot; i++) { - char *key = k->hot[i].key; - if (key && strncmp(key, fr, n) == 0 && (key[n] == 0 || key[n] == '/')) { - free(k->hot[i].name); - free(key); - continue; - } - if (hw != i) - k->hot[hw] = k->hot[i]; - hw++; - } - k->nhot = hw; - } -} - kvlangKv_t *kvlangKvConnect(const char *dsn) { kvlangKv_t *k = calloc(1, sizeof(*k)); + if (!k) + return NULL; k->h = kvspaceConnect(dsn); if (!k->h) { free(k); return NULL; } - k->ref_on = 1; - k->parent_on = 1; return k; } void kvlangKvDisconnect(kvlangKv_t *k) { if (!k) return; - for (int i = 0; i < k->nref; i++) free(k->ref[i].key); - parent_clear(k); if (k->h) kvspaceClose(k->h); free(k); @@ -349,22 +23,8 @@ void kvlangKvDisconnect(kvlangKv_t *k) { /* Borrowed read (resolve=0). Empty -> out len=0. */ int kvlangKvGetOne(kvlangKv_t *k, const char *key, kvlangXvalue_t *out) { kvlangXvalueZero(out); - uint8_t *d; - uint32_t len; - if (parent_ok(k) && k->npref && key) { - kvlangRefEnt_t *pe = (k->npref == 1) - ? (parent_prefix_ok(&k->pref[0], key) ? &k->pref[0] : NULL) - : pref_cover(k, key); - if (pe) { - kvspaceRef_t r = { pe->block_id, pe->gen, 0, 0 }; - if (kvspaceGetByRef(k->h, &r, key, &d, &len) == 0 && d && len > 0) { - out->data = d; - out->len = len; - out->borrowed = 1; - return 0; - } - } - } + uint8_t *d = NULL; + uint32_t len = 0; if (kvspaceGet(k->h, key, 0, &d, &len) != 0) return -1; if (d && len > 0) { @@ -375,85 +35,24 @@ int kvlangKvGetOne(kvlangKv_t *k, const char *key, kvlangXvalue_t *out) { return 0; } -/* Frame member: concat dir+name, borrowed read. Empty -> out len=0. */ -int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXvalue_t *out) { +int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, + kvlangXvalue_t *out) { kvlangXvalueZero(out); - if (!name || !name[0]) - return 0; - uint8_t *d; - uint32_t len; - if (ref_ok(k) && dir && k->nhot && hot_get(k, dir, name, &d, &len)) { - out->data = d; - out->len = len; - out->borrowed = 1; + if (!dir || !name || !name[0]) return 0; - } size_t dl = strlen(dir), nl = strlen(name); - /* 短 key 走栈缓冲,免读参热路径每步一次 malloc。 */ + if (dl > SIZE_MAX - nl - 1) + return -1; char stackbuf[512]; - char *key = stackbuf; - if (dl + nl + 1 > sizeof stackbuf) { - key = malloc(dl + nl + 1); - if (!key) - return -1; - } + char *key = dl + nl + 1 <= sizeof stackbuf ? stackbuf : malloc(dl + nl + 1); + if (!key) + return -1; memcpy(key, dir, dl); - memcpy(key + dl, name, nl); - key[dl + nl] = 0; - if (parent_ok(k) && !hot_name_ok(name) && k->fpar.key && - memcmp(k->fpar.key, dir, k->fpar.klen) == 0 && dir[k->fpar.klen] == 0) { - kvspaceRef_t r = { k->fpar.block_id, k->fpar.gen, 0, 0 }; - if (kvspaceGetByRef(k->h, &r, key, &d, &len) == 0 && d && len > 0) { - out->data = d; - out->len = len; - out->borrowed = 1; - if (key != stackbuf) - free(key); - return 0; - } - } - kvlangRefEnt_t *e = ref_ok(k) ? ref_find(k, key) : NULL; - if (e) { - kvspaceRef_t r = { e->block_id, e->gen, 0, 0 }; - if (kvspaceGetByRef(k->h, &r, key, &d, &len) == 0 && d && len > 0) { - e->block_id = r.block_id; - e->gen = r.gen; - hot_put(k, name, key, r.block_id, r.gen); - out->data = d; - out->len = len; - out->borrowed = 1; - if (key != stackbuf) - free(key); - return 0; - } - } - if (parent_ok(k)) { - int hit = 0; - if (nl >= MEMBER_SEP_LEN && memchr(name, 0xC2, nl)) { - if (k->npref) - hit = parent_hit(k, key, &d, &len); - } else if (hot_name_ok(name) && k->fpar.key && - memcmp(k->fpar.key, dir, k->fpar.klen) == 0 && dir[k->fpar.klen] == 0) { - kvspaceRef_t r = { k->fpar.block_id, k->fpar.gen, 0, 0 }; - hit = kvspaceGetByRef(k->h, &r, key, &d, &len) == 0 && d && len > 0; - } - if (hit) { - out->data = d; - out->len = len; - out->borrowed = 1; - if (key != stackbuf) - free(key); - return 0; - } - } - if (kvspaceGet(k->h, key, 0, &d, &len) == 0 && d && len > 0) { - out->data = d; - out->len = len; - out->borrowed = 1; - } + memcpy(key + dl, name, nl + 1); + int rc = kvlangKvGetOne(k, key, out); if (key != stackbuf) free(key); - return 0; + return rc; } /* Recycle the read-borrow pool at instruction boundaries. */ @@ -485,103 +84,32 @@ int kvlangKvGetHead(kvlangKv_t *k, const char *key, kvspaceHead_t *out) { return kvspaceGetHead(k->h, key, out); } -/* Write each pair: same body_len -> in place, else new place. */ int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, uint32_t err_cap) { - int rc = 0; - if (n == 1 && ref_ok(k) && kvspaceSetPartByRef && pairs[0].key && pairs[0].val.data && - pairs[0].val.len) { - const char *key = pairs[0].key; - kvlangHotEnt_t *he = k->nhot ? hot_find_key(k, key) : NULL; - if (he) { - kvspaceRef_t r = { he->block_id, he->gen, 0, 0 }; - if (kvspaceSetPartByRef(k->h, &r, key, 0, pairs[0].val.data, - pairs[0].val.len, err, err_cap) == 0) { - he->block_id = r.block_id; - he->gen = r.gen; - return 0; - } - } - kvlangRefEnt_t *e = ref_find(k, key); - if (e) { - kvspaceRef_t r = { e->block_id, e->gen, 0, 0 }; - if (kvspaceSetPartByRef(k->h, &r, key, 0, pairs[0].val.data, - pairs[0].val.len, err, err_cap) == 0) { - e->block_id = r.block_id; - e->gen = r.gen; - { - const char *slash = strrchr(key, '/'); - const char *nm = slash ? slash + 1 : key; - hot_put(k, nm, key, r.block_id, r.gen); - } - return 0; - } - } - } for (int i = 0; i < n; i++) { const kvlangXvalue_t *v = &pairs[i].val; - if (!v->data || v->len == 0) { /* None deletes the key */ + if (kvlangXvalueNone(v)) { const char *dk[1] = {pairs[i].key}; - kvspaceDel(k->h, dk, 1, err, err_cap); - continue; - } - kvspaceHead_t h; - /* Skip only if head decode fails. Empty langtype is still a valid Ptr. */ - if (kvspaceDecodeHead(v->data, v->len, &h) != 0) + if (kvspaceDel(k->h, dk, 1, err, err_cap) != 0) + return -1; continue; - uint32_t body_len = h.body_len < 0 ? 0 : (uint32_t)h.body_len; - const uint8_t *body = v->data + h.body_offset; - uint8_t *dst = NULL; - if (kvspaceWriteInPlace(k->h, pairs[i].key, 0, body_len, &dst, err, err_cap) != 0) { - if (kvspaceWriteNewPlace(k->h, pairs[i].key, h.ref, h.storetype, h.ro, h.vid, (const char *)h.langtype, body_len, &dst, err, err_cap) != 0) { - rc = -1; - break; - } - } - if (body_len > 0 && dst) - memcpy(dst, body, body_len); - if (rc == 0 && n == 1 && ref_ok(k) && pairs[i].key) { - const char *key = pairs[i].key; - const char *slash = strrchr(key, '/'); - const char *rest = slash ? slash + 1 : key; - int is_member = rest && memchr(rest, 0xC2, strlen(rest)) != NULL; - if (is_member && (!parent_ok(k) || pref_cover(k, key) || - (key[0] == '/' && strncmp(key, "/lib/", 5) == 0))) - continue; - kvspaceRef_t rr = {0}; - if (kvspaceResolveRef(k->h, key, &rr) == 0) { - parent_note(k, key, &rr); - if (!is_member) { - ref_put(k, key, &rr); - hot_put(k, rest, key, rr.block_id, rr.gen); - } - if (parent_ok(k)) { - size_t seplen = 0; - int si = last_dir_sep(key, &seplen); - if (si >= 0) - parent_put(k, key, (size_t)si + seplen, &rr); - } - } } + if (kvspaceSetValue(k->h, pairs[i].key, v->data, v->len, 0, 0, + err, err_cap) != 0) + return -1; } - return rc; + return 0; } -/* 直写 char/utf8 值:跳过「TLV 编码 → KvSet 再解 head」的往返,直接向后端要 body 指针填字节。 - * 供 PC 推进热路径(每指令一次)用;语义等价于 NewCharUtf8 + KvSet。 */ int kvlangKvSetChar(kvlangKv_t *k, const char *key, const char *s) { - uint32_t len = (uint32_t)strlen(s); - uint8_t *dst = NULL; + kvlangXvalue_t value; + kvlangXvalueNewCharUtf8(&value, s); + if (!value.data || value.len == 0) + return -1; + kvlangKvPair_t pair = {(char *)key, value}; char err[256]; - if (kvspaceWriteInPlace(k->h, key, 0, len, &dst, err, sizeof err) != 0) { - if (kvspaceWriteNewPlace(k->h, key, KVSPACE_REF_INLINE, - KVSPACE_STORETYPE_ATOM, 0, 0, - KVSPACE_KIND_CHAR_UTF8, len, &dst, err, - sizeof err) != 0) - return -1; - } - if (len && dst) - memcpy(dst, s, len); - return 0; + int rc = kvlangKvSet(k, &pair, 1, err, sizeof err); + kvlangXvalueFree(&value); + return rc; } int kvlangKvDel(kvlangKv_t *k, const char *key, char *err, uint32_t err_cap) { @@ -590,7 +118,6 @@ int kvlangKvDel(kvlangKv_t *k, const char *key, char *err, uint32_t err_cap) { } int kvlangKvDelTree(kvlangKv_t *k, const char *prefix, char *err, uint32_t err_cap) { - kvlangKvInvalidateFrame(k, prefix); return kvspaceDelTree(k->h, prefix, err, err_cap); } diff --git a/runtime/src/langtype.c b/runtime/src/langtype.c index 1aefc3d4..fe044254 100644 --- a/runtime/src/langtype.c +++ b/runtime/src/langtype.c @@ -35,9 +35,8 @@ static bool known_kind(const char *s, size_t len) { kind_eq(s, len, KVSPACE_KIND_CHAR) || kind_eq(s, len, KVSPACE_KIND_CHAR_UTF8) || kind_eq(s, len, KVSPACE_KIND_CHAR_ASCII) || kind_eq(s, len, KVSPACE_KIND_MAP) || - kind_eq(s, len, KVSPACE_KIND_INDEX) || kind_eq(s, len, KVSPACE_KIND_EXT_INDEX) || kind_eq(s, len, KVSPACE_KIND_RWIR) || kind_eq(s, len, KVSPACE_KIND_RWFUNC) || - kind_eq(s, len, KVSPACE_KIND_SCOPE) || kind_eq(s, len, KVSPACE_KIND_STRUCT) || + kind_eq(s, len, KVSPACE_KIND_SCOPE) || kind_eq(s, len, KVSPACE_KIND_DEF_STRUCT) || kind_eq(s, len, KVSPACE_KIND_TIME) || kind_eq(s, len, KVSPACE_KIND_DURATION); } @@ -207,4 +206,3 @@ bool kvlangLangtypeMatch(const char *expr, const char *kind, int32_t ndim, const } return false; } - diff --git a/runtime/src/langtypetable.c b/runtime/src/langtypetable.c index a55de1b0..61febdc9 100644 --- a/runtime/src/langtypetable.c +++ b/runtime/src/langtypetable.c @@ -27,12 +27,10 @@ static const lt_ent_t LANGTYPETABLE[] = { LT(KVSPACE_KIND_CHAR_UTF8, KVLANG_LT_CHAR_UTF8), LT(KVSPACE_KIND_CHAR_ASCII, KVLANG_LT_CHAR_ASCII), LT(KVSPACE_KIND_MAP, KVLANG_LT_MAP), - LT(KVSPACE_KIND_INDEX, KVLANG_LT_INDEX), - LT(KVSPACE_KIND_EXT_INDEX, KVLANG_LT_EXTINDEX), LT(KVSPACE_KIND_RWIR, KVLANG_LT_RWIR), LT(KVSPACE_KIND_RWFUNC, KVLANG_LT_RWFUNC), LT(KVSPACE_KIND_SCOPE, KVLANG_LT_SCOPE), - LT(KVSPACE_KIND_STRUCT, KVLANG_LT_STRUCT), + LT(KVSPACE_KIND_DEF_STRUCT, KVLANG_LT_STRUCT), LT(KVSPACE_KIND_TIME, KVLANG_LT_TIME), LT(KVSPACE_KIND_DURATION, KVLANG_LT_DURATION), LT(KVSPACE_KIND_NONE, KVLANG_LT_NONE), diff --git a/runtime/src/logx.c b/runtime/src/logx.c index 32280778..f73f6c59 100644 --- a/runtime/src/logx.c +++ b/runtime/src/logx.c @@ -1,4 +1,3 @@ -#define _GNU_SOURCE #include #include /* getprogname(macOS/BSD) */ #include "runtime_internal.h" diff --git a/runtime/src/myrwircaps.c b/runtime/src/myrwircaps.c index 4af96b3c..18791d16 100644 --- a/runtime/src/myrwircaps.c +++ b/runtime/src/myrwircaps.c @@ -4,42 +4,48 @@ typedef int (*kvlangBuiltinFn)(kvlangFrame_t *f); -/* 共享队列根:第一个 rwir 的 /lib//vids 绝对路径。 */ -static char *g_first_vids = NULL; - -/* 建立 rwir 的 vids 队列:第一个 rwir 是真实 strkeymap,后续是 Ptr 指向第一个。 - * 幂等:vids 已存在则跳过——否则脏 kvspace 上重复注册时,Set 经路径穿透会把首队列 - * 改成自指 Ptr,令 resolve_path 死循环(父子 kvlang 共享同一 redis 的挂起根因)。 */ -static void register_vids(kvlangKv_t *k, const char *opcode) { +/* Keep the shared queue in KVSpace. */ +static int register_vids(kvlangKv_t *k, const char *opcode) { + char queue[] = "/vthread/‥vids"; + const char *queue_type = "[]char/utf32·[]char/utf32"; + kvlangXvalue_t root; + kvlangXvalueZero(&root); + bool root_exists = kvlangKvGetOne(k, queue, &root) == 0 && !kvlangXvalueNone(&root); + kvlangXvalueFree(&root); + if (!root_exists) { + kvlangXvalue_t value; + kvlangXvalueZero(&value); + kvlangXvalueNewTlv(&value, queue_type, NULL, 0, 1); + kvlangKvPair_t pair = {queue, value}; + char err[256]; + int rc = kvlangKvSet(k, &pair, 1, err, sizeof err); + kvlangXvalueFree(&value); + if (rc != 0) + return rc; + } char *base = kvlangKeytreeRwir(opcode); kvlangStrbuf_t tk; kvlangStrbufInit(&tk); kvlangStrbufPuts(&tk, base); kvlangStrbufPuts(&tk, "/vids"); - bool first = (g_first_vids == NULL); - if (first) - g_first_vids = strdup(tk.p); kvlangXvalue_t cur; kvlangXvalueZero(&cur); bool exists = (kvlangKvGetOne(k, tk.p, &cur) == 0 && !kvlangXvalueNone(&cur)); kvlangXvalueFree(&cur); + int rc = 0; if (!exists) { kvlangXvalue_t v; kvlangXvalueZero(&v); - int32_t dims[1] = {0}; - if (first) - kvlangXvalueNewTlvDims(&v, KVSPACE_KIND_MAP, (const uint8_t *)"", 0, - dims, 1); - else - kvlangXvalueNewPtr(&v, KVSPACE_KIND_MAP, g_first_vids); + kvlangXvalueNewPtr(&v, queue_type, queue); kvlangKvPair_t p = {tk.p, v}; char err[256]; - kvlangKvSet(k, &p, 1, err, sizeof err); + rc = kvlangKvSet(k, &p, 1, err, sizeof err); kvlangXvalueFree(&v); } kvlangStrbufFree(&tk); free(base); + return rc; } /* 写 [0,x] 签名行槽 = def langtype(body 为该参数 langtype 串)。 */ @@ -89,8 +95,9 @@ int kvlangDefRwir(void *kvspace, const char *opcode, const char *const *rp, write_sig_slot(&k, base, i + 1, wp[i], strlen(wp[i])); free(base); - /* 建立共享 vids 队列(第一个真实 strkeymap,后续 Ptr 指向它;幂等查 kvspace)。 */ - register_vids(&k, opcode); + /* Register the shared KVSpace queue. */ + if (rc == 0) + rc = register_vids(&k, opcode); return rc; } diff --git a/runtime/src/runtime.c b/runtime/src/runtime.c index 3341406a..5ebf5c84 100644 --- a/runtime/src/runtime.c +++ b/runtime/src/runtime.c @@ -10,6 +10,10 @@ kvlangRuntime_t *kvlangRuntimeConnect(const char *dsn) { if (!kv) return NULL; kvlangRuntime_t *rt = malloc(sizeof(*rt)); + if (!rt) { + kvlangKvDisconnect(kv); + return NULL; + } rt->kv = kv; return rt; } @@ -68,10 +72,11 @@ static char *alloc_vtid(kvlangKv_t *kv) { kvlangXvalueNewCharUtf8(&nv, buf); kvlangKvPair_t p = {seq.p, nv}; char err[256]; - kvlangKvSet(kv, &p, 1, err, sizeof err); + int rc = kvlangXvalueNone(&nv) ? -1 : + kvlangKvSet(kv, &p, 1, err, sizeof err); kvlangXvalueFree(&nv); kvlangStrbufFree(&seq); - return strdup(buf); + return rc == 0 ? strdup(buf) : NULL; } /* 创建一个 vthread(分配 vid + 建栈索引 + bootstrap 首指令 + 置 init),返回 @@ -80,12 +85,19 @@ static char *alloc_vtid(kvlangKv_t *kv) { char *kvlangVthreadSpawn(kvlangKv_t *kv, const char *funcname, const char *const *args, int nargs) { char *vtid = alloc_vtid(kv); + if (!vtid) + return NULL; kvlangStrbuf_t vtroot; kvlangStrbufInit(&vtroot); kvlangKeytreeVthread(vtid, &vtroot); char *stack_vt = kvlangKeytreeStack(vtroot.p); char e[256]; - kvlangKvMkindex(kv, stack_vt, 0, e, sizeof e); + if (kvlangKvMkindex(kv, stack_vt, 0, e, sizeof e) != 0) { + free(stack_vt); + free(vtid); + kvlangStrbufFree(&vtroot); + return NULL; + } char *first_pc = kvlangKvcpuBootstrap(kv, vtid, funcname, args, nargs); if (!first_pc) { free(stack_vt); @@ -93,10 +105,14 @@ char *kvlangVthreadSpawn(kvlangKv_t *kv, const char *funcname, kvlangStrbufFree(&vtroot); return NULL; } - kvlangVthreadSet(kv, vtid, first_pc, "init"); + int persisted = kvlangVthreadSet(kv, vtid, first_pc, "init"); free(first_pc); free(stack_vt); kvlangStrbufFree(&vtroot); + if (persisted != 0) { + free(vtid); + return NULL; + } return vtid; } diff --git a/runtime/src/runtime_internal.h b/runtime/src/runtime_internal.h index 600aced7..c9a56cfe 100644 --- a/runtime/src/runtime_internal.h +++ b/runtime/src/runtime_internal.h @@ -8,63 +8,35 @@ #include #include -/* ── kvspace-durable C ABI ─────────────────────────────────────────── */ - -/* 三正交轴 head(逐字段对齐 kvspace/include/kvspace/kvspace.h 的 kvspaceHead_t)。 - * langtype 即该 ABI 的 langtype 槽(本 runtime 内部沿用 langtype 命名)。 */ +/* Match kvspaceHead_t in the KVSpace C ABI. */ typedef struct { - uint16_t headlen; /* head 总字节数;body 起于偏移 headlen */ - uint8_t ref; /* 存储位置:见 KVSPACE_REF_* */ - uint8_t storetype; /* 物理布局:见 KVSPACE_STORETYPE_* */ - uint8_t ro; /* 1=只读,0=可写 */ - uint32_t vid; /* vthread id */ - int32_t body_len; /* body 字节数 */ - int32_t ndim; /* ARRAYND:维数;index/extindex:3;NONE/ATOM:0 */ - int32_t dims[8]; /* 各维长度 / [len,cap,M](X_MAX_NDIM=8) */ - uint8_t langtype - [256]; /* 语义类型 langtype 串,NUL 终止(含 [dims]、无 ref/ext 前缀) */ - int32_t langtype_len; /* langtype 内容长度(去 padding) */ - int32_t body_offset; /* body 在 data 内的起始偏移(= headlen) */ + uint16_t headlen; + uint8_t ref; + uint8_t storetype; + uint8_t ro; + uint32_t vid; + int32_t body_len; + int32_t ndim; + int32_t dims[8]; + uint8_t langtype[256]; + int32_t langtype_len; + int32_t body_offset; + uint64_t body_cap; } kvspaceHead_t; #define KVSPACE_REF_INLINE 0 #define KVSPACE_REF_PTR 1 #define KVSPACE_REF_EXT 2 -#define KVSPACE_STORETYPE_NONE 0 -#define KVSPACE_STORETYPE_ATOM 1 -#define KVSPACE_STORETYPE_ARRAYND 2 -#define KVSPACE_STORETYPE_INDEX 3 -#define KVSPACE_STORETYPE_EXTINDEX 4 - extern void *kvspaceConnect(const char *dsn); extern void kvspaceClose(void *h); /* 借用读:*out 指向后端常驻/回收空间,调用方不得 free。resolve=1 穿透 link。 */ extern int kvspaceGet(void *h, const char *key, int resolve, uint8_t **out, uint32_t *out_len); +extern int kvspaceSetValue(void *h, const char *key, const uint8_t *value, + uint32_t value_len, uint8_t ro, uint32_t vid, + char *err, uint32_t err_cap); -/* 对齐 kvspace/include/kvspace/kvspace.h。parent_id/depth 由 ResolveRef 填; - * 后端只写前 8 字节时 parent_id 保持 0,runtime 永久关闭父缓存。 */ -typedef struct { - uint32_t block_id; - uint32_t gen; - uint32_t parent_id; - uint32_t depth; -} kvspaceRef_t; -#if defined(__APPLE__) -#define KVLANG_KVSPACE_WEAK __attribute__((weak_import)) -#else -#define KVLANG_KVSPACE_WEAK __attribute__((weak)) -#endif -extern int kvspaceResolveRef(void *h, const char *key, kvspaceRef_t *ref) - KVLANG_KVSPACE_WEAK; -extern int kvspaceGetByRef(void *h, kvspaceRef_t *ref, const char *key_fallback, - uint8_t **out, uint32_t *out_len) - KVLANG_KVSPACE_WEAK; -extern int kvspaceSetPartByRef(void *h, kvspaceRef_t *ref, - const char *key_fallback, uint32_t offset, - const uint8_t *buf, uint32_t buf_len, char *err, - uint32_t err_cap) KVLANG_KVSPACE_WEAK; /* 指令边界回收读借用池;定位读/写(分片);只读 head 前缀。见 kvspace.h 契约。 */ extern void kvspaceReadReset(void *h); extern int kvspaceGetPart(void *h, const char *key, uint32_t offset, @@ -73,15 +45,6 @@ extern int kvspaceSetPart(void *h, const char *key, uint32_t offset, const uint8_t *buf, uint32_t buf_len, char *err, uint32_t err_cap); extern int kvspaceGetHead(void *h, const char *key, kvspaceHead_t *out); -/* 就地写:key 已存在、body_len==原 body_len → 返回原 box body 偏移指针;否则非 0 + err。 */ -extern int kvspaceWriteInPlace(void *h, const char *key, int resolve, - uint32_t body_len, uint8_t **body, char *err, - uint32_t err_cap); -/* 新位置写:按 (ref, storetype, ro, vid, langtype, body_len) 分配新 box、写 head,返回 body 偏移指针。 */ -extern int kvspaceWriteNewPlace(void *h, const char *key, uint8_t ref, - uint8_t storetype, uint8_t ro, uint32_t vid, - const char *langtype, uint32_t body_len, - uint8_t **body, char *err, uint32_t err_cap); /* 前缀遍历:listlen 定计数,逐 idx 取名(借用回收缓冲,不得 free),不一次性返回整段名单。 */ extern int kvspaceListLen(void *h, const char *prefix, int expand_ext, int resolve, int32_t *out_count); @@ -151,24 +114,8 @@ typedef struct { kvlangXvalue_t val; } kvlangKvPair_t; -#define KVLANG_REF_CAP 64 -#define KVLANG_PREF_CAP 5 -#define KVLANG_HOT_CAP 4 -typedef struct { char *key; uint32_t block_id, gen, klen; } kvlangRefEnt_t; -typedef struct { char *name; char *key; uint32_t block_id, gen, dlen; } kvlangHotEnt_t; typedef struct { void *h; - kvlangRefEnt_t ref[KVLANG_REF_CAP]; - int nref; - int ref_on; - int parent_on; /* 嵌套 ResolveRef 后 parent_id==0 则永久关闭 */ - int parent_probed; - kvlangRefEnt_t pref[KVLANG_PREF_CAP]; /* · map ART parents */ - int npref; - int pref_i; - kvlangRefEnt_t fpar; /* frame `/` parent for GetMember siblings */ - kvlangHotEnt_t hot[KVLANG_HOT_CAP]; /* a/i/n leaf refs */ - int nhot; } kvlangKv_t; /* growable string buffer */ @@ -198,7 +145,13 @@ static inline void kvlangStrbufFree(kvlangStrbuf_t *b) { /* ── XValue 操作 ───────────────────────────────────────────────────── */ static inline bool kvlangXvalueNone(const kvlangXvalue_t *v) { - return v->data == NULL || v->len == 0; + if (!v->data || v->len == 0) + return true; + if (v->len != 32 || v->data[0] != 5 || v->data[1] != 0 || + v->data[18] != 0) + return false; + kvspaceHead_t h; + return kvspaceDecodeHead(v->data, v->len, &h) == 0 && h.langtype_len == 0; } static inline void kvlangXvalueZero(kvlangXvalue_t *v) { v->data = NULL; @@ -253,8 +206,6 @@ enum { KVLANG_LT_CHAR_UTF8, KVLANG_LT_CHAR_ASCII, KVLANG_LT_MAP, - KVLANG_LT_INDEX, - KVLANG_LT_EXTINDEX, KVLANG_LT_RWIR, KVLANG_LT_RWFUNC, KVLANG_LT_SCOPE, @@ -379,7 +330,6 @@ int kvlangKvSetChar(kvlangKv_t *k, const char *key, const char *s); int kvlangKvDel(kvlangKv_t *k, const char *key, char *err, uint32_t err_cap); int kvlangKvDelTree(kvlangKv_t *k, const char *prefix, char *err, uint32_t err_cap); -void kvlangKvInvalidateFrame(kvlangKv_t *k, const char *frame_root); int kvlangKvCp(kvlangKv_t *k, const char *src, const char *dst, char *err, uint32_t err_cap); int kvlangKvCpTree(kvlangKv_t *k, const char *src, const char *dst, char *err, @@ -421,7 +371,6 @@ const char *kvlangKeytreeVtidFromPc(const char *pc, char *kvlangKeytreeStack(const char *root); /* malloc */ size_t kvlangKeytreeStackBuf(const char *root, char *buf, size_t cap); /* 栈缓冲,返长度 */ char *kvlangKeytreeFrameRoot(const char *pc); /* malloc,无效 NULL */ -size_t kvlangKeytreeFrameRootLen(const char *pc); /* 帧根长度,无分配 */ char *kvlangKeytreeEntryPc(const char *root); /* malloc */ char *kvlangKeytreeFrameAt(const char *vtid, int depth); /* malloc */ int kvlangKeytreeFrameNum(const char *path); /* [d]; panics if invalid */ @@ -469,6 +418,8 @@ static inline int kvlangOpClassify(const char *op) { typedef struct { char *name; + char *type; + int address; kvlangXvalue_t val; } kvlangParam_t; @@ -501,33 +452,27 @@ void kvlangVthreadGet(kvlangKv_t *kv, const char *vtid, char **pc, char **status); void kvlangVthreadPcGet(kvlangKv_t *kv, const char *vtid, char **pc); void kvlangVthreadStatusGet(kvlangKv_t *kv, const char *vtid, char **status); -void kvlangVthreadSet(kvlangKv_t *kv, const char *vtid, const char *pc, - const char *status); -void kvlangVthreadSetDone(kvlangKv_t *kv, const char *vtid, const char *ret); +int kvlangVthreadSet(kvlangKv_t *kv, const char *vtid, const char *pc, + const char *status); +int kvlangVthreadSetDone(kvlangKv_t *kv, const char *vtid, const char *ret); void kvlangVthreadSetError(kvlangKv_t *kv, const char *vtid, const char *pc, const char *msg); /* ── builtin ───────────────────────────────────────────────────────── */ -/* yield_pc:native builtin 把「须交回上层驱动就地派发的 pc」写入 *yield_pc(否则留 NULL)。 - * 唯 vthread·run 的 return 模式用:驱动一个子 vthread 遇非本执行器 rwir 时,把其 pc 冒泡给驱动。 - * fb_pc:主循环给的 **PC 回传槽**(非 NULL 时)。新 PC 照旧先写 kvspace(崩溃恢复),同时回传此槽, - * 令主循环直接用之、免掉「刚写就回读」的那次后端 Get + 路径重建;回传值就是本指令自己刚写进去的 - * 值,不引入第二份事实源。**status 不回传**——状态门每步从 kvspace 回读(见 kvlangVthreadAdvance)。 - * fb_pc 由 Advance malloc 写入、循环接管所有权。 */ +/* yield_pc returns a child PC to the external runtime. */ typedef struct { kvlangKv_t *kv; const char *vtid; const char *pc; kvlangRwirInst_t *inst; char **yield_pc; - char **fb_pc; - const char *frame_root; /* 主循环已缓存的帧根(借用);NULL 时各 helper 自行计算 */ + const char *frame_root; /* Borrowed for this instruction. */ const char *status_known; /* 本步开始前从 kvspace 读到的 ‥status(借用);NULL = 未知 */ + bool persist_failed; } kvlangFrame_t; -/* 循环内 PC/status 推进:PC 恒写 kvspace;status 与 f->status_known(来自 kvspace 的**源值**) - * 不同才写——绝不拿进程内副本当依据,故不会与 kvspace 分叉;fb_pc 非 NULL 时回传新 PC。 */ +/* Write PC to kvspace; write status when it changes. */ void kvlangVthreadAdvance(kvlangFrame_t *f, const char *pc, const char *status); /* notinmycaps:查 myrwircaps table,opcode 不在本 runtime 能力表内 → true。 */ diff --git a/runtime/src/rwir_array.c b/runtime/src/rwir_array.c index 68053af9..8ab34379 100644 --- a/runtime/src/rwir_array.c +++ b/runtime/src/rwir_array.c @@ -40,6 +40,55 @@ static int separated_len(kvlangKv_t *kv, const char *base) { } } +static int64_t separated_next(kvlangKv_t *kv, const char *base) { + char *dir = kvlangKeytreeMember(base, ""); + char **names = NULL; + int count = 0; + int64_t next = 0; + if (kvlangKvList(kv, dir, false, false, &names, &count) == 0) { + for (int i = 0; i < count; i++) { + char *end = NULL; + if (names[i][0] == '[') { + long long index = strtoll(names[i] + 1, &end, 10); + if (end && *end == ']' && end[1] == 0 && index >= 0 && + index < INT64_MAX && index + 1 > next) + next = index + 1; + } + free(names[i]); + } + free(names); + } + free(dir); + return next; +} + +static int clear_members(kvlangFrame_t *f, const char *base) { + char *prefix = kvlangKeytreeMember(base, ""); + char **names = NULL; + int count = 0; + if (kvlangKvList(f->kv, prefix, false, false, &names, &count) != 0) { + free(prefix); + return kvlangBuiltinSetErr(f, "array.scatter: cannot list members"); + } + char err[256]; + int rc = 0; + for (int i = 0; i < count; i++) { + if (rc == 0) { + kvlangStrbuf_t key; + kvlangStrbufInit(&key); + kvlangStrbufPuts(&key, prefix); + kvlangStrbufPuts(&key, names[i]); + if (kvlangKvDelTree(f->kv, key.p, err, sizeof err) != 0) + rc = kvlangBuiltinSetErr(f, "%s", err); + kvlangStrbufFree(&key); + } + free(names[i]); + } + free(names); + free(prefix); + return rc; +} + /* 坐标段 key:base·[s0,s1,...]。1 维即 base·[s0]。 */ char *kvlangBuiltinScatterKey(const char *base, const int64_t *coords, int ncoord) { @@ -57,14 +106,32 @@ char *kvlangBuiltinScatterKey(const char *base, const int64_t *coords, return kvlangStrbufDetach(&b); } -static void ensure_scattered(kvlangFrame_t *f, const char *base) { +static int ensure_scattered(kvlangFrame_t *f, const char *base, const char *fallback_kind) { kvlangXvalue_t arr; kvlangXvalueZero(&arr); kvlangKvGetOne(f->kv, base, &arr); - if (kvlangXvalueNone(&arr) || - kvlangXvalueElemSize(kvlangXvalueKind(&arr)) <= 0) { + kvlangXvalueMaterialize(&arr); + bool missing = kvlangXvalueNone(&arr); + const char *kind = missing ? fallback_kind : kvlangXvalueKind(&arr); + if (!kind || kvlangXvalueElemSize(kind) <= 0) { kvlangXvalueFree(&arr); - return; + return missing ? kvlangBuiltinSetErr(f, "array: missing element type") : 0; + } + char ty[256]; + snprintf(ty, sizeof ty, "[int64]%s%s", MEMBER_SEP, kind); + kvlangXvalue_t mark; + kvlangBuiltinMapMarker(&mark, ty); + kvlangKvPair_t head = {(char *)base, mark}; + char err[256]; + int rc = kvlangKvSet(f->kv, &head, 1, err, sizeof err); + kvlangXvalueFree(&mark); + if (rc != 0) { + kvlangXvalueFree(&arr); + return kvlangBuiltinSetErr(f, "%s", err); + } + if (missing) { + kvlangXvalueFree(&arr); + return 0; } int n = kvlangXvalueArrayLen(&arr); for (int i = 0; i < n; i++) { @@ -73,14 +140,16 @@ static void ensure_scattered(kvlangFrame_t *f, const char *base) { kvlangXvalue_t e; kvlangBuiltinXvalueAt(&arr, i, &e); kvlangKvPair_t p = {k, e}; - char err[256]; - kvlangKvSet(f->kv, &p, 1, err, sizeof err); + rc = kvlangKvSet(f->kv, &p, 1, err, sizeof err); kvlangXvalueFree(&e); free(k); + if (rc != 0) { + kvlangXvalueFree(&arr); + return kvlangBuiltinSetErr(f, "%s", err); + } } kvlangXvalueFree(&arr); - char err[256]; - kvlangKvDel(f->kv, base, err, sizeof err); + return 0; } int kvlangBuiltinArray(kvlangFrame_t *f) { @@ -196,19 +265,28 @@ int kvlangBuiltinScatter(kvlangFrame_t *f) { char *fr = kvlangKeytreeFrameRoot(f->pc); char *dst = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); + int rc = clear_members(f, dst); + if (rc != 0) { + free(dst); + free(fr); + kvlangBuiltinFreeInputs(in, n); + return rc; + } int al = kvlangXvalueArrayLen(&in[0]); - if (al > 0) { - char err[256]; - int32_t dims[1] = {al}; - /* 散 key 化的容器值类型:键是坐标段 `[i]`,值是原 compact 元素类型(逐元素原样搬)。 */ - char ty[256]; - snprintf(ty, sizeof ty, "[int64]%s%s", MEMBER_SEP, - kvlangXvalueKind(&in[0])); - kvlangXvalue_t mark; - kvlangBuiltinMapMarker(&mark, ty, dims, 1); - kvlangKvPair_t p0 = {dst, mark}; - kvlangKvSet(f->kv, &p0, 1, err, sizeof err); - kvlangXvalueFree(&mark); + char err[256]; + char ty[256]; + snprintf(ty, sizeof ty, "[int64]%s%s", MEMBER_SEP, + kvlangXvalueKind(&in[0])); + kvlangXvalue_t mark; + kvlangBuiltinMapMarker(&mark, ty); + kvlangKvPair_t p0 = {dst, mark}; + rc = kvlangKvSet(f->kv, &p0, 1, err, sizeof err); + kvlangXvalueFree(&mark); + if (rc != 0) { + free(dst); + free(fr); + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr(f, "%s", err); } for (int i = 0; i < al; i++) { int64_t c[1] = {i}; @@ -216,10 +294,15 @@ int kvlangBuiltinScatter(kvlangFrame_t *f) { kvlangXvalue_t e; kvlangBuiltinXvalueAt(&in[0], i, &e); kvlangKvPair_t p = {k, e}; - char err[256]; - kvlangKvSet(f->kv, &p, 1, err, sizeof err); + rc = kvlangKvSet(f->kv, &p, 1, err, sizeof err); kvlangXvalueFree(&e); free(k); + if (rc != 0) { + free(dst); + free(fr); + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr(f, "%s", err); + } } free(dst); free(fr); @@ -284,19 +367,25 @@ int kvlangBuiltinAppend(kvlangFrame_t *f) { char *fr = kvlangKeytreeFrameRoot(f->pc); char *base = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); - ensure_scattered(f, base); - int len = separated_len(f->kv, base); - int64_t c[1] = {len}; + int rc = ensure_scattered(f, base, n >= 2 ? kvlangXvalueKind(&in[1]) : NULL); + if (rc != 0) { + free(base); + free(fr); + kvlangBuiltinFreeInputs(in, n); + return rc; + } + int64_t c[1] = {separated_next(f->kv, base)}; char *k = kvlangBuiltinScatterKey(base, c, 1); kvlangKvPair_t p = {k, n >= 2 ? in[1] : in[0]}; char err[256]; - kvlangKvSet(f->kv, &p, 1, err, sizeof err); + rc = kvlangKvSet(f->kv, &p, 1, err, sizeof err); free(k); free(base); free(fr); - kvlangBuiltinNextPc(f); + if (rc == 0) + kvlangBuiltinNextPc(f); kvlangBuiltinFreeInputs(in, n); - return 0; + return rc == 0 ? 0 : kvlangBuiltinSetErr(f, "%s", err); } int kvlangBuiltinSlice(kvlangFrame_t *f) { @@ -311,7 +400,13 @@ int kvlangBuiltinSlice(kvlangFrame_t *f) { char *fr = kvlangKeytreeFrameRoot(f->pc); char *base = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); - ensure_scattered(f, base); + int rc = ensure_scattered(f, base, NULL); + if (rc != 0) { + free(base); + free(fr); + kvlangBuiltinFreeInputs(in, n); + return rc; + } int al = separated_len(f->kv, base); int lo = (int)kvlangScalarI64(kvlangXvalueScalar(&in[1])), hi = (int)kvlangScalarI64(kvlangXvalueScalar(&in[2])); @@ -350,4 +445,3 @@ int kvlangBuiltinSlice(kvlangFrame_t *f) { kvlangBuiltinFreeInputs(in, n); return 0; } - diff --git a/runtime/src/rwir_decode.c b/runtime/src/rwir_decode.c index 1fc89212..214123ab 100644 --- a/runtime/src/rwir_decode.c +++ b/runtime/src/rwir_decode.c @@ -37,10 +37,12 @@ void kvlangRwirInstFree(kvlangRwirInst_t *inst) { free(inst->opcode); for (int i = 0; i < inst->nr; i++) { free(inst->reads[i].name); + free(inst->reads[i].type); kvlangXvalueFree(&inst->reads[i].val); } for (int i = 0; i < inst->nw; i++) { free(inst->writes[i].name); + free(inst->writes[i].type); kvlangXvalueFree(&inst->writes[i].val); } free(inst->reads); @@ -51,6 +53,149 @@ void kvlangRwirInstFree(kvlangRwirInst_t *inst) { inst->nr = inst->nw = 0; } +static char *operand_type(const kvlangXvalue_t *v) { + kvspaceHead_t h; + if (kvlangXvalueHead(v, &h) != 0) + return NULL; + if (!kvlangXvalueKindIs(v, KVSPACE_KIND_RWIR) && + !kvlangXvalueKindIs(v, KVSPACE_KIND_RWFUNC)) + return strdup((const char *)h.langtype); + int32_t len = 0; + const uint8_t *body = kvlangXvalueBody(v, &h, &len); + const uint8_t *end = body && len > 5 ? + memchr(body + 5, 0, (size_t)len - 5) : NULL; + return end && end + 1 < body + len ? + strndup((const char *)end + 1, (size_t)(body + len - end - 1)) : NULL; +} + +static int materialize_operand(kvlangKv_t *kv, const char *link_base, + const char *frame_root, const char *slot, + kvlangXvalue_t *v, char *err, uint32_t err_cap) { + if (kvlangXvalueIsPtr(v)) + return 0; + kvlangXvalueMaterialize(v); + int descriptor = kvlangXvalueKindIs(v, KVSPACE_KIND_RWIR) || + kvlangXvalueKindIs(v, KVSPACE_KIND_RWFUNC); + char *target = NULL; + char *type = operand_type(v); + if (descriptor) { + char *name = kvlangXvalueSlotName(v); + if (!name || !name[0]) { + snprintf(err, err_cap, "Decode: empty operand %s", slot); + free(name); + free(type); + return -1; + } + target = kvlangBuiltinResolveWriteSlot(kv, frame_root, name); + free(name); + if (target && (!type || strcmp(type, "any") != 0)) { + kvspaceHead_t actual; + if (kvlangKvGetHead(kv, target, &actual) == 0 && + actual.langtype_len > 0 && + (size_t)actual.langtype_len <= sizeof actual.langtype) { + char *actual_type = strndup((const char *)actual.langtype, + (size_t)actual.langtype_len); + if (!actual_type) { + snprintf(err, err_cap, "Decode: out of memory"); + free(target); + free(type); + return -1; + } + free(type); + type = actual_type; + } + } + } else { + kvlangStrbuf_t literal; + kvlangStrbufInit(&literal); + kvlangStrbufPrintf(&literal, "%s%soperand%s", link_base, + RUNTIME_MEMBER_SEP, slot); + target = kvlangStrbufDetach(&literal); + kvlangKvPair_t pair = {target, *v}; + if (kvlangKvSet(kv, &pair, 1, err, err_cap) != 0) { + free(target); + free(type); + return -1; + } + } + if (!target || !target[0]) { + snprintf(err, err_cap, "Decode: invalid operand %s", slot); + free(target); + free(type); + return -1; + } + kvlangXvalue_t ptr; + kvlangXvalueZero(&ptr); + kvlangXvalueNewPtr(&ptr, type ? type : "any", target); + free(type); + free(target); + if (kvlangXvalueNone(&ptr)) { + snprintf(err, err_cap, "Decode: invalid pointer at %s", slot); + return -1; + } + kvlangStrbuf_t physical; + kvlangStrbufInit(&physical); + kvlangStrbufPrintf(&physical, "%s%s", link_base, slot); + kvlangKvPair_t pair = {physical.p, ptr}; + int rc = kvlangKvSet(kv, &pair, 1, err, err_cap); + kvlangStrbufFree(&physical); + if (rc != 0) { + kvlangXvalueFree(&ptr); + return -1; + } + kvlangXvalueFree(v); + *v = ptr; + return 0; +} + +static int decode_operand(kvlangKv_t *kv, const char *link_base, + const char *frame_root, const char *slot, + kvlangParam_t *out, bool read_target, + char *err, uint32_t err_cap) { + memset(out, 0, sizeof *out); + kvlangXvalue_t v; + kvlangXvalueZero(&v); + if (kvlangKvGetMember(kv, link_base, slot, &v) != 0) { + snprintf(err, err_cap, "Decode: cannot read %s", slot); + return -1; + } + if (kvlangXvalueNone(&v)) { + kvlangXvalueFree(&v); + return 0; + } + if (materialize_operand(kv, link_base, frame_root, slot, + &v, err, err_cap) != 0) { + kvlangXvalueFree(&v); + return -1; + } + kvlangXvalueMaterialize(&v); + out->name = kvlangXvaluePtrTarget(&v); + kvspaceHead_t h; + if (!out->name || kvlangXvalueHead(&v, &h) != 0) { + snprintf(err, err_cap, "Decode: invalid pointer at %s", slot); + kvlangXvalueFree(&v); + free(out->name); + out->name = NULL; + return -1; + } + out->type = strdup((const char *)h.langtype); + out->address = 1; + if (!out->type || + (read_target && kvlangKvGetOne(kv, out->name, &out->val) != 0)) { + snprintf(err, err_cap, "Decode: cannot read target for %s", slot); + free(out->name); + free(out->type); + out->name = out->type = NULL; + kvlangXvalueFree(&out->val); + kvlangXvalueFree(&v); + return -1; + } + if (read_target) + kvlangXvalueMaterialize(&out->val); + kvlangXvalueFree(&v); + return 1; +} + int kvlangRwirDecode(kvlangKv_t *kv, const char *link_base, const char *pc, kvlangRwirInst_t *out, char *err, uint32_t err_cap) { memset(out, 0, sizeof(*out)); @@ -63,58 +208,80 @@ int kvlangRwirDecode(kvlangKv_t *kv, const char *link_base, const char *pc, } int addr0 = kvlangRwirExtractAddr0(last + 1); - kvlangStrbuf_t key; - kvlangStrbufInit(&key); + char *frame_root = kvlangKeytreeFrameRoot(pc); - out->reads = malloc(sizeof(kvlangParam_t) * MAX_PARAMS); - out->writes = malloc(sizeof(kvlangParam_t) * MAX_PARAMS); - out->nr = out->nw = 0; - - /* 指令槽是稠密数组:opcode 在 [addr0,0],读参 [addr0,-1..]、写参 [addr0,1..] 各自从 1 连续, - * 首个缺失槽即终止。逐槽读、遇空即停,替代每步固定读满 1+2*MAX_PARAMS 个槽——durable 后端上 - * 那些缺失槽会各触发一次祖先 ext-index 解析,放大成 syscall 风暴(prime_sieve fs/redis 超时根因)。 */ kvlangXvalue_t v; - char *nm; + char slot[48]; + int nr = 0, nw = 0; - kvlangStrbufPrintf(&key, "[%d,0]", addr0); - nm = kvlangStrbufDetach(&key); - kvlangKvGetMember(kv, link_base, nm, &v); - free(nm); - if (!kvlangXvalueNone(&v)) + snprintf(slot, sizeof slot, "[%d,0]", addr0); + int opcode_rc = kvlangKvGetMember(kv, link_base, slot, &v); + if (opcode_rc != 0) { + snprintf(err, err_cap, "Decode: cannot read opcode at %s", pc); + goto fail; + } + if (!kvlangXvalueNone(&v)) { + kvspaceHead_t h; + int32_t len = 0; + const uint8_t *body = kvlangXvalueHead(&v, &h) == 0 ? + kvlangXvalueBody(&v, &h, &len) : NULL; + if (!body || len < 5) { + snprintf(err, err_cap, "Decode: invalid opcode at %s", pc); + kvlangXvalueFree(&v); + goto fail; + } + nr = (int)body[0] | ((int)body[1] << 8); + nw = (int)body[2] | ((int)body[3] << 8); + if (nr > MAX_PARAMS || nw > MAX_PARAMS) { + snprintf(err, err_cap, "Decode: too many operands at %s", pc); + kvlangXvalueFree(&v); + goto fail; + } out->opcode = kvlangXvalueValueString(&v); + } kvlangXvalueFree(&v); out->op_id = out->opcode ? kvlangOpClassify(out->opcode) : OPID_notinmyrwircaps; + out->reads = malloc(sizeof(*out->reads) * (size_t)(nr ? nr : 1)); + out->writes = malloc(sizeof(*out->writes) * (size_t)(nw ? nw : 1)); + if (!out->reads || !out->writes) { + snprintf(err, err_cap, "Decode: out of memory"); + goto fail; + } - for (int i = 1; i <= MAX_PARAMS; i++) { - kvlangStrbufPrintf(&key, "[%d,-%d]", addr0, i); - nm = kvlangStrbufDetach(&key); - kvlangKvGetMember(kv, link_base, nm, &v); - free(nm); - if (kvlangXvalueNone(&v)) { - kvlangXvalueFree(&v); - break; + for (int i = 1; i <= nr; i++) { + snprintf(slot, sizeof slot, "[%d,-%d]", addr0, i); + int rc = decode_operand(kv, link_base, frame_root, slot, + &out->reads[out->nr], true, err, err_cap); + if (rc < 0) + goto fail; + if (rc == 0) { + snprintf(err, err_cap, "Decode: missing operand %s", slot); + goto fail; } - out->reads[out->nr].name = kvlangXvalueSlotName(&v); - kvlangXvalueMaterialize(&v); /* 指令进 rwir_cache 长存,字面量须自持 */ - out->reads[out->nr].val = v; out->nr++; } - for (int i = 1; i <= MAX_PARAMS; i++) { - kvlangStrbufPrintf(&key, "[%d,%d]", addr0, i); - nm = kvlangStrbufDetach(&key); - kvlangKvGetMember(kv, link_base, nm, &v); - free(nm); - if (kvlangXvalueNone(&v)) { - kvlangXvalueFree(&v); - break; + bool load_write_value = out->opcode && + (strcmp(out->opcode, "obj") == 0 || strcmp(out->opcode, "map") == 0); + for (int i = 1; i <= nw; i++) { + snprintf(slot, sizeof slot, "[%d,%d]", addr0, i); + int rc = decode_operand(kv, link_base, frame_root, slot, + &out->writes[out->nw], load_write_value, + err, err_cap); + if (rc < 0) + goto fail; + if (rc == 0) { + snprintf(err, err_cap, "Decode: missing operand %s", slot); + goto fail; } - out->writes[out->nw].name = kvlangXvalueSlotName(&v); - kvlangXvalueMaterialize(&v); /* 指令进 rwir_cache 长存,字面量须自持 */ - out->writes[out->nw].val = v; out->nw++; } - kvlangStrbufFree(&key); + free(frame_root); return 0; + +fail: + free(frame_root); + kvlangRwirInstFree(out); + return -1; } diff --git a/runtime/src/rwir_func.c b/runtime/src/rwir_func.c index 82b5a7b6..3464989e 100644 --- a/runtime/src/rwir_func.c +++ b/runtime/src/rwir_func.c @@ -7,34 +7,21 @@ void kvlangBuiltinResolveReadValue(kvlangKv_t *kv, const char *frame_root, const char *name, const kvlangXvalue_t *val, kvlangXvalue_t *out) { kvlangXvalueZero(out); + if (name && name[0] == '/') { + kvlangKvGetOne(kv, name, out); + return; + } if (val && !kvlangXvalueNone(val) && !kvlangXvalueKindIs(val, KVSPACE_KIND_RWIR) && !kvlangXvalueKindIs(val, KVSPACE_KIND_RWFUNC)) { - out->data = val->data; /* 借指令内冻结字面量(生命周期同 decode 缓存,永不 flush/free) */ + out->data = val->data; /* Borrowed for this instruction. */ out->len = val->len; out->borrowed = 1; return; } if (!name || !name[0]) return; - if (name[0] == '/') { kvlangKvGetOne(kv, name, out); return; } if (name[0] == '*') { - /* 显式解引用:*[0,±k] → 读 frame_root/[0,±k](ref=1 Ptr)→ 解引用到实参值。 - * 同 ResolveWriteSlot:帧槽必有 Ptr,读不到即传参链路已坏 → panic, - * 绝不静默留 None(那会让形参读成空值,把真因藏到几层之外)。 */ - char stkbuf[512]; - char *stk = kvlangKeytreeStackBuf(frame_root, stkbuf, sizeof stkbuf) - ? stkbuf - : kvlangKeytreeStack(frame_root); - kvlangXvalue_t pv; kvlangXvalueZero(&pv); - kvlangKvGetMember(kv, stk, name + 1, &pv); - if (!kvlangXvalueIsPtr(&pv)) { - fprintf(stderr, "panic: %s%s is not a Ptr — frame slot missing, param passing broken\n", stk, name + 1); - abort(); - } - char *target = kvlangXvaluePtrTarget(&pv); + char *target = kvlangBuiltinResolveWriteSlot(kv, frame_root, name); kvlangKvGetOne(kv, target, out); free(target); - kvlangXvalueFree(&pv); - if (stk != stkbuf) - free(stk); return; } char stkbuf[512]; @@ -59,6 +46,8 @@ void kvlangBuiltinResolveReadValue(kvlangKv_t *kv, const char *frame_root, const * ptr 追链到最终目标),供 xv 系列对 key 直发 GetHead/GetPart/SetPart 做分片读写。 */ char *kvlangBuiltinResolveReadKey(kvlangKv_t *kv, const char *frame_root, const char *name, const kvlangXvalue_t *val) { + if (name && name[0] == '/') + return strdup(name); if (val && !kvlangXvalueNone(val) && !kvlangXvalueKindIs(val, KVSPACE_KIND_RWIR) && !kvlangXvalueKindIs(val, KVSPACE_KIND_RWFUNC)) return NULL; if (!name || !name[0]) return NULL; @@ -72,10 +61,6 @@ char *kvlangBuiltinResolveWriteSlot(kvlangKv_t *kv, const char *frame_root, cons ? stkbuf : kvlangKeytreeStack(frame_root); if (name[0] == '*') { - /* 显式解引用:*[0,±k] → 读 frame_root/[0,±k](ref=1 Ptr)→ target=写槽路径。 - * `*[0,±k]` 是 layout 编译期生成的形参引用,帧槽必有调用点写入的 Ptr;读不到 - * 即传参链路已坏(不变量违反,非用户错误)→ panic,绝不回退成字面路径—— - * 回退会把「写形参」静默变成「写一个叫 *[0,±k] 的键」,掩盖真因。 */ kvlangXvalue_t pv; kvlangXvalueZero(&pv); kvlangKvGetMember(kv, stk, name + 1, &pv); if (!kvlangXvalueIsPtr(&pv)) { @@ -84,6 +69,19 @@ char *kvlangBuiltinResolveWriteSlot(kvlangKv_t *kv, const char *frame_root, cons } char *target = kvlangXvaluePtrTarget(&pv); kvlangXvalueFree(&pv); + if (strncmp(name + 1, "[0,", 3) == 0) { + kvlangXvalue_t arg; + kvlangXvalueZero(&arg); + kvlangKvGetOne(kv, target, &arg); + if (!kvlangXvalueIsPtr(&arg)) { + fprintf(stderr, "panic: %s is not an argument Ptr\n", target); + abort(); + } + char *value_key = kvlangXvaluePtrTarget(&arg); + kvlangXvalueFree(&arg); + free(target); + target = value_key; + } if (stk != stkbuf) free(stk); return target; @@ -173,7 +171,7 @@ void kvlangDisplay(const kvlangXvalue_t *v, char **out) { /* ── frame helper ─────────────────────────────────────────────────── */ int kvlangBuiltinReadInputs(kvlangFrame_t *f, kvlangXvalue_t *out, int cap) { - /* 帧根优先用主循环缓存(免每步 malloc + 扫描)。 */ + /* Reuse this instruction's frame root. */ char *owned = NULL; const char *fr = f->frame_root; if (!fr) { owned = kvlangKeytreeFrameRoot(f->pc); fr = owned; } @@ -228,7 +226,6 @@ int kvlangBuiltinSetErr(kvlangFrame_t *f, const char *fmt, ...) { int kvlangBuiltinExecuteCopy(kvlangFrame_t *f) { kvlangKv_t *kv = f->kv; kvlangRwirInst_t *inst = f->inst; - /* 帧根优先用主循环缓存;status_known/fb_pc 随 f 透传(走同一套 PC 推进语义)。 */ char *owned = NULL; const char *fr = f->frame_root; if (!fr) { owned = kvlangKeytreeFrameRoot(f->pc); fr = owned; } diff --git a/runtime/src/rwir_internal.h b/runtime/src/rwir_internal.h index ba529ac8..36087baa 100644 --- a/runtime/src/rwir_internal.h +++ b/runtime/src/rwir_internal.h @@ -11,10 +11,7 @@ int kvlangBuiltinSetErr(kvlangFrame_t *f, const char *fmt, ...); void kvlangBuiltinXvalueAt(const kvlangXvalue_t *v, int i, kvlangXvalue_t *out); char *kvlangBuiltinScatterKey(const char *base, const int64_t *coords, int ncoord); -void kvlangBuiltinMemindex(kvlangXvalue_t *out, const char *const *names, - int n); -void kvlangBuiltinMapMarker(kvlangXvalue_t *out, const char *langtype, - const int32_t *dims, int ndim); +void kvlangBuiltinMapMarker(kvlangXvalue_t *out, const char *langtype); /* char 拼接(+ 与 string·concat 共用):均须 char kind;编码不同返 false(调用方 throw),相同则 out=拼接结果并保持该编码。 */ bool kvlangBuiltinCharConcat(const kvlangXvalue_t *a, const kvlangXvalue_t *b, kvlangXvalue_t *out); diff --git a/runtime/src/rwir_kv.c b/runtime/src/rwir_kv.c index 6a370e2b..d3546b9b 100644 --- a/runtime/src/rwir_kv.c +++ b/runtime/src/rwir_kv.c @@ -18,10 +18,52 @@ static char *kvlangKvKey(const kvlangXvalue_t *v) { return strdup(""); } +static char *original_absolute_arg(kvlangFrame_t *f, int idx) { + char *fr = kvlangKeytreeFrameRoot(f->pc); + if (!fr) + return NULL; + kvlangStrbuf_t key; + kvlangStrbufInit(&key); + kvlangStrbufPrintf(&key, "%s/%slib", fr, RUNTIME_MEMBER_SEP); + kvlangXvalue_t lib; + kvlangXvalueZero(&lib); + kvlangKvGetOne(f->kv, key.p, &lib); + char *root = kvlangXvalueNone(&lib) ? NULL : kvlangXvalueValueString(&lib); + kvlangXvalueFree(&lib); + if (!root || root[0] != '/') { + free(root); + free(fr); + kvlangStrbufFree(&key); + return NULL; + } + int row = kvlangRwirExtractAddr0(strrchr(f->pc, '/') + 1); + kvlangStrbufClear(&key); + kvlangStrbufPrintf(&key, "%s/[%d,-%d]", root, row, idx + 1); + kvlangXvalue_t source; + kvlangXvalueZero(&source); + kvlangKvGetOne(f->kv, key.p, &source); + char *name = kvlangXvalueKindIs(&source, KVSPACE_KIND_RWIR) ? + kvlangXvalueSlotName(&source) : NULL; + if (name && name[0] != '/') { + free(name); + name = NULL; + } + kvlangXvalueFree(&source); + kvlangStrbufFree(&key); + free(root); + free(fr); + return name; +} + static char *path_arg(kvlangFrame_t *f, int idx, const kvlangXvalue_t *in) { const char *name = f->inst->reads[idx].name; - if (name[0] == '/') - return strdup(name); + if (name[0] == '/') { + if (strncmp(name, "/vthread/", 9) != 0) + return strdup(name); + char *source = original_absolute_arg(f, idx); + if (source) + return source; + } if (!kvlangXvalueNone(&in[idx])) { char *s = kvlangXvalueValueString(&in[idx]); if (s[0] == '/') @@ -31,15 +73,12 @@ static char *path_arg(kvlangFrame_t *f, int idx, const kvlangXvalue_t *in) { return NULL; } -/* 容器 base:stringkeymap/index/extindex 或 struct 实例(kind=structref,以 / 起头)。 - * 容器取其写槽路径拼成员 key;非容器把 base 值当 key 串。 */ +/* Container members use physical key prefixes. */ static bool base_is_container(const kvlangXvalue_t *base) { if (kvlangXvalueNone(base)) return true; const char *k = kvlangXvalueKind(base); - return k[0] == '/' || kvlangKindIsMap(k) || - strcmp(k, KVSPACE_KIND_INDEX) == 0 || - strcmp(k, KVSPACE_KIND_EXT_INDEX) == 0; + return k[0] == '/' || kvlangKindIsMap(k); } /* 非容器 base 的统一报错(返回 NULL 供调用方判定失败):带上实际 langtype 便于定位。 */ @@ -298,15 +337,12 @@ int kvlangCList(kvlangFrame_t *f) { int n = kvlangBuiltinReadInputs(f, in, 1); char *key = n >= 1 ? path_arg(f, 0, in) : NULL; if (!key && f->inst->nr >= 1) { - /* 裸变量(obj/map 成员目录):解析为 /. 目录。 */ const char *name = f->inst->reads[0].name; - if (name[0] != '/') { - char *fr = kvlangKeytreeFrameRoot(f->pc); - char *base = kvlangBuiltinResolveWriteSlot(f->kv, fr, name); - free(fr); - key = kvlangKeytreeMember(base, ""); - free(base); - } + char *fr = kvlangKeytreeFrameRoot(f->pc); + char *base = kvlangBuiltinResolveWriteSlot(f->kv, fr, name); + free(fr); + key = kvlangKeytreeMember(base, ""); + free(base); } if (!key) { kvlangBuiltinFreeInputs(in, n); @@ -320,32 +356,13 @@ int kvlangCList(kvlangFrame_t *f) { char *dst = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); free(fr); - /* 结果是一个 stringkeymap:容器值在 dst(body 空,dims=[count] 落 head), - * kvlangBuiltinMemindex 在 dst·,成员名是坐标段 [i],值是对应的成员名字符串。 */ - char **coords = malloc(sizeof(char *) * (size_t)(count > 0 ? count : 1)); - for (int i = 0; i < count; i++) { - kvlangStrbuf_t s; - kvlangStrbufInit(&s); - kvlangStrbufPrintf(&s, "[%d]", i); - coords[i] = kvlangStrbufDetach(&s); - } + /* Members are physical keys under dst·. */ char err[256]; - int32_t dims[1] = {count}; kvlangXvalue_t mark; - kvlangBuiltinMapMarker(&mark, "[int64]" MEMBER_SEP "[]char/utf8", dims, 1); + kvlangBuiltinMapMarker(&mark, "[int64]" MEMBER_SEP "[]char/utf8"); kvlangKvPair_t p0 = {dst, mark}; kvlangKvSet(f->kv, &p0, 1, err, sizeof err); kvlangXvalueFree(&mark); - char *dir = kvlangKeytreeMember(dst, ""); - kvlangXvalue_t mi; - kvlangBuiltinMemindex(&mi, (const char *const *)coords, count); - kvlangKvPair_t p1 = {dir, mi}; - kvlangKvSet(f->kv, &p1, 1, err, sizeof err); - kvlangXvalueFree(&mi); - free(dir); - for (int i = 0; i < count; i++) - free(coords[i]); - free(coords); for (int i = 0; i < count; i++) { int64_t c[1] = {i}; char *k = kvlangBuiltinScatterKey(dst, c, 1); @@ -385,13 +402,11 @@ static char *kv_list_dir(kvlangFrame_t *f, kvlangXvalue_t *in, int n) { char *key = n >= 1 ? path_arg(f, 0, in) : NULL; if (!key && f->inst->nr >= 1) { const char *name = f->inst->reads[0].name; - if (name[0] != '/') { - char *fr = kvlangKeytreeFrameRoot(f->pc); - char *base = kvlangBuiltinResolveWriteSlot(f->kv, fr, name); - free(fr); - key = kvlangKeytreeMember(base, ""); - free(base); - } + char *fr = kvlangKeytreeFrameRoot(f->pc); + char *base = kvlangBuiltinResolveWriteSlot(f->kv, fr, name); + free(fr); + key = kvlangKeytreeMember(base, ""); + free(base); } return key; } diff --git a/runtime/src/rwir_map.c b/runtime/src/rwir_map.c index 8bf37ef9..784725d2 100644 --- a/runtime/src/rwir_map.c +++ b/runtime/src/rwir_map.c @@ -2,29 +2,10 @@ /* ── 容器值 / 成员索引 ──────────────────────────────────────────── */ -/* kvlangBuiltinMemindex(p·):kind=index,body=[4B count LE][name\n...],成员列表唯一权威。 */ -void kvlangBuiltinMemindex(kvlangXvalue_t *out, const char *const *names, - int n) { - kvlangStrbuf_t body; - kvlangStrbufInit(&body); - char count[4] = {(char)(n & 0xFF), (char)((n >> 8) & 0xFF), - (char)((n >> 16) & 0xFF), (char)((n >> 24) & 0xFF)}; - kvlangStrbufPutn(&body, count, 4); - for (int i = 0; i < n; i++) { - if (i) - kvlangStrbufPutc(&body, '\n'); - kvlangStrbufPuts(&body, names[i]); - } - kvlangXvalueNewTlv(out, KVSPACE_KIND_INDEX, (const uint8_t *)body.p, - (uint32_t)body.len, 1); - kvlangStrbufFree(&body); -} - /* map 容器值(p):body 空、storetype=index,langtype 为 map langtype(见 [[map容器]])。 * langtype 恒非空——layout 强制容器字面量写目标带 map langtype(缺则 layout 报错)。 */ -void kvlangBuiltinMapMarker(kvlangXvalue_t *out, const char *langtype, - const int32_t *dims, int ndim) { - kvlangXvalueNewTlvDims(out, langtype, (const uint8_t *)"", 0, dims, ndim); +void kvlangBuiltinMapMarker(kvlangXvalue_t *out, const char *langtype) { + kvlangXvalueNewTlv(out, langtype, (const uint8_t *)"", 0, 1); } /* 写槽 `w` 的声明容器类型:layout 的 write_slot_value 把 map langtype 落进写槽的 langtype @@ -36,16 +17,26 @@ static char *declared_map_langtype(kvlangFrame_t *f, int w) { f->inst->nw); abort(); } - char buf[256]; - kvlangXvalueLangtype(&f->inst->writes[w].val, buf, sizeof buf); - if (!strstr(buf, MEMBER_SEP)) { + if (f->inst->writes[w].type && + strstr(f->inst->writes[w].type, MEMBER_SEP)) + return strdup(f->inst->writes[w].type); + kvspaceHead_t head; + if (kvlangXvalueHead(&f->inst->writes[w].val, &head) != 0) + abort(); + int32_t len = 0; + const uint8_t *body = kvlangXvalueBody(&f->inst->writes[w].val, + &head, &len); + const uint8_t *split = len > 5 ? memchr(body + 5, 0, (size_t)len - 5) : NULL; + char *type = split ? strndup((const char *)split + 1, + (size_t)(body + len - split - 1)) : strdup(""); + if (!strstr(type, MEMBER_SEP)) { fprintf(stderr, "panic: container literal target %s has no map langtype " "(layout must reject)\n", f->inst->writes[w].name); abort(); } - return strdup(buf); + return type; } /* ── obj / map ─────────────────────────────────────────────────── */ @@ -83,22 +74,13 @@ int kvlangBuiltinObj(kvlangFrame_t *f) { names[j++] = kvlangXvalueValueString(&in[i]); } /* 容器值 p:langtype=声明的 map langtype,dims=[0](命名字典无形状,成员在 memindex)。 */ - int32_t odims[1] = {0}; char *wty = declared_map_langtype(f, w); kvlangXvalue_t mark; - kvlangBuiltinMapMarker(&mark, wty, odims, 1); + kvlangBuiltinMapMarker(&mark, wty); free(wty); kvlangKvPair_t p0 = {ok, mark}; kvlangKvSet(f->kv, &p0, 1, err, sizeof err); kvlangXvalueFree(&mark); - /* kvlangBuiltinMemindex p·:kind=index,body=[4B count][names]。 */ - char *mip = kvlangKeytreeMember(ok, ""); - kvlangXvalue_t mi; - kvlangBuiltinMemindex(&mi, (const char *const *)names, cnt); - kvlangKvPair_t p1 = {mip, mi}; - kvlangKvSet(f->kv, &p1, 1, err, sizeof err); - kvlangXvalueFree(&mi); - free(mip); for (int i = 0, j = 0; i + 1 < n; i += 2) { if (kvlangXvalueNone(&in[i + 1])) continue; @@ -149,22 +131,13 @@ int kvlangBuiltinMap(kvlangFrame_t *f) { names[i] = kvlangStrbufDetach(&s); } /* 容器值 p:langtype=声明的 map langtype,body 空,dims=[n] 落 head。 */ - int32_t dims[1] = {n}; char *wty = declared_map_langtype(f, w); kvlangXvalue_t mark; - kvlangBuiltinMapMarker(&mark, wty, dims, 1); + kvlangBuiltinMapMarker(&mark, wty); free(wty); kvlangKvPair_t p0 = {ok, mark}; kvlangKvSet(f->kv, &p0, 1, err, sizeof err); kvlangXvalueFree(&mark); - /* kvlangBuiltinMemindex p·:kind=index,body=[4B count][[0]\n[1]...]。 */ - char *mip = kvlangKeytreeMember(ok, ""); - kvlangXvalue_t mi; - kvlangBuiltinMemindex(&mi, (const char *const *)names, n); - kvlangKvPair_t p1 = {mip, mi}; - kvlangKvSet(f->kv, &p1, 1, err, sizeof err); - kvlangXvalueFree(&mi); - free(mip); for (int i = 0; i < n; i++) { int64_t c[1] = {i}; char *k = kvlangBuiltinScatterKey(ok, c, 1); diff --git a/runtime/src/rwir_ndarray.c b/runtime/src/rwir_ndarray.c index c2136f11..b75d9dd4 100644 --- a/runtime/src/rwir_ndarray.c +++ b/runtime/src/rwir_ndarray.c @@ -20,12 +20,26 @@ int kvlangBuiltinNdarrayNumel(kvlangFrame_t *f) { kvspaceHead_t h; int64_t n_el = 0; if (xv_head1(f, &h) == 0) { - int g = ndarray_shape_guard(f, "ndarray·numel", &h); - if (g) - return g; kvlangLangtype kx; kvlangLangtypeParse(h.langtype, &kx); - n_el = kx.array_len; + if (kvlangKindIsMap(kx.kind)) { + char *fr = kvlangKeytreeFrameRoot(f->pc); + char *base = kvlangBuiltinResolveWriteSlot(f->kv, fr, + f->inst->reads[0].name); + char *dir = kvlangKeytreeMember(base, ""); + char **names = NULL; + int count = 0; + kvlangKvList(f->kv, dir, false, false, &names, &count); + for (int i = 0; i < count; i++) + free(names[i]); + free(names); + free(dir); + free(base); + free(fr); + n_el = count; + } else { + n_el = kx.array_len; + } } kvlangXvalue_t r; kvlangXvalueNewInt64(&r, n_el); diff --git a/runtime/src/rwir_struct.c b/runtime/src/rwir_struct.c index 9efef51d..736fb239 100644 --- a/runtime/src/rwir_struct.c +++ b/runtime/src/rwir_struct.c @@ -1,30 +1,26 @@ #include "rwir_internal.h" -static char *dupn(const char *s, size_t n) { - char *r = malloc(n + 1); - memcpy(r, s, n); - r[n] = 0; - return r; -} - -/* 在 "name:langtype\n..." 声明串里查字段名,返回其类型(malloc)或 NULL(无此字段)。 */ -static char *struct_field_type(const char *decl, const char *fname) { - size_t fl = strlen(fname); - const char *p = decl; - while (*p) { - const char *nl = strchr(p, '\n'); - size_t linelen = nl ? (size_t)(nl - p) : strlen(p); - const char *colon = memchr(p, ':', linelen); - if (colon) { - size_t nlen = (size_t)(colon - p); - if (nlen == fl && memcmp(p, fname, fl) == 0) - return dupn(colon + 1, linelen - nlen - 1); - } - if (!nl) - break; - p = nl + 1; +static char *struct_field_type(kvlangKv_t *kv, const char *base, + const char *fname) { + kvlangStrbuf_t key; + kvlangStrbufInit(&key); + kvlangStrbufPrintf(&key, "%s/%s", base, fname); + kvlangXvalue_t value; + kvlangXvalueZero(&value); + kvlangKvGetOne(kv, key.p, &value); + kvlangStrbufFree(&key); + if (kvlangXvalueNone(&value) || + strcmp(kvlangXvalueKind(&value), KVSPACE_KIND_DEF_LANGTYPE) != 0) { + kvlangXvalueFree(&value); + return NULL; } - return NULL; + kvspaceHead_t head; + kvlangXvalueHead(&value, &head); + int32_t len = 0; + const uint8_t *body = kvlangXvalueBody(&value, &head, &len); + char *type = strndup((const char *)body, (size_t)len); + kvlangXvalueFree(&value); + return type; } /* struct·new:克隆 /lib/Name 原型子树到写槽,覆盖给定字段(校验字段存在性+类型)。 @@ -41,7 +37,7 @@ int kvlangBuiltinStructNew(kvlangFrame_t *f) { kvlangXvalueZero(&proto); kvlangKvGetOne(f->kv, ref, &proto); if (kvlangXvalueNone(&proto) || - strcmp(kvlangXvalueKind(&proto), KVSPACE_KIND_STRUCT) != 0) { + strcmp(kvlangXvalueKind(&proto), KVSPACE_KIND_DEF_STRUCT) != 0) { int e = kvlangBuiltinSetErr(f, "TypeError: %s is not a struct type", ref); kvlangXvalueFree(&proto); @@ -49,11 +45,6 @@ int kvlangBuiltinStructNew(kvlangFrame_t *f) { kvlangBuiltinFreeInputs(in, n); return e; } - kvspaceHead_t ph; - kvlangXvalueHead(&proto, &ph); - int32_t dclen = 0; - const uint8_t *dcl = kvlangXvalueBody(&proto, &ph, &dclen); - char *decl = dupn((const char *)dcl, (size_t)(dclen > 0 ? dclen : 0)); kvlangXvalueFree(&proto); char err[256]; @@ -70,11 +61,12 @@ int kvlangBuiltinStructNew(kvlangFrame_t *f) { kvlangXvalue_t mark; kvlangXvalueNewTlv(&mark, ref, (const uint8_t *)"", 0, 1); kvlangKvPair_t p0 = {ok, mark}; - kvlangKvSet(f->kv, &p0, 1, err, sizeof err); + if (kvlangKvSet(f->kv, &p0, 1, err, sizeof err) != 0) + rc = kvlangBuiltinSetErr(f, "%s", err); kvlangXvalueFree(&mark); for (int i = 1; i + 1 < n && rc == 0; i += 2) { char *fname = kvlangXvalueValueString(&in[i]); - char *ftype = struct_field_type(decl, fname); + char *ftype = struct_field_type(f->kv, ref, fname); if (!ftype) { rc = kvlangBuiltinSetErr( f, "TypeError: struct %s has no field %s", ref, fname); @@ -105,7 +97,8 @@ int kvlangBuiltinStructNew(kvlangFrame_t *f) { } char *mk = kvlangKeytreeMember(ok, fname); kvlangKvPair_t p = {mk, in[i + 1]}; - kvlangKvSet(f->kv, &p, 1, err, sizeof err); + if (kvlangKvSet(f->kv, &p, 1, err, sizeof err) != 0) + rc = kvlangBuiltinSetErr(f, "%s", err); free(mk); free(ftype); free(fname); @@ -113,7 +106,6 @@ int kvlangBuiltinStructNew(kvlangFrame_t *f) { free(ok); } free(fr); - free(decl); free(ref); kvlangBuiltinFreeInputs(in, n); if (rc != 0) diff --git a/runtime/src/rwir_time.c b/runtime/src/rwir_time.c index ae44a21d..6c7ff275 100644 --- a/runtime/src/rwir_time.c +++ b/runtime/src/rwir_time.c @@ -70,7 +70,7 @@ int kvlangBuiltinDurArith(kvlangFrame_t *f) { kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); if (n < 2) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: time/duration arith requires 2 duration args"); } int64_t a = kvlangScalarI64(kvlangXvalueScalar(&in[0])), b = kvlangScalarI64(kvlangXvalueScalar(&in[1])); - bool sub = strstr(f->inst->opcode, ".sub") != NULL; + bool sub = strstr(f->inst->opcode, MEMBER_SEP "sub") != NULL; kvlangXvalue_t e; new_duration(&e, sub ? a - b : a + b); int rc = kvlangBuiltinWriteResult(f, &e); kvlangXvalueFree(&e); kvlangBuiltinFreeInputs(in, n); return rc; diff --git a/runtime/src/vthread.c b/runtime/src/vthread.c index 1688d6a5..6848b303 100644 --- a/runtime/src/vthread.c +++ b/runtime/src/vthread.c @@ -1,56 +1,19 @@ #include "runtime_internal.h" -/* vthread pc/status key 路径缓存(单条目):运行期同一 vtid 连续多步, - * 免每步重建 2 条 key(strbuf 分配 + 格式化)。key 纯由 vtid 派生、内容恒定, - * 不承载任何执行状态——状态一律每步回读 kvspace(见 kvlangVthreadAdvance)。vtid 变化即失效。 */ -static char g_vt_id[128]; -static bool g_vt_valid = false; -static char *g_vt_pc_key = NULL; -static char *g_vt_status_key = NULL; - -/* 取(必要时重建)vtid 的 pc/status key;vtid 过长则返回 false(调用方退化为逐次构造)。 */ -static bool vt_keys(const char *vtid, const char **pc_key, const char **status_key) { - if (g_vt_valid && strcmp(g_vt_id, vtid) == 0) { - *pc_key = g_vt_pc_key; *status_key = g_vt_status_key; - return true; - } - size_t n = strlen(vtid); - if (n == 0 || n >= sizeof g_vt_id) return false; - memcpy(g_vt_id, vtid, n + 1); - kvlangStrbuf_t b; kvlangStrbufInit(&b); - kvlangKeytreeVthreadPc(vtid, &b); - free(g_vt_pc_key); g_vt_pc_key = kvlangStrbufDetach(&b); - kvlangStrbufInit(&b); - kvlangKeytreeVthreadStatus(vtid, &b); - free(g_vt_status_key); g_vt_status_key = kvlangStrbufDetach(&b); - g_vt_valid = true; - *pc_key = g_vt_pc_key; *status_key = g_vt_status_key; - return true; -} - -/* 取单条 vthread 成员 key(pc / status):命中缓存则借用之,否则构造到调用方 strbuf。 */ -static const char *vt_member_key(const char *vtid, bool status, kvlangStrbuf_t *k) { - const char *pk, *sk; - if (vt_keys(vtid, &pk, &sk)) return status ? sk : pk; - if (status) kvlangKeytreeVthreadStatus(vtid, k); - else kvlangKeytreeVthreadPc(vtid, k); - return k->p; -} - #define VT_MEMBER_GET(fn, want_status) \ void fn(kvlangKv_t *kv, const char *vtid, char **out) { \ *out = NULL; \ kvlangStrbuf_t k; kvlangStrbufInit(&k); \ - const char *key = vt_member_key(vtid, (want_status), &k); \ + if (want_status) kvlangKeytreeVthreadStatus(vtid, &k); \ + else kvlangKeytreeVthreadPc(vtid, &k); \ kvlangXvalue_t v; kvlangXvalueZero(&v); \ - kvlangKvGetOne(kv, key, &v); \ + kvlangKvGetOne(kv, k.p, &v); \ if (!kvlangXvalueNone(&v)) *out = kvlangXvalueValueString(&v); \ kvlangXvalueFree(&v); \ kvlangStrbufFree(&k); \ } -/* 单读 ‥pc / ‥status:主循环每指令边界各取所需(状态门只读 status), - * 免掉「取一个成员却连带读另一个」的多余后端 Get。 */ +/* Read PC and status separately from KVSpace. */ VT_MEMBER_GET(kvlangVthreadPcGet, false) VT_MEMBER_GET(kvlangVthreadStatusGet, true) @@ -59,50 +22,38 @@ void kvlangVthreadGet(kvlangKv_t *kv, const char *vtid, char **pc, char **status kvlangVthreadStatusGet(kv, vtid, status); } -void kvlangVthreadSet(kvlangKv_t *kv, const char *vtid, const char *pc, const char *status) { - const char *pk, *sk; +int kvlangVthreadSet(kvlangKv_t *kv, const char *vtid, const char *pc, const char *status) { kvlangStrbuf_t k1, k2; kvlangStrbufInit(&k1); kvlangStrbufInit(&k2); - if (!vt_keys(vtid, &pk, &sk)) { - kvlangKeytreeVthreadPc(vtid, &k1); pk = k1.p; - kvlangKeytreeVthreadStatus(vtid, &k2); sk = k2.p; - } + kvlangKeytreeVthreadPc(vtid, &k1); + kvlangKeytreeVthreadStatus(vtid, &k2); kvlangXvalue_t v1, v2; kvlangXvalueZero(&v1); kvlangXvalueZero(&v2); kvlangXvalueNewCharUtf8(&v1, pc); kvlangXvalueNewCharUtf8(&v2, status); - kvlangKvPair_t pairs[2] = { { (char *)pk, v1 }, { (char *)sk, v2 } }; + kvlangKvPair_t pairs[2] = { { k1.p, v1 }, { k2.p, v2 } }; char err[256]; - kvlangKvSet(kv, pairs, 2, err, sizeof err); + int rc = kvlangKvSet(kv, pairs, 2, err, sizeof err); kvlangXvalueFree(&v1); kvlangXvalueFree(&v2); kvlangStrbufFree(&k1); kvlangStrbufFree(&k2); + return rc; } -/* 循环内 PC/status 推进(见 kvlangFrame_t 注释):先写 kvspace(崩溃恢复 + 唯一事实源)。 - * - PC 恒写:它是本指令自己算出来的新地址;写后经 fb_pc 回传,主循环免「刚写就回读」。 - * - status 只在**与 f->status_known 不同**时写:status_known 是本步开始前从 kvspace 读到的 - * ‥status(源值,非进程内副本)。正常执行期两者都是 "running",每步省 1 次后端 Set; - * 而一旦外部把 ‥status 改成 paused/error,主循环下一步的状态门就会读到并停机—— - * 既不拿副本当依据,也不会把 kvspace 留在与执行不符的状态。 - * 两写都走 kvlangKvSetChar 直写 body,跳过 TLV 编码往返。 */ +/* Persist PC and changed status after each instruction. */ void kvlangVthreadAdvance(kvlangFrame_t *f, const char *pc, const char *status) { - const char *pk, *sk; kvlangStrbuf_t k1, k2; kvlangStrbufInit(&k1); kvlangStrbufInit(&k2); - if (!vt_keys(f->vtid, &pk, &sk)) { - kvlangKeytreeVthreadPc(f->vtid, &k1); pk = k1.p; - kvlangKeytreeVthreadStatus(f->vtid, &k2); sk = k2.p; - } - kvlangKvSetChar(f->kv, pk, pc); - if (!f->status_known || strcmp(f->status_known, status) != 0) - kvlangKvSetChar(f->kv, sk, status); + kvlangKeytreeVthreadPc(f->vtid, &k1); + kvlangKeytreeVthreadStatus(f->vtid, &k2); + if (kvlangKvSetChar(f->kv, k1.p, pc) != 0) + f->persist_failed = true; + if (!f->persist_failed && + (!f->status_known || strcmp(f->status_known, status) != 0) && + kvlangKvSetChar(f->kv, k2.p, status) != 0) + f->persist_failed = true; kvlangStrbufFree(&k1); kvlangStrbufFree(&k2); - if (f->fb_pc) { - free(*f->fb_pc); - *f->fb_pc = strdup(pc); - } } -void kvlangVthreadSetDone(kvlangKv_t *kv, const char *vtid, const char *ret) { +int kvlangVthreadSetDone(kvlangKv_t *kv, const char *vtid, const char *ret) { if (ret == NULL || ret[0] == 0) ret = "ok"; kvlangStrbuf_t k; kvlangStrbufInit(&k); kvlangKeytreeVthreadStatus(vtid, &k); @@ -110,8 +61,9 @@ void kvlangVthreadSetDone(kvlangKv_t *kv, const char *vtid, const char *ret) { kvlangXvalueNewCharUtf8(&v, ret); kvlangKvPair_t pair = { k.p, v }; char err[256]; - kvlangKvSet(kv, &pair, 1, err, sizeof err); + int rc = kvlangKvSet(kv, &pair, 1, err, sizeof err); kvlangXvalueFree(&v); kvlangStrbufFree(&k); + return rc; } void kvlangVthreadSetError(kvlangKv_t *kv, const char *vtid, const char *pc, const char *msg) { diff --git a/runtime/src/xvalue.c b/runtime/src/xvalue.c index 794aad7e..add775bc 100644 --- a/runtime/src/xvalue.c +++ b/runtime/src/xvalue.c @@ -1,7 +1,6 @@ #include "runtime_internal.h" -/* 后端无关的 XValue TLV 编解码(对齐 kvspace-durable/kvspace-c 的 kindexp TLV)。 - * xval.data 一律 malloc(free 释放),后端在 kvspace.c 层负责拷贝。 */ +/* XValue helpers use the shared KVSpace codec. */ static uint32_t rd32(const uint8_t *p) { return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | @@ -28,8 +27,7 @@ void kvlangXvalueSetBytes(kvlangXvalue_t *v, uint8_t *data, uint32_t len) { v->borrowed = 0; } -/* 借用值 → 自持:凡要把读回的 XValue 存入生命周期超出本指令的结构(如 decode 缓存的指令 - * 字面量),必须先落地,否则 kvspace 借用池回收后指针悬空。 */ +/* Own borrowed values across reads within an instruction. */ void kvlangXvalueMaterialize(kvlangXvalue_t *v) { if (v->borrowed && v->data && v->len > 0) { uint8_t *o = malloc(v->len); @@ -75,15 +73,13 @@ void kvlangLangtypeParse(const uint8_t *kx, kvlangLangtype *out) { out->array_len *= out->dims[d]; } -/* head 编解码统一委托给链接的 kvspace .so(kvspace-c / kvspace-durable 同一 ABI), - * runtime 不再私持 TLV head 布局,杜绝多份手写偏移不一致。 */ +/* Decode through the shared KVSpace ABI. */ static int kvlangXvalueDecodeHeadRaw(const uint8_t *d, uint32_t len, kvspaceHead_t *h) { memset(h, 0, sizeof(*h)); if (!d || len == 0) return -1; - kvspaceDecodeHead(d, len, h); - return h->langtype[0] ? 0 : -1; + return kvspaceDecodeHead(d, len, h) == 0 && h->langtype[0] ? 0 : -1; } /* array_len → dims:char/* 恒一维(含空串/单字符);其余标量(≤1)=0 维、多元素=1 维。 */ @@ -99,7 +95,7 @@ static int32_t al_to_dims(const char *kind, int32_t array_len, int32_t *dims) { return 0; } -/* .so 分配的 TLV → 转交 runtime 所有权(统一 free 释放)。 */ +/* Copy codec-owned bytes into runtime-owned storage. */ static uint8_t *kvlangXvalueOwn(uint8_t *tmp, uint32_t tl, uint32_t *out_len) { if (!tmp) { *out_len = 0; @@ -189,12 +185,10 @@ int kvlangXvalueLangtype(const kvlangXvalue_t *v, char *buf, size_t cap) { static char *strndup2(const uint8_t *p, int32_t n); -/* 指令槽名:rwir 族槽跳过 5B 计数头取 sig;带 map langtype 标注的写槽 body 即变量名 - * (layout 的 write_slot_value 把声明容器类型落在槽的 langtype),其余交给 ValueString。 */ +/* Code slots store a five-byte prefix before the target name. */ char *kvlangXvalueSlotName(const kvlangXvalue_t *v) { const char *k = kvlangXvalueKind(v); - if (strcmp(k, KVSPACE_KIND_RWIR) == 0 || - strcmp(k, KVSPACE_KIND_RWIR_OR_RWFUNC) == 0) { + if (strcmp(k, KVSPACE_KIND_RWIR) == 0 || strcmp(k, KVSPACE_KIND_RWFUNC) == 0) { kvspaceHead_t h; if (kvlangXvalueHead(v, &h) < 0) return strdup(""); @@ -208,8 +202,7 @@ char *kvlangXvalueSlotName(const kvlangXvalue_t *v) { /* 值容器判定:裸种类名 `stringkeymap`,或完整 map langtype `{keylt}·{valt}`(见 [[map容器]])。 * 后者 `·` 之前是键类型(可能是 `[int64]`/`[float64,float64]`),故不能与 KIND_MAP 比串。 */ bool kvlangKindIsMap(const char *kind) { - return kind && (strcmp(kind, KVSPACE_KIND_MAP) == 0 || - strstr(kind, MEMBER_SEP) != NULL); + return kind && strstr(kind, MEMBER_SEP) != NULL; } bool kvlangXvalueIsPtr(const kvlangXvalue_t *v) { @@ -538,35 +531,19 @@ char *kvlangXvalueValueString(const kvlangXvalue_t *v) { return strndup2(body, blen); if (strcmp(k, KVSPACE_KIND_CHAR) == 0) return utf32_to_utf8(body, blen); - if (strcmp(k, KVSPACE_KIND_RWIR) == 0 || - strcmp(k, KVSPACE_KIND_RWIR_OR_RWFUNC) == 0) + if (strcmp(k, KVSPACE_KIND_RWIR) == 0) return strndup2(body + (blen >= 5 ? 5 : 0), blen >= 5 ? blen - 5 : 0); if (strcmp(k, KVSPACE_KIND_RWFUNC) == 0) { + if (blen > 5) + return strndup2(body + 5, blen - 5); kvlangStrbuf_t b; kvlangStrbufInit(&b); kvlangStrbufPrintf(&b, "r%d/w%d", (blen >= 2 ? rd16(body) : 0), (blen >= 4 ? rd16(body + 2) : 0)); return kvlangStrbufDetach(&b); } - if (strcmp(k, KVSPACE_KIND_INDEX) == 0) { - int n = blen >= 4 ? (int)rd32(body) : 0; - kvlangStrbuf_t b; - kvlangStrbufInit(&b); - kvlangStrbufPrintf(&b, "(%d)", n); - return kvlangStrbufDetach(&b); - } - if (strcmp(k, KVSPACE_KIND_MAP) == 0) { - kvlangStrbuf_t b; - kvlangStrbufInit(&b); - kvlangStrbufPuts(&b, "map["); - for (int d = 0; d < h.ndim; d++) { - if (d) - kvlangStrbufPutc(&b, ','); - kvlangStrbufPrintf(&b, "%d", h.dims[d]); - } - kvlangStrbufPutc(&b, ']'); - return kvlangStrbufDetach(&b); - } + if (kvlangKindIsMap(k)) + return strdup("map"); return strndup2(body, blen); } diff --git a/runtime/tests/test_crash_resume.c b/runtime/tests/test_crash_resume.c new file mode 100644 index 00000000..d127f2fd --- /dev/null +++ b/runtime/tests/test_crash_resume.c @@ -0,0 +1,59 @@ +#include "runtime_internal.h" + +#include +#include + +int main(void) { + char dir[] = "/tmp/kvlang-crash-resume-XXXXXX"; + char dsn[256]; + if (!mkdtemp(dir)) + return 1; + const char *scheme = getenv("KVLANG_TEST_SCHEME"); + snprintf(dsn, sizeof dsn, "%s://%s/s", scheme ? scheme : "shm", dir); + pid_t pid = fork(); + if (pid < 0) + return 1; + if (pid == 0) { + kvlangKv_t *kv = kvlangKvConnect(dsn); + if (!kv) + _exit(2); + kvlangStrbuf_t key; + kvlangStrbufInit(&key); + kvlangKeytreeVthreadPc("vt0", &key); + int rc = kvlangKvSetChar(kv, key.p, "/lib/f/[2,0]"); + kvlangStrbufFree(&key); + kvlangXvalue_t value; + kvlangXvalueNewInt64(&value, 73); + kvlangKvPair_t pair = {"/value", value}; + char err[128]; + if (rc == 0) + rc = kvlangKvSet(kv, &pair, 1, err, sizeof err); + kvlangXvalueFree(&value); + _exit(rc == 0 ? 0 : 3); + } + int status = 0; + if (waitpid(pid, &status, 0) != pid || !WIFEXITED(status) || WEXITSTATUS(status)) + return 1; + kvlangKv_t *kv = kvlangKvConnect(dsn); + if (!kv) + return 1; + char *pc = NULL; + kvlangVthreadPcGet(kv, "vt0", &pc); + int ok = pc && strcmp(pc, "/lib/f/[2,0]") == 0; + free(pc); + kvlangXvalue_t value; + kvlangXvalueZero(&value); + kvspaceHead_t head; + if (kvlangKvGetOne(kv, "/value", &value) != 0 || + kvlangXvalueHead(&value, &head) != 0 || head.body_len != 8) + ok = 0; + else { + const uint8_t *body = value.data + head.body_offset; + ok = ok && body[0] == 73; + for (int i = 1; i < 8; i++) + ok = ok && body[i] == 0; + } + kvlangXvalueFree(&value); + kvlangKvDisconnect(kv); + return ok ? 0 : 1; +} diff --git a/runtime/tests/test_getmember_siblings.c b/runtime/tests/test_getmember_siblings.c index d4be412d..76ddfc34 100644 --- a/runtime/tests/test_getmember_siblings.c +++ b/runtime/tests/test_getmember_siblings.c @@ -45,20 +45,6 @@ static int set_i64(kvlangKv_t *k, const char *key, int64_t n) { return 0; } -/* Drop leaf/hot so GetMember cannot use per-key block_id. Keep fpar. */ -static void drop_leaf_hot(kvlangKv_t *k) { - int i; - for (i = 0; i < k->nref; i++) - free(k->ref[i].key); - k->nref = 0; - for (i = 0; i < k->nhot; i++) { - free(k->hot[i].name); - free(k->hot[i].key); - k->hot[i].name = k->hot[i].key = NULL; - } - k->nhot = 0; -} - int main(void) { char dir[] = "/tmp/kvs-gm-XXXXXX"; char path[256], dsn[288]; @@ -80,23 +66,15 @@ int main(void) { fprintf(stderr, "kvlangKvConnect failed dsn=%s\n", dsn); return 1; } - CHECK(k->ref_on); - for (i = 0; i < n; i++) { char key[128]; snprintf(key, sizeof key, "%s%s", frm, names[i]); CHECK(set_i64(k, key, want[i]) == 0); } - CHECK(k->fpar.key != NULL); - CHECK(k->fpar.klen == (uint32_t)strlen(frm)); - CHECK(memcmp(k->fpar.key, frm, k->fpar.klen) == 0); - CHECK(k->fpar.block_id != 0); - CHECK(k->fpar.gen != 0); - - drop_leaf_hot(k); - CHECK(k->nref == 0); - CHECK(k->nhot == 0); - CHECK(k->fpar.block_id != 0); + kvlangKvDisconnect(k); + k = kvlangKvConnect(dsn); + if (!k) + return 1; for (i = 0; i < n; i++) { int64_t got; @@ -107,21 +85,23 @@ int main(void) { kvlangKvReadReset(k); } - printf("GetMember siblings via cached ART parent: %d keys, distinct values\n", n); - printf("fpar block_id=%u depth=%u\n", k->fpar.block_id, k->fpar.gen); + printf("GetMember siblings after reconnect: %d keys, distinct values\n", n); - /* Set lo with leaf/hot empty; hi must stay distinct. */ { char lokey[128]; snprintf(lokey, sizeof lokey, "%slo", frm); CHECK(set_i64(k, lokey, 88) == 0); + kvlangKvDisconnect(k); + k = kvlangKvConnect(dsn); + if (!k) + return 1; CHECK(kvlangKvGetMember(k, frm, "lo", &out) == 0); CHECK(xv_i64(&out) == 88); kvlangKvReadReset(k); CHECK(kvlangKvGetMember(k, frm, "hi", &out) == 0); CHECK(xv_i64(&out) == 77); kvlangKvReadReset(k); - printf("Set sibling via cached ART parent: lo=88 hi=77\n"); + printf("Set sibling after reconnect: lo=88 hi=77\n"); } kvlangKvDisconnect(k); diff --git a/runtime/tests/test_kvset_invalid_head.c b/runtime/tests/test_kvset_invalid_head.c new file mode 100644 index 00000000..0a8a181e --- /dev/null +++ b/runtime/tests/test_kvset_invalid_head.c @@ -0,0 +1,89 @@ +#include "runtime_internal.h" + +#include +#include +#include +#include + +static int failures; + +#define CHECK(c) do { \ + if (!(c)) { \ + fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #c); \ + failures++; \ + } \ +} while (0) + +int main(void) { + char dir[] = "/tmp/kvs-invalid-head-XXXXXX"; + char dsn[256]; + if (!mkdtemp(dir)) + return 1; + const char *scheme = getenv("KVLANG_TEST_SCHEME"); + snprintf(dsn, sizeof dsn, "%s://%s/s", scheme ? scheme : "shm", dir); + kvlangKv_t *kv = kvlangKvConnect(dsn); + if (!kv) + return 1; + + kvlangXvalue_t good; + kvlangXvalueNewInt64(&good, 42); + kvlangKvPair_t pair = {.key = "/value", .val = good}; + char err[128] = {0}; + CHECK(kvlangKvSet(kv, &pair, 1, err, sizeof err) == 0); + kvlangXvalueFree(&good); + + kvlangXvalue_t got; + CHECK(kvlangKvGetOne(kv, pair.key, &got) == 0); + uint8_t before[128]; + uint32_t before_len = got.len; + if (!got.data || before_len == 0 || before_len > sizeof before) { + kvlangKvDisconnect(kv); + return 1; + } + memcpy(before, got.data, before_len); + kvlangKvReadReset(kv); + + uint8_t incompatible[128] = {6, 1}; + pair.val = (kvlangXvalue_t){.data = incompatible, .len = before_len}; + err[0] = 0; + CHECK(kvlangKvSet(kv, &pair, 1, err, sizeof err) != 0); + CHECK(strstr(err, "invalid XValue") != NULL); + CHECK(kvlangKvGetOne(kv, pair.key, &got) == 0); + CHECK(got.data && got.len == before_len && + memcmp(got.data, before, before_len) == 0); + kvlangKvReadReset(kv); + + pair.val = (kvlangXvalue_t){.data = before, .len = before_len - 1}; + err[0] = 0; + CHECK(kvlangKvSet(kv, &pair, 1, err, sizeof err) != 0); + CHECK(strstr(err, "invalid XValue") != NULL); + CHECK(kvlangKvGetOne(kv, pair.key, &got) == 0); + CHECK(got.data && got.len == before_len && + memcmp(got.data, before, before_len) == 0); + kvlangKvReadReset(kv); + + pair.key = "/missing"; + pair.val = (kvlangXvalue_t){.data = incompatible, .len = before_len}; + err[0] = 0; + CHECK(kvlangKvSet(kv, &pair, 1, err, sizeof err) != 0); + CHECK(strstr(err, "invalid XValue") != NULL); + CHECK(kvlangKvGetOne(kv, pair.key, &got) == 0 && got.len == 0); + kvlangKvReadReset(kv); + + kvlangXvalue_t changed; + kvlangXvalueNewFloat64(&changed, 3.5); + pair.key = "/value"; + pair.val = changed; + CHECK(kvlangKvSet(kv, &pair, 1, err, sizeof err) == 0); + kvlangXvalueFree(&changed); + CHECK(kvlangKvGetOne(kv, pair.key, &got) == 0); + CHECK(kvlangXvalueKindIs(&got, KVSPACE_KIND_FLOAT64)); + kvlangKvReadReset(kv); + CHECK(kvlangKvSetChar(kv, pair.key, "12345678") == 0); + CHECK(kvlangKvGetOne(kv, pair.key, &got) == 0); + CHECK(kvlangXvalueKindIs(&got, KVSPACE_KIND_CHAR_UTF8)); + kvlangKvReadReset(kv); + + kvlangKvDisconnect(kv); + return failures ? 1 : 0; +} diff --git a/runtime/tests/test_param_address.c b/runtime/tests/test_param_address.c new file mode 100644 index 00000000..cc4749e6 --- /dev/null +++ b/runtime/tests/test_param_address.c @@ -0,0 +1,230 @@ +#include "rwir_internal.h" + +#include + +static int set_value(kvlangKv_t *kv, const char *key, kvlangXvalue_t *value) { + kvlangKvPair_t pair = {(char *)key, *value}; + char err[256]; + return kvlangKvSet(kv, &pair, 1, err, sizeof err); +} + +static int set_i64(kvlangKv_t *kv, const char *key, int64_t value) { + kvlangXvalue_t xv; + kvlangXvalueNewInt64(&xv, value); + int rc = set_value(kv, key, &xv); + kvlangXvalueFree(&xv); + return rc; +} + +static int64_t get_i64(kvlangKv_t *kv, const char *key) { + kvlangXvalue_t xv; + kvlangXvalueZero(&xv); + kvlangKvGetOne(kv, key, &xv); + int64_t value = kvlangScalarI64(kvlangXvalueScalar(&xv)); + kvlangXvalueFree(&xv); + return value; +} + +int main(void) { + char dir[] = "/tmp/kvlang-param-address-XXXXXX"; + char dsn[256]; + if (!mkdtemp(dir)) + return 1; + const char *scheme = getenv("KVLANG_TEST_SCHEME"); + snprintf(dsn, sizeof dsn, "%s://%s/s", scheme ? scheme : "shm", dir); + kvlangKv_t *kv = kvlangKvConnect(dsn); + if (!kv) + return 1; + char err[256]; + if (kvlangKvMkindex(kv, "/lib/f/", 0, err, sizeof err) != 0) + return 1; + + const uint8_t counts[5] = {1, 0, 1, 0, 0}; + const uint8_t read_def[] = {'x', 0, 'i', 'n', 't', '6', '4'}; + const uint8_t write_def[] = {'y', 0, 'i', 'n', 't', '6', '4'}; + const uint8_t op[] = {1, 0, 1, 0, 0, 'n', 'o', 'o', 'p'}; + const uint8_t operand[] = {0, 0, 0, 0, 0, '*', '[', '0', ',', '-', '1', ']'}; + const uint8_t write_operand[] = {0, 0, 0, 0, 0, 'z', 0, + 'i', 'n', 't', '6', '4'}; + kvlangXvalue_t anchor, read_type, write_type, opcode, read_operand, + write_slot; + kvlangXvalueNewTlv(&anchor, "rwfunc", counts, sizeof counts, 1); + kvlangXvalueNewTlv(&read_type, "def langtype", read_def, sizeof read_def, 1); + kvlangXvalueNewTlv(&write_type, "def langtype", write_def, sizeof write_def, 1); + kvlangXvalueNewTlv(&opcode, "rwir", op, sizeof op, 1); + kvlangXvalueNewTlv(&read_operand, "rwir", operand, sizeof operand, 1); + kvlangXvalueNewTlv(&write_slot, "rwir", write_operand, sizeof write_operand, 1); + int ok = set_value(kv, "/lib/f/[0,0]", &anchor) == 0 && + set_value(kv, "/lib/f.[0,-1]", &read_type) == 0 && + set_value(kv, "/lib/f.[0,1]", &write_type) == 0 && + set_value(kv, "/lib/f/[1,0]", &opcode) == 0 && + set_value(kv, "/lib/f/[1,-1]", &read_operand) == 0 && + set_value(kv, "/lib/f/[1,1]", &write_slot) == 0 && + set_i64(kv, "/vthread/vt0/[1]/x", 5) == 0 && + set_i64(kv, "/vthread/vt0/[1]/y", 3) == 0; + kvlangXvalueFree(&anchor); + kvlangXvalueFree(&read_type); + kvlangXvalueFree(&write_type); + kvlangXvalueFree(&opcode); + kvlangXvalueFree(&read_operand); + kvlangXvalueFree(&write_slot); + if (!ok) + return 1; + + kvlangParam_t reads[2] = {{.name = "/lib/f"}, {.name = "x"}}; + kvlangParam_t writes[1] = {{.name = "y"}}; + kvlangRwirInst_t inst = {.reads = reads, .nr = 2, .writes = writes, .nw = 1}; + kvlangFrame_t frame = {.kv = kv, .vtid = "vt0", + .pc = "/vthread/vt0/[1]/[1,0]", .inst = &inst}; + if (kvlangCtlCall(&frame) != 0) + return 1; + + kvlangRwirInst_t decoded; + const char *pc = "/vthread/vt0/[2]/[1,0]"; + const char *base = "/vthread/vt0/[2]/"; + if (kvlangRwirDecode(kv, base, pc, &decoded, err, sizeof err) != 0) + return 1; + ok = decoded.nr == 1 && decoded.nw == 1 && decoded.reads[0].address && + strcmp(decoded.reads[0].name, "/vthread/vt0/[1]/x") == 0 && + kvlangScalarI64(kvlangXvalueScalar(&decoded.reads[0].val)) == 5 && + strcmp(decoded.writes[0].type, "int64") == 0 && + strcmp(decoded.writes[0].name, "/vthread/vt0/[2]/z") == 0; + kvlangRwirInstFree(&decoded); + kvlangXvalue_t physical; + kvlangXvalueZero(&physical); + kvlangKvGetOne(kv, "/vthread/vt0/[2]/[1,-1]", &physical); + char *physical_target = kvlangXvaluePtrTarget(&physical); + ok = ok && kvlangXvalueIsPtr(&physical) && physical_target && + strcmp(physical_target, "/vthread/vt0/[1]/x") == 0; + free(physical_target); + kvlangXvalueFree(&physical); + kvlangXvalue_t extra; + kvlangXvalueNewInt64(&extra, 99); + ok = ok && set_value(kv, "/lib/f/[1,-2]", &extra) == 0; + kvlangXvalueFree(&extra); + if (!ok || kvlangRwirDecode(kv, base, pc, &decoded, err, sizeof err) != 0) + return 1; + ok = ok && decoded.nr == 1; + kvlangRwirInstFree(&decoded); + + const char *absolute = "/vthread/vt0/[2]/source"; + uint8_t absolute_operand[5 + 64] = {0}; + memcpy(absolute_operand + 5, absolute, strlen(absolute)); + const uint8_t result_operand[] = {0, 0, 0, 0, 0, 'r', 'e', 's', 'u', 'l', 't'}; + kvlangXvalue_t path_slot, result_slot, path_value; + kvlangXvalueNewTlv(&path_slot, "rwir", absolute_operand, + (uint32_t)(5 + strlen(absolute)), 1); + kvlangXvalueNewTlv(&result_slot, "rwir", result_operand, + sizeof result_operand, 1); + kvlangXvalueNewTlv(&opcode, "rwir", op, sizeof op, 1); + kvlangXvalueNewCharUtf8(&path_value, "/wrong"); + ok = ok && set_value(kv, "/lib/f/[2,0]", &opcode) == 0 && + set_value(kv, "/lib/f/[2,-1]", &path_slot) == 0 && + set_value(kv, "/lib/f/[2,1]", &result_slot) == 0 && + set_value(kv, absolute, &path_value) == 0; + kvlangXvalueFree(&path_slot); + kvlangXvalueFree(&result_slot); + kvlangXvalueFree(&opcode); + kvlangXvalueFree(&path_value); + const char *path_pc = "/vthread/vt0/[2]/[2,0]"; + if (!ok || kvlangRwirDecode(kv, base, path_pc, &decoded, err, sizeof err) != 0) + return 1; + kvlangFrame_t get_frame = {.kv = kv, .vtid = "vt0", .pc = path_pc, + .inst = &decoded}; + ok = ok && kvlangCGet(&get_frame) == 0; + kvlangRwirInstFree(&decoded); + kvlangXvalue_t result; + kvlangXvalueZero(&result); + kvlangKvGetOne(kv, "/vthread/vt0/[2]/result", &result); + char *result_text = kvlangXvalueValueString(&result); + ok = ok && strcmp(result_text, "/wrong") == 0; + free(result_text); + kvlangXvalueFree(&result); + + const uint8_t any_operand[] = {0, 0, 0, 0, 0, 's', 'o', 'u', 'r', 'c', 'e', + 0, 'a', 'n', 'y'}; + kvlangXvalue_t any_slot; + kvlangXvalueNewTlv(&any_slot, "rwir", any_operand, sizeof any_operand, 1); + const uint8_t one_read_op[] = {1, 0, 0, 0, 0, 'n', 'o', 'o', 'p'}; + kvlangXvalue_t one_read_opcode; + kvlangXvalueNewTlv(&one_read_opcode, "rwir", one_read_op, + sizeof one_read_op, 1); + ok = ok && set_value(kv, "/lib/f/[3,0]", &one_read_opcode) == 0 && + set_value(kv, "/lib/f/[3,-1]", &any_slot) == 0; + kvlangXvalueFree(&one_read_opcode); + kvlangXvalueFree(&any_slot); + const char *any_pc = "/vthread/vt0/[2]/[3,0]"; + if (!ok || kvlangRwirDecode(kv, base, any_pc, &decoded, err, sizeof err) != 0) + return 1; + ok = ok && decoded.nr == 1 && strcmp(decoded.reads[0].type, "any") == 0; + kvlangRwirInstFree(&decoded); + ok = ok && set_i64(kv, absolute, 17) == 0; + if (kvlangRwirDecode(kv, base, any_pc, &decoded, err, sizeof err) != 0) + return 1; + ok = ok && decoded.nr == 1 && + kvlangScalarI64(kvlangXvalueScalar(&decoded.reads[0].val)) == 17; + kvlangRwirInstFree(&decoded); + + kvlangXvalue_t outer, inner; + kvlangXvalueZero(&outer); + kvlangXvalueZero(&inner); + kvlangKvGetOne(kv, "/vthread/vt0/[2]/[0,-1]", &outer); + if (!kvlangXvalueIsPtr(&outer)) + return 1; + char *middle = kvlangXvaluePtrTarget(&outer); + kvlangXvalueFree(&outer); + kvlangKvGetOne(kv, middle, &inner); + if (!kvlangXvalueIsPtr(&inner)) + return 1; + char *source = kvlangXvaluePtrTarget(&inner); + kvlangXvalueFree(&inner); + ok = ok && strcmp(source, "/vthread/vt0/[1]/x") == 0; + free(middle); + free(source); + + kvlangXvalue_t got; + kvlangBuiltinResolveReadValue(kv, "/vthread/vt0/[2]", "*[0,-1]", NULL, &got); + ok = ok && kvlangScalarI64(kvlangXvalueScalar(&got)) == 5; + kvlangXvalueFree(&got); + ok = ok && set_i64(kv, "/vthread/vt0/[1]/x", 9) == 0; + kvlangKvDisconnect(kv); + kv = kvlangKvConnect(dsn); + if (!kv) + return 1; + frame.kv = kv; + if (kvlangRwirDecode(kv, base, pc, &decoded, err, sizeof err) != 0) + return 1; + ok = ok && decoded.nr == 1 && + kvlangScalarI64(kvlangXvalueScalar(&decoded.reads[0].val)) == 9; + kvlangRwirInstFree(&decoded); + kvlangBuiltinResolveReadValue(kv, "/vthread/vt0/[2]", "*[0,-1]", NULL, &got); + ok = ok && kvlangScalarI64(kvlangXvalueScalar(&got)) == 9; + kvlangXvalueFree(&got); + + char *dest = kvlangBuiltinResolveWriteSlot(kv, "/vthread/vt0/[2]", "*[0,1]"); + ok = ok && strcmp(dest, "/vthread/vt0/[1]/y") == 0; + ok = ok && set_i64(kv, dest, 11) == 0; + ok = ok && get_i64(kv, "/vthread/vt0/[1]/y") == 11; + free(dest); + + reads[1].name = "null"; + if (kvlangCtlCall(&frame) != 0) + return 1; + kvlangBuiltinResolveReadValue(kv, "/vthread/vt0/[2]", "*[0,-1]", NULL, &got); + ok = ok && kvlangXvalueNone(&got); + kvlangXvalueFree(&got); + + kvlangFrame_t child = {.kv = kv, .vtid = "vt0", + .pc = "/vthread/vt0/[2]/[1,0]"}; + ok = ok && kvlangCtlReturn(&child) == 0 && !child.persist_failed; + kvlangKvGetOne(kv, "/vthread/vt0/‥pc", &got); + char *resumed = kvlangXvalueValueString(&got); + ok = ok && strcmp(resumed, "/vthread/vt0/[1]/[2,0]") == 0; + free(resumed); + kvlangXvalueFree(&got); + kvlangKvGetOne(kv, "/vthread/vt0/[2]/[0,-1]", &got); + ok = ok && kvlangXvalueNone(&got); + kvlangXvalueFree(&got); + kvlangKvDisconnect(kv); + return ok ? 0 : 1; +} diff --git a/runtime/tests/test_pc_kvspace_source.c b/runtime/tests/test_pc_kvspace_source.c new file mode 100644 index 00000000..66d0c958 --- /dev/null +++ b/runtime/tests/test_pc_kvspace_source.c @@ -0,0 +1,35 @@ +#include "runtime_internal.h" + +#include +#include +#include + +int main(void) { + char dir[] = "/tmp/kvs-pc-source-XXXXXX"; + char dsn[256]; + const char *scheme = getenv("KVLANG_TEST_SCHEME"); + const char *stored = "/vthread/vt0/[1]/[1,0]"; + const char *stale = "/vthread/vt0/[1]/[999,0]"; + if (!mkdtemp(dir)) + return 1; + snprintf(dsn, sizeof dsn, "%s://%s/s", scheme ? scheme : "shm", dir); + kvlangKv_t *kv = kvlangKvConnect(dsn); + if (!kv) + return 1; + + kvlangVthreadSet(kv, "vt0", stored, "running"); + char *pc = NULL; + kvlangVthreadPcGet(kv, "vt0", &pc); + int ok = pc && strcmp(pc, stored) == 0; + free(pc); + if (ok) + ok = kvlangKvcpuExecuteMode(kv, stale, KVMODE_WATCH, NULL) == -1; + pc = NULL; + kvlangVthreadPcGet(kv, "vt0", &pc); + ok = ok && pc && strcmp(pc, stored) == 0; + if (!ok) + fprintf(stderr, "PC did not follow kvspace state\n"); + free(pc); + kvlangKvDisconnect(kv); + return ok ? 0 : 1; +} diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/01-\345\234\260\345\235\200\347\251\272\351\227\264.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/01-\345\234\260\345\235\200\347\251\272\351\227\264.kv" index 18b1d338..0155d27b 100644 --- "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/01-\345\234\260\345\235\200\347\251\272\351\227\264.kv" +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/01-\345\234\260\345\235\200\347\251\272\351\227\264.kv" @@ -11,17 +11,17 @@ kvspace 是 kvlang 的第一性前置概念:它**既是寻址空间也是内 kvspace 是文件系统风格的键值抽象。任一条目由 key 与 value 构成: - key **必须**是绝对、规范的字符串路径:以 `/` 开头,`/` 分隔层级;不得为相对路径。 -- value **必须**是一个 XValue 序列化后的字节串(TLV 编码,格式见 [[键系统与数组访问]])。 +- value **必须**是一个 XValue 序列化后的字节串(headlenpow 编码,格式见 [[XValueHead线格式]])。 - **不得**向 kvspace 直接写入裸基础类型字节;所有值必须经 XValue 编解码。违反此铁律的写入,读取方在 解码非法字节时行为未定义。 -目录以尾随 `/` 标记:key `/a/` 是目录(其 value 为 `index`),key `/a` 是普通值。同一路径的有尾斜杠 +目录以尾随 `/` 标记:key `/a/` 是目录值,key `/a` 是普通值。同一路径的有尾斜杠 与无尾斜杠形态可独立共存。 ## 扩展存储 kvspace 元存中的 value 分两类:**内联数据**(基础类型、容器元数据)与**扩展存储句柄**。句柄 value 的 -ref=2(@ext,见 [[键系统与数组访问]]),其 body 只记录目标位置描述符,真实字节位于元存之外: +storage class=3(@ext,见 [[XValueHead线格式]]),其 body 只记录目标位置描述符,真实字节位于元存之外: | 扩展位置 | 典型数据 | |---------|---------| @@ -29,7 +29,7 @@ ref=2(@ext,见 [[键系统与数组访问]]),其 body 只记录目标位 | GPU 显存 | 计算张量 | | 文件系统 / 对象存储 | 模型权重、检查点、数据集、外部进程输出 | -读取一个 ref=2 扩展句柄 value 时,由**支持 `@` 的 rwir** 按定位串还原真实字节——例如 runtime-rs 的 +读取一个 class-3 扩展句柄 value 时,由**支持 `@` 的 rwir** 按定位串还原真实字节——例如 runtime-rs 的 networld/fs 一族 rwir 直接以 `@` 开头的扩展 key 为输入(`runtime-rs/src/engine.rs` 的 `read_at` / `resolve_ext`)。核心 runtime-c 不解引用 `@`。 diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/03-\345\257\273\345\235\200\344\270\216\345\221\275\345\220\215.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/03-\345\257\273\345\235\200\344\270\216\345\221\275\345\220\215.kv" index 9a02411b..faa4b52a 100644 --- "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/03-\345\257\273\345\235\200\344\270\216\345\221\275\345\220\215.kv" +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/03-\345\257\273\345\235\200\344\270\216\345\221\275\345\220\215.kv" @@ -56,9 +56,8 @@ kvspace 路径与 XValue 的 langtype 共用一套分隔符语义,每个分隔 | 分隔符 | 码位 | 语义 | |--------|------|------| | `/` | U+002F | 层级;目录以尾随 `/` 标记 | -| `·` | U+00B7 | **两种独立含义,正交**(见 [[总体方案]]):①**memindex 标记**——值容器成员索引 `{key}·`(仅 stringkeymap/struct 用),以及成员访问 `base·field`、`m·[i]`;②**包·函数命名分隔符**——`/lib/{pkg}·{func}` 中路径段名字的一部分,rwfunc 目录成员仍走 `/` 子路径,与 memindex 无关 | +| `·` | U+00B7 | 值容器的物理成员键 `base·field`、`m·[i]`,也是 `/lib/{pkg}·{func}` 中包与函数的命名分隔符;rwfunc 目录成员走 `/` 子路径 | | `‥` | U+2025 | 运行时系统变量前缀(`X/‥pc`,见 [[系统变量]]) | -| `…` | U+2026 | extindex 句柄 body 的首元素前缀 | 标识符**不得**以 `‥` 开头,故用户代码永远造不出 `‥` 前缀段,系统变量命名空间与用户命名空间零交集(见 [[键系统与数组访问]] 的键形态三分)。 diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/06-XValueHead\347\272\277\346\240\274\345\274\217.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/06-XValueHead\347\272\277\346\240\274\345\274\217.kv" index 87360fa4..573d5460 100644 --- "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/06-XValueHead\347\272\277\346\240\274\345\274\217.kv" +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/06-XValueHead\347\272\277\346\240\274\345\274\217.kv" @@ -2,38 +2,25 @@ lib kvlang/spec/kvspace模型/XValueHead线格式 { r####"# XValueHead 线格式 -XValue = head + body。head 由三正交字段(ref × storetype × langtype)与辅助字段构成,langtype 串为语 -义类型真相(`kvspace/include/kvspace/kvspace.h`、`kvspace-c/src/xvalue.h`、 -`kvspace-durable/src/xvalue.rs`,三处 byte-identical): +XValue 由 head 与 body 组成。所有后端使用同一线格式: ``` -head = [headlen u16 LE][ref u8][storetype u8][ro u8][vid u32 LE][body_len u32 LE] - [storetype 物理字段(变长,按 storetype)][langtype 串(占至 headlen)] -body = [body_len B raw] +[pow:u8][flags:u8][a:u64 LE][b:u64 LE][langtype UTF-8][zero padding][body] ``` -| 字段 | 大小 | 含义 | -|------|------|------| -| `headlen` | 2B LE | head 总字节数;body 起于偏移 `headlen` | -| `ref` | 1B | 存储位置:0=inline、1=ptr(body=目标 key 路径)、2=@ext(body=扩展世界定位符) | -| `storetype` | 1B | 物理布局(codec 唯一分派):NONE/ATOM/ARRAYND/index/extindex | -| `ro` | 1B | 1=只读,0=可写 | -| `vid` | 4B LE | vthread id(默认 0) | -| `body_len` | 4B LE | body 字节数 | -| (storetype 物理字段) | 变 | ARRAYND:ndim u8 + dims[ndim] u32 LE;index/extindex:**成员枚举载体**(shm 载体为成员名 m×n 对齐字符串数组,dims=[len,cap,M],len=成员数、M align8、cap 预留容量;fs 载体把成员落成 `{key}·/` 子目录);值容器主槽 body 恒空、dims 空 | -| `langtype` | 变长 | 类型表达式串,占至 `headlen`(长度 = headlen − 当前偏移,无独立长度字段) | +固定前缀为 18 字节。head 长度是 `1 << pow`,body 从该偏移开始。flags 低两位为 storage class;bit 2 为指针标志。未使用的标志位必须为零。langtype 为真实目标类型,不含源码的 `*`/`@` 前缀,后面以零填充至 head 末尾。解码须校验填充、长度和类型。 -`langtype` 恒为 head 最后一段,无独立长度字段;body 起始偏移 = `headlen`。ref 与 storetype 各为独立 -u8 字节,不再编码进 langtype 串。ARRAYND 的 ndim、dims 直接读自 storetype 物理字段,与 langtype 串内 -`[dims]` 保持同步(由 codec 保证)。`char/*` kind 恒为 ARRAYND(ndim=1)。None 编码: -storetype=NONE、langtype=""、body 空。 +| class | a | b | body | +|-------|---|---|------| +| 0(短定长) | 0 | 0 | None、标量、目录、map 头等的定长内容;rwfunc 目录值为空 | +| 1(有冗余的长度) | 有效字节数 | 容量,且 ≥ a | 字符/字节数组、代码槽、指针路径;`[0,0]` rwfunc 锚点的 a=5、b≥5 | +| 2(大定长) | 元素数 | 元素宽度 | 张量元素按行主序连续存储 | +| 3(扩展定位) | UTF-8 定位串字节数 | 容量,且 ≥ a | `@ext` 定位串 | -index/extindex 的**语义是"可枚举成员"**(`kvspaceListLen` / `kvspaceListAt`),物理载体按后端而异:shm 把 -成员名收进 head 的 m×n 矩阵,fs 把成员落成 `{key}·/` 子目录——载体差异经对称编解码还原,**不构成 ABI -语义差异**(见 [[地址空间]])。值容器(stringkeymap/struct)主槽只持类型与形状,成员在 `{key}·`。 +UTF-8 字符数组使用 class 1,`a` 是**字节数**,langtype 里的长度是 Unicode 码点数;两者独立校验。指针使用 class 1 加 flags bit 2,body 是目标 KV 路径;目标类型写入 langtype。 -指针(ref=1)的 head langtype = 目标完整 langtype(langtype,含 `[dims]`),storetype = 目标 -storetype,body = 目标 key 路径;由 `kvspaceNewPtr(target_langtype, target)` 构造,Set 时据此单跳类 -型检查。 - "#### -> /lib/kvlang/spec/kvspace模型/XValueHead线格式 +`ro` 与 `vid` 不在 XValue 中。它们存于独立的 KV key:`/.kvspace-meta/` 加原 key UTF-8 字节的小写十六进制编码。读 head 时后端合并这两个元数据字段,复制和删除 key 时同步处理 sidecar。枚举成员按实际物理 key 前缀进行,不存在 index/extindex 成员矩阵或 memindex 载体。 + +目录 key 以 `/` 结尾,值与不带斜杠的 base key 独立。例如 `/lib/f/` 的 rwfunc 目录值为 class 0、空 body;`/lib/f/[0,0]` 是 class 1、5 字节计数头。map 的 base key 为 class 0、空 body,成员位于 `base·name` 的独立 key。扩展目录叠加通过 class 3 定位串表示。 +"#### -> /lib/kvlang/spec/kvspace模型/XValueHead线格式 } diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/07-\346\225\260\347\273\204\344\270\244\347\247\215\347\211\251\347\220\206\345\275\242\346\200\201.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/07-\346\225\260\347\273\204\344\270\244\347\247\215\347\211\251\347\220\206\345\275\242\346\200\201.kv" index 5410d421..07296b0c 100644 --- "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/07-\346\225\260\347\273\204\344\270\244\347\247\215\347\211\251\347\220\206\345\275\242\346\200\201.kv" +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/07-\346\225\260\347\273\204\344\270\244\347\247\215\347\211\251\347\220\206\345\275\242\346\200\201.kv" @@ -9,11 +9,10 @@ lib kvlang/spec/kvspace模型/数组两种物理形态 { | **compact** | 元素 kind(`int64`…) | `[1,2,3]`、`[N]T`、`[d0,d1]T` | 连续打包进单个 XValue 的 body | 1 | | **stringkeymap** | `stringkeymap` | `{v0,v1,…}`、`array·scatter` 等 | 每元素落独立子 key `base·[i]` | N+1 | -- **compact**:元素连续打包进一个 XValue,`raw_len = ∏dims × 元素字节宽`,head kind 即元素类型、 - ndim≥1。对齐 C `int[10]`、Go `[10]int`、Rust `[i32;10]`;多维即连续布局的 ndarray/tensor。要求元素 +- **compact**:元素连续打包进一个 XValue,`raw_len = ∏dims × 元素字节宽`,langtype 记录元素类型与形状。对齐 C `int[10]`、Go `[10]int`、Rust `[i32;10]`;多维即连续布局的 ndarray/tensor。要求元素 定长同类型——含变长字符串字面量的 `[…]` 在 layout 阶段报错。支持 `arr[i]` 随机访问(下标读经 `xv·at` 在 body 内定位),可零拷贝整块读。 -- **stringkeymap**:head kind=`stringkeymap`、ndim≥1,body 只记形状;每个元素是独立子 key,成员名即 +- **stringkeymap**:base key 的 langtype 为 map 类型、body 为空;每个元素是独立子 key,成员名即 **memitemkey 值逐字**(逐字铁律见 [[文法与合法性]]):裸标量键落 `b·200`、标量元组键落 `a·[342.124,23.2345]`、字符串键落 `m·a`;由 `scatter` 展开的数组元素落坐标段 `base·[i]`。变长、可 增长、允许变长元素——字符串数组、可追加数组走此形态。stringkeymap **必须** ndim≥1(无维度的键值容器 @@ -50,21 +49,19 @@ stringkeymap 元素的成员名是坐标段,物理 key 形如 `m·[i]`(一 ## ref(存储位置维)与扩展句柄 -| ref | 语义 | body | -|-----|------|------| -| 0(inline) | 内联值 | 数据本体 | -| 1(ptr) | kvspace 内指针 | 目标 key 路径 | -| 2(@ext) | 扩展存储句柄 | 元存外位置描述符 | +| head 形态 | 语义 | body | +|-----------|------|------| +| 指针位 0、class 2 | 内联 tensor | 数据本体 | +| 指针位 1、class 1 | kvspace 内指针 | 目标 key 路径 | +| class 3 | @ext 扩展存储句柄 | 元存外位置描述符 | -ref=2 表示值本体在扩展世界(SHM/GPU/文件),head 中 langtype 记录形态(如 `[256,256]uint8`),body -记位置描述符。@ext 只表**单个值**存于扩展世界(compact 形态),与 storetype=index/extindex 无关。读 +class 3 表示值本体在扩展世界(SHM/GPU/文件),head 中 langtype 记录形态(如 `[256,256]uint8`),body +记位置描述符。@ext 只表**单个值**存于扩展世界(compact 形态),与目录成员枚举无关。读 取时由支持 `@` 的 rwir(如 runtime-rs 的 fs/networld 一族)按定位串兑现(见 [[地址空间]] 扩展存储)。 -## memindex:定宽目录矩阵 +## 成员枚举 -storetype=index/extindex 的 head 物理字段记成员名 m×n 对齐字符串数组,参数 `dims=[len, cap, M]`,`M` -向上 8 对齐;`len` 为当前项数、`cap` 为预留容量、`M` 为行宽;主槽 body 恒空。`kvspaceMkindex` 带 -`capacity` 参数、构造器 `kvspaceXvalueNewIndexGrow(children, count, cap_hint, m_hint)` -(`kvspace-c/src/xvalue.h`)据此保留容量与行宽,使增删可就地覆写、不重分配。 - "#### -> /lib/kvlang/spec/kvspace模型/数组两种物理形态 +散 key 数组的 base key 是 class 0、空 body 的 map 头;元素落在 `base·[i]` 等物理 key。KVSpace 按此前缀枚举成员,不保存独立索引矩阵。`kvspaceMkindex` 为目录创建 key,目录成员按 `/` 前缀枚举。 + +"#### -> /lib/kvlang/spec/kvspace模型/数组两种物理形态 } diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/08-\346\214\207\344\273\244\345\270\203\345\261\200\346\240\274\345\274\217.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/08-\346\214\207\344\273\244\345\270\203\345\261\200\346\240\274\345\274\217.kv" index c78b95d7..ccd2a6a7 100644 --- "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/08-\346\214\207\344\273\244\345\270\203\345\261\200\346\240\274\345\274\217.kv" +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/08-\346\214\207\344\273\244\345\270\203\345\261\200\346\240\274\345\274\217.kv" @@ -35,7 +35,7 @@ body = 参数名 + NUL + 该参数的 **def langtype**(匹配类型)串— `def rwir` 则相反:`/lib/` 节点自身 langtype = `def rwir`(裸种类名)、body 同计数格式,各参数类型落**位置化**签 名 -行 `[0,x]` 子槽(`x<0` 读参、`x>0` 写参,storetype=ATOM、langtype=`def langtype`,body 为该参数 +行 `[0,x]` 子槽(`x<0` 读参、`x>0` 写参,class=1、langtype=`def langtype`,body 为该参数 langtype 串),无 `{param}` 命名键、无实现指令行——跨 runtime 队列按位置收参故须有序签名(见 [[总体方 案]])。 @@ -44,21 +44,26 @@ langtype 串),无 `{param}` 命名键、无实现指令行——跨 runtime 以 `id_val(x:int64) -> (R:int64) { int64·add(x, 0) -> R }` 为例,`/lib/id_val/` 下: ``` -/lib/id_val/ storetype=index langtype=index ← 普通目录节点(无值) -[0,0] storetype=index langtype=rwfunc body=[nr,nw,dyn] ← 签名行锚点(s0=0),只记计数头 +/lib/id_val/ class=0 langtype=rwfunc body=empty ← 函数目录 +[0,0] class=1 langtype=rwfunc body=[nr,nw,dyn] ← 签名行锚点(s0=0) .[0,-1] def langtype:"x\0int64" ← 参数定义键(点后缀):参数名 + NUL + def langtype 串 .[0,1] def langtype:"R\0int64" ← 参数定义键(写参) -[1,0] storetype=ATOM langtype=rwir|rwfunc body=counts+"int64·add" ← 指令0 opcode -[1,-1] storetype=ATOM langtype=rwir body=counts+"[0,-1]" ← 指令0 读槽1(形参已换成帧坐标) -[1,-2] storetype=ATOM langtype=int64 body=raw(8B) ← 指令0 读槽2(内联字面量,非 rwir、无 counts) -[1,1] storetype=ATOM langtype=rwir body=counts+"[0,1]" ← 指令0 写槽1 +[1,0] class=1 langtype=rwir body=counts+"int64·add" ← 指令0 opcode +[1,-1] class=1 langtype=rwir body=counts+"*[0,-1]\0int64" ← 指令0 读槽1 +[1,-2] class=0 langtype=int64 body=raw(8B) ← 指令0 读槽2(内联字面量,非 rwir、无 counts) +[1,1] class=1 langtype=rwir body=counts+"*[0,1]\0int64" ← 指令0 写槽1 ``` -opcode 槽 langtype 为 `rwir|rwfunc`——这是**派发标记**(layout 不静态判定目标是 runtime 兑现的 rwir 还 -是用户 rwfunc,判定推迟到 runtime 查 `/lib/`,见 [[指令槽编码]]),非值语言类型。 +opcode 槽由 layout 按目标写 `rwir` 或 `rwfunc`;两者的 body 均为 5 字节 counts 加目标名。 + +`/lib` 中的读写槽是共享的布局模板。runtime 首次解码执行帧的 `[s0,±j]` 时,在同坐标写入物理 +`*T` XValue,body 为目标 KV key;此后从执行帧读取该指针并回读目标值。内联字面量先落在该帧的 +`‥operand[s0,±j]` KV key,再由物理槽指向它。帧内槽覆盖 extindex 的读取结果,不修改共享模板; +opcode `[s0,0]` 仍从 `/lib` 读取。 **`rwir` 值的 body 格式(唯一)**:`counts` = `[nr:u16 LE][nw:u16 LE][dynamic:u8]`(5 字节,指令槽恒 -`0,0,0`)+ 槽文本;文本是 opcode 名,或变量引用(相对指针 `[0,±k]`)。**内联字面量不是 `rwir`**——按字 +`0,0,0`)+ 槽文本;文本是 opcode 名,或变量引用(相对指针 `*[0,±k]`)。已知目标类型的读写槽 +在文本后附 NUL 和类型串;目标已有值时 runtime 以实际 XValue head 的类型为准。**内联字面量不是 `rwir`**——按字 面量自身的 kind 编码(如 `int64` 的 8 字节 raw),**无** `counts`。故读/写槽的值两类:`rwir`(counts + 文本)或内联字面量(如 `int64raw`、`char/utf32` 串)。opcode 槽永不存变量引用,故值拷贝 `=` 与函数调用 `call` 在 KV 层无歧义。 diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/09-\350\260\203\347\224\250extindex.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/09-\350\260\203\347\224\250extindex.kv" index d40af78a..5cbd1b9c 100644 --- "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/09-\350\260\203\347\224\250extindex.kv" +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/09-\350\260\203\347\224\250extindex.kv" @@ -1,50 +1,15 @@ // 欢迎加入kvspace世界 lib kvlang/spec/kvspace模型/调用extindex { - r####"# 调用:extindex 复用指令树 + r####"# 调用:扩展目录复用指令树 -调用一个函数时,运行时**不**拷贝指令,而是为本次调用建帧子树,令帧根成为指向 `/lib/{pkg}·{name}/` 指 -令树的 extindex 叠加层,再把实参 / 结果地址写进帧根下的 `[0,±j]` 槽: +runtime 为每次调用创建帧子树,帧根通过 class 3 扩展定位指向 `/lib/{pkg}·{name}/` 的只读指令目录。指令树不复制,参数绑定、局部变量、PC 和返回地址都写入 KVSpace。 -- `/lib/{pkg}·{name}.[0,±j]`(rwfunc 的**参数定义键**,点后缀、与坐标斜杠键 `/[0,±j]` 不同名): - langtype=`def langtype`,body=该参数名字 + NUL + **def langtype**(匹配类型)串。此键是**签名元数据**(dump / 类型检查 - 用),**不参与运行时形参解析**;函数体指令里的形参引用已在 layout 编译期替换为帧坐标(值参 `[0,-1]` - 直读,地址参 `*[0,-1]` 解引用,见 [[函数]])。目录根**无** `/[0,±j]` 静态坐标槽——运行时向 - `frameRoot/[0,±j]` 写实参槽时, - 该坐标在只读 extindex 目标(函数树)中并不存在,extindex 写保护正因**该名在扩展层缺席而放行**帧本 - 地写,实参内容零拷贝。(反之若 layout 预落 `/[0,±j]` 静态槽,写保护会因扩展层已有同名只读节点而拒 - 写、丢参——故 rwfunc 目录根不落这些坐标。) -- 所有帧共享 `/lib` 下同一份指令树,零拷贝;帧根下叠加各自的实参槽与局部变量。 +`/lib/{pkg}·{name}.[0,±k]` 是静态参数定义键,类型为 `def langtype`,body 为参数名、NUL、匹配类型串。它与帧根的 `/[0,±k]` 坐标不同名,因而帧内可以保存本次调用的参数指针。 -崩溃恢复由此自然成立:PC 是路径字符串、帧根落 KV、返回点落 KV——进程重启后可从 kvspace 续跑。 +每个调用点参数先存为指向目标值 key 的 `*T`;帧槽 `[0,±k]` 再存指向该参数指针的 `**T`。layout 将体内形参名替换为 `*[0,±k]`。runtime 从帧槽自动取出参数指针,普通 rwir 再取目标值。写参沿链直接写到调用方 key,读参由 layout 保证只读。字面量先在 KVSpace 中物化,再通过相同的指针链传入。 -## 参数解析:值参直读、地址参一层引用 +KVSpace 的 Set/Del 不自动穿透指针整键;runtime 显式解析目标路径后再写。这样绑定帧槽不会改写实参。 -layout 编译期把形参名替换为**帧槽坐标** `[0,-k]`(值参)或**对帧槽坐标的解引用** `*[0,-k]`(地址参,即 -声明带 `*`/`@`;见 [[函数]])。两种帧槽由调用点写入(见 [[函数调用]]): - -``` -值参 x:T :frameRoot/[0,-k] → 值本体(本帧副本) 体内 [0,-k] 只读(param<0 只读) -地址参 p:*T :frameRoot/[0,-k] → 实参 key 路径 Ptr 体内 *[0,-k] 解引用取实参值 -扩展参 h:@T :frameRoot/[0,-k] → 扩展世界定位串(ref=2)由支持 @ 的 rwir 兑现(如 runtime-rs 的 fs/networld 族) -``` - -**读参(`param<0`)一律只读**:值读参的槽是本帧副本、地址读参 `*[0,-k]` 读到实参值,两者都不得作写槽。 -**写参(`param>0`)可读可写**:地址写参 `*[0,+k]` 直写调用方目标位置;值写参按 [[系统变量]] 的 `‥wdst` -表 copy-in / copy-out。`@` 参数的帧槽是扩展定位串——runtime-c 不可直达,读它须按定位串前缀由支持 `@` 的 -rwir(在支持它的 runtime 里,如 runtime-rs 的 fs/networld 一族)按 head 的 ref=2 定位串兑现(见 -[[ref存储位置]])。形参类型随帧槽的 XValue langtype 承载,不在解析链上额外查参数定义键。 - -写侧关键约束:kvspace 对 Set/Del **不穿透整键**(Ptr 键的写/删只作用该键本身,不追 target)——自动 -穿透会把「写实参地址 Ptr 到帧槽」重定向到 target,导致丢参 / 写坏被指向的值。解引用一律由 runtime 显 -式 `*` 掌控(见 [[ptr]])。 - -## init 与标签 - -- lib 内的裸指令收集为隐式 `init` 函数(无 `init` 关键字),同样以上述布局落 `/lib/{pkg}·init/`。 -- 具名块的标签落 `/lib/{pkg}·{name}/‥labels/{label}` → 指令序号(`layout/src/code.rs`、 - `layout/src/keytree.rs`)。 - -签名类型表达式文法见 [[文法]];帧的系统变量见 [[系统变量]];坐标键与 langtype 的分隔符语义见 [[键系 -统与数组访问]]。 - "#### -> /lib/kvlang/spec/kvspace模型/调用extindex +PC 位于 `/vthread//‥pc`,返回地址位于帧的 `‥returnpc`。worker 重启后从 KVSpace 恢复执行。 +"#### -> /lib/kvlang/spec/kvspace模型/调用extindex } diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/10-\347\263\273\347\273\237\345\217\230\351\207\217.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/10-\347\263\273\347\273\237\345\217\230\351\207\217.kv" index 91dd61e9..68dd694c 100644 --- "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/10-\347\263\273\347\273\237\345\217\230\351\207\217.kv" +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/10-\347\263\273\347\273\237\345\217\230\351\207\217.kv" @@ -30,12 +30,10 @@ lib kvlang/spec/kvspace模型/系统变量 { | `‥lib` | rwfunc 帧的 lib 路径,用于识别 rwfunc 帧边界 | | `‥callpc` | 帧内执行进度(每 op 更新;块帧重入亦更新) | | `‥returnpc` | 返回地址(帧创建时固化;块帧仅首次设置,重入不覆写) | -| `‥wdst` | **值写参的目标表**(仅当本帧有值写参时落):每行一个写参的调用方目标 key,行序即写参槽序,地址写参留空行。返回时据此 copy-out | +| `‥arg[0,±k]` | 本次调用的参数指针 `*T`;帧槽 `[0,±k]` 指向它 | +| `._litN` | 字面量实参物化后的 KV 值 | -**值写参的 copy-in / copy-out**(见 [[函数]]):调用点把调用方该位置的当前值**复制**进帧槽 -`[0,+k]`(累加器模式据此拿初值),并把目标 key 记进 `‥wdst`;被调方体内直接读写 `[0,+k]`;返回时 -runtime 按 `‥wdst` 把 `[0,+k]` **复制回**目标。地址写参(声明带 `*`/`@`)不走这套——槽里是调用方目 -标的地址 Ptr,体内 `*[0,+k]` 直写。 +写参沿两层指针链直接到达调用方目标,不使用值参副本或返回时复制。 调用点如何写实参 / 结果槽、如何经指针链解析读到重定向路径,见 [[指令布局格式]]。 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/01-\347\247\215\347\261\273\344\270\216\345\256\232\345\256\275\347\261\273\345\236\213.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/01-\347\247\215\347\261\273\344\270\216\345\256\232\345\256\275\347\261\273\345\236\213.kv" index 48582dc4..2a3f8272 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/01-\347\247\215\347\261\273\344\270\216\345\256\232\345\256\275\347\261\273\345\236\213.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/01-\347\247\215\347\261\273\344\270\216\345\256\232\345\256\275\347\261\273\345\236\213.kv" @@ -4,11 +4,7 @@ lib kvlang/spec/类型系统/种类与定宽类型 { kvlang 是**严格定宽类型语言**。 每个变量、参数、返回值都有确定的种类(kind);无无类型变量,运行时种类不隐式改变。 -类型信息随值自描述:每个 XValue 的类别由 head 三个正交字段表达(见 [[总体方案]] 与 [[XValueHead线格 -式]]): -- `ref`(存储位置):0=inline、1=ptr(软链)、2=@ext(单值在扩展世界); -- `storetype`(物理布局,codec 视角,闭合集:NONE/ATOM/ARRAYND/index/extindex); -- `langtype`(语义类型,完整类型表达式串,变长,见 [[langtype签名类型表达式]])。 +类型信息随值自描述:head 的 flags 给出四类存储规则和指针位,langtype 给出完整语义类型;@ext 是 class 3。ro/vid 由独立 KV 元数据保存(见 [[XValueHead线格式]])。 ## 锚例 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/02-\347\247\215\347\261\273-\346\240\207\351\207\217\346\227\266\351\227\264\345\256\271\345\231\250.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/02-\347\247\215\347\261\273-\346\240\207\351\207\217\346\227\266\351\227\264\345\256\271\345\231\250.kv" index 8a794165..799b9c5c 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/02-\347\247\215\347\261\273-\346\240\207\351\207\217\346\227\266\351\227\264\345\256\271\345\231\250.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/02-\347\247\215\347\261\273-\346\240\207\351\207\217\346\227\266\351\227\264\345\256\271\345\231\250.kv" @@ -57,10 +57,8 @@ kvlang 的数字种类**必须携带精确位宽**: |----|------|------| | `stringkeymap` | 种类(kind) | map 容器,langtype 为 `key·value`(如 `[]char/utf8·int64`) | | `struct` | 种类(kind) | 结构原型实例的种类(见 [[struct]]) | -| `index` | **storetype**(非 kind) | 索引型节点(map/struct 值容器的 `{key}·` memindex、lib/rwfunc 目录节点);成员枚举载体 | -| `extindex` | **storetype**(非 kind) | 可扩展索引型节点(vthread 运行栈等,cap 可增长);与 `ref=2`(@ext) 无关 | -容器的物理组成(值 + memindex)见 [[map容器]](struct 同此物理模型)。 +map/struct 的 base key 为 class 0 空值,成员使用物理 `base·member` key;函数和 lib 使用目录子路径。容器物理结构见 [[map容器]]。 ### 时间种类 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" index ac23f00c..7cc61342 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" @@ -1,34 +1,14 @@ // 欢迎加入kvspace世界 lib kvlang/spec/类型系统/种类_代码与特殊值 { - r####"# 种类(kind)—— 代码节点与特殊值 + r####"# 代码类型与特殊值 -### 代码种类 +- `rwir`:无 kvlang 实现体,调用槽为 class 1,body 为 5 字节计数头加目标名,由 runtime 兑现。 +- `rwfunc`:有 kvlang 实现体。目录值为 class 0 空 body;`[0,0]` 签名锚点为 class 1、5 字节;调用槽为 class 1、计数头加函数名。 +- `def rwir`:仅位于 `/lib/` 的 class 0 路由头,body 为 5 字节计数头。各 runtime 用 myrwircaps 声明可兑现能力。 +- `None`:class 0、空 langtype、空 body。缺值运算报 TypeError。 +- ptr:class 1 加指针标志,langtype 为目标真实类型,body 为目标 KV 路径。空指针用 None。 +- `@ext`:class 3,langtype 为目标真实类型,body 为 UTF-8 定位串。 -下列 langtype 描述 KV 树中的代码与目录节点(见 [[指令布局格式]]): - -- `rwir`:无 kvlang 实现体,storetype=ATOM,body = `[nr:u16 LE][nw:u16 LE][dynamic:u8]`(指令槽恒 - `0,0,0`)+ 指令名/引用文本;由 runtime 经 def rwir 路由直接兑现。 -- `def rwir`:**路由头**,仅存于 `/lib/`,是 rwir 统一调度入口,storetype=index(定义节点),** - 无 kvlang 实现体**;**不叫"扩展算子"**,**不得**出现在用户书写的类型标注中。每个 runtime 只声明 - **myrwircaps**(该 runtime 能兑现的 rwir 能力集);rwir 若不在本 runtime myrwircaps 内须经 - `/lib/` 的 def rwir 路由给能兑现它的其它 runtime。 -- `rwfunc`:有 kvlang 实现体(`/` 目录下的指令槽序列),storetype=index(静态定义)或 extindex - (vthread 运行栈),可被任意 main-runtime 直接解释执行。 - -**故有 def rwir 而无 def rwfunc**:rwfunc 有 kvlang 实现体(一串 rwir),任意 main-runtime 逐条解释 -即可,无需路由头;rwir 无实现体,不在本 runtime myrwircaps 内时必须路由,故需 def rwir。 - -### None 与 ptr 不是种类 - -- **None** 不是种类:它是 `storetype=NONE` 的空结构体 XValue。任何有名变量都有地址、都有 XValue;未 - 赋值即 None。None 参与算术、比较、分支条件、类型构造一律 TypeError(见下文与 [[执行模型]])。 -- **ptr(指针)** 不是种类:它是 head 的 `ref=1`(存储位置维),head 的 storetype/langtype 描述目 - 标的完整形态,body 存目标 key 路径(见 [[map容器]])。**空指针就是 None**(无值),不设"空 Ptr"、也 - 不用 `""` 冒充(见 [[ptr]])。 - -> ptr(`ref=1`)是 head 的 ref 字段(存储位置维);kvlang **源码**类型标注最前仍可书写 `*`/`@` 前缀 - (如 `*int64`、`@[256,256]uint8`),表 ref,layout 解析时剥离该前缀落成 head `ref` 独立字节; - **wire 层 langtype 串不含 `*`/`@` 前缀**,存储位置一律经 head `ref` 字节表达。extindex 是 - storetype(物理布局维),与 ref=2 无关(见 [[总体方案]])。 +源码 `*`/`@` 前缀不写入 wire langtype。ro/vid 位于独立元数据 key。 "#### -> /lib/kvlang/spec/类型系统/种类_代码与特殊值 } diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/05-langtype\347\255\276\345\220\215\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/05-langtype\347\255\276\345\220\215\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217.kv" index 4ca21840..28a142fe 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/05-langtype\347\255\276\345\220\215\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/05-langtype\347\255\276\345\220\215\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217.kv" @@ -21,8 +21,8 @@ lib kvlang/spec/类型系统/langtype签名类型表达式 { langtype** 对参数定义的 **def langtype** 逐值判定:命中即通过,失配报 TypeError(见 [[匹配语义与变参]])。 参数变参 `...` 是签名层 arity 构造,**不在** def langtype 串内(见 [[函数]])。 -**唯一例外**是 opcode 槽 `[s0,0]` 的 `rwir|rwfunc`——它是**派发标记**(layout 不静态判定目标是 rwir 还是 -rwfunc,交 runtime 查 `/lib/` 定,见 [[指令槽编码]]),既不是值类型、也不属 def langtype。 +opcode 槽 `[s0,0]` 使用实际的 `rwir` 或 `rwfunc` langtype;layout 在生成时区分已知调用目标, +runtime 再查 `/lib/` 分派(见 [[指令槽编码]])。 ## kindexpr(简写) diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/09-\346\225\260\347\273\204\345\275\242\346\200\201.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/09-\346\225\260\347\273\204\345\275\242\346\200\201.kv" index 01659bd0..eb1a30da 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/09-\346\225\260\347\273\204\345\275\242\346\200\201.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/09-\346\225\260\347\273\204\345\275\242\346\200\201.kv" @@ -22,8 +22,8 @@ kvlang 的数组有**两种物理形态**:**compact**(稠密:全量元素 | 字面量 | 形态 | 落库 | 约束 | |--------|------|------|------| -| `[e0, e1, …]` | compact | 元素连续打包进单个 XValue,head 的 langtype(langtype)= 元素种类(如 `[4]int64`),storetype=ARRAYND | 定长、同种类;元素**不得**为变长(字符串字面量禁入) | -| `{v0, v1, …}` | stringkeymap | 每元素落 `base·[i]` 坐标段独立子 key,容器值 langtype=map langtype(如 `[]char/utf8·int64`),storetype=index,主槽 body 空,成员名索引落 `base·` memindex | 变长/可增长;字符串数组等变长集合用此形式 | +| `[e0, e1, …]` | compact | 元素连续打包进单个 XValue,langtype 含形状与元素种类(如 `[4]int64`),storetype 为 class 2 | 定长、同种类;元素**不得**为变长(字符串字面量禁入) | +| `{v0, v1, …}` | stringkeymap | 每元素落 `base·[i]` 坐标段独立子 key,容器值 langtype=map langtype(如 `[]char/utf8·int64`),class 0 容器头 body 空,成员按物理 key 前缀枚举 | 变长/可增长;字符串数组等变长集合用此形式 | - compact 字面量降级为 `array(…)` 构造。变长字符串元素进 compact `[…]` 在 layout 语法检查期报错 (`compact array [...] cannot hold variable-length string element`)。 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/10-compact\345\275\242\346\200\201.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/10-compact\345\275\242\346\200\201.kv" index ad89735f..7907de6a 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/10-compact\345\275\242\346\200\201.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/10-compact\345\275\242\346\200\201.kv" @@ -3,14 +3,14 @@ lib kvlang/spec/类型系统/compact形态 { r####"# compact 形态 compact 数组是**单个 XValue**:head 的 langtype(langtype)形如 `[4]int64`(含维度+元素种类), -storetype=ARRAYND,body 是连续的定宽元素字节。 +storetype 为长定长类(class 2),body 是连续的定宽元素字节。 ```kv a:[]int64 = [10, 20, 30, 40] // head langtype = [4]int64,body = 4×8 字节 ``` - head 的 langtype = langtype 串形如 `[4]int64`(元素种类为 `int64`,**非** `array`); - storetype=ARRAYND(见 `tutorial/04-ndarray/xv_meta.kv`)。 + storetype 为 class 2(见 `tutorial/04-ndarray/xv_meta.kv`)。 - 字符串是 compact 的字符序列:`s:char/utf8 = "hi"` 的 langtype 为 `[2]char/utf8`。单个字符串是 compact;**字符串的数组**是变长集合,必须 stringkeymap。 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/11-stringkeymap\345\275\242\346\200\201.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/11-stringkeymap\345\275\242\346\200\201.kv" index 23ad4486..d08d8342 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/11-stringkeymap\345\275\242\346\200\201.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/11-stringkeymap\345\275\242\346\200\201.kv" @@ -2,9 +2,7 @@ lib kvlang/spec/类型系统/stringkeymap形态 { r####"# stringkeymap 形态 -stringkeymap 数组的**容器值**是一个 storetype=index 的 XValue(langtype 为 map langtype,如 -`[]char/utf8·int64`;body 空,成员名索引落 `base·` memindex,dims=[len,cap,M]),**每个元素**是独立 -子 key `base·[i]`(坐标段,见 [[数组两种物理形态]]): +stringkeymap 数组的容器值是 class 0、空 body 的 XValue(langtype 为完整 map 类型,如 `[int64]·int64`),每个元素在独立的 `base·[i]` key 中;成员由物理 key 前缀枚举: ```kv nums:[int64]·int64 = {10, 20, 30, 40} // 每元素落 nums·[0] … nums·[3] diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/12-\347\250\240\345\257\206vs\347\250\200\347\226\217.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/12-\347\250\240\345\257\206vs\347\250\200\347\226\217.kv" index 15a44c0d..8512ba5d 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/12-\347\250\240\345\257\206vs\347\250\200\347\226\217.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/12-\347\250\240\345\257\206vs\347\250\200\347\226\217.kv" @@ -7,15 +7,15 @@ lib kvlang/spec/类型系统/稠密vs稀疏 { | 维度 | compact(稠密) | stringkeymap(稀疏) | |------|----------------|---------------------| | langtype | `[1024,1024]int32` | 坐标元组键 map,如 `[int32,int32]·int32` | -| storetype | ARRAYND | index | +| storetype | class 2 长定长 | class 0 短定长 | | 落库 | 全量 1024×1024 元素连续打包进**单个** XValue 的 body(恒 1024×1024×4 = 4 MiB,未写元素亦占位) | 每个写过的坐标落一个独立子 key `base·[i,j]`;**未 set 的坐标子 key 根本不存在**,不占空间 | | 缺失元素 | 无「缺失」——全部就位(默认 0) | 读未写坐标返回 None(`kvspace·get` 缺失语义) | | 随机访问 | `a[i,j]` 按 row-major O(1) 偏移 | `base·[i,j]` / `kvspace·get` 按 key 查找 | | 适用 | 稠密张量、绝大多数元素有值、需连续内存与 O(1) 下标 | 稀疏矩阵、坐标空间极大而实际有值项稀少 | ```kv -dense:[]int32 = xv·reshape(array(…), 1024, 1024) // 4 MiB 稠密块,storetype=ARRAYND -sparse:[int32,int32]·int32 = {} // 稀疏,storetype=index +dense:[]int32 = xv·reshape(array(…), 1024, 1024) // 4 MiB 稠密块,class 2 +sparse:[int32,int32]·int32 = {} // 稀疏,class 0 容器头与物理成员 key 99 -> sparse·[3,7] // 仅此一项落子 key sparse·[3,7] sparse·[0,0] -> x // 未写坐标 → None(子 key 不存在) ``` diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-map\345\256\271\345\231\250.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-map\345\256\271\345\231\250.kv" index 8fe81f0d..15335da4 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-map\345\256\271\345\231\250.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-map\345\256\271\345\231\250.kv" @@ -72,19 +72,7 @@ memitemvalue 可递归嵌套(`[]char/utf8·[]char/utf8·int64` 即 `map[string ## 容器物理结构 -map 在 kvspace 中分两部分落库,`·` 是值容器唯一的 memindex 标记(注:`/lib/{pkg}·{func}` 中的 `·` 是 -包·函数命名分隔符,与 memindex 无关,详见 [[总体方案]]): - -- **容器值**:落在裸 base 键(memheadname),storetype=index,body 空;langtype 为 map langtype(如 - `[]char/utf8·int64`)。 -- **成员(item)**:`base·memitemkey` 是独立子 key,各自持该 item 的 body(memitemvalue)。 -- **memindex**:落在 `base·` 键(尾中点),storetype=index,其 body 是成员键索引矩阵,是成员枚举的权 - 威来源。 - -memindex 的线格式为 dims = `[len, cap, M]` 的矩阵:`len` = 有效成员数、`cap` = 预留行数 -(`cap ≥ len`,满则翻倍扩容)、`M` = 成员键 UTF-8 字节最大长向上 8 对齐后的**行宽**;body 为 -`cap × M` 字节,前 `len` 行是有序成员键(NUL 补齐),其余行全 NUL。容量内增删成员 body 长恒定、就地覆 -写。`M` 是行宽(行 stride),`cap` 是可翻倍的容量。(此物理模型 struct 同样适用。) +map 的 base key 存 class 0、body 为空,langtype 为完整 map 类型(例如 `[]char/utf8·int64`)。每个成员落在 `base·memitemkey` 的独立 KV key,保存该成员自己的 XValue。成员通过物理 key 前缀枚举;不存放 `base·` 索引矩阵。struct 成员遵循同一物理规则。`/lib/{pkg}·{func}` 中的 `·` 是函数命名字符,函数成员则位于 `/` 目录下。 ## 容器字面量须带类型标注 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" index d47fa499..37cc2b48 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" @@ -2,11 +2,10 @@ lib kvlang/spec/类型系统/ptr { r####"# ptr(指针) -**ptr** 不是种类,而是 head 的 `ref=1`(存储位置维):ptr 值的 head storetype/langtype 描述**目标的 -完整形态**,body 存**目标 key 路径**。源码类型标注最前可写 `*` 前缀(如 `*int64`)表 ref,`*` 后可 -跟任意 langtype 基(kind 或 structref,如 `*Node`、`[int64]·*Node`);layout 剥离后落成 head `ref` -独立字节;wire 层 langtype 串不含前缀(见 [[总体方案]]);赋值(Set)时据 head 的 storetype/langtype -做单跳类型检查。 +**ptr** 不是独立种类。18 字节 head 的 storetype 字节用 bit2 表示指针,低两位用长度类;两个 +u64 分别存目标 key 的字节长度与容量,body 存目标 key 路径。langtype 是去掉这一层 `*` 后的目标 +类型。源码可写 `*T`,例如 `*int64`、`*Node`;另一层指针由目标 key 的 head 再置指针位表达, +而不是在这一格的 langtype 中重复 `*`(见 [[XValueHead线格式]])。 指针是**值**:读一个 `*T` 变量得到**地址值本身**(ref=1 Ptr,不解引用),解引用是**显式**的——用一元 前缀 `*`。四项与 C 对齐: @@ -18,10 +17,8 @@ lib kvlang/spec/类型系统/ptr { | `*p` | 解引用 → 读 body 目标地址的值 | | `None` | **空指针**(唯一的空指针字面量;即键不存在=无值) | -`*` 是**唯一**的解引用路径,对任何 ref=1 的 Ptr 槽成立:既含函数体内的形参槽 `*[0,±k]`(见 -[[函数]]、[[调用extindex]]),也含局部指针变量(`&x -> p` 后的 `*p`)——二者是同一读参命名约定, -layout 一律把 `*` 落成读参名/写槽名的前缀。读 `*p` 取目标整值;写 `42 -> *p` 写穿到目标(读写同规 -则:槽里存的是指针,只有 `*` 才落到目标)。 +`*` 是显式解引用路径。局部 `*p` 沿一条 Ptr 路径读写目标;函数体的 `*[0,±k]` 由 runtime 自动 +沿帧槽 `**T` 和实参键 `*T` 两层路径,读写调用方原 key(见 [[函数]]、[[调用extindex]])。 `[]` 与 `·[…]` 都**不**对指针隐式下标:`p[i]`(`p:*[]T`)报 TypeError——先 `*p` 再下标;`p·[0]` 先按 指针解引用到目标,目标若是 compact 数组则不是容器,同样报 TypeError(见 [[成员访问]])。 @@ -55,8 +52,7 @@ kvlang·abs(t, "2")`),均产 ref=1 Ptr、body 为目标 key 路径。 成员访问 `p·field` 经指针**自动解引用**(对齐 Go/Rust 的 `p.field`);需要「指针指向的整值」(如指针 指向标量)时用显式 `*p`。二者不冲突:`*p` 取整值、`p·field` 取成员。 -嵌套指针(`**T`)不在本模型内:wire 层 langtype 串不含 `*`、ref 仅单字节,无法表达层数。链表/树/绝对地址 -皆一层指针,已覆盖;C 里 `**` 的「out 参数传地址」由 kvlang 写参取代——要直写调用方对象就写 -`-> (r:*T)`(地址写参),`-> (r:T)` 是值写参(copy-in / copy-out),无需 `**`。 +函数调用实际使用两层指针:帧参数槽是 `**T`,指向本帧的实参键;实参键是 `*T`,指向调用方 +值。只读参和写参都按地址传递,声明中的 `T` / `*T` 描述目标类型,不切换为传值。 "#### -> /lib/kvlang/spec/类型系统/ptr } diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/17-struct.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/17-struct.kv" index b45f7606..713618e2 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/17-struct.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/17-struct.kv" @@ -2,7 +2,7 @@ lib kvlang/spec/类型系统/struct { r####"# struct -`struct` 声明注册一个原型节点于 `/lib/`(storetype=index,langtype 为原型路径如 +`struct` 声明注册一个原型节点于 `/lib/`(class 0,实例 langtype 为原型路径如 `/lib/Point`),字段带类型标注与默认值: ```kv @@ -13,7 +13,7 @@ struct Point { ``` 实例化 `Name{f=v}` 克隆原型子树、覆盖给定字段(其余取默认),并对每个字段做类型校验;实例是 struct -类型命名成员容器(storetype=index,成员在 `{key}·` memindex),成员经 `·` 访问。字段类型不符或写不存 +类型命名成员容器(base key 为 class 0 空值,字段落独立 `{key}·field` key),成员经 `·` 访问。字段类型不符或写不存 在的字段在 runtime 报 TypeError。 ```kv @@ -26,7 +26,7 @@ q = Point{} // 全取默认:0.0, 0.0 型表达式]])。结构值可存进 map(int 键、struct 值)、以绝对路径指针连成链表/树/图(见 `tutorial/12-struct/`)。 -struct 原型**不是**平行于 kvspace 的类型注册表——它就是 KV 树里的一个索引节点(`/lib/`,字段类型 +struct 原型**不是**平行于 kvspace 的类型注册表——它就是 KV 树里的一个 KV 目录节点(`/lib/`,字段类型 与默认值都是 KV 数据),故不与「数据结构全在 KV 树」的原则冲突(见 [[程序即数据结构]])。 "#### -> /lib/kvlang/spec/类型系统/struct } diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/19-head\345\255\227\346\256\265.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/19-head\345\255\227\346\256\265.kv" index f3ad28b9..d5651380 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/19-head\345\255\227\346\256\265.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/19-head\345\255\227\346\256\265.kv" @@ -1,19 +1,17 @@ // 欢迎加入kvspace世界 lib kvlang/spec/类型系统/head字段 { - r####"# 通用 head 字段(一切 xvalue 皆有,按序) + r####"# 通用 XValue head -| 字段 | 宽 | 义 | -|------|----|----| -| `headlen` | u16 LE | head 总字节数;body 起于偏移 `headlen` | -| `ref` | u8 | 存储位置(见下) | -| `storetype` | u8 | 物理布局(见下) | -| `ro` | u8 | `1`=只读、`0`=可写 | -| `vid` | u32 LE | vthread id(默认 0) | -| `body_len` | u32 LE | body 字节数 | -| (storetype 物理字段) | 变 | 按 storetype 追加,见下表 | -| `langtype` | 变长 | 类型表达式串,占至 `headlen`(长度 = `headlen` − 当前偏移,无需独立长度字段) | +固定前缀为 18 字节: -`langtype` 恒为 head 最后一段(body 之前)。旧 1 字节 kindcode 方案作废——语义类型回归完整类型表达式 -串,方能承载 `[dims]`、map 的 `key·value`、struct 原型路径、def 族定义节点等高级定义。 +| 字段 | 宽 | 含义 | +|------|----|------| +| `pow` | u8 | head 总长度为 `1 << pow` | +| `flags` | u8 | 低两位为 class,bit 2 为指针 | +| `a` | u64 LE | 按 class 解释 | +| `b` | u64 LE | 按 class 解释 | +| `langtype` | UTF-8 | 真实语义类型;之后以零填充至 head 末尾 | + +body 从 `1 << pow` 开始,长度由 class、a/b 和 langtype 校验得出。ro/vid 是独立 KV 元数据,不在 head 中。各 class 的规则见 [[XValueHead线格式]]。 "#### -> /lib/kvlang/spec/类型系统/head字段 } diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/20-ref\345\255\230\345\202\250\344\275\215\347\275\256.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/20-ref\345\255\230\345\202\250\344\275\215\347\275\256.kv" index d5cb3cee..3ae02aa4 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/20-ref\345\255\230\345\202\250\344\275\215\347\275\256.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/20-ref\345\255\230\345\202\250\344\275\215\347\275\256.kv" @@ -1,28 +1,11 @@ // 欢迎加入kvspace世界 lib kvlang/spec/类型系统/ref存储位置 { - r####"# ref(存储位置维) + r####"# 值、指针与扩展定位 -只决定 body 语义与是否间接寻址;**不改变** storetype/langtype 描述——head 恒携带完整形态。 +wire 的 flags 低两位为 storage class,bit 2 表示指针。普通值按 class 0/1/2 存 body;指针使用 class 1 加指针位,body 是目标 KV key 的 UTF-8 路径,a 为路径字节数、b 为容量。目标 key 若也是指针,沿路径再次读取即可表示多层间接;每格只保存一层。空指针用 None。 -| ref | body | 义 | -|-----|------|----| -| 0 inline | 值 raw 本体 | 就地落入 kvspace | -| 1 ptr | 目标 key 路径 | 指针,单跳指向同型目标(head 的 storetype/langtype 即目标形态);`ptr → rwfunc` 即函数调用链。**空指针即 None**(无值、不落键),非"空 Ptr"(见 [[ptr]]) | -| 2 @ext | 扩展句柄/定位符 | 单个值本体在扩展世界(fs 文件 / gpu tensordata),head 存元描述 | +`@T` 使用 class 3:langtype 是真实目标类型,body 是扩展定位串,a 为定位串 UTF-8 字节数、b 为容量。核心 runtime-c 不兑现扩展存储;声明支持它的 rwir 按定位串读写实际内容。源码中的 `*`/`@` 类型前缀不进入 wire langtype。 -**源码书写与 wire 的分工**:kvlang **源码**里,指针/扩展引用仍以 `*`/`@` 前缀写在类型**最前** -(`*int64`、`@[256,256]uint8`,见 tutorial 例),这是源码类型标注文法的一部分。layout 解析源码时把该 -前缀**剥离**并落成 head 的 `ref` 独立字节;故 **wire 层 langtype 串不含 `*`/`@` 前缀**,存储位置一律 -经 `ref` 字节表达。`ref=2` 只表**单值**存于扩展世界,与目录类型 `index`/`extindex` 无关。 - -## 扩展世界句柄(ref=2 的 body 语义) - -ref=2 时 body **不是**真实值字节,而是**逻辑定位串**(如 `/networld//proc//stdout`); -langtype 仍为目标 langtype 串(不带 `@` 前缀),head 只存元描述。**`@` 的兑现交给支持它的 rwir**:如 -runtime-rs 的 `networld/proc` rwir 把逻辑路径 `/networld//proc//` 映射到 -`/tmp/kvlangruntime-rs//` 物理文件读回,`networld/fs` 一族 rwir 也直接以 `@` 开头的扩展 key -为输入;核心 runtime-c 不解引用 `@`。真实 xvalue 落在扩展世界(fs 文件、gpu -tensordata),非 kvspace 元存;扩展算子的输出即以此形式写回其写槽。锚例: -`tutorial/14-networld/05-capture.kv`。 +C ABI 的 `kvspaceHead_t.ref` 是从 flags 解码出的兼容视图,不是独立的 wire 字节。`ro`/`vid` 存于独立元数据 key。 "#### -> /lib/kvlang/spec/类型系统/ref存储位置 } diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/21-storetype\347\211\251\347\220\206\345\270\203\345\261\200.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/21-storetype\347\211\251\347\220\206\345\270\203\345\261\200.kv" index 0ac0e04a..c2df9e3f 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/21-storetype\347\211\251\347\220\206\345\270\203\345\261\200.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/21-storetype\347\211\251\347\220\206\345\270\203\345\261\200.kv" @@ -1,72 +1,18 @@ // 欢迎加入kvspace世界 lib kvlang/spec/类型系统/storetype物理布局 { - r####"# storetype(物理布局维,codec 视角) + r####"# storage class 与物理布局 -决定 head 追加的物理字段与 body 切法。**codec 仅凭此 1 字节分派**。 +wire 的 flags 低两位表示 class,具体 head 见 [[XValueHead线格式]]。语言语义由 langtype 决定,class 只决定 body 的长度规则。 -| storetype | 追加物理字段 | body(ref=0) | 承载的 langtype | -|-----------|-------------|--------------|----------------| -| `NONE` | 无 | 空 | None | -| `ATOM` | 无 | 单段不可切分 raw:定宽标量,或变长文本(`rwir` 槽 = 计数头 + 文本;`def langtype` 类型串) | 标量:bool、int/uint/float、time、duration;代码叶子 `rwir`、签名定义槽 `def langtype` | -| `ARRAYND` | `ndim` u8 + `dims[ndim]` u32 LE | 稠密等宽元素数组 | 张量、`[]char/*` 字符串(`.src` 源码副本亦此形) | -| `index` | 成员名 **m×n 对齐字符串数组**,`dims=[len,cap,M]`(len=成员数、M=对齐宽度 align8、cap 保留容量) | 空(rwfunc 的计数头在 `[0,0]`、值容器在 `{key}·`) | stringkeymap、struct(值容器);lib、rwfunc、def 族(目录/定义节点) | -| `extindex` | 同 index,`cap` 可增长(栈帧/运行栈扩容) | 同上 | rwfunc(vthread 运行栈等可扩展目录) | +| class | 用途 | 长度规则 | +|-------|------|----------| +| 0 短定长 | None、标量、map/struct 头、lib/rwfunc 目录、def rwir | 类型决定 body 长度,目录及容器头为空 | +| 1 有冗余的长度 | 字符/字节数组、rwir/rwfunc 调用槽、def langtype、ptr | a 是有效字节数,b 是容量 | +| 2 大定长 | 定宽元素张量 | a 是元素数,b 是元素宽度 | +| 3 扩展定位 | @ext | a 是定位串字节数,b 是容量 | -数值 `dims` 供 codec O(1) 切字节,与 langtype 串内的 `[dims]` 语法一致(由 codec 保证同步)。 -index/extindex 的物理载体沿用今日敲定的 m×n 对齐字符串数组(每成员名 padding 至 M,`cap` 预留扩 -容),存成员/子项名。 +rwir/rwfunc 调用槽的 body 为 5 字节计数头加目标名;layout 按目标在生成时选类型。rwfunc 目录 key 本身是 class 0 空值,其 `[0,0]` 锚点是 class 1、5 字节计数头。def rwir 是 class 0 的 5 字节路由头。参数签名为独立的 def langtype key。 -### 成员如何枚举:`/` 目录 vs `·` memindex - -storetype=index/extindex 只表「这是有成员的索引节点」,成员的**组织方式按 langtype 分两类**: - -| 类别 | langtype | 成员载体 | 例 | -|------|----------|---------|----| -| 目录节点 | lib、rwfunc、def rwir | **`/` 子路径**(kvspace 目录枚举) | `/lib/funca/[1,0]`、`/lib/funca/[1,1]` 为 `/lib/funca/` 的指令槽成员 | -| 值容器 | stringkeymap、struct | **`{key}·` memindex 兄弟槽** | `p·` 存 `p` 的成员名索引 | - -`{key}·` 中的 **memindex `·`** 是值容器(stringkeymap/struct)唯一的成员索引标记;rwfunc/lib/def 族 -是真目录,成员即 `/` 子路径,绝不用 memindex `·`。 - -**注意 `·` 的两种独立含义**——两者正交、勿混: - -1. **memindex 标记**:值容器主槽 `{key}` 的兄弟槽 `{key}·`,存该容器的成员名索引。**仅** - stringkeymap/struct 用。 -2. **包·函数命名分隔符**:`/lib/{pkg}·{func}`(如 `/lib/time·now`)中的 `·` 只是路径名里分隔包名与函 - 数名的字符,是 rwfunc 所在**目录的名字的一部分**,与 memindex 无关。故 `/lib/time·now/` 仍是纯 - `/` 目录,其成员是 `/` 子路径而非兄弟 `·` 槽。 - -两类值容器底层皆为同一 m×n 对齐字符串数组:目录节点由 kvspace 目录直接持有并按 `/` 枚举,值容器把它 -放在 `{key}·` 兄弟槽。 - -### rwir 无实现体(叶子)vs rwfunc 有实现体(目录);def 族定义节点 - -本质区别在「有无 kvlang 实现体」: - -| langtype | 有 kvlang 实现体? | 位置 | 谁实现 | 形态 | -|----------|------------------|------|--------|------| -| `rwir` | 无 | `/vthread//` 指令行 | runtime 直接兑现(经 def rwir 路由,myrwircaps) | ATOM 指令叶子(body = 计数头 + 指令名/引用文本) | -| `def rwir` | 无(仅签名,无指令行) | **仅 `/lib/`** | runtime 直接兑现 | index **定义节点**(有签名行 `[0,x]`,无实现指令行) | -| `rwfunc` | **有**(一串 rwir 指令行) | `/lib/·/` | kvlang 指令序列自身 | index/extindex **目录**(参数定义键 `.[0,±j]` + 实现指令行 `[s0>0,·]`) | - -**def rwir 是路由头**(原 `defrwir`,现规范书写为带空格的 **`def rwir`**,见下「def 族」):它只存在 -于 `/lib/`,是每个 rwir 指令的统一声明入口。`/vthread/` 下执行的每条 rwir 都**引用** `/lib/` -的 def rwir——为「rwir 统一调度」而设。def rwir 没有 kvlang 实现体(无 `s0>0` 的实现指令行),由 -runtime 直接兑现。 - -**没有「扩展 rwir」这个说法**:def rwir 不专指扩展算子。每个 runtime 只声明自己的 **myrwircaps**(该 -runtime 能兑现的 rwir 能力集),`/lib/` 的 def rwir 即这些能力对外的路由头。 - -rwfunc 与之相反:它**有** kvlang 实现体——就是它 `/` 目录下的实现指令行子成员 -(`/lib/pkg·funca/[1,0]`…)。这正是 rwir/def rwir(无实现指令行、runtime 兑现)与 rwfunc(有实现指令 -行、`/` 目录)的本质区别。 - -**为何有 def rwir 却无 def rwfunc**——根源在能力调度的不对称: - -- **rwfunc 可被任意 main-runtime 直接解释执行**:其实现体就是一串 rwir,main-runtime 逐条解释即可, - 不需路由头。故 rwfunc 无需、也没有 `def rwfunc`。 -- **rwir 若不在本 runtime 的 myrwircaps 内就执行不了**,必须依赖调度转发给**能兑现它的其它 - runtime**。这份「按能力路由到其它 runtime」的需求,正由 `/lib/` 的 **def rwir 路由头**承载——这 - 就是 def rwir 存在的根本原因。 +map、struct 成员在 `base·member`;函数和 lib 成员在目录下的 `/` 子路径。目录和 map 成员都由物理 key 前缀枚举,无额外索引矩阵。外部叠加目录由 class 3 定位串描述,和成员枚举分开处理。 "#### -> /lib/kvlang/spec/类型系统/storetype物理布局 } diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/22-storetype-def\346\227\217\344\270\216\347\255\276\345\220\215\350\241\214.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/22-storetype-def\346\227\217\344\270\216\347\255\276\345\220\215\350\241\214.kv" index b7195e88..33c063c7 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/22-storetype-def\346\227\217\344\270\216\347\255\276\345\220\215\350\241\214.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/22-storetype-def\346\227\217\344\270\216\347\255\276\345\220\215\350\241\214.kv" @@ -4,7 +4,7 @@ lib kvlang/spec/类型系统/storetype_def族与签名行 { ### 签名不入 langtype 串,而入签名行 `[0,x]` 槽 -> **铁律**:任何 rwir/rwfunc 值的 body = `[nr][nw][dynamic]` 计数,仅此而已。参数类型的承载则按种类 +> **铁律**:rwir/rwfunc 调用槽的 body 以 `[nr][nw][dynamic]` 计数头开头,后接目标名。def rwir 路由头与 rwfunc 的 `[0,0]` 锚点仅有 5 字节计数。参数类型按种类 分 两路:**def rwir** 每参独占一个 `[0,x]` 参数轴坐标 key(位置化);**rwfunc** 不落 `[0,x]`,改由命 名 @@ -15,8 +15,7 @@ rwfunc / def rwir 的**签名(各参数类型)不写进 langtype 串**,也 1. **计数头**(两者共有):`rwfunc` 落签名行锚点 `/lib/pkg·funca/[0,0]`、`def rwir` 落 `/lib/` 节点 自身,其 body 只记 `[nr:u16 LE][nw:u16 LE][dynamic:u8]`——读参个数 `nr`、写参个数 `nw`、是否变参 - `dynamic`(末位读参 `...`)。**不含任何参数 langtype 串**。rwfunc 的函数目录节点自身是普通 index、 - 无值。 + `dynamic`(末位读参 `...`)。**不含任何参数 langtype 串**。rwfunc 的函数目录节点自身是 class 0 空值。 2. **def rwir——位置化 `[0,x]` 签名槽**:每个参数类型落该目录签名行坐标 `[0,x]`——`x<0` 读参、`x>0` 写参、`[0,0]` 为锚点(见 [[指令布局格式]] 二元坐标 `[s0,s1]`,`s0=0` 即签名行)。每个 `[0,x]` 是 **`def langtype`** xvalue,body 存该参数的 **def langtype**(匹配类型)串(一参一槽),**纯净、不含变参 @@ -49,17 +48,17 @@ rwfunc 的散在 `.[0,±j]` 键;实现指令行(若有)在 `[s0>0,·]`。 `def` 与 base 间的空格是**规范强制**:连写形式 `defrwir`/`defstruct`/`deflangtype` 一律废除。 -### 目录节点即索引型容器 +### 目录与签名键 -KV 树中的目录路径(尾 `/`)本身是 xvalue,storetype ∈ {index, extindex},langtype 表达目录语义: +| 路径 | 类型及 class | 成员 | +|------|--------------|------| +| `/lib/` | `lib`,class 0、空 body | 物理 `/` 子 key | +| `/lib/pkg·funca/` | `rwfunc`,class 0、空 body | `[0,0]` 锚点、参数定义键和指令行 | +| `/lib/pkg·funca/[0,0]` | `rwfunc`,class 1、a=5、b≥5 | body 为 5 字节计数头 | +| `/lib/` | `def rwir`,class 0 | body 为 5 字节计数头,签名在独立 key | +| `/vthread/1/` | `rwfunc`,class 3 扩展定位 | 通过 locator 叠加到静态函数目录 | -| 路径 | langtype | storetype | 成员 | -|------|----------|-----------|------| -| `/lib/` | `lib` | index | `/lib/` 各包目录(`/` 子路径) | -| `/lib/pkg·funca/` | `rwfunc` | index(静态函数定义目录) | 参数定义键 `.[0,±j]`(def langtype,body=名字+NUL+类型)+ 实现指令行 `[s0>0,·]` | -| `/lib/` | `def rwir` | index(路由头,只有签名行) | 签名行 `[0,x]`(def langtype),无实现指令行 | -| `/vthread/1/` | `rwfunc` | extindex(运行栈,可扩展) | `/vthread/1/<坐标>` 栈帧/指令 | +目录成员由实际 `/` 前缀枚举;map/struct 成员由实际 `·` 前缀枚举。扩展叠加不需要成员矩阵。 -同为 rwfunc,静态定义落 `index`、运行栈落 `extindex`——langtype 一致、物理载体按场景选。 "#### -> /lib/kvlang/spec/类型系统/storetype_def族与签名行 } diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/23-langtype\350\257\255\344\271\211\347\261\273\345\236\213.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/23-langtype\350\257\255\344\271\211\347\261\273\345\236\213.kv" index a6fd162d..7aa9c458 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/23-langtype\350\257\255\344\271\211\347\261\273\345\236\213.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/23-langtype\350\257\255\344\271\211\347\261\273\345\236\213.kv" @@ -4,22 +4,20 @@ lib kvlang/spec/类型系统/langtype语义类型 { 本章界定 **langtype**——一个具体值 head 的**真实类型**(它与定义处匹配类型 `def langtype` 的区别见 [[langtype签名类型表达式]])。langtype 是完整的类型表达式串,逐字节即类型标注文法,天然含 base 种 -类、`[dims]`、map 键值、struct 原型路径。各 langtype 的串形态与其落定的 storetype: +类、`[dims]`、map 键值、struct 原型路径。各 langtype 与 storage class 的典型关系: -| langtype(示例串) | storetype | 说明 | -|--------------------------|-----------|------| -| `int64` `bool` `time` `duration` | ATOM | 定宽标量 | -| `[]char/utf8` | ARRAYND(elem 1B) | **utf8 变宽 → 物理即 [n]uint8**;禁码点索引是 langtype 层规则,物理按字节切 | -| `[]char/utf32` `[]char/ascii` | ARRAYND | 定宽,物理可 O(1) 码点索引 | -| `[3,4]float32` | ARRAYND | 张量 | -| `rwir`(指令行,`/vthread/`) | ATOM | 指令叶子(body = 计数头 + 指令名/引用文本),**无实现体,由 runtime 经 def rwir 路由直接兑现(myrwircaps)** | -| `def rwir`(路由头,仅 `/lib/`) | index | 定义节点:签名行 `[0,x]`,无实现指令行 | -| `[]char/utf8·int64`(map) | index | **stringkeymap**,键值经 `·` mapexpr;成员在 `{key}·` | -| `/lib/Point`(struct 原型路径) | index | 结构实例,字段固定;成员在 `{key}·` | -| `def struct`(原型定义节点) | index | struct 原型自身;字段类型定义在其子槽 | -| `rwfunc` | index / extindex | 函数即 `/` 目录:签名行锚点 `[0,0]`(langtype `rwfunc`,body 只记 nr/nw/dynamic)+ 参数定义键 `.[0,±j]` + 实现指令行;目录节点自身为 `index`、无值 | -| `def langtype`(签名行 `[0,x]` 槽) | ATOM | 单个参数的**匹配类型**定义,body=该参数的 def langtype 串 | -| `lib`(包目录) | index | `/lib/` 及各 pkg 目录;成员为 `/` 子路径 | +| langtype(示例) | class | 说明 | +|------------------|-------|------| +| `int64` `bool` `time` `duration` | 0 | 定宽标量 | +| `[N]char/utf8`、`[N]char/utf32`、`[N]char/ascii` | 1 | 字符序列;UTF-8 的 a 为字节数,N 为码点数 | +| `[3,4]float32` | 2 | 定宽张量 | +| `rwir` / `rwfunc` 调用槽 | 1 | 5 字节计数头加目标名 | +| `def rwir` | 0 | 5 字节路由头 | +| `[]char/utf8·int64`、`/lib/Point` | 0 | map/struct 空 body 头,成员为物理 `·` key | +| `def struct`、`lib`、`rwfunc` 目录 | 0 | 空 body 目录值,成员为物理 `/` key | +| `rwfunc` 的 `[0,0]` 锚点、`def langtype` | 1 | 5 字节计数头或类型定义文本 | + +指针为 class 1 加指针标志;@ext 使用 class 3,langtype 仍为目标真实类型。 **def 族的来源**(`def` 不是 kvlang 关键字、不在 kv 源码书写):`def struct`(源码 `struct Name{}`, layout 落原型定义节点)、`def langtype`(源码参数标注 `x:int64`,layout 铺签名行时逐参生成)由 @@ -30,7 +28,6 @@ layout 落原型定义节点)、`def langtype`(源码参数标注 `x:int64` [[struct]])。容器**语义类型**只剩 `stringkeymap`、`struct`(口语称「map」指的即 stringkeymap,规范一律书 写种类名 `stringkeymap`)。 -**一对多正是拆分的价值**:同为 rwfunc,静态函数定义落 `index`、vthread 运行栈落 `extindex`;语言层看 -到的是同一 langtype,物理层按场景选载体。 +同一 langtype 可出现在不同 class;例如 rwfunc 目录值为 class 0,而其签名锚点和调用槽为 class 1。 "#### -> /lib/kvlang/spec/类型系统/langtype语义类型 } diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/24-wire\345\270\203\345\261\200\347\244\272\344\276\213.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/24-wire\345\270\203\345\261\200\347\244\272\344\276\213.kv" index c75f24aa..d4a99ee5 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/24-wire\345\270\203\345\261\200\347\244\272\344\276\213.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/24-wire\345\270\203\345\261\200\347\244\272\344\276\213.kv" @@ -2,25 +2,24 @@ lib kvlang/spec/类型系统/wire布局示例 { r####"# wire 布局示例 -通用前缀 `U = [headlen:u16][ref][storetype][ro][vid:u32][body_len:u32]`,其后按 storetype 追加物理字 -段,再接 langtype 串(占至 headlen),末接 body: +通用 head 前缀 `U=[pow:u8][flags:u8][a:u64 LE][b:u64 LE]`,后面是 langtype、零填充和 body。head 长度为 `1 << pow`。以下省略填充字节: ``` -None : U(store=NONE)[lang=""] | 空 -atom int64 : U(store=ATOM,body_len=8)[lang="int64"] | 8B raw -[]char/utf8 : U(store=ARRAYND)[ndim=1][dims=n][lang="[]char/utf8"] | n 字节 -[3,4]f32 : U(store=ARRAYND)[ndim=2][dims=3,4][lang="[3,4]float32"] | 48B raw -map : U(store=index)[count/cap…][lang="[]char/utf8·int64"] | 主槽空;成员在 {key}· -struct 实例 : U(store=index)[count/cap…][lang="/lib/Point"] | 主槽空;成员在 {key}· -def struct : U(store=index)[count/cap…][lang="def struct"] | Point 原型;字段定义在子槽 -/lib/ : U(store=index)[count/cap…][lang="lib"] | 成员为 /lib/ 子路径 -/lib/pkg·funca : 目录节点 U(store=index)[lang="index"](无值);其 [0,0] : U(store=index)[lang="rwfunc"] | body=[nr][nw][dyn];签名在 .[0,±j],实现在 [s0>0,·] -/lib/ : U(store=index)[count/cap…][lang="def rwir"] | 路由头;body=[nr][nw][dyn];签名在 [0,x],无实现行 -签名行 [0,x]: U(store=ATOM)[lang="def langtype"] | body=该参数 langtype 串 -rwir 指令行 : U(store=ATOM)[lang="rwir"] | body=[nr][nw][dyn](指令槽恒 0)+ 指令名/引用文本 -/vthread/1/ : U(store=extindex)[ext 元数据][lang="rwfunc"] | 运行栈,/vthread/1/<坐标> 子成员 -ptr→int64 : U(ref=1,store=ATOM)[lang="int64"] | body="/x/y" -ptr→rwfunc : U(ref=1,store=index)[lang="rwfunc"] | body="/lib/pkg·funca"(调用链) +None : U(class=0,a=0,b=0) lang="" body=空 +int64 : U(class=0,a=0,b=0) lang="int64" body=8 字节 +[]char/utf8 : U(class=1,a=字节数,b≥a) lang="[码点数]char/utf8" body=UTF-8 +[3,4]float32 : U(class=2,a=12,b=4) lang="[3,4]float32" body=48 字节 +map : U(class=0,a=0,b=0) lang="[]char/utf8·int64" body=空;成员在 base·key +struct 实例 : U(class=0,a=0,b=0) lang="/lib/Point" body=空;成员在 base·field +/lib/pkg·f/ : U(class=0,a=0,b=0) lang="rwfunc" body=空;成员在目录下 +/lib/pkg·f/[0,0] : U(class=1,a=5,b≥5) lang="rwfunc" body=[nr][nw][dynamic] +/lib/op : U(class=0,a=0,b=0) lang="def rwir" body=[nr][nw][dynamic] +rwir 调用槽 : U(class=1,a=len,b≥a) lang="rwir" body=计数头+目标名 +rwfunc 调用槽 : U(class=1,a=len,b≥a) lang="rwfunc" body=计数头+目标名 +ptr→int64 : U(class=1|ptr,a=len,b≥a) lang="int64" body=目标 KV 路径 +@ext→T : U(class=3,a=len,b≥a) lang="T" body=UTF-8 定位串 ``` + +`ro` 与 `vid` 在 `/.kvspace-meta/`;目录与 map 成员通过实际 key 前缀枚举。 "#### -> /lib/kvlang/spec/类型系统/wire布局示例 } diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/25-\345\267\262\346\225\262\345\256\232.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/25-\345\267\262\346\225\262\345\256\232.kv" index 54744f84..bcc00fe9 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/25-\345\267\262\346\225\262\345\256\232.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/25-\345\267\262\346\225\262\345\256\232.kv" @@ -2,58 +2,14 @@ lib kvlang/spec/类型系统/已敲定 { r####"# 已敲定 -- 三维正交:`ref` × `storetype` × `langtype`,codec 只认 storetype(1 字节)、语言层只认 langtype - (即类型表达式串)。 -- **langtype = 完整类型表达式串**(变长),保留高级语言全部类型定义;1 字节 kindcode 方案作废。 -- storetype 集:`NONE`/`ATOM`/`ARRAYND`/`index`/`extindex`(index/extindex 是 storetype,非 - langtype)。 -- 容器语义类型只剩 `stringkeymap`/`struct`(口语「map」= stringkeymap;`object`/`scope` **彻底删除 - **,无此种类/关键字);物理落 index。 -- 字符串:ascii/utf32 → ARRAYND;utf8 → ARRAYND(elem 1B)。 -- time/duration → ATOM;rwir 指令行 → ATOM(body = 计数头 + 指令名文本,无实现体)。 -- **rwir vs rwfunc 的本质区别**:rwir 无实现指令行、由 runtime 直接兑现(myrwircaps);rwfunc 有 - kvlang 实现指令行(`/` 目录下的 `[s0>0,·]`)→ 目录。 -- **def rwir 是路由头**(原 defrwir):仅存于 `/lib/`,是 rwir 统一调度入口;`/vthread/` 的每条 - rwir 引用 `/lib/` 的 def rwir。**无「扩展 rwir」之说**,只有各 runtime 的 myrwircaps。 -- **签名不入 langtype 串**:计数头节点的 langtype 为裸名 `rwfunc`(`/lib/·/[0,0]`)/ - `def rwir`(`/lib/` 节点自身);其 body 只记 `[nr][nw][dynamic]`;参数真实类型按符号分两路承 - 载——`def rwir` 落位置化签名行 `[0,x]` 槽(`x<0` 读参、`x>0` 写参、`[0,0]` 锚点),`rwfunc` 落参数定 - 义键 `.[0,±j]`;两者都是 `def langtype` xvalue。 -- **ATOM = 单段不可切分 raw**:定宽标量(bool/int/uint/float/time/duration)与**变长文本**(`rwir` 槽 = - 计数头 + 文本、`def langtype` 类型串)同落 ATOM;ARRAYND 只承载带 `ndim`/`dims` 的稠密数组(张量、 - `[]char/*`)。 -- **`rwir` 槽 body 的唯一格式**:`[nr:u16 LE][nw:u16 LE][dynamic:u8]`(指令槽恒 `0,0,0`,5 字节)+ - 槽文本(opcode 名或相对指针)。构造侧 `kvkind::rwir_slot_body`、读取侧 `kvlangXvalueSlotName` 均按此 - 约定(`body + 5`);内联字面量不是 `rwir`,按自身 kind 编码、无计数头。 -- **参数传递由签名 ref 前缀定**(见 [[函数]]):`T` 值传(帧槽存副本)、`*T` 址传(值在 kvspace,体内解 - 引用一层)、`@T` 扩展存储(值在 kvspace 之外,runtime-c 不可直达,由**支持 `@` 的 rwir** 按定位串兑现 - ——如 runtime-rs 的 fs/networld 一族);值容器(map langtype / struct 原型路径)无值本体可拷,**必 - 须**带 `*`/`@`。**读参一律只读**:参数定义键 `.[0,param]` 中 `param<0` 是只读参数、`param>0` 是要写 - 的参——与值传/址传无关(见 [[函数]])。 -- **opcode 槽的 `rwir|rwfunc` 是派发标记**,不是值类型(见 [[指令槽编码]])。 -- **`langtype` 与 `def langtype` 的区别**:`langtype` 是值的**真实类型**(具体、单一种类 + 确定 - `[dims]`);`def langtype` 是定义函数时参数/返回值的**匹配类型**(可含并集/轴量词/`any`),**一个 - def langtype 可匹配多种 langtype**——调用时按它逐值判定(见 [[langtype签名类型表达式]]、[[匹配语义与 - 变参]])。承载 def langtype 的节点 kind 就叫 `def langtype`。 -- **def 族带空格书写**:`def rwir`、`def struct`、`def langtype`——`def` 与 base 间强制空格,连写形式 - 废除。 -- **rwfunc/lib 是目录节点**:成员即 `/` 子路径;rwfunc 静态定义 → index、vthread 运行栈 → extindex。 -- **index/extindex 物理载体 = m×n 对齐字符串数组**(`dims=[len,cap,M]`,M align8、cap 扩容),沿用今 - 日敲定方案,存成员/子项名;载体**语义是可枚举成员**(`kvspaceListLen`/`ListAt`),shm 用矩阵、fs 用 - `{key}·/` 子目录,载体差异不影响 ABI 语义。 -- **`·` 双义正交**:① memindex 标记 `{key}·` 仅 stringkeymap/struct 用;② `/lib/{pkg}·{func}` 里的 - `·` 是包·函数命名分隔符(路径名一部分),与 memindex 无关,rwfunc 目录成员仍走 `/`。 -- 索引型容器(stringkeymap/struct)主槽 `{key}` body 恒空。 -- **源码 `*`/`@` 前缀保留**:kvlang 源码类型标注最前可写 `*`/`@`(表 ref),layout 剥离前缀落成 head - `ref` 字节;wire 层 langtype 串不含前缀。 -- `headlen` u16 LE;`body_len` 保留为显式 u32 LE 字段;langtype 串占至 headlen,无独立长度字段。 -- `ref=2`(@ext)与 index/extindex 无关。 -- in-memory head struct 按 storetype 拆分——**属实现层,本规范只定 wire 与字段语义**。 - -## 仍待敲定 - -(本卷所有类型分歧已敲定。剩余仅实现层细节——如 index/extindex m×n 数组各字段精确偏移、签名行 `[0,x]` -坐标与 [[指令布局格式]] 二元坐标的具体编码、extindex 扩容策略——随 01-04 重构与 [[map容器]] memindex 线 -格式对齐后落定,不属本卷规范分歧。) +- 线格式为 18 字节固定前缀 `[pow][flags][a:u64 LE][b:u64 LE]`,后接 langtype、零填充及 body;head 长度为 `1 << pow`。flags 低两位为 storage class,bit 2 为指针。 +- class 0 为短定长值;class 1 为有冗余的长度值(a=有效字节数,b=容量);class 2 为张量(a=元素数,b=元素宽度);class 3 为 @ext(a=定位串 UTF-8 字节数,b=容量)。 +- langtype 是完整语义类型。源码类型标注的 `*`/`@` 前缀在 wire 中分别表示为指针标志和 class 3,不进入 langtype 串。 +- `ro`/`vid` 存于独立 KV key `/.kvspace-meta/` 加原 key 字节的小写十六进制编码,不占 XValue head。 +- map/struct 的 base key 存 class 0 空 body,成员直接在 `base·member`;函数和 lib 使用 `/` 目录。成员由物理 key 前缀枚举,无 index/extindex 矩阵或 memindex body。 +- rwfunc 目录值为 class 0、body 空;`[0,0]` 锚点为 class 1、a=5、b≥5。def rwir 路由头为 class 0、5 字节。调用槽由 layout 判定为 rwir 或 rwfunc,均使用 class 1,body 为计数头加目标名。 +- 字符串与 byte 数组使用 class 1;UTF-8 的 a 是字节数,langtype 的形状记录码点数,解码同时校验。定宽张量使用 class 2。 +- 指针 class 1 的 body 是目标 KV 路径;@ext class 3 的 body 是扩展定位串,langtype 为实际目标类型。 +- runtime 每次从 kvspace 读取变量和 PC,完成指令后写回;进程内不保留其私有状态副本。 "#### -> /lib/kvlang/spec/类型系统/已敲定 } diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/01-\346\214\207\344\273\244\346\236\266\346\236\204.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/01-\346\214\207\344\273\244\346\236\266\346\236\204.kv" index 5abd4ad9..7ec10e14 100644 --- "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/01-\346\214\207\344\273\244\346\236\266\346\236\204.kv" +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/01-\346\214\207\344\273\244\346\236\266\346\236\204.kv" @@ -4,7 +4,7 @@ lib kvlang/spec/layout语义/指令架构 { layout 把每个函数体布进 kvspace 的一棵子树:**指令即路径**,每条指令占据一个二维坐标 `[s0, s1]`。本 章界定该坐标系、opcode 与读写槽的角色、两种赋值书写形态,以及槽值的 XValue 编码。二维坐标的线格式 -(TLV、head langtype)见 [[指令布局格式]](kvspace 模型卷);本章只界定 layout 产出的槽位语义。 +(headlenpow、head langtype)见 [[指令布局格式]](kvspace 模型卷);本章只界定 layout 产出的槽位语义。 ## 二维坐标系 `[s0, s1]` diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/02-\346\214\207\344\273\244\346\247\275\347\274\226\347\240\201.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/02-\346\214\207\344\273\244\346\247\275\347\274\226\347\240\201.kv" index 548d2d63..3d1a3861 100644 --- "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/02-\346\214\207\344\273\244\346\247\275\347\274\226\347\240\201.kv" +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/02-\346\214\207\344\273\244\346\247\275\347\274\226\347\240\201.kv" @@ -19,17 +19,20 @@ opcode 位恒为操作码,故拷贝(opcode=`=`)与零参函数调用(`gr opcode 槽 `[s0,0]` 的 langtype 由 layout 按 opcode 类别决定: - 控制流与拷贝原语(`return` / `goto` / `br` / `call` / `=`)——langtype `rwir`。 -- 其余(运算符、函数/rwir 调用目标)——langtype `rwir|rwfunc`。这是**派发标记**(非值类型):layout - **不**静态判定该目标是否在本 runtime 的 myrwircaps 内(rwir)或为 rwfunc;判定推迟到 runtime 查 - `/lib/` 分派(见 [[lib与rwir数据]])。 +- 运算符与 rwir 调用目标——langtype `rwir`;已知的用户函数调用目标——langtype `rwfunc`。 + layout 根据本次源码的函数声明及 KVSpace 中已有的定义确定类型;runtime 仍按目标定义分派。 ## 读写槽的槽值编码 读/写槽的槽值按内容编码为 XValue: -- 字面量(整数、浮点、`true`/`false`、字符串、绝对路径)——按其类型编码为对应 kind 的 XValue(`int64` +- 字面量(整数、浮点、`true`/`false`、字符串)——按其类型编码为对应 kind 的 XValue(`int64` / `float64` / `bool` / `char/*` …);字符串字面量在 KV 传输层以 `"` 前缀区分于变量名。 -- 变量名、标签等引用——langtype `rwir`,body 即该名字符串。 +- 变量名、标签、绝对路径等引用——langtype `rwir`,body 为 5 字节 counts 加目标名;已知目标类型 + 时再附 NUL 和类型串。 + +这些值留在共享 `/lib` 模板。执行帧在同坐标将读写槽实体化为指向目标 KV key 的 `*T`;字面量 +的目标值也保存在执行帧 KV 中。opcode 槽不实体化。 ## 增量覆盖 diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/03-\345\207\275\346\225\260.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/03-\345\207\275\346\225\260.kv" index c3670837..8bb23e78 100644 --- "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/03-\345\207\275\346\225\260.kv" +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/03-\345\207\275\346\225\260.kv" @@ -2,143 +2,34 @@ lib kvlang/spec/layout语义/函数 { r####"# 函数 -kvlang 源码只有一种命名单元:**rwfunc**(有指令体的用户函数),暴露 `(读参) -> (写参)` 箭头接口。函 -数**没有返回值**,只有读参与写参。 +rwfunc 使用 `(读参) -> (写参)` 接口,没有返回值。读参在负坐标轴 `[0,-k]`,写参在正坐标轴 `[0,+k]`。参数都须声明类型;读参只读,写参可读写。要修改调用方对象,必须把它列在写参侧。 -签名与体内 rwir 落入 kvspace 的布局**一律**按 `->` 的「左读 / 右写」轴实现:读参在负轴、写参在正轴 -(见 [[指令架构]])。`=` 只存在于 kvlang 上层 code 层,是单条指令「写左」的书写别名,不进入签名、也 -不改变槽位布局——所以没有 `=` 形式的 rwfunc 签名,接口只用 `(读参) -> (写参)` 表达。rwir 是 runtime -兑现的原子能力,源码**不声明**、只调用;其 `def rwir` 路由头由各 runtime 注册到 `/lib`(见 [[rwfunc -布局与def_rwir]])。 +## 传址模型 -## 读参与写参 +函数调用只传地址。调用点的参数指针 `*T` 指向实际 KV 值;被调帧的 `[0,±k]` 再存一个指向该参数指针的指针,因此帧槽为 `**T`。runtime 从帧槽自动解引用一层,交给普通 rwir 的仍是 `*T`;rwir 读取目标 KV 值后运算。函数体中的形参引用由 layout 编为 `*[0,±k]`,最终读写实际目标 key,不复制值本体。 -| 参数 | 布局槽位 | 方向 | -|------|---------|------| -| **读参** | `[0,-1], [0,-2], …` | 调用方 → 被调方 | -| **写参** | `[0,+1], [0,+2], …` | 被调方 → 调用方 | +签名中的 `T` 是目标值类型,`*T` 表示目标格自身是一个指针值,`@T` 表示目标格是扩展定位句柄;三者都通过地址绑定。指针深度由沿 KV 路径逐格读取的指针位决定,wire langtype 不含 `*`/`@` 前缀。字面量先物化为 KV 值,再按地址传给函数。普通 rwir 参数按 `*T` 处理;创建 XValue 的初始化算子可以直接接收字面量。 -`-> result` 是**写参的跨帧路径映射**,不是返回值:runtime 将子帧写参直写调用方目标位置(见 [[函数调 -用]])。 +写参直接指向调用方写目标,调用期间读取它能看到调用方当前值,写它立即更新调用方 key。未赋值的写目标读出 None;对 None 做算术是错误。调用期不创建值参副本,也没有返回时 copy-out 或 `‥wdst` 表。 -调用时**写参 arity 必须全部匹配**(对齐 Go/Rust):`f() -> s` 对多写参函数是错误;不需要的写参**必须 -**用 `_` 显式丢弃。 +## 只读与类型 -## 参数类型除了值类型,还可以标注 `*` 传址、`@` 扩展存储 +读参 `[0,-k]` 不能作为写槽,包括成员、下标和别名写;layout 拒绝这类代码。若要计算新值,写到新局部变量或写参。形参定义的 `def langtype` 可用并集、`any` 等匹配类型;每个参数的定义写在函数目录旁的 `.[0,±k]` key,body 为参数名、NUL、类型串。参数定义键与运行时帧槽是不同的 KV key。 -每个读参与写参**必须**声明类型(`name:type_expr`,type_expr 见 [[langtype签名类型表达式]])。 -缺类型标注是错误: +调用时写参个数须匹配;不需要的写参用 `_` 显式丢弃。读参的 `...` 仅表示最后一个参数的变参 arity,不进入 langtype 串。 -``` -error: func f: param "x" has no type annotation — every parameter must declare its type -error: func f: return value "r" has no type annotation -``` - -参数类型除了值类型(不写前缀,实参值拷进帧槽),还可以在类型最前标注 `*`/`@`——它们是 head 的 `ref` -前缀(见 [[ref存储位置]]),两个**正交维**:`*` 说「多一跳间接」,`@` 说「值不在 kvspace」: - -| 签名写法 | 值在哪 | 帧槽 `[0,±k]` 存什么 | 体内形参引用 | -|---------|--------|--------------------|-------------| -| `x:T` | 本帧 | 调用方该位置的值**复制**进槽 | `[0,±k]` **只读**(`param<0` 只读,见下) | -| `p:*T` | kvspace 内某个 key | 该 key 的路径(ref=1 Ptr) | `*[0,±k]` 解引用一次取实参值(**只读**) | -| `h:@T` | **kvspace 之外**(fs 文件、GPU 显存、外部进程输出…) | 扩展世界定位串(ref=2) | runtime-c **不可**直接读写;由**支持 `@` 的 rwir**(如 runtime-rs 的 fs/networld 一族)按定位串兑现 | - -`*T` 与 `@T` 都"不是值本体",但性质不同:`*` 是**间接**——指针的值(那段 key 路径)本身就存在 kvspace -的 body 里,故 layout 与 runtime-c 都能直接寻址、解引用;`@` 是**位置陈述**——值本体不在 kvspace, -runtime-c 寻不到,**由支持 `@` 的 rwir 自己兑现**——这些 rwir 在其实现里按 head 的 ref=2 定位串读写扩展 -世界的真实字节(如 runtime-rs 的 networld/fs 一族 rwir 直接以 `@` 开头的扩展 key 为输入)。核心 -runtime-c 不解引用 `@`,也不设通用 extresolver。 - -值容器(map langtype、struct 原型路径)**没有可复制的值本体**,**必须**带 `*`(值在 kvspace)或 `@` -(值在扩展世界)——`p:/lib/Point` 是错误,须写 `p:*/lib/Point`;标量、compact 数组、`[]char/*` 字符串 -写 `T` 即按值传递。 - -参数声明的类型串是 **def langtype**(定义处的匹配类型,见 [[langtype签名类型表达式]]):它可含并集与 -`any`,故**同一参数可接受多种 `langtype` 的实参**(如 `a:int8|int16` 同时接受 int8 与 int16 值)。 - -局部指针变量与形参槽走**同一约定**:`*p`(`p` 为局部 `*T`)也落成读参名/写槽名的 `*` 前缀。layout 只 -在名下加 `*`,解引用由 runtime 按该槽里的 Ptr 完成——与 `*[0,±k]` 是同一机制,故 `*` 是唯一的解引用路 -径(见 [[ptr]])。地址形参名在体内**已被解引用一次**(名字即实参值),故体内再写 `*形参` 是两层间接, -layout 报错(待 [[调用extindex]] 的名称绑定模型收拢,见 issue #286)。 - -由此:读参(`param<0`,不论值传/址传)**只读**——`p:*Point` 的 `p·field = v`、值参 `x:int64` 的 -`x = x + 1` 都是写槽命中读参,layout 报错。要改值就先拷到新局部变量(`x -> local`,此后写 `local`);要 -改调用方对象就把该参数放到写参侧(`-> (p:*/lib/Point)`)。写参(`param>0`)可读可写:`-> (p:*/lib/Point)` -直写调用方对象,`-> (acc:int64)` 值写参按 [[系统变量]] 的 `‥wdst` 表 copy-in / copy-out。 - -## 参数变参(arity,非类型) - -末读参**可**在参数上尾缀 `...` 声明变参(吸收 0..N 个同型实参,如 `A:int64|float64...`)。`...` 是** -签名层 arity 构造,不是 type_expr 的一部分**(轴量词见 [[langtype签名类型表达式]]):layout 解析时把 -它从参数上**剥离**,给该函数的签名行锚点 `[0,0]` 置 `dynamic=1`(见 [[指令布局格式]]),而参数的**纯净** langtype(不含 -`...`)由承载点存放——def rwir 落位置化签名槽 `[0,x]`、rwfunc 落参数定义键 `.[0,±j]`(两者都是 -`def langtype`,rwfunc 的 body = 参数名 + NUL + 类型串;此键为签名元数据,不参与运行时形参解析,见 -[[调用extindex]])。故 `LangtypeValid`/`LangtypeMatch` 永不见 `...`。 - -- 变参**必须**是最后一个读参;非末位读参带 `...`:装载期错误。 -- 写参(返回值)**不得**变参:装载期错误。 -- 派发时,签名行锚点 `dynamic=1` 则末读参对每个尾随实参按其纯净 langtype 逐个判定(见 [[匹配语义与变 - 参]])。 - -## 写参初值 - -值写参(`-> (acc:int64)`)的初值是**调用方该位置的当前值**——调用点把它 copy-in 进帧槽 `[0,+k]`,返回时 -按 `‥wdst` copy-out 回目标(累加器写法 `acc + x -> acc` 据此拿初值)。地址写参(`-> (p:*T)`)的帧槽存 -调用方目标地址,`*[0,+k]` 解引用即调用方当前值。调用方位置尚未赋值时初值即 None——对 None 做算术在 -runtime 被拒,故累加器写法前须保证目标已有值(如 `0 -> acc` 起步)。 - -## 读参只读(`param<0` 一律只读) - -**永久规定**:参数定义键 `.[op,param]` 中 `param<0` 是**只读参数**(读参)、`param>0` 是**要写的参** -(写参,见 [[storetype_def族与签名行]])。函数体内读参的裸名**一律**不得作写槽(含 `for … in` 的迭代 -变量、成员/下标写、`kvspace·set(base, …)` 的 base),layout 报错拒绝装载: +## 名称与读写轴 -``` -error: func f: read param "A" cannot be used as write slot (read params are read-only) -``` - -只读与**值传/址传无关**:值读参的槽是本帧副本、地址读参(声明带 `*`/`@`)的槽是调用方对象的地址,两者 -都只读——写值读参就是写一个只读的帧槽,写地址读参还会破坏数据流方向。要改值就先拷到新局部变量 -(`x -> local`,此后写 `local`);要就地改调用方对象就把该参数放到写参侧(`-> (p:*/lib/Point)`)。 - -**只读覆盖到子成员,且别名不洗白**:`p·field = v`、`p[k] = v`、`kvspace·set(p, k, v)` 同样命中该检查——经 -desugar 后 base 就是读参裸名;对地址读参,只读性还沿别名传播:`q = p` / `p -> q` 使 `q` 指到**同一个对 -象**,此后 `q·field = v` 写的仍是 `p` 的对象,layout 一并拒绝(否则一句话就能绕开本规则): - -``` -error: func f: read param "p" cannot be used as write slot (read params are read-only) -error: func f: "q" aliases read param "p" (`q = p`); writing its members writes that object - — put it on the write side: `-> (p:*/lib/Point)` -``` - -**签名诚实原则**:要就地改调用方的对象,就把该参数写到写参侧、并按地址声明: +同一签名中的参数名不得重复,读参和写参也不得同名。调用点可以将同一个变量同时用于读槽与写槽;两条地址各自解析。 ```kv -rwfunc shift(dx:int64) -> (p:*/lib/Point) { p·x + dx -> p·x } -rwfunc test() -> () { a:Point = {x=1 y=2} shift(10) -> a } -``` - -``` -// 非法:读参 p 是地址参(*),写它的成员就是写调用方对象 -rwfunc bump(p:*/lib/Point) -> () { p·x + 1 -> p·x } -// 非法:读参 x 按值传也只读(param<0)——要改先拷到新局部变量 -rwfunc inc(x:int64) -> (r:int64) { x = x + 1 x -> r } -// 合法:同一个对象放到写参侧(地址写参直写调用方) -rwfunc bump() -> (p:*/lib/Point) { p·x + 1 -> p·x } -// 合法:先拷到局部变量再改 -rwfunc inc(x:int64) -> (r:int64) { x -> local local = local + 1 local -> r } +rwfunc add_one(x:int64) -> (result:int64) { + x + 1 -> result +} +rwfunc test() -> () { + 3 -> n + add_one(n) -> n +} ``` - -(`q·next -> q` 这类**重绑别名本身**的写槽是裸名、不碰调用方对象,不受影响。)判定规则(实现):写槽 -含 `/`、`[`、`·` 者视为路径/下标/成员写,不计入本检查;但 `kvspace·set(base, …)` 的 base 命中读参裸名同样 -拒绝(成员目录被改写)。 - -## 参数同名规则 - -- **定义时**:读参列表内、写参列表内、以及读写列表之间均**不得**同名(变量名即指针,同名即同址)—— - `rwfunc f(A:int64) -> (A:int64)` 报 `param "A" appears in both read-params and write-params`; - `rwfunc f(a:int64, a:int64)` 报 `duplicate param "a" in read-params`(写参列表内同理报 - `in write-params`)。 -- **调用时**:同一变量**可**同时出现在读槽与写槽——`inc(x) -> x` 合法,读写独立解析、互不冲突。 - "#### -> /lib/kvlang/spec/layout语义/函数 +"#### -> /lib/kvlang/spec/layout语义/函数 } diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/04-\345\207\275\346\225\260\345\206\231\346\247\275\344\270\216\345\270\203\345\261\200.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/04-\345\207\275\346\225\260\345\206\231\346\247\275\344\270\216\345\270\203\345\261\200.kv" index 45347d30..9f2df586 100644 --- "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/04-\345\207\275\346\225\260\345\206\231\346\247\275\344\270\216\345\270\203\345\261\200.kv" +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/04-\345\207\275\346\225\260\345\206\231\346\247\275\344\270\216\345\270\203\345\261\200.kv" @@ -17,11 +17,11 @@ rwfunc 布进 `/lib/·/`: 1. `[0,0]`——签名行锚点,langtype `rwfunc`,body `[nr:u16 LE][nw:u16 LE][dynamic:u8]`(读参数/写参数/ 是否变参),仅此计数头。rwfunc **不**在 func 目录根落 `[0,±k]` 参数类型槽。 -2. `/lib/·.src`——源码副本(langtype `[]char/utf8`,storetype ARRAYND)。 +2. `/lib/·.src`——源码副本(langtype `[n]char/utf8`,class 1)。 3. 每个参数定义键——`funcDir.[0,-j]`(读参)/ `funcDir.[0,+j]`(写参),点后缀、与坐标斜杠键 `/[0,±j]` 不同名;langtype=`def langtype`,body = 参数名 + NUL + 该参的 def langtype(匹配类型)串。此键是**签名元数 据**(dump / 类型检查 / 调用期类型检查用),**不参与运行时形参解析**——函数体指令里的形参引用 - 已在编译期替换为帧坐标:值参 `[0,±j]` 只读、地址参(声明带 `*`/`@`)`*[0,±j]` 解引用(也只读)—— + 已在编译期替换为 `*[0,±j]`;runtime 沿帧槽 `**T` 和实参键 `*T` 读写调用方原 key。 读参(`param<0`)一律不得作写槽(见 [[函数]]、[[调用extindex]])。 4. 指令体从 `[1,0]` 起(`[0,0]` 为签名行锚点)。 diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/10-\345\270\270\350\247\201\350\257\212\346\226\255.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/10-\345\270\270\350\247\201\350\257\212\346\226\255.kv" index 449334b1..b3b62b37 100644 --- "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/10-\345\270\270\350\247\201\350\257\212\346\226\255.kv" +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/10-\345\270\270\350\247\201\350\257\212\346\226\255.kv" @@ -6,8 +6,7 @@ lib kvlang/spec/layout语义/常见诊断 { - 参数缺类型标注——`param "x" has no type annotation` / `return value "r" has no type annotation`。 - 读参被写——`read param "A" cannot be used as write slot (read params are read-only)`。 -- 值容器按值传参——`param "p": 值容器类型只能按地址传递 —— 写 "*/lib/Point"`(值容器无值本体可拷,须带 - `*`/`@`)。 +- 值容器参数须标注其实际目标类型;调用统一传地址,见 [[函数]]。 - 参数名重复(读参列表内、写参列表内、读写之间均不得同名,见 [[函数]])——读写之间 `param "A" appears in both read-params and write-params`;列表内 `duplicate param "a" in read-params` / `in write-params`。**调用**时不受限:`inc(x) -> x` 合法。 diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/12-rwfunc\345\270\203\345\261\200\344\270\216def-rwir.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/12-rwfunc\345\270\203\345\261\200\344\270\216def-rwir.kv" index e7b2475c..f82041d7 100644 --- "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/12-rwfunc\345\270\203\345\261\200\344\270\216def-rwir.kv" +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/12-rwfunc\345\270\203\345\261\200\344\270\216def-rwir.kv" @@ -8,11 +8,11 @@ lib kvlang/spec/layout语义/rwfunc布局与def_rwir { - `[0,0]`——rwfunc 签名锚点,body 仅 `[nr][nw][dynamic]` 计数头;func 目录根**不**落 `[0,±k]` 参数 类型槽。 -- `.src`(ASCII 点后缀,`/lib/·.src`)——源码副本,langtype `[]char/utf8`(storetype ARRAYND)。 +- `.src`(ASCII 点后缀,`/lib/·.src`)——源码副本,langtype `[n]char/utf8`(class 1)。 - 每个参数定义键——`funcDir.[0,-j]`(读参)/ `funcDir.[0,+j]`(写参),点后缀、与坐标斜杠键 `/[0,±j]` 不同名;langtype=`def langtype`,body = 参数名 + NUL + 该参的 def langtype(匹配类型)串。此键为签名元数据 (dump / 类型检查用),不参与运行时形参解析——函数体指令里的形参引用已编译期替换为帧坐标的显式解 - 引用 `*[0,-j]`/`*[0,+j]`,运行期直接读帧槽解引用到实参值(见 [[调用extindex]])。 + 引用 `*[0,-j]`/`*[0,+j]`,运行期沿帧槽 `**T` 与实参键 `*T` 指向调用方值(见 [[调用extindex]])。 - `[i,j]`(i≥1)——线性指令(见 [[控制流]])。 - `‥labels/