smokes: add curated LLVM runner; archive legacy smokes; PHI-off unified across Bridge/Builder; LLVM resolver tracing; minimal Throw lowering; config env getters; dev profile and root cleaner; docs updated; CI workflow runs curated LLVM (PHI-on/off)
This commit is contained in:
@ -3,9 +3,12 @@
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Mutex, atomic::{AtomicU32, Ordering}};
|
||||
use std::os::raw::c_char;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
// ===== Error Codes (aligned with existing plugins) =====
|
||||
const NYB_SUCCESS: i32 = 0;
|
||||
@ -17,27 +20,34 @@ const NYB_E_PLUGIN_ERROR: i32 = -5;
|
||||
const NYB_E_INVALID_HANDLE: i32 = -8;
|
||||
|
||||
// ===== Method IDs =====
|
||||
const METHOD_BIRTH: u32 = 0; // constructor -> returns instance_id (u32 LE, no TLV)
|
||||
const METHOD_LENGTH: u32 = 1; // returns TLV i64
|
||||
const METHOD_GET: u32 = 2; // args: i64 index -> returns TLV i64
|
||||
const METHOD_PUSH: u32 = 3; // args: i64 value -> returns TLV i64 (new length)
|
||||
const METHOD_SET: u32 = 4; // args: i64 index, i64 value -> returns TLV i64 (new length)
|
||||
const METHOD_FINI: u32 = u32::MAX; // destructor
|
||||
const METHOD_BIRTH: u32 = 0; // constructor -> returns instance_id (u32 LE, no TLV)
|
||||
const METHOD_LENGTH: u32 = 1; // returns TLV i64
|
||||
const METHOD_GET: u32 = 2; // args: i64 index -> returns TLV i64
|
||||
const METHOD_PUSH: u32 = 3; // args: i64 value -> returns TLV i64 (new length)
|
||||
const METHOD_SET: u32 = 4; // args: i64 index, i64 value -> returns TLV i64 (new length)
|
||||
const METHOD_FINI: u32 = u32::MAX; // destructor
|
||||
|
||||
// Assign a unique type_id for ArrayBox (as declared in nyash.toml)
|
||||
const TYPE_ID_ARRAY: u32 = 10;
|
||||
|
||||
// ===== Instance state (PoC: store i64 values only) =====
|
||||
struct ArrayInstance { data: Vec<i64> }
|
||||
struct ArrayInstance {
|
||||
data: Vec<i64>,
|
||||
}
|
||||
|
||||
static INSTANCES: Lazy<Mutex<HashMap<u32, ArrayInstance>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static INSTANCES: Lazy<Mutex<HashMap<u32, ArrayInstance>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static INSTANCE_COUNTER: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 { NYB_SUCCESS }
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 {
|
||||
NYB_SUCCESS
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_invoke(
|
||||
@ -49,17 +59,25 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
if type_id != TYPE_ID_ARRAY { return NYB_E_INVALID_TYPE; }
|
||||
if type_id != TYPE_ID_ARRAY {
|
||||
return NYB_E_INVALID_TYPE;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
match method_id {
|
||||
METHOD_BIRTH => {
|
||||
if result_len.is_null() { return NYB_E_INVALID_ARGS; }
|
||||
if preflight(result, result_len, 4) { return NYB_E_SHORT_BUFFER; }
|
||||
if result_len.is_null() {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
if preflight(result, result_len, 4) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
let id = INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
map.insert(id, ArrayInstance { data: Vec::new() });
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
let bytes = id.to_le_bytes();
|
||||
std::ptr::copy_nonoverlapping(bytes.as_ptr(), result, 4);
|
||||
*result_len = 4;
|
||||
@ -69,39 +87,71 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
map.remove(&instance_id);
|
||||
NYB_SUCCESS
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_LENGTH => {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
return write_tlv_i64(inst.data.len() as i64, result, result_len);
|
||||
} else { return NYB_E_INVALID_HANDLE; }
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
METHOD_GET => {
|
||||
let idx = match read_arg_i64(args, args_len, 0) { Some(v) => v, None => return NYB_E_INVALID_ARGS };
|
||||
if idx < 0 { return NYB_E_INVALID_ARGS; }
|
||||
let idx = match read_arg_i64(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
if idx < 0 {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
let i = idx as usize;
|
||||
if i >= inst.data.len() { return NYB_E_INVALID_ARGS; }
|
||||
if i >= inst.data.len() {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
return write_tlv_i64(inst.data[i], result, result_len);
|
||||
} else { return NYB_E_INVALID_HANDLE; }
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
METHOD_PUSH => {
|
||||
let val = match read_arg_i64(args, args_len, 0) { Some(v) => v, None => return NYB_E_INVALID_ARGS };
|
||||
let val = match read_arg_i64(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get_mut(&instance_id) {
|
||||
inst.data.push(val);
|
||||
return write_tlv_i64(inst.data.len() as i64, result, result_len);
|
||||
} else { return NYB_E_INVALID_HANDLE; }
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
METHOD_SET => {
|
||||
let idx = match read_arg_i64(args, args_len, 0) { Some(v) => v, None => return NYB_E_INVALID_ARGS };
|
||||
let val = match read_arg_i64(args, args_len, 1) { Some(v) => v, None => return NYB_E_INVALID_ARGS };
|
||||
if idx < 0 { return NYB_E_INVALID_ARGS; }
|
||||
let idx = match read_arg_i64(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
let val = match read_arg_i64(args, args_len, 1) {
|
||||
Some(v) => v,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
if idx < 0 {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get_mut(&instance_id) {
|
||||
let i = idx as usize;
|
||||
@ -114,8 +164,12 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
return write_tlv_i64(inst.data.len() as i64, result, result_len);
|
||||
} else { return NYB_E_INVALID_HANDLE; }
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
_ => NYB_E_INVALID_METHOD,
|
||||
}
|
||||
@ -136,7 +190,9 @@ pub struct NyashTypeBoxFfi {
|
||||
unsafe impl Sync for NyashTypeBoxFfi {}
|
||||
|
||||
extern "C" fn array_resolve(name: *const c_char) -> u32 {
|
||||
if name.is_null() { return 0; }
|
||||
if name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||
match s.as_ref() {
|
||||
"len" | "length" => METHOD_LENGTH,
|
||||
@ -147,26 +203,95 @@ extern "C" fn array_resolve(name: *const c_char) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn array_invoke_id(instance_id: u32, method_id: u32, args: *const u8, args_len: usize, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
extern "C" fn array_invoke_id(
|
||||
instance_id: u32,
|
||||
method_id: u32,
|
||||
args: *const u8,
|
||||
args_len: usize,
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
unsafe {
|
||||
match method_id {
|
||||
METHOD_LENGTH => {
|
||||
if let Ok(map) = INSTANCES.lock() { if let Some(inst) = map.get(&instance_id) { return write_tlv_i64(inst.data.len() as i64, result, result_len); } else { return NYB_E_INVALID_HANDLE; } } else { return NYB_E_PLUGIN_ERROR; }
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
return write_tlv_i64(inst.data.len() as i64, result, result_len);
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
METHOD_GET => {
|
||||
let idx = match read_arg_i64(args, args_len, 0) { Some(v) => v, None => return NYB_E_INVALID_ARGS };
|
||||
if idx < 0 { return NYB_E_INVALID_ARGS; }
|
||||
if let Ok(map) = INSTANCES.lock() { if let Some(inst) = map.get(&instance_id) { let i = idx as usize; if i >= inst.data.len() { return NYB_E_INVALID_ARGS; } return write_tlv_i64(inst.data[i], result, result_len); } else { return NYB_E_INVALID_HANDLE; } } else { return NYB_E_PLUGIN_ERROR; }
|
||||
let idx = match read_arg_i64(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
if idx < 0 {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
let i = idx as usize;
|
||||
if i >= inst.data.len() {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
return write_tlv_i64(inst.data[i], result, result_len);
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
METHOD_SET => {
|
||||
let idx = match read_arg_i64(args, args_len, 0) { Some(v) => v, None => return NYB_E_INVALID_ARGS };
|
||||
let val = match read_arg_i64(args, args_len, 1) { Some(v) => v, None => return NYB_E_INVALID_ARGS };
|
||||
if idx < 0 { return NYB_E_INVALID_ARGS; }
|
||||
if let Ok(mut map) = INSTANCES.lock() { if let Some(inst) = map.get_mut(&instance_id) { let i = idx as usize; let len = inst.data.len(); if i < len { inst.data[i] = val; } else if i == len { inst.data.push(val); } else { return NYB_E_INVALID_ARGS; } return write_tlv_i64(inst.data.len() as i64, result, result_len); } else { return NYB_E_INVALID_HANDLE; } } else { return NYB_E_PLUGIN_ERROR; }
|
||||
let idx = match read_arg_i64(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
let val = match read_arg_i64(args, args_len, 1) {
|
||||
Some(v) => v,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
if idx < 0 {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get_mut(&instance_id) {
|
||||
let i = idx as usize;
|
||||
let len = inst.data.len();
|
||||
if i < len {
|
||||
inst.data[i] = val;
|
||||
} else if i == len {
|
||||
inst.data.push(val);
|
||||
} else {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
return write_tlv_i64(inst.data.len() as i64, result, result_len);
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
METHOD_PUSH => {
|
||||
let val = match read_arg_i64(args, args_len, 0) { Some(v) => v, None => return NYB_E_INVALID_ARGS };
|
||||
if let Ok(mut map) = INSTANCES.lock() { if let Some(inst) = map.get_mut(&instance_id) { inst.data.push(val); return write_tlv_i64(inst.data.len() as i64, result, result_len); } else { return NYB_E_INVALID_HANDLE; } } else { return NYB_E_PLUGIN_ERROR; }
|
||||
let val = match read_arg_i64(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get_mut(&instance_id) {
|
||||
inst.data.push(val);
|
||||
return write_tlv_i64(inst.data.len() as i64, result, result_len);
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
_ => NYB_E_INVALID_METHOD,
|
||||
}
|
||||
@ -187,7 +312,9 @@ pub static nyash_typebox_ArrayBox: NyashTypeBoxFfi = NyashTypeBoxFfi {
|
||||
// ===== Minimal TLV helpers (compatible with host expectations) =====
|
||||
fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
unsafe {
|
||||
if result_len.is_null() { return false; }
|
||||
if result_len.is_null() {
|
||||
return false;
|
||||
}
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return true;
|
||||
@ -197,8 +324,11 @@ fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
}
|
||||
|
||||
fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
if result_len.is_null() { return NYB_E_INVALID_ARGS; }
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(4 + payloads.iter().map(|(_,p)| 4 + p.len()).sum::<usize>());
|
||||
if result_len.is_null() {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
let mut buf: Vec<u8> =
|
||||
Vec::with_capacity(4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes()); // version
|
||||
buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes()); // argc
|
||||
for (tag, payload) in payloads {
|
||||
@ -225,19 +355,27 @@ fn write_tlv_i64(v: i64, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
|
||||
/// Read nth TLV argument as i64 (tag 3)
|
||||
fn read_arg_i64(args: *const u8, args_len: usize, n: usize) -> Option<i64> {
|
||||
if args.is_null() || args_len < 4 { return None; }
|
||||
if args.is_null() || args_len < 4 {
|
||||
return None;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize; // skip header
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 { return None; }
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let _rsv = buf[off+1];
|
||||
let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize;
|
||||
if buf.len() < off + 4 + size { return None; }
|
||||
let _rsv = buf[off + 1];
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
if tag != 3 || size != 8 { return None; }
|
||||
let mut b = [0u8;8];
|
||||
b.copy_from_slice(&buf[off+4 .. off+4+8]);
|
||||
if tag != 3 || size != 8 {
|
||||
return None;
|
||||
}
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(&buf[off + 4..off + 4 + 8]);
|
||||
return Some(i64::from_le_bytes(b));
|
||||
}
|
||||
off += 4 + size;
|
||||
|
||||
@ -2,9 +2,12 @@
|
||||
//! Provides simple stdout printing via ConsoleBox
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::os::raw::c_char;
|
||||
use std::sync::{Mutex, atomic::{AtomicU32, Ordering}};
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
// ===== Error Codes (BID-1) =====
|
||||
const NYB_SUCCESS: i32 = 0;
|
||||
@ -16,7 +19,7 @@ const NYB_E_PLUGIN_ERROR: i32 = -5;
|
||||
|
||||
// ===== Method IDs =====
|
||||
const METHOD_BIRTH: u32 = 0;
|
||||
const METHOD_LOG: u32 = 1; // log(text)
|
||||
const METHOD_LOG: u32 = 1; // log(text)
|
||||
const METHOD_PRINTLN: u32 = 2; // println(text)
|
||||
const METHOD_FINI: u32 = u32::MAX;
|
||||
|
||||
@ -24,58 +27,126 @@ const METHOD_FINI: u32 = u32::MAX;
|
||||
const TYPE_ID_CONSOLE_BOX: u32 = 5; // keep in sync with nyash.toml [box_types]
|
||||
|
||||
// ===== Instance management =====
|
||||
struct ConsoleInstance { /* no state for now */ }
|
||||
struct ConsoleInstance {/* no state for now */}
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
static INSTANCES: Lazy<Mutex<HashMap<u32, ConsoleInstance>>> = Lazy::new(|| {
|
||||
Mutex::new(HashMap::new())
|
||||
});
|
||||
static INSTANCES: Lazy<Mutex<HashMap<u32, ConsoleInstance>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static INSTANCE_COUNTER: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
// ===== TLV helpers (minimal) =====
|
||||
// TLV layout: [u16 ver=1][u16 argc][entries...]
|
||||
// Entry: [u16 tag][u16 size][payload...]
|
||||
fn parse_first_string(args: &[u8]) -> Result<String, ()> {
|
||||
if args.len() < 4 { return Err(()); }
|
||||
let argc = u16::from_le_bytes([args[2], args[3]]) as usize;
|
||||
if argc == 0 { return Err(()); }
|
||||
let mut p = 4usize;
|
||||
// first entry
|
||||
if args.len() < p + 4 { return Err(()); }
|
||||
let tag = u16::from_le_bytes([args[p], args[p+1]]); p += 2;
|
||||
let sz = u16::from_le_bytes([args[p], args[p+1]]) as usize; p += 2;
|
||||
if tag != 6 && tag != 7 { // String or Bytes
|
||||
if args.len() < 4 {
|
||||
return Err(());
|
||||
}
|
||||
if args.len() < p + sz { return Err(()); }
|
||||
let s = String::from_utf8_lossy(&args[p..p+sz]).to_string();
|
||||
let argc = u16::from_le_bytes([args[2], args[3]]) as usize;
|
||||
if argc == 0 {
|
||||
return Err(());
|
||||
}
|
||||
let mut p = 4usize;
|
||||
// first entry
|
||||
if args.len() < p + 4 {
|
||||
return Err(());
|
||||
}
|
||||
let tag = u16::from_le_bytes([args[p], args[p + 1]]);
|
||||
p += 2;
|
||||
let sz = u16::from_le_bytes([args[p], args[p + 1]]) as usize;
|
||||
p += 2;
|
||||
if tag != 6 && tag != 7 {
|
||||
// String or Bytes
|
||||
return Err(());
|
||||
}
|
||||
if args.len() < p + sz {
|
||||
return Err(());
|
||||
}
|
||||
let s = String::from_utf8_lossy(&args[p..p + sz]).to_string();
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
fn format_first_any(args: &[u8]) -> Option<String> {
|
||||
if args.len() < 4 { return None; }
|
||||
if args.len() < 4 {
|
||||
return None;
|
||||
}
|
||||
let mut p = 4usize;
|
||||
if args.len() < p + 4 { return None; }
|
||||
let tag = u16::from_le_bytes([args[p], args[p+1]]); p += 2;
|
||||
let sz = u16::from_le_bytes([args[p], args[p+1]]) as usize; p += 2;
|
||||
if args.len() < p + sz { return None; }
|
||||
let payload = &args[p..p+sz];
|
||||
if args.len() < p + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = u16::from_le_bytes([args[p], args[p + 1]]);
|
||||
p += 2;
|
||||
let sz = u16::from_le_bytes([args[p], args[p + 1]]) as usize;
|
||||
p += 2;
|
||||
if args.len() < p + sz {
|
||||
return None;
|
||||
}
|
||||
let payload = &args[p..p + sz];
|
||||
match tag {
|
||||
1 => Some(if sz>0 && payload[0]!=0 { "true".into() } else { "false".into() }),
|
||||
2 => { if sz!=4 { None } else { let mut b=[0u8;4]; b.copy_from_slice(payload); Some((i32::from_le_bytes(b)).to_string()) } },
|
||||
3 => { if sz!=8 { None } else { let mut b=[0u8;8]; b.copy_from_slice(payload); Some((i64::from_le_bytes(b)).to_string()) } },
|
||||
5 => { if sz!=8 { None } else { let mut b=[0u8;8]; b.copy_from_slice(payload); Some(f64::from_le_bytes(b).to_string()) } },
|
||||
6 => { std::str::from_utf8(payload).ok().map(|s| s.to_string()) },
|
||||
1 => Some(if sz > 0 && payload[0] != 0 {
|
||||
"true".into()
|
||||
} else {
|
||||
"false".into()
|
||||
}),
|
||||
2 => {
|
||||
if sz != 4 {
|
||||
None
|
||||
} else {
|
||||
let mut b = [0u8; 4];
|
||||
b.copy_from_slice(payload);
|
||||
Some((i32::from_le_bytes(b)).to_string())
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
if sz != 8 {
|
||||
None
|
||||
} else {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
Some((i64::from_le_bytes(b)).to_string())
|
||||
}
|
||||
}
|
||||
5 => {
|
||||
if sz != 8 {
|
||||
None
|
||||
} else {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
Some(f64::from_le_bytes(b).to_string())
|
||||
}
|
||||
}
|
||||
6 => std::str::from_utf8(payload).ok().map(|s| s.to_string()),
|
||||
7 => Some(format!("<bytes:{}>", sz)),
|
||||
8 => { if sz==8 { let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); Some(format!("<handle {}:{}>", u32::from_le_bytes(t), u32::from_le_bytes(i))) } else { None } },
|
||||
8 => {
|
||||
if sz == 8 {
|
||||
let mut t = [0u8; 4];
|
||||
t.copy_from_slice(&payload[0..4]);
|
||||
let mut i = [0u8; 4];
|
||||
i.copy_from_slice(&payload[4..8]);
|
||||
Some(format!(
|
||||
"<handle {}:{}>",
|
||||
u32::from_le_bytes(t),
|
||||
u32::from_le_bytes(i)
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Write TLV birth result: Handle(tag=8,size=8) with (type_id, instance_id)
|
||||
unsafe fn write_tlv_birth(type_id: u32, instance_id: u32, out: *mut u8, out_len: *mut usize) -> i32 {
|
||||
unsafe fn write_tlv_birth(
|
||||
type_id: u32,
|
||||
instance_id: u32,
|
||||
out: *mut u8,
|
||||
out_len: *mut usize,
|
||||
) -> i32 {
|
||||
let need = 4 + 4 + 8; // header + entry + payload
|
||||
if *out_len < need { *out_len = need; return NYB_E_SHORT_BUFFER; }
|
||||
if *out_len < need {
|
||||
*out_len = need;
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
let mut buf = Vec::with_capacity(need);
|
||||
// header
|
||||
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||
@ -92,7 +163,10 @@ unsafe fn write_tlv_birth(type_id: u32, instance_id: u32, out: *mut u8, out_len:
|
||||
|
||||
unsafe fn write_tlv_void(out: *mut u8, out_len: *mut usize) -> i32 {
|
||||
let need = 4 + 4; // header + entry
|
||||
if *out_len < need { *out_len = need; return NYB_E_SHORT_BUFFER; }
|
||||
if *out_len < need {
|
||||
*out_len = need;
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
let mut buf = Vec::with_capacity(need);
|
||||
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||
@ -105,7 +179,9 @@ unsafe fn write_tlv_void(out: *mut u8, out_len: *mut usize) -> i32 {
|
||||
|
||||
// ===== Entry points =====
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 {
|
||||
@ -123,18 +199,24 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
if type_id != TYPE_ID_CONSOLE_BOX { return NYB_E_INVALID_TYPE; }
|
||||
if type_id != TYPE_ID_CONSOLE_BOX {
|
||||
return NYB_E_INVALID_TYPE;
|
||||
}
|
||||
unsafe {
|
||||
match method_id {
|
||||
METHOD_BIRTH => {
|
||||
let id = INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut m) = INSTANCES.lock() {
|
||||
m.insert(id, ConsoleInstance{});
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
m.insert(id, ConsoleInstance {});
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
return write_tlv_birth(TYPE_ID_CONSOLE_BOX, id, result, result_len);
|
||||
}
|
||||
METHOD_FINI => {
|
||||
if let Ok(mut m) = INSTANCES.lock() { m.remove(&instance_id); }
|
||||
if let Ok(mut m) = INSTANCES.lock() {
|
||||
m.remove(&instance_id);
|
||||
}
|
||||
return NYB_SUCCESS;
|
||||
}
|
||||
METHOD_LOG | METHOD_PRINTLN => {
|
||||
@ -143,7 +225,11 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
Ok(s) => s,
|
||||
Err(_) => format_first_any(slice).unwrap_or_else(|| "".to_string()),
|
||||
};
|
||||
if method_id == METHOD_LOG { print!("{}", s); } else { println!("{}", s); }
|
||||
if method_id == METHOD_LOG {
|
||||
print!("{}", s);
|
||||
} else {
|
||||
println!("{}", s);
|
||||
}
|
||||
return write_tlv_void(result, result_len);
|
||||
}
|
||||
_ => NYB_E_INVALID_METHOD,
|
||||
@ -165,7 +251,9 @@ pub struct NyashTypeBoxFfi {
|
||||
unsafe impl Sync for NyashTypeBoxFfi {}
|
||||
|
||||
extern "C" fn console_resolve(name: *const c_char) -> u32 {
|
||||
if name.is_null() { return 0; }
|
||||
if name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||
match s.as_ref() {
|
||||
"log" => METHOD_LOG,
|
||||
@ -174,7 +262,14 @@ extern "C" fn console_resolve(name: *const c_char) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn console_invoke_id(instance_id: u32, method_id: u32, args: *const u8, args_len: usize, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
extern "C" fn console_invoke_id(
|
||||
instance_id: u32,
|
||||
method_id: u32,
|
||||
args: *const u8,
|
||||
args_len: usize,
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
unsafe {
|
||||
match method_id {
|
||||
METHOD_LOG | METHOD_PRINTLN => {
|
||||
@ -183,7 +278,11 @@ extern "C" fn console_invoke_id(instance_id: u32, method_id: u32, args: *const u
|
||||
Ok(s) => s,
|
||||
Err(_) => format_first_any(slice).unwrap_or_else(|| "".to_string()),
|
||||
};
|
||||
if method_id == METHOD_LOG { print!("{}", s); } else { println!("{}", s); }
|
||||
if method_id == METHOD_LOG {
|
||||
print!("{}", s);
|
||||
} else {
|
||||
println!("{}", s);
|
||||
}
|
||||
return write_tlv_void(result, result_len);
|
||||
}
|
||||
_ => NYB_E_INVALID_METHOD,
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
//! Nyash CounterBox Plugin - BID-FFI v1 Implementation
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Mutex, atomic::{AtomicU32, Ordering}};
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
@ -15,25 +18,32 @@ const NYB_E_PLUGIN_ERROR: i32 = -5;
|
||||
const NYB_E_INVALID_HANDLE: i32 = -8;
|
||||
|
||||
// ===== Method IDs =====
|
||||
const METHOD_BIRTH: u32 = 0; // constructor
|
||||
const METHOD_INC: u32 = 1; // increments and returns new count
|
||||
const METHOD_GET: u32 = 2; // returns current count
|
||||
const METHOD_FINI: u32 = u32::MAX; // destructor
|
||||
const METHOD_BIRTH: u32 = 0; // constructor
|
||||
const METHOD_INC: u32 = 1; // increments and returns new count
|
||||
const METHOD_GET: u32 = 2; // returns current count
|
||||
const METHOD_FINI: u32 = u32::MAX; // destructor
|
||||
|
||||
// Assign a unique type_id for CounterBox (distinct from FileBox=6)
|
||||
const TYPE_ID_COUNTER: u32 = 7;
|
||||
|
||||
// ===== Instance state =====
|
||||
struct CounterInstance { count: i32 }
|
||||
struct CounterInstance {
|
||||
count: i32,
|
||||
}
|
||||
|
||||
static INSTANCES: Lazy<Mutex<HashMap<u32, CounterInstance>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static INSTANCES: Lazy<Mutex<HashMap<u32, CounterInstance>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static INSTANCE_COUNTER: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 { NYB_SUCCESS }
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 {
|
||||
NYB_SUCCESS
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_invoke(
|
||||
@ -45,18 +55,26 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
if type_id != TYPE_ID_COUNTER { return NYB_E_INVALID_TYPE; }
|
||||
if type_id != TYPE_ID_COUNTER {
|
||||
return NYB_E_INVALID_TYPE;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
match method_id {
|
||||
METHOD_BIRTH => {
|
||||
// Return new instance handle (u32 id)
|
||||
if result_len.is_null() { return NYB_E_INVALID_ARGS; }
|
||||
if preflight(result, result_len, 4) { return NYB_E_SHORT_BUFFER; }
|
||||
if result_len.is_null() {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
if preflight(result, result_len, 4) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
let id = INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
map.insert(id, CounterInstance { count: 0 });
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
let bytes = id.to_le_bytes();
|
||||
std::ptr::copy_nonoverlapping(bytes.as_ptr(), result, 4);
|
||||
*result_len = 4;
|
||||
@ -66,7 +84,9 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
map.remove(&instance_id);
|
||||
NYB_SUCCESS
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_INC => {
|
||||
// increments and returns new count as I32 TLV
|
||||
@ -74,18 +94,30 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
if let Some(inst) = map.get_mut(&instance_id) {
|
||||
inst.count += 1;
|
||||
let v = inst.count;
|
||||
if preflight(result, result_len, 12) { return NYB_E_SHORT_BUFFER; }
|
||||
if preflight(result, result_len, 12) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
return write_tlv_i32(v, result, result_len);
|
||||
} else { return NYB_E_INVALID_HANDLE; }
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
METHOD_GET => {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
if preflight(result, result_len, 12) { return NYB_E_SHORT_BUFFER; }
|
||||
if preflight(result, result_len, 12) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
return write_tlv_i32(inst.count, result, result_len);
|
||||
} else { return NYB_E_INVALID_HANDLE; }
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
_ => NYB_E_INVALID_METHOD,
|
||||
}
|
||||
@ -94,8 +126,11 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
|
||||
// ===== TLV helpers =====
|
||||
fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
if result_len.is_null() { return NYB_E_INVALID_ARGS; }
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(4 + payloads.iter().map(|(_,p)| 4 + p.len()).sum::<usize>());
|
||||
if result_len.is_null() {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
let mut buf: Vec<u8> =
|
||||
Vec::with_capacity(4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes()); // version
|
||||
buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes()); // argc
|
||||
for (tag, payload) in payloads {
|
||||
@ -122,7 +157,9 @@ fn write_tlv_i32(v: i32, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
|
||||
fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
unsafe {
|
||||
if result_len.is_null() { return false; }
|
||||
if result_len.is_null() {
|
||||
return false;
|
||||
}
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return true;
|
||||
@ -130,4 +167,3 @@ fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,13 @@
|
||||
//! - Windows GUI integration (egui/eframe) can be enabled later via `with-egui` feature
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{collections::HashMap, sync::{Mutex, atomic::{AtomicU32, Ordering}}};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
},
|
||||
};
|
||||
|
||||
// ===== Error/Status codes (BID-FFI v1 aligned) =====
|
||||
const OK: i32 = 0;
|
||||
@ -18,12 +24,12 @@ const TID_EGUI: u32 = 70; // match nyash.toml [box_types]
|
||||
|
||||
// methods
|
||||
const M_BIRTH: u32 = 0;
|
||||
const M_OPEN: u32 = 1; // open(width:int, height:int, title:str)
|
||||
const M_UI_LABEL: u32 = 2; // uiLabel(text:str)
|
||||
const M_UI_BUTTON: u32 = 3; // uiButton(text:str) -> future: events
|
||||
const M_OPEN: u32 = 1; // open(width:int, height:int, title:str)
|
||||
const M_UI_LABEL: u32 = 2; // uiLabel(text:str)
|
||||
const M_UI_BUTTON: u32 = 3; // uiButton(text:str) -> future: events
|
||||
const M_POLL_EVENT: u32 = 4; // pollEvent() -> Result.Ok(text) / Result.Err("none")
|
||||
const M_RUN: u32 = 5; // run() -> enters loop or no-op
|
||||
const M_CLOSE: u32 = 6; // close()
|
||||
const M_RUN: u32 = 5; // run() -> enters loop or no-op
|
||||
const M_CLOSE: u32 = 6; // close()
|
||||
const M_FINI: u32 = u32::MAX;
|
||||
|
||||
#[derive(Default)]
|
||||
@ -56,7 +62,9 @@ const ABI_TAG: u32 = 0x58594254; // 'T''Y''B''X' little-endian (TYBX)
|
||||
|
||||
extern "C" fn tb_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||
unsafe {
|
||||
if name.is_null() { return 0; }
|
||||
if name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let s = std::ffi::CStr::from_ptr(name).to_string_lossy();
|
||||
match s.as_ref() {
|
||||
"birth" => M_BIRTH,
|
||||
@ -72,8 +80,23 @@ extern "C" fn tb_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn tb_invoke_id(_method_id: u32, instance_id: u32, args: *const u8, args_len: usize, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
nyash_plugin_invoke(TID_EGUI, _method_id, instance_id, args, args_len, result, result_len)
|
||||
extern "C" fn tb_invoke_id(
|
||||
_method_id: u32,
|
||||
instance_id: u32,
|
||||
args: *const u8,
|
||||
args_len: usize,
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
nyash_plugin_invoke(
|
||||
TID_EGUI,
|
||||
_method_id,
|
||||
instance_id,
|
||||
args,
|
||||
args_len,
|
||||
result,
|
||||
result_len,
|
||||
)
|
||||
}
|
||||
|
||||
static TYPE_NAME: &[u8] = b"EguiBox\0";
|
||||
@ -90,10 +113,14 @@ pub static nyash_typebox_EguiBox: NyashTypeBoxFfi = NyashTypeBoxFfi {
|
||||
|
||||
// ===== Plugin entry points =====
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 { OK }
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 {
|
||||
OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_invoke(
|
||||
@ -105,34 +132,81 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
if type_id != TID_EGUI { return E_TYPE; }
|
||||
if type_id != TID_EGUI {
|
||||
return E_TYPE;
|
||||
}
|
||||
unsafe {
|
||||
match method_id {
|
||||
M_BIRTH => {
|
||||
let need = 4; // instance_id (u32 LE)
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
if result.is_null() || *result_len < need { *result_len = need; return E_SHORT; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
if result.is_null() || *result_len < need {
|
||||
*result_len = need;
|
||||
return E_SHORT;
|
||||
}
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut m) = INST.lock() { m.insert(id, EguiInstance::default()); } else { return E_FAIL; }
|
||||
let b = id.to_le_bytes(); std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4); *result_len = 4; OK
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.insert(id, EguiInstance::default());
|
||||
} else {
|
||||
return E_FAIL;
|
||||
}
|
||||
let b = id.to_le_bytes();
|
||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
||||
*result_len = 4;
|
||||
OK
|
||||
}
|
||||
M_FINI => {
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.remove(&instance_id);
|
||||
OK
|
||||
} else {
|
||||
E_FAIL
|
||||
}
|
||||
}
|
||||
M_FINI => { if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_FAIL } }
|
||||
M_OPEN => {
|
||||
eprintln!("[EGUI] M_OPEN invoked");
|
||||
let (w, h, title) = match tlv_read_open_args(args, args_len) { Some(v) => v, None => return E_ARGS };
|
||||
if let Ok(mut m) = INST.lock() { if let Some(inst) = m.get_mut(&instance_id) { inst.width=w; inst.height=h; inst.title=title; } else { return E_FAIL; } } else { return E_FAIL; }
|
||||
let (w, h, title) = match tlv_read_open_args(args, args_len) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
if let Some(inst) = m.get_mut(&instance_id) {
|
||||
inst.width = w;
|
||||
inst.height = h;
|
||||
inst.title = title;
|
||||
} else {
|
||||
return E_FAIL;
|
||||
}
|
||||
} else {
|
||||
return E_FAIL;
|
||||
}
|
||||
write_tlv_void(result, result_len)
|
||||
}
|
||||
M_UI_LABEL => {
|
||||
eprintln!("[EGUI] M_UI_LABEL invoked");
|
||||
let text = match tlv_read_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
if let Ok(mut m) = INST.lock() { if let Some(inst) = m.get_mut(&instance_id) { inst.labels.push(text); } else { return E_FAIL; } } else { return E_FAIL; }
|
||||
let text = match tlv_read_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
if let Some(inst) = m.get_mut(&instance_id) {
|
||||
inst.labels.push(text);
|
||||
} else {
|
||||
return E_FAIL;
|
||||
}
|
||||
} else {
|
||||
return E_FAIL;
|
||||
}
|
||||
write_tlv_void(result, result_len)
|
||||
}
|
||||
M_UI_BUTTON => {
|
||||
eprintln!("[EGUI] M_UI_BUTTON invoked");
|
||||
// For now: stub, accept and return Void
|
||||
if tlv_read_string(args, args_len, 0).is_none() { return E_ARGS; }
|
||||
if tlv_read_string(args, args_len, 0).is_none() {
|
||||
return E_ARGS;
|
||||
}
|
||||
write_tlv_void(result, result_len)
|
||||
}
|
||||
M_POLL_EVENT => {
|
||||
@ -147,7 +221,12 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
{
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
guirun::run_window(inst.width, inst.height, &inst.title, inst.labels.clone());
|
||||
guirun::run_window(
|
||||
inst.width,
|
||||
inst.height,
|
||||
&inst.title,
|
||||
inst.labels.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,54 +244,108 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
|
||||
// ===== TLV helpers (version=1) =====
|
||||
fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(4 + payloads.iter().map(|(_,p)| 4 + p.len()).sum::<usize>());
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
let mut buf: Vec<u8> =
|
||||
Vec::with_capacity(4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||
buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, payload) in payloads {
|
||||
buf.push(*tag); buf.push(0); buf.extend_from_slice(&(payload.len() as u16).to_le_bytes()); buf.extend_from_slice(payload);
|
||||
buf.push(*tag);
|
||||
buf.push(0);
|
||||
buf.extend_from_slice(&(payload.len() as u16).to_le_bytes());
|
||||
buf.extend_from_slice(payload);
|
||||
}
|
||||
unsafe {
|
||||
let need = buf.len();
|
||||
if result.is_null() || *result_len < need { *result_len = need; return E_SHORT; }
|
||||
if result.is_null() || *result_len < need {
|
||||
*result_len = need;
|
||||
return E_SHORT;
|
||||
}
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), result, need);
|
||||
*result_len = need;
|
||||
}
|
||||
OK
|
||||
}
|
||||
fn write_tlv_void(result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(9u8, &[])], result, result_len) }
|
||||
fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(6u8, s.as_bytes())], result, result_len) }
|
||||
fn write_tlv_void(result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(9u8, &[])], result, result_len)
|
||||
}
|
||||
fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(6u8, s.as_bytes())], result, result_len)
|
||||
}
|
||||
|
||||
unsafe fn tlv_parse_header(data: *const u8, len: usize) -> Option<(u16,u16,usize)> {
|
||||
if data.is_null() || len < 4 { return None; }
|
||||
unsafe fn tlv_parse_header(data: *const u8, len: usize) -> Option<(u16, u16, usize)> {
|
||||
if data.is_null() || len < 4 {
|
||||
return None;
|
||||
}
|
||||
let b = std::slice::from_raw_parts(data, len);
|
||||
let ver = u16::from_le_bytes([b[0], b[1]]);
|
||||
let argc = u16::from_le_bytes([b[2], b[3]]);
|
||||
if ver != 1 { return None; }
|
||||
if ver != 1 {
|
||||
return None;
|
||||
}
|
||||
Some((ver, argc, 4))
|
||||
}
|
||||
unsafe fn tlv_read_entry_at(data: *const u8, len: usize, mut pos: usize) -> Option<(u8, usize, usize)> {
|
||||
unsafe fn tlv_read_entry_at(
|
||||
data: *const u8,
|
||||
len: usize,
|
||||
mut pos: usize,
|
||||
) -> Option<(u8, usize, usize)> {
|
||||
let b = std::slice::from_raw_parts(data, len);
|
||||
if pos + 4 > len { return None; }
|
||||
let tag = b[pos]; let _ = b[pos+1]; let size = u16::from_le_bytes([b[pos+2], b[pos+3]]) as usize; pos += 4;
|
||||
if pos + size > len { return None; }
|
||||
if pos + 4 > len {
|
||||
return None;
|
||||
}
|
||||
let tag = b[pos];
|
||||
let _ = b[pos + 1];
|
||||
let size = u16::from_le_bytes([b[pos + 2], b[pos + 3]]) as usize;
|
||||
pos += 4;
|
||||
if pos + size > len {
|
||||
return None;
|
||||
}
|
||||
Some((tag, size, pos))
|
||||
}
|
||||
unsafe fn tlv_read_i64(data: *const u8, len: usize, index: usize) -> Option<i64> {
|
||||
let (_, argc, mut pos) = tlv_parse_header(data, len)?; if argc < (index as u16 + 1) { return None; }
|
||||
for i in 0..=index { let (tag, size, p) = tlv_read_entry_at(data, len, pos)?; if tag == 3 && size == 8 { if i == index { let b = std::slice::from_raw_parts(data.add(p), 8); let mut t=[0u8;8]; t.copy_from_slice(b); return Some(i64::from_le_bytes(t)); } } pos = p + size; }
|
||||
let (_, argc, mut pos) = tlv_parse_header(data, len)?;
|
||||
if argc < (index as u16 + 1) {
|
||||
return None;
|
||||
}
|
||||
for i in 0..=index {
|
||||
let (tag, size, p) = tlv_read_entry_at(data, len, pos)?;
|
||||
if tag == 3 && size == 8 {
|
||||
if i == index {
|
||||
let b = std::slice::from_raw_parts(data.add(p), 8);
|
||||
let mut t = [0u8; 8];
|
||||
t.copy_from_slice(b);
|
||||
return Some(i64::from_le_bytes(t));
|
||||
}
|
||||
}
|
||||
pos = p + size;
|
||||
}
|
||||
None
|
||||
}
|
||||
unsafe fn tlv_read_string(data: *const u8, len: usize, index: usize) -> Option<String> {
|
||||
let (_, argc, mut pos) = tlv_parse_header(data, len)?; if argc < (index as u16 + 1) { return None; }
|
||||
for i in 0..=index { let (tag, size, p) = tlv_read_entry_at(data, len, pos)?; if tag == 6 || tag == 7 { if i == index { let s = std::slice::from_raw_parts(data.add(p), size); return Some(String::from_utf8_lossy(s).to_string()); } } pos = p + size; }
|
||||
let (_, argc, mut pos) = tlv_parse_header(data, len)?;
|
||||
if argc < (index as u16 + 1) {
|
||||
return None;
|
||||
}
|
||||
for i in 0..=index {
|
||||
let (tag, size, p) = tlv_read_entry_at(data, len, pos)?;
|
||||
if tag == 6 || tag == 7 {
|
||||
if i == index {
|
||||
let s = std::slice::from_raw_parts(data.add(p), size);
|
||||
return Some(String::from_utf8_lossy(s).to_string());
|
||||
}
|
||||
}
|
||||
pos = p + size;
|
||||
}
|
||||
None
|
||||
}
|
||||
unsafe fn tlv_read_open_args(args: *const u8, len: usize) -> Option<(i32,i32,String)> {
|
||||
unsafe fn tlv_read_open_args(args: *const u8, len: usize) -> Option<(i32, i32, String)> {
|
||||
let w = tlv_read_i64(args, len, 0)? as i32;
|
||||
let h = tlv_read_i64(args, len, 1)? as i32;
|
||||
let t = tlv_read_string(args, len, 2)?;
|
||||
Some((w,h,t))
|
||||
Some((w, h, t))
|
||||
}
|
||||
|
||||
// ===== GUI 実行(with-egui, クロスプラットフォーム) =====
|
||||
@ -224,7 +357,9 @@ mod guirun {
|
||||
pub fn run_window(w: i32, h: i32, title: &str, labels: Vec<String>) {
|
||||
eprintln!("[EGUI] run_window: w={} h={} title='{}'", w, h, title);
|
||||
let diag = std::env::var("NYASH_EGUI_DIAG").ok().as_deref() == Some("1");
|
||||
let scale_override = std::env::var("NYASH_EGUI_SCALE").ok().and_then(|s| s.parse::<f32>().ok());
|
||||
let scale_override = std::env::var("NYASH_EGUI_SCALE")
|
||||
.ok()
|
||||
.and_then(|s| s.parse::<f32>().ok());
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
.with_inner_size([w.max(100) as f32, h.max(100) as f32])
|
||||
@ -232,7 +367,14 @@ mod guirun {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
struct App { labels: Vec<String>, diag: bool, printed: bool, init_w: i32, init_h: i32, scale: Option<f32> }
|
||||
struct App {
|
||||
labels: Vec<String>,
|
||||
diag: bool,
|
||||
printed: bool,
|
||||
init_w: i32,
|
||||
init_h: i32,
|
||||
scale: Option<f32>,
|
||||
}
|
||||
impl eframe::App for App {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
if self.diag && !self.printed {
|
||||
@ -252,13 +394,21 @@ mod guirun {
|
||||
let rect = ctx.screen_rect();
|
||||
ui.small(format!(
|
||||
"DPI: ppp={:.3} logical={:.1}x{:.1} init={}x{} scale={}",
|
||||
ppp, rect.width(), rect.height(), self.init_w, self.init_h,
|
||||
self.scale.map(|v| format!("{:.2}", v)).unwrap_or_else(|| "auto".into())
|
||||
ppp,
|
||||
rect.width(),
|
||||
rect.height(),
|
||||
self.init_w,
|
||||
self.init_h,
|
||||
self.scale
|
||||
.map(|v| format!("{:.2}", v))
|
||||
.unwrap_or_else(|| "auto".into())
|
||||
));
|
||||
}
|
||||
});
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
for s in &self.labels { ui.label(s); }
|
||||
for s in &self.labels {
|
||||
ui.label(s);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -277,7 +427,14 @@ mod guirun {
|
||||
cc.egui_ctx.set_pixels_per_point(ppp);
|
||||
eprintln!("[EGUI][diag] override pixels_per_point to {:.3}", ppp);
|
||||
}
|
||||
Box::new(App { labels, diag, printed: false, init_w, init_h, scale: scale_override })
|
||||
Box::new(App {
|
||||
labels,
|
||||
diag,
|
||||
printed: false,
|
||||
init_w,
|
||||
init_h,
|
||||
scale: scale_override,
|
||||
})
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
@ -2,7 +2,10 @@
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Mutex, atomic::{AtomicU32, Ordering}};
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
const OK: i32 = 0;
|
||||
const E_SHORT: i32 = -1;
|
||||
@ -12,13 +15,13 @@ const E_ARGS: i32 = -4;
|
||||
const E_PLUGIN: i32 = -5;
|
||||
const E_HANDLE: i32 = -8;
|
||||
|
||||
const M_BIRTH: u32 = 0; // constructor (stateless)
|
||||
const M_TO_UTF8_BYTES: u32 = 1; // toUtf8Bytes(s) -> bytes
|
||||
const M_BIRTH: u32 = 0; // constructor (stateless)
|
||||
const M_TO_UTF8_BYTES: u32 = 1; // toUtf8Bytes(s) -> bytes
|
||||
const M_FROM_UTF8_BYTES: u32 = 2; // fromUtf8Bytes(bytes) -> string
|
||||
const M_BASE64_ENC: u32 = 3; // base64Encode(s|bytes) -> string
|
||||
const M_BASE64_DEC: u32 = 4; // base64Decode(str) -> bytes
|
||||
const M_HEX_ENC: u32 = 5; // hexEncode(s|bytes) -> string
|
||||
const M_HEX_DEC: u32 = 6; // hexDecode(str) -> bytes
|
||||
const M_BASE64_ENC: u32 = 3; // base64Encode(s|bytes) -> string
|
||||
const M_BASE64_DEC: u32 = 4; // base64Decode(str) -> bytes
|
||||
const M_HEX_ENC: u32 = 5; // hexEncode(s|bytes) -> string
|
||||
const M_HEX_DEC: u32 = 6; // hexDecode(str) -> bytes
|
||||
const M_FINI: u32 = u32::MAX;
|
||||
|
||||
// Assign an unused type id
|
||||
@ -30,10 +33,14 @@ static INST: Lazy<Mutex<HashMap<u32, EncInstance>>> = Lazy::new(|| Mutex::new(Ha
|
||||
static NEXT_ID: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 { OK }
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 {
|
||||
OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_invoke(
|
||||
@ -45,44 +52,97 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
if type_id != TYPE_ID_ENCODING { return E_TYPE; }
|
||||
if type_id != TYPE_ID_ENCODING {
|
||||
return E_TYPE;
|
||||
}
|
||||
unsafe {
|
||||
match method_id {
|
||||
M_BIRTH => {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
if preflight(result, result_len, 4) { return E_SHORT; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
if preflight(result, result_len, 4) {
|
||||
return E_SHORT;
|
||||
}
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut m) = INST.lock() { m.insert(id, EncInstance); } else { return E_PLUGIN; }
|
||||
let b = id.to_le_bytes(); std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4); *result_len = 4; OK
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.insert(id, EncInstance);
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
let b = id.to_le_bytes();
|
||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
||||
*result_len = 4;
|
||||
OK
|
||||
}
|
||||
M_FINI => {
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.remove(&instance_id);
|
||||
OK
|
||||
} else {
|
||||
E_PLUGIN
|
||||
}
|
||||
}
|
||||
M_FINI => { if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_PLUGIN } }
|
||||
M_TO_UTF8_BYTES => {
|
||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
let s = match read_arg_string(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
write_tlv_bytes(s.as_bytes(), result, result_len)
|
||||
}
|
||||
M_FROM_UTF8_BYTES => {
|
||||
let bytes = match read_arg_bytes(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
match String::from_utf8(bytes) { Ok(s) => write_tlv_string(&s, result, result_len), Err(_) => write_tlv_string("", result, result_len) }
|
||||
let bytes = match read_arg_bytes(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
match String::from_utf8(bytes) {
|
||||
Ok(s) => write_tlv_string(&s, result, result_len),
|
||||
Err(_) => write_tlv_string("", result, result_len),
|
||||
}
|
||||
}
|
||||
M_BASE64_ENC => {
|
||||
if let Some(b) = read_arg_bytes(args, args_len, 0) { let s = base64::encode(b); return write_tlv_string(&s, result, result_len); }
|
||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
if let Some(b) = read_arg_bytes(args, args_len, 0) {
|
||||
let s = base64::encode(b);
|
||||
return write_tlv_string(&s, result, result_len);
|
||||
}
|
||||
let s = match read_arg_string(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let enc = base64::encode(s.as_bytes());
|
||||
write_tlv_string(&enc, result, result_len)
|
||||
}
|
||||
M_BASE64_DEC => {
|
||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
match base64::decode(s.as_bytes()) { Ok(b) => write_tlv_bytes(&b, result, result_len), Err(_) => write_tlv_bytes(&[], result, result_len) }
|
||||
let s = match read_arg_string(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
match base64::decode(s.as_bytes()) {
|
||||
Ok(b) => write_tlv_bytes(&b, result, result_len),
|
||||
Err(_) => write_tlv_bytes(&[], result, result_len),
|
||||
}
|
||||
}
|
||||
M_HEX_ENC => {
|
||||
if let Some(b) = read_arg_bytes(args, args_len, 0) { let s = hex::encode(b); return write_tlv_string(&s, result, result_len); }
|
||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
if let Some(b) = read_arg_bytes(args, args_len, 0) {
|
||||
let s = hex::encode(b);
|
||||
return write_tlv_string(&s, result, result_len);
|
||||
}
|
||||
let s = match read_arg_string(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let enc = hex::encode(s.as_bytes());
|
||||
write_tlv_string(&enc, result, result_len)
|
||||
}
|
||||
M_HEX_DEC => {
|
||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
match hex::decode(s.as_bytes()) { Ok(b) => write_tlv_bytes(&b, result, result_len), Err(_) => write_tlv_bytes(&[], result, result_len) }
|
||||
let s = match read_arg_string(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
match hex::decode(s.as_bytes()) {
|
||||
Ok(b) => write_tlv_bytes(&b, result, result_len),
|
||||
Err(_) => write_tlv_bytes(&[], result, result_len),
|
||||
}
|
||||
}
|
||||
_ => E_METHOD,
|
||||
}
|
||||
@ -90,29 +150,98 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
}
|
||||
|
||||
fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
unsafe { if result_len.is_null() { return false; } if result.is_null() || *result_len < needed { *result_len = needed; return true; } }
|
||||
unsafe {
|
||||
if result_len.is_null() {
|
||||
return false;
|
||||
}
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(4 + payloads.iter().map(|(_,p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes()); buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, payload) in payloads { buf.push(*tag); buf.push(0); buf.extend_from_slice(&(payload.len() as u16).to_le_bytes()); buf.extend_from_slice(payload); }
|
||||
unsafe { let needed = buf.len(); if result.is_null() || *result_len < needed { *result_len = needed; return E_SHORT; } std::ptr::copy_nonoverlapping(buf.as_ptr(), result, needed); *result_len = needed; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
let mut buf: Vec<u8> =
|
||||
Vec::with_capacity(4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||
buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, payload) in payloads {
|
||||
buf.push(*tag);
|
||||
buf.push(0);
|
||||
buf.extend_from_slice(&(payload.len() as u16).to_le_bytes());
|
||||
buf.extend_from_slice(payload);
|
||||
}
|
||||
unsafe {
|
||||
let needed = buf.len();
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return E_SHORT;
|
||||
}
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), result, needed);
|
||||
*result_len = needed;
|
||||
}
|
||||
OK
|
||||
}
|
||||
fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(6u8, s.as_bytes())], result, result_len) }
|
||||
fn write_tlv_bytes(b: &[u8], result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(7u8, b)], result, result_len) }
|
||||
fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(6u8, s.as_bytes())], result, result_len)
|
||||
}
|
||||
fn write_tlv_bytes(b: &[u8], result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(7u8, b)], result, result_len)
|
||||
}
|
||||
|
||||
fn read_arg_string(args: *const u8, args_len: usize, n: usize) -> Option<String> {
|
||||
if args.is_null() || args_len < 4 { return None; }
|
||||
if args.is_null() || args_len < 4 {
|
||||
return None;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize; for i in 0..=n { if buf.len() < off + 4 { return None; } let tag = buf[off]; let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize; if buf.len() < off + 4 + size { return None; } if i == n { if tag == 6 || tag == 7 { return Some(String::from_utf8_lossy(&buf[off+4..off+4+size]).to_string()); } else { return None; } } off += 4 + size; }
|
||||
let mut off = 4usize;
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
if tag == 6 || tag == 7 {
|
||||
return Some(String::from_utf8_lossy(&buf[off + 4..off + 4 + size]).to_string());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
off += 4 + size;
|
||||
}
|
||||
None
|
||||
}
|
||||
fn read_arg_bytes(args: *const u8, args_len: usize, n: usize) -> Option<Vec<u8>> {
|
||||
if args.is_null() || args_len < 4 { return None; }
|
||||
if args.is_null() || args_len < 4 {
|
||||
return None;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize; for i in 0..=n { if buf.len() < off + 4 { return None; } let tag = buf[off]; let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize; if buf.len() < off + 4 + size { return None; } if i == n { if tag == 7 || tag == 6 { return Some(buf[off+4..off+4+size].to_vec()); } else { return None; } } off += 4 + size; }
|
||||
let mut off = 4usize;
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
if tag == 7 || tag == 6 {
|
||||
return Some(buf[off + 4..off + 4 + size].to_vec());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
off += 4 + size;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
//! FileBox Dynamic Plugin
|
||||
//!
|
||||
//!
|
||||
//! C ABIを使用した動的ライブラリとしてFileBox機能を提供
|
||||
|
||||
use std::ffi::{c_char, c_void, CStr, CString};
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Read, Write, Seek};
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
/// プラグインのマジックナンバー('NYAS')
|
||||
@ -36,7 +36,7 @@ pub extern "C" fn nyash_plugin_init() -> *const FileBoxPlugin {
|
||||
}
|
||||
|
||||
/// FileBoxを開く
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// - pathは有効なUTF-8のC文字列である必要がある
|
||||
/// - 返されたポインタはnyash_file_freeで解放する必要がある
|
||||
@ -45,12 +45,12 @@ pub unsafe extern "C" fn nyash_file_open(path: *const c_char) -> *mut c_void {
|
||||
if path.is_null() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
|
||||
let path_str = match CStr::from_ptr(path).to_str() {
|
||||
Ok(s) => s,
|
||||
Err(_) => return std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
|
||||
match OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
@ -69,7 +69,7 @@ pub unsafe extern "C" fn nyash_file_open(path: *const c_char) -> *mut c_void {
|
||||
}
|
||||
|
||||
/// ファイルの内容を読み取る
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// - handleはnyash_file_openから返された有効なポインタである必要がある
|
||||
/// - 返された文字列はnyash_string_freeで解放する必要がある
|
||||
@ -78,46 +78,41 @@ pub unsafe extern "C" fn nyash_file_read(handle: *mut c_void) -> *mut c_char {
|
||||
if handle.is_null() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
|
||||
let file_box = &mut *(handle as *mut FileBoxHandle);
|
||||
let mut content = String::new();
|
||||
|
||||
|
||||
// ファイルポインタを最初に戻す
|
||||
if let Err(_) = file_box.file.seek(std::io::SeekFrom::Start(0)) {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
|
||||
match file_box.file.read_to_string(&mut content) {
|
||||
Ok(_) => {
|
||||
match CString::new(content) {
|
||||
Ok(c_str) => c_str.into_raw(),
|
||||
Err(_) => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
Ok(_) => match CString::new(content) {
|
||||
Ok(c_str) => c_str.into_raw(),
|
||||
Err(_) => std::ptr::null_mut(),
|
||||
},
|
||||
Err(_) => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// ファイルに内容を書き込む
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// - handleはnyash_file_openから返された有効なポインタである必要がある
|
||||
/// - contentは有効なUTF-8のC文字列である必要がある
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn nyash_file_write(
|
||||
handle: *mut c_void,
|
||||
content: *const c_char
|
||||
) -> c_int {
|
||||
pub unsafe extern "C" fn nyash_file_write(handle: *mut c_void, content: *const c_char) -> c_int {
|
||||
if handle.is_null() || content.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
let file_box = &mut *(handle as *mut FileBoxHandle);
|
||||
let content_str = match CStr::from_ptr(content).to_str() {
|
||||
Ok(s) => s,
|
||||
Err(_) => return 0,
|
||||
};
|
||||
|
||||
|
||||
// ファイルをクリアして最初から書き込む
|
||||
if let Err(_) = file_box.file.set_len(0) {
|
||||
return 0;
|
||||
@ -125,15 +120,15 @@ pub unsafe extern "C" fn nyash_file_write(
|
||||
if let Err(_) = file_box.file.seek(std::io::SeekFrom::Start(0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
match file_box.file.write_all(content_str.as_bytes()) {
|
||||
Ok(_) => 1, // 成功
|
||||
Err(_) => 0, // 失敗
|
||||
Err(_) => 0, // 失敗
|
||||
}
|
||||
}
|
||||
|
||||
/// ファイルが存在するかチェック
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// - pathは有効なUTF-8のC文字列である必要がある
|
||||
#[no_mangle]
|
||||
@ -141,12 +136,12 @@ pub unsafe extern "C" fn nyash_file_exists(path: *const c_char) -> c_int {
|
||||
if path.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
let path_str = match CStr::from_ptr(path).to_str() {
|
||||
Ok(s) => s,
|
||||
Err(_) => return 0,
|
||||
};
|
||||
|
||||
|
||||
if std::path::Path::new(path_str).exists() {
|
||||
1
|
||||
} else {
|
||||
@ -155,7 +150,7 @@ pub unsafe extern "C" fn nyash_file_exists(path: *const c_char) -> c_int {
|
||||
}
|
||||
|
||||
/// FileBoxハンドルを解放
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// - handleはnyash_file_openから返された有効なポインタである必要がある
|
||||
/// - 解放後はhandleを使用してはいけない
|
||||
@ -167,7 +162,7 @@ pub unsafe extern "C" fn nyash_file_free(handle: *mut c_void) {
|
||||
}
|
||||
|
||||
/// 文字列を解放(nyash_file_readの戻り値用)
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// - strはnyash_file_readから返された有効なポインタである必要がある
|
||||
#[no_mangle]
|
||||
@ -181,7 +176,7 @@ pub unsafe extern "C" fn nyash_string_free(str: *mut c_char) {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::ffi::CString;
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_plugin_init() {
|
||||
unsafe {
|
||||
@ -193,4 +188,4 @@ mod tests {
|
||||
assert_eq!(plugin_info.api_version, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
//! Nyash FileBox Plugin - BID-FFI v1 Implementation
|
||||
//!
|
||||
//!
|
||||
//! Provides file I/O operations as a Nyash plugin
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::os::raw::c_char;
|
||||
// std::ptr削除(未使用)
|
||||
use std::sync::{Mutex, atomic::{AtomicU32, Ordering}};
|
||||
use std::io::{Read, Write, Seek, SeekFrom};
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
// ============ FFI Types ============
|
||||
|
||||
@ -37,7 +40,7 @@ const NYB_E_PLUGIN_ERROR: i32 = -5;
|
||||
const NYB_E_INVALID_HANDLE: i32 = -8;
|
||||
|
||||
// ============ Method IDs ============
|
||||
const METHOD_BIRTH: u32 = 0; // Constructor
|
||||
const METHOD_BIRTH: u32 = 0; // Constructor
|
||||
const METHOD_OPEN: u32 = 1;
|
||||
const METHOD_READ: u32 = 2;
|
||||
const METHOD_WRITE: u32 = 3;
|
||||
@ -45,20 +48,19 @@ const METHOD_CLOSE: u32 = 4;
|
||||
const METHOD_EXISTS: u32 = 5;
|
||||
const METHOD_COPY_FROM: u32 = 7; // New: copyFrom(other: Handle)
|
||||
const METHOD_CLONE_SELF: u32 = 8; // New: cloneSelf() -> Handle
|
||||
const METHOD_FINI: u32 = u32::MAX; // Destructor
|
||||
const METHOD_FINI: u32 = u32::MAX; // Destructor
|
||||
|
||||
// ============ FileBox Instance ============
|
||||
struct FileBoxInstance {
|
||||
file: Option<std::fs::File>,
|
||||
path: String,
|
||||
buffer: Option<Vec<u8>>, // プラグインが管理するバッファ
|
||||
buffer: Option<Vec<u8>>, // プラグインが管理するバッファ
|
||||
}
|
||||
|
||||
// グローバルインスタンス管理(実際の実装ではより安全な方法を使用)
|
||||
use once_cell::sync::Lazy;
|
||||
static INSTANCES: Lazy<Mutex<HashMap<u32, FileBoxInstance>>> = Lazy::new(|| {
|
||||
Mutex::new(HashMap::new())
|
||||
});
|
||||
static INSTANCES: Lazy<Mutex<HashMap<u32, FileBoxInstance>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
// ホスト関数テーブルは使用しない(Host VTable廃止)
|
||||
|
||||
@ -70,7 +72,7 @@ static INSTANCE_COUNTER: AtomicU32 = AtomicU32::new(1);
|
||||
/// ABI version
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1 // BID-1 support
|
||||
1 // BID-1 support
|
||||
}
|
||||
|
||||
/// Plugin initialization (optional - global setup)
|
||||
@ -119,11 +121,14 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
// 新しいインスタンスを作成
|
||||
let instance_id = INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
map.insert(instance_id, FileBoxInstance {
|
||||
file: None,
|
||||
path: String::new(),
|
||||
buffer: None,
|
||||
});
|
||||
map.insert(
|
||||
instance_id,
|
||||
FileBoxInstance {
|
||||
file: None,
|
||||
path: String::new(),
|
||||
buffer: None,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
@ -149,7 +154,9 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
match tlv_parse_two_strings(args) {
|
||||
Ok((path, mode)) => {
|
||||
// Preflight for Void TLV: header(4) + entry(4)
|
||||
if preflight(_result, _result_len, 8) { return NYB_E_SHORT_BUFFER; }
|
||||
if preflight(_result, _result_len, 8) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
log_info(&format!("OPEN path='{}' mode='{}'", path, mode));
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get_mut(&_instance_id) {
|
||||
@ -161,8 +168,12 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
}
|
||||
Err(_) => return NYB_E_PLUGIN_ERROR,
|
||||
}
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
Err(_) => NYB_E_INVALID_ARGS,
|
||||
}
|
||||
@ -172,18 +183,20 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
let args = std::slice::from_raw_parts(_args, _args_len);
|
||||
if _args_len > 0 {
|
||||
match tlv_parse_string(args) {
|
||||
Ok(path) => {
|
||||
match open_file("r", &path) {
|
||||
Ok(mut file) => {
|
||||
let mut buf = Vec::new();
|
||||
if let Err(_) = file.read_to_end(&mut buf) { return NYB_E_PLUGIN_ERROR; }
|
||||
let need = 8usize.saturating_add(buf.len());
|
||||
if preflight(_result, _result_len, need) { return NYB_E_SHORT_BUFFER; }
|
||||
return write_tlv_bytes(&buf, _result, _result_len);
|
||||
Ok(path) => match open_file("r", &path) {
|
||||
Ok(mut file) => {
|
||||
let mut buf = Vec::new();
|
||||
if let Err(_) = file.read_to_end(&mut buf) {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
Err(_) => return NYB_E_PLUGIN_ERROR,
|
||||
let need = 8usize.saturating_add(buf.len());
|
||||
if preflight(_result, _result_len, need) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
return write_tlv_bytes(&buf, _result, _result_len);
|
||||
}
|
||||
}
|
||||
Err(_) => return NYB_E_PLUGIN_ERROR,
|
||||
},
|
||||
Err(_) => return NYB_E_INVALID_ARGS,
|
||||
}
|
||||
} else {
|
||||
@ -196,14 +209,22 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
Ok(n) => {
|
||||
log_info(&format!("READ {} bytes (entire file)", n));
|
||||
let need = 8usize.saturating_add(buf.len());
|
||||
if preflight(_result, _result_len, need) { return NYB_E_SHORT_BUFFER; }
|
||||
if preflight(_result, _result_len, need) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
return write_tlv_bytes(&buf, _result, _result_len);
|
||||
}
|
||||
Err(_) => return NYB_E_PLUGIN_ERROR,
|
||||
}
|
||||
} else { return NYB_E_INVALID_HANDLE; }
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
METHOD_WRITE => {
|
||||
@ -211,50 +232,68 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
let args = std::slice::from_raw_parts(_args, _args_len);
|
||||
match tlv_parse_optional_string_and_bytes(args) {
|
||||
Ok((Some(path), data)) => {
|
||||
if preflight(_result, _result_len, 12) { return NYB_E_SHORT_BUFFER; }
|
||||
if preflight(_result, _result_len, 12) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
match open_file("w", &path) {
|
||||
Ok(mut file) => {
|
||||
if let Err(_) = file.write_all(&data) { return NYB_E_PLUGIN_ERROR; }
|
||||
if let Err(_) = file.flush() { return NYB_E_PLUGIN_ERROR; }
|
||||
if let Err(_) = file.write_all(&data) {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
if let Err(_) = file.flush() {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
return write_tlv_i32(data.len() as i32, _result, _result_len);
|
||||
}
|
||||
Err(_) => return NYB_E_PLUGIN_ERROR,
|
||||
}
|
||||
}
|
||||
Ok((None, data)) => {
|
||||
if preflight(_result, _result_len, 12) { return NYB_E_SHORT_BUFFER; }
|
||||
if preflight(_result, _result_len, 12) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get_mut(&_instance_id) {
|
||||
if let Some(file) = inst.file.as_mut() {
|
||||
match file.write(&data) {
|
||||
Ok(n) => {
|
||||
if let Err(_) = file.flush() { return NYB_E_PLUGIN_ERROR; }
|
||||
if let Err(_) = file.flush() {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
log_info(&format!("WRITE {} bytes", n));
|
||||
inst.buffer = Some(data.clone());
|
||||
return write_tlv_i32(n as i32, _result, _result_len);
|
||||
}
|
||||
Err(_) => return NYB_E_PLUGIN_ERROR,
|
||||
}
|
||||
} else { return NYB_E_INVALID_HANDLE; }
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
Err(_) => NYB_E_INVALID_ARGS,
|
||||
}
|
||||
}
|
||||
METHOD_CLOSE => {
|
||||
// Preflight for Void TLV
|
||||
if preflight(_result, _result_len, 8) { return NYB_E_SHORT_BUFFER; }
|
||||
if preflight(_result, _result_len, 8) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
log_info("CLOSE");
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get_mut(&_instance_id) {
|
||||
inst.file = None;
|
||||
return write_tlv_void(_result, _result_len);
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
METHOD_COPY_FROM => {
|
||||
@ -262,8 +301,12 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
let args = std::slice::from_raw_parts(_args, _args_len);
|
||||
match tlv_parse_handle(args) {
|
||||
Ok((type_id, other_id)) => {
|
||||
if type_id != _type_id { return NYB_E_INVALID_TYPE; }
|
||||
if preflight(_result, _result_len, 8) { return NYB_E_SHORT_BUFFER; }
|
||||
if type_id != _type_id {
|
||||
return NYB_E_INVALID_TYPE;
|
||||
}
|
||||
if preflight(_result, _result_len, 8) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
// 1) まずsrcからデータを取り出す(不変参照のみ)
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
@ -283,21 +326,31 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
read_ok = true;
|
||||
}
|
||||
}
|
||||
if !read_ok { return NYB_E_PLUGIN_ERROR; }
|
||||
} else { return NYB_E_INVALID_HANDLE; }
|
||||
if !read_ok {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
// 2) dstへ書き込み(可変参照)
|
||||
if let Some(dst) = map.get_mut(&_instance_id) {
|
||||
if let Some(fdst) = dst.file.as_mut() {
|
||||
let _ = fdst.seek(SeekFrom::Start(0));
|
||||
if fdst.write_all(&data).is_err() { return NYB_E_PLUGIN_ERROR; }
|
||||
if fdst.write_all(&data).is_err() {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
let _ = fdst.set_len(data.len() as u64);
|
||||
let _ = fdst.flush();
|
||||
}
|
||||
dst.buffer = Some(data);
|
||||
return write_tlv_void(_result, _result_len);
|
||||
} else { return NYB_E_INVALID_HANDLE; }
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
} else {
|
||||
return NYB_E_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
}
|
||||
Err(_) => NYB_E_INVALID_ARGS,
|
||||
}
|
||||
@ -305,11 +358,22 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
METHOD_CLONE_SELF => {
|
||||
// Return a new instance (handle) as TLV Handle
|
||||
// Preflight for Handle TLV: header(4) + entry(4) + payload(8)
|
||||
if preflight(_result, _result_len, 16) { return NYB_E_SHORT_BUFFER; }
|
||||
if preflight(_result, _result_len, 16) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
let new_id = INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut map) = INSTANCES.lock() { map.insert(new_id, FileBoxInstance { file: None, path: String::new(), buffer: None }); }
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
map.insert(
|
||||
new_id,
|
||||
FileBoxInstance {
|
||||
file: None,
|
||||
path: String::new(),
|
||||
buffer: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
// Build TLV result
|
||||
let mut payload = [0u8;8];
|
||||
let mut payload = [0u8; 8];
|
||||
payload[0..4].copy_from_slice(&_type_id.to_le_bytes());
|
||||
payload[4..8].copy_from_slice(&new_id.to_le_bytes());
|
||||
return write_tlv_result(&[(8u8, &payload)], _result, _result_len);
|
||||
@ -320,13 +384,15 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
match tlv_parse_string(args) {
|
||||
Ok(path) => {
|
||||
let exists = std::path::Path::new(&path).exists();
|
||||
if preflight(_result, _result_len, 9) { return NYB_E_SHORT_BUFFER; }
|
||||
if preflight(_result, _result_len, 9) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
return write_tlv_bool(exists, _result, _result_len);
|
||||
}
|
||||
Err(_) => NYB_E_INVALID_ARGS,
|
||||
}
|
||||
}
|
||||
_ => NYB_SUCCESS
|
||||
_ => NYB_SUCCESS,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -337,16 +403,27 @@ fn open_file(mode: &str, path: &str) -> Result<std::fs::File, std::io::Error> {
|
||||
use std::fs::OpenOptions;
|
||||
match mode {
|
||||
"r" => OpenOptions::new().read(true).open(path),
|
||||
"w" => OpenOptions::new().write(true).create(true).truncate(true).open(path),
|
||||
"w" => OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(path),
|
||||
"a" => OpenOptions::new().append(true).create(true).open(path),
|
||||
"rw" | "r+" => OpenOptions::new().read(true).write(true).create(true).open(path),
|
||||
"rw" | "r+" => OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(path),
|
||||
_ => OpenOptions::new().read(true).open(path),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
if result_len.is_null() { return NYB_E_INVALID_ARGS; }
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(4 + payloads.iter().map(|(_,p)| 4 + p.len()).sum::<usize>());
|
||||
if result_len.is_null() {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
let mut buf: Vec<u8> =
|
||||
Vec::with_capacity(4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes()); // version
|
||||
buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes()); // argc
|
||||
for (tag, payload) in payloads {
|
||||
@ -380,13 +457,15 @@ fn write_tlv_i32(v: i32, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
}
|
||||
|
||||
fn write_tlv_bool(v: bool, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
let b = [if v {1u8} else {0u8}];
|
||||
let b = [if v { 1u8 } else { 0u8 }];
|
||||
write_tlv_result(&[(1u8, &b)], result, result_len)
|
||||
}
|
||||
|
||||
fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
unsafe {
|
||||
if result_len.is_null() { return false; }
|
||||
if result_len.is_null() {
|
||||
return false;
|
||||
}
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return true;
|
||||
@ -395,58 +474,90 @@ fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn tlv_parse_header(data: &[u8]) -> Result<(u16,u16,usize), ()> {
|
||||
if data.len() < 4 { return Err(()); }
|
||||
fn tlv_parse_header(data: &[u8]) -> Result<(u16, u16, usize), ()> {
|
||||
if data.len() < 4 {
|
||||
return Err(());
|
||||
}
|
||||
let ver = u16::from_le_bytes([data[0], data[1]]);
|
||||
let argc = u16::from_le_bytes([data[2], data[3]]);
|
||||
if ver != 1 { return Err(()); }
|
||||
if ver != 1 {
|
||||
return Err(());
|
||||
}
|
||||
Ok((ver, argc, 4))
|
||||
}
|
||||
|
||||
fn tlv_parse_two_strings(data: &[u8]) -> Result<(String, String), ()> {
|
||||
let (_, argc, mut pos) = tlv_parse_header(data)?;
|
||||
if argc < 2 { return Err(()); }
|
||||
if argc < 2 {
|
||||
return Err(());
|
||||
}
|
||||
let s1 = tlv_parse_string_at(data, &mut pos)?;
|
||||
let s2 = tlv_parse_string_at(data, &mut pos)?;
|
||||
Ok((s1, s2))
|
||||
}
|
||||
|
||||
fn tlv_parse_string_at(data: &[u8], pos: &mut usize) -> Result<String, ()> {
|
||||
if *pos + 4 > data.len() { return Err(()); }
|
||||
let tag = data[*pos]; let _res = data[*pos+1];
|
||||
let size = u16::from_le_bytes([data[*pos+2], data[*pos+3]]) as usize;
|
||||
if *pos + 4 > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
let tag = data[*pos];
|
||||
let _res = data[*pos + 1];
|
||||
let size = u16::from_le_bytes([data[*pos + 2], data[*pos + 3]]) as usize;
|
||||
*pos += 4;
|
||||
if tag != 6 || *pos + size > data.len() { return Err(()); }
|
||||
let slice = &data[*pos..*pos+size];
|
||||
if tag != 6 || *pos + size > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
let slice = &data[*pos..*pos + size];
|
||||
*pos += size;
|
||||
std::str::from_utf8(slice).map(|s| s.to_string()).map_err(|_| ())
|
||||
std::str::from_utf8(slice)
|
||||
.map(|s| s.to_string())
|
||||
.map_err(|_| ())
|
||||
}
|
||||
|
||||
fn tlv_parse_i32(data: &[u8]) -> Result<i32, ()> {
|
||||
let (_, argc, mut pos) = tlv_parse_header(data)?;
|
||||
if argc < 1 { return Err(()); }
|
||||
if pos + 8 > data.len() { return Err(()); }
|
||||
let tag = data[pos]; let _res = data[pos+1];
|
||||
let size = u16::from_le_bytes([data[pos+2], data[pos+3]]) as usize; pos += 4;
|
||||
if tag != 2 || size != 4 || pos + size > data.len() { return Err(()); }
|
||||
let mut b = [0u8;4]; b.copy_from_slice(&data[pos..pos+4]);
|
||||
if argc < 1 {
|
||||
return Err(());
|
||||
}
|
||||
if pos + 8 > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
let tag = data[pos];
|
||||
let _res = data[pos + 1];
|
||||
let size = u16::from_le_bytes([data[pos + 2], data[pos + 3]]) as usize;
|
||||
pos += 4;
|
||||
if tag != 2 || size != 4 || pos + size > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
let mut b = [0u8; 4];
|
||||
b.copy_from_slice(&data[pos..pos + 4]);
|
||||
Ok(i32::from_le_bytes(b))
|
||||
}
|
||||
|
||||
fn tlv_parse_bytes(data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
let (_, argc, mut pos) = tlv_parse_header(data)?;
|
||||
if argc < 1 { return Err(()); }
|
||||
if pos + 4 > data.len() { return Err(()); }
|
||||
let tag = data[pos]; let _res = data[pos+1];
|
||||
let size = u16::from_le_bytes([data[pos+2], data[pos+3]]) as usize; pos += 4;
|
||||
if argc < 1 {
|
||||
return Err(());
|
||||
}
|
||||
if pos + 4 > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
let tag = data[pos];
|
||||
let _res = data[pos + 1];
|
||||
let size = u16::from_le_bytes([data[pos + 2], data[pos + 3]]) as usize;
|
||||
pos += 4;
|
||||
// StringタグもBytesタグも受け付ける(互換性のため)
|
||||
if (tag != 6 && tag != 7) || pos + size > data.len() { return Err(()); }
|
||||
Ok(data[pos..pos+size].to_vec())
|
||||
if (tag != 6 && tag != 7) || pos + size > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
Ok(data[pos..pos + size].to_vec())
|
||||
}
|
||||
|
||||
fn tlv_parse_string(data: &[u8]) -> Result<String, ()> {
|
||||
let (_, argc, mut pos) = tlv_parse_header(data)?;
|
||||
if argc < 1 { return Err(()); }
|
||||
if argc < 1 {
|
||||
return Err(());
|
||||
}
|
||||
tlv_parse_string_at(data, &mut pos)
|
||||
}
|
||||
|
||||
@ -454,18 +565,30 @@ fn tlv_parse_optional_string_and_bytes(data: &[u8]) -> Result<(Option<String>, V
|
||||
let (_, argc, mut pos) = tlv_parse_header(data)?;
|
||||
if argc == 1 {
|
||||
// only bytes
|
||||
if pos + 4 > data.len() { return Err(()); }
|
||||
let tag = data[pos]; let _res = data[pos+1];
|
||||
let size = u16::from_le_bytes([data[pos+2], data[pos+3]]) as usize; pos += 4;
|
||||
if (tag != 6 && tag != 7) || pos + size > data.len() { return Err(()); }
|
||||
return Ok((None, data[pos..pos+size].to_vec()));
|
||||
if pos + 4 > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
let tag = data[pos];
|
||||
let _res = data[pos + 1];
|
||||
let size = u16::from_le_bytes([data[pos + 2], data[pos + 3]]) as usize;
|
||||
pos += 4;
|
||||
if (tag != 6 && tag != 7) || pos + size > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
return Ok((None, data[pos..pos + size].to_vec()));
|
||||
} else if argc >= 2 {
|
||||
let s = tlv_parse_string_at(data, &mut pos)?;
|
||||
if pos + 4 > data.len() { return Err(()); }
|
||||
let tag = data[pos]; let _res = data[pos+1];
|
||||
let size = u16::from_le_bytes([data[pos+2], data[pos+3]]) as usize; pos += 4;
|
||||
if (tag != 6 && tag != 7) || pos + size > data.len() { return Err(()); }
|
||||
Ok((Some(s), data[pos..pos+size].to_vec()))
|
||||
if pos + 4 > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
let tag = data[pos];
|
||||
let _res = data[pos + 1];
|
||||
let size = u16::from_le_bytes([data[pos + 2], data[pos + 3]]) as usize;
|
||||
pos += 4;
|
||||
if (tag != 6 && tag != 7) || pos + size > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
Ok((Some(s), data[pos..pos + size].to_vec()))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
@ -473,13 +596,23 @@ fn tlv_parse_optional_string_and_bytes(data: &[u8]) -> Result<(Option<String>, V
|
||||
|
||||
fn tlv_parse_handle(data: &[u8]) -> Result<(u32, u32), ()> {
|
||||
let (_, argc, mut pos) = tlv_parse_header(data)?;
|
||||
if argc < 1 { return Err(()); }
|
||||
if pos + 4 > data.len() { return Err(()); }
|
||||
let tag = data[pos]; let _res = data[pos+1];
|
||||
let size = u16::from_le_bytes([data[pos+2], data[pos+3]]) as usize; pos += 4;
|
||||
if tag != 8 || size != 8 || pos + size > data.len() { return Err(()); }
|
||||
let mut t = [0u8;4]; t.copy_from_slice(&data[pos..pos+4]);
|
||||
let mut i = [0u8;4]; i.copy_from_slice(&data[pos+4..pos+8]);
|
||||
if argc < 1 {
|
||||
return Err(());
|
||||
}
|
||||
if pos + 4 > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
let tag = data[pos];
|
||||
let _res = data[pos + 1];
|
||||
let size = u16::from_le_bytes([data[pos + 2], data[pos + 3]]) as usize;
|
||||
pos += 4;
|
||||
if tag != 8 || size != 8 || pos + size > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
let mut t = [0u8; 4];
|
||||
t.copy_from_slice(&data[pos..pos + 4]);
|
||||
let mut i = [0u8; 4];
|
||||
i.copy_from_slice(&data[pos + 4..pos + 8]);
|
||||
Ok((u32::from_le_bytes(t), u32::from_le_bytes(i)))
|
||||
}
|
||||
|
||||
|
||||
@ -3,9 +3,12 @@
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Mutex, atomic::{AtomicU32, Ordering}};
|
||||
use std::os::raw::c_char;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
// Error codes
|
||||
const OK: i32 = 0;
|
||||
@ -18,23 +21,29 @@ const E_HANDLE: i32 = -8;
|
||||
|
||||
// Methods
|
||||
const M_BIRTH: u32 = 0;
|
||||
const M_GET: u32 = 1;
|
||||
const M_SET: u32 = 2;
|
||||
const M_FINI: u32 = u32::MAX;
|
||||
const M_GET: u32 = 1;
|
||||
const M_SET: u32 = 2;
|
||||
const M_FINI: u32 = u32::MAX;
|
||||
|
||||
// Assigned type id (nyash.toml must match)
|
||||
const TYPE_ID_INTEGER: u32 = 12;
|
||||
|
||||
struct IntInstance { value: i64 }
|
||||
struct IntInstance {
|
||||
value: i64,
|
||||
}
|
||||
|
||||
static INST: Lazy<Mutex<HashMap<u32, IntInstance>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static NEXT_ID: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 { OK }
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 {
|
||||
OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_invoke(
|
||||
@ -46,29 +55,65 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
if type_id != TYPE_ID_INTEGER { return E_TYPE; }
|
||||
if type_id != TYPE_ID_INTEGER {
|
||||
return E_TYPE;
|
||||
}
|
||||
unsafe {
|
||||
match method_id {
|
||||
M_BIRTH => {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
if preflight(result, result_len, 4) { return E_SHORT; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
if preflight(result, result_len, 4) {
|
||||
return E_SHORT;
|
||||
}
|
||||
// Optional initial value from first arg (i64/i32)
|
||||
let init = read_arg_i64(args, args_len, 0).unwrap_or(0);
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut m) = INST.lock() { m.insert(id, IntInstance { value: init }); }
|
||||
else { return E_PLUGIN; }
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.insert(id, IntInstance { value: init });
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
let b = id.to_le_bytes();
|
||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4); *result_len = 4; OK
|
||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
||||
*result_len = 4;
|
||||
OK
|
||||
}
|
||||
M_FINI => {
|
||||
if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_PLUGIN }
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.remove(&instance_id);
|
||||
OK
|
||||
} else {
|
||||
E_PLUGIN
|
||||
}
|
||||
}
|
||||
M_GET => {
|
||||
if let Ok(m) = INST.lock() { if let Some(inst) = m.get(&instance_id) { return write_tlv_i64(inst.value, result, result_len); } else { return E_HANDLE; } } else { return E_PLUGIN; }
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
return write_tlv_i64(inst.value, result, result_len);
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_SET => {
|
||||
let v = match read_arg_i64(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
if let Ok(mut m) = INST.lock() { if let Some(inst) = m.get_mut(&instance_id) { inst.value = v; return write_tlv_i64(inst.value, result, result_len); } else { return E_HANDLE; } } else { return E_PLUGIN; }
|
||||
let v = match read_arg_i64(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
if let Some(inst) = m.get_mut(&instance_id) {
|
||||
inst.value = v;
|
||||
return write_tlv_i64(inst.value, result, result_len);
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
_ => E_METHOD,
|
||||
}
|
||||
@ -89,7 +134,9 @@ pub struct NyashTypeBoxFfi {
|
||||
unsafe impl Sync for NyashTypeBoxFfi {}
|
||||
|
||||
extern "C" fn integer_resolve(name: *const c_char) -> u32 {
|
||||
if name.is_null() { return 0; }
|
||||
if name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||
match s.as_ref() {
|
||||
"get" => M_GET,
|
||||
@ -98,15 +145,42 @@ extern "C" fn integer_resolve(name: *const c_char) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn integer_invoke_id(instance_id: u32, method_id: u32, args: *const u8, args_len: usize, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
extern "C" fn integer_invoke_id(
|
||||
instance_id: u32,
|
||||
method_id: u32,
|
||||
args: *const u8,
|
||||
args_len: usize,
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
unsafe {
|
||||
match method_id {
|
||||
M_GET => {
|
||||
if let Ok(m) = INST.lock() { if let Some(inst) = m.get(&instance_id) { return write_tlv_i64(inst.value, result, result_len); } else { return E_HANDLE; } } else { return E_PLUGIN; }
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
return write_tlv_i64(inst.value, result, result_len);
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_SET => {
|
||||
let v = match read_arg_i64(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
if let Ok(mut m) = INST.lock() { if let Some(inst) = m.get_mut(&instance_id) { inst.value = v; return write_tlv_i64(inst.value, result, result_len); } else { return E_HANDLE; } } else { return E_PLUGIN; }
|
||||
let v = match read_arg_i64(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
if let Some(inst) = m.get_mut(&instance_id) {
|
||||
inst.value = v;
|
||||
return write_tlv_i64(inst.value, result, result_len);
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
_ => E_METHOD,
|
||||
}
|
||||
@ -125,34 +199,76 @@ pub static nyash_typebox_IntegerBox: NyashTypeBoxFfi = NyashTypeBoxFfi {
|
||||
};
|
||||
|
||||
fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
unsafe { if result_len.is_null() { return false; } if result.is_null() || *result_len < needed { *result_len = needed; return true; } }
|
||||
unsafe {
|
||||
if result_len.is_null() {
|
||||
return false;
|
||||
}
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(4 + payloads.iter().map(|(_,p)| 4 + p.len()).sum::<usize>());
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
let mut buf: Vec<u8> =
|
||||
Vec::with_capacity(4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||
buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, payload) in payloads { buf.push(*tag); buf.push(0); buf.extend_from_slice(&(payload.len() as u16).to_le_bytes()); buf.extend_from_slice(payload); }
|
||||
unsafe { let needed = buf.len(); if result.is_null() || *result_len < needed { *result_len = needed; return E_SHORT; } std::ptr::copy_nonoverlapping(buf.as_ptr(), result, needed); *result_len = needed; }
|
||||
for (tag, payload) in payloads {
|
||||
buf.push(*tag);
|
||||
buf.push(0);
|
||||
buf.extend_from_slice(&(payload.len() as u16).to_le_bytes());
|
||||
buf.extend_from_slice(payload);
|
||||
}
|
||||
unsafe {
|
||||
let needed = buf.len();
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return E_SHORT;
|
||||
}
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), result, needed);
|
||||
*result_len = needed;
|
||||
}
|
||||
OK
|
||||
}
|
||||
|
||||
fn write_tlv_i64(v: i64, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(3u8, &v.to_le_bytes())], result, result_len) }
|
||||
fn write_tlv_i64(v: i64, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(3u8, &v.to_le_bytes())], result, result_len)
|
||||
}
|
||||
|
||||
fn read_arg_i64(args: *const u8, args_len: usize, n: usize) -> Option<i64> {
|
||||
if args.is_null() || args_len < 4 { return None; }
|
||||
if args.is_null() || args_len < 4 {
|
||||
return None;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize; // header
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 { return None; }
|
||||
let tag = buf[off]; let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize;
|
||||
if buf.len() < off + 4 + size { return None; }
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
match (tag, size) {
|
||||
(3, 8) => { let mut b=[0u8;8]; b.copy_from_slice(&buf[off+4..off+12]); return Some(i64::from_le_bytes(b)); }
|
||||
(2, 4) => { let mut b=[0u8;4]; b.copy_from_slice(&buf[off+4..off+8]); let v = i32::from_le_bytes(b) as i64; return Some(v); }
|
||||
(3, 8) => {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(&buf[off + 4..off + 12]);
|
||||
return Some(i64::from_le_bytes(b));
|
||||
}
|
||||
(2, 4) => {
|
||||
let mut b = [0u8; 4];
|
||||
b.copy_from_slice(&buf[off + 4..off + 8]);
|
||||
let v = i32::from_le_bytes(b) as i64;
|
||||
return Some(v);
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,9 +4,12 @@
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Mutex, atomic::{AtomicU32, Ordering}};
|
||||
use std::os::raw::c_char;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
// Error codes
|
||||
const NYB_SUCCESS: i32 = 0;
|
||||
@ -19,37 +22,41 @@ const NYB_E_INVALID_HANDLE: i32 = -8;
|
||||
|
||||
// Methods
|
||||
const METHOD_BIRTH: u32 = 0;
|
||||
const METHOD_SIZE: u32 = 1;
|
||||
const METHOD_GET: u32 = 2; // args: i64 key -> TLV i64
|
||||
const METHOD_HAS: u32 = 3; // args: i64 key -> TLV bool
|
||||
const METHOD_SET: u32 = 4; // args: key(int|string), value(i64) -> TLV i64 (size)
|
||||
const METHOD_REMOVE:u32 = 6; // args: key(int|string) -> TLV bool (removed)
|
||||
const METHOD_SIZE: u32 = 1;
|
||||
const METHOD_GET: u32 = 2; // args: i64 key -> TLV i64
|
||||
const METHOD_HAS: u32 = 3; // args: i64 key -> TLV bool
|
||||
const METHOD_SET: u32 = 4; // args: key(int|string), value(i64) -> TLV i64 (size)
|
||||
const METHOD_REMOVE: u32 = 6; // args: key(int|string) -> TLV bool (removed)
|
||||
const METHOD_CLEAR: u32 = 7; // args: () -> TLV i64 (size after clear=0)
|
||||
const METHOD_KEYS_S:u32 = 8; // args: () -> TLV string (newline-joined keys)
|
||||
const METHOD_GET_OR:u32 = 9; // args: key(int|string), default(i64) -> TLV i64
|
||||
const METHOD_FINI: u32 = u32::MAX;
|
||||
const METHOD_KEYS_S: u32 = 8; // args: () -> TLV string (newline-joined keys)
|
||||
const METHOD_GET_OR: u32 = 9; // args: key(int|string), default(i64) -> TLV i64
|
||||
const METHOD_FINI: u32 = u32::MAX;
|
||||
// Extended string-key methods
|
||||
const METHOD_SET_STR: u32 = 10; // setS(name: string, val: i64) -> i64(size)
|
||||
const METHOD_GET_STR: u32 = 11; // getS(name: string) -> i64
|
||||
const METHOD_HAS_STR: u32 = 12; // hasS(name: string) -> bool
|
||||
const METHOD_VALUES_S: u32 = 13; // valuesStr() -> string (newline-joined)
|
||||
const METHOD_TO_JSON: u32 = 14; // toJson() -> string
|
||||
const METHOD_TO_JSON: u32 = 14; // toJson() -> string
|
||||
|
||||
// Type id (nyash.toml に合わせる)
|
||||
const TYPE_ID_MAP: u32 = 11;
|
||||
|
||||
struct MapInstance {
|
||||
data_i64: HashMap<i64,i64>,
|
||||
data_str: HashMap<String,i64>,
|
||||
data_i64: HashMap<i64, i64>,
|
||||
data_str: HashMap<String, i64>,
|
||||
}
|
||||
static INSTANCES: Lazy<Mutex<HashMap<u32, MapInstance>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static INSTANCE_COUNTER: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 { NYB_SUCCESS }
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 {
|
||||
NYB_SUCCESS
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_invoke(
|
||||
@ -61,44 +68,81 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
if type_id != TYPE_ID_MAP { return NYB_E_INVALID_TYPE; }
|
||||
if type_id != TYPE_ID_MAP {
|
||||
return NYB_E_INVALID_TYPE;
|
||||
}
|
||||
unsafe {
|
||||
match method_id {
|
||||
METHOD_BIRTH => {
|
||||
if result_len.is_null() { return NYB_E_INVALID_ARGS; }
|
||||
if preflight(result, result_len, 4) { return NYB_E_SHORT_BUFFER; }
|
||||
if result_len.is_null() {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
if preflight(result, result_len, 4) {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
let id = INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
map.insert(id, MapInstance { data_i64: HashMap::new(), data_str: HashMap::new() });
|
||||
} else { return NYB_E_PLUGIN_ERROR; }
|
||||
map.insert(
|
||||
id,
|
||||
MapInstance {
|
||||
data_i64: HashMap::new(),
|
||||
data_str: HashMap::new(),
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
}
|
||||
std::ptr::copy_nonoverlapping(id.to_le_bytes().as_ptr(), result, 4);
|
||||
*result_len = 4;
|
||||
NYB_SUCCESS
|
||||
}
|
||||
METHOD_FINI => {
|
||||
if let Ok(mut map) = INSTANCES.lock() { map.remove(&instance_id); NYB_SUCCESS } else { NYB_E_PLUGIN_ERROR }
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
map.remove(&instance_id);
|
||||
NYB_SUCCESS
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_SIZE => {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
let sz = inst.data_i64.len() + inst.data_str.len();
|
||||
write_tlv_i64(sz as i64, result, result_len)
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_GET => {
|
||||
if let Some(ik) = read_arg_i64(args, args_len, 0) {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
match inst.data_i64.get(&ik).copied() { Some(v) => write_tlv_i64(v, result, result_len), None => NYB_E_INVALID_ARGS }
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
match inst.data_i64.get(&ik).copied() {
|
||||
Some(v) => write_tlv_i64(v, result, result_len),
|
||||
None => NYB_E_INVALID_ARGS,
|
||||
}
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
} else if let Some(sk) = read_arg_string(args, args_len, 0) {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
match inst.data_str.get(&sk).copied() { Some(v) => write_tlv_i64(v, result, result_len), None => NYB_E_INVALID_ARGS }
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
match inst.data_str.get(&sk).copied() {
|
||||
Some(v) => write_tlv_i64(v, result, result_len),
|
||||
None => NYB_E_INVALID_ARGS,
|
||||
}
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
} else {
|
||||
NYB_E_INVALID_ARGS
|
||||
}
|
||||
@ -106,35 +150,58 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
METHOD_HAS => {
|
||||
if let Some(ik) = read_arg_i64(args, args_len, 0) {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) { write_tlv_bool(inst.data_i64.contains_key(&ik), result, result_len) } else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
write_tlv_bool(inst.data_i64.contains_key(&ik), result, result_len)
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
} else if let Some(sk) = read_arg_string(args, args_len, 0) {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) { write_tlv_bool(inst.data_str.contains_key(&sk), result, result_len) } else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
write_tlv_bool(inst.data_str.contains_key(&sk), result, result_len)
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
} else {
|
||||
NYB_E_INVALID_ARGS
|
||||
}
|
||||
}
|
||||
METHOD_SET => {
|
||||
// value は i64 限定
|
||||
let val = match read_arg_i64(args, args_len, 1) { Some(v) => v, None => return NYB_E_INVALID_ARGS };
|
||||
let val = match read_arg_i64(args, args_len, 1) {
|
||||
Some(v) => v,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
if let Some(ik) = read_arg_i64(args, args_len, 0) {
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get_mut(&instance_id) {
|
||||
inst.data_i64.insert(ik, val);
|
||||
let sz = inst.data_i64.len() + inst.data_str.len();
|
||||
return write_tlv_i64(sz as i64, result, result_len);
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
} else if let Some(sk) = read_arg_string(args, args_len, 0) {
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get_mut(&instance_id) {
|
||||
inst.data_str.insert(sk, val);
|
||||
let sz = inst.data_i64.len() + inst.data_str.len();
|
||||
return write_tlv_i64(sz as i64, result, result_len);
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
} else {
|
||||
NYB_E_INVALID_ARGS
|
||||
}
|
||||
@ -143,12 +210,28 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get_mut(&instance_id) {
|
||||
// try int key
|
||||
if let Some(ik) = read_arg_i64(args, args_len, 0) { return write_tlv_bool(inst.data_i64.remove(&ik).is_some(), result, result_len); }
|
||||
if let Some(ik) = read_arg_i64(args, args_len, 0) {
|
||||
return write_tlv_bool(
|
||||
inst.data_i64.remove(&ik).is_some(),
|
||||
result,
|
||||
result_len,
|
||||
);
|
||||
}
|
||||
// try string key
|
||||
if let Some(sk) = read_arg_string(args, args_len, 0) { return write_tlv_bool(inst.data_str.remove(&sk).is_some(), result, result_len); }
|
||||
if let Some(sk) = read_arg_string(args, args_len, 0) {
|
||||
return write_tlv_bool(
|
||||
inst.data_str.remove(&sk).is_some(),
|
||||
result,
|
||||
result_len,
|
||||
);
|
||||
}
|
||||
NYB_E_INVALID_ARGS
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_CLEAR => {
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
@ -156,23 +239,39 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
inst.data_i64.clear();
|
||||
inst.data_str.clear();
|
||||
return write_tlv_i64(0, result, result_len);
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_KEYS_S => {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
let mut keys: Vec<String> = Vec::with_capacity(inst.data_i64.len() + inst.data_str.len());
|
||||
for k in inst.data_i64.keys() { keys.push(k.to_string()); }
|
||||
for k in inst.data_str.keys() { keys.push(k.clone()); }
|
||||
let mut keys: Vec<String> =
|
||||
Vec::with_capacity(inst.data_i64.len() + inst.data_str.len());
|
||||
for k in inst.data_i64.keys() {
|
||||
keys.push(k.to_string());
|
||||
}
|
||||
for k in inst.data_str.keys() {
|
||||
keys.push(k.clone());
|
||||
}
|
||||
keys.sort();
|
||||
let out = keys.join("\n");
|
||||
return write_tlv_string(&out, result, result_len);
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_GET_OR => {
|
||||
let defv = match read_arg_i64(args, args_len, 1) { Some(v) => v, None => return NYB_E_INVALID_ARGS };
|
||||
let defv = match read_arg_i64(args, args_len, 1) {
|
||||
Some(v) => v,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
// prefer exact match, else default
|
||||
@ -185,44 +284,86 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
return write_tlv_i64(v, result, result_len);
|
||||
}
|
||||
NYB_E_INVALID_ARGS
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_SET_STR => {
|
||||
let key = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return NYB_E_INVALID_ARGS };
|
||||
let val = match read_arg_i64(args, args_len, 1) { Some(v) => v, None => return NYB_E_INVALID_ARGS };
|
||||
let key = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
let val = match read_arg_i64(args, args_len, 1) {
|
||||
Some(v) => v,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get_mut(&instance_id) {
|
||||
inst.data_str.insert(key, val);
|
||||
let sz = inst.data_i64.len() + inst.data_str.len();
|
||||
return write_tlv_i64(sz as i64, result, result_len);
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_GET_STR => {
|
||||
let key = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return NYB_E_INVALID_ARGS };
|
||||
let key = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
match inst.data_str.get(&key).copied() { Some(v) => write_tlv_i64(v, result, result_len), None => NYB_E_INVALID_ARGS }
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
match inst.data_str.get(&key).copied() {
|
||||
Some(v) => write_tlv_i64(v, result, result_len),
|
||||
None => NYB_E_INVALID_ARGS,
|
||||
}
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_HAS_STR => {
|
||||
let key = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return NYB_E_INVALID_ARGS };
|
||||
let key = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) { write_tlv_bool(inst.data_str.contains_key(&key), result, result_len) } else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
write_tlv_bool(inst.data_str.contains_key(&key), result, result_len)
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_VALUES_S => {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
let mut vals: Vec<String> = Vec::with_capacity(inst.data_i64.len() + inst.data_str.len());
|
||||
for v in inst.data_i64.values() { vals.push(v.to_string()); }
|
||||
for v in inst.data_str.values() { vals.push(v.to_string()); }
|
||||
let mut vals: Vec<String> =
|
||||
Vec::with_capacity(inst.data_i64.len() + inst.data_str.len());
|
||||
for v in inst.data_i64.values() {
|
||||
vals.push(v.to_string());
|
||||
}
|
||||
for v in inst.data_str.values() {
|
||||
vals.push(v.to_string());
|
||||
}
|
||||
let out = vals.join("\n");
|
||||
return write_tlv_string(&out, result, result_len);
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_TO_JSON => {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
@ -230,23 +371,35 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
let mut s = String::from("{");
|
||||
let mut first = true;
|
||||
for (k, v) in inst.data_str.iter() {
|
||||
if !first { s.push(','); }
|
||||
if !first {
|
||||
s.push(',');
|
||||
}
|
||||
first = false;
|
||||
// JSON string key
|
||||
s.push('"'); s.push_str(&escape_json(k)); s.push_str("\": ");
|
||||
s.push('"');
|
||||
s.push_str(&escape_json(k));
|
||||
s.push_str("\": ");
|
||||
s.push_str(&v.to_string());
|
||||
}
|
||||
for (k, v) in inst.data_i64.iter() {
|
||||
if !first { s.push(','); }
|
||||
if !first {
|
||||
s.push(',');
|
||||
}
|
||||
first = false;
|
||||
// numeric key as string per JSON
|
||||
s.push('"'); s.push_str(&k.to_string()); s.push_str("\": ");
|
||||
s.push('"');
|
||||
s.push_str(&k.to_string());
|
||||
s.push_str("\": ");
|
||||
s.push_str(&v.to_string());
|
||||
}
|
||||
s.push('}');
|
||||
return write_tlv_string(&s, result, result_len);
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
_ => NYB_E_INVALID_METHOD,
|
||||
}
|
||||
@ -267,7 +420,9 @@ pub struct NyashTypeBoxFfi {
|
||||
unsafe impl Sync for NyashTypeBoxFfi {}
|
||||
|
||||
extern "C" fn mapbox_resolve(name: *const c_char) -> u32 {
|
||||
if name.is_null() { return 0; }
|
||||
if name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||
match s.as_ref() {
|
||||
"size" | "len" => METHOD_SIZE,
|
||||
@ -280,41 +435,82 @@ extern "C" fn mapbox_resolve(name: *const c_char) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn mapbox_invoke_id(instance_id: u32, method_id: u32, args: *const u8, args_len: usize, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
extern "C" fn mapbox_invoke_id(
|
||||
instance_id: u32,
|
||||
method_id: u32,
|
||||
args: *const u8,
|
||||
args_len: usize,
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
match method_id {
|
||||
METHOD_SIZE => {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
let sz = inst.data_i64.len() + inst.data_str.len();
|
||||
return write_tlv_i64(sz as i64, result, result_len);
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_GET => {
|
||||
if let Some(ik) = read_arg_i64(args, args_len, 0) {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
match inst.data_i64.get(&ik).copied() { Some(v) => write_tlv_i64(v, result, result_len), None => NYB_E_INVALID_ARGS }
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
match inst.data_i64.get(&ik).copied() {
|
||||
Some(v) => write_tlv_i64(v, result, result_len),
|
||||
None => NYB_E_INVALID_ARGS,
|
||||
}
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
} else if let Some(sk) = read_arg_string(args, args_len, 0) {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
match inst.data_str.get(&sk).copied() { Some(v) => write_tlv_i64(v, result, result_len), None => NYB_E_INVALID_ARGS }
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else { NYB_E_INVALID_ARGS }
|
||||
match inst.data_str.get(&sk).copied() {
|
||||
Some(v) => write_tlv_i64(v, result, result_len),
|
||||
None => NYB_E_INVALID_ARGS,
|
||||
}
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
} else {
|
||||
NYB_E_INVALID_ARGS
|
||||
}
|
||||
}
|
||||
METHOD_HAS => {
|
||||
if let Some(ik) = read_arg_i64(args, args_len, 0) {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) { write_tlv_bool(inst.data_i64.contains_key(&ik), result, result_len) } else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
write_tlv_bool(inst.data_i64.contains_key(&ik), result, result_len)
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
} else if let Some(sk) = read_arg_string(args, args_len, 0) {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) { write_tlv_bool(inst.data_str.contains_key(&sk), result, result_len) } else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else { NYB_E_INVALID_ARGS }
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
write_tlv_bool(inst.data_str.contains_key(&sk), result, result_len)
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
} else {
|
||||
NYB_E_INVALID_ARGS
|
||||
}
|
||||
}
|
||||
METHOD_SET => {
|
||||
// key: i64 or string, value: i64
|
||||
@ -325,55 +521,104 @@ extern "C" fn mapbox_invoke_id(instance_id: u32, method_id: u32, args: *const u8
|
||||
inst.data_i64.insert(ik, val);
|
||||
let sz = inst.data_i64.len() + inst.data_str.len();
|
||||
return write_tlv_i64(sz as i64, result, result_len);
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
} else if let Some(sk) = read_arg_string(args, args_len, 0) {
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get_mut(&instance_id) {
|
||||
inst.data_str.insert(sk, val);
|
||||
let sz = inst.data_i64.len() + inst.data_str.len();
|
||||
return write_tlv_i64(sz as i64, result, result_len);
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else { NYB_E_INVALID_ARGS }
|
||||
} else { NYB_E_INVALID_ARGS }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
} else {
|
||||
NYB_E_INVALID_ARGS
|
||||
}
|
||||
} else {
|
||||
NYB_E_INVALID_ARGS
|
||||
}
|
||||
}
|
||||
METHOD_GET_STR => {
|
||||
let key = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return NYB_E_INVALID_ARGS };
|
||||
let key = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
match inst.data_str.get(&key).copied() { Some(v) => write_tlv_i64(v, result, result_len), None => NYB_E_INVALID_ARGS }
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
match inst.data_str.get(&key).copied() {
|
||||
Some(v) => write_tlv_i64(v, result, result_len),
|
||||
None => NYB_E_INVALID_ARGS,
|
||||
}
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_HAS_STR => {
|
||||
let key = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return NYB_E_INVALID_ARGS };
|
||||
let key = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return NYB_E_INVALID_ARGS,
|
||||
};
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) { write_tlv_bool(inst.data_str.contains_key(&key), result, result_len) } else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
write_tlv_bool(inst.data_str.contains_key(&key), result, result_len)
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_KEYS_S => {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
let mut keys: Vec<String> = Vec::with_capacity(inst.data_i64.len() + inst.data_str.len());
|
||||
for k in inst.data_i64.keys() { keys.push(k.to_string()); }
|
||||
for k in inst.data_str.keys() { keys.push(k.clone()); }
|
||||
let mut keys: Vec<String> =
|
||||
Vec::with_capacity(inst.data_i64.len() + inst.data_str.len());
|
||||
for k in inst.data_i64.keys() {
|
||||
keys.push(k.to_string());
|
||||
}
|
||||
for k in inst.data_str.keys() {
|
||||
keys.push(k.clone());
|
||||
}
|
||||
keys.sort();
|
||||
let out = keys.join("\n");
|
||||
return write_tlv_string(&out, result, result_len);
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
METHOD_VALUES_S => {
|
||||
if let Ok(map) = INSTANCES.lock() {
|
||||
if let Some(inst) = map.get(&instance_id) {
|
||||
let mut vals: Vec<String> = Vec::with_capacity(inst.data_i64.len() + inst.data_str.len());
|
||||
for v in inst.data_i64.values() { vals.push(v.to_string()); }
|
||||
for v in inst.data_str.values() { vals.push(v.to_string()); }
|
||||
let mut vals: Vec<String> =
|
||||
Vec::with_capacity(inst.data_i64.len() + inst.data_str.len());
|
||||
for v in inst.data_i64.values() {
|
||||
vals.push(v.to_string());
|
||||
}
|
||||
for v in inst.data_str.values() {
|
||||
vals.push(v.to_string());
|
||||
}
|
||||
let out = vals.join("\n");
|
||||
return write_tlv_string(&out, result, result_len);
|
||||
} else { NYB_E_INVALID_HANDLE }
|
||||
} else { NYB_E_PLUGIN_ERROR }
|
||||
} else {
|
||||
NYB_E_INVALID_HANDLE
|
||||
}
|
||||
} else {
|
||||
NYB_E_PLUGIN_ERROR
|
||||
}
|
||||
}
|
||||
_ => NYB_E_INVALID_METHOD,
|
||||
}
|
||||
@ -391,7 +636,7 @@ pub static nyash_typebox_MapBox: NyashTypeBoxFfi = NyashTypeBoxFfi {
|
||||
};
|
||||
|
||||
fn escape_json(s: &str) -> String {
|
||||
let mut out = String::with_capacity(s.len()+8);
|
||||
let mut out = String::with_capacity(s.len() + 8);
|
||||
for ch in s.chars() {
|
||||
match ch {
|
||||
'"' => out.push_str("\\\""),
|
||||
@ -409,7 +654,9 @@ fn escape_json(s: &str) -> String {
|
||||
// TLV helpers
|
||||
fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
unsafe {
|
||||
if result_len.is_null() { return false; }
|
||||
if result_len.is_null() {
|
||||
return false;
|
||||
}
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return true;
|
||||
@ -419,8 +666,11 @@ fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
}
|
||||
|
||||
fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
if result_len.is_null() { return NYB_E_INVALID_ARGS; }
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(4 + payloads.iter().map(|(_,p)| 4 + p.len()).sum::<usize>());
|
||||
if result_len.is_null() {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
let mut buf: Vec<u8> =
|
||||
Vec::with_capacity(4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||
buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, payload) in payloads {
|
||||
@ -441,27 +691,44 @@ fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut
|
||||
NYB_SUCCESS
|
||||
}
|
||||
|
||||
fn write_tlv_i64(v: i64, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(3u8, &v.to_le_bytes())], result, result_len) }
|
||||
fn write_tlv_bool(bv: bool, result: *mut u8, result_len: *mut usize) -> i32 { let b = [if bv {1u8} else {0u8}]; write_tlv_result(&[(1u8, &b)], result, result_len) }
|
||||
fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(6u8, s.as_bytes())], result, result_len) }
|
||||
fn write_tlv_i64(v: i64, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(3u8, &v.to_le_bytes())], result, result_len)
|
||||
}
|
||||
fn write_tlv_bool(bv: bool, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
let b = [if bv { 1u8 } else { 0u8 }];
|
||||
write_tlv_result(&[(1u8, &b)], result, result_len)
|
||||
}
|
||||
fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(6u8, s.as_bytes())], result, result_len)
|
||||
}
|
||||
|
||||
fn read_arg_i64(args: *const u8, args_len: usize, n: usize) -> Option<i64> {
|
||||
if args.is_null() || args_len < 4 { return None; }
|
||||
if args.is_null() || args_len < 4 {
|
||||
return None;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize;
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 { return None; }
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize;
|
||||
if buf.len() < off + 4 + size { return None; }
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
if tag == 3 && size == 8 {
|
||||
let mut b = [0u8;8]; b.copy_from_slice(&buf[off+4..off+12]);
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(&buf[off + 4..off + 12]);
|
||||
return Some(i64::from_le_bytes(b));
|
||||
} else if tag == 2 && size == 4 {
|
||||
let mut b = [0u8;4]; b.copy_from_slice(&buf[off+4..off+8]);
|
||||
let mut b = [0u8; 4];
|
||||
b.copy_from_slice(&buf[off + 4..off + 8]);
|
||||
return Some(i32::from_le_bytes(b) as i64);
|
||||
} else { return None; }
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
off += 4 + size;
|
||||
}
|
||||
@ -469,19 +736,27 @@ fn read_arg_i64(args: *const u8, args_len: usize, n: usize) -> Option<i64> {
|
||||
}
|
||||
|
||||
fn read_arg_string(args: *const u8, args_len: usize, n: usize) -> Option<String> {
|
||||
if args.is_null() || args_len < 4 { return None; }
|
||||
if args.is_null() || args_len < 4 {
|
||||
return None;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize;
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 { return None; }
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize;
|
||||
if buf.len() < off + 4 + size { return None; }
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
if tag == 6 || tag == 7 {
|
||||
let s = &buf[off+4..off+4+size];
|
||||
let s = &buf[off + 4..off + 4 + size];
|
||||
return Some(String::from_utf8_lossy(s).to_string());
|
||||
} else { return None; }
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
off += 4 + size;
|
||||
}
|
||||
|
||||
@ -3,7 +3,10 @@
|
||||
//! TimeBox: now() -> i64 (unix seconds)
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Mutex, atomic::{AtomicU32, Ordering}};
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
// Error codes
|
||||
const OK: i32 = 0;
|
||||
@ -37,18 +40,56 @@ static ID: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
// TLV helpers
|
||||
mod tlv {
|
||||
pub fn header(argc: u16) -> Vec<u8> { let mut b=Vec::with_capacity(4); b.extend_from_slice(&1u16.to_le_bytes()); b.extend_from_slice(&argc.to_le_bytes()); b }
|
||||
pub fn encode_handle(buf: &mut Vec<u8>, t: u32, i: u32) { buf.push(8); buf.push(0); buf.push(8); buf.push(0); buf.extend_from_slice(&t.to_le_bytes()); buf.extend_from_slice(&i.to_le_bytes()); }
|
||||
pub fn encode_i64(buf: &mut Vec<u8>, v: i64) { buf.push(3); buf.push(0); buf.push(8); buf.push(0); buf.extend_from_slice(&v.to_le_bytes()); }
|
||||
pub fn encode_void(buf: &mut Vec<u8>) { buf.push(9); buf.push(0); buf.push(0); buf.push(0); }
|
||||
pub fn decode_first(args:&[u8]) -> Option<(u16,u16,usize)> { if args.len()<8 {return None;} let argc=u16::from_le_bytes([args[2],args[3]]); if argc==0{return None;} let tag=u16::from_le_bytes([args[4],args[5]]); let sz=u16::from_le_bytes([args[6],args[7]]); Some((tag,sz,8)) }
|
||||
pub fn header(argc: u16) -> Vec<u8> {
|
||||
let mut b = Vec::with_capacity(4);
|
||||
b.extend_from_slice(&1u16.to_le_bytes());
|
||||
b.extend_from_slice(&argc.to_le_bytes());
|
||||
b
|
||||
}
|
||||
pub fn encode_handle(buf: &mut Vec<u8>, t: u32, i: u32) {
|
||||
buf.push(8);
|
||||
buf.push(0);
|
||||
buf.push(8);
|
||||
buf.push(0);
|
||||
buf.extend_from_slice(&t.to_le_bytes());
|
||||
buf.extend_from_slice(&i.to_le_bytes());
|
||||
}
|
||||
pub fn encode_i64(buf: &mut Vec<u8>, v: i64) {
|
||||
buf.push(3);
|
||||
buf.push(0);
|
||||
buf.push(8);
|
||||
buf.push(0);
|
||||
buf.extend_from_slice(&v.to_le_bytes());
|
||||
}
|
||||
pub fn encode_void(buf: &mut Vec<u8>) {
|
||||
buf.push(9);
|
||||
buf.push(0);
|
||||
buf.push(0);
|
||||
buf.push(0);
|
||||
}
|
||||
pub fn decode_first(args: &[u8]) -> Option<(u16, u16, usize)> {
|
||||
if args.len() < 8 {
|
||||
return None;
|
||||
}
|
||||
let argc = u16::from_le_bytes([args[2], args[3]]);
|
||||
if argc == 0 {
|
||||
return None;
|
||||
}
|
||||
let tag = u16::from_le_bytes([args[4], args[5]]);
|
||||
let sz = u16::from_le_bytes([args[6], args[7]]);
|
||||
Some((tag, sz, 8))
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 { OK }
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 {
|
||||
OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_invoke(
|
||||
@ -77,31 +118,67 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn birth<T>(tid: u32, map: &Lazy<Mutex<HashMap<u32,T>>>, out: *mut u8, out_len: *mut usize) -> i32 where T: Default {
|
||||
let need = 4+4+8; if *out_len < need { *out_len = need; return E_SHORT; }
|
||||
unsafe fn birth<T>(
|
||||
tid: u32,
|
||||
map: &Lazy<Mutex<HashMap<u32, T>>>,
|
||||
out: *mut u8,
|
||||
out_len: *mut usize,
|
||||
) -> i32
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
let need = 4 + 4 + 8;
|
||||
if *out_len < need {
|
||||
*out_len = need;
|
||||
return E_SHORT;
|
||||
}
|
||||
let id = ID.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut m) = map.lock() { m.insert(id, T::default()); } else { return E_FAIL; }
|
||||
let mut buf = tlv::header(1); tlv::encode_handle(&mut buf, tid, id);
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), out, buf.len()); *out_len = buf.len(); OK
|
||||
if let Ok(mut m) = map.lock() {
|
||||
m.insert(id, T::default());
|
||||
} else {
|
||||
return E_FAIL;
|
||||
}
|
||||
let mut buf = tlv::header(1);
|
||||
tlv::encode_handle(&mut buf, tid, id);
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), out, buf.len());
|
||||
*out_len = buf.len();
|
||||
OK
|
||||
}
|
||||
|
||||
unsafe fn fini<T>(map: &Lazy<Mutex<HashMap<u32,T>>>, instance_id: u32) -> i32 {
|
||||
if let Ok(mut m) = map.lock() { m.remove(&instance_id); OK } else { E_FAIL }
|
||||
unsafe fn fini<T>(map: &Lazy<Mutex<HashMap<u32, T>>>, instance_id: u32) -> i32 {
|
||||
if let Ok(mut m) = map.lock() {
|
||||
m.remove(&instance_id);
|
||||
OK
|
||||
} else {
|
||||
E_FAIL
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn sqrt_call(args: *const u8, args_len: usize, out: *mut u8, out_len: *mut usize) -> i32 {
|
||||
if args_len < 8 { return E_ARGS; }
|
||||
if args_len < 8 {
|
||||
return E_ARGS;
|
||||
}
|
||||
let a = std::slice::from_raw_parts(args, args_len);
|
||||
if let Some((tag, sz, p)) = tlv::decode_first(a) {
|
||||
if tag == 3 && sz == 8 && a.len() >= p+8 {
|
||||
let mut b=[0u8;8]; b.copy_from_slice(&a[p..p+8]);
|
||||
if tag == 3 && sz == 8 && a.len() >= p + 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(&a[p..p + 8]);
|
||||
let x = i64::from_le_bytes(b) as f64;
|
||||
let r = x.sqrt();
|
||||
let need = 4+4+8; if *out_len < need { *out_len = need; return E_SHORT; }
|
||||
let need = 4 + 4 + 8;
|
||||
if *out_len < need {
|
||||
*out_len = need;
|
||||
return E_SHORT;
|
||||
}
|
||||
let mut buf = tlv::header(1);
|
||||
// encode f64 (tag=5)
|
||||
buf.push(5); buf.push(0); buf.push(8); buf.push(0); buf.extend_from_slice(&r.to_le_bytes());
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), out, buf.len()); *out_len = buf.len();
|
||||
buf.push(5);
|
||||
buf.push(0);
|
||||
buf.push(8);
|
||||
buf.push(0);
|
||||
buf.extend_from_slice(&r.to_le_bytes());
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), out, buf.len());
|
||||
*out_len = buf.len();
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
@ -109,26 +186,53 @@ unsafe fn sqrt_call(args: *const u8, args_len: usize, out: *mut u8, out_len: *mu
|
||||
}
|
||||
|
||||
unsafe fn now_call(out: *mut u8, out_len: *mut usize) -> i32 {
|
||||
let ts = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_secs() as i64).unwrap_or(0);
|
||||
let need = 4+4+8; if *out_len < need { *out_len = need; return E_SHORT; }
|
||||
let mut buf = tlv::header(1); tlv::encode_i64(&mut buf, ts);
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), out, buf.len()); *out_len = buf.len();
|
||||
let ts = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_secs() as i64)
|
||||
.unwrap_or(0);
|
||||
let need = 4 + 4 + 8;
|
||||
if *out_len < need {
|
||||
*out_len = need;
|
||||
return E_SHORT;
|
||||
}
|
||||
let mut buf = tlv::header(1);
|
||||
tlv::encode_i64(&mut buf, ts);
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), out, buf.len());
|
||||
*out_len = buf.len();
|
||||
OK
|
||||
}
|
||||
|
||||
unsafe fn trig_call(args: *const u8, args_len: usize, out: *mut u8, out_len: *mut usize, is_sin: bool) -> i32 {
|
||||
if args_len < 8 { return E_ARGS; }
|
||||
unsafe fn trig_call(
|
||||
args: *const u8,
|
||||
args_len: usize,
|
||||
out: *mut u8,
|
||||
out_len: *mut usize,
|
||||
is_sin: bool,
|
||||
) -> i32 {
|
||||
if args_len < 8 {
|
||||
return E_ARGS;
|
||||
}
|
||||
let a = std::slice::from_raw_parts(args, args_len);
|
||||
if let Some((tag, sz, p)) = tlv::decode_first(a) {
|
||||
if tag == 3 && sz == 8 && a.len() >= p+8 {
|
||||
let mut b=[0u8;8]; b.copy_from_slice(&a[p..p+8]);
|
||||
if tag == 3 && sz == 8 && a.len() >= p + 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(&a[p..p + 8]);
|
||||
let x = i64::from_le_bytes(b) as f64;
|
||||
let r = if is_sin { x.sin() } else { x.cos() };
|
||||
let need = 4+4+8; if *out_len < need { *out_len = need; return E_SHORT; }
|
||||
let need = 4 + 4 + 8;
|
||||
if *out_len < need {
|
||||
*out_len = need;
|
||||
return E_SHORT;
|
||||
}
|
||||
let mut buf = tlv::header(1);
|
||||
// encode f64 (tag=5)
|
||||
buf.push(5); buf.push(0); buf.push(8); buf.push(0); buf.extend_from_slice(&r.to_le_bytes());
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), out, buf.len()); *out_len = buf.len();
|
||||
buf.push(5);
|
||||
buf.push(0);
|
||||
buf.push(8);
|
||||
buf.push(0);
|
||||
buf.extend_from_slice(&r.to_le_bytes());
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), out, buf.len());
|
||||
*out_len = buf.len();
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
@ -136,18 +240,30 @@ unsafe fn trig_call(args: *const u8, args_len: usize, out: *mut u8, out_len: *mu
|
||||
}
|
||||
|
||||
unsafe fn round_call(args: *const u8, args_len: usize, out: *mut u8, out_len: *mut usize) -> i32 {
|
||||
if args_len < 8 { return E_ARGS; }
|
||||
if args_len < 8 {
|
||||
return E_ARGS;
|
||||
}
|
||||
let a = std::slice::from_raw_parts(args, args_len);
|
||||
if let Some((tag, sz, p)) = tlv::decode_first(a) {
|
||||
if tag == 3 && sz == 8 && a.len() >= p+8 {
|
||||
let mut b=[0u8;8]; b.copy_from_slice(&a[p..p+8]);
|
||||
if tag == 3 && sz == 8 && a.len() >= p + 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(&a[p..p + 8]);
|
||||
let x = i64::from_le_bytes(b) as f64;
|
||||
let r = x.round();
|
||||
let need = 4+4+8; if *out_len < need { *out_len = need; return E_SHORT; }
|
||||
let need = 4 + 4 + 8;
|
||||
if *out_len < need {
|
||||
*out_len = need;
|
||||
return E_SHORT;
|
||||
}
|
||||
let mut buf = tlv::header(1);
|
||||
// encode f64 (tag=5)
|
||||
buf.push(5); buf.push(0); buf.push(8); buf.push(0); buf.extend_from_slice(&r.to_le_bytes());
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), out, buf.len()); *out_len = buf.len();
|
||||
buf.push(5);
|
||||
buf.push(0);
|
||||
buf.push(8);
|
||||
buf.push(0);
|
||||
buf.extend_from_slice(&r.to_le_bytes());
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), out, buf.len());
|
||||
*out_len = buf.len();
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use chrono::{DateTime, Datelike, Timelike, Utc};
|
||||
use rand::Rng;
|
||||
use std::ffi::{c_char, c_void, CStr, CString};
|
||||
use std::ptr;
|
||||
use chrono::{DateTime, Utc, Datelike, Timelike};
|
||||
use rand::Rng;
|
||||
|
||||
// MathBox構造体
|
||||
pub struct MathBox {
|
||||
@ -185,7 +185,7 @@ pub extern "C" fn nyash_time_parse(time_str: *const c_char) -> *mut c_void {
|
||||
if time_str.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
|
||||
unsafe {
|
||||
let c_str = CStr::from_ptr(time_str);
|
||||
if let Ok(rust_str) = c_str.to_str() {
|
||||
@ -196,7 +196,7 @@ pub extern "C" fn nyash_time_parse(time_str: *const c_char) -> *mut c_void {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
@ -217,11 +217,11 @@ pub extern "C" fn nyash_datetime_to_string(ptr: *mut c_void) -> *mut c_char {
|
||||
if ptr.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
|
||||
unsafe {
|
||||
let datetime_box = &*(ptr as *mut DateTimeBox);
|
||||
let datetime_str = datetime_box.datetime.to_rfc3339();
|
||||
|
||||
|
||||
if let Ok(c_string) = CString::new(datetime_str) {
|
||||
c_string.into_raw()
|
||||
} else {
|
||||
@ -325,4 +325,4 @@ pub extern "C" fn nyash_string_free(ptr: *mut c_char) {
|
||||
unsafe {
|
||||
let _ = CString::from_raw(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2,8 +2,11 @@
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, Component};
|
||||
use std::sync::{Mutex, atomic::{AtomicU32, Ordering}};
|
||||
use std::path::{Component, Path};
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
const OK: i32 = 0;
|
||||
const E_SHORT: i32 = -1;
|
||||
@ -12,14 +15,14 @@ const E_METHOD: i32 = -3;
|
||||
const E_ARGS: i32 = -4;
|
||||
const E_PLUGIN: i32 = -5;
|
||||
|
||||
const M_BIRTH: u32 = 0; // constructor -> instance
|
||||
const M_JOIN: u32 = 1; // join(base, rest) -> string
|
||||
const M_DIRNAME: u32 = 2; // dirname(path) -> string
|
||||
const M_BASENAME: u32 = 3; // basename(path) -> string
|
||||
const M_EXTNAME: u32 = 4; // extname(path) -> string
|
||||
const M_IS_ABS: u32 = 5; // isAbs(path) -> bool
|
||||
const M_NORMALIZE: u32 = 6; // normalize(path) -> string
|
||||
const M_FINI: u32 = u32::MAX; // fini
|
||||
const M_BIRTH: u32 = 0; // constructor -> instance
|
||||
const M_JOIN: u32 = 1; // join(base, rest) -> string
|
||||
const M_DIRNAME: u32 = 2; // dirname(path) -> string
|
||||
const M_BASENAME: u32 = 3; // basename(path) -> string
|
||||
const M_EXTNAME: u32 = 4; // extname(path) -> string
|
||||
const M_IS_ABS: u32 = 5; // isAbs(path) -> bool
|
||||
const M_NORMALIZE: u32 = 6; // normalize(path) -> string
|
||||
const M_FINI: u32 = u32::MAX; // fini
|
||||
|
||||
const TYPE_ID_PATH: u32 = 55;
|
||||
|
||||
@ -29,10 +32,14 @@ static INST: Lazy<Mutex<HashMap<u32, PathInstance>>> = Lazy::new(|| Mutex::new(H
|
||||
static NEXT_ID: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 { OK }
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 {
|
||||
OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_invoke(
|
||||
@ -44,45 +51,99 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
if type_id != TYPE_ID_PATH { return E_TYPE; }
|
||||
if type_id != TYPE_ID_PATH {
|
||||
return E_TYPE;
|
||||
}
|
||||
unsafe {
|
||||
match method_id {
|
||||
M_BIRTH => {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
if preflight(result, result_len, 4) { return E_SHORT; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
if preflight(result, result_len, 4) {
|
||||
return E_SHORT;
|
||||
}
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut m) = INST.lock() { m.insert(id, PathInstance); } else { return E_PLUGIN; }
|
||||
let b = id.to_le_bytes(); std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4); *result_len = 4; OK
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.insert(id, PathInstance);
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
let b = id.to_le_bytes();
|
||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
||||
*result_len = 4;
|
||||
OK
|
||||
}
|
||||
M_FINI => {
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.remove(&instance_id);
|
||||
OK
|
||||
} else {
|
||||
E_PLUGIN
|
||||
}
|
||||
}
|
||||
M_FINI => { if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_PLUGIN } }
|
||||
M_JOIN => {
|
||||
let base = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let rest = match read_arg_string(args, args_len, 1) { Some(s) => s, None => return E_ARGS };
|
||||
let joined = if base.ends_with('/') || base.ends_with('\\') { format!("{}{}", base, rest) } else { format!("{}/{}", base, rest) };
|
||||
let base = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let rest = match read_arg_string(args, args_len, 1) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let joined = if base.ends_with('/') || base.ends_with('\\') {
|
||||
format!("{}{}", base, rest)
|
||||
} else {
|
||||
format!("{}/{}", base, rest)
|
||||
};
|
||||
write_tlv_string(&joined, result, result_len)
|
||||
}
|
||||
M_DIRNAME => {
|
||||
let p = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let d = Path::new(&p).parent().map(|x| x.to_string_lossy().to_string()).unwrap_or_else(|| "".to_string());
|
||||
let p = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let d = Path::new(&p)
|
||||
.parent()
|
||||
.map(|x| x.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|| "".to_string());
|
||||
write_tlv_string(&d, result, result_len)
|
||||
}
|
||||
M_BASENAME => {
|
||||
let p = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let b = Path::new(&p).file_name().map(|x| x.to_string_lossy().to_string()).unwrap_or_else(|| "".to_string());
|
||||
let p = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let b = Path::new(&p)
|
||||
.file_name()
|
||||
.map(|x| x.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|| "".to_string());
|
||||
write_tlv_string(&b, result, result_len)
|
||||
}
|
||||
M_EXTNAME => {
|
||||
let p = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let ext = Path::new(&p).extension().map(|x| format!(".{}", x.to_string_lossy())).unwrap_or_else(|| "".to_string());
|
||||
let p = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let ext = Path::new(&p)
|
||||
.extension()
|
||||
.map(|x| format!(".{}", x.to_string_lossy()))
|
||||
.unwrap_or_else(|| "".to_string());
|
||||
write_tlv_string(&ext, result, result_len)
|
||||
}
|
||||
M_IS_ABS => {
|
||||
let p = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let p = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let abs = Path::new(&p).is_absolute() || p.contains(":\\");
|
||||
write_tlv_bool(abs, result, result_len)
|
||||
}
|
||||
M_NORMALIZE => {
|
||||
let p = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let p = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let norm = path_clean::PathClean::clean(Path::new(&p));
|
||||
write_tlv_string(norm.to_string_lossy().as_ref(), result, result_len)
|
||||
}
|
||||
@ -92,23 +153,72 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
}
|
||||
|
||||
fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
unsafe { if result_len.is_null() { return false; } if result.is_null() || *result_len < needed { *result_len = needed; return true; } }
|
||||
unsafe {
|
||||
if result_len.is_null() {
|
||||
return false;
|
||||
}
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(4 + payloads.iter().map(|(_,p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes()); buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, payload) in payloads { buf.push(*tag); buf.push(0); buf.extend_from_slice(&(payload.len() as u16).to_le_bytes()); buf.extend_from_slice(payload); }
|
||||
unsafe { let needed = buf.len(); if result.is_null() || *result_len < needed { *result_len = needed; return E_SHORT; } std::ptr::copy_nonoverlapping(buf.as_ptr(), result, needed); *result_len = needed; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
let mut buf: Vec<u8> =
|
||||
Vec::with_capacity(4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||
buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, payload) in payloads {
|
||||
buf.push(*tag);
|
||||
buf.push(0);
|
||||
buf.extend_from_slice(&(payload.len() as u16).to_le_bytes());
|
||||
buf.extend_from_slice(payload);
|
||||
}
|
||||
unsafe {
|
||||
let needed = buf.len();
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return E_SHORT;
|
||||
}
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), result, needed);
|
||||
*result_len = needed;
|
||||
}
|
||||
OK
|
||||
}
|
||||
fn write_tlv_bool(v: bool, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(1u8, &[if v {1u8} else {0u8}])], result, result_len) }
|
||||
fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(6u8, s.as_bytes())], result, result_len) }
|
||||
fn write_tlv_bool(v: bool, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(1u8, &[if v { 1u8 } else { 0u8 }])], result, result_len)
|
||||
}
|
||||
fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(6u8, s.as_bytes())], result, result_len)
|
||||
}
|
||||
|
||||
fn read_arg_string(args: *const u8, args_len: usize, n: usize) -> Option<String> {
|
||||
if args.is_null() || args_len < 4 { return None; }
|
||||
if args.is_null() || args_len < 4 {
|
||||
return None;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize; for i in 0..=n { if buf.len() < off + 4 { return None; } let tag = buf[off]; let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize; if buf.len() < off + 4 + size { return None; } if i == n { if tag == 6 || tag == 7 { return Some(String::from_utf8_lossy(&buf[off+4..off+4+size]).to_string()); } else { return None; } } off += 4 + size; }
|
||||
let mut off = 4usize;
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
if tag == 6 || tag == 7 {
|
||||
return Some(String::from_utf8_lossy(&buf[off + 4..off + 4 + size]).to_string());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
off += 4 + size;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Mutex;
|
||||
use serde_json::Value as Json;
|
||||
use std::sync::Mutex;
|
||||
|
||||
const NYB_SUCCESS: i32 = 0;
|
||||
const NYB_E_INVALID_METHOD: i32 = -3;
|
||||
@ -14,10 +14,14 @@ const METHOD_FINI: u32 = u32::MAX;
|
||||
static NEXT_ID: Lazy<Mutex<u32>> = Lazy::new(|| Mutex::new(1));
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 { NYB_SUCCESS }
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 {
|
||||
NYB_SUCCESS
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_invoke(
|
||||
@ -29,14 +33,20 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
if type_id != TYPE_ID_COMPILER { return NYB_E_INVALID_METHOD; }
|
||||
if type_id != TYPE_ID_COMPILER {
|
||||
return NYB_E_INVALID_METHOD;
|
||||
}
|
||||
match method_id {
|
||||
METHOD_BIRTH => {
|
||||
unsafe {
|
||||
let mut id_g = NEXT_ID.lock().unwrap();
|
||||
let id = *id_g; *id_g += 1;
|
||||
let id = *id_g;
|
||||
*id_g += 1;
|
||||
let need = 4usize;
|
||||
if *result_len < need { *result_len = need; return NYB_E_SHORT_BUFFER; }
|
||||
if *result_len < need {
|
||||
*result_len = need;
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
let out = std::slice::from_raw_parts_mut(result, *result_len);
|
||||
out[0..4].copy_from_slice(&(id as u32).to_le_bytes());
|
||||
*result_len = need;
|
||||
@ -46,16 +56,20 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
METHOD_COMPILE => {
|
||||
// Decode TLV first string arg as JSON IR
|
||||
let ir = unsafe {
|
||||
if args.is_null() || args_len < 8 { None } else {
|
||||
if args.is_null() || args_len < 8 {
|
||||
None
|
||||
} else {
|
||||
let buf = std::slice::from_raw_parts(args, args_len);
|
||||
let tag = u16::from_le_bytes([buf[4], buf[5]]);
|
||||
let len = u16::from_le_bytes([buf[6], buf[7]]) as usize;
|
||||
if tag == 6 && 8 + len <= buf.len() {
|
||||
match std::str::from_utf8(&buf[8..8+len]) {
|
||||
match std::str::from_utf8(&buf[8..8 + len]) {
|
||||
Ok(s) => Some(s.to_string()),
|
||||
Err(_) => None
|
||||
Err(_) => None,
|
||||
}
|
||||
} else { None }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
let nyash_source = if let Some(s) = ir.or_else(|| std::env::var("NYASH_PY_IR").ok()) {
|
||||
@ -67,15 +81,22 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
} else if let Some(module) = map.get("module") {
|
||||
// Try module.functions[0].name and maybe return value
|
||||
let mut ret_expr = "0".to_string();
|
||||
if let Some(funcs) = module.get("functions").and_then(|v| v.as_array()) {
|
||||
if let Some(funcs) = module.get("functions").and_then(|v| v.as_array())
|
||||
{
|
||||
if let Some(fun0) = funcs.get(0) {
|
||||
if let Some(retv) = fun0.get("return_value") {
|
||||
if retv.is_number() { ret_expr = retv.to_string(); }
|
||||
else if let Some(s) = retv.as_str() { ret_expr = s.to_string(); }
|
||||
if retv.is_number() {
|
||||
ret_expr = retv.to_string();
|
||||
} else if let Some(s) = retv.as_str() {
|
||||
ret_expr = s.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
format!("static box Generated {{\n main() {{\n return {}\n }}\n}}", ret_expr)
|
||||
format!(
|
||||
"static box Generated {{\n main() {{\n return {}\n }}\n}}",
|
||||
ret_expr
|
||||
)
|
||||
} else {
|
||||
"static box Generated { main() { return 0 } }".to_string()
|
||||
}
|
||||
@ -88,11 +109,14 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
unsafe {
|
||||
let bytes = nyash_source.as_bytes();
|
||||
let need = 4 + bytes.len();
|
||||
if *result_len < need { *result_len = need; return NYB_E_SHORT_BUFFER; }
|
||||
if *result_len < need {
|
||||
*result_len = need;
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
let out = std::slice::from_raw_parts_mut(result, *result_len);
|
||||
out[0..2].copy_from_slice(&6u16.to_le_bytes());
|
||||
out[2..4].copy_from_slice(&(bytes.len() as u16).to_le_bytes());
|
||||
out[4..4+bytes.len()].copy_from_slice(bytes);
|
||||
out[4..4 + bytes.len()].copy_from_slice(bytes);
|
||||
*result_len = need;
|
||||
}
|
||||
NYB_SUCCESS
|
||||
|
||||
@ -46,11 +46,11 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
const METHOD_BIRTH: u32 = 0;
|
||||
const METHOD_PARSE: u32 = 1;
|
||||
const METHOD_FINI: u32 = u32::MAX;
|
||||
|
||||
|
||||
if type_id != TYPE_ID_PARSER {
|
||||
return -3; // NYB_E_INVALID_METHOD
|
||||
}
|
||||
|
||||
|
||||
match method_id {
|
||||
METHOD_BIRTH => {
|
||||
// インスタンスIDを返す
|
||||
@ -71,7 +71,8 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
let code = unsafe {
|
||||
if args.is_null() || args_len < 4 {
|
||||
// 引数なしの場合は環境変数から取得
|
||||
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||
std::env::var("NYASH_PY_CODE")
|
||||
.unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||
} else {
|
||||
// TLVデコード(簡易版)
|
||||
let buf = std::slice::from_raw_parts(args, args_len);
|
||||
@ -79,24 +80,25 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
let tag = u16::from_le_bytes([buf[0], buf[1]]);
|
||||
let len = u16::from_le_bytes([buf[2], buf[3]]) as usize;
|
||||
if tag == 6 && 4 + len <= args_len {
|
||||
match std::str::from_utf8(&buf[4..4+len]) {
|
||||
match std::str::from_utf8(&buf[4..4 + len]) {
|
||||
Ok(s) => s.to_string(),
|
||||
Err(_) => std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||
Err(_) => std::env::var("NYASH_PY_CODE")
|
||||
.unwrap_or_else(|_| "def main():\n return 0".to_string()),
|
||||
}
|
||||
} else {
|
||||
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||
std::env::var("NYASH_PY_CODE")
|
||||
.unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||
}
|
||||
} else {
|
||||
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||
std::env::var("NYASH_PY_CODE")
|
||||
.unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// パース実行
|
||||
let parse_result = Python::with_gil(|py| {
|
||||
parse_python_code(py, &code)
|
||||
});
|
||||
|
||||
let parse_result = Python::with_gil(|py| parse_python_code(py, &code));
|
||||
|
||||
// JSONにシリアライズ
|
||||
match serde_json::to_string(&parse_result) {
|
||||
Ok(json) => {
|
||||
@ -111,22 +113,24 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
// TLVエンコード(tag=6:string)
|
||||
out[0..2].copy_from_slice(&6u16.to_le_bytes());
|
||||
out[2..4].copy_from_slice(&(bytes.len() as u16).to_le_bytes());
|
||||
out[4..4+bytes.len()].copy_from_slice(bytes);
|
||||
out[4..4 + bytes.len()].copy_from_slice(bytes);
|
||||
*result_len = need;
|
||||
}
|
||||
0
|
||||
}
|
||||
Err(_) => -4 // エラー
|
||||
Err(_) => -4, // エラー
|
||||
}
|
||||
}
|
||||
METHOD_FINI => 0,
|
||||
_ => -3 // NYB_E_INVALID_METHOD
|
||||
_ => -3, // NYB_E_INVALID_METHOD
|
||||
}
|
||||
}
|
||||
|
||||
/// FFI: Pythonコードをパース
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_python_parse(code: *const std::os::raw::c_char) -> *mut std::os::raw::c_char {
|
||||
pub extern "C" fn nyash_python_parse(
|
||||
code: *const std::os::raw::c_char,
|
||||
) -> *mut std::os::raw::c_char {
|
||||
let code = unsafe {
|
||||
if code.is_null() {
|
||||
return std::ptr::null_mut();
|
||||
@ -137,9 +141,7 @@ pub extern "C" fn nyash_python_parse(code: *const std::os::raw::c_char) -> *mut
|
||||
}
|
||||
};
|
||||
|
||||
let result = Python::with_gil(|py| {
|
||||
parse_python_code(py, code)
|
||||
});
|
||||
let result = Python::with_gil(|py| parse_python_code(py, code));
|
||||
|
||||
match serde_json::to_string(&result) {
|
||||
Ok(json) => {
|
||||
@ -253,7 +255,7 @@ fn analyze_ast(_py: Python, node: &Bound<'_, PyAny>, result: &mut ParseResult) {
|
||||
// 再帰的に解析(ただし walk は全ノードを返すので、
|
||||
// 実際には再帰なしでフラットに処理される)
|
||||
result.counts.total_nodes += 1;
|
||||
|
||||
|
||||
if let Ok(class_obj) = child.getattr("__class__") {
|
||||
if let Ok(name_obj) = class_obj.getattr("__name__") {
|
||||
if let Ok(type_name) = name_obj.extract::<String>() {
|
||||
@ -265,15 +267,25 @@ fn analyze_ast(_py: Python, node: &Bound<'_, PyAny>, result: &mut ParseResult) {
|
||||
"AsyncFunctionDef" => {
|
||||
result.counts.functions += 1;
|
||||
result.counts.unsupported += 1;
|
||||
if !result.unsupported.contains(&"async function".to_string()) {
|
||||
result.unsupported.push("async function".to_string());
|
||||
if !result
|
||||
.unsupported
|
||||
.contains(&"async function".to_string())
|
||||
{
|
||||
result
|
||||
.unsupported
|
||||
.push("async function".to_string());
|
||||
}
|
||||
}
|
||||
"ClassDef" => {
|
||||
result.counts.classes += 1;
|
||||
result.counts.unsupported += 1;
|
||||
if !result.unsupported.contains(&"class definition".to_string()) {
|
||||
result.unsupported.push("class definition".to_string());
|
||||
if !result
|
||||
.unsupported
|
||||
.contains(&"class definition".to_string())
|
||||
{
|
||||
result
|
||||
.unsupported
|
||||
.push("class definition".to_string());
|
||||
}
|
||||
}
|
||||
"For" | "While" | "If" => {
|
||||
@ -281,8 +293,13 @@ fn analyze_ast(_py: Python, node: &Bound<'_, PyAny>, result: &mut ParseResult) {
|
||||
}
|
||||
"Yield" | "YieldFrom" => {
|
||||
result.counts.unsupported += 1;
|
||||
if !result.unsupported.contains(&"generator".to_string()) {
|
||||
result.unsupported.push("generator".to_string());
|
||||
if !result
|
||||
.unsupported
|
||||
.contains(&"generator".to_string())
|
||||
{
|
||||
result
|
||||
.unsupported
|
||||
.push("generator".to_string());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -305,14 +322,14 @@ mod tests {
|
||||
#[test]
|
||||
fn test_simple_parse() {
|
||||
pyo3::prepare_freethreaded_python();
|
||||
|
||||
|
||||
Python::with_gil(|py| {
|
||||
let code = "def main():\n return 0";
|
||||
let result = parse_python_code(py, code);
|
||||
|
||||
|
||||
assert!(result.success);
|
||||
assert_eq!(result.counts.functions, 1);
|
||||
assert_eq!(result.counts.supported, 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,10 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Mutex, atomic::{AtomicU32, Ordering}};
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
// Error/status codes aligned with other plugins
|
||||
const OK: i32 = 0;
|
||||
@ -15,27 +18,33 @@ const E_PLUGIN: i32 = -5;
|
||||
const E_HANDLE: i32 = -8;
|
||||
|
||||
// Methods
|
||||
const M_BIRTH: u32 = 0; // birth(pattern?) -> instance
|
||||
const M_COMPILE: u32 = 1; // compile(pattern) -> self (new compiled)
|
||||
const M_IS_MATCH: u32 = 2; // isMatch(text) -> bool
|
||||
const M_FIND: u32 = 3; // find(text) -> String (first match or empty)
|
||||
const M_BIRTH: u32 = 0; // birth(pattern?) -> instance
|
||||
const M_COMPILE: u32 = 1; // compile(pattern) -> self (new compiled)
|
||||
const M_IS_MATCH: u32 = 2; // isMatch(text) -> bool
|
||||
const M_FIND: u32 = 3; // find(text) -> String (first match or empty)
|
||||
const M_REPLACE_ALL: u32 = 4; // replaceAll(text, repl) -> String
|
||||
const M_SPLIT: u32 = 5; // split(text, limit) -> String (joined by '\n') minimal
|
||||
const M_SPLIT: u32 = 5; // split(text, limit) -> String (joined by '\n') minimal
|
||||
const M_FINI: u32 = u32::MAX; // fini()
|
||||
|
||||
// Assign an unused type id (see nyash.toml [box_types])
|
||||
const TYPE_ID_REGEX: u32 = 52;
|
||||
|
||||
struct RegexInstance { re: Option<Regex> }
|
||||
struct RegexInstance {
|
||||
re: Option<Regex>,
|
||||
}
|
||||
|
||||
static INST: Lazy<Mutex<HashMap<u32, RegexInstance>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static NEXT_ID: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 { OK }
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 {
|
||||
OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_invoke(
|
||||
@ -47,42 +56,155 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
if type_id != TYPE_ID_REGEX { return E_TYPE; }
|
||||
if type_id != TYPE_ID_REGEX {
|
||||
return E_TYPE;
|
||||
}
|
||||
unsafe {
|
||||
match method_id {
|
||||
M_BIRTH => {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
if preflight(result, result_len, 4) { return E_SHORT; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
if preflight(result, result_len, 4) {
|
||||
return E_SHORT;
|
||||
}
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
// Optional pattern in arg0
|
||||
let inst = if let Some(pat) = read_arg_string(args, args_len, 0) {
|
||||
match Regex::new(&pat) { Ok(re) => RegexInstance { re: Some(re) }, Err(_) => RegexInstance { re: None } }
|
||||
} else { RegexInstance { re: None } };
|
||||
if let Ok(mut m) = INST.lock() { m.insert(id, inst); } else { return E_PLUGIN; }
|
||||
let b = id.to_le_bytes(); std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4); *result_len = 4; OK
|
||||
match Regex::new(&pat) {
|
||||
Ok(re) => RegexInstance { re: Some(re) },
|
||||
Err(_) => RegexInstance { re: None },
|
||||
}
|
||||
} else {
|
||||
RegexInstance { re: None }
|
||||
};
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.insert(id, inst);
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
let b = id.to_le_bytes();
|
||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
||||
*result_len = 4;
|
||||
OK
|
||||
}
|
||||
M_FINI => {
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.remove(&instance_id);
|
||||
OK
|
||||
} else {
|
||||
E_PLUGIN
|
||||
}
|
||||
}
|
||||
M_FINI => { if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_PLUGIN } }
|
||||
M_COMPILE => {
|
||||
let pat = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
if let Ok(mut m) = INST.lock() { if let Some(inst) = m.get_mut(&instance_id) { inst.re = Regex::new(&pat).ok(); OK } else { E_HANDLE } } else { E_PLUGIN }
|
||||
let pat = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
if let Some(inst) = m.get_mut(&instance_id) {
|
||||
inst.re = Regex::new(&pat).ok();
|
||||
OK
|
||||
} else {
|
||||
E_HANDLE
|
||||
}
|
||||
} else {
|
||||
E_PLUGIN
|
||||
}
|
||||
}
|
||||
M_IS_MATCH => {
|
||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
if let Ok(m) = INST.lock() { if let Some(inst) = m.get(&instance_id) { if let Some(re) = &inst.re { return write_tlv_bool(re.is_match(&text), result, result_len); } else { return write_tlv_bool(false, result, result_len); } } else { return E_HANDLE; } } else { return E_PLUGIN; }
|
||||
let text = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
if let Some(re) = &inst.re {
|
||||
return write_tlv_bool(re.is_match(&text), result, result_len);
|
||||
} else {
|
||||
return write_tlv_bool(false, result, result_len);
|
||||
}
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_FIND => {
|
||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
if let Ok(m) = INST.lock() { if let Some(inst) = m.get(&instance_id) { if let Some(re) = &inst.re { let s = re.find(&text).map(|m| m.as_str().to_string()).unwrap_or_else(|| "".to_string()); return write_tlv_string(&s, result, result_len); } else { return write_tlv_string("", result, result_len); } } else { return E_HANDLE; } } else { return E_PLUGIN; }
|
||||
let text = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
if let Some(re) = &inst.re {
|
||||
let s = re
|
||||
.find(&text)
|
||||
.map(|m| m.as_str().to_string())
|
||||
.unwrap_or_else(|| "".to_string());
|
||||
return write_tlv_string(&s, result, result_len);
|
||||
} else {
|
||||
return write_tlv_string("", result, result_len);
|
||||
}
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_REPLACE_ALL => {
|
||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let repl = match read_arg_string(args, args_len, 1) { Some(s) => s, None => return E_ARGS };
|
||||
if let Ok(m) = INST.lock() { if let Some(inst) = m.get(&instance_id) { if let Some(re) = &inst.re { let out = re.replace_all(&text, repl.as_str()).to_string(); return write_tlv_string(&out, result, result_len); } else { return write_tlv_string(&text, result, result_len); } } else { return E_HANDLE; } } else { return E_PLUGIN; }
|
||||
let text = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let repl = match read_arg_string(args, args_len, 1) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
if let Some(re) = &inst.re {
|
||||
let out = re.replace_all(&text, repl.as_str()).to_string();
|
||||
return write_tlv_string(&out, result, result_len);
|
||||
} else {
|
||||
return write_tlv_string(&text, result, result_len);
|
||||
}
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_SPLIT => {
|
||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let text = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let limit = read_arg_i64(args, args_len, 1).unwrap_or(0);
|
||||
if let Ok(m) = INST.lock() { if let Some(inst) = m.get(&instance_id) { if let Some(re) = &inst.re { let mut parts: Vec<String> = if limit > 0 { re.splitn(&text, limit as usize).map(|s| s.to_string()).collect() } else { re.split(&text).map(|s| s.to_string()).collect() }; let out = parts.join("\n"); return write_tlv_string(&out, result, result_len); } else { return write_tlv_string(&text, result, result_len); } } else { return E_HANDLE; } } else { return E_PLUGIN; }
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
if let Some(re) = &inst.re {
|
||||
let mut parts: Vec<String> = if limit > 0 {
|
||||
re.splitn(&text, limit as usize)
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
} else {
|
||||
re.split(&text).map(|s| s.to_string()).collect()
|
||||
};
|
||||
let out = parts.join("\n");
|
||||
return write_tlv_string(&out, result, result_len);
|
||||
} else {
|
||||
return write_tlv_string(&text, result, result_len);
|
||||
}
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
_ => E_METHOD,
|
||||
}
|
||||
@ -90,31 +212,103 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
}
|
||||
|
||||
fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
unsafe { if result_len.is_null() { return false; } if result.is_null() || *result_len < needed { *result_len = needed; return true; } }
|
||||
unsafe {
|
||||
if result_len.is_null() {
|
||||
return false;
|
||||
}
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(4 + payloads.iter().map(|(_,p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes()); buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, payload) in payloads { buf.push(*tag); buf.push(0); buf.extend_from_slice(&(payload.len() as u16).to_le_bytes()); buf.extend_from_slice(payload); }
|
||||
unsafe { let needed = buf.len(); if result.is_null() || *result_len < needed { *result_len = needed; return E_SHORT; } std::ptr::copy_nonoverlapping(buf.as_ptr(), result, needed); *result_len = needed; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
let mut buf: Vec<u8> =
|
||||
Vec::with_capacity(4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||
buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, payload) in payloads {
|
||||
buf.push(*tag);
|
||||
buf.push(0);
|
||||
buf.extend_from_slice(&(payload.len() as u16).to_le_bytes());
|
||||
buf.extend_from_slice(payload);
|
||||
}
|
||||
unsafe {
|
||||
let needed = buf.len();
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return E_SHORT;
|
||||
}
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), result, needed);
|
||||
*result_len = needed;
|
||||
}
|
||||
OK
|
||||
}
|
||||
fn write_tlv_i64(v: i64, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(3u8, &v.to_le_bytes())], result, result_len) }
|
||||
fn write_tlv_bool(v: bool, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(1u8, &[if v {1u8} else {0u8}])], result, result_len) }
|
||||
fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(6u8, s.as_bytes())], result, result_len) }
|
||||
fn write_tlv_i64(v: i64, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(3u8, &v.to_le_bytes())], result, result_len)
|
||||
}
|
||||
fn write_tlv_bool(v: bool, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(1u8, &[if v { 1u8 } else { 0u8 }])], result, result_len)
|
||||
}
|
||||
fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(6u8, s.as_bytes())], result, result_len)
|
||||
}
|
||||
|
||||
fn read_arg_i64(args: *const u8, args_len: usize, n: usize) -> Option<i64> {
|
||||
if args.is_null() || args_len < 4 { return None; }
|
||||
if args.is_null() || args_len < 4 {
|
||||
return None;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize; for i in 0..=n { if buf.len() < off + 4 { return None; } let tag = buf[off]; let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize; if buf.len() < off + 4 + size { return None; } if i == n { if tag != 3 || size != 8 { return None; } let mut b=[0u8;8]; b.copy_from_slice(&buf[off+4..off+12]); return Some(i64::from_le_bytes(b)); } off += 4 + size; }
|
||||
let mut off = 4usize;
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
if tag != 3 || size != 8 {
|
||||
return None;
|
||||
}
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(&buf[off + 4..off + 12]);
|
||||
return Some(i64::from_le_bytes(b));
|
||||
}
|
||||
off += 4 + size;
|
||||
}
|
||||
None
|
||||
}
|
||||
fn read_arg_string(args: *const u8, args_len: usize, n: usize) -> Option<String> {
|
||||
if args.is_null() || args_len < 4 { return None; }
|
||||
if args.is_null() || args_len < 4 {
|
||||
return None;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize; for i in 0..=n { if buf.len() < off + 4 { return None; } let tag = buf[off]; let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize; if buf.len() < off + 4 + size { return None; } if i == n { if tag == 6 || tag == 7 { let s = String::from_utf8_lossy(&buf[off+4..off+4+size]).to_string(); return Some(s); } else { return None; } } off += 4 + size; }
|
||||
let mut off = 4usize;
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
if tag == 6 || tag == 7 {
|
||||
let s = String::from_utf8_lossy(&buf[off + 4..off + 4 + size]).to_string();
|
||||
return Some(s);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
off += 4 + size;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
@ -3,9 +3,12 @@
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Mutex, atomic::{AtomicU32, Ordering}};
|
||||
use std::os::raw::c_char;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
const OK: i32 = 0;
|
||||
const E_SHORT: i32 = -1;
|
||||
@ -19,23 +22,29 @@ const M_BIRTH: u32 = 0;
|
||||
const M_LENGTH: u32 = 1;
|
||||
const M_IS_EMPTY: u32 = 2;
|
||||
const M_CHAR_CODE_AT: u32 = 3;
|
||||
const M_CONCAT: u32 = 4; // concat(other: String|Handle) -> Handle(new)
|
||||
const M_FROM_UTF8: u32 = 5; // fromUtf8(data: String|Bytes) -> Handle(new)
|
||||
const M_TO_UTF8: u32 = 6; // toUtf8() -> String
|
||||
const M_CONCAT: u32 = 4; // concat(other: String|Handle) -> Handle(new)
|
||||
const M_FROM_UTF8: u32 = 5; // fromUtf8(data: String|Bytes) -> Handle(new)
|
||||
const M_TO_UTF8: u32 = 6; // toUtf8() -> String
|
||||
const M_FINI: u32 = u32::MAX;
|
||||
|
||||
const TYPE_ID_STRING: u32 = 13;
|
||||
|
||||
struct StrInstance { s: String }
|
||||
struct StrInstance {
|
||||
s: String,
|
||||
}
|
||||
|
||||
static INST: Lazy<Mutex<HashMap<u32, StrInstance>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static NEXT_ID: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 { OK }
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 {
|
||||
OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_invoke(
|
||||
@ -47,29 +56,69 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
if type_id != TYPE_ID_STRING { return E_TYPE; }
|
||||
if type_id != TYPE_ID_STRING {
|
||||
return E_TYPE;
|
||||
}
|
||||
unsafe {
|
||||
match method_id {
|
||||
M_BIRTH => {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
if preflight(result, result_len, 4) { return E_SHORT; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
if preflight(result, result_len, 4) {
|
||||
return E_SHORT;
|
||||
}
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
// Optional init from first arg (String/Bytes)
|
||||
let init = read_arg_string(args, args_len, 0).unwrap_or_else(|| String::new());
|
||||
if let Ok(mut m) = INST.lock() { m.insert(id, StrInstance { s: init }); }
|
||||
else { return E_PLUGIN; }
|
||||
let b = id.to_le_bytes(); std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4); *result_len = 4; OK
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.insert(id, StrInstance { s: init });
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
let b = id.to_le_bytes();
|
||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
||||
*result_len = 4;
|
||||
OK
|
||||
}
|
||||
M_FINI => {
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.remove(&instance_id);
|
||||
OK
|
||||
} else {
|
||||
E_PLUGIN
|
||||
}
|
||||
}
|
||||
M_FINI => { if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_PLUGIN } }
|
||||
M_LENGTH => {
|
||||
if let Ok(m) = INST.lock() { if let Some(inst) = m.get(&instance_id) { return write_tlv_i64(inst.s.len() as i64, result, result_len); } else { return E_HANDLE; } } else { return E_PLUGIN; }
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
return write_tlv_i64(inst.s.len() as i64, result, result_len);
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_IS_EMPTY => {
|
||||
if let Ok(m) = INST.lock() { if let Some(inst) = m.get(&instance_id) { return write_tlv_bool(inst.s.is_empty(), result, result_len); } else { return E_HANDLE; } } else { return E_PLUGIN; }
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
return write_tlv_bool(inst.s.is_empty(), result, result_len);
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_CHAR_CODE_AT => {
|
||||
let idx = match read_arg_i64(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
if idx < 0 { return E_ARGS; }
|
||||
let idx = match read_arg_i64(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if idx < 0 {
|
||||
return E_ARGS;
|
||||
}
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
// Interpret index as char-index into Unicode scalar values
|
||||
@ -77,40 +126,78 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
let ch_opt = inst.s.chars().nth(i);
|
||||
let code = ch_opt.map(|c| c as u32 as i64).unwrap_or(0);
|
||||
return write_tlv_i64(code, result, result_len);
|
||||
} else { return E_HANDLE; }
|
||||
} else { return E_PLUGIN; }
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_CONCAT => {
|
||||
// Accept either Handle(tag=8) to another StringBox, or String/Bytes payload
|
||||
let (ok, rhs) = if let Some((t, inst)) = read_arg_handle(args, args_len, 0) {
|
||||
if t != TYPE_ID_STRING { return E_TYPE; }
|
||||
if let Ok(m) = INST.lock() { if let Some(s2) = m.get(&inst) { (true, s2.s.clone()) } else { (false, String::new()) } } else { return E_PLUGIN; }
|
||||
} else if let Some(s) = read_arg_string(args, args_len, 0) { (true, s) } else { (false, String::new()) };
|
||||
if !ok { return E_ARGS; }
|
||||
if t != TYPE_ID_STRING {
|
||||
return E_TYPE;
|
||||
}
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(s2) = m.get(&inst) {
|
||||
(true, s2.s.clone())
|
||||
} else {
|
||||
(false, String::new())
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
} else if let Some(s) = read_arg_string(args, args_len, 0) {
|
||||
(true, s)
|
||||
} else {
|
||||
(false, String::new())
|
||||
};
|
||||
if !ok {
|
||||
return E_ARGS;
|
||||
}
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
let mut new_s = inst.s.clone();
|
||||
new_s.push_str(&rhs);
|
||||
drop(m);
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut mm) = INST.lock() { mm.insert(id, StrInstance { s: new_s }); }
|
||||
if let Ok(mut mm) = INST.lock() {
|
||||
mm.insert(id, StrInstance { s: new_s });
|
||||
}
|
||||
return write_tlv_handle(TYPE_ID_STRING, id, result, result_len);
|
||||
} else { return E_HANDLE; }
|
||||
} else { return E_PLUGIN; }
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_FROM_UTF8 => {
|
||||
// Create new instance from UTF-8 (accept String/Bytes)
|
||||
let s = if let Some(s) = read_arg_string(args, args_len, 0) { s } else { return E_ARGS; };
|
||||
let s = if let Some(s) = read_arg_string(args, args_len, 0) {
|
||||
s
|
||||
} else {
|
||||
return E_ARGS;
|
||||
};
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut m) = INST.lock() { m.insert(id, StrInstance { s }); } else { return E_PLUGIN; }
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.insert(id, StrInstance { s });
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
return write_tlv_handle(TYPE_ID_STRING, id, result, result_len);
|
||||
}
|
||||
M_TO_UTF8 => {
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
return write_tlv_string(&inst.s, result, result_len);
|
||||
} else { return E_HANDLE; }
|
||||
} else { return E_PLUGIN; }
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
_ => E_METHOD,
|
||||
}
|
||||
@ -131,7 +218,9 @@ pub struct NyashTypeBoxFfi {
|
||||
unsafe impl Sync for NyashTypeBoxFfi {}
|
||||
|
||||
extern "C" fn string_resolve(name: *const c_char) -> u32 {
|
||||
if name.is_null() { return 0; }
|
||||
if name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||
match s.as_ref() {
|
||||
"len" | "length" => M_LENGTH,
|
||||
@ -141,30 +230,77 @@ extern "C" fn string_resolve(name: *const c_char) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn string_invoke_id(instance_id: u32, method_id: u32, args: *const u8, args_len: usize, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
extern "C" fn string_invoke_id(
|
||||
instance_id: u32,
|
||||
method_id: u32,
|
||||
args: *const u8,
|
||||
args_len: usize,
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
unsafe {
|
||||
match method_id {
|
||||
M_LENGTH => {
|
||||
if let Ok(m) = INST.lock() { if let Some(inst) = m.get(&instance_id) { return write_tlv_i64(inst.s.len() as i64, result, result_len); } else { return E_HANDLE; } } else { return E_PLUGIN; }
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
return write_tlv_i64(inst.s.len() as i64, result, result_len);
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_TO_UTF8 => {
|
||||
if let Ok(m) = INST.lock() { if let Some(inst) = m.get(&instance_id) { return write_tlv_string(&inst.s, result, result_len); } else { return E_HANDLE; } } else { return E_PLUGIN; }
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
return write_tlv_string(&inst.s, result, result_len);
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_CONCAT => {
|
||||
// support String/Bytes or StringBox handle
|
||||
let (ok, rhs) = if let Some((t, inst)) = read_arg_handle(args, args_len, 0) {
|
||||
if t != TYPE_ID_STRING { return E_TYPE; }
|
||||
if let Ok(m) = INST.lock() { if let Some(s2) = m.get(&inst) { (true, s2.s.clone()) } else { (false, String::new()) } } else { return E_PLUGIN; }
|
||||
} else if let Some(s) = read_arg_string(args, args_len, 0) { (true, s) } else { (false, String::new()) };
|
||||
if !ok { return E_ARGS; }
|
||||
if t != TYPE_ID_STRING {
|
||||
return E_TYPE;
|
||||
}
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(s2) = m.get(&inst) {
|
||||
(true, s2.s.clone())
|
||||
} else {
|
||||
(false, String::new())
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
} else if let Some(s) = read_arg_string(args, args_len, 0) {
|
||||
(true, s)
|
||||
} else {
|
||||
(false, String::new())
|
||||
};
|
||||
if !ok {
|
||||
return E_ARGS;
|
||||
}
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
let mut new_s = inst.s.clone(); new_s.push_str(&rhs); drop(m);
|
||||
let mut new_s = inst.s.clone();
|
||||
new_s.push_str(&rhs);
|
||||
drop(m);
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut mm) = INST.lock() { mm.insert(id, StrInstance { s: new_s }); }
|
||||
if let Ok(mut mm) = INST.lock() {
|
||||
mm.insert(id, StrInstance { s: new_s });
|
||||
}
|
||||
return write_tlv_handle(TYPE_ID_STRING, id, result, result_len);
|
||||
} else { return E_HANDLE; }
|
||||
} else { return E_PLUGIN; }
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
_ => E_METHOD,
|
||||
}
|
||||
@ -183,20 +319,54 @@ pub static nyash_typebox_StringBox: NyashTypeBoxFfi = NyashTypeBoxFfi {
|
||||
};
|
||||
|
||||
fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
unsafe { if result_len.is_null() { return false; } if result.is_null() || *result_len < needed { *result_len = needed; return true; } }
|
||||
unsafe {
|
||||
if result_len.is_null() {
|
||||
return false;
|
||||
}
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(4 + payloads.iter().map(|(_,p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes()); buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, payload) in payloads { buf.push(*tag); buf.push(0); buf.extend_from_slice(&(payload.len() as u16).to_le_bytes()); buf.extend_from_slice(payload); }
|
||||
unsafe { let needed = buf.len(); if result.is_null() || *result_len < needed { *result_len = needed; return E_SHORT; } std::ptr::copy_nonoverlapping(buf.as_ptr(), result, needed); *result_len = needed; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
let mut buf: Vec<u8> =
|
||||
Vec::with_capacity(4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||
buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, payload) in payloads {
|
||||
buf.push(*tag);
|
||||
buf.push(0);
|
||||
buf.extend_from_slice(&(payload.len() as u16).to_le_bytes());
|
||||
buf.extend_from_slice(payload);
|
||||
}
|
||||
unsafe {
|
||||
let needed = buf.len();
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return E_SHORT;
|
||||
}
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), result, needed);
|
||||
*result_len = needed;
|
||||
}
|
||||
OK
|
||||
}
|
||||
fn write_tlv_i64(v: i64, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(3u8, &v.to_le_bytes())], result, result_len) }
|
||||
fn write_tlv_bool(v: bool, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(1u8, &[if v {1u8} else {0u8}])], result, result_len) }
|
||||
fn write_tlv_handle(type_id: u32, instance_id: u32, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
fn write_tlv_i64(v: i64, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(3u8, &v.to_le_bytes())], result, result_len)
|
||||
}
|
||||
fn write_tlv_bool(v: bool, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(1u8, &[if v { 1u8 } else { 0u8 }])], result, result_len)
|
||||
}
|
||||
fn write_tlv_handle(
|
||||
type_id: u32,
|
||||
instance_id: u32,
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
let mut payload = Vec::with_capacity(8);
|
||||
payload.extend_from_slice(&type_id.to_le_bytes());
|
||||
payload.extend_from_slice(&instance_id.to_le_bytes());
|
||||
@ -206,22 +376,85 @@ fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(6u8, s.as_bytes())], result, result_len)
|
||||
}
|
||||
fn read_arg_i64(args: *const u8, args_len: usize, n: usize) -> Option<i64> {
|
||||
if args.is_null() || args_len < 4 { return None; }
|
||||
if args.is_null() || args_len < 4 {
|
||||
return None;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize; for i in 0..=n { if buf.len() < off + 4 { return None; } let tag = buf[off]; let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize; if buf.len() < off + 4 + size { return None; } if i == n { if tag != 3 || size != 8 { return None; } let mut b=[0u8;8]; b.copy_from_slice(&buf[off+4..off+12]); return Some(i64::from_le_bytes(b)); } off += 4 + size; }
|
||||
let mut off = 4usize;
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
if tag != 3 || size != 8 {
|
||||
return None;
|
||||
}
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(&buf[off + 4..off + 12]);
|
||||
return Some(i64::from_le_bytes(b));
|
||||
}
|
||||
off += 4 + size;
|
||||
}
|
||||
None
|
||||
}
|
||||
fn read_arg_handle(args: *const u8, args_len: usize, n: usize) -> Option<(u32,u32)> {
|
||||
if args.is_null() || args_len < 4 { return None; }
|
||||
fn read_arg_handle(args: *const u8, args_len: usize, n: usize) -> Option<(u32, u32)> {
|
||||
if args.is_null() || args_len < 4 {
|
||||
return None;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize; for i in 0..=n { if buf.len() < off + 4 { return None; } let tag = buf[off]; let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize; if buf.len() < off + 4 + size { return None; } if i == n { if tag != 8 || size != 8 { return None; } let mut t=[0u8;4]; t.copy_from_slice(&buf[off+4..off+8]); let mut id=[0u8;4]; id.copy_from_slice(&buf[off+8..off+12]); return Some((u32::from_le_bytes(t), u32::from_le_bytes(id))); } off += 4 + size; }
|
||||
let mut off = 4usize;
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
if tag != 8 || size != 8 {
|
||||
return None;
|
||||
}
|
||||
let mut t = [0u8; 4];
|
||||
t.copy_from_slice(&buf[off + 4..off + 8]);
|
||||
let mut id = [0u8; 4];
|
||||
id.copy_from_slice(&buf[off + 8..off + 12]);
|
||||
return Some((u32::from_le_bytes(t), u32::from_le_bytes(id)));
|
||||
}
|
||||
off += 4 + size;
|
||||
}
|
||||
None
|
||||
}
|
||||
fn read_arg_string(args: *const u8, args_len: usize, n: usize) -> Option<String> {
|
||||
if args.is_null() || args_len < 4 { return None; }
|
||||
if args.is_null() || args_len < 4 {
|
||||
return None;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize; for i in 0..=n { if buf.len() < off + 4 { return None; } let tag = buf[off]; let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize; if buf.len() < off + 4 + size { return None; } if i == n {
|
||||
if tag == 6 || tag == 7 { let s = String::from_utf8_lossy(&buf[off+4..off+4+size]).to_string(); return Some(s); } else { return None; }
|
||||
} off += 4 + size; }
|
||||
let mut off = 4usize;
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
if tag == 6 || tag == 7 {
|
||||
let s = String::from_utf8_lossy(&buf[off + 4..off + 4 + size]).to_string();
|
||||
return Some(s);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
off += 4 + size;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//! Test Multi-Box Plugin for Nyash
|
||||
//!
|
||||
//!
|
||||
//! Provides TestBoxA and TestBoxB to demonstrate multi-box plugin support
|
||||
|
||||
use std::collections::HashMap;
|
||||
@ -125,26 +125,26 @@ static TESTBOX_B_INFO: NyashPluginInfo = NyashPluginInfo {
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1 // BID-1 ABI version
|
||||
1 // BID-1 ABI version
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init(
|
||||
host: *const NyashHostVtable,
|
||||
_info: *mut std::ffi::c_void, // For v2, we use get_box_info instead
|
||||
_info: *mut std::ffi::c_void, // For v2, we use get_box_info instead
|
||||
) -> i32 {
|
||||
if host.is_null() {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
|
||||
|
||||
unsafe {
|
||||
HOST_VTABLE = Some(&*host);
|
||||
INSTANCES = Some(Mutex::new(HashMap::new()));
|
||||
|
||||
|
||||
// Log initialization
|
||||
log_info("Multi-box test plugin initialized");
|
||||
}
|
||||
|
||||
|
||||
NYB_SUCCESS
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ pub extern "C" fn nyash_plugin_init(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_get_box_count() -> u32 {
|
||||
2 // TestBoxA and TestBoxB
|
||||
2 // TestBoxA and TestBoxB
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -169,7 +169,7 @@ pub extern "C" fn nyash_plugin_get_type_id(box_name: *const c_char) -> u32 {
|
||||
if box_name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
unsafe {
|
||||
let name = std::ffi::CStr::from_ptr(box_name).to_string_lossy();
|
||||
match name.as_ref() {
|
||||
@ -195,27 +195,15 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
unsafe {
|
||||
match (type_id, method_id) {
|
||||
// TestBoxA methods
|
||||
(TYPE_ID_TESTBOX_A, METHOD_BIRTH) => {
|
||||
create_instance_a(result, result_len)
|
||||
}
|
||||
(TYPE_ID_TESTBOX_A, METHOD_HELLO) => {
|
||||
hello_method(instance_id, result, result_len)
|
||||
}
|
||||
(TYPE_ID_TESTBOX_A, METHOD_FINI) => {
|
||||
destroy_instance(instance_id)
|
||||
}
|
||||
|
||||
(TYPE_ID_TESTBOX_A, METHOD_BIRTH) => create_instance_a(result, result_len),
|
||||
(TYPE_ID_TESTBOX_A, METHOD_HELLO) => hello_method(instance_id, result, result_len),
|
||||
(TYPE_ID_TESTBOX_A, METHOD_FINI) => destroy_instance(instance_id),
|
||||
|
||||
// TestBoxB methods
|
||||
(TYPE_ID_TESTBOX_B, METHOD_BIRTH) => {
|
||||
create_instance_b(result, result_len)
|
||||
}
|
||||
(TYPE_ID_TESTBOX_B, METHOD_GREET) => {
|
||||
greet_method(instance_id, result, result_len)
|
||||
}
|
||||
(TYPE_ID_TESTBOX_B, METHOD_FINI) => {
|
||||
destroy_instance(instance_id)
|
||||
}
|
||||
|
||||
(TYPE_ID_TESTBOX_B, METHOD_BIRTH) => create_instance_b(result, result_len),
|
||||
(TYPE_ID_TESTBOX_B, METHOD_GREET) => greet_method(instance_id, result, result_len),
|
||||
(TYPE_ID_TESTBOX_B, METHOD_FINI) => destroy_instance(instance_id),
|
||||
|
||||
_ => NYB_E_INVALID_ARGS,
|
||||
}
|
||||
}
|
||||
@ -228,17 +216,20 @@ unsafe fn create_instance_a(result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
if let Ok(mut map) = mutex.lock() {
|
||||
let id = INSTANCE_COUNTER;
|
||||
INSTANCE_COUNTER += 1;
|
||||
|
||||
map.insert(id, TestInstance::BoxA {
|
||||
message: "Hello from TestBoxA!".to_string(),
|
||||
});
|
||||
|
||||
|
||||
map.insert(
|
||||
id,
|
||||
TestInstance::BoxA {
|
||||
message: "Hello from TestBoxA!".to_string(),
|
||||
},
|
||||
);
|
||||
|
||||
// Return instance ID
|
||||
if *result_len >= 4 {
|
||||
let bytes = id.to_le_bytes();
|
||||
ptr::copy_nonoverlapping(bytes.as_ptr(), result, 4);
|
||||
*result_len = 4;
|
||||
|
||||
|
||||
log_info(&format!("Created TestBoxA instance {}", id));
|
||||
return NYB_SUCCESS;
|
||||
}
|
||||
@ -252,17 +243,15 @@ unsafe fn create_instance_b(result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
if let Ok(mut map) = mutex.lock() {
|
||||
let id = INSTANCE_COUNTER;
|
||||
INSTANCE_COUNTER += 1;
|
||||
|
||||
map.insert(id, TestInstance::BoxB {
|
||||
counter: 0,
|
||||
});
|
||||
|
||||
|
||||
map.insert(id, TestInstance::BoxB { counter: 0 });
|
||||
|
||||
// Return instance ID
|
||||
if *result_len >= 4 {
|
||||
let bytes = id.to_le_bytes();
|
||||
ptr::copy_nonoverlapping(bytes.as_ptr(), result, 4);
|
||||
*result_len = 4;
|
||||
|
||||
|
||||
log_info(&format!("Created TestBoxB instance {}", id));
|
||||
return NYB_SUCCESS;
|
||||
}
|
||||
@ -294,7 +283,7 @@ unsafe fn greet_method(instance_id: u32, result: *mut u8, result_len: *mut usize
|
||||
if let Some(TestInstance::BoxB { counter }) = map.get_mut(&instance_id) {
|
||||
*counter += 1;
|
||||
let message = format!("Greeting #{} from TestBoxB!", counter);
|
||||
|
||||
|
||||
// Return message as TLV string
|
||||
write_tlv_string(&message, result, result_len)
|
||||
} else {
|
||||
@ -329,28 +318,28 @@ unsafe fn destroy_instance(instance_id: u32) -> i32 {
|
||||
|
||||
unsafe fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
let bytes = s.as_bytes();
|
||||
let needed = 8 + bytes.len(); // header(4) + entry(4) + string
|
||||
|
||||
let needed = 8 + bytes.len(); // header(4) + entry(4) + string
|
||||
|
||||
if *result_len < needed {
|
||||
return NYB_E_INVALID_ARGS;
|
||||
}
|
||||
|
||||
|
||||
// TLV header
|
||||
*result = 1; // version low
|
||||
*result.offset(1) = 0; // version high
|
||||
*result.offset(2) = 1; // argc low
|
||||
*result.offset(3) = 0; // argc high
|
||||
|
||||
*result = 1; // version low
|
||||
*result.offset(1) = 0; // version high
|
||||
*result.offset(2) = 1; // argc low
|
||||
*result.offset(3) = 0; // argc high
|
||||
|
||||
// String entry
|
||||
*result.offset(4) = 6; // Tag::String
|
||||
*result.offset(5) = 0; // padding
|
||||
*result.offset(4) = 6; // Tag::String
|
||||
*result.offset(5) = 0; // padding
|
||||
let len_bytes = (bytes.len() as u16).to_le_bytes();
|
||||
*result.offset(6) = len_bytes[0];
|
||||
*result.offset(7) = len_bytes[1];
|
||||
|
||||
|
||||
// String data
|
||||
ptr::copy_nonoverlapping(bytes.as_ptr(), result.offset(8), bytes.len());
|
||||
|
||||
|
||||
*result_len = needed;
|
||||
NYB_SUCCESS
|
||||
}
|
||||
@ -370,4 +359,4 @@ pub extern "C" fn nyash_plugin_shutdown() {
|
||||
INSTANCES = None;
|
||||
HOST_VTABLE = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,10 @@
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Mutex, atomic::{AtomicU32, Ordering}};
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
const OK: i32 = 0;
|
||||
const E_SHORT: i32 = -1;
|
||||
@ -12,24 +15,30 @@ const E_ARGS: i32 = -4;
|
||||
const E_PLUGIN: i32 = -5;
|
||||
const E_HANDLE: i32 = -8;
|
||||
|
||||
const M_BIRTH: u32 = 0; // constructor -> instance
|
||||
const M_PARSE: u32 = 1; // parse(text) -> bool
|
||||
const M_GET: u32 = 2; // get(path.dot.segments) -> string (toml-display) or empty
|
||||
const M_TO_JSON: u32 = 3; // toJson() -> string (JSON)
|
||||
const M_BIRTH: u32 = 0; // constructor -> instance
|
||||
const M_PARSE: u32 = 1; // parse(text) -> bool
|
||||
const M_GET: u32 = 2; // get(path.dot.segments) -> string (toml-display) or empty
|
||||
const M_TO_JSON: u32 = 3; // toJson() -> string (JSON)
|
||||
const M_FINI: u32 = u32::MAX; // fini()
|
||||
|
||||
const TYPE_ID_TOML: u32 = 54;
|
||||
|
||||
struct TomlInstance { value: Option<toml::Value> }
|
||||
struct TomlInstance {
|
||||
value: Option<toml::Value>,
|
||||
}
|
||||
|
||||
static INST: Lazy<Mutex<HashMap<u32, TomlInstance>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static NEXT_ID: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
|
||||
pub extern "C" fn nyash_plugin_abi() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 { OK }
|
||||
pub extern "C" fn nyash_plugin_init() -> i32 {
|
||||
OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyash_plugin_invoke(
|
||||
@ -41,35 +50,84 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
result: *mut u8,
|
||||
result_len: *mut usize,
|
||||
) -> i32 {
|
||||
if type_id != TYPE_ID_TOML { return E_TYPE; }
|
||||
if type_id != TYPE_ID_TOML {
|
||||
return E_TYPE;
|
||||
}
|
||||
unsafe {
|
||||
match method_id {
|
||||
M_BIRTH => {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
if preflight(result, result_len, 4) { return E_SHORT; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
if preflight(result, result_len, 4) {
|
||||
return E_SHORT;
|
||||
}
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut m) = INST.lock() { m.insert(id, TomlInstance { value: None }); } else { return E_PLUGIN; }
|
||||
let b = id.to_le_bytes(); std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4); *result_len = 4; OK
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.insert(id, TomlInstance { value: None });
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
let b = id.to_le_bytes();
|
||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
||||
*result_len = 4;
|
||||
OK
|
||||
}
|
||||
M_FINI => {
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.remove(&instance_id);
|
||||
OK
|
||||
} else {
|
||||
E_PLUGIN
|
||||
}
|
||||
}
|
||||
M_FINI => { if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_PLUGIN } }
|
||||
M_PARSE => {
|
||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
if let Ok(mut m) = INST.lock() { if let Some(inst) = m.get_mut(&instance_id) { inst.value = toml::from_str::<toml::Value>(&text).ok(); return write_tlv_bool(inst.value.is_some(), result, result_len); } else { return E_HANDLE; } } else { return E_PLUGIN; }
|
||||
let text = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
if let Some(inst) = m.get_mut(&instance_id) {
|
||||
inst.value = toml::from_str::<toml::Value>(&text).ok();
|
||||
return write_tlv_bool(inst.value.is_some(), result, result_len);
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_GET => {
|
||||
let path = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let path = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
let mut cur = match &inst.value { Some(v) => v, None => { return write_tlv_string("", result, result_len); } };
|
||||
let mut cur = match &inst.value {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return write_tlv_string("", result, result_len);
|
||||
}
|
||||
};
|
||||
if !path.is_empty() {
|
||||
for seg in path.split('.') {
|
||||
match cur.get(seg) { Some(v) => cur = v, None => { return write_tlv_string("", result, result_len); } }
|
||||
match cur.get(seg) {
|
||||
Some(v) => cur = v,
|
||||
None => {
|
||||
return write_tlv_string("", result, result_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let out = cur.to_string();
|
||||
return write_tlv_string(&out, result, result_len);
|
||||
} else { return E_HANDLE; }
|
||||
} else { return E_PLUGIN; }
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_TO_JSON => {
|
||||
if let Ok(m) = INST.lock() {
|
||||
@ -77,10 +135,19 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
if let Some(v) = &inst.value {
|
||||
// Convert via serde_json::Value
|
||||
let sv = toml_to_json(v);
|
||||
return match serde_json::to_string(&sv) { Ok(s) => write_tlv_string(&s, result, result_len), Err(_) => write_tlv_string("{}", result, result_len) };
|
||||
} else { return write_tlv_string("{}", result, result_len); }
|
||||
} else { return E_HANDLE; }
|
||||
} else { return E_PLUGIN; }
|
||||
return match serde_json::to_string(&sv) {
|
||||
Ok(s) => write_tlv_string(&s, result, result_len),
|
||||
Err(_) => write_tlv_string("{}", result, result_len),
|
||||
};
|
||||
} else {
|
||||
return write_tlv_string("{}", result, result_len);
|
||||
}
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
_ => E_METHOD,
|
||||
}
|
||||
@ -97,31 +164,81 @@ fn toml_to_json(v: &toml::Value) -> serde_json::Value {
|
||||
toml::Value::Array(arr) => serde_json::Value::Array(arr.iter().map(toml_to_json).collect()),
|
||||
toml::Value::Table(map) => {
|
||||
let mut m = serde_json::Map::new();
|
||||
for (k, vv) in map.iter() { m.insert(k.clone(), toml_to_json(vv)); }
|
||||
for (k, vv) in map.iter() {
|
||||
m.insert(k.clone(), toml_to_json(vv));
|
||||
}
|
||||
serde_json::Value::Object(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||
unsafe { if result_len.is_null() { return false; } if result.is_null() || *result_len < needed { *result_len = needed; return true; } }
|
||||
unsafe {
|
||||
if result_len.is_null() {
|
||||
return false;
|
||||
}
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
fn write_tlv_result(payloads: &[(u8, &[u8])], result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(4 + payloads.iter().map(|(_,p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes()); buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, payload) in payloads { buf.push(*tag); buf.push(0); buf.extend_from_slice(&(payload.len() as u16).to_le_bytes()); buf.extend_from_slice(payload); }
|
||||
unsafe { let needed = buf.len(); if result.is_null() || *result_len < needed { *result_len = needed; return E_SHORT; } std::ptr::copy_nonoverlapping(buf.as_ptr(), result, needed); *result_len = needed; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
let mut buf: Vec<u8> =
|
||||
Vec::with_capacity(4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>());
|
||||
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||
buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, payload) in payloads {
|
||||
buf.push(*tag);
|
||||
buf.push(0);
|
||||
buf.extend_from_slice(&(payload.len() as u16).to_le_bytes());
|
||||
buf.extend_from_slice(payload);
|
||||
}
|
||||
unsafe {
|
||||
let needed = buf.len();
|
||||
if result.is_null() || *result_len < needed {
|
||||
*result_len = needed;
|
||||
return E_SHORT;
|
||||
}
|
||||
std::ptr::copy_nonoverlapping(buf.as_ptr(), result, needed);
|
||||
*result_len = needed;
|
||||
}
|
||||
OK
|
||||
}
|
||||
fn write_tlv_bool(v: bool, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(1u8, &[if v {1u8} else {0u8}])], result, result_len) }
|
||||
fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 { write_tlv_result(&[(6u8, s.as_bytes())], result, result_len) }
|
||||
|
||||
fn read_arg_string(args: *const u8, args_len: usize, n: usize) -> Option<String> {
|
||||
if args.is_null() || args_len < 4 { return None; }
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize; for i in 0..=n { if buf.len() < off + 4 { return None; } let tag = buf[off]; let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize; if buf.len() < off + 4 + size { return None; } if i == n { if tag == 6 || tag == 7 { return Some(String::from_utf8_lossy(&buf[off+4..off+4+size]).to_string()); } else { return None; } } off += 4 + size; }
|
||||
None
|
||||
fn write_tlv_bool(v: bool, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(1u8, &[if v { 1u8 } else { 0u8 }])], result, result_len)
|
||||
}
|
||||
fn write_tlv_string(s: &str, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(6u8, s.as_bytes())], result, result_len)
|
||||
}
|
||||
|
||||
fn read_arg_string(args: *const u8, args_len: usize, n: usize) -> Option<String> {
|
||||
if args.is_null() || args_len < 4 {
|
||||
return None;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize;
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
if tag == 6 || tag == 7 {
|
||||
return Some(String::from_utf8_lossy(&buf[off + 4..off + 4 + size]).to_string());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
off += 4 + size;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user