1use std::ffi::c_void;
9use std::fmt;
10
11pub const ABI_VERSION: u32 = 1;
13
14#[repr(transparent)]
20#[derive(Copy, Clone, PartialEq, Eq, Hash)]
21pub struct NrStatus(u32);
22
23#[allow(non_upper_case_globals)]
24impl NrStatus {
25 pub const Ok: Self = Self(0);
26 pub const Err: Self = Self(1);
27 pub const Invalid: Self = Self(2);
28 pub const Unsupported: Self = Self(3);
29 pub const StreamEnd: Self = Self(4);
31 pub const Panic: Self = Self(5);
33 pub const Backpressure: Self = Self(6);
35
36 pub const fn from_raw(value: u32) -> Self {
38 Self(value)
39 }
40
41 pub const fn as_raw(self) -> u32 {
43 self.0
44 }
45
46 pub const fn is_terminal(self) -> bool {
48 matches!(
49 self,
50 Self::Err | Self::Invalid | Self::Unsupported | Self::StreamEnd | Self::Panic
51 )
52 }
53}
54
55impl fmt::Debug for NrStatus {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 let name = match *self {
58 Self::Ok => "Ok",
59 Self::Err => "Err",
60 Self::Invalid => "Invalid",
61 Self::Unsupported => "Unsupported",
62 Self::StreamEnd => "StreamEnd",
63 Self::Panic => "Panic",
64 Self::Backpressure => "Backpressure",
65 Self(value) => return f.debug_tuple("Unknown").field(&value).finish(),
66 };
67 f.write_str(name)
68 }
69}
70
71#[repr(C)]
77#[derive(Debug, Copy, Clone, Default)]
78pub struct NrStr {
79 pub ptr: *const u8,
80 pub len: u32,
81 pub _reserved: u32,
82}
83
84#[repr(C)]
87#[derive(Debug, Copy, Clone, Default)]
88pub struct NrBytes {
89 pub ptr: *const u8,
90 pub len: u64,
91}
92
93#[repr(C)]
95#[derive(Debug, Copy, Clone, Default)]
96pub struct NrKV {
97 pub key: NrStr,
98 pub value: NrStr,
99}
100
101#[derive(Debug, Clone, PartialEq, Eq)]
103pub enum NrViewError {
104 NullPointer,
106 LengthOverflow,
108 InvalidUtf8(std::str::Utf8Error),
110}
111
112impl fmt::Display for NrViewError {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 match self {
115 Self::NullPointer => f.write_str("non-empty ABI view has a null pointer"),
116 Self::LengthOverflow => f.write_str("ABI view length exceeds usize"),
117 Self::InvalidUtf8(error) => write!(f, "ABI string is not valid UTF-8: {error}"),
118 }
119 }
120}
121
122impl std::error::Error for NrViewError {
123 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
124 match self {
125 Self::InvalidUtf8(error) => Some(error),
126 Self::NullPointer | Self::LengthOverflow => None,
127 }
128 }
129}
130
131#[repr(C)]
134#[derive(Debug, Default)]
135pub struct NrKVAny {
136 key: NrStr,
137 key_storage: NrVec<u8>,
138 pub value: NrAny,
139}
140
141#[repr(C)]
144#[derive(Debug, Copy, Clone, Default)]
145pub struct NrIndexSlot {
146 pub hash: u64,
147 pub entry_idx: u32, pub state: u8, pub _pad: [u8; 3],
150}
151
152#[repr(C)]
155#[derive(Debug, Default)]
156pub struct NrMap {
157 entries: NrVec<NrKVAny>,
158 index: NrVec<NrIndexSlot>, used: u32, tomb: u32, }
162
163#[repr(C)]
166#[derive(Debug)]
167pub struct NrAny {
168 data: *mut c_void,
170 size: u64,
172 type_tag: u32,
174 clone_fn: Option<unsafe extern "C" fn(*const c_void) -> *mut c_void>,
176 drop_fn: Option<unsafe extern "C" fn(*mut c_void)>,
178}
179
180#[repr(C)]
188#[derive(Debug)]
189pub struct NrVec<T> {
190 ptr: *mut T,
191 len: usize,
192 cap: usize,
193 owned: u8,
194 _reserved: [u8; 7],
195 drop_fn: Option<unsafe extern "C" fn(*mut T, usize, usize)>,
196}
197
198impl<T> Default for NrVec<T> {
199 fn default() -> Self {
200 Self {
201 ptr: std::ptr::null_mut(),
202 len: 0,
203 cap: 0,
204 owned: 0,
205 _reserved: [0; 7],
206 drop_fn: None,
207 }
208 }
209}
210
211impl Default for NrAny {
212 fn default() -> Self {
213 Self {
214 data: std::ptr::null_mut(),
215 size: 0,
216 type_tag: 0,
217 clone_fn: None,
218 drop_fn: None,
219 }
220 }
221}
222
223#[repr(C)]
226#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
227pub struct NrTuple<A, B> {
228 pub a: A,
229 pub b: B,
230}
231
232#[repr(C)]
234#[derive(Debug, Copy, Clone)]
235pub struct NrHostVTable {
236 pub send_result: unsafe extern "C" fn(
237 host_ctx: *mut c_void,
238 sid: u64,
239 status: NrStatus,
240 payload: NrVec<u8>,
241 ) -> NrStatus,
242}
243
244#[repr(C)]
247#[derive(Debug, Copy, Clone)]
248pub struct NrHostExt {
249 pub set_state: unsafe extern "C" fn(
252 host_ctx: *mut c_void,
253 sid: u64,
254 key: NrStr,
255 value: NrBytes,
256 ) -> NrStatus,
257
258 pub get_state: unsafe extern "C" fn(host_ctx: *mut c_void, sid: u64, key: NrStr) -> NrVec<u8>,
261}
262
263unsafe impl Send for NrHostExt {}
265unsafe impl Sync for NrHostExt {}
266
267#[repr(C)]
269#[derive(Debug, Copy, Clone)]
270pub struct NrPluginVTable {
271 pub init: Option<
272 unsafe extern "C" fn(host_ctx: *mut c_void, host_vtable: *const NrHostVTable) -> NrStatus,
273 >,
274
275 pub handle: Option<unsafe extern "C" fn(entry: NrStr, sid: u64, payload: NrBytes) -> NrStatus>,
276
277 pub shutdown: Option<unsafe extern "C" fn()>,
278
279 pub stream_data: Option<unsafe extern "C" fn(sid: u64, data: NrBytes) -> NrStatus>,
280
281 pub stream_close: Option<unsafe extern "C" fn(sid: u64) -> NrStatus>,
282}
283
284#[macro_export]
285macro_rules! define_plugin {
286 (
287 init: $init_fn:path,
288 shutdown: $shutdown_fn:path,
289 entries: {
290 $($entry_name:literal => $handler_fn:path),* $(,)?
291 }
292 $(, stream_handlers: {
293 data: $stream_data_fn:path,
294 close: $stream_close_fn:path $(,)?
295 })?
296 ) => {
297 static PLUGIN_VTABLE: $crate::NrPluginVTable = $crate::NrPluginVTable {
299 init: Some(plugin_init_wrapper),
300 handle: Some(plugin_handle_wrapper),
301 shutdown: Some(plugin_shutdown_wrapper),
302 stream_data: Some(plugin_stream_data_wrapper),
303 stream_close: Some(plugin_stream_close_wrapper),
304 };
305
306 static PLUGIN_INFO: $crate::NrPluginInfo = $crate::NrPluginInfo {
308 abi_version: $crate::ABI_VERSION,
309 struct_size: std::mem::size_of::<$crate::NrPluginInfo>() as u32,
310 name: $crate::NrStr::from_static(env!("CARGO_PKG_NAME")),
311 version: $crate::NrStr::from_static(env!("CARGO_PKG_VERSION")),
312 plugin_ctx: std::ptr::null_mut(),
313 vtable: &PLUGIN_VTABLE,
314 };
315
316 #[unsafe(no_mangle)]
318 pub extern "C" fn nylon_ring_get_plugin_v1() -> *const $crate::NrPluginInfo {
319 &PLUGIN_INFO
320 }
321
322 unsafe extern "C" fn plugin_init_wrapper(
324 host_ctx: *mut std::ffi::c_void,
325 host_vtable: *const $crate::NrHostVTable,
326 ) -> $crate::NrStatus {
327 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
328 $init_fn(host_ctx, host_vtable)
329 }))
330 .unwrap_or($crate::NrStatus::Panic)
331 }
332
333 unsafe extern "C" fn plugin_shutdown_wrapper() {
334 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
335 $shutdown_fn();
336 }));
337 }
338
339 unsafe extern "C" fn plugin_handle_wrapper(
340 entry: $crate::NrStr,
341 sid: u64,
342 payload: $crate::NrBytes,
343 ) -> $crate::NrStatus {
344 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
345 let entry_bytes = match unsafe { entry.as_bytes() } {
349 Ok(entry) => entry,
350 Err(_) => return $crate::NrStatus::Invalid,
351 };
352 $(
353 if entry_bytes == $entry_name.as_bytes() {
354 return unsafe { $handler_fn(sid, payload) };
355 }
356 )*
357 $crate::NrStatus::Invalid
358 }))
359 .unwrap_or($crate::NrStatus::Panic)
360 }
361
362 unsafe extern "C" fn plugin_stream_data_wrapper(
363 sid: u64,
364 data: $crate::NrBytes,
365 ) -> $crate::NrStatus {
366 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
367 let _ = (sid, data);
368 $(
369 return unsafe { $stream_data_fn(sid, data) };
370 )?
371 #[allow(unreachable_code)]
372 $crate::NrStatus::Unsupported
373 }))
374 .unwrap_or($crate::NrStatus::Panic)
375 }
376
377 unsafe extern "C" fn plugin_stream_close_wrapper(
378 sid: u64,
379 ) -> $crate::NrStatus {
380 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
381 let _ = sid;
382 $(
383 return unsafe { $stream_close_fn(sid) };
384 )?
385 #[allow(unreachable_code)]
386 $crate::NrStatus::Unsupported
387 }))
388 .unwrap_or($crate::NrStatus::Panic)
389 }
390 };
391}
392
393#[repr(C)]
395#[derive(Debug, Copy, Clone)]
396pub struct NrPluginInfo {
397 pub abi_version: u32,
398 pub struct_size: u32,
399
400 pub name: NrStr,
401 pub version: NrStr,
402
403 pub plugin_ctx: *mut c_void,
404 pub vtable: *const NrPluginVTable,
405}
406
407impl NrStr {
408 pub fn new(s: &str) -> Self {
413 let len = u32::try_from(s.len()).expect("NrStr cannot represent strings larger than 4 GiB");
414 Self {
415 ptr: s.as_ptr(),
416 len,
417 _reserved: 0,
418 }
419 }
420
421 pub const fn from_static(s: &'static str) -> Self {
423 assert!(
424 s.len() <= u32::MAX as usize,
425 "static string is too large for NrStr"
426 );
427 Self {
428 ptr: s.as_ptr(),
429 len: s.len() as u32,
430 _reserved: 0,
431 }
432 }
433
434 pub unsafe fn as_str<'a>(&self) -> Result<&'a str, NrViewError> {
442 let bytes = unsafe { view_bytes(self.ptr, u64::from(self.len))? };
443 std::str::from_utf8(bytes).map_err(NrViewError::InvalidUtf8)
444 }
445
446 pub unsafe fn as_bytes<'a>(&self) -> Result<&'a [u8], NrViewError> {
454 unsafe { view_bytes(self.ptr, u64::from(self.len)) }
455 }
456
457 pub const fn is_empty(&self) -> bool {
459 self.len == 0
460 }
461
462 pub const fn len(&self) -> usize {
464 self.len as usize
465 }
466
467 pub const fn as_ptr(&self) -> *const u8 {
469 self.ptr
470 }
471
472 pub fn clear(&mut self) {
474 self.ptr = std::ptr::null();
475 self.len = 0;
476 self._reserved = 0;
477 }
478}
479
480unsafe fn view_bytes<'a>(ptr: *const u8, len: u64) -> Result<&'a [u8], NrViewError> {
481 if len == 0 {
482 return Ok(&[]);
483 }
484 if ptr.is_null() {
485 return Err(NrViewError::NullPointer);
486 }
487 let len = usize::try_from(len).map_err(|_| NrViewError::LengthOverflow)?;
488 Ok(unsafe { std::slice::from_raw_parts(ptr, len) })
489}
490
491impl NrBytes {
492 pub fn from_slice(s: &[u8]) -> Self {
494 Self {
495 ptr: s.as_ptr(),
496 len: u64::try_from(s.len()).expect("NrBytes length does not fit in u64"),
497 }
498 }
499
500 pub unsafe fn as_slice<'a>(&self) -> Result<&'a [u8], NrViewError> {
508 unsafe { view_bytes(self.ptr, self.len) }
509 }
510
511 pub const fn is_empty(&self) -> bool {
513 self.len == 0
514 }
515
516 pub const fn len(&self) -> u64 {
518 self.len
519 }
520}
521
522impl Clone for NrKVAny {
523 fn clone(&self) -> Self {
524 Self::new(self.key(), self.value.clone())
525 }
526}
527
528impl Clone for NrMap {
529 fn clone(&self) -> Self {
530 Self {
531 entries: self.entries.clone(),
532 index: self.index.clone(),
533 used: self.used,
534 tomb: self.tomb,
535 }
536 }
537}
538
539impl Clone for NrAny {
540 fn clone(&self) -> Self {
555 if self.data.is_null() {
556 return Self::default();
557 }
558 let clone_fn = self
559 .clone_fn
560 .expect("non-null NrAny values always have a clone function");
561 let data = unsafe { clone_fn(self.data.cast_const()) };
562 assert!(!data.is_null(), "NrAny clone callback failed");
563 Self {
564 data,
565 size: self.size,
566 type_tag: self.type_tag,
567 clone_fn: self.clone_fn,
568 drop_fn: self.drop_fn,
569 }
570 }
571}
572
573impl<T: Clone> Clone for NrVec<T> {
574 fn clone(&self) -> Self {
575 if self.len == 0 {
576 return Self::default();
577 }
578 let v = self.as_slice().to_vec();
579 Self::from_vec(v)
580 }
581}
582
583impl NrKV {
584 pub fn new(key: &str, value: &str) -> Self {
585 Self {
586 key: NrStr::new(key),
587 value: NrStr::new(value),
588 }
589 }
590
591 pub fn from_nr_str(key: NrStr, value: NrStr) -> Self {
592 Self { key, value }
593 }
594}
595
596impl NrKVAny {
597 pub fn new(key: &str, value: NrAny) -> Self {
598 let key_storage = NrVec::from_vec(key.as_bytes().to_vec());
599 let key = NrStr::new(
600 std::str::from_utf8(key_storage.as_slice())
601 .expect("key bytes originate from a valid UTF-8 string"),
602 );
603 Self {
604 key,
605 key_storage,
606 value,
607 }
608 }
609
610 pub unsafe fn from_nr_str(key: NrStr, value: NrAny) -> Result<Self, NrViewError> {
616 let key = unsafe { key.as_str()? };
617 Ok(Self::new(key, value))
618 }
619
620 pub fn key(&self) -> &str {
622 unsafe { std::str::from_utf8_unchecked(self.key_storage.as_slice()) }
624 }
625}
626
627#[inline]
629fn hash_str(s: &str) -> u64 {
630 const FNV_OFFSET: u64 = 0xcbf29ce484222325;
631 const FNV_PRIME: u64 = 0x100000001b3;
632 let mut h = FNV_OFFSET;
633 for &b in s.as_bytes() {
634 h ^= b as u64;
635 h = h.wrapping_mul(FNV_PRIME);
636 }
637 h
638}
639
640impl NrMap {
641 pub fn new() -> Self {
642 Self::default()
643 }
644
645 #[inline]
646 fn index_len(&self) -> usize {
647 self.index.len
648 }
649
650 fn ensure_index(&mut self) {
651 if self.index.is_empty() && self.entries.len >= 8 {
653 self.rehash(16);
654 }
655 }
656
657 fn rehash(&mut self, mut new_cap: usize) {
658 new_cap = new_cap.next_power_of_two().max(16);
660
661 let mut slots = Vec::with_capacity(new_cap);
663 slots.resize_with(new_cap, NrIndexSlot::default);
664
665 self.index = NrVec::from_vec(slots);
666 self.used = 0;
667 self.tomb = 0;
668
669 for i in 0..self.entries.len {
671 let kv = unsafe { &*self.entries.ptr.add(i) };
672 let k = kv.key();
673 let entry_idx =
674 u32::try_from(i).expect("NrMap cannot contain more than u32::MAX entries");
675 self.index_insert(hash_str(k), entry_idx);
676 }
677 }
678
679 #[inline]
680 fn should_grow(&self) -> bool {
681 if self.index.is_empty() {
683 return false;
684 }
685 let occupied = u64::from(self.used) + u64::from(self.tomb);
686 occupied * 10 >= self.index_len() as u64 * 7
687 }
688
689 fn maybe_grow(&mut self) {
690 if self.should_grow() {
691 let cap = self.index_len();
692 self.rehash(cap * 2);
693 }
694 }
695
696 fn index_insert(&mut self, hash: u64, entry_idx: u32) {
697 let cap = self.index_len();
698 if cap == 0 {
699 return;
700 }
701 let mask = cap - 1;
702 let mut pos = (hash as usize) & mask;
703 let mut first_tomb: Option<usize> = None;
704
705 for _ in 0..cap {
706 let slot = unsafe { &mut *self.index.ptr.add(pos) };
707 match slot.state {
708 0 => {
709 let target = first_tomb.unwrap_or(pos);
710 let s2 = unsafe { &mut *self.index.ptr.add(target) };
711 s2.hash = hash;
712 s2.entry_idx = entry_idx;
713 s2.state = 1;
714 if first_tomb.is_some() {
715 self.tomb -= 1;
716 }
717 self.used += 1;
718 return;
719 }
720 2 if first_tomb.is_none() => first_tomb = Some(pos),
721 _ => {}
722 }
723 pos = (pos + 1) & mask;
724 }
725
726 let cap2 = cap * 2;
728 self.rehash(cap2);
729 self.index_insert(hash, entry_idx);
730 }
731
732 pub fn insert(&mut self, key: &str, value: NrAny) {
733 if let Some(v) = self.get_mut(key) {
735 *v = value;
736 return;
737 }
738
739 assert!(
740 self.entries.len < u32::MAX as usize,
741 "NrMap cannot contain more than u32::MAX entries"
742 );
743 let kv = NrKVAny::new(key, value);
744 self.entries.push(kv);
745
746 self.ensure_index();
747 if !self.index.is_empty() {
748 self.maybe_grow();
749 let idx = u32::try_from(self.entries.len - 1)
750 .expect("NrMap cannot contain more than u32::MAX entries");
751 self.index_insert(hash_str(key), idx);
752 }
753 }
754
755 pub unsafe fn insert_nr(&mut self, key: NrStr, value: NrAny) -> Result<(), NrViewError> {
761 let key_str = unsafe { key.as_str()? };
762 if let Some(v) = self.get_mut(key_str) {
764 *v = value;
765 return Ok(());
766 }
767
768 assert!(
769 self.entries.len < u32::MAX as usize,
770 "NrMap cannot contain more than u32::MAX entries"
771 );
772 let kv = NrKVAny::new(key_str, value);
773 self.entries.push(kv);
774
775 self.ensure_index();
776 if !self.index.is_empty() {
777 self.maybe_grow();
778 let idx = u32::try_from(self.entries.len - 1)
779 .expect("NrMap cannot contain more than u32::MAX entries");
780 self.index_insert(hash_str(key_str), idx);
781 }
782 Ok(())
783 }
784
785 pub fn get(&self, key: &str) -> Option<&NrAny> {
786 if self.index.is_empty() {
787 for kv in self.entries.iter() {
789 if kv.key() == key {
790 return Some(&kv.value);
791 }
792 }
793 return None;
794 }
795
796 let h = hash_str(key);
797 let cap = self.index.len;
798 let mask = cap - 1;
799 let mut pos = (h as usize) & mask;
800
801 for _ in 0..cap {
802 let slot = unsafe { &*self.index.ptr.add(pos) };
803 match slot.state {
804 0 => return None, 1 if slot.hash == h => {
806 let kv = unsafe { &*self.entries.ptr.add(slot.entry_idx as usize) };
807 if kv.key() == key {
808 return Some(&kv.value);
809 }
810 }
811 _ => {}
812 }
813 pos = (pos + 1) & mask;
814 }
815 None
816 }
817
818 pub fn get_mut(&mut self, key: &str) -> Option<&mut NrAny> {
819 if self.index.is_empty() {
820 for kv in self.entries.iter_mut() {
821 if kv.key() == key {
822 return Some(&mut kv.value);
823 }
824 }
825 return None;
826 }
827
828 let h = hash_str(key);
829 let cap = self.index.len;
830 let mask = cap - 1;
831 let mut pos = (h as usize) & mask;
832
833 for _ in 0..cap {
834 let slot = unsafe { &*self.index.ptr.add(pos) };
835 match slot.state {
836 0 => return None,
837 1 if slot.hash == h => {
838 let kv = unsafe { &mut *self.entries.ptr.add(slot.entry_idx as usize) };
839 if kv.key() == key {
840 return Some(&mut kv.value);
841 }
842 }
843 _ => {}
844 }
845 pos = (pos + 1) & mask;
846 }
847 None
848 }
849
850 pub fn remove(&mut self, key: &str) -> Option<NrKVAny> {
851 let (idx, removed_slot) = if self.index.is_empty() {
854 (self.entries.iter().position(|kv| kv.key() == key)?, None)
856 } else {
857 let h = hash_str(key);
859 let cap = self.index.len;
860 let mask = cap - 1;
861 let mut pos = (h as usize) & mask;
862 let mut found: Option<(usize, usize)> = None;
863
864 for _ in 0..cap {
865 let slot = unsafe { &*self.index.ptr.add(pos) };
866 match slot.state {
867 0 => break, 1 if slot.hash == h => {
869 let entry_idx = slot.entry_idx as usize;
870 let kv = unsafe { &*self.entries.ptr.add(entry_idx) };
871 if kv.key() == key {
872 found = Some((entry_idx, pos));
873 break;
874 }
875 }
876 _ => {}
877 }
878 pos = (pos + 1) & mask;
879 }
880
881 let (entry_idx, slot) = found?;
882 (entry_idx, Some(slot))
883 };
884
885 let last = self.entries.len - 1;
886
887 let removed = unsafe { std::ptr::read(self.entries.ptr.add(idx)) };
889
890 if idx != last {
891 unsafe {
893 let last_val = std::ptr::read(self.entries.ptr.add(last));
894 std::ptr::write(self.entries.ptr.add(idx), last_val);
895 }
896
897 if !self.index.is_empty() {
899 let h_last = unsafe {
900 let kv = &*self.entries.ptr.add(idx);
901 hash_str(kv.key())
902 };
903 let cap = self.index.len;
904 let mask = cap - 1;
905 let mut pos = (h_last as usize) & mask;
906
907 for _ in 0..cap {
908 let slot = unsafe { &mut *self.index.ptr.add(pos) };
909 if slot.state == 1 && slot.entry_idx == last as u32 {
910 slot.entry_idx = idx as u32;
911 break;
912 }
913 pos = (pos + 1) & mask;
914 }
915 }
916 }
917
918 self.entries.len -= 1;
919
920 if let Some(pos) = removed_slot {
922 let slot = unsafe { &mut *self.index.ptr.add(pos) };
923 debug_assert_eq!(slot.state, 1);
924 slot.state = 2;
925 self.used -= 1;
926 self.tomb += 1;
927
928 if self.should_grow() {
930 self.rehash(self.index_len().max(16));
931 }
932 }
933
934 Some(removed)
935 }
936
937 pub fn len(&self) -> usize {
938 self.entries.len
939 }
940
941 pub fn is_empty(&self) -> bool {
942 self.entries.len == 0
943 }
944
945 pub fn clear(&mut self) {
946 self.entries.clear();
947 self.index = NrVec::default();
948 self.used = 0;
949 self.tomb = 0;
950 }
951}
952
953impl NrAny {
954 pub fn new<T: Clone + Send + Sync + 'static>(value: T, type_tag: u32) -> Self {
956 let size = std::mem::size_of::<T>() as u64;
957 let data = Box::into_raw(Box::new(value)) as *mut c_void;
958 Self {
959 data,
960 size,
961 type_tag,
962 clone_fn: Some(clone_any::<T>),
963 drop_fn: Some(drop_any::<T>),
964 }
965 }
966
967 pub fn from_bytes(bytes: &[u8], type_tag: u32) -> Self {
969 Self::new(bytes.to_vec(), type_tag)
970 }
971
972 pub fn as_ptr<T>(&self) -> Result<*const T, NrStatus> {
977 if self.data.is_null() {
978 return Err(NrStatus::Invalid);
979 }
980 let expected_size = std::mem::size_of::<T>() as u64;
981 if self.size != expected_size {
982 return Err(NrStatus::Err);
983 }
984 Ok(self.data as *const T)
985 }
986
987 pub fn as_mut_ptr<T>(&mut self) -> Result<*mut T, NrStatus> {
988 if self.data.is_null() {
989 return Err(NrStatus::Invalid);
990 }
991 let expected_size = std::mem::size_of::<T>() as u64;
992 if self.size != expected_size {
993 return Err(NrStatus::Err);
994 }
995 Ok(self.data as *mut T)
996 }
997
998 pub fn is_null(&self) -> bool {
999 self.data.is_null()
1000 }
1001
1002 pub fn type_tag(&self) -> u32 {
1003 self.type_tag
1004 }
1005
1006 pub fn size(&self) -> u64 {
1007 self.size
1008 }
1009}
1010
1011unsafe extern "C" fn clone_any<T: Clone>(ptr: *const c_void) -> *mut c_void {
1012 if !ptr.is_null() {
1013 return std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1014 let value = unsafe { &*ptr.cast::<T>() };
1015 Box::into_raw(Box::new(value.clone())).cast::<c_void>()
1016 }))
1017 .unwrap_or(std::ptr::null_mut());
1018 }
1019 std::ptr::null_mut()
1020}
1021
1022unsafe extern "C" fn drop_any<T>(ptr: *mut c_void) {
1023 if !ptr.is_null() {
1024 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
1025 drop(Box::from_raw(ptr.cast::<T>()));
1026 }));
1027 }
1028}
1029
1030impl Drop for NrAny {
1031 fn drop(&mut self) {
1032 if let Some(drop_fn) = self.drop_fn
1033 && !self.data.is_null()
1034 {
1035 unsafe {
1036 drop_fn(self.data);
1037 }
1038 }
1039 }
1040}
1041
1042impl NrPluginInfo {
1043 pub fn compatible(&self, expected_abi_version: u32) -> bool {
1044 self.abi_version == expected_abi_version
1045 }
1046}
1047
1048impl NrVec<u8> {
1049 pub unsafe fn from_nr_bytes(bytes: NrBytes) -> Result<Self, NrViewError> {
1055 let v = unsafe { bytes.as_slice()? }.to_vec();
1056 Ok(Self::from_vec(v))
1057 }
1058
1059 pub fn from_string(s: String) -> Self {
1060 Self::from_vec(s.into_bytes())
1061 }
1062}
1063
1064impl<T> NrVec<T> {
1065 pub fn from_vec(v: Vec<T>) -> Self {
1067 let mut v = std::mem::ManuallyDrop::new(v);
1068 let ptr = v.as_mut_ptr();
1069 let len = v.len();
1070 let cap = v.capacity();
1071 Self {
1072 ptr,
1073 len,
1074 cap,
1075 owned: 1,
1076 _reserved: [0; 7],
1077 drop_fn: Some(drop_vec::<T>),
1078 }
1079 }
1080
1081 pub fn into_vec(self) -> Vec<T>
1086 where
1087 T: Clone,
1088 {
1089 self.as_slice().to_vec()
1090 }
1091
1092 fn push(&mut self, value: T) {
1093 if self.owned == 0 && self.ptr.is_null() && self.len == 0 && self.cap == 0 {
1094 self.owned = 1;
1095 self.drop_fn = Some(drop_vec::<T>);
1096 }
1097 assert_eq!(self.owned, 1, "cannot mutate a borrowed NrVec");
1098 if self.len == self.cap {
1099 self.reserve(1);
1100 }
1101 unsafe {
1102 std::ptr::write(self.ptr.add(self.len), value);
1103 }
1104 self.len += 1;
1105 }
1106
1107 fn clear(&mut self) {
1108 assert_eq!(self.owned, 1, "cannot mutate a borrowed NrVec");
1109 while self.len > 0 {
1110 self.len -= 1;
1111 unsafe {
1112 std::ptr::drop_in_place(self.ptr.add(self.len));
1113 }
1114 }
1115 }
1116
1117 fn reserve(&mut self, additional: usize) {
1118 assert_eq!(self.owned, 1, "cannot resize a borrowed NrVec");
1119 if std::mem::size_of::<T>() == 0 {
1120 assert!(
1121 self.len.checked_add(additional).is_some(),
1122 "capacity overflow"
1123 );
1124 if self.cap == 0 {
1125 self.ptr = std::ptr::NonNull::<T>::dangling().as_ptr();
1126 self.cap = usize::MAX;
1127 }
1128 return;
1129 }
1130
1131 let available = self.cap - self.len;
1132 if available < additional {
1133 let required = self.len.checked_add(additional).expect("capacity overflow");
1134 let new_cap = if self.cap == 0 {
1135 std::cmp::max(1, required)
1136 } else {
1137 std::cmp::max(self.cap.saturating_mul(2), required)
1138 };
1139
1140 let new_layout = match std::alloc::Layout::array::<T>(new_cap) {
1141 Ok(layout) => layout,
1142 Err(_) => {
1143 std::alloc::handle_alloc_error(
1145 std::alloc::Layout::from_size_align(usize::MAX, 1)
1146 .unwrap_or_else(|_| std::alloc::Layout::new::<u8>()),
1147 )
1148 }
1149 };
1150
1151 let new_ptr = if self.cap == 0 {
1152 unsafe { std::alloc::alloc(new_layout) }
1153 } else {
1154 let old_layout = match std::alloc::Layout::array::<T>(self.cap) {
1155 Ok(layout) => layout,
1156 Err(_) => {
1157 std::alloc::handle_alloc_error(new_layout)
1160 }
1161 };
1162 unsafe { std::alloc::realloc(self.ptr as *mut u8, old_layout, new_layout.size()) }
1163 };
1164
1165 if new_ptr.is_null() {
1166 std::alloc::handle_alloc_error(new_layout);
1167 }
1168
1169 self.ptr = new_ptr as *mut T;
1170 self.cap = new_cap;
1171 }
1172 }
1173
1174 pub fn capacity(&self) -> usize {
1175 self.cap
1176 }
1177
1178 pub fn len(&self) -> usize {
1179 self.len
1180 }
1181
1182 pub fn is_empty(&self) -> bool {
1183 self.len == 0
1184 }
1185
1186 pub fn as_ptr(&self) -> *const T {
1187 self.ptr
1188 }
1189
1190 pub fn as_mut_ptr(&mut self) -> *mut T {
1191 self.ptr
1192 }
1193}
1194
1195impl<T> Drop for NrVec<T> {
1196 fn drop(&mut self) {
1197 if self.owned == 1
1198 && let Some(drop_fn) = self.drop_fn
1199 {
1200 unsafe {
1201 drop_fn(self.ptr, self.len, self.cap);
1202 }
1203 }
1204 }
1205}
1206
1207unsafe extern "C" fn drop_vec<T>(ptr: *mut T, len: usize, cap: usize) {
1208 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1209 if cap != 0 {
1210 unsafe {
1211 drop(Vec::from_raw_parts(ptr, len, cap));
1212 }
1213 }
1214 }));
1215}
1216
1217impl<T> NrVec<T> {
1218 pub fn iter(&self) -> std::slice::Iter<'_, T> {
1219 self.as_slice().iter()
1220 }
1221
1222 fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
1223 self.as_mut_slice().iter_mut()
1224 }
1225
1226 pub fn as_slice(&self) -> &[T] {
1227 if self.len == 0 {
1228 &[]
1229 } else {
1230 unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
1231 }
1232 }
1233
1234 fn as_mut_slice(&mut self) -> &mut [T] {
1235 if self.len == 0 {
1236 &mut []
1237 } else {
1238 unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) }
1239 }
1240 }
1241}
1242
1243impl<'a, T> IntoIterator for &'a NrVec<T> {
1244 type Item = &'a T;
1245 type IntoIter = std::slice::Iter<'a, T>;
1246
1247 fn into_iter(self) -> Self::IntoIter {
1248 self.iter()
1249 }
1250}
1251
1252impl<T: Clone> IntoIterator for NrVec<T> {
1253 type Item = T;
1254 type IntoIter = std::vec::IntoIter<T>;
1255
1256 fn into_iter(self) -> Self::IntoIter {
1257 self.into_vec().into_iter()
1258 }
1259}
1260
1261unsafe impl Send for NrStr {}
1264unsafe impl Sync for NrStr {}
1265
1266unsafe impl Send for NrBytes {}
1267unsafe impl Sync for NrBytes {}
1268
1269unsafe impl Send for NrKV {}
1270unsafe impl Sync for NrKV {}
1271
1272unsafe impl Send for NrKVAny {}
1273unsafe impl Sync for NrKVAny {}
1274
1275unsafe impl Send for NrMap {}
1276unsafe impl Sync for NrMap {}
1277
1278unsafe impl Send for NrAny {}
1279unsafe impl Sync for NrAny {}
1280
1281unsafe impl Send for NrHostVTable {}
1282unsafe impl Sync for NrHostVTable {}
1283
1284unsafe impl Send for NrPluginVTable {}
1285unsafe impl Sync for NrPluginVTable {}
1286
1287unsafe impl Send for NrPluginInfo {}
1288unsafe impl Sync for NrPluginInfo {}
1289
1290unsafe impl<T: Send> Send for NrVec<T> {}
1291unsafe impl<T: Sync> Sync for NrVec<T> {}
1292
1293unsafe impl<A: Send, B: Send> Send for NrTuple<A, B> {}
1294unsafe impl<A: Sync, B: Sync> Sync for NrTuple<A, B> {}
1295
1296#[cfg(test)]
1297mod tests {
1298 use super::*;
1299 use std::mem::{align_of, size_of};
1300
1301 unsafe fn test_plugin_init(_: *mut c_void, _: *const NrHostVTable) -> NrStatus {
1302 NrStatus::Ok
1303 }
1304
1305 unsafe fn panicking_handler(_: u64, _: NrBytes) -> NrStatus {
1306 panic!("test panic must not cross the FFI boundary");
1307 }
1308
1309 fn test_plugin_shutdown() {}
1310
1311 define_plugin! {
1312 init: test_plugin_init,
1313 shutdown: test_plugin_shutdown,
1314 entries: {
1315 "panic" => panicking_handler,
1316 }
1317 }
1318
1319 #[test]
1320 fn test_layout() {
1321 assert_eq!(size_of::<NrStr>(), 16);
1324 assert_eq!(align_of::<NrStr>(), 8);
1325
1326 assert_eq!(size_of::<NrBytes>(), 16);
1329 assert_eq!(align_of::<NrBytes>(), 8);
1330
1331 assert_eq!(size_of::<NrVec<u8>>(), 40);
1333 assert_eq!(align_of::<NrVec<u8>>(), 8);
1334
1335 assert_eq!(size_of::<NrTuple<u64, u64>>(), 16);
1338 assert_eq!(align_of::<NrTuple<u64, u64>>(), 8);
1339
1340 assert_eq!(size_of::<NrKV>(), 32);
1343 assert_eq!(align_of::<NrKV>(), 8);
1344 }
1345
1346 #[test]
1347 fn test_nr_vec() {
1348 let mut v = NrVec::<u32>::default();
1349 assert_eq!(v.len, 0);
1350 assert_eq!(v.cap, 0);
1351
1352 v.push(1);
1353 assert_eq!(v.len, 1);
1354 assert!(v.cap >= 1);
1355 unsafe {
1356 assert_eq!(*v.ptr, 1);
1357 }
1358
1359 v.push(2);
1360 assert_eq!(v.len, 2);
1361 unsafe {
1362 assert_eq!(*v.ptr.add(1), 2);
1363 }
1364
1365 v.reserve(10);
1366 assert!(v.cap >= 12); v.clear();
1369 assert_eq!(v.len, 0);
1370 assert!(v.cap >= 12);
1371 }
1372 #[test]
1373 fn test_nr_vec_iter() {
1374 let mut v = NrVec::<u32>::default();
1375 v.push(1);
1376 v.push(2);
1377 v.push(3);
1378
1379 let mut iter = v.iter();
1380 assert_eq!(iter.next(), Some(&1));
1381 assert_eq!(iter.next(), Some(&2));
1382 assert_eq!(iter.next(), Some(&3));
1383 assert_eq!(iter.next(), None);
1384 }
1385
1386 #[test]
1387 fn test_nr_vec_iter_mut() {
1388 let mut v = NrVec::<u32>::default();
1389 v.push(1);
1390 v.push(2);
1391 v.push(3);
1392
1393 for x in v.iter_mut() {
1394 *x *= 2;
1395 }
1396
1397 let mut iter = v.iter();
1398 assert_eq!(iter.next(), Some(&2));
1399 assert_eq!(iter.next(), Some(&4));
1400 assert_eq!(iter.next(), Some(&6));
1401 assert_eq!(iter.next(), None);
1402 }
1403
1404 #[test]
1405 fn test_nr_vec_into_iter() {
1406 let mut v = NrVec::<u32>::default();
1407 v.push(1);
1408 v.push(2);
1409 v.push(3);
1410
1411 let mut iter = v.into_iter();
1412 assert_eq!(iter.next(), Some(1));
1413 assert_eq!(iter.next(), Some(2));
1414 assert_eq!(iter.next(), Some(3));
1415 assert_eq!(iter.next(), None);
1416 }
1417
1418 #[test]
1419 fn test_nr_vec_empty_and_zero_sized_values() {
1420 assert!(NrVec::<u8>::default().into_vec().is_empty());
1421
1422 let mut values = NrVec::default();
1423 values.push(());
1424 values.push(());
1425 assert_eq!(values.len(), 2);
1426 assert_eq!(values.into_iter().count(), 2);
1427 }
1428
1429 #[test]
1430 fn test_nr_vec_collect() {
1431 let mut v = NrVec::<u32>::default();
1432 v.push(10);
1433 v.push(20);
1434
1435 let collected: Vec<u32> = v.iter().cloned().collect();
1436 assert_eq!(collected, vec![10, 20]);
1437 }
1438
1439 #[test]
1440 fn test_nr_map() {
1441 let mut map = NrMap::new();
1442 assert!(map.is_empty());
1443 assert_eq!(map.len(), 0);
1444
1445 let str_value1 = NrAny::new(String::from("value1"), 1);
1447 let str_value2 = NrAny::new(String::from("value2"), 1);
1448 map.insert("key1", str_value1);
1449 map.insert("key2", str_value2);
1450 assert_eq!(map.len(), 2);
1451
1452 let value1 = map.get("key1").unwrap();
1454 let str_ptr1 = value1.as_ptr::<String>().unwrap();
1455 unsafe {
1456 assert_eq!(*str_ptr1, "value1");
1457 }
1458
1459 let value2 = map.get("key2").unwrap();
1460 let str_ptr2 = value2.as_ptr::<String>().unwrap();
1461 unsafe {
1462 assert_eq!(*str_ptr2, "value2");
1463 }
1464
1465 assert!(map.get("key3").is_none());
1466
1467 let value_mut = map.get_mut("key1");
1469 assert!(value_mut.is_some());
1470
1471 let int_value = NrAny::new(42i32, 2);
1473 map.insert("key3", int_value);
1474 assert_eq!(map.len(), 3);
1475
1476 let int_val = map.get("key3").unwrap();
1477 let int_ptr = int_val.as_ptr::<i32>().unwrap();
1478 unsafe {
1479 assert_eq!(*int_ptr, 42);
1480 }
1481
1482 let removed = map.remove("key2");
1483 assert!(removed.is_some());
1484 assert_eq!(map.len(), 2);
1485 assert!(map.get("key2").is_none());
1486
1487 map.clear();
1488 assert!(map.is_empty());
1489
1490 {
1492 let temporary_key = String::from("owned-key");
1493 map.insert(&temporary_key, NrAny::new(7_u32, 2));
1494 }
1495 let stored = map.get("owned-key").unwrap();
1496 assert_eq!(unsafe { *stored.as_ptr::<u32>().unwrap() }, 7);
1497
1498 for index in 0..16 {
1500 map.insert(&format!("key-{index}"), NrAny::new(index, 2));
1501 }
1502 for index in 0..16 {
1503 assert!(map.remove(&format!("key-{index}")).is_some());
1504 }
1505 map.clear();
1506 assert!(map.get("missing").is_none());
1507 }
1508
1509 #[test]
1510 fn test_nr_any() {
1511 let any_int = NrAny::new(42i32, 1);
1512 assert!(!any_int.is_null());
1513 assert_eq!(any_int.type_tag(), 1);
1514 assert_eq!(any_int.size(), std::mem::size_of::<i32>() as u64);
1515
1516 let ptr = any_int.as_ptr::<i32>().unwrap();
1517 unsafe {
1518 assert_eq!(*ptr, 42);
1519 }
1520
1521 let any_string = NrAny::new(String::from("hello"), 2);
1522 assert_eq!(any_string.type_tag(), 2);
1523 let str_ptr = any_string.as_ptr::<String>().unwrap();
1524 unsafe {
1525 assert_eq!(*str_ptr, "hello");
1526 }
1527 let cloned_string = any_string.clone();
1528 let cloned_ptr = cloned_string.as_ptr::<String>().unwrap();
1529 unsafe {
1530 assert_eq!(*cloned_ptr, "hello");
1531 assert_ne!(str_ptr, cloned_ptr);
1532 }
1533
1534 let any_bytes = NrAny::from_bytes(b"test", 3);
1535 assert_eq!(any_bytes.type_tag(), 3);
1536 assert_eq!(any_bytes.size(), std::mem::size_of::<Vec<u8>>() as u64);
1537 let bytes_ptr = any_bytes.as_ptr::<Vec<u8>>().unwrap();
1538 unsafe {
1539 assert_eq!(&*bytes_ptr, b"test");
1540 }
1541
1542 let default_any = NrAny::default();
1543 assert!(default_any.is_null());
1544 assert_eq!(default_any.type_tag(), 0);
1545 assert_eq!(default_any.size(), 0);
1546
1547 assert_eq!(default_any.as_ptr::<i32>(), Err(NrStatus::Invalid));
1549 let mut default_any_mut = NrAny::default();
1550 assert_eq!(default_any_mut.as_mut_ptr::<i32>(), Err(NrStatus::Invalid));
1551
1552 let any_int = NrAny::new(42i32, 1);
1554 assert_eq!(any_int.as_ptr::<u64>(), Err(NrStatus::Err)); let mut any_int_mut = NrAny::new(42i32, 1);
1556 assert_eq!(any_int_mut.as_mut_ptr::<u64>(), Err(NrStatus::Err));
1557 }
1558
1559 #[test]
1560 fn test_borrowed_view_validation() {
1561 let empty = NrStr::default();
1562 assert_eq!(unsafe { empty.as_str() }.unwrap(), "");
1563
1564 let invalid_utf8 = [0xff];
1565 let invalid = NrStr {
1566 ptr: invalid_utf8.as_ptr(),
1567 len: 1,
1568 _reserved: 0,
1569 };
1570 assert!(matches!(
1571 unsafe { invalid.as_str() },
1572 Err(NrViewError::InvalidUtf8(_))
1573 ));
1574
1575 let null_bytes = NrBytes {
1576 ptr: std::ptr::null(),
1577 len: 1,
1578 };
1579 assert_eq!(
1580 unsafe { null_bytes.as_slice() },
1581 Err(NrViewError::NullPointer)
1582 );
1583 }
1584
1585 #[test]
1586 fn plugin_macro_contains_panics_and_rejects_invalid_utf8() {
1587 let panic_entry = NrStr::new("panic");
1588 assert_eq!(
1589 unsafe { plugin_handle_wrapper(panic_entry, 1, NrBytes::default()) },
1590 NrStatus::Panic
1591 );
1592
1593 let invalid_utf8 = [0xff];
1594 let invalid_entry = NrStr {
1595 ptr: invalid_utf8.as_ptr(),
1596 len: 1,
1597 _reserved: 0,
1598 };
1599 assert_eq!(
1600 unsafe { plugin_handle_wrapper(invalid_entry, 2, NrBytes::default()) },
1601 NrStatus::Invalid
1602 );
1603 }
1604}