Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions deps.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"kvspace": "v0.2.6",
"kvspace-c": "v0.2.6",
"kvspace-durable": "v0.2.6",
"kvspace": "v0.2.7",
"kvspace-c": "v0.2.7",
"kvspace-durable": "v0.2.7",
"blockmalloc": "v0.1.4",
"slotsboxmalloc": "v0.1.5"
}
17 changes: 14 additions & 3 deletions layout/src/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,12 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) {
let _ = kv.mkindex(&keytree::lib_labels_dir(pkg, &fn_.sig.name));
let lpairs: Vec<(String, Vec<u8>)> = labels
.iter()
.map(|(label, irseq)| (keytree::lib_label(pkg, &fn_.sig.name, label), ffi::new_int64(*irseq as i64)))
.map(|(label, irseq)| {
(
keytree::lib_label(pkg, &fn_.sig.name, label),
ffi::new_int64(*irseq as i64),
)
})
.collect();
let _ = kv.set(&lpairs);
}
Expand Down Expand Up @@ -497,7 +502,10 @@ fn write_linear_inst(
}
_ => {}
}
let target_char = if s.writes.len() == 1 && !s.write_types.is_empty() && kvkind::is_char_kind(&s.write_types[0]) {
let target_char = if s.writes.len() == 1
&& !s.write_types.is_empty()
&& kvkind::is_char_kind(&s.write_types[0])
{
s.write_types[0].as_str()
} else {
""
Expand All @@ -508,7 +516,10 @@ fn write_linear_inst(
pairs.push((format!("{prefix}/[{n},0]"), opcode_value(&opcode)));
}
for (j, r) in reads.iter().enumerate() {
pairs.push((format!("{prefix}/[{n},-{}]", j + 1), slot_value(r, target_char)));
pairs.push((
format!("{prefix}/[{n},-{}]", j + 1),
slot_value(r, target_char),
));
}
for (j, w) in s.writes.iter().enumerate() {
pairs.push((format!("{prefix}/[{n},{}]", j + 1), slot_value(w, "")));
Expand Down
162 changes: 112 additions & 50 deletions layout/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,51 @@ pub type Handle = *mut c_void;
extern "C" {
fn kvspaceConnect(dsn: *const c_char) -> Handle;
fn kvspaceClose(h: Handle);
fn kvspaceBytesFree(p: *mut u8, len: u32);
/// codec 产出为 frontend malloc 缓冲,调用方以 libc free 释放(无 kvspaceBytesFree)。
fn free(p: *mut c_void);

fn kvspaceSet(
/// 借用读:*out 指向后端常驻/回收空间,调用方不得 free。resolve=1 穿透 link。
fn kvspaceGet(
h: Handle,
keys: *const *const c_char,
vals: *const u8,
lens: *const u32,
n: u32,
key: *const c_char,
resolve: c_int,
out: *mut *mut u8,
out_len: *mut u32,
) -> c_int;
/// 就地写:key 已存在、body_len==原 body_len → 返回原 box body 偏移指针;否则非 0。
fn kvspaceWriteInPlace(
h: Handle,
key: *const c_char,
resolve: c_int,
body_len: u32,
body: *mut *mut u8,
err: *mut c_char,
err_cap: u32,
) -> c_int;
fn kvspaceGet(h: Handle, key: *const c_char, out: *mut *mut u8, out_len: *mut u32) -> c_int;
fn kvspaceList(
/// 新位置写:按 (kindexpr, body_len) 分配新 box、写 head,返回 body 偏移指针。
fn kvspaceWriteNewPlace(
h: Handle,
key: *const c_char,
kindexpr: *const c_char,
body_len: u32,
body: *mut *mut u8,
err: *mut c_char,
err_cap: u32,
) -> c_int;
/// 前缀遍历:listlen 定计数,逐 idx 取名(借用回收缓冲,不得 free),不一次性返回整段名单。
fn kvspaceListLen(
h: Handle,
prefix: *const c_char,
expand_ext: c_int,
resolve: c_int,
out_count: *mut i32,
) -> c_int;
fn kvspaceListAt(
h: Handle,
prefix: *const c_char,
expand_ext: c_int,
resolve: c_int,
idx: i32,
out: *mut *mut u8,
out_len: *mut u32,
) -> c_int;
Expand Down Expand Up @@ -106,26 +134,28 @@ fn err_ret(buf: &mut [c_char; 256], ret: c_int) -> Result<(), String> {
})
}

/// 调用带 (out, out_len) 输出参数的 extern fn,返回分配的字节并释放
fn call_alloc(f: impl FnOnce(*mut *mut u8, *mut u32) -> c_int) -> Vec<u8> {
/// codec 调用:产出 frontend malloc 缓冲,拷出后以 libc free 释放
fn call_codec(f: impl FnOnce(*mut *mut u8, *mut u32) -> c_int) -> Vec<u8> {
let mut out: *mut u8 = std::ptr::null_mut();
let mut out_len: u32 = 0;
f(&mut out, &mut out_len);
if out.is_null() || out_len == 0 {
return Vec::new();
}
let bytes = unsafe { std::slice::from_raw_parts(out, out_len as usize) }.to_vec();
unsafe { kvspaceBytesFree(out, out_len) };
unsafe { free(out as *mut c_void) };
bytes
}

fn to_cstrings(ss: &[String]) -> (Vec<CString>, Vec<*const c_char>) {
let cs: Vec<CString> = ss
.iter()
.map(|s| CString::new(s.as_str()).expect("no NUL in key"))
.collect();
let ptrs: Vec<*const c_char> = cs.iter().map(|c| c.as_ptr()).collect();
(cs, ptrs)
/// 借用调用:*out 指向后端常驻/回收空间,拷出自持(借用只需活到本次拷贝),不 free。
fn call_borrow(f: impl FnOnce(*mut *mut u8, *mut u32) -> c_int) -> Vec<u8> {
let mut out: *mut u8 = std::ptr::null_mut();
let mut out_len: u32 = 0;
f(&mut out, &mut out_len);
if out.is_null() || out_len == 0 {
return Vec::new();
}
unsafe { std::slice::from_raw_parts(out, out_len as usize) }.to_vec()
}

// ── 安全句柄 ─────────────────────────────────────────────────────────
Expand All @@ -142,55 +172,87 @@ impl Kv {
Kv { h }
}

/// 写:pairs 的值为预编码 TLV;逐条解 head 取 (kindexpr, body),经 WriteNewPlace
/// 向 kvspace 要 body 偏移指针后直接写入 body 字节(新建/换 kind/换尺寸唯一原语)。
pub fn set(&mut self, pairs: &[(String, Vec<u8>)]) -> Result<(), String> {
let keys: Vec<String> = pairs.iter().map(|(k, _)| k.clone()).collect();
let (_cs, key_ptrs) = to_cstrings(&keys);
let mut vals: Vec<u8> = Vec::new();
let mut lens: Vec<u32> = Vec::new();
for (_, v) in pairs {
vals.extend_from_slice(v);
lens.push(v.len() as u32);
for (key, tlv) in pairs {
self.write_new_place(key, tlv)?;
}
Ok(())
}

fn write_new_place(&mut self, key: &str, tlv: &[u8]) -> Result<(), String> {
let h = decode_head(tlv);
let klen = h.kindexpr.iter().position(|&b| b == 0).unwrap_or(0);
let kindexpr = CString::new(&h.kindexpr[..klen]).expect("no NUL in kindexpr");
let ck = CString::new(key).expect("no NUL in key");
let body_off = h.body_offset as usize;
let body_len = h.body_len.max(0) as usize;
let mut body: *mut u8 = std::ptr::null_mut();
let mut err: [c_char; 256] = [0; 256];
let ret = unsafe {
kvspaceSet(
kvspaceWriteNewPlace(
self.h,
key_ptrs.as_ptr(),
vals.as_ptr(),
lens.as_ptr(),
lens.len() as u32,
ck.as_ptr(),
kindexpr.as_ptr(),
body_len as u32,
&mut body,
err.as_mut_ptr(),
err.len() as u32,
)
};
err_ret(&mut err, ret)
err_ret(&mut err, ret)?;
if body_len > 0 {
if body.is_null() {
return Err(format!("kvspace: WriteNewPlace null body at {key}"));
}
unsafe {
std::ptr::copy_nonoverlapping(tlv[body_off..].as_ptr(), body, body_len);
}
}
Ok(())
}

/// 单点读:None 返回空字节。
/// 单点读(借用后拷出自持):None 返回空字节。resolve=1 穿透 link
pub fn get_one(&mut self, key: &str) -> Vec<u8> {
let c = CString::new(key).expect("no NUL in key");
call_alloc(|out, out_len| unsafe { kvspaceGet(self.h, c.as_ptr(), out, out_len) })
call_borrow(|out, out_len| unsafe { kvspaceGet(self.h, c.as_ptr(), 0, out, out_len) })
}

pub fn list(&mut self, prefix: &str, expand_ext: bool, resolve: bool) -> Vec<String> {
let c = CString::new(prefix).expect("no NUL in prefix");
let bytes = call_alloc(|out, out_len| unsafe {
kvspaceList(
let mut count: i32 = 0;
if unsafe {
kvspaceListLen(
self.h,
c.as_ptr(),
expand_ext as c_int,
resolve as c_int,
out,
out_len,
&mut count,
)
});
if bytes.is_empty() {
} != 0
|| count <= 0
{
return Vec::new();
}
String::from_utf8_lossy(&bytes)
.split('\n')
.map(|s| s.to_string())
.collect()
let mut v = Vec::with_capacity(count as usize);
for i in 0..count {
let bytes = call_borrow(|out, out_len| unsafe {
kvspaceListAt(
self.h,
c.as_ptr(),
expand_ext as c_int,
resolve as c_int,
i,
out,
out_len,
)
});
if !bytes.is_empty() {
v.push(String::from_utf8_lossy(&bytes).into_owned());
}
}
v
}

pub fn del_tree(&mut self, prefix: &str) -> Result<(), String> {
Expand Down Expand Up @@ -255,7 +317,7 @@ fn al_to_dims(kind: &str, array_len: i32) -> Vec<i32> {
pub fn tlv_encode(kind: &str, raw: &[u8], array_len: i32) -> Vec<u8> {
let ck = CString::new(kind).expect("no NUL in kind");
let dims = al_to_dims(kind, array_len);
call_alloc(|out, out_len| unsafe {
call_codec(|out, out_len| unsafe {
kvspaceTlvEncode(
ck.as_ptr(),
raw.as_ptr(),
Expand Down Expand Up @@ -288,7 +350,7 @@ pub fn decode_head(data: &[u8]) -> kvspaceHead_t {
pub fn new_ptr(target_kindexpr: &str, target: &str) -> Vec<u8> {
let ck = CString::new(target_kindexpr).expect("no NUL");
let ct = CString::new(target).expect("no NUL");
call_alloc(|out, out_len| unsafe { kvspaceNewPtr(ck.as_ptr(), ct.as_ptr(), out, out_len) })
call_codec(|out, out_len| unsafe { kvspaceNewPtr(ck.as_ptr(), ct.as_ptr(), out, out_len) })
}

pub fn new_char(kind: &str, s: &str) -> Vec<u8> {
Expand All @@ -305,7 +367,7 @@ pub fn new_char(kind: &str, s: &str) -> Vec<u8> {
};
let ck = CString::new(kind).expect("no NUL");
let dims = [n];
call_alloc(|out, out_len| unsafe {
call_codec(|out, out_len| unsafe {
kvspaceTlvEncode(
ck.as_ptr(),
raw.as_ptr(),
Expand All @@ -319,19 +381,19 @@ pub fn new_char(kind: &str, s: &str) -> Vec<u8> {
}

pub fn new_char_byte(bytes: &[u8]) -> Vec<u8> {
call_alloc(|out, out_len| unsafe {
call_codec(|out, out_len| unsafe {
kvspaceNewChar(bytes.as_ptr(), bytes.len() as u32, out, out_len)
})
}

pub fn new_bool(v: bool) -> Vec<u8> {
call_alloc(|out, out_len| unsafe { kvspaceNewBool(v as u8, out, out_len) })
call_codec(|out, out_len| unsafe { kvspaceNewBool(v as u8, out, out_len) })
}

pub fn new_int64(v: i64) -> Vec<u8> {
call_alloc(|out, out_len| unsafe { kvspaceNewInt64(v, out, out_len) })
call_codec(|out, out_len| unsafe { kvspaceNewInt64(v, out, out_len) })
}

pub fn new_float64(v: f64) -> Vec<u8> {
call_alloc(|out, out_len| unsafe { kvspaceNewFloat64(v, out, out_len) })
call_codec(|out, out_len| unsafe { kvspaceNewFloat64(v, out, out_len) })
}
7 changes: 6 additions & 1 deletion layout/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,12 @@ pub fn lower_func(fn_: &Func) -> Func {
let mut targets = HashSet::new();
collect_goto_targets(&fn_.body, &mut targets);
let body = terminate(lower_body(&fn_.body, &mut lg, None, &tm, &targets));
Func { comments: Vec::new(), sig: fn_.sig.clone(), body, pkg: String::new() }
Func {
comments: Vec::new(),
sig: fn_.sig.clone(),
body,
pkg: String::new(),
}
}

fn return_inst() -> Stmt {
Expand Down
Loading
Loading