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:
Selfhosting Dev
2025-09-16 23:49:36 +09:00
parent 97a76c0571
commit 5c9213cd03
104 changed files with 8094 additions and 2930 deletions

View File

@ -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;

View File

@ -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,

View File

@ -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
}

View File

@ -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,
})
}
}),
);

View File

@ -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
}

View File

@ -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);
}
}
}
}

View File

@ -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)))
}

View File

@ -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,
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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;
}
}
}

View File

@ -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
}