1use std::ffi::c_void;
27use std::sync::Arc;
28
29use libloading::Library;
30use serde::de::DeserializeOwned;
31use serde::Serialize;
32
33use fidius_core::descriptor::{BufferStrategyKind, PluginDescriptor};
34use fidius_core::status::*;
35use fidius_core::wire;
36use fidius_core::PluginError;
37
38use crate::arena::{acquire_arena, grow_arena, release_arena, DEFAULT_ARENA_CAPACITY};
39use crate::error::{CallError, LoadError};
40use crate::executor::PluginExecutor;
41use crate::types::PluginInfo;
42
43type FfiFn = unsafe extern "C" fn(*mut c_void, *const u8, u32, *mut *mut u8, *mut u32) -> i32;
46
47type ArenaFn =
49 unsafe extern "C" fn(*mut c_void, *const u8, u32, *mut u8, u32, *mut u32, *mut u32) -> i32;
50
51unsafe fn construct_instance(descriptor: *const PluginDescriptor, cfg: &[u8]) -> *mut c_void {
57 match (*descriptor).construct {
58 Some(ctor) => ctor(cfg.as_ptr(), cfg.len() as u32),
59 None => std::ptr::null_mut(),
60 }
61}
62
63pub struct CdylibExecutor {
72 _library: Option<Arc<Library>>,
76 vtable: *const c_void,
78 descriptor: *const PluginDescriptor,
82 free_buffer: Option<unsafe extern "C" fn(*mut u8, usize)>,
84 capabilities: u64,
86 method_count: u32,
88 info: PluginInfo,
90 instance: *mut c_void,
94 destroy: Option<unsafe extern "C" fn(*mut c_void)>,
96}
97
98unsafe impl Send for CdylibExecutor {}
107unsafe impl Sync for CdylibExecutor {}
108
109impl Drop for CdylibExecutor {
110 fn drop(&mut self) {
111 if let Some(destroy) = self.destroy {
113 if !self.instance.is_null() {
114 unsafe { destroy(self.instance) };
115 }
116 }
117 }
118}
119
120impl CdylibExecutor {
121 #[allow(dead_code)]
123 pub(crate) fn new(
124 library: Arc<Library>,
125 vtable: *const c_void,
126 descriptor: *const PluginDescriptor,
127 free_buffer: Option<unsafe extern "C" fn(*mut u8, usize)>,
128 capabilities: u64,
129 method_count: u32,
130 info: PluginInfo,
131 ) -> Self {
132 let instance = unsafe { construct_instance(descriptor, &[]) };
133 let destroy = unsafe { (*descriptor).destroy };
134 Self {
135 _library: Some(library),
136 vtable,
137 descriptor,
138 free_buffer,
139 capabilities,
140 method_count,
141 info,
142 instance,
143 destroy,
144 }
145 }
146
147 pub fn from_loaded(plugin: crate::loader::LoadedPlugin) -> Self {
149 Self::from_loaded_with_config(plugin, &[])
150 }
151
152 pub fn from_loaded_with_config(plugin: crate::loader::LoadedPlugin, cfg: &[u8]) -> Self {
160 let instance = unsafe { construct_instance(plugin.descriptor, cfg) };
161 let destroy = unsafe { (*plugin.descriptor).destroy };
162 Self {
163 _library: Some(plugin.library),
164 vtable: plugin.vtable,
165 descriptor: plugin.descriptor,
166 free_buffer: plugin.free_buffer,
167 capabilities: plugin.info.capabilities,
168 method_count: plugin.method_count,
169 info: plugin.info,
170 instance,
171 destroy,
172 }
173 }
174
175 pub fn from_descriptor(desc: &'static PluginDescriptor) -> Result<Self, LoadError> {
183 Self::from_descriptor_with_config(desc, &[])
184 }
185
186 pub fn from_descriptor_with_config(
190 desc: &'static PluginDescriptor,
191 cfg: &[u8],
192 ) -> Result<Self, LoadError> {
193 let info = PluginInfo {
194 name: unsafe { desc.plugin_name_str() }.to_string(),
195 interface_name: unsafe { desc.interface_name_str() }.to_string(),
196 interface_hash: desc.interface_hash,
197 interface_version: desc.interface_version,
198 capabilities: desc.capabilities,
199 buffer_strategy: desc
200 .buffer_strategy_kind()
201 .map_err(|v| LoadError::UnknownBufferStrategy { value: v })?,
202 runtime: crate::types::PluginRuntimeKind::Cdylib,
203 };
204 let descriptor = desc as *const PluginDescriptor;
205 let instance = unsafe { construct_instance(descriptor, cfg) };
206 Ok(Self {
207 _library: None,
208 vtable: desc.vtable,
209 descriptor,
210 free_buffer: desc.free_buffer,
211 capabilities: desc.capabilities,
212 method_count: desc.method_count,
213 info,
214 instance,
215 destroy: desc.destroy,
216 })
217 }
218
219 pub fn find_in_process_descriptor(
226 plugin_name: &str,
227 ) -> Result<&'static PluginDescriptor, LoadError> {
228 let reg = fidius_core::registry::get_registry();
229 for i in 0..reg.plugin_count as usize {
230 let desc_ptr = unsafe { *reg.descriptors.add(i) };
231 let desc = unsafe { &*desc_ptr };
232 if unsafe { desc.plugin_name_str() } == plugin_name {
233 return Ok(desc);
234 }
235 }
236 Err(LoadError::PluginNotFound {
237 name: plugin_name.to_string(),
238 })
239 }
240
241 pub fn call_method<I: Serialize, O: DeserializeOwned>(
258 &self,
259 index: usize,
260 input: &I,
261 ) -> Result<O, CallError> {
262 if index >= self.method_count as usize {
264 return Err(CallError::InvalidMethodIndex {
265 index,
266 count: self.method_count,
267 });
268 }
269
270 let input_bytes =
271 wire::serialize(input).map_err(|e| CallError::Serialization(e.to_string()))?;
272
273 match self.info.buffer_strategy {
274 BufferStrategyKind::PluginAllocated => self.call_plugin_allocated(index, &input_bytes),
275 BufferStrategyKind::Arena => self.call_arena(index, &input_bytes),
276 }
277 }
278
279 pub fn call_method_raw(&self, index: usize, input: &[u8]) -> Result<Vec<u8>, CallError> {
289 if index >= self.method_count as usize {
290 return Err(CallError::InvalidMethodIndex {
291 index,
292 count: self.method_count,
293 });
294 }
295 match self.info.buffer_strategy {
296 BufferStrategyKind::PluginAllocated => self.call_plugin_allocated_raw(index, input),
297 BufferStrategyKind::Arena => self.call_arena_raw(index, input),
298 }
299 }
300
301 fn call_plugin_allocated<O: DeserializeOwned>(
304 &self,
305 index: usize,
306 input_bytes: &[u8],
307 ) -> Result<O, CallError> {
308 let fn_ptr = match unsafe { *(self.vtable as *const Option<FfiFn>).add(index) } {
311 Some(f) => f,
312 None => return Err(CallError::NotImplemented { bit: index as u32 }),
313 };
314
315 let mut out_ptr: *mut u8 = std::ptr::null_mut();
316 let mut out_len: u32 = 0;
317
318 let status = unsafe {
319 fn_ptr(
320 self.instance,
321 input_bytes.as_ptr(),
322 input_bytes.len() as u32,
323 &mut out_ptr,
324 &mut out_len,
325 )
326 };
327
328 match status {
329 STATUS_OK => {}
330 STATUS_BUFFER_TOO_SMALL => return Err(CallError::BufferTooSmall),
331 STATUS_SERIALIZATION_ERROR => {
332 return Err(CallError::Serialization("FFI serialization failed".into()))
333 }
334 STATUS_PLUGIN_ERROR => {
335 if !out_ptr.is_null() && out_len > 0 {
336 let output_slice =
337 unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
338 let plugin_err: PluginError = wire::deserialize(output_slice)
339 .map_err(|e| CallError::Deserialization(e.to_string()))?;
340
341 if let Some(free) = self.free_buffer {
342 unsafe { free(out_ptr, out_len as usize) };
343 }
344
345 return Err(CallError::Plugin(plugin_err));
346 }
347 return Err(CallError::Plugin(PluginError::new(
348 "UNKNOWN",
349 "plugin returned error but no error data",
350 )));
351 }
352 STATUS_PANIC => {
353 let msg = if !out_ptr.is_null() && out_len > 0 {
354 let slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
355 let msg = wire::deserialize::<String>(slice)
356 .unwrap_or_else(|_| "unknown panic".into());
357 if let Some(free) = self.free_buffer {
358 unsafe { free(out_ptr, out_len as usize) };
359 }
360 msg
361 } else {
362 "unknown panic".into()
363 };
364 return Err(CallError::Panic(msg));
365 }
366 _ => return Err(CallError::UnknownStatus { code: status }),
367 }
368
369 if out_ptr.is_null() {
370 return Err(CallError::Serialization(
371 "plugin returned null output buffer".into(),
372 ));
373 }
374
375 let output_slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
376 let result: Result<O, CallError> =
377 wire::deserialize(output_slice).map_err(|e| CallError::Deserialization(e.to_string()));
378
379 if let Some(free) = self.free_buffer {
380 unsafe { free(out_ptr, out_len as usize) };
381 }
382
383 result
384 }
385
386 fn call_arena<O: DeserializeOwned>(
391 &self,
392 index: usize,
393 input_bytes: &[u8],
394 ) -> Result<O, CallError> {
395 let fn_ptr = match unsafe { *(self.vtable as *const Option<ArenaFn>).add(index) } {
396 Some(f) => f,
397 None => return Err(CallError::NotImplemented { bit: index as u32 }),
398 };
399
400 let mut arena = acquire_arena(DEFAULT_ARENA_CAPACITY);
401 let mut out_offset: u32 = 0;
402 let mut out_len: u32 = 0;
403 let mut retried = false;
404
405 let status = loop {
406 let s = unsafe {
407 fn_ptr(
408 self.instance,
409 input_bytes.as_ptr(),
410 input_bytes.len() as u32,
411 arena.as_mut_ptr(),
412 arena.len() as u32,
413 &mut out_offset,
414 &mut out_len,
415 )
416 };
417 if s == STATUS_BUFFER_TOO_SMALL && !retried {
418 let needed = out_len as usize;
420 grow_arena(&mut arena, needed);
421 retried = true;
422 continue;
423 }
424 break s;
425 };
426
427 match status {
428 STATUS_OK => {
429 let start = out_offset as usize;
430 let end = start + out_len as usize;
431 if end > arena.len() {
432 release_arena(arena);
433 return Err(CallError::Serialization(
434 "plugin reported out_offset/out_len outside arena".into(),
435 ));
436 }
437 let result = wire::deserialize(&arena[start..end])
438 .map_err(|e| CallError::Deserialization(e.to_string()));
439 release_arena(arena);
440 result
441 }
442 STATUS_BUFFER_TOO_SMALL => {
443 release_arena(arena);
444 Err(CallError::BufferTooSmall)
445 }
446 STATUS_SERIALIZATION_ERROR => {
447 release_arena(arena);
448 Err(CallError::Serialization("FFI serialization failed".into()))
449 }
450 STATUS_PLUGIN_ERROR => {
451 let start = out_offset as usize;
452 let end = start + out_len as usize;
453 let plugin_err = if out_len > 0 && end <= arena.len() {
454 wire::deserialize::<PluginError>(&arena[start..end]).unwrap_or_else(|_| {
455 PluginError::new("UNKNOWN", "plugin returned malformed error")
456 })
457 } else {
458 PluginError::new("UNKNOWN", "plugin returned error but no error data")
459 };
460 release_arena(arena);
461 Err(CallError::Plugin(plugin_err))
462 }
463 STATUS_PANIC => {
464 release_arena(arena);
468 Err(CallError::Panic(
469 "plugin panicked (message not transmitted via Arena strategy)".into(),
470 ))
471 }
472 code => {
473 release_arena(arena);
474 Err(CallError::UnknownStatus { code })
475 }
476 }
477 }
478
479 fn call_plugin_allocated_raw(
483 &self,
484 index: usize,
485 input_bytes: &[u8],
486 ) -> Result<Vec<u8>, CallError> {
487 let fn_ptr = match unsafe { *(self.vtable as *const Option<FfiFn>).add(index) } {
490 Some(f) => f,
491 None => return Err(CallError::NotImplemented { bit: index as u32 }),
492 };
493
494 let mut out_ptr: *mut u8 = std::ptr::null_mut();
495 let mut out_len: u32 = 0;
496
497 let status = unsafe {
498 fn_ptr(
499 self.instance,
500 input_bytes.as_ptr(),
501 input_bytes.len() as u32,
502 &mut out_ptr,
503 &mut out_len,
504 )
505 };
506
507 match status {
508 STATUS_OK => {}
509 STATUS_BUFFER_TOO_SMALL => return Err(CallError::BufferTooSmall),
510 STATUS_SERIALIZATION_ERROR => {
511 return Err(CallError::Serialization("FFI serialization failed".into()))
512 }
513 STATUS_PLUGIN_ERROR => {
514 if !out_ptr.is_null() && out_len > 0 {
515 let output_slice =
516 unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
517 let plugin_err: PluginError = wire::deserialize(output_slice)
518 .map_err(|e| CallError::Deserialization(e.to_string()))?;
519 if let Some(free) = self.free_buffer {
520 unsafe { free(out_ptr, out_len as usize) };
521 }
522 return Err(CallError::Plugin(plugin_err));
523 }
524 return Err(CallError::Plugin(PluginError::new(
525 "UNKNOWN",
526 "plugin returned error but no error data",
527 )));
528 }
529 STATUS_PANIC => {
530 let msg = if !out_ptr.is_null() && out_len > 0 {
531 let slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
532 let msg = wire::deserialize::<String>(slice)
533 .unwrap_or_else(|_| "unknown panic".into());
534 if let Some(free) = self.free_buffer {
535 unsafe { free(out_ptr, out_len as usize) };
536 }
537 msg
538 } else {
539 "unknown panic".into()
540 };
541 return Err(CallError::Panic(msg));
542 }
543 _ => return Err(CallError::UnknownStatus { code: status }),
544 }
545
546 if out_ptr.is_null() {
547 return Err(CallError::Serialization(
548 "plugin returned null output buffer".into(),
549 ));
550 }
551
552 let output_slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
556 let result = output_slice.to_vec();
557
558 if let Some(free) = self.free_buffer {
559 unsafe { free(out_ptr, out_len as usize) };
560 }
561
562 Ok(result)
563 }
564
565 #[cfg(feature = "streaming")]
575 pub unsafe fn call_client_streaming_raw(
576 &self,
577 index: usize,
578 handle: *mut fidius_core::stream_ffi::FidiusStreamHandle,
579 input_bytes: &[u8],
580 ) -> Result<Vec<u8>, CallError> {
581 if index >= self.method_count as usize {
582 return Err(CallError::InvalidMethodIndex {
583 index,
584 count: self.method_count,
585 });
586 }
587 type ClientStreamFn = unsafe extern "C" fn(
588 *mut c_void,
589 *mut fidius_core::stream_ffi::FidiusStreamHandle,
590 *const u8,
591 u32,
592 *mut *mut u8,
593 *mut u32,
594 ) -> i32;
595 let fn_ptr = match unsafe { *(self.vtable as *const Option<ClientStreamFn>).add(index) } {
596 Some(f) => f,
597 None => return Err(CallError::NotImplemented { bit: index as u32 }),
598 };
599
600 let mut out_ptr: *mut u8 = std::ptr::null_mut();
601 let mut out_len: u32 = 0;
602 let status = unsafe {
603 fn_ptr(
604 self.instance,
605 handle,
606 input_bytes.as_ptr(),
607 input_bytes.len() as u32,
608 &mut out_ptr,
609 &mut out_len,
610 )
611 };
612
613 match status {
614 STATUS_OK => {}
615 STATUS_SERIALIZATION_ERROR => {
616 return Err(CallError::Serialization("FFI serialization failed".into()))
617 }
618 STATUS_PLUGIN_ERROR => {
619 let err = if !out_ptr.is_null() && out_len > 0 {
620 let slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
621 let pe: PluginError = wire::deserialize(slice)
622 .unwrap_or_else(|_| PluginError::new("UNKNOWN", "plugin error"));
623 if let Some(free) = self.free_buffer {
624 unsafe { free(out_ptr, out_len as usize) };
625 }
626 pe
627 } else {
628 PluginError::new("UNKNOWN", "plugin returned error but no data")
629 };
630 return Err(CallError::Plugin(err));
631 }
632 STATUS_PANIC => {
633 let msg = if !out_ptr.is_null() && out_len > 0 {
634 let slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
635 let m = wire::deserialize::<String>(slice)
636 .unwrap_or_else(|_| "unknown panic".into());
637 if let Some(free) = self.free_buffer {
638 unsafe { free(out_ptr, out_len as usize) };
639 }
640 m
641 } else {
642 "unknown panic".into()
643 };
644 return Err(CallError::Panic(msg));
645 }
646 _ => return Err(CallError::UnknownStatus { code: status }),
647 }
648
649 if out_ptr.is_null() {
650 return Err(CallError::Serialization(
651 "plugin returned null output buffer".into(),
652 ));
653 }
654 let result = unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) }.to_vec();
655 if let Some(free) = self.free_buffer {
656 unsafe { free(out_ptr, out_len as usize) };
657 }
658 Ok(result)
659 }
660
661 fn call_arena_raw(&self, index: usize, input_bytes: &[u8]) -> Result<Vec<u8>, CallError> {
664 let fn_ptr = match unsafe { *(self.vtable as *const Option<ArenaFn>).add(index) } {
665 Some(f) => f,
666 None => return Err(CallError::NotImplemented { bit: index as u32 }),
667 };
668
669 let mut arena = acquire_arena(DEFAULT_ARENA_CAPACITY);
670 let mut out_offset: u32 = 0;
671 let mut out_len: u32 = 0;
672 let mut retried = false;
673
674 let status = loop {
675 let s = unsafe {
676 fn_ptr(
677 self.instance,
678 input_bytes.as_ptr(),
679 input_bytes.len() as u32,
680 arena.as_mut_ptr(),
681 arena.len() as u32,
682 &mut out_offset,
683 &mut out_len,
684 )
685 };
686 if s == STATUS_BUFFER_TOO_SMALL && !retried {
687 let needed = out_len as usize;
688 grow_arena(&mut arena, needed);
689 retried = true;
690 continue;
691 }
692 break s;
693 };
694
695 match status {
696 STATUS_OK => {
697 let start = out_offset as usize;
698 let end = start + out_len as usize;
699 if end > arena.len() {
700 release_arena(arena);
701 return Err(CallError::Serialization(
702 "plugin reported out_offset/out_len outside arena".into(),
703 ));
704 }
705 let result = arena[start..end].to_vec();
706 release_arena(arena);
707 Ok(result)
708 }
709 STATUS_BUFFER_TOO_SMALL => {
710 release_arena(arena);
711 Err(CallError::BufferTooSmall)
712 }
713 STATUS_SERIALIZATION_ERROR => {
714 release_arena(arena);
715 Err(CallError::Serialization("FFI serialization failed".into()))
716 }
717 STATUS_PLUGIN_ERROR => {
718 let start = out_offset as usize;
719 let end = start + out_len as usize;
720 let plugin_err = if out_len > 0 && end <= arena.len() {
721 wire::deserialize::<PluginError>(&arena[start..end]).unwrap_or_else(|_| {
722 PluginError::new("UNKNOWN", "plugin returned malformed error")
723 })
724 } else {
725 PluginError::new("UNKNOWN", "plugin returned error but no error data")
726 };
727 release_arena(arena);
728 Err(CallError::Plugin(plugin_err))
729 }
730 STATUS_PANIC => {
731 release_arena(arena);
732 Err(CallError::Panic(
733 "plugin panicked (message not transmitted via Arena strategy)".into(),
734 ))
735 }
736 code => {
737 release_arena(arena);
738 Err(CallError::UnknownStatus { code })
739 }
740 }
741 }
742
743 #[cfg(feature = "streaming")]
758 pub fn call_streaming_raw(
759 &self,
760 index: usize,
761 input_bytes: &[u8],
762 decode_item: fn(&[u8]) -> Result<fidius_core::Value, CallError>,
763 ) -> Result<crate::stream::ChunkStream, CallError> {
764 if index >= self.method_count as usize {
765 return Err(CallError::InvalidMethodIndex {
766 index,
767 count: self.method_count,
768 });
769 }
770
771 let init = match unsafe { *(self.vtable as *const Option<FfiFn>).add(index) } {
774 Some(f) => f,
775 None => return Err(CallError::NotImplemented { bit: index as u32 }),
776 };
777 let mut out_ptr: *mut u8 = std::ptr::null_mut();
778 let mut out_len: u32 = 0;
779 let status = unsafe {
780 init(
781 self.instance,
782 input_bytes.as_ptr(),
783 input_bytes.len() as u32,
784 &mut out_ptr,
785 &mut out_len,
786 )
787 };
788 match status {
789 STATUS_OK => {}
790 STATUS_SERIALIZATION_ERROR => {
791 return Err(CallError::Serialization(
792 "stream init: argument decode failed".into(),
793 ))
794 }
795 STATUS_PANIC => return Err(CallError::Panic("plugin panicked in stream init".into())),
796 code => return Err(CallError::UnknownStatus { code }),
797 }
798 if out_ptr.is_null() {
799 return Err(CallError::Backend {
800 runtime: "cdylib".into(),
801 message: "stream init returned a null handle".into(),
802 });
803 }
804
805 Ok(pump_stream_handle(out_ptr, decode_item))
808 }
809
810 #[cfg(feature = "streaming")]
821 pub unsafe fn call_bidi_streaming_raw(
822 &self,
823 index: usize,
824 handle: *mut fidius_core::stream_ffi::FidiusStreamHandle,
825 input_bytes: &[u8],
826 decode_item: fn(&[u8]) -> Result<fidius_core::Value, CallError>,
827 ) -> Result<crate::stream::ChunkStream, CallError> {
828 if index >= self.method_count as usize {
829 return Err(CallError::InvalidMethodIndex {
830 index,
831 count: self.method_count,
832 });
833 }
834 type ClientStreamFn = unsafe extern "C" fn(
836 *mut c_void,
837 *mut fidius_core::stream_ffi::FidiusStreamHandle,
838 *const u8,
839 u32,
840 *mut *mut u8,
841 *mut u32,
842 ) -> i32;
843 let init = match unsafe { *(self.vtable as *const Option<ClientStreamFn>).add(index) } {
844 Some(f) => f,
845 None => return Err(CallError::NotImplemented { bit: index as u32 }),
846 };
847 let mut out_ptr: *mut u8 = std::ptr::null_mut();
848 let mut out_len: u32 = 0;
849 let status = unsafe {
850 init(
851 self.instance,
852 handle,
853 input_bytes.as_ptr(),
854 input_bytes.len() as u32,
855 &mut out_ptr,
856 &mut out_len,
857 )
858 };
859 match status {
860 STATUS_OK => {}
861 STATUS_SERIALIZATION_ERROR => {
862 return Err(CallError::Serialization(
863 "bidi stream init: argument decode failed".into(),
864 ))
865 }
866 STATUS_PANIC => {
867 return Err(CallError::Panic(
868 "plugin panicked in bidi stream init".into(),
869 ))
870 }
871 code => return Err(CallError::UnknownStatus { code }),
872 }
873 if out_ptr.is_null() {
874 return Err(CallError::Backend {
875 runtime: "cdylib".into(),
876 message: "bidi stream init returned a null output handle".into(),
877 });
878 }
879 Ok(pump_stream_handle(out_ptr, decode_item))
880 }
881
882 pub fn has_capability(&self, bit: u32) -> bool {
886 if bit >= 64 {
887 return false;
888 }
889 self.capabilities & (1u64 << bit) != 0
890 }
891
892 pub fn info(&self) -> &PluginInfo {
894 &self.info
895 }
896
897 pub fn method_metadata(&self, method_id: u32) -> Vec<(&str, &str)> {
909 if method_id >= self.method_count {
910 return Vec::new();
911 }
912 let desc = unsafe { &*self.descriptor };
914 if desc.method_metadata.is_null() {
915 return Vec::new();
916 }
917 let entries =
920 unsafe { std::slice::from_raw_parts(desc.method_metadata, self.method_count as usize) };
921 let entry = &entries[method_id as usize];
922 if entry.kvs.is_null() || entry.kv_count == 0 {
923 return Vec::new();
924 }
925 let kvs = unsafe { std::slice::from_raw_parts(entry.kvs, entry.kv_count as usize) };
927 kvs.iter()
928 .map(|kv| {
929 let k = unsafe { std::ffi::CStr::from_ptr(kv.key) }
932 .to_str()
933 .expect("metadata key is not valid UTF-8");
934 let v = unsafe { std::ffi::CStr::from_ptr(kv.value) }
935 .to_str()
936 .expect("metadata value is not valid UTF-8");
937 (k, v)
938 })
939 .collect()
940 }
941
942 pub fn trait_metadata(&self) -> Vec<(&str, &str)> {
947 let desc = unsafe { &*self.descriptor };
949 if desc.trait_metadata.is_null() || desc.trait_metadata_count == 0 {
950 return Vec::new();
951 }
952 let kvs = unsafe {
954 std::slice::from_raw_parts(desc.trait_metadata, desc.trait_metadata_count as usize)
955 };
956 kvs.iter()
957 .map(|kv| {
958 let k = unsafe { std::ffi::CStr::from_ptr(kv.key) }
959 .to_str()
960 .expect("trait metadata key is not valid UTF-8");
961 let v = unsafe { std::ffi::CStr::from_ptr(kv.value) }
962 .to_str()
963 .expect("trait metadata value is not valid UTF-8");
964 (k, v)
965 })
966 .collect()
967 }
968}
969
970impl PluginExecutor for CdylibExecutor {
971 fn info(&self) -> &PluginInfo {
972 &self.info
973 }
974
975 fn method_count(&self) -> u32 {
976 self.method_count
977 }
978
979 fn call_raw(&self, method: usize, input: &[u8]) -> Result<Vec<u8>, CallError> {
984 self.call_method_raw(method, input)
985 }
986}
987
988#[cfg(feature = "streaming")]
997fn pump_stream_handle(
998 out_ptr: *mut u8,
999 decode_item: fn(&[u8]) -> Result<fidius_core::Value, CallError>,
1000) -> crate::stream::ChunkStream {
1001 use fidius_core::stream_ffi::FidiusStreamHandle;
1002 use fidius_core::Value;
1003
1004 const STREAM_CHANNEL_CAP: usize = 4;
1007
1008 struct SendHandle(*mut FidiusStreamHandle);
1011 unsafe impl Send for SendHandle {}
1012 let send_handle = SendHandle(out_ptr as *mut FidiusStreamHandle);
1013
1014 let (tx, rx) = tokio::sync::mpsc::channel::<Result<Value, CallError>>(STREAM_CHANNEL_CAP);
1015
1016 std::thread::spawn(move || {
1017 let send_handle = send_handle;
1020 let handle = send_handle.0;
1021
1022 const INITIAL_ITEM_CAP: usize = 64;
1027 let mut buf = vec![0u8; INITIAL_ITEM_CAP];
1028
1029 loop {
1030 let next = unsafe { (*handle).next };
1031 let mut out_len: u32 = 0;
1032 let mut status =
1033 unsafe { next(handle, buf.as_mut_ptr(), buf.len() as u32, &mut out_len) };
1034 if status == STATUS_BUFFER_TOO_SMALL {
1035 buf.resize(out_len as usize, 0);
1038 status = unsafe { next(handle, buf.as_mut_ptr(), buf.len() as u32, &mut out_len) };
1039 }
1040 match status {
1041 STATUS_OK => {
1042 let item = decode_item(&buf[..out_len as usize]);
1043 let is_err = item.is_err();
1044 if tx.blocking_send(item).is_err() {
1045 break; }
1047 if is_err {
1048 break;
1049 }
1050 }
1051 STATUS_STREAM_END => break,
1052 STATUS_PLUGIN_ERROR => {
1053 let pe = if out_len > 0 {
1054 wire::deserialize::<PluginError>(&buf[..out_len as usize]).unwrap_or_else(
1055 |_| PluginError::new("UNKNOWN", "malformed stream error"),
1056 )
1057 } else {
1058 PluginError::new("UNKNOWN", "stream error without data")
1059 };
1060 let _ = tx.blocking_send(Err(CallError::Plugin(pe)));
1061 break;
1062 }
1063 STATUS_BUFFER_TOO_SMALL => {
1064 let _ = tx.blocking_send(Err(CallError::BufferTooSmall));
1066 break;
1067 }
1068 STATUS_PANIC => {
1069 let _ = tx.blocking_send(Err(CallError::Panic(
1070 "plugin panicked in stream next".into(),
1071 )));
1072 break;
1073 }
1074 code => {
1075 let _ = tx.blocking_send(Err(CallError::UnknownStatus { code }));
1076 break;
1077 }
1078 }
1079 }
1080 unsafe {
1082 let drop_fn = (*handle).drop_fn;
1083 drop_fn(handle);
1084 }
1085 });
1086
1087 let body = futures::stream::unfold(rx, |mut rx| async move {
1088 rx.recv().await.map(|item| (item, rx))
1089 });
1090 crate::stream::ChunkStream::new(body)
1091}