From 9c51aa453283cf0c93f06a8a588cb9b978f684d4 Mon Sep 17 00:00:00 2001 From: miaobyte Date: Fri, 30 Oct 2026 09:43:54 +0800 Subject: [PATCH] =?UTF-8?q?kvspace:=20XValue=20head=20=E5=9B=BA=E5=AE=9A?= =?UTF-8?q?=E5=8C=96=20+=20WriteNewPlace=20=E5=8E=BB=20langtype=20+=20side?= =?UTF-8?q?car=E2=86=92attr=20=E5=91=BD=E5=90=8D=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - head 固定 8 字节:[storetype:u8][ptr:u8][reserved:u16][len:u32], body 偏移恒定;emit/decode 不再拼/解 langtype - WriteNewPlace 去 langtype 参数;sidecar 命名词统一为 attr - 前端 dispatch / codec ABI / CLI / C 与多语言 tutorial 同步 - .gitignore:忽略 CLI 冒烟 store 产物(cli/smoke*) Co-Authored-By: Claude Code --- .gitignore | 3 + CMakeLists.txt | 4 +- README.md | 2 +- cli/kvspace.rs | 293 ++++++++++------- include/kvspace/kvspace.h | 40 +-- include/kvspace/xvalue_head.h | 101 +++--- src/codec_abi.c | 157 ++------- src/frontend.c | 29 +- src/xvalue_head.c | 603 ++++------------------------------ src/xvalue_meta.c | 60 ---- src/xvalue_meta.h | 18 - tests/test_codec_abi.c | 101 +++--- tests/test_xvalue_head.c | 443 +++++-------------------- tutorial-c/01_basic.c | 10 +- tutorial-c/02_cpp.cpp | 42 ++- tutorial-c/03_python.py | 90 +++-- tutorial-c/04_rust.rs | 109 +++--- tutorial-c/05_integrity.c | 32 +- tutorial-c/06_multiprocess.c | 24 +- tutorial-c/common.h | 65 ++-- tutorial-c/test.py | 3 +- 21 files changed, 742 insertions(+), 1487 deletions(-) delete mode 100644 src/xvalue_meta.c delete mode 100644 src/xvalue_meta.h diff --git a/.gitignore b/.gitignore index f196a35..039dd61 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ kvtest* # CodeGraph 索引(本地知识图,不入库) .codegraph/ + +# CLI 冒烟测试 store 产物 +cli/smoke* diff --git a/CMakeLists.txt b/CMakeLists.txt index c0b44ab..2b47091 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "" FORCE) endif() -add_library(kvspace SHARED src/frontend.c src/codec_abi.c src/xvalue_head.c src/xvalue_meta.c) +add_library(kvspace SHARED src/frontend.c src/codec_abi.c src/xvalue_head.c) include(CTest) if(BUILD_TESTING) @@ -49,8 +49,6 @@ install(TARGETS kvspace LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/kvspace ) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/xvalue_meta.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/kvspace) configure_file(kvspace.pc.in ${CMAKE_CURRENT_BINARY_DIR}/kvspace.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kvspace.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) diff --git a/README.md b/README.md index bd290af..ca4d296 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ KVSpace 的 dispatch 前端。消费者只链接 `libkvspace`,运行期按 DSN 两个后端导出同一套 `kvspace*` C ABI(见 `include/kvspace/kvspace.h`)。 前端用 `dlopen(RTLD_NOW | RTLD_LOCAL)` 装载后端,handle 包一层 vtable; -codec(`kvspaceXvalueEncode*`/`kvspaceDecodeHead`/`kvspaceNew*`)无 handle,由前端静态实现, +XValue 构造(`kvspaceNew*`/`kvspaceXvalueHeadNew*`)无 handle,由前端静态实现, head 格式 byte-identical,两个后端与前端三者共用同一份契约。 后端一律按固定目录查找(Linux `/usr/lib/kvspace`,macOS `/usr/local/lib/kvspace`); diff --git a/cli/kvspace.rs b/cli/kvspace.rs index e95b789..3f1acae 100644 --- a/cli/kvspace.rs +++ b/cli/kvspace.rs @@ -5,13 +5,16 @@ use std::env; use std::os::raw::{c_char, c_int, c_void}; use std::process::exit; +/* 固定 head 8 字节(见 kvspace/xvalue_head.h):body 偏移恒 8。 */ +const KVSPACE_XVALUE_HEAD_LEN: usize = 8; +const KVSPACE_XVALUE_ST_SHORT: u8 = 0; + +/* 与 kvspace.h 的 kvspaceHead_t 布局一致(ro/vid 已从 ABI 删除)。 */ #[repr(C)] struct Head { headlen: u16, r#ref: u8, storetype: u8, - ro: u8, - vid: u32, body_len: i32, ndim: i32, dims: [i32; 8], @@ -52,8 +55,6 @@ extern "C" { key: *const c_char, value: *const u8, value_len: u32, - ro: u8, - vid: u32, err: *mut c_char, err_cap: u32, ) -> c_int; @@ -123,15 +124,6 @@ extern "C" { err_cap: u32, ) -> c_int; fn kvspaceClear(h: *mut c_void, err: *mut c_char, err_cap: u32) -> c_int; - fn kvspaceXvalueEncode( - kind: *const c_char, - raw: *const u8, - raw_len: u32, - dims: *const i32, - ndim: i32, - out: *mut *mut u8, - out_len: *mut u32, - ) -> c_int; fn kvspaceNewPtr( target_kindexpr: *const c_char, target: *const c_char, @@ -142,7 +134,13 @@ extern "C" { fn kvspaceNewBool(v: u8, out: *mut *mut u8, out_len: *mut u32) -> c_int; fn kvspaceNewInt64(v: i64, out: *mut *mut u8, out_len: *mut u32) -> c_int; fn kvspaceNewFloat64(v: f64, out: *mut *mut u8, out_len: *mut u32) -> c_int; - fn kvspaceDecodeHead(data: *const u8, data_len: u32, out: *mut Head) -> c_int; + fn kvspaceXvalueHeadNewScalar( + storetype: u8, + raw: *const u8, + raw_len: u32, + out: *mut *mut u8, + out_len: *mut u64, + ) -> c_int; fn kvspaceGetHead(h: *mut c_void, key: *const c_char, out: *mut Head) -> c_int; } @@ -177,25 +175,27 @@ struct Value { body: Vec, } -fn decode(data: &[u8]) -> Value { +/* head 固定 8 字节 [len:u48 LE@0-5][storetype@6][ptr@7],body 偏移恒 8;wire 无 + langtype——类型/形状由 kvspaceGetHead 从 x‥langtype attr key 读。 */ +fn from_wire(kv: *mut c_void, key: &str, data: &[u8]) -> Value { let mut h = zero_head(); - unsafe { - kvspaceDecodeHead(data.as_ptr(), data.len() as u32, &mut h); - } - let lt = String::from_utf8_lossy(langtype_bytes(&h)).into_owned(); + let ok = unsafe { kvspaceGetHead(kv, cs(key), &mut h) == 0 }; + let lt = if ok { + String::from_utf8_lossy(langtype_bytes(&h)).into_owned() + } else { + String::new() + }; let (_ltdims, kind) = parse_langtype(<); - let ndim = (h.ndim.max(0) as usize).min(8); - let dims: Vec = h.dims[..ndim].to_vec(); - let off = h.body_offset as usize; - let len = h.body_len.max(0) as usize; - let body = if off + len <= data.len() { - data[off..off + len].to_vec() + let ndim = if ok { (h.ndim.max(0) as usize).min(8) } else { 0 }; + let dims: Vec = if ok { h.dims[..ndim].to_vec() } else { vec![] }; + let body = if data.len() > KVSPACE_XVALUE_HEAD_LEN { + data[KVSPACE_XVALUE_HEAD_LEN..].to_vec() } else { vec![] }; Value { kind, - r#ref: h.r#ref as i32, + r#ref: if ok { h.r#ref as i32 } else { 0 }, dims, body, } @@ -301,84 +301,126 @@ fn get(kv: *mut c_void, key: &str) -> Vec { } } -fn parse_value(raw: &str) -> Vec { +// 解析 value 语法为 (wire, langtype):langtype 是无前缀裸 kindexpr(int64/[]char/utf8/键·值), +// 由 set_one 写 x‥langtype attr key(字符串 XValue);空 = 无类型(不约束指针)。 +fn parse_value(raw: &str) -> (Vec, String) { let mut out: *mut u8 = std::ptr::null_mut(); let mut len: u32 = 0; - let ok = if let Some(rest) = raw.strip_prefix('*') { + if let Some(rest) = raw.strip_prefix('*') { // `*:kindexpr:target`(kindexpr 在前,与 string:/int: 同构);`*:target` 为不约束指针。 let rest = rest.strip_prefix(':').unwrap_or(rest); let (k, t) = match rest.find(':') { Some(i) => (&rest[..i], &rest[i + 1..]), None => ("", rest), }; - unsafe { kvspaceNewPtr(cs(k), cs(t), &mut out, &mut len) == 0 } - } else if let Some(langtype) = raw.strip_prefix("map:") { + if unsafe { kvspaceNewPtr(cs(k), cs(t), &mut out, &mut len) != 0 } { + fatalf(&format!("parse value failed: {:?}", raw)); + } + let v = take_wire(out, len); + return (v, k.to_string()); // kindexpr 无 * 前缀;不约束(空 kindexpr)→ 空 langtype + } + if let Some(langtype) = raw.strip_prefix("map:") { if !langtype.contains('·') { fatalf("map requires key·value langtype"); } - unsafe { - kvspaceXvalueEncode( - cs(langtype), - std::ptr::null(), - 0, + // 空 body 的短定长头(类型在 x‥langtype attr key,由 layout/明文层写)。 + let mut len64: u64 = 0; + if unsafe { + kvspaceXvalueHeadNewScalar( + KVSPACE_XVALUE_ST_SHORT, std::ptr::null(), 0, &mut out, - &mut len, - ) == 0 + &mut len64, + ) != 0 + } { + fatalf(&format!("parse value failed: {:?}", raw)); } - } else { - let split = raw.find(':').unwrap_or(raw.len()); - let kind = &raw[..split]; - let repr = raw[split..].trim_start_matches(':'); - match kind { - "int" => unsafe { - repr.parse::() - .map(|i| kvspaceNewInt64(i, &mut out, &mut len) == 0) - .unwrap_or(false) - }, - "float" => unsafe { - repr.parse::() - .map(|f| kvspaceNewFloat64(f, &mut out, &mut len) == 0) - .unwrap_or(false) - }, - "float32" => unsafe { - repr.parse::() - .map(|f| { - kvspaceXvalueEncode( - cs("float32"), - &f as *const f32 as *const u8, - 4, - std::ptr::null(), - 0, - &mut out, - &mut len, - ) == 0 - }) - .unwrap_or(false) - }, - "bool" => { - let b = match repr { - "true" => true, - "false" => false, - _ => fatalf(&format!("bool 只接受 true/false: {:?}", raw)), - }; - unsafe { kvspaceNewBool(b as u8, &mut out, &mut len) == 0 } + let v = take_wire(out, len64 as u32); + return (v, langtype.to_string()); // 裸 kindexpr(如 int·string) + } + let split = raw.find(':').unwrap_or(raw.len()); + let kind = &raw[..split]; + let repr = raw[split..].trim_start_matches(':'); + match kind { + "int" => unsafe { + if kvspaceNewInt64( + repr.parse().unwrap_or_else(|_| fatalf(&format!("parse value failed: {:?}", raw))), + &mut out, + &mut len, + ) != 0 + { + fatalf(&format!("parse value failed: {:?}", raw)); } - "string" => unsafe { - kvspaceNewChar(repr.as_ptr(), repr.len() as u32, &mut out, &mut len) == 0 - }, - "nil" => { - return vec![]; + }, + "float" => unsafe { + if kvspaceNewFloat64( + repr.parse().unwrap_or_else(|_| fatalf(&format!("parse value failed: {:?}", raw))), + &mut out, + &mut len, + ) != 0 + { + fatalf(&format!("parse value failed: {:?}", raw)); + } + }, + "float32" => { + let f: f32 = repr + .parse() + .unwrap_or_else(|_| fatalf(&format!("parse value failed: {:?}", raw))); + // 4 字节小端 f32 body 的短定长头。 + let mut len64: u64 = 0; + if unsafe { + kvspaceXvalueHeadNewScalar( + KVSPACE_XVALUE_ST_SHORT, + &f as *const f32 as *const u8, + 4, + &mut out, + &mut len64, + ) != 0 + } { + fatalf(&format!("parse value failed: {:?}", raw)); + } + len = len64 as u32; + } + "bool" => { + let b = match repr { + "true" => true, + "false" => false, + _ => fatalf(&format!("bool 只接受 true/false: {:?}", raw)), + }; + unsafe { + if kvspaceNewBool(b as u8, &mut out, &mut len) != 0 { + fatalf(&format!("parse value failed: {:?}", raw)); + } } - "index" => fatalf("use mkindex for directories"), - // 无 kind 前缀或未识别前缀 → 默认按字符串处理(见 help 的 value 语法)。 - _ => unsafe { kvspaceNewChar(raw.as_ptr(), raw.len() as u32, &mut out, &mut len) == 0 }, } + "string" => unsafe { + if kvspaceNewChar(repr.as_ptr(), repr.len() as u32, &mut out, &mut len) != 0 { + fatalf(&format!("parse value failed: {:?}", raw)); + } + }, + "nil" => return (Vec::new(), String::new()), // 删除(无 attr) + "index" => fatalf("use mkindex for directories"), + // 无 kind 前缀或未识别前缀 → 默认按字符串处理(见 help 的 value 语法)。 + _ => unsafe { + if kvspaceNewChar(raw.as_ptr(), raw.len() as u32, &mut out, &mut len) != 0 { + fatalf(&format!("parse value failed: {:?}", raw)); + } + }, + } + let langtype = match kind { + "int" => "int64", + "float" => "float64", + "float32" => "float32", + "bool" => "bool", + "string" => "[]char/utf8", + _ => "[]char/utf8", // 默认字符串同 string }; - if !ok { - fatalf(&format!("parse value failed: {:?}", raw)); - } + (take_wire(out, len), langtype.to_string()) +} + +// codec 产出为 frontend malloc,读出即 free;失败已 fatalf 退出,到这里的 out 恒非空。 +fn take_wire(out: *mut u8, len: u32) -> Vec { unsafe { let v = std::slice::from_raw_parts(out, len as usize).to_vec(); free(out as *mut c_void); // codec 产出为 frontend malloc,libc free @@ -386,13 +428,51 @@ fn parse_value(raw: &str) -> Vec { } } -fn set_one(kv: *mut c_void, key: &str, val: &[u8], ro: u8, vid: u32) { +// 写主值 + x‥langtype attr key(#566:创建 key 时 attr 与主 key 同时写; +// 写序 attr 先落、x 后落——x 写=提交点)。attr 值是字符串 XValue(char/utf8, +// body=明文类型串)。langtype 空(如不约束指针)→ 只写主值。 +fn set_one(kv: *mut c_void, key: &str, val: &[u8], langtype: &str) { if val.is_empty() { let key_ptr = cs(key); let mut err = [0u8; 256]; unsafe { kvspaceDel(kv, &key_ptr, 1, err.as_mut_ptr() as *mut c_char, 256) }; return; } + if !langtype.is_empty() { + let attr = format!("{key}\u{2025}langtype"); + let mut out: *mut u8 = std::ptr::null_mut(); + let mut len: u32 = 0; + if unsafe { + kvspaceNewChar( + langtype.as_ptr(), + langtype.len() as u32, + &mut out, + &mut len, + ) != 0 + } { + fatalf("parse value failed: langtype attr key"); + } + let aw = take_wire(out, len); + let mut err = [0u8; 256]; + let rc = unsafe { + kvspaceSetValue( + kv, + cs(&attr), + aw.as_ptr(), + aw.len() as u32, + err.as_mut_ptr() as *mut c_char, + 256, + ) + }; + if rc != 0 { + let n = err.iter().position(|&b| b == 0).unwrap_or(err.len()); + fatalf(&format!( + "set {} attr key: {}", + attr, + String::from_utf8_lossy(&err[..n]) + )); + } + } let ck = cs(key); let mut err = [0u8; 256]; let rc = unsafe { @@ -401,8 +481,6 @@ fn set_one(kv: *mut c_void, key: &str, val: &[u8], ro: u8, vid: u32) { ck, val.as_ptr(), val.len() as u32, - ro, - vid, err.as_mut_ptr() as *mut c_char, 256, ) @@ -472,7 +550,7 @@ fn read_ext(kv: *mut c_void, prefix: &str) -> String { if v.is_empty() { return String::new(); } - let d = decode(&v); + let d = from_wire(kv, prefix, &v); if d.kind != "extindex" || d.body.is_empty() { return String::new(); } @@ -489,7 +567,7 @@ fn print_help() { println!("子命令:"); println!(" get ... 读取并打印 key:kind:value"); println!(" set 写入值;value 语法见下;nil: 删除键"); - println!(" head ... 打印值头(ref/ro/vid/ndim/dims)"); + println!(" head ... 打印值头(ref/storetype/ndim/dims)"); println!(" del ... 删除键"); println!(" deltree 删除整棵子树"); println!(" cp 拷贝单键"); @@ -539,28 +617,16 @@ fn main() { if v.is_empty() { println!("{}\t(nil)", k); } else { - println!("{}\t{}", k, format_value(&decode(&v))); + println!("{}\t{}", k, format_value(&from_wire(kv, k, &v))); } } } "set" => { - if tail.len() < 2 { - fatalf("usage: kvspace set [ro|rw] [vid]"); - } - let val = parse_value(&tail[1]); - let (mut ro, mut vid) = (0u8, 0u32); - for t in &tail[2..] { - match t.as_str() { - "ro" => ro = 1, - "rw" => ro = 0, - n => { - vid = n - .parse() - .unwrap_or_else(|_| fatalf(&format!("set: bad token {}", n))) - } - } + if tail.len() != 2 { + fatalf("usage: kvspace set "); } - set_one(kv, &tail[0], &val, ro, vid); + let (val, langtype) = parse_value(&tail[1]); + set_one(kv, &tail[0], &val, &langtype); } "head" => { for k in tail { @@ -572,12 +638,11 @@ fn main() { let (_, kind) = parse_langtype(<); let dims = &h.dims[..(h.ndim.max(0) as usize).min(8)]; println!( - "{}\t{}\tref={}\tro={}\tvid={}\tndim={}\tdims=[{}]", + "{}\t{}\tref={}\tstoretype={}\tndim={}\tdims=[{}]", k, kind, h.r#ref, - h.ro, - h.vid, + h.storetype, h.ndim, dims.iter() .map(|d| d.to_string()) @@ -786,10 +851,10 @@ fn fprint_list(kv: *mut c_void, prefix: &str, show_ext: bool, show_kind: bool) { if v.is_empty() { println!("{}", c); } else if show_kind { - let d = decode(&v); + let d = from_wire(kv, &full, &v); println!("{}\t{}\t{}", c, d.kind, plain(&d)); } else { - let d = decode(&v); + let d = from_wire(kv, &full, &v); println!("{}\t{}", c, plain(&d)); } } @@ -832,10 +897,10 @@ fn fprint_tree(kv: *mut c_void, prefix: &str, indent: &str, show_ext: bool, show if v.is_empty() { println!("{}{}{}", indent, branch, c); } else if show_kind { - let d = decode(&v); + let d = from_wire(kv, &full, &v); println!("{}{}{}\t{}\t{}", indent, branch, c, d.kind, plain(&d)); } else { - let d = decode(&v); + let d = from_wire(kv, &full, &v); println!("{}{}{}\t{}", indent, branch, c, plain(&d)); } } diff --git a/include/kvspace/kvspace.h b/include/kvspace/kvspace.h index a3456ca..79f3670 100644 --- a/include/kvspace/kvspace.h +++ b/include/kvspace/kvspace.h @@ -6,9 +6,9 @@ * shm://... → libkvspace-c.so.1 * 其余(redis/fs/s3)→ libkvspace_durable.so.1 * - * Wire: [pow:u8][flags:u8][a:u64le][b:u64le][langtype][padding][body]. - * Head length is 1 << pow; flags hold the storage class and pointer bit. - * ro/vid are stored under /.kvspace-meta/, outside the XValue. + * Wire: [len:u48 LE@0-5][storetype:u8@6][ptr:u8@7][body@8]. Head is fixed + * 8 bytes; body offset is always 8. Type/shape lives in the x‥langtype + * attr key as a plaintext langtype string. */ #ifndef KVSPACE_H @@ -22,24 +22,22 @@ extern "C" { #endif -/* Decoded location kind. */ +/* 存储位置种类。 */ #define KVSPACE_REF_INLINE 0 #define KVSPACE_REF_PTR 1 #define KVSPACE_REF_EXT 2 -/* Storage classes are the low two wire flag bits. */ +/* Storage classes (kvspaceXvalueHead.storetype wire byte). */ #define KVSPACE_STORETYPE_FIXED_SMALL 0 #define KVSPACE_STORETYPE_SLACK 1 #define KVSPACE_STORETYPE_FIXED_LARGE 2 #define KVSPACE_STORETYPE_EXT 3 -/* Decoded XValue metadata. ro/vid come from the sidecar key on GetHead. */ +/* XValue 元数据。ro/vid 来自 GetHead 的 attr key。 */ typedef struct { uint16_t headlen; /* head 总字节数;body 起于偏移 headlen */ uint8_t ref; /* 存储位置:见 KVSPACE_REF_* */ uint8_t storetype; /* Storage class. */ - uint8_t ro; /* 1=只读,0=可写 */ - uint32_t vid; /* vthread id(默认 0) */ int32_t body_len; /* body 字节数 */ int32_t ndim; /* Tensor dimensions; zero for other values. */ int32_t dims[8]; /* Tensor shape. */ @@ -67,7 +65,7 @@ void kvspaceClose(void *h); int kvspaceGet(void *h, const char *key, int resolve, uint8_t **out, uint32_t *out_len); /* Store a complete XValue before returning. */ 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); + char *err, uint32_t err_cap); /* ResolveRef:block_id=叶子、gen=0;parent_id=目录祖先 ART 节点, * depth=进入该节点时 key 已消费字节数。GetByRef:gen==0 直取叶子; @@ -88,8 +86,8 @@ int kvspaceSetPartByRef(void *h, kvspaceRef_t *ref, const char *key_fallback, /* 整值写 by cached ref:与 kvspaceSetValue 同语义;ref 失效时后端退回 key 的完整写 * 并重解析。后端未实现该符号时,前端退回按 key 的 SetValue。 */ int kvspaceSetValueByRef(void *h, kvspaceRef_t *ref, const char *key_fallback, - const uint8_t *value, uint32_t value_len, uint8_t ro, - uint32_t vid, char *err, uint32_t err_cap); + const uint8_t *value, uint32_t value_len, + char *err, uint32_t err_cap); /* shm-only slot view:把已绑定槽的映射内地址一次性交给调用方,供每步直读/直写。 * `head` 指向 XValue 起点,`body = head + headlen`;`cap` 是整 box 物理容量 @@ -148,7 +146,7 @@ int kvspaceGetPart(void *h, const char *key, uint32_t offset, uint32_t len, int kvspaceSetPart(void *h, const char *key, uint32_t offset, const uint8_t *buf, uint32_t buf_len, char *err, uint32_t err_cap); -/* Read the wire head and ro/vid sidecar. Missing keys return nonzero. */ +/* Read the wire head and ro/vid attr key. Missing keys return nonzero. */ int kvspaceGetHead(void *h, const char *key, kvspaceHead_t *out); /* 就地写:key 必须已存在、kind 不变、body_len 必须等于原 body_len——返回原 box 的 body @@ -158,10 +156,10 @@ int kvspaceGetHead(void *h, const char *key, kvspaceHead_t *out); int kvspaceWriteInPlace(void *h, const char *key, int resolve, uint32_t body_len, uint8_t **body, char *err, uint32_t err_cap); -/* Reserve a new XValue and return its body pointer; fill it before another KVSpace call. */ +/* Reserve a new XValue and return its body pointer; fill it before another KVSpace call. + * kvspace 层不写 attr key——langtype 由上层调用方写 `x‥langtype` attr key(attr 先落、x 后落)。 */ 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, - uint64_t body_cap, + uint32_t body_len, uint64_t body_cap, uint8_t **body, char *err, uint32_t err_cap); /* 只返回前缀下子项计数,无缓冲、无需释放。resolve=1 穿透 link。 */ @@ -187,15 +185,9 @@ int kvspaceClear(void *h, char *err, uint32_t err_cap); int kvspaceWatch(void *h, const char *key, const uint8_t *target, uint32_t target_len, uint64_t tick_ns, uint8_t **out, uint32_t *out_len); -/* ── codec(无 handle,由前端静态实现,byte-identical) ─────────── */ -int kvspaceXvalueEncode(const char *kind, const uint8_t *raw, uint32_t raw_len, - const int32_t *dims, int32_t ndim, uint8_t **out, - uint32_t *out_len); -int kvspaceXvalueEncodeMode(const char *kind, const uint8_t *raw, - uint32_t raw_len, const int32_t *dims, int32_t ndim, - int32_t ref, uint8_t ro, uint32_t vid, uint8_t **out, - uint32_t *out_len); -int kvspaceDecodeHead(const uint8_t *data, uint32_t data_len, kvspaceHead_t *out); +/* ── XValue 构造(无 handle,由前端静态实现,byte-identical)─────────── + * head 固定 8 字节,直接 memcpy struct + body(见 kvspace/xvalue_head.h), + * 类型/形状走 x‥langtype attr key(kvspaceGetHead 读)。 */ int kvspaceNewPtr(const char *target_kindexpr, const char *target, uint8_t **out, uint32_t *out_len); diff --git a/include/kvspace/xvalue_head.h b/include/kvspace/xvalue_head.h index c38a9ce..080d759 100644 --- a/include/kvspace/xvalue_head.h +++ b/include/kvspace/xvalue_head.h @@ -1,5 +1,6 @@ -/* Wire: [pow][flags][a:u64le][b:u64le][langtype][body]. Head length is 1 << pow. - flags: low two bits are storage class; bit 2 marks a pointer. */ +/* Wire: [len:u48][storetype:u8][ptr:u8][body]. Head is fixed 8 bytes, + body offset is always 8. Type/shape (including dims) is NOT in the head — it + lives in the x‥langtype attr key as a plaintext langtype string. */ #ifndef XVALUE_HEAD_H #define XVALUE_HEAD_H @@ -9,59 +10,59 @@ extern "C" { #endif -#define KVSPACE_XH_PREFIX 18u +#define KVSPACE_XVALUE_HEAD_LEN 8u -#define KVSPACE_XH_FIXED_SMALL 0u -#define KVSPACE_XH_SLACK 1u -#define KVSPACE_XH_FIXED_LARGE 2u -#define KVSPACE_XH_EXT 3u -#define KVSPACE_XH_PTR_FLAG 4u +/* storetype:存储形态(= 旧 class) */ +#define KVSPACE_XVALUE_ST_SHORT 0u /* 短定长(标量/None/map 头等,body 宽度由类型定) */ +#define KVSPACE_XVALUE_ST_SLACK 1u /* 变长(字节/字符数组/代码槽/指针路径,len=有效字节) */ +#define KVSPACE_XVALUE_ST_LARGE 2u /* 大定长(张量,body=元素连续) */ +#define KVSPACE_XVALUE_ST_EXT 3u /* 扩展定位(@ext,body=定位串) */ -#define KVSPACE_XH_POW_SCALAR 5u -#define KVSPACE_XH_POW_SLACK 6u -#define KVSPACE_XH_POW_TENSOR 7u +/* ptr:指针/扩展形态 */ +#define KVSPACE_XVALUE_PTR_INLINE 0u +#define KVSPACE_XVALUE_PTR_PTR 1u +#define KVSPACE_XVALUE_PTR_EXT 2u -#define KVSPACE_XH_UTF8 1 -#define KVSPACE_XH_BYTE 2 -#define KVSPACE_XH_UTF32 3 -#define KVSPACE_XH_ASCII 4 +/* 固定 8 字节布局(直接 memcpy struct + body)。 + wire:len:u48@0-5(小端)、storetype@6、ptr@7、body@8。 */ +typedef struct kvspaceXvalueHead { + uint8_t len[6]; /* u48 小端(offset 0-5) */ + uint8_t storetype; + uint8_t ptr; +} kvspaceXvalueHead; -typedef struct kvspaceXh { - uint8_t pow; - uint8_t kind; - uint64_t a; - uint64_t b; - const uint8_t *langtype; - uint32_t langtype_len; - const uint8_t *body; - uint64_t content_len; - uint64_t body_cap; - uint32_t headlen; - uint64_t total; -} kvspaceXh; - -/* 0 on success. -1 and *out cleared on failure. Bytes past total are ignored. */ -int kvspaceXhDecode(const uint8_t *data, uint64_t len, kvspaceXh *out); +/* u48 len 的唯一定义点:读/写一律走这两个 inline 方法,禁止各处手写拼接。 */ +static inline uint64_t kvspaceXvalueHeadLen(const kvspaceXvalueHead *h) { + uint64_t v = 0; + for (int i = 5; i >= 0; i--) + v = (v << 8) | h->len[i]; + return v; +} +static inline void kvspaceXvalueHeadSetLen(kvspaceXvalueHead *h, uint64_t v) { + for (int i = 0; i < 6; i++) { + h->len[i] = (uint8_t)(v & 0xff); + v >>= 8; + } +} -int kvspaceXhNewNone(uint8_t **out, uint64_t *out_len); -int kvspaceXhNewScalar(const char *langtype, const uint8_t *raw, uint32_t raw_len, - uint8_t **out, uint64_t *out_len); -int kvspaceXhNewShort(const char *langtype, const uint8_t *body, uint32_t body_len, - uint8_t **out, uint64_t *out_len); -/* data_len and cap are bytes; character counts are derived. */ -int kvspaceXhNewSlack(int elem, const uint8_t *data, uint64_t data_len, uint64_t cap, - uint8_t **out, uint64_t *out_len); -int kvspaceXhNewTensor(const uint64_t *dims, uint32_t ndim, const char *elem, - const uint8_t *raw, uint64_t raw_len, - uint8_t **out, uint64_t *out_len); -int kvspaceXhNewPtr(const char *langtype, const char *path, uint64_t cap, - uint8_t **out, uint64_t *out_len); -int kvspaceXhNewExt(const char *langtype, const char *locator, uint64_t cap, - uint8_t **out, uint64_t *out_len); -int kvspaceXhNewCode(const char *langtype, const uint8_t *body, uint64_t body_len, - uint64_t cap, uint8_t **out, uint64_t *out_len); -int kvspaceXhReserve(uint8_t kind, const char *langtype, uint64_t body_len, - uint64_t cap, uint8_t **out, uint64_t *out_len); +/* 构造(固定头 + body,不含类型;langtype 由调用方写 x‥langtype attr key)。 + 0 on success. -1 and *out cleared on failure. */ +int kvspaceXvalueHeadNewNone(uint8_t **out, uint64_t *out_len); +int kvspaceXvalueHeadNewScalar(uint8_t storetype, const uint8_t *raw, + uint32_t raw_len, uint8_t **out, uint64_t *out_len); +int kvspaceXvalueHeadNewSlack(const uint8_t *data, uint64_t data_len, + uint64_t cap, uint8_t **out, uint64_t *out_len); +int kvspaceXvalueHeadNewTensor(const uint64_t *dims, uint32_t ndim, + const uint8_t *raw, uint64_t raw_len, + uint8_t **out, uint64_t *out_len); +int kvspaceXvalueHeadNewPtr(const uint8_t *path, uint32_t path_len, + uint8_t **out, uint64_t *out_len); +int kvspaceXvalueHeadNewExt(const uint8_t *locator, uint32_t locator_len, + uint8_t **out, uint64_t *out_len); +int kvspaceXvalueHeadNewCode(const uint8_t *body, uint64_t body_len, + uint64_t cap, uint8_t **out, uint64_t *out_len); +int kvspaceXvalueHeadReserve(uint8_t storetype, uint64_t body_len, + uint64_t cap, uint8_t **out, uint64_t *out_len); #ifdef __cplusplus } diff --git a/src/codec_abi.c b/src/codec_abi.c index a49eada..5a7aae4 100644 --- a/src/codec_abi.c +++ b/src/codec_abi.c @@ -15,123 +15,6 @@ static int finish(uint8_t *value, uint64_t len, uint8_t **out, uint32_t *out_len return 0; } -static int locator(int ext, const char *type, const uint8_t *raw, uint32_t len, - uint8_t **out, uint32_t *out_len) { - if (!raw || memchr(raw, 0, len)) - return 1; - char *path = malloc((size_t)len + 1); - if (!path) - return 1; - memcpy(path, raw, len); - path[len] = 0; - uint8_t *value = NULL; - uint64_t total = 0; - int rc = ext ? kvspaceXhNewExt(type, path, len, &value, &total) : - kvspaceXhNewPtr(type, path, len, &value, &total); - free(path); - return rc == 0 ? finish(value, total, out, out_len) : 1; -} - -int kvspaceXvalueEncode(const char *kind, const uint8_t *raw, uint32_t raw_len, - const int32_t *dims, int32_t ndim, uint8_t **out, - uint32_t *out_len) { - return kvspaceXvalueEncodeMode(kind, raw, raw_len, dims, ndim, 0, 0, 0, out, out_len); -} - -int kvspaceXvalueEncodeMode(const char *kind, const uint8_t *raw, - uint32_t raw_len, const int32_t *dims, int32_t ndim, - int32_t ref, uint8_t ro, uint32_t vid, uint8_t **out, - uint32_t *out_len) { - if (!out || !out_len || !kind || ndim < 0 || ndim > 8 || - (ndim && !dims) || (raw_len && !raw) || ro || vid) - return 1; - *out = NULL; - *out_len = 0; - if (ref == KVSPACE_REF_PTR || ref == KVSPACE_REF_EXT) - return locator(ref == KVSPACE_REF_EXT, kind, raw, raw_len, out, out_len); - if (ref != KVSPACE_REF_INLINE) - return 1; - - uint8_t *value = NULL; - uint64_t total = 0; - int rc = -1; - if ((kind[0] == 0 || strcmp(kind, "None") == 0) && raw_len == 0 && ndim == 0) { - rc = kvspaceXhNewNone(&value, &total); - } else if (strcmp(kind, KVSPACE_KIND_CHAR_UTF8) == 0 || - strcmp(kind, KVSPACE_KIND_CHAR_ASCII) == 0 || - strcmp(kind, KVSPACE_KIND_CHAR) == 0) { - int elem = strcmp(kind, KVSPACE_KIND_CHAR_UTF8) == 0 ? KVSPACE_XH_UTF8 : - strcmp(kind, KVSPACE_KIND_CHAR_ASCII) == 0 ? KVSPACE_XH_ASCII : - KVSPACE_XH_UTF32; - rc = kvspaceXhNewSlack(elem, raw, raw_len, raw_len, &value, &total); - } else if (ndim > 0) { - uint64_t shape[8]; - for (int32_t i = 0; i < ndim; i++) { - if (dims[i] < 0) - return 1; - shape[i] = (uint64_t)dims[i]; - } - rc = kvspaceXhNewTensor(shape, (uint32_t)ndim, kind, raw, raw_len, - &value, &total); - } else if (strcmp(kind, "rwir") == 0 || strcmp(kind, "def langtype") == 0 || - (strcmp(kind, "rwfunc") == 0 && raw_len != 0)) { - rc = kvspaceXhNewCode(kind, raw, raw_len, raw_len, &value, &total); - } else if (kvspaceXhNewScalar(kind, raw, raw_len, &value, &total) == 0) { - rc = 0; - } else { - rc = kvspaceXhNewShort(kind, raw, raw_len, &value, &total); - } - return rc == 0 ? finish(value, total, out, out_len) : 1; -} - -static int parse_dims(const char *type, int32_t *dims, int32_t *ndim) { - *ndim = 0; - if (type[0] != '[') - return 0; - const char *p = type + 1; - for (;;) { - if (*ndim == 8 || *p < '0' || *p > '9') - return -1; - uint64_t n = 0; - do { - n = n * 10 + (uint64_t)(*p++ - '0'); - if (n > INT32_MAX) - return -1; - } while (*p >= '0' && *p <= '9'); - dims[(*ndim)++] = (int32_t)n; - if (*p == ']') - return 0; - if (*p++ != ',') - return -1; - } -} - -int kvspaceDecodeHead(const uint8_t *data, uint32_t data_len, kvspaceHead_t *out) { - if (!out) - return 1; - memset(out, 0, sizeof *out); - kvspaceXh h; - if (kvspaceXhDecode(data, data_len, &h) != 0 || h.total != data_len || - h.headlen > UINT16_MAX || h.content_len > INT32_MAX || - h.langtype_len > sizeof out->langtype - 1) - return 1; - out->headlen = (uint16_t)h.headlen; - out->ref = h.kind & KVSPACE_XH_PTR_FLAG ? KVSPACE_REF_PTR : - h.kind == KVSPACE_XH_EXT ? KVSPACE_REF_EXT : KVSPACE_REF_INLINE; - out->storetype = h.kind & 3; - out->body_len = (int32_t)h.content_len; - out->body_offset = (int32_t)h.headlen; - out->body_cap = h.body_cap; - out->langtype_len = (int32_t)h.langtype_len; - memcpy(out->langtype, h.langtype, h.langtype_len); - if (h.kind == KVSPACE_XH_FIXED_LARGE || - (h.kind == KVSPACE_XH_SLACK && h.langtype_len && h.langtype[0] == '[')) { - if (parse_dims(out->langtype, out->dims, &out->ndim) != 0) - return 1; - } - return 0; -} - int kvspaceNewPtr(const char *target_kindexpr, const char *target, uint8_t **out, uint32_t *out_len) { if (!target_kindexpr || !target || !out || !out_len) @@ -139,34 +22,64 @@ int kvspaceNewPtr(const char *target_kindexpr, const char *target, /* `any` 不是类型名(空 kindexpr = 不约束,见 spec [[文法与合法性]])。 */ if (strcmp(target_kindexpr, "any") == 0) return 1; + /* target_kindexpr 写入 x‥langtype attr key(由调用方/GetHead 层处理); + head 本体固定,只存指针路径。 */ uint8_t *value = NULL; uint64_t total = 0; - if (kvspaceXhNewPtr(target_kindexpr, target, strlen(target), &value, &total) != 0) + if (kvspaceXvalueHeadNewPtr((const uint8_t *)target, + (uint32_t)strlen(target), &value, &total) != 0) return 1; return finish(value, total, out, out_len); } int kvspaceNewChar(const uint8_t *bytes, uint32_t len, uint8_t **out, uint32_t *out_len) { - return kvspaceXvalueEncode(KVSPACE_KIND_CHAR_UTF8, bytes, len, NULL, 0, out, out_len); + if (!out || !out_len || (len && !bytes)) + return 1; + uint8_t *value = NULL; + uint64_t total = 0; + if (kvspaceXvalueHeadNewSlack(bytes, len, len, &value, &total) != 0) + return 1; + return finish(value, total, out, out_len); } int kvspaceNewBool(uint8_t v, uint8_t **out, uint32_t *out_len) { + if (!out || !out_len) + return 1; uint8_t b = v ? 1 : 0; - return kvspaceXvalueEncode(KVSPACE_KIND_BOOL, &b, 1, NULL, 0, out, out_len); + uint8_t *value = NULL; + uint64_t total = 0; + if (kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, &b, 1, + &value, &total) != 0) + return 1; + return finish(value, total, out, out_len); } int kvspaceNewInt64(int64_t v, uint8_t **out, uint32_t *out_len) { + if (!out || !out_len) + return 1; uint8_t raw[8]; for (int i = 0; i < 8; i++) raw[i] = (uint8_t)((uint64_t)v >> (8 * i)); - return kvspaceXvalueEncode(KVSPACE_KIND_INT64, raw, 8, NULL, 0, out, out_len); + uint8_t *value = NULL; + uint64_t total = 0; + if (kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, + &value, &total) != 0) + return 1; + return finish(value, total, out, out_len); } int kvspaceNewFloat64(double v, uint8_t **out, uint32_t *out_len) { + if (!out || !out_len) + return 1; uint64_t bits; memcpy(&bits, &v, sizeof bits); uint8_t raw[8]; for (int i = 0; i < 8; i++) raw[i] = (uint8_t)(bits >> (8 * i)); - return kvspaceXvalueEncode(KVSPACE_KIND_FLOAT64, raw, 8, NULL, 0, out, out_len); + uint8_t *value = NULL; + uint64_t total = 0; + if (kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, + &value, &total) != 0) + return 1; + return finish(value, total, out, out_len); } diff --git a/src/frontend.c b/src/frontend.c index 3386a06..4cb24db 100644 --- a/src/frontend.c +++ b/src/frontend.c @@ -2,7 +2,8 @@ * * 导出 kvspace* C ABI(与两个后端同名同签名)。运行期按 DSN scheme 用 dlopen * (RTLD_NOW | RTLD_LOCAL)装载后端,把 handle 包一层 {vtable, dl, backend}。 - * codec(XvalueEncode、DecodeHead、New 等)与 kvspaceConst 无 handle,由前端静态实现,byte-identical。 + * XValue 构造(kvspaceNew*,head 固定 8 字节 memcpy + body)与 kvspaceConst + * 无 handle,由前端静态实现,byte-identical。 * * 后端装载名(后缀随平台): * shm://... → libkvspace-c.so.1 / macOS: libkvspace-c.dylib @@ -29,7 +30,7 @@ typedef struct { void (*free)(void *h); int (*get)(void *h, const char *key, int resolve, uint8_t **out, uint32_t *out_len); int (*setvalue)(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); + char *err, uint32_t err_cap); int (*getpart)(void *h, const char *key, uint32_t offset, uint32_t len, uint8_t **out, uint32_t *out_len); int (*setpart)(void *h, const char *key, uint32_t offset, const uint8_t *buf, @@ -38,8 +39,7 @@ typedef struct { int (*writeinplace)(void *h, const char *key, int resolve, uint32_t body_len, uint8_t **body, char *err, uint32_t err_cap); int (*writenewplace)(void *h, const char *key, uint8_t ref, uint8_t storetype, - uint8_t ro, uint32_t vid, const char *langtype, uint32_t body_len, - uint64_t body_cap, + uint32_t body_len, uint64_t body_cap, uint8_t **body, char *err, uint32_t err_cap); int (*listlen)(void *h, const char *prefix, int expand_ext, int resolve, int32_t *out_count); int (*listat)(void *h, const char *prefix, int expand_ext, int resolve, int32_t idx, @@ -62,8 +62,8 @@ typedef struct { uint32_t offset, const uint8_t *buf, uint32_t buf_len, char *err, uint32_t err_cap); int (*setvaluebyref)(void *h, kvspaceRef_t *ref, const char *key_fallback, - const uint8_t *value, uint32_t value_len, uint8_t ro, - uint32_t vid, char *err, uint32_t err_cap); + const uint8_t *value, uint32_t value_len, + char *err, uint32_t err_cap); int (*xvalue_body_view)(void *h, const kvspaceRef_t *ref, const char *key_fallback, kvspaceXvalueBodyView_t *out); int (*viewsweep)(void *h); @@ -272,9 +272,9 @@ int kvspaceGet(void *h, const char *key, int resolve, uint8_t **out, uint32_t *o } 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) { + char *err, uint32_t err_cap) { kvspace_handle *x = H(h); - return x->vt->setvalue(x->backend, key, value, value_len, ro, vid, err, err_cap); + return x->vt->setvalue(x->backend, key, value, value_len, err, err_cap); } int kvspaceResolveRef(void *h, const char *key, kvspaceRef_t *ref) { @@ -311,15 +311,15 @@ int kvspaceXvalueBodyView(void *h, const kvspaceRef_t *ref, const char *key_fall } int kvspaceSetValueByRef(void *h, kvspaceRef_t *ref, const char *key_fallback, - const uint8_t *value, uint32_t value_len, uint8_t ro, - uint32_t vid, char *err, uint32_t err_cap) { + const uint8_t *value, uint32_t value_len, + char *err, uint32_t err_cap) { kvspace_handle *x = H(h); if (x->vt->setvaluebyref) return x->vt->setvaluebyref(x->backend, ref, key_fallback, value, - value_len, ro, vid, err, err_cap); + value_len, err, err_cap); if (!key_fallback) return 1; - return x->vt->setvalue(x->backend, key_fallback, value, value_len, ro, vid, + return x->vt->setvalue(x->backend, key_fallback, value, value_len, err, err_cap); } @@ -347,11 +347,10 @@ int kvspaceWriteInPlace(void *h, const char *key, int resolve, uint32_t body_len } 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, - uint64_t body_cap, + uint32_t body_len, uint64_t body_cap, uint8_t **body, char *err, uint32_t err_cap) { kvspace_handle *x = H(h); - return x->vt->writenewplace(x->backend, key, ref, storetype, ro, vid, langtype, + return x->vt->writenewplace(x->backend, key, ref, storetype, body_len, body_cap, body, err, err_cap); } diff --git a/src/xvalue_head.c b/src/xvalue_head.c index c6492f6..232bac2 100644 --- a/src/xvalue_head.c +++ b/src/xvalue_head.c @@ -1,579 +1,116 @@ #include -#include -#include #include #include -static void put_u64(uint8_t *d, uint64_t v) { - for (int i = 0; i < 8; i++) - d[i] = (uint8_t)(v >> (8 * i)); -} +/* V2 最终态(#566):head 固定 8 字节 [len:u48 LE@0-5][storetype:u8@6][ptr:u8@7], + body 偏移恒 = 8。类型/形状(含 dims)不在 head——明文 langtype 串存 x‥langtype + attr key。固定布局:直接 memcpy struct + body。 */ -static uint64_t get_u64(const uint8_t *d) { - uint64_t v = 0; - for (int i = 0; i < 8; i++) - v |= (uint64_t)d[i] << (8 * i); - return v; -} +#define UINT48_MAX UINT64_C(0xFFFFFFFFFFFF) -static int headlen_of(uint8_t pow, uint32_t *headlen) { - if (pow < KVSPACE_XH_POW_SCALAR || pow > 31) +static int build(uint8_t storetype, uint8_t ptr, const uint8_t *body, + uint64_t len, uint8_t **out, uint64_t *out_len) { + if (!out || !out_len || (len && !body)) return -1; - *headlen = 1u << pow; - return 0; -} - -static int scalar_width(const uint8_t *s, uint32_t n) { - static const struct { - const char *name; - uint8_t w; - } tab[] = { - {"bool", 1}, {"int8", 1}, {"int16", 2}, {"int32", 4}, - {"int64", 8}, {"uint8", 1}, {"uint16", 2}, {"uint32", 4}, - {"uint64", 8}, {"float8/e4m3", 1}, {"float8/e5m2", 1}, - {"float16", 2}, {"bfloat16", 2}, {"float32", 4}, {"float64", 8}, - }; - for (size_t i = 0; i < sizeof tab / sizeof tab[0]; i++) { - size_t L = strlen(tab[i].name); - if (L == n && memcmp(s, tab[i].name, n) == 0) - return tab[i].w; - } - return -1; -} - -static int type_eq(const uint8_t *s, uint32_t n, const char *want) { - size_t m = strlen(want); - return n == m && memcmp(s, want, m) == 0; -} - -static int short_width(const uint8_t *s, uint32_t n) { - int width = scalar_width(s, n); - if (width >= 0 || n == 0) - return width >= 0 ? width : 0; - if (type_eq(s, n, "def rwir")) - return 5; - if (type_eq(s, n, "time") || type_eq(s, n, "duration")) - return 8; - if (type_eq(s, n, "def struct") || type_eq(s, n, "lib") || - type_eq(s, n, "rwfunc") || s[0] == '/') - return 0; - for (uint32_t i = 0; i + 1 < n; i++) - if (s[i] == 0xc2 && s[i + 1] == 0xb7) - return 0; - return -1; -} - -static int min_pow(uint32_t type_len) { - for (int pow = KVSPACE_XH_POW_SCALAR; pow <= 31; pow++) - if ((uint64_t)type_len + KVSPACE_XH_PREFIX <= (1ULL << pow)) - return pow; - return -1; -} - -static const struct { - const char *suf; - uint8_t width; -} k_slack[] = { - {"char/utf8", 0}, - {"uint8", 1}, - {"char/utf32", 4}, - {"char/ascii", 1}, -}; - -static int slack_count(size_t k, const uint8_t *data, uint64_t len, uint64_t *count) { - uint8_t width = k_slack[k].width; - if (width) { - if (len % width) - return -1; - if (k == KVSPACE_XH_ASCII - KVSPACE_XH_UTF8) { - for (uint64_t i = 0; i < len; i++) - if (data[i] > 0x7f) - return -1; - } else if (k == KVSPACE_XH_UTF32 - KVSPACE_XH_UTF8) { - for (uint64_t i = 0; i < len; i += 4) { - uint32_t cp = (uint32_t)data[i] | (uint32_t)data[i + 1] << 8 | - (uint32_t)data[i + 2] << 16 | (uint32_t)data[i + 3] << 24; - if (cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff)) - return -1; - } - } - *count = len / width; - return 0; - } - *count = 0; - for (uint64_t i = 0; i < len; (*count)++) { - uint8_t c = data[i++]; - if (c < 0x80) - continue; - unsigned tail = c < 0xe0 ? 1 : c < 0xf0 ? 2 : 3; - if (c < 0xc2 || c > 0xf4 || len - i < tail) - return -1; - uint8_t next = data[i]; - if ((c == 0xe0 && next < 0xa0) || (c == 0xed && next > 0x9f) || - (c == 0xf0 && next < 0x90) || (c == 0xf4 && next > 0x8f)) - return -1; - for (unsigned j = 0; j < tail; j++) - if ((data[i++] & 0xc0) != 0x80) - return -1; - } - return 0; -} - -static int parse_u64(const uint8_t *s, uint32_t n, uint32_t *i, uint64_t *v) { - if (*i >= n || s[*i] < '0' || s[*i] > '9') + *out = NULL; + if (storetype > KVSPACE_XVALUE_ST_EXT || ptr > KVSPACE_XVALUE_PTR_EXT) return -1; - if (s[*i] == '0') { - *v = 0; - (*i)++; - if (*i < n && s[*i] >= '0' && s[*i] <= '9') - return -1; - return 0; - } - uint64_t x = 0; - while (*i < n && s[*i] >= '0' && s[*i] <= '9') { - uint64_t d = (uint64_t)(s[*i] - '0'); - if (x > (UINT64_MAX - d) / 10) - return -1; - x = x * 10 + d; - (*i)++; - } - *v = x; - return 0; -} - -static int alloc_value(uint32_t headlen, uint64_t body_len, uint8_t **out, uint64_t *out_len) { - if (body_len > UINT64_MAX - headlen) + if (len > UINT48_MAX) return -1; - uint64_t n = (uint64_t)headlen + body_len; + uint64_t n = (uint64_t)KVSPACE_XVALUE_HEAD_LEN + len; if (n > SIZE_MAX) return -1; uint8_t *p = (uint8_t *)calloc(1, (size_t)n); if (!p) return -1; + kvspaceXvalueHead h = {.storetype = storetype, .ptr = ptr}; + kvspaceXvalueHeadSetLen(&h, len); + memcpy(p, &h, sizeof h); + if (len) + memcpy(p + KVSPACE_XVALUE_HEAD_LEN, body, len); *out = p; *out_len = n; return 0; } -static int emit(uint8_t pow, uint8_t kind, uint64_t a, uint64_t b, - const char *lang, uint32_t lang_len, - const uint8_t *body, uint64_t copy_len, uint64_t store_len, - uint8_t **out, uint64_t *out_len) { - uint32_t headlen = 0; - if (headlen_of(pow, &headlen) != 0 || lang_len > headlen - KVSPACE_XH_PREFIX || - copy_len > store_len) - return -1; - if (alloc_value(headlen, store_len, out, out_len) != 0) - return -1; - uint8_t *p = *out; - p[0] = pow; - p[1] = kind; - put_u64(p + 2, a); - put_u64(p + 10, b); - if (lang_len) - memcpy(p + KVSPACE_XH_PREFIX, lang, lang_len); - if (copy_len) - memcpy(p + headlen, body, (size_t)copy_len); - return 0; +int kvspaceXvalueHeadNewNone(uint8_t **out, uint64_t *out_len) { + return build(KVSPACE_XVALUE_ST_SHORT, KVSPACE_XVALUE_PTR_INLINE, NULL, 0, + out, out_len); } -int kvspaceXhNewNone(uint8_t **out, uint64_t *out_len) { - if (!out || !out_len) +int kvspaceXvalueHeadNewScalar(uint8_t storetype, const uint8_t *raw, + uint32_t raw_len, uint8_t **out, uint64_t *out_len) { + if (raw_len && !raw) return -1; - *out = NULL; - return emit(KVSPACE_XH_POW_SCALAR, KVSPACE_XH_FIXED_SMALL, 0, 0, NULL, 0, NULL, 0, 0, - out, out_len); + return build(storetype, KVSPACE_XVALUE_PTR_INLINE, raw, raw_len, out, out_len); } -int kvspaceXhNewScalar(const char *langtype, const uint8_t *raw, uint32_t raw_len, - uint8_t **out, uint64_t *out_len) { - if (!out || !out_len) - return -1; - *out = NULL; - if (!langtype || !raw) +int kvspaceXvalueHeadNewSlack(const uint8_t *data, uint64_t data_len, + uint64_t cap, uint8_t **out, uint64_t *out_len) { + if (cap < data_len || data_len > UINT48_MAX || (data_len && !data)) return -1; - uint32_t lt = (uint32_t)strlen(langtype); - int width = scalar_width((const uint8_t *)langtype, lt); - if (width < 0 || raw_len != (uint32_t)width || - (strcmp(langtype, "bool") == 0 && raw[0] > 1)) - return -1; - return emit(KVSPACE_XH_POW_SCALAR, KVSPACE_XH_FIXED_SMALL, 0, 0, langtype, lt, raw, - width, width, out, out_len); -} - -static int decode_emitted(uint8_t **out, uint64_t *out_len) { - kvspaceXh h; - if (kvspaceXhDecode(*out, *out_len, &h) == 0) - return 0; - free(*out); - *out = NULL; - *out_len = 0; - return -1; + (void)cap; + return build(KVSPACE_XVALUE_ST_SLACK, KVSPACE_XVALUE_PTR_INLINE, data, + data_len, out, out_len); } -int kvspaceXhNewShort(const char *langtype, const uint8_t *body, uint32_t body_len, - uint8_t **out, uint64_t *out_len) { - if (!out || !out_len) - return -1; - *out = NULL; - if (!langtype || (body_len && !body)) +int kvspaceXvalueHeadNewTensor(const uint64_t *dims, uint32_t ndim, + const uint8_t *raw, uint64_t raw_len, + uint8_t **out, uint64_t *out_len) { + if (!dims || ndim == 0 || ndim > 64 || raw_len > UINT48_MAX || + (raw_len && !raw)) return -1; - size_t lt = strlen(langtype); - if (lt > UINT32_MAX || short_width((const uint8_t *)langtype, (uint32_t)lt) != - (int)body_len) - return -1; - int pow = min_pow((uint32_t)lt); - if (pow < 0 || emit((uint8_t)pow, KVSPACE_XH_FIXED_SMALL, 0, 0, - langtype, (uint32_t)lt, body, body_len, body_len, - out, out_len) != 0) - return -1; - return decode_emitted(out, out_len); + (void)dims; + (void)ndim; + /* 形状在 attr key langtype;body 字节数 = raw_len(明文层校验 numel×width) */ + return build(KVSPACE_XVALUE_ST_LARGE, KVSPACE_XVALUE_PTR_INLINE, raw, + raw_len, out, out_len); } -int kvspaceXhNewSlack(int elem, const uint8_t *data, uint64_t data_len, uint64_t cap, - uint8_t **out, uint64_t *out_len) { - if (!out || !out_len) - return -1; - *out = NULL; - if (elem < KVSPACE_XH_UTF8 || elem > KVSPACE_XH_ASCII || cap < data_len || - cap > SIZE_MAX - (1u << KVSPACE_XH_POW_SLACK) || (data_len && !data)) - return -1; - size_t k = (size_t)(elem - KVSPACE_XH_UTF8); - uint64_t count = 0; - if (slack_count(k, data, data_len, &count) != 0) - return -1; - char text[48]; - int kn = snprintf(text, sizeof text, "[%" PRIu64 "]%s", count, k_slack[k].suf); - if (kn < 0 || (size_t)kn >= sizeof text) +int kvspaceXvalueHeadNewPtr(const uint8_t *path, uint32_t path_len, + uint8_t **out, uint64_t *out_len) { + if (!path_len || !path) return -1; - return emit(KVSPACE_XH_POW_SLACK, KVSPACE_XH_SLACK, data_len, cap, text, (uint32_t)kn, - data, data_len, cap, out, out_len); + return build(KVSPACE_XVALUE_ST_SLACK, KVSPACE_XVALUE_PTR_PTR, path, path_len, + out, out_len); } -int kvspaceXhNewTensor(const uint64_t *dims, uint32_t ndim, const char *elem, - const uint8_t *raw, uint64_t raw_len, - uint8_t **out, uint64_t *out_len) { - if (!out || !out_len) - return -1; - *out = NULL; - if (!dims || ndim == 0 || ndim > 64 || !elem) - return -1; - uint32_t el = (uint32_t)strlen(elem); - int width = scalar_width((const uint8_t *)elem, el); - if (width < 0) +int kvspaceXvalueHeadNewExt(const uint8_t *locator, uint32_t locator_len, + uint8_t **out, uint64_t *out_len) { + if (!locator_len || !locator) return -1; - uint64_t numel = 1; - for (uint32_t i = 0; i < ndim; i++) { - if (dims[i] != 0 && numel > UINT64_MAX / dims[i]) - return -1; - numel *= dims[i]; - } - if (numel > UINT64_MAX / (uint64_t)width) - return -1; - uint64_t bytes = numel * (uint64_t)width; - if (raw_len != bytes || (bytes > 0 && !raw)) - return -1; - char text[111]; - size_t o = 0; - text[o++] = '['; - for (uint32_t i = 0; i < ndim; i++) { - if (i && o < sizeof text) - text[o++] = ','; - int kn = snprintf(text + o, sizeof text - o, "%" PRIu64, dims[i]); - if (kn < 0 || (size_t)kn >= sizeof text - o) - return -1; - o += (size_t)kn; - } - if (o + 1 + el >= sizeof text) - return -1; - text[o++] = ']'; - memcpy(text + o, elem, el); - o += el; - return emit(KVSPACE_XH_POW_TENSOR, KVSPACE_XH_FIXED_LARGE, numel, (uint64_t)width, text, - (uint32_t)o, raw, bytes, bytes, out, out_len); + return build(KVSPACE_XVALUE_ST_EXT, KVSPACE_XVALUE_PTR_EXT, locator, + locator_len, out, out_len); } -static int new_locator(uint8_t kind, const char *langtype, const char *locator, - uint64_t cap, uint8_t **out, uint64_t *out_len) { - if (!out || !out_len) - return -1; - *out = NULL; - if (!langtype || !locator) - return -1; - /* `any` 不是类型名:Ptr/Ext 的 target kindexpr 不得为 any(空 = 不约束)。 */ - if (strcmp(langtype, "any") == 0) +int kvspaceXvalueHeadNewCode(const uint8_t *body, uint64_t body_len, + uint64_t cap, uint8_t **out, uint64_t *out_len) { + if (body_len > cap || body_len > UINT48_MAX || (body_len && !body)) return -1; - size_t lt = strlen(langtype); - size_t locator_len = strlen(locator); - if (lt > UINT32_MAX || cap < locator_len) - return -1; - int pow = min_pow((uint32_t)lt); - if (pow < 0 || emit((uint8_t)pow, kind, - locator_len, cap, langtype, (uint32_t)lt, - (const uint8_t *)locator, locator_len, cap, - out, out_len) != 0) - return -1; - return decode_emitted(out, out_len); -} - -int kvspaceXhNewPtr(const char *langtype, const char *path, uint64_t cap, - uint8_t **out, uint64_t *out_len) { - return new_locator(KVSPACE_XH_SLACK | KVSPACE_XH_PTR_FLAG, - langtype, path, cap, out, out_len); -} - -int kvspaceXhNewExt(const char *langtype, const char *locator, uint64_t cap, - uint8_t **out, uint64_t *out_len) { - return new_locator(KVSPACE_XH_EXT, langtype, locator, cap, out, out_len); -} - -int kvspaceXhNewCode(const char *langtype, const uint8_t *body, uint64_t body_len, - uint64_t cap, uint8_t **out, uint64_t *out_len) { - if (!out || !out_len) - return -1; - *out = NULL; - if (!langtype || (body_len && !body) || cap < body_len) - return -1; - size_t lt = strlen(langtype); - if (lt > UINT32_MAX || - (!type_eq((const uint8_t *)langtype, (uint32_t)lt, "rwir") && - !type_eq((const uint8_t *)langtype, (uint32_t)lt, "def langtype") && - !type_eq((const uint8_t *)langtype, (uint32_t)lt, "rwfunc"))) - return -1; - if (emit(KVSPACE_XH_POW_SCALAR, KVSPACE_XH_SLACK, body_len, cap, - langtype, (uint32_t)lt, body, body_len, cap, - out, out_len) != 0) - return -1; - return decode_emitted(out, out_len); -} - -static int parse_slack(const uint8_t *s, uint32_t n, const uint8_t *body, uint64_t len) { - if (n < 3 || s[0] != '[') - return -1; - uint32_t i = 1; - uint64_t logical = 0; - if (parse_u64(s, n, &i, &logical) != 0 || i >= n || s[i] != ']') - return -1; - i++; - uint32_t sl = n - i; - for (size_t k = 0; k < sizeof k_slack / sizeof k_slack[0]; k++) { - size_t L = strlen(k_slack[k].suf); - if (L != sl || memcmp(s + i, k_slack[k].suf, L) != 0) - continue; - uint64_t count = 0; - return slack_count(k, body, len, &count) == 0 && count == logical ? 0 : -1; - } - return -1; -} - -static int parse_tensor(const uint8_t *s, uint32_t n, uint64_t *numel, int *width) { - if (n < 4 || s[0] != '[') - return -1; - uint32_t i = 1; - uint64_t prod = 1; - int any = 0; - while (i < n && s[i] != ']') { - uint64_t d = 0; - if (parse_u64(s, n, &i, &d) != 0) - return -1; - if (d != 0 && prod > UINT64_MAX / d) - return -1; - prod *= d; - any = 1; - if (i >= n) - return -1; - if (s[i] == ',') { - i++; - if (i >= n || s[i] == ']') - return -1; - continue; - } - if (s[i] != ']') - return -1; - } - if (!any || i >= n || s[i] != ']') - return -1; - i++; - int w = scalar_width(s + i, n - i); - if (w < 0) - return -1; - *numel = prod; - *width = w; - return 0; + (void)cap; + return build(KVSPACE_XVALUE_ST_SLACK, KVSPACE_XVALUE_PTR_INLINE, body, + body_len, out, out_len); } -static int slack_langtype(const uint8_t *s, uint32_t n) { - if (n < 3 || s[0] != '[') +int kvspaceXvalueHeadReserve(uint8_t storetype, uint64_t body_len, + uint64_t cap, uint8_t **out, uint64_t *out_len) { + if (body_len > UINT48_MAX || cap < body_len) return -1; - uint32_t i = 1; - uint64_t count = 0; - if (parse_u64(s, n, &i, &count) != 0 || i >= n || s[i++] != ']') + /* 固定头无容量字段:预留 = 产 body_len 字节零 body 的占位(后由调用方填充)。 */ + (void)cap; + if (body_len > SIZE_MAX - KVSPACE_XVALUE_HEAD_LEN) return -1; - for (size_t k = 0; k < sizeof k_slack / sizeof k_slack[0]; k++) { - size_t len = strlen(k_slack[k].suf); - if (n - i == len && memcmp(s + i, k_slack[k].suf, len) == 0) - return 0; - } - return -1; -} - -int kvspaceXhReserve(uint8_t kind, const char *langtype, uint64_t body_len, - uint64_t cap, uint8_t **out, uint64_t *out_len) { - if (!out || !out_len) - return -1; - *out = NULL; - *out_len = 0; - if (!langtype) - return -1; - size_t lt = strlen(langtype); - if (lt > UINT32_MAX) - return -1; - const uint8_t *type = (const uint8_t *)langtype; - uint64_t type_chars = 0; - if (slack_count(0, type, (uint64_t)lt, &type_chars) != 0) - return -1; - uint64_t a = 0, b = 0, reserve = cap; - int pow = -1; - if (kind == KVSPACE_XH_FIXED_SMALL) { - int width = short_width(type, (uint32_t)lt); - if (width < 0 || body_len != (uint64_t)width || cap != body_len) - return -1; - pow = min_pow((uint32_t)lt); - } else if (kind == KVSPACE_XH_SLACK) { - int code = type_eq(type, (uint32_t)lt, "rwir") || - type_eq(type, (uint32_t)lt, "def langtype") || - type_eq(type, (uint32_t)lt, "rwfunc"); - if ((!code && slack_langtype(type, (uint32_t)lt) != 0) || - (code && ((type_eq(type, (uint32_t)lt, "rwir") && body_len < 5) || - (type_eq(type, (uint32_t)lt, "rwfunc") && body_len < 5)))) - return -1; - pow = code ? KVSPACE_XH_POW_SCALAR : KVSPACE_XH_POW_SLACK; - a = body_len; - b = cap; - } else if (kind == KVSPACE_XH_FIXED_LARGE) { - int width = 0; - if (parse_tensor(type, (uint32_t)lt, &a, &width) != 0 || - a > UINT64_MAX / (uint64_t)width || - body_len != a * (uint64_t)width || cap != body_len) - return -1; - b = (uint64_t)width; - pow = KVSPACE_XH_POW_TENSOR; - reserve = body_len; - } else if (kind == KVSPACE_XH_EXT || - kind == (KVSPACE_XH_SLACK | KVSPACE_XH_PTR_FLAG)) { - if (!body_len) - return -1; - pow = min_pow((uint32_t)lt); - a = body_len; - b = cap; - } else { - return -1; - } - if (pow < 0 || body_len > cap) - return -1; - return emit((uint8_t)pow, kind, a, b, langtype, (uint32_t)lt, - NULL, 0, reserve, out, out_len); -} - -int kvspaceXhDecode(const uint8_t *data, uint64_t len, kvspaceXh *out) { - if (!out) - return -1; - memset(out, 0, sizeof *out); - if (!data || len < KVSPACE_XH_PREFIX) - return -1; - uint8_t pow = data[0]; - uint32_t headlen = 0; - if (headlen_of(pow, &headlen) != 0 || len < headlen) - return -1; - uint8_t kind = data[1]; - if (kind & ~(KVSPACE_XH_PTR_FLAG | 3u)) - return -1; - uint64_t a = get_u64(data + 2); - uint64_t b = get_u64(data + 10); - const uint8_t *lt = data + KVSPACE_XH_PREFIX; - uint32_t region = headlen - KVSPACE_XH_PREFIX; - uint32_t ltlen = 0; - while (ltlen < region && lt[ltlen] != 0) - ltlen++; - uint64_t type_chars = 0; - if (slack_count(0, lt, ltlen, &type_chars) != 0) - return -1; - - uint64_t content = 0; - uint64_t cap = 0; - if (kind == KVSPACE_XH_FIXED_SMALL) { - if (pow != min_pow(ltlen) || a != 0 || b != 0) - return -1; - int width = short_width(lt, ltlen); - if (width < 0) - return -1; - content = (uint64_t)width; - cap = content; - } else if (kind == KVSPACE_XH_SLACK) { - int code = type_eq(lt, ltlen, "rwir") || - type_eq(lt, ltlen, "def langtype") || - type_eq(lt, ltlen, "rwfunc"); - if (pow != (code ? KVSPACE_XH_POW_SCALAR : KVSPACE_XH_POW_SLACK) || - a > b) - return -1; - content = a; - cap = b; - } else if (kind == KVSPACE_XH_EXT || - kind == (KVSPACE_XH_SLACK | KVSPACE_XH_PTR_FLAG)) { - if (pow != min_pow(ltlen) || a == 0 || a > b) - return -1; - content = a; - cap = b; - } else if (kind == KVSPACE_XH_FIXED_LARGE) { - if (pow != KVSPACE_XH_POW_TENSOR) - return -1; - uint64_t numel = 0; - int width = 0; - if (parse_tensor(lt, ltlen, &numel, &width) != 0 || numel != a || - (uint64_t)width != b) - return -1; - if (b != 0 && a > UINT64_MAX / b) - return -1; - content = a * b; - cap = content; - } else { - return -1; - } - if (cap > len - headlen) + uint64_t n = (uint64_t)KVSPACE_XVALUE_HEAD_LEN + body_len; + if (n > SIZE_MAX) return -1; - if (kind == KVSPACE_XH_FIXED_SMALL && type_eq(lt, ltlen, "bool") && - data[headlen] > 1) + uint8_t *p = (uint8_t *)calloc(1, (size_t)n); + if (!p) return -1; - if (kind == KVSPACE_XH_SLACK) { - if (type_eq(lt, ltlen, "rwir") || type_eq(lt, ltlen, "def langtype") || - type_eq(lt, ltlen, "rwfunc")) { - if (pow != KVSPACE_XH_POW_SCALAR || - (type_eq(lt, ltlen, "rwir") && content < 5) || - (type_eq(lt, ltlen, "rwfunc") && content < 5)) - return -1; - } else if (parse_slack(lt, ltlen, data + headlen, content) != 0) { - return -1; - } - } else if (kind == KVSPACE_XH_EXT || - kind == (KVSPACE_XH_SLACK | KVSPACE_XH_PTR_FLAG)) { - if (memchr(data + headlen, 0, (size_t)content)) - return -1; - uint64_t path_chars = 0; - if (slack_count(0, data + headlen, content, &path_chars) != 0) - return -1; - } - - out->pow = pow; - out->kind = kind; - out->a = a; - out->b = b; - out->langtype = lt; - out->langtype_len = ltlen; - out->body = data + headlen; - out->content_len = content; - out->body_cap = cap; - out->headlen = headlen; - out->total = (uint64_t)headlen + cap; + kvspaceXvalueHead h = {.storetype = storetype, .ptr = KVSPACE_XVALUE_PTR_INLINE}; + kvspaceXvalueHeadSetLen(&h, body_len); + memcpy(p, &h, sizeof h); + *out = p; + *out_len = n; return 0; } diff --git a/src/xvalue_meta.c b/src/xvalue_meta.c deleted file mode 100644 index 3d56a9a..0000000 --- a/src/xvalue_meta.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "xvalue_meta.h" -#include - -#include -#include - -int kvspaceMetaKey(const char *key, char **out) { - static const char prefix[] = "/.kvspace-meta/"; - static const char hex[] = "0123456789abcdef"; - if (!out) - return -1; - *out = NULL; - if (!key || key[0] != '/' || - (strncmp(key, prefix, sizeof prefix - 2) == 0 && - (key[sizeof prefix - 2] == '/' || key[sizeof prefix - 2] == 0))) - return -1; - size_t len = strlen(key); - if (len > (SIZE_MAX - sizeof prefix) / 2) - return -1; - char *p = malloc(sizeof prefix + len * 2); - if (!p) - return -1; - memcpy(p, prefix, sizeof prefix - 1); - for (size_t i = 0; i < len; i++) { - unsigned char b = (unsigned char)key[i]; - p[sizeof prefix - 1 + 2 * i] = hex[b >> 4]; - p[sizeof prefix + 2 * i] = hex[b & 15]; - } - p[sizeof prefix - 1 + 2 * len] = 0; - *out = p; - return 0; -} - -int kvspaceMetaEncode(uint8_t ro, uint32_t vid, uint8_t **out, uint64_t *out_len) { - if (!out || !out_len) - return -1; - *out = NULL; - *out_len = 0; - if (ro > 1) - return -1; - uint8_t body[5] = {ro, (uint8_t)vid, (uint8_t)(vid >> 8), - (uint8_t)(vid >> 16), (uint8_t)(vid >> 24)}; - return kvspaceXhNewSlack(KVSPACE_XH_BYTE, body, sizeof body, sizeof body, - out, out_len); -} - -int kvspaceMetaDecode(const uint8_t *data, uint64_t len, uint8_t *ro, uint32_t *vid) { - if (!ro || !vid) - return -1; - kvspaceXh h; - if (kvspaceXhDecode(data, len, &h) != 0 || h.total != len || - h.kind != KVSPACE_XH_SLACK || h.a != 5 || h.b != 5 || - h.langtype_len != 8 || memcmp(h.langtype, "[5]uint8", 8) != 0 || - h.body[0] > 1) - return -1; - *ro = h.body[0]; - *vid = (uint32_t)h.body[1] | (uint32_t)h.body[2] << 8 | - (uint32_t)h.body[3] << 16 | (uint32_t)h.body[4] << 24; - return 0; -} diff --git a/src/xvalue_meta.h b/src/xvalue_meta.h deleted file mode 100644 index ccb23b7..0000000 --- a/src/xvalue_meta.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef XVALUE_META_H -#define XVALUE_META_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -int kvspaceMetaKey(const char *key, char **out); -int kvspaceMetaEncode(uint8_t ro, uint32_t vid, uint8_t **out, uint64_t *out_len); -int kvspaceMetaDecode(const uint8_t *data, uint64_t len, uint8_t *ro, uint32_t *vid); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/tests/test_codec_abi.c b/tests/test_codec_abi.c index 7c127eb..c68e186 100644 --- a/tests/test_codec_abi.c +++ b/tests/test_codec_abi.c @@ -8,74 +8,77 @@ #define CHECK(x) do { if (!(x)) { fprintf(stderr, "failed: %s\n", #x); return 1; } } while (0) +/* 固定 head 8 字节 [len:u48 LE@0-5][storetype@6][ptr@7],body 偏移恒 8; + langtype 不在 head(x‥langtype attr key)。构造直接 memcpy struct + body—— + New* 产物用 kvspaceXvalueHead 字段断言。 */ + int main(void) { uint8_t *value = NULL; uint32_t len = 0; - kvspaceHead_t head; - CHECK(kvspaceXvalueEncode("None", NULL, 0, NULL, 0, &value, &len) == 0); - CHECK(len == 32 && kvspaceDecodeHead(value, len, &head) == 0 && - head.langtype_len == 0 && head.body_len == 0); - free(value); + kvspaceXvalueHead h; CHECK(kvspaceNewInt64(42, &value, &len) == 0); - CHECK(len == 40 && value[0] == 5 && value[1] == 0); - CHECK(kvspaceDecodeHead(value, len, &head) == 0 && - strcmp(head.langtype, "int64") == 0 && head.body_len == 8); + CHECK(len == 16); + memcpy(&h, value, sizeof h); + CHECK(h.storetype == KVSPACE_XVALUE_ST_SHORT && h.ptr == KVSPACE_XVALUE_PTR_INLINE && + kvspaceXvalueHeadLen(&h) == 8); + uint64_t v = 0; + for (int i = 7; i >= 0; i--) + v = (v << 8) | value[KVSPACE_XVALUE_HEAD_LEN + i]; + CHECK(v == 42); + free(value); + + CHECK(kvspaceNewBool(1, &value, &len) == 0); + CHECK(len == 9); + memcpy(&h, value, sizeof h); + CHECK(h.storetype == KVSPACE_XVALUE_ST_SHORT && + kvspaceXvalueHeadLen(&h) == 1 && value[KVSPACE_XVALUE_HEAD_LEN] == 1); + free(value); + CHECK(kvspaceNewBool(0, &value, &len) == 0); + CHECK(value[KVSPACE_XVALUE_HEAD_LEN] == 0); + free(value); + + CHECK(kvspaceNewFloat64(2.5, &value, &len) == 0); + CHECK(len == 16); + memcpy(&h, value, sizeof h); + CHECK(h.storetype == KVSPACE_XVALUE_ST_SHORT && + kvspaceXvalueHeadLen(&h) == 8); + uint64_t bits = 0; + for (int i = 7; i >= 0; i--) + bits = (bits << 8) | value[KVSPACE_XVALUE_HEAD_LEN + i]; + double d; + memcpy(&d, &bits, sizeof d); + CHECK(d == 2.5); free(value); const uint8_t utf8[] = {0xe4, 0xbd, 0xa0, 0xf0, 0x9f, 0x98, 0x80}; CHECK(kvspaceNewChar(utf8, sizeof utf8, &value, &len) == 0); - CHECK(kvspaceDecodeHead(value, len, &head) == 0 && - strcmp(head.langtype, "[2]char/utf8") == 0 && - head.body_len == (int32_t)sizeof utf8 && head.body_cap == sizeof utf8 && - head.body_offset == 64); + CHECK(len == 8 + sizeof utf8); + memcpy(&h, value, sizeof h); + CHECK(h.storetype == KVSPACE_XVALUE_ST_SLACK && + h.ptr == KVSPACE_XVALUE_PTR_INLINE && + kvspaceXvalueHeadLen(&h) == sizeof utf8); + CHECK(memcmp(value + KVSPACE_XVALUE_HEAD_LEN, utf8, sizeof utf8) == 0); free(value); - - const int32_t dims[2] = {2, 3}; - uint8_t raw[24] = {0}; - CHECK(kvspaceXvalueEncode("float32", raw, sizeof raw - 1, dims, 2, - &value, &len) != 0); - CHECK(kvspaceXvalueEncode("float32", raw, sizeof raw, dims, 2, - &value, &len) == 0); - CHECK(kvspaceDecodeHead(value, len, &head) == 0 && - head.ndim == 2 && head.dims[0] == 2 && head.dims[1] == 3 && - head.body_offset == 128); + CHECK(kvspaceNewChar(NULL, 0, &value, &len) == 0); + CHECK(len == 8); free(value); CHECK(kvspaceNewPtr("int64", "/x", &value, &len) == 0); - CHECK(kvspaceDecodeHead(value, len, &head) == 0 && - head.ref == KVSPACE_REF_PTR && head.body_len == 2); + CHECK(len == 10); + memcpy(&h, value, sizeof h); + CHECK(h.ptr == KVSPACE_XVALUE_PTR_PTR && h.storetype == KVSPACE_XVALUE_ST_SLACK && + kvspaceXvalueHeadLen(&h) == 2); + CHECK(memcmp(value + KVSPACE_XVALUE_HEAD_LEN, "/x", 2) == 0); free(value); /* 空 kindexpr = 不约束:Ptr 目标允许为空;`any` 不是类型名,创建即拒。 */ CHECK(kvspaceNewPtr("", "/x", &value, &len) == 0); - CHECK(kvspaceDecodeHead(value, len, &head) == 0 && - head.ref == KVSPACE_REF_PTR && head.langtype_len == 0 && - head.body_len == 2); + CHECK(len == 10); + memcpy(&h, value, sizeof h); + CHECK(h.ptr == KVSPACE_XVALUE_PTR_PTR && kvspaceXvalueHeadLen(&h) == 2); free(value); CHECK(kvspaceNewPtr("any", "/x", &value, &len) != 0); - CHECK(kvspaceXvalueEncodeMode("int64", (const uint8_t *)"s3://", 5, - NULL, 0, KVSPACE_REF_EXT, 0, 0, - &value, &len) == 0); - CHECK(kvspaceDecodeHead(value, len, &head) == 0 && - head.ref == KVSPACE_REF_EXT && head.storetype == KVSPACE_XH_EXT); - free(value); - CHECK(kvspaceXvalueEncodeMode("int64", raw, 8, NULL, 0, 0, 1, 0, - &value, &len) != 0); - CHECK(kvspaceXvalueEncode("rwfunc", NULL, 0, NULL, 0, &value, &len) == 0); - CHECK(kvspaceDecodeHead(value, len, &head) == 0 && - head.storetype == KVSPACE_XH_FIXED_SMALL && head.body_len == 0); - free(value); - uint8_t anchor[5] = {0}; - CHECK(kvspaceXvalueEncode("rwfunc", anchor, 5, NULL, 0, &value, &len) == 0); - CHECK(kvspaceDecodeHead(value, len, &head) == 0 && - head.storetype == KVSPACE_XH_SLACK && head.body_len == 5); - free(value); - CHECK(kvspaceXvalueEncode("[int64]·[]char/utf32", NULL, 0, NULL, 0, - &value, &len) == 0); - CHECK(kvspaceDecodeHead(value, len, &head) == 0 && - head.storetype == KVSPACE_XH_FIXED_SMALL && head.ndim == 0); - free(value); puts("ok"); return 0; } diff --git a/tests/test_xvalue_head.c b/tests/test_xvalue_head.c index bdb32a2..5609876 100644 --- a/tests/test_xvalue_head.c +++ b/tests/test_xvalue_head.c @@ -1,10 +1,12 @@ #include -#include "xvalue_meta.h" #include #include #include +/* 固定 head 8 字节 [len:u48 LE@0-5][storetype@6][ptr@7],body 偏移恒 8。 + 类型不在 head(x‥langtype attr key)——存储层只验固定头/body。 */ + static int failures = 0; #define CHECK(cond, ...) \ do { \ @@ -19,173 +21,50 @@ static int failures = 0; static void test_none(void) { uint8_t *buf = NULL; uint64_t n = 0; - CHECK(kvspaceXhNewNone(&buf, &n) == 0, "none encode"); - CHECK(n == 32, "none len %llu", (unsigned long long)n); - CHECK(buf[0] == 5, "pow"); - int rest = 0; - for (int i = 1; i < 32; i++) - rest |= buf[i]; - CHECK(rest == 0, "none head tail"); - kvspaceXh h; - CHECK(kvspaceXhDecode(buf, n, &h) == 0, "none decode"); - CHECK(h.kind == KVSPACE_XH_FIXED_SMALL && h.langtype_len == 0, "none kind"); - CHECK(h.a == 0 && h.b == 0 && h.content_len == 0 && h.total == 32, "none fields"); - uint8_t zeros[32]; - memset(zeros, 0, sizeof zeros); - CHECK(kvspaceXhDecode(zeros, 32, &h) != 0, "pow 0"); - CHECK(h.langtype_len == 0 && h.total == 0, "fail clears"); + CHECK(kvspaceXvalueHeadNewNone(&buf, &n) == 0, "none new"); + CHECK(n == 8, "none len %llu", (unsigned long long)n); + kvspaceXvalueHead h; + memcpy(&h, buf, sizeof h); + CHECK(h.storetype == KVSPACE_XVALUE_ST_SHORT && + h.ptr == KVSPACE_XVALUE_PTR_INLINE && kvspaceXvalueHeadLen(&h) == 0, "none head"); + /* wire 布局:len:u48@0-5、storetype@6、ptr@7 */ + CHECK(buf[0] == 0 && buf[5] == 0 && + buf[6] == KVSPACE_XVALUE_ST_SHORT && buf[7] == KVSPACE_XVALUE_PTR_INLINE, + "none wire layout"); free(buf); } -static void test_int64(void) { +static void test_scalar(void) { uint8_t raw[8] = {0x0a, 0, 0, 0, 0, 0, 0, 0}; uint8_t *buf = NULL; uint64_t n = 0; - CHECK(kvspaceXhNewScalar("int64", raw, 8, &buf, &n) == 0, "i64"); - CHECK(n == 40, "i64 len %llu", (unsigned long long)n); - CHECK(buf[0] == 5 && buf[1] == 0, "i64 prefix"); - CHECK(memcmp(buf + 18, "int64", 5) == 0, "i64 lang"); - CHECK(buf[23] == 0, "i64 nul"); - CHECK(memcmp(buf + 32, raw, 8) == 0, "i64 body"); - kvspaceXh h; - uint8_t padded[64] = {0}; - memcpy(padded, buf, (size_t)n); - CHECK(kvspaceXhDecode(padded, sizeof padded, &h) == 0, "read 64"); - CHECK(h.total == 40 && h.content_len == 8 && h.body[0] == 0x0a, "ignore overrun"); - CHECK(kvspaceXhDecode(buf, 39, &h) != 0, "short body"); - free(buf); - - uint8_t b = 1; - CHECK(kvspaceXhNewScalar("bool", &b, 1, &buf, &n) == 0, "bool"); - CHECK(n == 33 && buf[32] == 1, "bool body"); - buf[32] = 2; - CHECK(kvspaceXhDecode(buf, n, &h) != 0, "invalid bool decode"); + CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, + &buf, &n) == 0, "i64 new"); + CHECK(n == 16, "i64 len %llu", (unsigned long long)n); + kvspaceXvalueHead h; + memcpy(&h, buf, sizeof h); + CHECK(kvspaceXvalueHeadLen(&h) == 8 && h.storetype == KVSPACE_XVALUE_ST_SHORT, "i64 head"); + CHECK(memcmp(buf + KVSPACE_XVALUE_HEAD_LEN, raw, 8) == 0, "i64 body@8"); free(buf); - b = 2; - CHECK(kvspaceXhNewScalar("bool", &b, 1, &buf, &n) != 0, "invalid bool encode"); - CHECK(kvspaceXhNewScalar("int64", raw, 4, &buf, &n) != 0, "bad width"); - CHECK(buf == NULL, "no buf"); } static void test_slack(void) { const uint8_t hi[] = {'h', 'i'}; uint8_t *buf = NULL; uint64_t n = 0; - kvspaceXh h; - uint8_t cps[8] = {0x41, 0, 0, 0, 0x16, 0x4e, 0, 0}; - CHECK(kvspaceXhNewSlack(KVSPACE_XH_UTF32, cps, sizeof cps, 8, &buf, &n) == 0, "utf32"); - CHECK(kvspaceXhDecode(buf, n, &h) == 0, "utf32 dec"); - CHECK(h.a == 8 && h.langtype_len == 13 && memcmp(h.langtype, "[2]char/utf32", 13) == 0, - "utf32 lt"); - free(buf); - - CHECK(kvspaceXhNewSlack(KVSPACE_XH_UTF32, cps, 3, 8, &buf, &n) != 0, - "partial utf32"); - CHECK(buf == NULL, "partial utf32 output"); - uint8_t surrogate[4] = {0, 0xd8, 0, 0}; - CHECK(kvspaceXhNewSlack(KVSPACE_XH_UTF32, surrogate, 4, 4, &buf, &n) != 0, - "utf32 surrogate"); - uint8_t too_high[4] = {0, 0, 0x11, 0}; - CHECK(kvspaceXhNewSlack(KVSPACE_XH_UTF32, too_high, 4, 4, &buf, &n) != 0, - "utf32 range"); - - CHECK(kvspaceXhNewSlack(KVSPACE_XH_ASCII, hi, 2, 2, &buf, &n) == 0, "ascii"); - CHECK(kvspaceXhDecode(buf, n, &h) == 0 && h.a == 2, "ascii dec"); - CHECK(memcmp(h.langtype, "[2]char/ascii", h.langtype_len) == 0, "ascii lt"); - free(buf); - uint8_t non_ascii = 0x80; - CHECK(kvspaceXhNewSlack(KVSPACE_XH_ASCII, &non_ascii, 1, 1, &buf, &n) != 0, - "ascii range"); - - CHECK(kvspaceXhNewSlack(KVSPACE_XH_BYTE, hi, 2, 2, &buf, &n) == 0, "byte"); - CHECK(kvspaceXhDecode(buf, n, &h) == 0, "byte dec"); - CHECK(memcmp(h.langtype, "[2]uint8", h.langtype_len) == 0, "byte lt"); - buf[2] = 9; - CHECK(kvspaceXhDecode(buf, n, &h) != 0, "len > cap"); - free(buf); -} - -static void test_utf8(void) { - static const struct { - const char *data; - uint8_t len, count; - } cases[] = { - {"", 0, 0}, - {"hi", 2, 2}, - {"\xe4\xbd\xa0\xe5\xa5\xbd", 6, 2}, - {"\xf0\x9f\x98\x80", 4, 1}, - {"A\xc3\xa9\xe4\xb8\xad\xf0\x9f\x98\x80", 10, 4}, - {"A\0B", 3, 3}, - {"\xc2\x80\xdf\xbf", 4, 2}, - {"\xe0\xa0\x80\xed\x9f\xbf\xee\x80\x80\xef\xbf\xbf", 12, 4}, - {"\xf0\x90\x80\x80\xf4\x8f\xbf\xbf", 8, 2}, - }; - for (size_t i = 0; i < sizeof cases / sizeof cases[0]; i++) { - uint8_t wire[80] = {6, 1}; - wire[2] = cases[i].len; - wire[10] = 16; - memcpy(wire + 18, "[0]char/utf8", 12); - wire[19] += cases[i].count; - memcpy(wire + 64, cases[i].data, cases[i].len); - uint8_t *buf = NULL; - uint64_t n = 0; - CHECK(kvspaceXhNewSlack(KVSPACE_XH_UTF8, (const uint8_t *)cases[i].data, - cases[i].len, 16, &buf, &n) == 0, "utf8 encode %zu", i); - CHECK(buf && n == sizeof wire && memcmp(buf, wire, sizeof wire) == 0, - "utf8 wire %zu", i); - free(buf); - kvspaceXh h; - CHECK(kvspaceXhDecode(wire, sizeof wire, &h) == 0, "utf8 fixture %zu", i); - CHECK(h.content_len == cases[i].len && h.body_cap == 16 && h.total == 80, - "utf8 lengths %zu", i); - wire[19]++; - CHECK(kvspaceXhDecode(wire, sizeof wire, &h) != 0, "utf8 count %zu", i); - CHECK(h.body == NULL && h.total == 0, "utf8 failure clears %zu", i); - wire[19]--; - memset(wire + 64 + cases[i].len, 0xff, 16 - cases[i].len); - CHECK(kvspaceXhDecode(wire, sizeof wire, &h) == 0, "utf8 slack %zu", i); - for (uint64_t cut = 64; cut < sizeof wire; cut++) - CHECK(kvspaceXhDecode(wire, cut, &h) != 0, "utf8 truncation %zu", i); - if (cases[i].len) { - CHECK(kvspaceXhNewSlack(KVSPACE_XH_UTF8, (const uint8_t *)cases[i].data, - cases[i].len, cases[i].len - 1, &buf, &n) != 0, - "utf8 capacity %zu", i); - CHECK(buf == NULL, "utf8 capacity output %zu", i); - } - wire[2] = wire[10] = 255; - CHECK(kvspaceXhDecode(wire, sizeof wire, &h) != 0, "utf8 body bounds %zu", i); - } - uint8_t *buf = NULL; - uint64_t n = 0; - CHECK(kvspaceXhNewSlack(KVSPACE_XH_UTF8, NULL, 0, 0, &buf, &n) == 0, "empty utf8"); - kvspaceXh h; - CHECK(kvspaceXhDecode(buf, n, &h) == 0 && h.content_len == 0, "empty utf8 decode"); + CHECK(kvspaceXvalueHeadNewSlack(hi, 2, 2, &buf, &n) == 0, "slack new"); + CHECK(n == 10, "slack len %llu", (unsigned long long)n); + kvspaceXvalueHead h; + memcpy(&h, buf, sizeof h); + CHECK(h.storetype == KVSPACE_XVALUE_ST_SLACK && + kvspaceXvalueHeadLen(&h) == 2, "slack head"); + /* wire:len u48 LE@0-5=2,storetype@6,ptr@7 */ + CHECK(buf[0] == 2 && buf[1] == 0 && buf[5] == 0 && + buf[6] == KVSPACE_XVALUE_ST_SLACK && buf[7] == KVSPACE_XVALUE_PTR_INLINE, + "slack wire layout"); + CHECK(memcmp(buf + KVSPACE_XVALUE_HEAD_LEN, hi, 2) == 0, "slack body"); free(buf); - CHECK(kvspaceXhNewSlack(KVSPACE_XH_UTF8, NULL, 1, 1, &buf, &n) != 0, "null utf8"); -} - -static void test_invalid_utf8(void) { - static const char *cases[] = { - "\x80", "\xbf", "\xff", "\xc0\xaf", "\xc1\xbf", "\xe0\x80\x80", - "\xed\xa0\x80", "\xf0\x80\x80\x80", "\xf4\x90\x80\x80", "\xf5\x80\x80\x80", - "\xc2", "\xe2\x82", "\xf0\x9f\x98", "\xc2\x41", "\xe2\x28\xa1", "\xf0\x90\x41\x80", - }; - for (size_t i = 0; i < sizeof cases / sizeof cases[0]; i++) { - size_t len = strlen(cases[i]); - uint8_t *buf = NULL; - uint64_t n = 0; - CHECK(kvspaceXhNewSlack(KVSPACE_XH_UTF8, (const uint8_t *)cases[i], len, 16, - &buf, &n) != 0, "invalid utf8 encode %zu", i); - CHECK(buf == NULL, "invalid utf8 output %zu", i); - free(buf); - uint8_t wire[80] = {6, 1}; - wire[2] = (uint8_t)len; - wire[10] = 16; - memcpy(wire + 18, "[1]char/utf8", 12); - memcpy(wire + 64, cases[i], len); - kvspaceXh h; - CHECK(kvspaceXhDecode(wire, sizeof wire, &h) != 0, "invalid utf8 decode %zu", i); - } + CHECK(kvspaceXvalueHeadNewSlack(hi, 2, 1, &buf, &n) != 0, "slack capcap"); - uint8_t def[37] = {5, KVSPACE_XH_FIXED_SMALL}; - memcpy(def + 18, "def rwir", 8); - CHECK(kvspaceXhDecode(def, sizeof def, &h) == 0, "def rwir head"); - CHECK(h.content_len == 5 && h.total == sizeof def, "def rwir body"); - CHECK(kvspaceXhNewShort("def rwir", def + 32, 5, &encoded, &encoded_len) == 0, - "def rwir encode"); - CHECK(encoded_len == sizeof def && memcmp(encoded, def, sizeof def) == 0, - "def rwir wire"); - free(encoded); - - uint8_t time_raw[8] = {1}; - CHECK(kvspaceXhNewShort("time", time_raw, 8, &encoded, &encoded_len) == 0, - "time encode"); - CHECK(kvspaceXhDecode(encoded, encoded_len, &h) == 0 && - h.content_len == 8 && h.headlen == 32, "time head"); - free(encoded); - - uint8_t func[32] = {5, KVSPACE_XH_FIXED_SMALL}; - memcpy(func + 18, "rwfunc", 6); - CHECK(kvspaceXhDecode(func, sizeof func, &h) == 0 && h.content_len == 0, - "rwfunc directory"); - CHECK(kvspaceXhNewShort("rwfunc", NULL, 0, &encoded, &encoded_len) == 0, - "rwfunc directory encode"); - CHECK(encoded_len == sizeof func && memcmp(encoded, func, sizeof func) == 0, - "rwfunc directory wire"); - free(encoded); - - uint8_t anchor[40] = {5, KVSPACE_XH_SLACK}; - anchor[2] = 5; - anchor[10] = 8; - memcpy(anchor + 18, "rwfunc", 6); - CHECK(kvspaceXhDecode(anchor, sizeof anchor, &h) == 0 && - h.content_len == 5 && h.body_cap == 8, "rwfunc anchor"); - CHECK(kvspaceXhNewCode("rwfunc", anchor + 32, 5, 8, - &encoded, &encoded_len) == 0, "rwfunc anchor encode"); - CHECK(encoded_len == sizeof anchor && memcmp(encoded, anchor, sizeof anchor) == 0, - "rwfunc anchor wire"); - free(encoded); const uint8_t call_body[] = {0, 0, 0, 0, 0, 'f'}; - CHECK(kvspaceXhNewCode("rwfunc", call_body, sizeof call_body, sizeof call_body, - &encoded, &encoded_len) == 0, "rwfunc call encode"); - CHECK(kvspaceXhDecode(encoded, encoded_len, &h) == 0 && - h.content_len == sizeof call_body && - memcmp(h.body, call_body, sizeof call_body) == 0, "rwfunc call wire"); - free(encoded); - anchor[2] = 4; - CHECK(kvspaceXhDecode(anchor, sizeof anchor, &h) != 0, "short rwfunc anchor"); - - uint8_t ptr[40] = {5, KVSPACE_XH_SLACK | KVSPACE_XH_PTR_FLAG}; - ptr[2] = 5; - ptr[10] = 8; - memcpy(ptr + 18, "int64", 5); - memcpy(ptr + 32, "/varx", 5); - CHECK(kvspaceXhDecode(ptr, sizeof ptr, &h) == 0, "pointer head"); - CHECK(h.content_len == 5 && h.body_cap == 8 && h.kind == 5, - "pointer fields"); - CHECK(kvspaceXhNewPtr("int64", "/varx", 8, &encoded, &encoded_len) == 0, - "pointer encode"); - CHECK(encoded_len == sizeof ptr && memcmp(encoded, ptr, sizeof ptr) == 0, - "pointer wire"); - free(encoded); - /* 空 kindexpr = 不约束:Ptr 目标允许为空;`any` 不是类型名。 */ - CHECK(kvspaceXhNewPtr("", "/varx", 8, &encoded, &encoded_len) == 0, - "empty pointer kindexpr"); - CHECK(kvspaceXhDecode(encoded, encoded_len, &h) == 0 && - h.kind == 5 && h.langtype_len == 0 && h.content_len == 5, - "empty pointer fields"); - free(encoded); - CHECK(kvspaceXhNewPtr("any", "/varx", 8, &encoded, &encoded_len) != 0, - "any kindexpr rejected"); - ptr[1] |= 0x80; - CHECK(kvspaceXhDecode(ptr, sizeof ptr, &h) != 0, "reserved pointer bit"); - ptr[1] &= 0x7f; - ptr[2] = 9; - CHECK(kvspaceXhDecode(ptr, sizeof ptr, &h) != 0, "pointer length"); - ptr[2] = 5; - ptr[32] = 0; - CHECK(kvspaceXhDecode(ptr, sizeof ptr, &h) != 0, "pointer NUL"); - - uint8_t ext[40] = {5, KVSPACE_XH_EXT}; - ext[2] = 5; - ext[10] = 8; - memcpy(ext + 18, "int64", 5); - memcpy(ext + 32, "s3://", 5); - CHECK(kvspaceXhDecode(ext, sizeof ext, &h) == 0 && - h.kind == KVSPACE_XH_EXT && h.content_len == 5 && h.body_cap == 8, - "external locator"); - CHECK(kvspaceXhNewExt("int64", "s3://", 8, &encoded, &encoded_len) == 0, - "external encode"); - CHECK(encoded_len == sizeof ext && memcmp(encoded, ext, sizeof ext) == 0, - "external wire"); - free(encoded); - ext[32] = 0; - CHECK(kvspaceXhDecode(ext, sizeof ext, &h) != 0, "external NUL"); - ext[32] = 0xff; - CHECK(kvspaceXhDecode(ext, sizeof ext, &h) != 0, "external UTF-8"); - ext[32] = 's'; - ext[2] = 0; - CHECK(kvspaceXhDecode(ext, sizeof ext, &h) != 0, "empty external locator"); - - uint8_t rwir[40] = {5, KVSPACE_XH_SLACK}; - rwir[2] = 8; - rwir[10] = 8; - memcpy(rwir + 18, "rwir", 4); - CHECK(kvspaceXhDecode(rwir, sizeof rwir, &h) == 0, "rwir head"); - CHECK(h.content_len == 8 && h.total == sizeof rwir, "rwir body"); - CHECK(kvspaceXhNewCode("rwir", rwir + 32, 8, 8, &encoded, &encoded_len) == 0, - "rwir encode"); - CHECK(encoded_len == sizeof rwir && memcmp(encoded, rwir, sizeof rwir) == 0, - "rwir wire"); - free(encoded); - rwir[2] = 4; - CHECK(kvspaceXhDecode(rwir, sizeof rwir, &h) != 0, "short rwir body"); - - uint8_t def_type[35] = {5, KVSPACE_XH_SLACK}; - def_type[2] = def_type[10] = 3; - memcpy(def_type + 18, "def langtype", 12); - memcpy(def_type + 32, "int", 3); - CHECK(kvspaceXhDecode(def_type, sizeof def_type, &h) == 0, - "def langtype head"); - CHECK(kvspaceXhNewCode("def langtype", (const uint8_t *)"int", 3, 3, - &encoded, &encoded_len) == 0, - "def langtype encode"); - CHECK(encoded_len == sizeof def_type && - memcmp(encoded, def_type, sizeof def_type) == 0, - "def langtype wire"); - free(encoded); + CHECK(kvspaceXvalueHeadNewCode(call_body, sizeof call_body, sizeof call_body, + &value, &len) == 0, "code new"); + memcpy(&h, value, sizeof h); + CHECK(h.storetype == KVSPACE_XVALUE_ST_SLACK && + kvspaceXvalueHeadLen(&h) == sizeof call_body, + "code head"); + CHECK(memcmp(value + KVSPACE_XVALUE_HEAD_LEN, call_body, + sizeof call_body) == 0, "code body"); + free(value); + CHECK(kvspaceXvalueHeadNewCode(call_body, 5, 3, &value, &len) != 0, + "code cap= 8) printf("%ld", *(const int64_t *)body); else if (strcmp(kind, "char/utf8") == 0) printf("%.*s", (int)bl, body); @@ -37,10 +37,10 @@ static void get(void *kv, const char *key) { } static void set_int(void *kv, const char *key, int64_t v) { - uint32_t l; uint8_t *b = enc_int64(v, &l); kv_set(kv, key, b, l); free(b); + uint32_t l; uint8_t *b = new_int64(v, &l); kv_set(kv, key, "int64", b, l); free(b); } static void set_str(void *kv, const char *key, const char *s) { - uint32_t l; uint8_t *b = enc_str(s, &l); kv_set(kv, key, b, l); free(b); + uint32_t l; uint8_t *b = new_str(s, &l); kv_set(kv, key, "char/utf8", b, l); free(b); } static void list(void *kv, const char *dir, int show_kind) { uint32_t len; uint8_t *out = NULL; @@ -58,7 +58,7 @@ static void list(void *kv, const char *dir, int show_kind) { char full[512]; snprintf(full, sizeof(full), "%s%s", dir, name); uint32_t vl; uint8_t *v = kv_get(kv, full, &vl); if (v) { char kind[32]; const uint8_t *body; uint32_t bl; - dec(v, vl, kind, sizeof(kind), &body, &bl); + read_kind(kv, full, v, vl, kind, sizeof(kind), &body, &bl); printf("%s\t%s", name, kind); if (strcmp(kind, "int64") == 0 && bl >= 8) printf("\t%ld", *(const int64_t *)body); printf("\n"); @@ -77,7 +77,7 @@ int main(void) { remove(path); void *kv = kv_open_shm(path); if (!kv) return 1; - kvspaceMkindex(kv, "/t01/", NULL, 0); + kvspaceMkindex(kv, "/t01/", 0, NULL, 0); printf("=== Set & Get ===\n"); set_int(kv, "/t01/a", 42); diff --git a/tutorial-c/02_cpp.cpp b/tutorial-c/02_cpp.cpp index 1e587fc..b4835e3 100644 --- a/tutorial-c/02_cpp.cpp +++ b/tutorial-c/02_cpp.cpp @@ -1,11 +1,14 @@ /* * 02_cpp — C++ RAII 封装:extern "C" 直接调用 libkvspace dispatch 前端 * + * #566:head 固定 8 字节(body 偏移恒 8),类型走 x‥langtype attr key。 + * * 编译: g++ -std=c++17 02_cpp.cpp -lkvspace */ extern "C" { #include "kvspace/kvspace.h" + #include } #include @@ -28,28 +31,33 @@ struct KV { } ~KV() { kvspaceClose(kv); ::unlink(path.c_str()); } - // 写即构造:解预编码 XValue 取 kindexpr+body,就地/新位置二选一原语,往返回指针写 body。 - void set(const char *key, const uint8_t *val, uint32_t len) { - kvspaceHead_t h; - if (kvspaceDecodeHead(val, len, &h) != 0) return; - uint32_t bl = h.body_len > 0 ? (uint32_t)h.body_len : 0; + // 写即构造:memcpy 固定头取 body_len/storetype/ptr,就地/新位置二选一原语, + // 往返回指针写 body。langtype 由调用方显式给(写 x‥langtype attr key)。 + void set(const char *key, const char *langtype, const uint8_t *val, uint32_t len) { + if (!val || len < KVSPACE_XVALUE_HEAD_LEN) return; + kvspaceXvalueHead hd; + memcpy(&hd, val, sizeof hd); + uint64_t bl = kvspaceXvalueHeadLen(&hd); uint8_t *dst = nullptr; char err[256]; - if (kvspaceWriteInPlace(kv, key, 1, bl, &dst, err, sizeof(err)) != 0) - kvspaceWriteNewPlace(kv, key, (const char *)h.kindexpr, bl, &dst, err, sizeof(err)); - if (bl && dst) memcpy(dst, val + h.body_offset, bl); + if (kvspaceWriteInPlace(kv, key, 1, (uint32_t)bl, &dst, err, sizeof(err)) != 0) + kvspaceWriteNewPlace(kv, key, hd.ptr, hd.storetype, langtype, + (uint32_t)bl, bl, &dst, err, sizeof(err)); + if (bl && dst) memcpy(dst, val + KVSPACE_XVALUE_HEAD_LEN, (size_t)bl); } // 借用读:out 指向 kvspace 常驻空间,拷进 std::string 后即失效,不 free。 + // kind 由 kvspaceGetHead 从 x‥langtype attr key 读。 bool get(const char *key, std::string &kind, std::string &raw) { uint8_t *out = nullptr; uint32_t len = 0; kvspaceGet(kv, key, 0, &out, &len); if (!out || len == 0) return false; kvspaceHead_t h; - kvspaceDecodeHead(out, len, &h); - const char *k = (const char *)h.kindexpr; + if (kvspaceGetHead(kv, key, &h) != 0) return false; + const char *k = (const char *)h.langtype; if (k[0] == '*' || k[0] == '@') k++; if (k[0] == '[') { const char *e = strchr(k, ']'); if (e) k = e + 1; } kind = k; - raw.assign((const char *)(out + h.body_offset), h.body_len > 0 ? h.body_len : 0); + raw.assign((const char *)(out + KVSPACE_XVALUE_HEAD_LEN), + len > KVSPACE_XVALUE_HEAD_LEN ? len - KVSPACE_XVALUE_HEAD_LEN : 0); return true; } void del(const char *key) { @@ -57,15 +65,15 @@ struct KV { kvspaceDel(kv, keys, 1, nullptr, 0); } void deltree(const char *p) { kvspaceDelTree(kv, p, nullptr, 0); } - void mkindex(const char *p) { kvspaceMkindex(kv, p, nullptr, 0); } - // 前缀枚举:ListLen 定计数 + 逐 idx ListAt 借用取名(读出即拷入 vector)。 + void mkindex(const char *p) { kvspaceMkindex(kv, p, 0, nullptr, 0); } + // 前缀枚举:ListLen 定计数 + 逐 idx ListAt 取名(自备缓冲,读出即拷入 vector)。 std::vector list(const char *prefix) { std::vector r; int32_t count = 0; if (kvspaceListLen(kv, prefix, 0, 1, &count) != 0 || count <= 0) return r; for (int32_t i = 0; i < count; i++) { - uint8_t *nm = nullptr; uint32_t nl = 0; - if (kvspaceListAt(kv, prefix, 0, 1, i, &nm, &nl) == 0 && nm) + uint8_t nm[1024]; uint32_t nl = 0; + if (kvspaceListAt(kv, prefix, 0, 1, i, nm, sizeof(nm), &nl) == 0 && nl > 0) r.push_back(std::string((const char *)nm, nl)); } return r; @@ -86,11 +94,11 @@ int main() { kv.mkindex("/cpp/"); auto [vi, li] = xv_int(42); - kv.set("/cpp/a", vi, li); + kv.set("/cpp/a", "int64", vi, li); free(vi); auto [vs, ls] = xv_str("hello"); - kv.set("/cpp/b", vs, ls); + kv.set("/cpp/b", "char/utf8", vs, ls); free(vs); std::string kind, raw; diff --git a/tutorial-c/03_python.py b/tutorial-c/03_python.py index dcb7a13..cf56c2b 100644 --- a/tutorial-c/03_python.py +++ b/tutorial-c/03_python.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 -"""03_python — ctypes 直调 libkvspace(dispatch 前端),shm:// 后端""" +"""03_python — ctypes 直调 libkvspace(dispatch 前端),shm:// 后端 +#566:head 固定 8 字节(body 偏移恒 8),类型走 x‥langtype attr key。""" import ctypes import os @@ -10,19 +11,44 @@ U8P = ctypes.POINTER(ctypes.c_uint8) U32P = ctypes.POINTER(ctypes.c_uint32) +U64P = ctypes.POINTER(ctypes.c_uint64) CCHARPP = ctypes.POINTER(ctypes.c_char_p) +KVSPACE_XVALUE_HEAD_LEN = 8 + +# 与 kvspaceHead_t 对齐(langtype 由 kvspaceGetHead 从 attr key 填)。 class Head(ctypes.Structure): _fields_ = [ - ("kindexpr", ctypes.c_uint8 * 256), - ("ro", ctypes.c_uint8), - ("vid", ctypes.c_uint32), + ("headlen", ctypes.c_uint16), + ("ref", ctypes.c_uint8), + ("storetype", ctypes.c_uint8), ("body_len", ctypes.c_int32), + ("ndim", ctypes.c_int32), + ("dims", ctypes.c_int32 * 8), + ("langtype", ctypes.c_char * 256), + ("langtype_len", ctypes.c_int32), ("body_offset", ctypes.c_int32), + ("body_cap", ctypes.c_uint64), ] +# 固定 8 字节 head:[len:u48 LE@0-5][storetype@6][ptr@7]。 +class XvalueHead(ctypes.Structure): + _fields_ = [ + ("len", ctypes.c_uint8 * 6), + ("storetype", ctypes.c_uint8), + ("ptr", ctypes.c_uint8), + ] + + +def xvalue_head_len(h): + v = 0 + for i in reversed(range(6)): + v = (v << 8) | h.len[i] + return v + + def bind(fn, args, restype): fn.argtypes = args fn.restype = restype @@ -32,18 +58,21 @@ def bind(fn, args, restype): bind(lib.kvspaceClose, [ctypes.c_void_p], None) # 借用读:out 指向 kvspace 常驻空间,string_at 拷出后不 free。resolve 穿透 link。 bind(lib.kvspaceGet, [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(U8P), U32P], ctypes.c_int) +bind(lib.kvspaceGetHead, [ctypes.c_void_p, ctypes.c_char_p, ctypes.POINTER(Head)], ctypes.c_int) # 写即构造:就地/新位置二选一原语,往返回的 body 偏移指针写字节。 bind(lib.kvspaceWriteInPlace, [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_uint32, ctypes.POINTER(U8P), ctypes.c_char_p, ctypes.c_uint32], ctypes.c_int) -bind(lib.kvspaceWriteNewPlace, [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint32, ctypes.POINTER(U8P), ctypes.c_char_p, ctypes.c_uint32], ctypes.c_int) +bind(lib.kvspaceWriteNewPlace, [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint8, ctypes.c_uint8, + ctypes.c_char_p, ctypes.c_uint32, ctypes.c_uint64, + ctypes.POINTER(U8P), ctypes.c_char_p, ctypes.c_uint32], ctypes.c_int) # 前缀枚举:ListLen 定计数 + 逐 idx ListAt 借用取名。 bind(lib.kvspaceListLen, [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int32)], ctypes.c_int) -bind(lib.kvspaceListAt, [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_int, ctypes.c_int32, ctypes.POINTER(U8P), U32P], ctypes.c_int) +bind(lib.kvspaceListAt, [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_int, ctypes.c_int32, + U8P, ctypes.c_uint32, U32P], ctypes.c_int) bind(lib.kvspaceDel, [ctypes.c_void_p, CCHARPP, ctypes.c_uint32, ctypes.c_char_p, ctypes.c_uint32], ctypes.c_int) bind(lib.kvspaceDelTree, [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint32], ctypes.c_int) -bind(lib.kvspaceMkindex, [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint32], ctypes.c_int) +bind(lib.kvspaceMkindex, [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint32, ctypes.c_char_p, ctypes.c_uint32], ctypes.c_int) bind(lib.kvspaceNewInt64, [ctypes.c_int64, ctypes.POINTER(U8P), U32P], ctypes.c_int) bind(lib.kvspaceNewChar, [U8P, ctypes.c_uint32, ctypes.POINTER(U8P), U32P], ctypes.c_int) -bind(lib.kvspaceDecodeHead, [U8P, ctypes.c_uint32, ctypes.POINTER(Head)], ctypes.c_int) def enc_int(v): @@ -52,7 +81,7 @@ def enc_int(v): return ctypes.string_at(out, n.value) -def enc_str(s): +def new_str(s): b = s.encode() buf = (ctypes.c_uint8 * len(b)).from_buffer_copy(b) out, n = U8P(), ctypes.c_uint32() @@ -60,16 +89,18 @@ def enc_str(s): return ctypes.string_at(out, n.value) -def decode(data): - buf = (ctypes.c_uint8 * len(data)).from_buffer_copy(data) +# 读 kind:kvspaceGetHead 从 x‥langtype attr key 填 h.langtype,body 从偏移 8 起。 +def read_kind(kv, key, data): h = Head() - lib.kvspaceDecodeHead(buf, len(data), ctypes.byref(h)) - kx = bytes(h.kindexpr).split(b"\0", 1)[0].decode() + if lib.kvspaceGetHead(kv, key.encode(), ctypes.byref(h)) != 0: + return "", b"" + kx = bytes(h.langtype).split(b"\0", 1)[0].decode() if kx.startswith(("*", "@")): kx = kx[1:] if kx.startswith("["): kx = kx[kx.index("]") + 1:] - return kx, data[h.body_offset:h.body_offset + h.body_len] + bl = h.body_len if h.body_len > 0 else 0 + return kx, data[KVSPACE_XVALUE_HEAD_LEN:KVSPACE_XVALUE_HEAD_LEN + bl] class KV: @@ -96,18 +127,22 @@ def __enter__(self): def __exit__(self, *a): self.close() - def set(self, key, val): + # 写即构造:memcpy 固定头取 body_len/storetype/ptr,就地/新位置二选一原语, + # 往返回指针写 body。langtype 由调用方显式给(写 x‥langtype attr key)。 + def set(self, key, langtype, val): + if len(val) < KVSPACE_XVALUE_HEAD_LEN: + return buf = (ctypes.c_uint8 * len(val)).from_buffer_copy(val) - h = Head() - lib.kvspaceDecodeHead(buf, len(val), ctypes.byref(h)) - bl = h.body_len if h.body_len > 0 else 0 - kindexpr = bytes(h.kindexpr).split(b"\0", 1)[0] + hd = XvalueHead() + ctypes.memmove(ctypes.byref(hd), buf, ctypes.sizeof(hd)) + bl = xvalue_head_len(hd) dst = U8P() err = ctypes.create_string_buffer(256) if lib.kvspaceWriteInPlace(self.kv, key.encode(), 1, bl, ctypes.byref(dst), err, 256) != 0: - lib.kvspaceWriteNewPlace(self.kv, key.encode(), kindexpr, bl, ctypes.byref(dst), err, 256) + lib.kvspaceWriteNewPlace(self.kv, key.encode(), hd.ptr, hd.storetype, + langtype.encode(), bl, bl, ctypes.byref(dst), err, 256) if bl > 0 and dst: - ctypes.memmove(dst, ctypes.byref(buf, h.body_offset), bl) + ctypes.memmove(dst, ctypes.byref(buf, KVSPACE_XVALUE_HEAD_LEN), bl) def get(self, key): out, n = U8P(), ctypes.c_uint32() @@ -124,7 +159,7 @@ def deltree(self, prefix): lib.kvspaceDelTree(self.kv, prefix.encode(), None, 0) def mkindex(self, path): - lib.kvspaceMkindex(self.kv, path.encode(), None, 0) + lib.kvspaceMkindex(self.kv, path.encode(), 0, None, 0) def list(self, prefix): count = ctypes.c_int32() @@ -132,8 +167,9 @@ def list(self, prefix): return [] names = [] for i in range(count.value): - out, n = U8P(), ctypes.c_uint32() - if lib.kvspaceListAt(self.kv, prefix.encode(), 0, 1, i, ctypes.byref(out), ctypes.byref(n)) == 0 and out: + out = (ctypes.c_uint8 * 1024)() + n = ctypes.c_uint32() + if lib.kvspaceListAt(self.kv, prefix.encode(), 0, 1, i, out, 1024, ctypes.byref(n)) == 0 and n.value > 0: names.append(ctypes.string_at(out, n.value).decode()) return names @@ -141,11 +177,11 @@ def list(self, prefix): with KV("/tmp/kvspace_py.shm") as kv: kv.mkindex("/py/") - kv.set("/py/a", enc_int(42)) - kv.set("/py/b", enc_str("你好")) + kv.set("/py/a", "int64", enc_int(42)) + kv.set("/py/b", "char/utf8", new_str("你好")) for k in ["/py/a", "/py/b"]: - kind, raw = decode(kv.get(k)) + kind, raw = read_kind(kv.kv, k, kv.get(k)) if kind == "int64": val = struct.unpack(" Self { + Head { headlen: 0, r#ref: 0, storetype: 0, body_len: 0, ndim: 0, dims: [0; 8], + langtype: [0; 256], langtype_len: 0, body_offset: 0, body_cap: 0 } + } +} + +// 固定 8 字节 head:[len:u48 LE@0-5][storetype@6][ptr@7],body 偏移恒 8。 +#[repr(C)] +struct XvalueHead { + len: [u8; 6], + storetype: u8, + ptr: u8, +} + +fn xvalue_head_len(h: &XvalueHead) -> u64 { + let mut v: u64 = 0; + for i in (0..6).rev() { v = (v << 8) | h.len[i] as u64; } + v } extern "C" { @@ -21,25 +49,29 @@ extern "C" { // 借用读:out 指向 kvspace 常驻空间,调用方不得 free。resolve=1 穿透 link。 fn kvspaceGet(h: *mut c_void, key: *const c_char, resolve: c_int, out: *mut *mut u8, out_len: *mut u32) -> c_int; + fn kvspaceGetHead(h: *mut c_void, key: *const c_char, out: *mut Head) -> c_int; // 就地写:key 已存在且 body_len 不变 → 返回原 box body 偏移指针;否则非 0。 fn kvspaceWriteInPlace(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; - // 新位置写:按 (kindexpr, body_len) 分配新 box、写 head,返回 body 偏移指针。 - fn kvspaceWriteNewPlace(h: *mut c_void, key: *const c_char, kindexpr: *const c_char, - body_len: u32, body: *mut *mut u8, err: *mut c_char, err_cap: u32) -> c_int; + // 新位置写:按 (ref, storetype, langtype, body_len) 分配 box、写 head(顺写 attr key), + // 返回 body 偏移指针。 + fn kvspaceWriteNewPlace(h: *mut c_void, key: *const c_char, r#ref: u8, storetype: u8, + langtype: *const c_char, body_len: u32, body_cap: u64, + body: *mut *mut u8, err: *mut c_char, err_cap: u32) -> c_int; // 前缀枚举:ListLen 定计数,逐 idx ListAt 借用取名(不得 free)。 fn kvspaceListLen(h: *mut c_void, prefix: *const c_char, expand_ext: c_int, resolve: c_int, out_count: *mut i32) -> c_int; fn kvspaceListAt(h: *mut c_void, prefix: *const c_char, expand_ext: c_int, resolve: c_int, - idx: i32, out: *mut *mut u8, out_len: *mut u32) -> c_int; + idx: i32, out: *mut u8, out_cap: u32, out_len: *mut u32) -> c_int; fn kvspaceDel(h: *mut c_void, keys: *const *const c_char, n: u32, err: *mut c_char, err_cap: u32) -> c_int; fn kvspaceDelTree(h: *mut c_void, prefix: *const c_char, err: *mut c_char, err_cap: u32) -> c_int; - fn kvspaceMkindex(h: *mut c_void, path: *const c_char, err: *mut c_char, err_cap: u32) -> c_int; + fn kvspaceMkindex(h: *mut c_void, path: *const c_char, capacity: u32, err: *mut c_char, err_cap: u32) -> c_int; fn kvspaceNewInt64(v: i64, out: *mut *mut u8, out_len: *mut u32) -> c_int; fn kvspaceNewChar(bytes: *const u8, len: u32, out: *mut *mut u8, out_len: *mut u32) -> c_int; - fn kvspaceDecodeHead(data: *const u8, data_len: u32, out: *mut Head) -> c_int; } +fn cs(s: &str) -> CString { CString::new(s).unwrap() } + fn enc_int(v: i64) -> Vec { let mut out: *mut u8 = ptr::null_mut(); let mut n: u32 = 0; @@ -49,7 +81,7 @@ fn enc_int(v: i64) -> Vec { b } -fn enc_str(s: &str) -> Vec { +fn new_str(s: &str) -> Vec { let b = s.as_bytes(); let mut out: *mut u8 = ptr::null_mut(); let mut n: u32 = 0; @@ -59,16 +91,19 @@ fn enc_str(s: &str) -> Vec { r } -fn decode(data: &[u8]) -> (String, Vec) { - let mut h = Head { kindexpr: [0; 256], ro: 0, vid: 0, body_len: 0, body_offset: 0 }; - unsafe { kvspaceDecodeHead(data.as_ptr(), data.len() as u32, &mut h); } - let klen = h.kindexpr.iter().position(|&b| b == 0).unwrap_or(256); - let mut kx = String::from_utf8_lossy(&h.kindexpr[..klen]).into_owned(); +// 读 kind:kvspaceGetHead 从 x‥langtype attr key 填 h.langtype,body 从偏移 8 起。 +fn read_kind(kv: *mut c_void, key: &str, data: &[u8]) -> (String, Vec) { + let mut h = Head::default(); + if unsafe { kvspaceGetHead(kv, cs(key).as_ptr(), &mut h) } != 0 { + return (String::new(), vec![]); + } + let klen = h.langtype.iter().position(|&b| b == 0).unwrap_or(256); + let mut kx = String::from_utf8_lossy(&h.langtype[..klen]).into_owned(); if kx.starts_with('*') || kx.starts_with('@') { kx.drain(..1); } if let Some(i) = kx.find(']') { kx.drain(..=i); } - let off = h.body_offset as usize; let bl = h.body_len.max(0) as usize; - (kx, data[off..off + bl].to_vec()) + let end = (8 + bl).min(data.len()); + (kx, data[8..end].to_vec()) } struct KV { ptr: *mut c_void, path: String } @@ -81,22 +116,23 @@ impl KV { assert!(!ptr.is_null()); KV { ptr, path: path.to_string() } } - // 写即构造:解预编码 XValue 取 kindexpr+body,就地/新位置二选一原语,往返回指针写 body。 - fn set(&self, key: &str, val: &[u8]) { - let ck = CString::new(key).unwrap(); - let mut h = Head { kindexpr: [0; 256], ro: 0, vid: 0, body_len: 0, body_offset: 0 }; - unsafe { kvspaceDecodeHead(val.as_ptr(), val.len() as u32, &mut h); } - let bl = h.body_len.max(0) as u32; - let bo = h.body_offset as usize; + // 写即构造:memcpy 固定头取 body_len/storetype/ptr,就地/新位置二选一原语, + // 往返回指针写 body。langtype 由调用方显式给(写 x‥langtype attr key)。 + fn set(&self, key: &str, langtype: &str, val: &[u8]) { + if val.len() < 8 { return; } + let hd: XvalueHead = unsafe { std::ptr::read(val.as_ptr() as *const XvalueHead) }; + let bl = xvalue_head_len(&hd) as u32; + let ck = cs(key); + let lt = cs(langtype); let mut dst: *mut u8 = ptr::null_mut(); let mut err = [0i8; 256]; unsafe { if kvspaceWriteInPlace(self.ptr, ck.as_ptr(), 1, bl, &mut dst, err.as_mut_ptr(), 256) != 0 { - kvspaceWriteNewPlace(self.ptr, ck.as_ptr(), h.kindexpr.as_ptr() as *const c_char, - bl, &mut dst, err.as_mut_ptr(), 256); + kvspaceWriteNewPlace(self.ptr, ck.as_ptr(), hd.ptr, hd.storetype, lt.as_ptr(), + bl, bl as u64, &mut dst, err.as_mut_ptr(), 256); } if bl > 0 && !dst.is_null() { - ptr::copy_nonoverlapping(val[bo..].as_ptr(), dst, bl as usize); + ptr::copy_nonoverlapping(val[8..].as_ptr(), dst, bl as usize); } } } @@ -111,7 +147,7 @@ impl KV { } fn mkindex(&self, path: &str) { let cp = CString::new(path).unwrap(); - unsafe { kvspaceMkindex(self.ptr, cp.as_ptr(), ptr::null_mut(), 0); } + unsafe { kvspaceMkindex(self.ptr, cp.as_ptr(), 0, ptr::null_mut(), 0); } } fn del(&self, key: &str) { let ck = CString::new(key).unwrap(); @@ -131,12 +167,11 @@ impl KV { } let mut r = Vec::with_capacity(count as usize); for i in 0..count { - let mut out: *mut u8 = ptr::null_mut(); + let mut nm = [0u8; 1024]; let mut n: u32 = 0; - unsafe { kvspaceListAt(self.ptr, cp.as_ptr(), 0, 1, i, &mut out, &mut n); } - if !out.is_null() && n > 0 { - let s = unsafe { std::slice::from_raw_parts(out, n as usize) }; - r.push(String::from_utf8_lossy(s).into_owned()); + unsafe { kvspaceListAt(self.ptr, cp.as_ptr(), 0, 1, i, nm.as_mut_ptr(), nm.len() as u32, &mut n); } + if n > 0 { + r.push(String::from_utf8_lossy(&nm[..n as usize]).into_owned()); } } r @@ -153,11 +188,11 @@ impl Drop for KV { fn main() { let kv = KV::open("/tmp/kvspace_rs.shm"); kv.mkindex("/rs/"); - kv.set("/rs/a", &enc_int(42)); - kv.set("/rs/b", &enc_str("hello")); + kv.set("/rs/a", "int64", &enc_int(42)); + kv.set("/rs/b", "char/utf8", &new_str("hello")); let v = kv.get("/rs/a").unwrap(); - let (kind, raw) = decode(&v); + let (kind, raw) = read_kind(kv.ptr, "/rs/a", &v); let mut arr = [0u8; 8]; arr.copy_from_slice(&raw[..8]); let val = i64::from_le_bytes(arr); diff --git a/tutorial-c/05_integrity.c b/tutorial-c/05_integrity.c index cc259f4..f8c69e1 100644 --- a/tutorial-c/05_integrity.c +++ b/tutorial-c/05_integrity.c @@ -18,7 +18,7 @@ static int check(void *kv, const char *key, int64_t expected, int round) { uint32_t len; uint8_t *v = kv_get(kv, key, &len); if (!v || len == 0) { printf("ROUND%d MISS %s\n", round, key); return 0; } char kind[32]; const uint8_t *body; uint32_t bl; - dec(v, len, kind, sizeof(kind), &body, &bl); + read_kind(kv, key, v, len, kind, sizeof(kind), &body, &bl); if (bl < 8 || strcmp(kind, "int64") != 0) { printf("ROUND%d CORRUPT %s: bad kind=%s bl=%u\n", round, key, kind, bl); free(v); return 0; @@ -36,7 +36,7 @@ static int check_str(void *kv, const char *key, const char *expected, int round) uint32_t len; uint8_t *v = kv_get(kv, key, &len); if (!v || len == 0) { printf("ROUND%d MISS %s\n", round, key); return 0; } char kind[32]; const uint8_t *body; uint32_t bl; - dec(v, len, kind, sizeof(kind), &body, &bl); + read_kind(kv, key, v, len, kind, sizeof(kind), &body, &bl); if (strcmp(kind, "char/utf8") != 0) { printf("ROUND%d CORRUPT %s: bad kind\n", round, key); free(v); return 0; } @@ -52,7 +52,7 @@ int main(void) { void *kv = kv_open_shm(path); if (!kv) { printf("FAIL: open\n"); return 1; } - kvspaceMkindex(kv, "/it/", NULL, 0); + kvspaceMkindex(kv, "/it/", 0, NULL, 0); srand((unsigned)time(NULL)); char keys[N_KEYS][64]; @@ -66,8 +66,8 @@ int main(void) { for (int i = 0; i < n_keys; i++) { snprintf(keys[i], sizeof(keys[i]), "/it/k%d", i); values[i] = ((int64_t)rand() << 32) | (int64_t)rand(); - uint32_t len; uint8_t *v = enc_int64(values[i], &len); - kv_set(kv, keys[i], v, len); free(v); n++; + uint32_t len; uint8_t *v = new_int64(values[i], &len); + kv_set(kv, keys[i], "int64", v, len); free(v); n++; } printf(" wrote %d keys\n", n); @@ -85,8 +85,8 @@ int main(void) { int idx = n + i; snprintf(keys[idx], sizeof(keys[idx]), "/it/new_k%d", i); values[idx] = (int64_t)i * 1000; - uint32_t len; uint8_t *v = enc_int64(values[idx], &len); - kv_set(kv, keys[idx], v, len); free(v); + uint32_t len; uint8_t *v = new_int64(values[idx], &len); + kv_set(kv, keys[idx], "int64", v, len); free(v); } n += del_count; @@ -99,8 +99,8 @@ int main(void) { int i = rand() % n; if (!keys[i][0]) continue; values[i] = ((int64_t)rand() << 32) | (int64_t)rand(); - uint32_t len; uint8_t *v = enc_int64(values[i], &len); - kv_set(kv, keys[i], v, len); free(v); + uint32_t len; uint8_t *v = new_int64(values[i], &len); + kv_set(kv, keys[i], "int64", v, len); free(v); } for (int i = 0; i < n; i++) if (keys[i][0] && !check(kv, keys[i], values[i], 3)) errors++; @@ -110,8 +110,8 @@ int main(void) { for (int i = 0; i < 100; i++) { snprintf(keys[str_base + i], sizeof(keys[0]), "/it/s%d", i); snprintf(strvals[i], sizeof(strvals[0]), "val_%d_%d", i, rand() % 1000); - uint32_t len; uint8_t *v = enc_str(strvals[i], &len); - kv_set(kv, keys[str_base + i], v, len); free(v); + uint32_t len; uint8_t *v = new_str(strvals[i], &len); + kv_set(kv, keys[str_base + i], "char/utf8", v, len); free(v); } for (int i = 0; i < 100; i++) { char k[64]; snprintf(k, sizeof(k), "/it/s%d", i); @@ -123,22 +123,22 @@ int main(void) { for (int i = 0; i < 20; i++) { for (int j = 0; j < MAX_VAL_SZ; j++) big_vals[i][j] = (uint8_t)(rand() & 0xFF); char k[64]; snprintf(k, sizeof(k), "/it/big%d", i); - uint32_t len; uint8_t *v = enc_bytes(big_vals[i], MAX_VAL_SZ, &len); - kv_set(kv, k, v, len); free(v); + uint32_t len; uint8_t *v = new_bytes(big_vals[i], MAX_VAL_SZ, &len); + kv_set(kv, k, "", v, len); free(v); } for (int i = 0; i < 10; i++) { char k[64]; snprintf(k, sizeof(k), "/it/big%d", i); kv_del(kv, k); snprintf(k, sizeof(k), "/it/reuse%d", i); - uint32_t len; uint8_t *v = enc_int64((int64_t)i, &len); - kv_set(kv, k, v, len); free(v); + uint32_t len; uint8_t *v = new_int64((int64_t)i, &len); + kv_set(kv, k, "int64", v, len); free(v); } for (int i = 10; i < 20; i++) { char k[64]; snprintf(k, sizeof(k), "/it/big%d", i); uint32_t len; uint8_t *v = kv_get(kv, k, &len); if (!v || len == 0) { printf("ROUND5 MISS big%d\n", i); errors++; continue; } char kind[32]; const uint8_t *body; uint32_t bl; - dec(v, len, kind, sizeof(kind), &body, &bl); + read_kind(kv, k, v, len, kind, sizeof(kind), &body, &bl); if (bl != MAX_VAL_SZ || memcmp(body, big_vals[i], MAX_VAL_SZ) != 0) { printf("ROUND5 CORRUPT big%d\n", i); errors++; } diff --git a/tutorial-c/06_multiprocess.c b/tutorial-c/06_multiprocess.c index 2ae433f..76d4741 100644 --- a/tutorial-c/06_multiprocess.c +++ b/tutorial-c/06_multiprocess.c @@ -18,14 +18,14 @@ static int writer(const char *path, int64_t seed) { int errors = 0; void *kv = kv_open_shm(path); if (!kv) { printf("FAIL: writer open\n"); return 1; } - kvspaceMkindex(kv, "/mp/", NULL, 0); + kvspaceMkindex(kv, "/mp/", 0, NULL, 0); printf("[writer] round 1: write %d ints\n", N_KEYS); for (int i = 0; i < N_KEYS; i++) { char k[64]; snprintf(k, sizeof(k), "/mp/k%d", i); int64_t v = ((int64_t)rand() << 32) | rand(); - uint32_t l; uint8_t *b = enc_int64(v, &l); - if (kv_set(kv, k, b, l) != 0) { printf("[writer] FAIL set %s\n", k); errors++; } + uint32_t l; uint8_t *b = new_int64(v, &l); + if (kv_set(kv, k, "int64", b, l) != 0) { printf("[writer] FAIL set %s\n", k); errors++; } free(b); } @@ -39,8 +39,8 @@ static int writer(const char *path, int64_t seed) { for (int i = 0; i < 100; i++) { char k[64], s[64]; snprintf(k, sizeof(k), "/mp/s%d", i); snprintf(s, sizeof(s), "str_val_%d_%d", i, rand() % 1000); - uint32_t l; uint8_t *b = enc_str(s, &l); - if (kv_set(kv, k, b, l) != 0) { printf("[writer] FAIL set %s\n", k); errors++; } + uint32_t l; uint8_t *b = new_str(s, &l); + if (kv_set(kv, k, "char/utf8", b, l) != 0) { printf("[writer] FAIL set %s\n", k); errors++; } free(b); } @@ -49,14 +49,14 @@ static int writer(const char *path, int64_t seed) { for (int j = 0; j < 1024; j++) big[j] = (uint8_t)(rand() & 0xFF); for (int i = 0; i < 20; i++) { char k[64]; snprintf(k, sizeof(k), "/mp/big%d", i); - uint32_t l; uint8_t *b = enc_bytes(big, 1024, &l); - kv_set(kv, k, b, l); free(b); + uint32_t l; uint8_t *b = new_bytes(big, 1024, &l); + kv_set(kv, k, "", b, l); free(b); } for (int j = 0; j < 50; j++) { char k[64]; snprintf(k, sizeof(k), "/mp/extra%d", j); int64_t v = ((int64_t)rand() << 32) | rand(); - uint32_t l; uint8_t *b = enc_int64(v, &l); - kv_set(kv, k, b, l); free(b); + uint32_t l; uint8_t *b = new_int64(v, &l); + kv_set(kv, k, "int64", b, l); free(b); } printf("[writer] done, errors=%d\n", errors); @@ -88,7 +88,7 @@ static int reader(const char *path, int64_t seed) { uint32_t l; uint8_t *v = kv_get(kv, k, &l); if (v && l > 0) { char kind[32]; const uint8_t *body; uint32_t bl; - dec(v, l, kind, sizeof(kind), &body, &bl); + read_kind(kv, k, v, l, kind, sizeof(kind), &body, &bl); if (bl >= 8 && strcmp(kind, "int64") == 0) { int64_t got = *(const int64_t *)body; if (got != expected) { @@ -108,7 +108,7 @@ static int reader(const char *path, int64_t seed) { uint32_t l; uint8_t *v = kv_get(kv, k, &l); if (!v || l == 0) continue; char kind[32]; const uint8_t *body; uint32_t bl; - dec(v, l, kind, sizeof(kind), &body, &bl); + read_kind(kv, k, v, l, kind, sizeof(kind), &body, &bl); if (strcmp(kind, "char/utf8") != 0) { printf("[reader] BADKIND %s: %s\n", k, kind); errors++; } @@ -123,7 +123,7 @@ static int reader(const char *path, int64_t seed) { uint32_t l; uint8_t *v = kv_get(kv, k, &l); if (v && l > 0) { char kind[32]; const uint8_t *body; uint32_t bl; - dec(v, l, kind, sizeof(kind), &body, &bl); + read_kind(kv, k, v, l, kind, sizeof(kind), &body, &bl); if (bl == 1024) big_ok++; free(v); } diff --git a/tutorial-c/common.h b/tutorial-c/common.h index 6f92d3a..f5df54c 100644 --- a/tutorial-c/common.h +++ b/tutorial-c/common.h @@ -1,65 +1,74 @@ -// common.h — tutorial 公共助手:统一 kvspace* ABI 的编码/解码与单键读写便捷封装。 +// common.h — tutorial 公共助手:统一 kvspace* ABI 的 XValue 构造/读取与单键读写便捷封装。 // 所有 tutorial 均链接 libkvspace(dispatch 前端),运行期经 shm:// 选择 kvspace-c 后端。 +// #566:head 固定 8 字节(body 偏移恒 8),类型/形状走 x‥langtype attr key。 #ifndef KVSPACE_TUTORIAL_COMMON_H #define KVSPACE_TUTORIAL_COMMON_H #include "kvspace/kvspace.h" +#include #include #include #include #include -// ── 编码:返回 malloc 的 XValue 字节,长度写入 *len,调用方 free ── +// ── 构造:返回 malloc 的 XValue 字节(固定头 + body),长度写入 *len,调用方 free ── -static inline uint8_t *enc_int64(int64_t v, uint32_t *len) { +static inline uint8_t *new_int64(int64_t v, uint32_t *len) { uint8_t *out = NULL; uint32_t n = 0; kvspaceNewInt64(v, &out, &n); *len = n; return out; } -static inline uint8_t *enc_str(const char *s, uint32_t *len) { +static inline uint8_t *new_str(const char *s, uint32_t *len) { uint8_t *out = NULL; uint32_t n = 0; kvspaceNewChar((const uint8_t *)s, (uint32_t)strlen(s), &out, &n); *len = n; return out; } -static inline uint8_t *enc_bytes(const uint8_t *raw, uint32_t rl, uint32_t *len) { - uint8_t *out = NULL; uint32_t n = 0; - int32_t dims[1] = { (int32_t)rl }; - kvspaceXvalueEncode("uint8", raw, rl, dims, 1, &out, &n); - *len = n; return out; +static inline uint8_t *new_bytes(const uint8_t *raw, uint32_t rl, uint32_t *len) { + uint8_t *out = NULL; uint64_t n = 0; + uint64_t dims[1] = { rl }; + kvspaceXvalueHeadNewTensor(dims, 1, raw, rl, &out, &n); + *len = n > UINT32_MAX ? 0 : (uint32_t)n; + return out; } -// ── 解码:填 base kind(去 */@ 前缀与 [dims] 段),返回 body 指针/长度 ── +// ── 读取:kvspaceGetHead 读 x‥langtype attr key 取 base kind(去 */@ 前缀与 [dims] 段), +// 返回 body 指针/长度(body 偏移恒 8)── -static inline const char *dec(const uint8_t *data, uint32_t len, char *kind, size_t cap, +static inline const char *read_kind(void *h, const char *key, const uint8_t *data, uint32_t len, + char *kind, size_t cap, const uint8_t **body, uint32_t *body_len) { - kvspaceHead_t h; - if (kvspaceDecodeHead(data, len, &h) != 0) return NULL; - const char *k = (const char *)h.kindexpr; + if (!h || !key || !data || len < KVSPACE_XVALUE_HEAD_LEN) return NULL; + kvspaceHead_t hd; + if (kvspaceGetHead(h, key, &hd) != 0) return NULL; + const char *k = (const char *)hd.langtype; if (k[0] == '*' || k[0] == '@') k++; const char *base = k; if (k[0] == '[') { const char *e = strchr(k, ']'); if (e) base = e + 1; } size_t n = strlen(base); if (n >= cap) n = cap - 1; memcpy(kind, base, n); kind[n] = 0; - *body = data + h.body_offset; - *body_len = h.body_len > 0 ? (uint32_t)h.body_len : 0; + *body = data + KVSPACE_XVALUE_HEAD_LEN; + *body_len = len - KVSPACE_XVALUE_HEAD_LEN; return kind; } -// ── 单键读写封装(写即构造:解预编码 XValue 取 kindexpr+body,就地/新位置二选一原语) ── +// ── 单键读写封装(写:New* 构造 XValue + 显式 langtype → WriteNewPlace 顺写 attr key+body; +// 就地/新位置二选一原语)── -static inline int kv_set(void *h, const char *key, const uint8_t *val, uint32_t len) { - kvspaceHead_t hd; - if (kvspaceDecodeHead(val, len, &hd) != 0) return -1; - uint32_t bl = hd.body_len > 0 ? (uint32_t)hd.body_len : 0; - const uint8_t *body = val + hd.body_offset; +static inline int kv_set(void *h, const char *key, const char *langtype, + const uint8_t *val, uint32_t len) { + if (!val || len < KVSPACE_XVALUE_HEAD_LEN) return -1; + kvspaceXvalueHead hd; + memcpy(&hd, val, sizeof hd); + uint64_t bl = kvspaceXvalueHeadLen(&hd); uint8_t *dst = NULL; char err[256]; - if (kvspaceWriteInPlace(h, key, 1, bl, &dst, err, sizeof(err)) != 0 && - kvspaceWriteNewPlace(h, key, (const char *)hd.kindexpr, bl, &dst, err, sizeof(err)) != 0) + if (kvspaceWriteInPlace(h, key, 1, (uint32_t)bl, &dst, err, sizeof err) != 0 && + kvspaceWriteNewPlace(h, key, hd.ptr, hd.storetype, langtype, + (uint32_t)bl, bl, &dst, err, sizeof err) != 0) return -1; - if (bl && dst) memcpy(dst, body, bl); + if (bl && dst) memcpy(dst, val + KVSPACE_XVALUE_HEAD_LEN, (size_t)bl); return 0; } @@ -73,7 +82,7 @@ static inline uint8_t *kv_get(void *h, const char *key, uint32_t *len) { *len = n; return copy; } -// 前缀枚举:ListLen 定计数 + 逐 idx ListAt 取名,拼成 \n 分隔的 malloc 缓冲(调用方 free)。 +// 前缀枚举:ListLen 定计数 + 逐 idx ListAt 取名(自备缓冲),拼成 \n 分隔的 malloc 缓冲(调用方 free)。 static inline int kv_list(void *h, const char *prefix, uint8_t **out, uint32_t *out_len) { *out = NULL; *out_len = 0; int32_t count = 0; @@ -81,8 +90,8 @@ static inline int kv_list(void *h, const char *prefix, uint8_t **out, uint32_t * size_t cap = 256, used = 0; uint8_t *buf = malloc(cap); for (int32_t i = 0; i < count; i++) { - uint8_t *nm = NULL; uint32_t nl = 0; - if (kvspaceListAt(h, prefix, 0, 1, i, &nm, &nl) != 0 || !nm) continue; + uint8_t nm[1024]; uint32_t nl = 0; + if (kvspaceListAt(h, prefix, 0, 1, i, nm, sizeof(nm), &nl) != 0 || nl == 0) continue; if (used + nl + 1 > cap) { while (used + nl + 1 > cap) cap *= 2; buf = realloc(buf, cap); } if (used > 0) buf[used++] = '\n'; memcpy(buf + used, nm, nl); used += nl; diff --git a/tutorial-c/test.py b/tutorial-c/test.py index c2a5eb4..a98344a 100644 --- a/tutorial-c/test.py +++ b/tutorial-c/test.py @@ -7,7 +7,8 @@ ROOT = Path(__file__).resolve().parent BUILD = ROOT.parent / "build" / "tutorial-c" -INC = "/usr/include" +# #566:用仓库 include(固定 8 字节 head + New* 构造器);系统 /usr/include 仍是旧 wire 头。 +INC = str(ROOT.parent / "include") LIB = "/usr/lib" passed = 0