//! Nyash Math/Time Plugin - BID-FFI v1 (minimal) //! MathBox: sqrt(i64) -> i64 //! TimeBox: now() -> i64 (unix seconds) use std::collections::HashMap; use std::sync::{ atomic::{AtomicU32, Ordering}, Mutex, }; // Error codes const OK: i32 = 0; const E_SHORT: i32 = -1; const E_TYPE: i32 = -2; const E_METHOD: i32 = -3; const E_ARGS: i32 = -4; const E_FAIL: i32 = -5; // Type IDs (align with nyash.toml [box_types]) const TID_MATH: u32 = 50; const TID_TIME: u32 = 51; // Methods const M_BIRTH: u32 = 0; const M_FINI: u32 = u32::MAX; // MathBox const M_SQRT: u32 = 1; const M_SIN: u32 = 2; const M_COS: u32 = 3; const M_ROUND: u32 = 4; // TimeBox const T_NOW: u32 = 1; use once_cell::sync::Lazy; #[derive(Default)] struct Empty; static MATH_INST: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); static TIME_INST: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); static ID: AtomicU32 = AtomicU32::new(1); // TLV helpers mod tlv { pub fn header(argc: u16) -> Vec { 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, 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, 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) { 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 } #[no_mangle] pub extern "C" fn nyash_plugin_init() -> i32 { OK } #[no_mangle] pub extern "C" fn nyash_plugin_invoke( type_id: u32, method_id: u32, instance_id: u32, args: *const u8, args_len: usize, result: *mut u8, result_len: *mut usize, ) -> i32 { unsafe { match (type_id, method_id) { (TID_MATH, M_BIRTH) => birth(TID_MATH, &MATH_INST, result, result_len), (TID_TIME, M_BIRTH) => birth(TID_TIME, &TIME_INST, result, result_len), (TID_MATH, M_FINI) => fini(&MATH_INST, instance_id), (TID_TIME, M_FINI) => fini(&TIME_INST, instance_id), (TID_MATH, M_SQRT) => sqrt_call(args, args_len, result, result_len), (TID_MATH, M_SIN) => trig_call(args, args_len, result, result_len, true), (TID_MATH, M_COS) => trig_call(args, args_len, result, result_len, false), (TID_MATH, M_ROUND) => round_call(args, args_len, result, result_len), (TID_TIME, T_NOW) => now_call(result, result_len), (TID_MATH, _) | (TID_TIME, _) => E_METHOD, _ => E_TYPE, } } } unsafe fn birth( tid: u32, map: &Lazy>>, 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 } unsafe fn fini(map: &Lazy>>, 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; } 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]); 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 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(); return OK; } } E_ARGS } 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(); 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; } 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]); 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 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(); return OK; } } E_ARGS } 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; } 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]); 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 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(); return OK; } } E_ARGS }