1use std::ffi::c_void;
9use std::fmt;
10
11pub mod plugin;
12
13pub use plugin::{AsyncSession, AsyncTask, Plugin, Reply, Session};
14
15pub const ABI_VERSION: u32 = 2;
18
19#[repr(transparent)]
25#[derive(Copy, Clone, PartialEq, Eq, Hash)]
26pub struct NrStatus(u32);
27
28#[allow(non_upper_case_globals)]
29impl NrStatus {
30 pub const Ok: Self = Self(0);
31 pub const Err: Self = Self(1);
32 pub const Invalid: Self = Self(2);
33 pub const Unsupported: Self = Self(3);
34 pub const StreamEnd: Self = Self(4);
36 pub const Panic: Self = Self(5);
38 pub const Backpressure: Self = Self(6);
40
41 pub const fn from_raw(value: u32) -> Self {
43 Self(value)
44 }
45
46 pub const fn as_raw(self) -> u32 {
48 self.0
49 }
50
51 pub const fn is_terminal(self) -> bool {
53 matches!(
54 self,
55 Self::Err | Self::Invalid | Self::Unsupported | Self::StreamEnd | Self::Panic
56 )
57 }
58}
59
60impl fmt::Debug for NrStatus {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 let name = match *self {
63 Self::Ok => "Ok",
64 Self::Err => "Err",
65 Self::Invalid => "Invalid",
66 Self::Unsupported => "Unsupported",
67 Self::StreamEnd => "StreamEnd",
68 Self::Panic => "Panic",
69 Self::Backpressure => "Backpressure",
70 Self(value) => return f.debug_tuple("Unknown").field(&value).finish(),
71 };
72 f.write_str(name)
73 }
74}
75
76#[repr(C)]
82#[derive(Debug, Copy, Clone, Default)]
83pub struct NrStr {
84 pub ptr: *const u8,
85 pub len: u32,
86 pub _reserved: u32,
87}
88
89#[repr(C)]
92#[derive(Debug, Copy, Clone, Default)]
93pub struct NrBytes {
94 pub ptr: *const u8,
95 pub len: u64,
96}
97
98#[repr(C)]
100#[derive(Debug, Copy, Clone, Default)]
101pub struct NrKV {
102 pub key: NrStr,
103 pub value: NrStr,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
108pub enum NrViewError {
109 NullPointer,
111 LengthOverflow,
113 InvalidUtf8(std::str::Utf8Error),
115}
116
117impl fmt::Display for NrViewError {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 match self {
120 Self::NullPointer => f.write_str("non-empty ABI view has a null pointer"),
121 Self::LengthOverflow => f.write_str("ABI view length exceeds usize"),
122 Self::InvalidUtf8(error) => write!(f, "ABI string is not valid UTF-8: {error}"),
123 }
124 }
125}
126
127impl std::error::Error for NrViewError {
128 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
129 match self {
130 Self::InvalidUtf8(error) => Some(error),
131 Self::NullPointer | Self::LengthOverflow => None,
132 }
133 }
134}
135
136#[repr(C)]
139#[derive(Debug, Default)]
140pub struct NrKVAny {
141 key: NrStr,
142 key_storage: NrVec<u8>,
143 pub value: NrAny,
144}
145
146#[repr(C)]
149#[derive(Debug, Copy, Clone, Default)]
150pub struct NrIndexSlot {
151 pub hash: u64,
152 pub entry_idx: u32, pub state: u8, pub _pad: [u8; 3],
155}
156
157#[repr(C)]
160#[derive(Debug, Default)]
161pub struct NrMap {
162 entries: NrVec<NrKVAny>,
163 index: NrVec<NrIndexSlot>, used: u32, tomb: u32, }
167
168#[repr(C)]
171#[derive(Debug)]
172pub struct NrAny {
173 data: *mut c_void,
175 size: u64,
177 type_tag: u32,
179 clone_fn: Option<unsafe extern "C" fn(*const c_void) -> *mut c_void>,
181 drop_fn: Option<unsafe extern "C" fn(*mut c_void)>,
183}
184
185#[repr(C)]
193#[derive(Debug)]
194pub struct NrVec<T> {
195 ptr: *mut T,
196 len: usize,
197 cap: usize,
198 owned: u8,
199 _reserved: [u8; 7],
200 drop_fn: Option<unsafe extern "C" fn(*mut T, usize, usize)>,
201}
202
203impl<T> Default for NrVec<T> {
204 fn default() -> Self {
205 Self {
206 ptr: std::ptr::null_mut(),
207 len: 0,
208 cap: 0,
209 owned: 0,
210 _reserved: [0; 7],
211 drop_fn: None,
212 }
213 }
214}
215
216impl Default for NrAny {
217 fn default() -> Self {
218 Self {
219 data: std::ptr::null_mut(),
220 size: 0,
221 type_tag: 0,
222 clone_fn: None,
223 drop_fn: None,
224 }
225 }
226}
227
228#[repr(C)]
231#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
232pub struct NrTuple<A, B> {
233 pub a: A,
234 pub b: B,
235}
236
237#[repr(C)]
244#[derive(Debug)]
245pub struct NrOwnedBytes {
246 pub ptr: *const u8,
247 pub len: u64,
248 pub owner_ctx: *mut c_void,
249 pub release: Option<unsafe extern "C" fn(owner_ctx: *mut c_void, ptr: *const u8, len: u64)>,
250}
251
252impl NrOwnedBytes {
253 pub const fn empty() -> Self {
255 Self {
256 ptr: std::ptr::null(),
257 len: 0,
258 owner_ctx: std::ptr::null_mut(),
259 release: None,
260 }
261 }
262
263 pub const fn from_static(bytes: &'static [u8]) -> Self {
265 Self {
266 ptr: bytes.as_ptr(),
267 len: bytes.len() as u64,
268 owner_ctx: std::ptr::null_mut(),
269 release: None,
270 }
271 }
272
273 pub fn from_vec(vec: Vec<u8>) -> Self {
277 unsafe extern "C" fn release_vec(owner_ctx: *mut c_void, ptr: *const u8, len: u64) {
278 if !ptr.is_null() {
279 drop(unsafe {
281 Vec::from_raw_parts(ptr.cast_mut(), len as usize, owner_ctx as usize)
282 });
283 }
284 }
285 let mut vec = std::mem::ManuallyDrop::new(vec);
286 Self {
287 ptr: vec.as_mut_ptr(),
288 len: vec.len() as u64,
289 owner_ctx: vec.capacity() as *mut c_void,
290 release: Some(release_vec),
291 }
292 }
293}
294
295#[repr(C)]
304#[derive(Debug)]
305pub struct NrBufferLease {
306 pub ptr: *mut u8,
307 pub cap: u64,
308 pub token: u64,
309}
310
311impl NrBufferLease {
312 pub const fn failed() -> Self {
314 Self {
315 ptr: std::ptr::null_mut(),
316 cap: 0,
317 token: 0,
318 }
319 }
320
321 pub fn is_failed(&self) -> bool {
323 self.ptr.is_null()
324 }
325}
326
327#[repr(C)]
329#[derive(Debug, Copy, Clone)]
330pub struct NrHostVTable {
331 pub send_result: unsafe extern "C" fn(
335 host_ctx: *mut c_void,
336 sid: u64,
337 status: NrStatus,
338 payload: NrVec<u8>,
339 ) -> NrStatus,
340 pub send_result_owned: unsafe extern "C" fn(
343 host_ctx: *mut c_void,
344 sid: u64,
345 status: NrStatus,
346 payload: NrOwnedBytes,
347 ) -> NrStatus,
348 pub acquire_result_buffer:
352 unsafe extern "C" fn(host_ctx: *mut c_void, sid: u64, capacity: u64) -> NrBufferLease,
353 pub commit_result_buffer: unsafe extern "C" fn(
359 host_ctx: *mut c_void,
360 sid: u64,
361 status: NrStatus,
362 token: u64,
363 initialized_len: u64,
364 ) -> NrStatus,
365}
366
367pub const NR_ENTRY_UNKNOWN: u32 = u32::MAX;
369
370#[repr(C)]
372#[derive(Debug, Copy, Clone)]
373pub struct NrPluginVTable {
374 pub init: Option<
375 unsafe extern "C" fn(host_ctx: *mut c_void, host_vtable: *const NrHostVTable) -> NrStatus,
376 >,
377
378 pub handle: Option<unsafe extern "C" fn(entry: NrStr, sid: u64, payload: NrBytes) -> NrStatus>,
379
380 pub shutdown: Option<unsafe extern "C" fn()>,
381
382 pub stream_data: Option<unsafe extern "C" fn(sid: u64, data: NrBytes) -> NrStatus>,
383
384 pub stream_close: Option<unsafe extern "C" fn(sid: u64) -> NrStatus>,
385
386 pub resolve_entry: Option<unsafe extern "C" fn(entry: NrStr) -> u32>,
391
392 pub handle_by_id: Option<unsafe extern "C" fn(id: u32, sid: u64, payload: NrBytes) -> NrStatus>,
395}
396
397#[repr(C)]
399#[derive(Debug, Copy, Clone)]
400pub struct NrPluginInfo {
401 pub abi_version: u32,
402 pub struct_size: u32,
403
404 pub name: NrStr,
405 pub version: NrStr,
406
407 pub plugin_ctx: *mut c_void,
408 pub vtable: *const NrPluginVTable,
409}
410
411#[repr(C)]
414#[derive(Debug, Copy, Clone)]
415pub struct NrHostExt {
416 pub set_state: unsafe extern "C" fn(
419 host_ctx: *mut c_void,
420 sid: u64,
421 key: NrStr,
422 value: NrBytes,
423 ) -> NrStatus,
424
425 pub get_state: unsafe extern "C" fn(host_ctx: *mut c_void, sid: u64, key: NrStr) -> NrVec<u8>,
428}
429
430unsafe impl Send for NrHostExt {}
432unsafe impl Sync for NrHostExt {}
433
434#[macro_export]
435macro_rules! define_plugin {
436 (
437 init: $init_fn:path,
438 shutdown: $shutdown_fn:path,
439 entries: {
440 $($entry_name:literal => $handler_fn:path),* $(,)?
441 }
442 $(, stream_handlers: {
443 data: $stream_data_fn:path,
444 close: $stream_close_fn:path $(,)?
445 })?
446 ) => {
447 static PLUGIN_VTABLE: $crate::NrPluginVTable = $crate::NrPluginVTable {
449 init: Some(plugin_init_wrapper),
450 handle: Some(plugin_handle_wrapper),
451 shutdown: Some(plugin_shutdown_wrapper),
452 stream_data: Some(plugin_stream_data_wrapper),
453 stream_close: Some(plugin_stream_close_wrapper),
454 resolve_entry: Some(plugin_resolve_entry_wrapper),
455 handle_by_id: Some(plugin_handle_by_id_wrapper),
456 };
457
458 static PLUGIN_INFO: $crate::NrPluginInfo = $crate::NrPluginInfo {
460 abi_version: $crate::ABI_VERSION,
461 struct_size: std::mem::size_of::<$crate::NrPluginInfo>() as u32,
462 name: $crate::NrStr::from_static(env!("CARGO_PKG_NAME")),
463 version: $crate::NrStr::from_static(env!("CARGO_PKG_VERSION")),
464 plugin_ctx: std::ptr::null_mut(),
465 vtable: &PLUGIN_VTABLE,
466 };
467
468 #[unsafe(no_mangle)]
470 pub extern "C" fn nylon_ring_get_plugin() -> *const $crate::NrPluginInfo {
471 &PLUGIN_INFO
472 }
473
474 unsafe extern "C" fn plugin_init_wrapper(
476 host_ctx: *mut std::ffi::c_void,
477 host_vtable: *const $crate::NrHostVTable,
478 ) -> $crate::NrStatus {
479 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
480 $init_fn(host_ctx, host_vtable)
481 }))
482 .unwrap_or($crate::NrStatus::Panic)
483 }
484
485 unsafe extern "C" fn plugin_shutdown_wrapper() {
486 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
487 $shutdown_fn();
488 }));
489 }
490
491 unsafe extern "C" fn plugin_handle_wrapper(
492 entry: $crate::NrStr,
493 sid: u64,
494 payload: $crate::NrBytes,
495 ) -> $crate::NrStatus {
496 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
497 let entry_bytes = match unsafe { entry.as_bytes() } {
501 Ok(entry) => entry,
502 Err(_) => return $crate::NrStatus::Invalid,
503 };
504 $(
505 if entry_bytes == $entry_name.as_bytes() {
506 return unsafe { $handler_fn(sid, payload) };
507 }
508 )*
509 $crate::NrStatus::Invalid
510 }))
511 .unwrap_or($crate::NrStatus::Panic)
512 }
513
514 static PLUGIN_ENTRY_NAMES: &[&[u8]] = &[$($entry_name.as_bytes()),*];
517 static PLUGIN_ENTRY_HANDLERS: &[unsafe fn(u64, $crate::NrBytes) -> $crate::NrStatus] =
518 &[$($handler_fn as unsafe fn(u64, $crate::NrBytes) -> $crate::NrStatus),*];
519
520 unsafe extern "C" fn plugin_resolve_entry_wrapper(entry: $crate::NrStr) -> u32 {
521 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
522 let entry_bytes = match unsafe { entry.as_bytes() } {
523 Ok(entry) => entry,
524 Err(_) => return $crate::NR_ENTRY_UNKNOWN,
525 };
526 for (id, name) in PLUGIN_ENTRY_NAMES.iter().enumerate() {
527 if entry_bytes == *name {
528 return id as u32;
529 }
530 }
531 $crate::NR_ENTRY_UNKNOWN
532 }))
533 .unwrap_or($crate::NR_ENTRY_UNKNOWN)
534 }
535
536 unsafe extern "C" fn plugin_handle_by_id_wrapper(
537 id: u32,
538 sid: u64,
539 payload: $crate::NrBytes,
540 ) -> $crate::NrStatus {
541 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
542 match PLUGIN_ENTRY_HANDLERS.get(id as usize) {
543 Some(handler) => unsafe { handler(sid, payload) },
544 None => $crate::NrStatus::Invalid,
545 }
546 }))
547 .unwrap_or($crate::NrStatus::Panic)
548 }
549
550 unsafe extern "C" fn plugin_stream_data_wrapper(
551 sid: u64,
552 data: $crate::NrBytes,
553 ) -> $crate::NrStatus {
554 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
555 let _ = (sid, data);
556 $(
557 return unsafe { $stream_data_fn(sid, data) };
558 )?
559 #[allow(unreachable_code)]
560 $crate::NrStatus::Unsupported
561 }))
562 .unwrap_or($crate::NrStatus::Panic)
563 }
564
565 unsafe extern "C" fn plugin_stream_close_wrapper(
566 sid: u64,
567 ) -> $crate::NrStatus {
568 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
569 let _ = sid;
570 $(
571 return unsafe { $stream_close_fn(sid) };
572 )?
573 #[allow(unreachable_code)]
574 $crate::NrStatus::Unsupported
575 }))
576 .unwrap_or($crate::NrStatus::Panic)
577 }
578 };
579}
580
581impl NrStr {
582 pub fn new(s: &str) -> Self {
587 let len = u32::try_from(s.len()).expect("NrStr cannot represent strings larger than 4 GiB");
588 Self {
589 ptr: s.as_ptr(),
590 len,
591 _reserved: 0,
592 }
593 }
594
595 pub const fn from_static(s: &'static str) -> Self {
597 assert!(
598 s.len() <= u32::MAX as usize,
599 "static string is too large for NrStr"
600 );
601 Self {
602 ptr: s.as_ptr(),
603 len: s.len() as u32,
604 _reserved: 0,
605 }
606 }
607
608 pub unsafe fn as_str<'a>(&self) -> Result<&'a str, NrViewError> {
616 let bytes = unsafe { view_bytes(self.ptr, u64::from(self.len))? };
617 std::str::from_utf8(bytes).map_err(NrViewError::InvalidUtf8)
618 }
619
620 pub unsafe fn as_bytes<'a>(&self) -> Result<&'a [u8], NrViewError> {
628 unsafe { view_bytes(self.ptr, u64::from(self.len)) }
629 }
630
631 pub const fn is_empty(&self) -> bool {
633 self.len == 0
634 }
635
636 pub const fn len(&self) -> usize {
638 self.len as usize
639 }
640
641 pub const fn as_ptr(&self) -> *const u8 {
643 self.ptr
644 }
645
646 pub fn clear(&mut self) {
648 self.ptr = std::ptr::null();
649 self.len = 0;
650 self._reserved = 0;
651 }
652}
653
654unsafe fn view_bytes<'a>(ptr: *const u8, len: u64) -> Result<&'a [u8], NrViewError> {
655 if len == 0 {
656 return Ok(&[]);
657 }
658 if ptr.is_null() {
659 return Err(NrViewError::NullPointer);
660 }
661 let len = usize::try_from(len).map_err(|_| NrViewError::LengthOverflow)?;
662 Ok(unsafe { std::slice::from_raw_parts(ptr, len) })
663}
664
665impl NrBytes {
666 pub fn from_slice(s: &[u8]) -> Self {
668 Self {
669 ptr: s.as_ptr(),
670 len: u64::try_from(s.len()).expect("NrBytes length does not fit in u64"),
671 }
672 }
673
674 pub unsafe fn as_slice<'a>(&self) -> Result<&'a [u8], NrViewError> {
682 unsafe { view_bytes(self.ptr, self.len) }
683 }
684
685 pub const fn is_empty(&self) -> bool {
687 self.len == 0
688 }
689
690 pub const fn len(&self) -> u64 {
692 self.len
693 }
694}
695
696impl Clone for NrKVAny {
697 fn clone(&self) -> Self {
698 Self::new(self.key(), self.value.clone())
699 }
700}
701
702impl Clone for NrMap {
703 fn clone(&self) -> Self {
704 Self {
705 entries: self.entries.clone(),
706 index: self.index.clone(),
707 used: self.used,
708 tomb: self.tomb,
709 }
710 }
711}
712
713impl Clone for NrAny {
714 fn clone(&self) -> Self {
729 if self.data.is_null() {
730 return Self::default();
731 }
732 let clone_fn = self
733 .clone_fn
734 .expect("non-null NrAny values always have a clone function");
735 let data = unsafe { clone_fn(self.data.cast_const()) };
736 assert!(!data.is_null(), "NrAny clone callback failed");
737 Self {
738 data,
739 size: self.size,
740 type_tag: self.type_tag,
741 clone_fn: self.clone_fn,
742 drop_fn: self.drop_fn,
743 }
744 }
745}
746
747impl<T: Clone> Clone for NrVec<T> {
748 fn clone(&self) -> Self {
749 if self.len == 0 {
750 return Self::default();
751 }
752 let v = self.as_slice().to_vec();
753 Self::from_vec(v)
754 }
755}
756
757impl NrKV {
758 pub fn new(key: &str, value: &str) -> Self {
759 Self {
760 key: NrStr::new(key),
761 value: NrStr::new(value),
762 }
763 }
764
765 pub fn from_nr_str(key: NrStr, value: NrStr) -> Self {
766 Self { key, value }
767 }
768}
769
770impl NrKVAny {
771 pub fn new(key: &str, value: NrAny) -> Self {
772 let key_storage = NrVec::from_vec(key.as_bytes().to_vec());
773 let key = NrStr::new(
774 std::str::from_utf8(key_storage.as_slice())
775 .expect("key bytes originate from a valid UTF-8 string"),
776 );
777 Self {
778 key,
779 key_storage,
780 value,
781 }
782 }
783
784 pub unsafe fn from_nr_str(key: NrStr, value: NrAny) -> Result<Self, NrViewError> {
790 let key = unsafe { key.as_str()? };
791 Ok(Self::new(key, value))
792 }
793
794 pub fn key(&self) -> &str {
796 unsafe { std::str::from_utf8_unchecked(self.key_storage.as_slice()) }
798 }
799}
800
801#[inline]
803fn hash_str(s: &str) -> u64 {
804 const FNV_OFFSET: u64 = 0xcbf29ce484222325;
805 const FNV_PRIME: u64 = 0x100000001b3;
806 let mut h = FNV_OFFSET;
807 for &b in s.as_bytes() {
808 h ^= b as u64;
809 h = h.wrapping_mul(FNV_PRIME);
810 }
811 h
812}
813
814impl NrMap {
815 pub fn new() -> Self {
816 Self::default()
817 }
818
819 #[inline]
820 fn index_len(&self) -> usize {
821 self.index.len
822 }
823
824 fn ensure_index(&mut self) {
825 if self.index.is_empty() && self.entries.len >= 8 {
827 self.rehash(16);
828 }
829 }
830
831 fn rehash(&mut self, mut new_cap: usize) {
832 new_cap = new_cap.next_power_of_two().max(16);
834
835 let mut slots = Vec::with_capacity(new_cap);
837 slots.resize_with(new_cap, NrIndexSlot::default);
838
839 self.index = NrVec::from_vec(slots);
840 self.used = 0;
841 self.tomb = 0;
842
843 for i in 0..self.entries.len {
845 let kv = unsafe { &*self.entries.ptr.add(i) };
846 let k = kv.key();
847 let entry_idx =
848 u32::try_from(i).expect("NrMap cannot contain more than u32::MAX entries");
849 self.index_insert(hash_str(k), entry_idx);
850 }
851 }
852
853 #[inline]
854 fn should_grow(&self) -> bool {
855 if self.index.is_empty() {
857 return false;
858 }
859 let occupied = u64::from(self.used) + u64::from(self.tomb);
860 occupied * 10 >= self.index_len() as u64 * 7
861 }
862
863 fn maybe_grow(&mut self) {
864 if self.should_grow() {
865 let cap = self.index_len();
866 self.rehash(cap * 2);
867 }
868 }
869
870 fn index_insert(&mut self, hash: u64, entry_idx: u32) {
871 let cap = self.index_len();
872 if cap == 0 {
873 return;
874 }
875 let mask = cap - 1;
876 let mut pos = (hash as usize) & mask;
877 let mut first_tomb: Option<usize> = None;
878
879 for _ in 0..cap {
880 let slot = unsafe { &mut *self.index.ptr.add(pos) };
881 match slot.state {
882 0 => {
883 let target = first_tomb.unwrap_or(pos);
884 let s2 = unsafe { &mut *self.index.ptr.add(target) };
885 s2.hash = hash;
886 s2.entry_idx = entry_idx;
887 s2.state = 1;
888 if first_tomb.is_some() {
889 self.tomb -= 1;
890 }
891 self.used += 1;
892 return;
893 }
894 2 if first_tomb.is_none() => first_tomb = Some(pos),
895 _ => {}
896 }
897 pos = (pos + 1) & mask;
898 }
899
900 let cap2 = cap * 2;
902 self.rehash(cap2);
903 self.index_insert(hash, entry_idx);
904 }
905
906 pub fn insert(&mut self, key: &str, value: NrAny) {
907 if let Some(v) = self.get_mut(key) {
909 *v = value;
910 return;
911 }
912
913 assert!(
914 self.entries.len < u32::MAX as usize,
915 "NrMap cannot contain more than u32::MAX entries"
916 );
917 let kv = NrKVAny::new(key, value);
918 self.entries.push(kv);
919
920 self.ensure_index();
921 if !self.index.is_empty() {
922 self.maybe_grow();
923 let idx = u32::try_from(self.entries.len - 1)
924 .expect("NrMap cannot contain more than u32::MAX entries");
925 self.index_insert(hash_str(key), idx);
926 }
927 }
928
929 pub unsafe fn insert_nr(&mut self, key: NrStr, value: NrAny) -> Result<(), NrViewError> {
935 let key_str = unsafe { key.as_str()? };
936 if let Some(v) = self.get_mut(key_str) {
938 *v = value;
939 return Ok(());
940 }
941
942 assert!(
943 self.entries.len < u32::MAX as usize,
944 "NrMap cannot contain more than u32::MAX entries"
945 );
946 let kv = NrKVAny::new(key_str, value);
947 self.entries.push(kv);
948
949 self.ensure_index();
950 if !self.index.is_empty() {
951 self.maybe_grow();
952 let idx = u32::try_from(self.entries.len - 1)
953 .expect("NrMap cannot contain more than u32::MAX entries");
954 self.index_insert(hash_str(key_str), idx);
955 }
956 Ok(())
957 }
958
959 pub fn get(&self, key: &str) -> Option<&NrAny> {
960 if self.index.is_empty() {
961 for kv in self.entries.iter() {
963 if kv.key() == key {
964 return Some(&kv.value);
965 }
966 }
967 return None;
968 }
969
970 let h = hash_str(key);
971 let cap = self.index.len;
972 let mask = cap - 1;
973 let mut pos = (h as usize) & mask;
974
975 for _ in 0..cap {
976 let slot = unsafe { &*self.index.ptr.add(pos) };
977 match slot.state {
978 0 => return None, 1 if slot.hash == h => {
980 let kv = unsafe { &*self.entries.ptr.add(slot.entry_idx as usize) };
981 if kv.key() == key {
982 return Some(&kv.value);
983 }
984 }
985 _ => {}
986 }
987 pos = (pos + 1) & mask;
988 }
989 None
990 }
991
992 pub fn get_mut(&mut self, key: &str) -> Option<&mut NrAny> {
993 if self.index.is_empty() {
994 for kv in self.entries.iter_mut() {
995 if kv.key() == key {
996 return Some(&mut kv.value);
997 }
998 }
999 return None;
1000 }
1001
1002 let h = hash_str(key);
1003 let cap = self.index.len;
1004 let mask = cap - 1;
1005 let mut pos = (h as usize) & mask;
1006
1007 for _ in 0..cap {
1008 let slot = unsafe { &*self.index.ptr.add(pos) };
1009 match slot.state {
1010 0 => return None,
1011 1 if slot.hash == h => {
1012 let kv = unsafe { &mut *self.entries.ptr.add(slot.entry_idx as usize) };
1013 if kv.key() == key {
1014 return Some(&mut kv.value);
1015 }
1016 }
1017 _ => {}
1018 }
1019 pos = (pos + 1) & mask;
1020 }
1021 None
1022 }
1023
1024 pub fn remove(&mut self, key: &str) -> Option<NrKVAny> {
1025 let (idx, removed_slot) = if self.index.is_empty() {
1028 (self.entries.iter().position(|kv| kv.key() == key)?, None)
1030 } else {
1031 let h = hash_str(key);
1033 let cap = self.index.len;
1034 let mask = cap - 1;
1035 let mut pos = (h as usize) & mask;
1036 let mut found: Option<(usize, usize)> = None;
1037
1038 for _ in 0..cap {
1039 let slot = unsafe { &*self.index.ptr.add(pos) };
1040 match slot.state {
1041 0 => break, 1 if slot.hash == h => {
1043 let entry_idx = slot.entry_idx as usize;
1044 let kv = unsafe { &*self.entries.ptr.add(entry_idx) };
1045 if kv.key() == key {
1046 found = Some((entry_idx, pos));
1047 break;
1048 }
1049 }
1050 _ => {}
1051 }
1052 pos = (pos + 1) & mask;
1053 }
1054
1055 let (entry_idx, slot) = found?;
1056 (entry_idx, Some(slot))
1057 };
1058
1059 let last = self.entries.len - 1;
1060
1061 let removed = unsafe { std::ptr::read(self.entries.ptr.add(idx)) };
1063
1064 if idx != last {
1065 unsafe {
1067 let last_val = std::ptr::read(self.entries.ptr.add(last));
1068 std::ptr::write(self.entries.ptr.add(idx), last_val);
1069 }
1070
1071 if !self.index.is_empty() {
1073 let h_last = unsafe {
1074 let kv = &*self.entries.ptr.add(idx);
1075 hash_str(kv.key())
1076 };
1077 let cap = self.index.len;
1078 let mask = cap - 1;
1079 let mut pos = (h_last as usize) & mask;
1080
1081 for _ in 0..cap {
1082 let slot = unsafe { &mut *self.index.ptr.add(pos) };
1083 if slot.state == 1 && slot.entry_idx == last as u32 {
1084 slot.entry_idx = idx as u32;
1085 break;
1086 }
1087 pos = (pos + 1) & mask;
1088 }
1089 }
1090 }
1091
1092 self.entries.len -= 1;
1093
1094 if let Some(pos) = removed_slot {
1096 let slot = unsafe { &mut *self.index.ptr.add(pos) };
1097 debug_assert_eq!(slot.state, 1);
1098 slot.state = 2;
1099 self.used -= 1;
1100 self.tomb += 1;
1101
1102 if self.should_grow() {
1104 self.rehash(self.index_len().max(16));
1105 }
1106 }
1107
1108 Some(removed)
1109 }
1110
1111 pub fn len(&self) -> usize {
1112 self.entries.len
1113 }
1114
1115 pub fn is_empty(&self) -> bool {
1116 self.entries.len == 0
1117 }
1118
1119 pub fn clear(&mut self) {
1120 self.entries.clear();
1121 self.index = NrVec::default();
1122 self.used = 0;
1123 self.tomb = 0;
1124 }
1125}
1126
1127impl NrAny {
1128 pub fn new<T: Clone + Send + Sync + 'static>(value: T, type_tag: u32) -> Self {
1130 let size = std::mem::size_of::<T>() as u64;
1131 let data = Box::into_raw(Box::new(value)) as *mut c_void;
1132 Self {
1133 data,
1134 size,
1135 type_tag,
1136 clone_fn: Some(clone_any::<T>),
1137 drop_fn: Some(drop_any::<T>),
1138 }
1139 }
1140
1141 pub fn from_bytes(bytes: &[u8], type_tag: u32) -> Self {
1143 Self::new(bytes.to_vec(), type_tag)
1144 }
1145
1146 pub fn as_ptr<T>(&self) -> Result<*const T, NrStatus> {
1151 if self.data.is_null() {
1152 return Err(NrStatus::Invalid);
1153 }
1154 let expected_size = std::mem::size_of::<T>() as u64;
1155 if self.size != expected_size {
1156 return Err(NrStatus::Err);
1157 }
1158 Ok(self.data as *const T)
1159 }
1160
1161 pub fn as_mut_ptr<T>(&mut self) -> Result<*mut T, NrStatus> {
1162 if self.data.is_null() {
1163 return Err(NrStatus::Invalid);
1164 }
1165 let expected_size = std::mem::size_of::<T>() as u64;
1166 if self.size != expected_size {
1167 return Err(NrStatus::Err);
1168 }
1169 Ok(self.data as *mut T)
1170 }
1171
1172 pub fn is_null(&self) -> bool {
1173 self.data.is_null()
1174 }
1175
1176 pub fn type_tag(&self) -> u32 {
1177 self.type_tag
1178 }
1179
1180 pub fn size(&self) -> u64 {
1181 self.size
1182 }
1183}
1184
1185unsafe extern "C" fn clone_any<T: Clone>(ptr: *const c_void) -> *mut c_void {
1186 if !ptr.is_null() {
1187 return std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1188 let value = unsafe { &*ptr.cast::<T>() };
1189 Box::into_raw(Box::new(value.clone())).cast::<c_void>()
1190 }))
1191 .unwrap_or(std::ptr::null_mut());
1192 }
1193 std::ptr::null_mut()
1194}
1195
1196unsafe extern "C" fn drop_any<T>(ptr: *mut c_void) {
1197 if !ptr.is_null() {
1198 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
1199 drop(Box::from_raw(ptr.cast::<T>()));
1200 }));
1201 }
1202}
1203
1204impl Drop for NrAny {
1205 fn drop(&mut self) {
1206 if let Some(drop_fn) = self.drop_fn
1207 && !self.data.is_null()
1208 {
1209 unsafe {
1210 drop_fn(self.data);
1211 }
1212 }
1213 }
1214}
1215
1216impl NrPluginInfo {
1217 pub fn compatible(&self, expected_abi_version: u32) -> bool {
1218 self.abi_version == expected_abi_version
1219 }
1220}
1221
1222impl NrVec<u8> {
1223 pub unsafe fn from_nr_bytes(bytes: NrBytes) -> Result<Self, NrViewError> {
1229 let v = unsafe { bytes.as_slice()? }.to_vec();
1230 Ok(Self::from_vec(v))
1231 }
1232
1233 pub fn from_string(s: String) -> Self {
1234 Self::from_vec(s.into_bytes())
1235 }
1236}
1237
1238impl<T> NrVec<T> {
1239 pub fn from_vec(v: Vec<T>) -> Self {
1241 let mut v = std::mem::ManuallyDrop::new(v);
1242 let ptr = v.as_mut_ptr();
1243 let len = v.len();
1244 let cap = v.capacity();
1245 Self {
1246 ptr,
1247 len,
1248 cap,
1249 owned: 1,
1250 _reserved: [0; 7],
1251 drop_fn: Some(drop_vec::<T>),
1252 }
1253 }
1254
1255 pub fn into_vec(self) -> Vec<T>
1260 where
1261 T: Clone,
1262 {
1263 self.as_slice().to_vec()
1264 }
1265
1266 fn push(&mut self, value: T) {
1267 if self.owned == 0 && self.ptr.is_null() && self.len == 0 && self.cap == 0 {
1268 self.owned = 1;
1269 self.drop_fn = Some(drop_vec::<T>);
1270 }
1271 assert_eq!(self.owned, 1, "cannot mutate a borrowed NrVec");
1272 if self.len == self.cap {
1273 self.reserve(1);
1274 }
1275 unsafe {
1276 std::ptr::write(self.ptr.add(self.len), value);
1277 }
1278 self.len += 1;
1279 }
1280
1281 fn clear(&mut self) {
1282 assert_eq!(self.owned, 1, "cannot mutate a borrowed NrVec");
1283 while self.len > 0 {
1284 self.len -= 1;
1285 unsafe {
1286 std::ptr::drop_in_place(self.ptr.add(self.len));
1287 }
1288 }
1289 }
1290
1291 fn reserve(&mut self, additional: usize) {
1292 assert_eq!(self.owned, 1, "cannot resize a borrowed NrVec");
1293 if std::mem::size_of::<T>() == 0 {
1294 assert!(
1295 self.len.checked_add(additional).is_some(),
1296 "capacity overflow"
1297 );
1298 if self.cap == 0 {
1299 self.ptr = std::ptr::NonNull::<T>::dangling().as_ptr();
1300 self.cap = usize::MAX;
1301 }
1302 return;
1303 }
1304
1305 let available = self.cap - self.len;
1306 if available < additional {
1307 let required = self.len.checked_add(additional).expect("capacity overflow");
1308 let new_cap = if self.cap == 0 {
1309 std::cmp::max(1, required)
1310 } else {
1311 std::cmp::max(self.cap.saturating_mul(2), required)
1312 };
1313
1314 let new_layout = match std::alloc::Layout::array::<T>(new_cap) {
1315 Ok(layout) => layout,
1316 Err(_) => {
1317 std::alloc::handle_alloc_error(
1319 std::alloc::Layout::from_size_align(usize::MAX, 1)
1320 .unwrap_or_else(|_| std::alloc::Layout::new::<u8>()),
1321 )
1322 }
1323 };
1324
1325 let new_ptr = if self.cap == 0 {
1326 unsafe { std::alloc::alloc(new_layout) }
1327 } else {
1328 let old_layout = match std::alloc::Layout::array::<T>(self.cap) {
1329 Ok(layout) => layout,
1330 Err(_) => {
1331 std::alloc::handle_alloc_error(new_layout)
1334 }
1335 };
1336 unsafe { std::alloc::realloc(self.ptr as *mut u8, old_layout, new_layout.size()) }
1337 };
1338
1339 if new_ptr.is_null() {
1340 std::alloc::handle_alloc_error(new_layout);
1341 }
1342
1343 self.ptr = new_ptr as *mut T;
1344 self.cap = new_cap;
1345 }
1346 }
1347
1348 pub fn capacity(&self) -> usize {
1349 self.cap
1350 }
1351
1352 pub fn len(&self) -> usize {
1353 self.len
1354 }
1355
1356 pub fn is_empty(&self) -> bool {
1357 self.len == 0
1358 }
1359
1360 pub fn as_ptr(&self) -> *const T {
1361 self.ptr
1362 }
1363
1364 pub fn as_mut_ptr(&mut self) -> *mut T {
1365 self.ptr
1366 }
1367}
1368
1369impl<T> Drop for NrVec<T> {
1370 fn drop(&mut self) {
1371 if self.owned == 1
1372 && let Some(drop_fn) = self.drop_fn
1373 {
1374 unsafe {
1375 drop_fn(self.ptr, self.len, self.cap);
1376 }
1377 }
1378 }
1379}
1380
1381unsafe extern "C" fn drop_vec<T>(ptr: *mut T, len: usize, cap: usize) {
1382 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1383 if cap != 0 {
1384 unsafe {
1385 drop(Vec::from_raw_parts(ptr, len, cap));
1386 }
1387 }
1388 }));
1389}
1390
1391impl<T> NrVec<T> {
1392 pub fn iter(&self) -> std::slice::Iter<'_, T> {
1393 self.as_slice().iter()
1394 }
1395
1396 fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
1397 self.as_mut_slice().iter_mut()
1398 }
1399
1400 pub fn as_slice(&self) -> &[T] {
1401 if self.len == 0 {
1402 &[]
1403 } else {
1404 unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
1405 }
1406 }
1407
1408 fn as_mut_slice(&mut self) -> &mut [T] {
1409 if self.len == 0 {
1410 &mut []
1411 } else {
1412 unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) }
1413 }
1414 }
1415}
1416
1417impl<'a, T> IntoIterator for &'a NrVec<T> {
1418 type Item = &'a T;
1419 type IntoIter = std::slice::Iter<'a, T>;
1420
1421 fn into_iter(self) -> Self::IntoIter {
1422 self.iter()
1423 }
1424}
1425
1426impl<T: Clone> IntoIterator for NrVec<T> {
1427 type Item = T;
1428 type IntoIter = std::vec::IntoIter<T>;
1429
1430 fn into_iter(self) -> Self::IntoIter {
1431 self.into_vec().into_iter()
1432 }
1433}
1434
1435unsafe impl Send for NrStr {}
1438unsafe impl Sync for NrStr {}
1439
1440unsafe impl Send for NrBytes {}
1441unsafe impl Sync for NrBytes {}
1442
1443unsafe impl Send for NrKV {}
1444unsafe impl Sync for NrKV {}
1445
1446unsafe impl Send for NrKVAny {}
1447unsafe impl Sync for NrKVAny {}
1448
1449unsafe impl Send for NrMap {}
1450unsafe impl Sync for NrMap {}
1451
1452unsafe impl Send for NrAny {}
1453unsafe impl Sync for NrAny {}
1454
1455unsafe impl Send for NrHostVTable {}
1456unsafe impl Sync for NrHostVTable {}
1457
1458unsafe impl Send for NrPluginVTable {}
1459unsafe impl Sync for NrPluginVTable {}
1460
1461unsafe impl Send for NrPluginInfo {}
1462unsafe impl Sync for NrPluginInfo {}
1463
1464unsafe impl<T: Send> Send for NrVec<T> {}
1465unsafe impl<T: Sync> Sync for NrVec<T> {}
1466
1467unsafe impl<A: Send, B: Send> Send for NrTuple<A, B> {}
1468unsafe impl<A: Sync, B: Sync> Sync for NrTuple<A, B> {}
1469
1470#[cfg(test)]
1471mod tests {
1472 use super::*;
1473 use std::mem::{align_of, offset_of, size_of};
1474
1475 unsafe fn test_plugin_init(_: *mut c_void, _: *const NrHostVTable) -> NrStatus {
1476 NrStatus::Ok
1477 }
1478
1479 unsafe fn panicking_handler(_: u64, _: NrBytes) -> NrStatus {
1480 panic!("test panic must not cross the FFI boundary");
1481 }
1482
1483 fn test_plugin_shutdown() {}
1484
1485 define_plugin! {
1486 init: test_plugin_init,
1487 shutdown: test_plugin_shutdown,
1488 entries: {
1489 "panic" => panicking_handler,
1490 }
1491 }
1492
1493 #[test]
1494 fn test_layout() {
1495 assert_eq!(size_of::<NrStr>(), 16);
1498 assert_eq!(align_of::<NrStr>(), 8);
1499
1500 assert_eq!(size_of::<NrBytes>(), 16);
1503 assert_eq!(align_of::<NrBytes>(), 8);
1504
1505 assert_eq!(size_of::<NrVec<u8>>(), 40);
1507 assert_eq!(align_of::<NrVec<u8>>(), 8);
1508
1509 assert_eq!(size_of::<NrTuple<u64, u64>>(), 16);
1512 assert_eq!(align_of::<NrTuple<u64, u64>>(), 8);
1513
1514 assert_eq!(size_of::<NrKV>(), 32);
1517 assert_eq!(align_of::<NrKV>(), 8);
1518 }
1519
1520 #[test]
1523 fn test_layout_matches_c_header() {
1524 assert_eq!(size_of::<NrStatus>(), 4);
1525
1526 assert_eq!(size_of::<NrStr>(), 16);
1527 assert_eq!(offset_of!(NrStr, ptr), 0);
1528 assert_eq!(offset_of!(NrStr, len), 8);
1529 assert_eq!(offset_of!(NrStr, _reserved), 12);
1530
1531 assert_eq!(size_of::<NrBytes>(), 16);
1532 assert_eq!(offset_of!(NrBytes, ptr), 0);
1533 assert_eq!(offset_of!(NrBytes, len), 8);
1534
1535 assert_eq!(size_of::<NrVec<u8>>(), 40);
1536 assert_eq!(offset_of!(NrVec<u8>, ptr), 0);
1537 assert_eq!(offset_of!(NrVec<u8>, len), 8);
1538 assert_eq!(offset_of!(NrVec<u8>, cap), 16);
1539 assert_eq!(offset_of!(NrVec<u8>, owned), 24);
1540 assert_eq!(offset_of!(NrVec<u8>, _reserved), 25);
1541 assert_eq!(offset_of!(NrVec<u8>, drop_fn), 32);
1542
1543 assert_eq!(size_of::<NrOwnedBytes>(), 32);
1544 assert_eq!(offset_of!(NrOwnedBytes, ptr), 0);
1545 assert_eq!(offset_of!(NrOwnedBytes, len), 8);
1546 assert_eq!(offset_of!(NrOwnedBytes, owner_ctx), 16);
1547 assert_eq!(offset_of!(NrOwnedBytes, release), 24);
1548
1549 assert_eq!(size_of::<NrBufferLease>(), 24);
1550 assert_eq!(offset_of!(NrBufferLease, ptr), 0);
1551 assert_eq!(offset_of!(NrBufferLease, cap), 8);
1552 assert_eq!(offset_of!(NrBufferLease, token), 16);
1553
1554 assert_eq!(size_of::<NrHostVTable>(), 32);
1555 assert_eq!(offset_of!(NrHostVTable, send_result), 0);
1556 assert_eq!(offset_of!(NrHostVTable, send_result_owned), 8);
1557 assert_eq!(offset_of!(NrHostVTable, acquire_result_buffer), 16);
1558 assert_eq!(offset_of!(NrHostVTable, commit_result_buffer), 24);
1559
1560 assert_eq!(size_of::<NrPluginVTable>(), 56);
1561 assert_eq!(offset_of!(NrPluginVTable, init), 0);
1562 assert_eq!(offset_of!(NrPluginVTable, handle), 8);
1563 assert_eq!(offset_of!(NrPluginVTable, shutdown), 16);
1564 assert_eq!(offset_of!(NrPluginVTable, stream_data), 24);
1565 assert_eq!(offset_of!(NrPluginVTable, stream_close), 32);
1566 assert_eq!(offset_of!(NrPluginVTable, resolve_entry), 40);
1567 assert_eq!(offset_of!(NrPluginVTable, handle_by_id), 48);
1568
1569 assert_eq!(size_of::<NrPluginInfo>(), 56);
1570 assert_eq!(offset_of!(NrPluginInfo, abi_version), 0);
1571 assert_eq!(offset_of!(NrPluginInfo, struct_size), 4);
1572 assert_eq!(offset_of!(NrPluginInfo, name), 8);
1573 assert_eq!(offset_of!(NrPluginInfo, version), 24);
1574 assert_eq!(offset_of!(NrPluginInfo, plugin_ctx), 40);
1575 assert_eq!(offset_of!(NrPluginInfo, vtable), 48);
1576 }
1577
1578 #[test]
1579 fn test_nr_vec() {
1580 let mut v = NrVec::<u32>::default();
1581 assert_eq!(v.len, 0);
1582 assert_eq!(v.cap, 0);
1583
1584 v.push(1);
1585 assert_eq!(v.len, 1);
1586 assert!(v.cap >= 1);
1587 unsafe {
1588 assert_eq!(*v.ptr, 1);
1589 }
1590
1591 v.push(2);
1592 assert_eq!(v.len, 2);
1593 unsafe {
1594 assert_eq!(*v.ptr.add(1), 2);
1595 }
1596
1597 v.reserve(10);
1598 assert!(v.cap >= 12); v.clear();
1601 assert_eq!(v.len, 0);
1602 assert!(v.cap >= 12);
1603 }
1604 #[test]
1605 fn test_nr_vec_iter() {
1606 let mut v = NrVec::<u32>::default();
1607 v.push(1);
1608 v.push(2);
1609 v.push(3);
1610
1611 let mut iter = v.iter();
1612 assert_eq!(iter.next(), Some(&1));
1613 assert_eq!(iter.next(), Some(&2));
1614 assert_eq!(iter.next(), Some(&3));
1615 assert_eq!(iter.next(), None);
1616 }
1617
1618 #[test]
1619 fn test_nr_vec_iter_mut() {
1620 let mut v = NrVec::<u32>::default();
1621 v.push(1);
1622 v.push(2);
1623 v.push(3);
1624
1625 for x in v.iter_mut() {
1626 *x *= 2;
1627 }
1628
1629 let mut iter = v.iter();
1630 assert_eq!(iter.next(), Some(&2));
1631 assert_eq!(iter.next(), Some(&4));
1632 assert_eq!(iter.next(), Some(&6));
1633 assert_eq!(iter.next(), None);
1634 }
1635
1636 #[test]
1637 fn test_nr_vec_into_iter() {
1638 let mut v = NrVec::<u32>::default();
1639 v.push(1);
1640 v.push(2);
1641 v.push(3);
1642
1643 let mut iter = v.into_iter();
1644 assert_eq!(iter.next(), Some(1));
1645 assert_eq!(iter.next(), Some(2));
1646 assert_eq!(iter.next(), Some(3));
1647 assert_eq!(iter.next(), None);
1648 }
1649
1650 #[test]
1651 fn test_nr_vec_empty_and_zero_sized_values() {
1652 assert!(NrVec::<u8>::default().into_vec().is_empty());
1653
1654 let mut values = NrVec::default();
1655 values.push(());
1656 values.push(());
1657 assert_eq!(values.len(), 2);
1658 assert_eq!(values.into_iter().count(), 2);
1659 }
1660
1661 #[test]
1662 fn test_nr_vec_collect() {
1663 let mut v = NrVec::<u32>::default();
1664 v.push(10);
1665 v.push(20);
1666
1667 let collected: Vec<u32> = v.iter().cloned().collect();
1668 assert_eq!(collected, vec![10, 20]);
1669 }
1670
1671 #[test]
1672 fn test_nr_map() {
1673 let mut map = NrMap::new();
1674 assert!(map.is_empty());
1675 assert_eq!(map.len(), 0);
1676
1677 let str_value1 = NrAny::new(String::from("value1"), 1);
1679 let str_value2 = NrAny::new(String::from("value2"), 1);
1680 map.insert("key1", str_value1);
1681 map.insert("key2", str_value2);
1682 assert_eq!(map.len(), 2);
1683
1684 let value1 = map.get("key1").unwrap();
1686 let str_ptr1 = value1.as_ptr::<String>().unwrap();
1687 unsafe {
1688 assert_eq!(*str_ptr1, "value1");
1689 }
1690
1691 let value2 = map.get("key2").unwrap();
1692 let str_ptr2 = value2.as_ptr::<String>().unwrap();
1693 unsafe {
1694 assert_eq!(*str_ptr2, "value2");
1695 }
1696
1697 assert!(map.get("key3").is_none());
1698
1699 let value_mut = map.get_mut("key1");
1701 assert!(value_mut.is_some());
1702
1703 let int_value = NrAny::new(42i32, 2);
1705 map.insert("key3", int_value);
1706 assert_eq!(map.len(), 3);
1707
1708 let int_val = map.get("key3").unwrap();
1709 let int_ptr = int_val.as_ptr::<i32>().unwrap();
1710 unsafe {
1711 assert_eq!(*int_ptr, 42);
1712 }
1713
1714 let removed = map.remove("key2");
1715 assert!(removed.is_some());
1716 assert_eq!(map.len(), 2);
1717 assert!(map.get("key2").is_none());
1718
1719 map.clear();
1720 assert!(map.is_empty());
1721
1722 {
1724 let temporary_key = String::from("owned-key");
1725 map.insert(&temporary_key, NrAny::new(7_u32, 2));
1726 }
1727 let stored = map.get("owned-key").unwrap();
1728 assert_eq!(unsafe { *stored.as_ptr::<u32>().unwrap() }, 7);
1729
1730 for index in 0..16 {
1732 map.insert(&format!("key-{index}"), NrAny::new(index, 2));
1733 }
1734 for index in 0..16 {
1735 assert!(map.remove(&format!("key-{index}")).is_some());
1736 }
1737 map.clear();
1738 assert!(map.get("missing").is_none());
1739 }
1740
1741 #[test]
1742 fn test_nr_any() {
1743 let any_int = NrAny::new(42i32, 1);
1744 assert!(!any_int.is_null());
1745 assert_eq!(any_int.type_tag(), 1);
1746 assert_eq!(any_int.size(), std::mem::size_of::<i32>() as u64);
1747
1748 let ptr = any_int.as_ptr::<i32>().unwrap();
1749 unsafe {
1750 assert_eq!(*ptr, 42);
1751 }
1752
1753 let any_string = NrAny::new(String::from("hello"), 2);
1754 assert_eq!(any_string.type_tag(), 2);
1755 let str_ptr = any_string.as_ptr::<String>().unwrap();
1756 unsafe {
1757 assert_eq!(*str_ptr, "hello");
1758 }
1759 let cloned_string = any_string.clone();
1760 let cloned_ptr = cloned_string.as_ptr::<String>().unwrap();
1761 unsafe {
1762 assert_eq!(*cloned_ptr, "hello");
1763 assert_ne!(str_ptr, cloned_ptr);
1764 }
1765
1766 let any_bytes = NrAny::from_bytes(b"test", 3);
1767 assert_eq!(any_bytes.type_tag(), 3);
1768 assert_eq!(any_bytes.size(), std::mem::size_of::<Vec<u8>>() as u64);
1769 let bytes_ptr = any_bytes.as_ptr::<Vec<u8>>().unwrap();
1770 unsafe {
1771 assert_eq!(&*bytes_ptr, b"test");
1772 }
1773
1774 let default_any = NrAny::default();
1775 assert!(default_any.is_null());
1776 assert_eq!(default_any.type_tag(), 0);
1777 assert_eq!(default_any.size(), 0);
1778
1779 assert_eq!(default_any.as_ptr::<i32>(), Err(NrStatus::Invalid));
1781 let mut default_any_mut = NrAny::default();
1782 assert_eq!(default_any_mut.as_mut_ptr::<i32>(), Err(NrStatus::Invalid));
1783
1784 let any_int = NrAny::new(42i32, 1);
1786 assert_eq!(any_int.as_ptr::<u64>(), Err(NrStatus::Err)); let mut any_int_mut = NrAny::new(42i32, 1);
1788 assert_eq!(any_int_mut.as_mut_ptr::<u64>(), Err(NrStatus::Err));
1789 }
1790
1791 #[test]
1792 fn test_borrowed_view_validation() {
1793 let empty = NrStr::default();
1794 assert_eq!(unsafe { empty.as_str() }.unwrap(), "");
1795
1796 let invalid_utf8 = [0xff];
1797 let invalid = NrStr {
1798 ptr: invalid_utf8.as_ptr(),
1799 len: 1,
1800 _reserved: 0,
1801 };
1802 assert!(matches!(
1803 unsafe { invalid.as_str() },
1804 Err(NrViewError::InvalidUtf8(_))
1805 ));
1806
1807 let null_bytes = NrBytes {
1808 ptr: std::ptr::null(),
1809 len: 1,
1810 };
1811 assert_eq!(
1812 unsafe { null_bytes.as_slice() },
1813 Err(NrViewError::NullPointer)
1814 );
1815 }
1816
1817 #[test]
1818 fn plugin_macro_contains_panics_and_rejects_invalid_utf8() {
1819 let panic_entry = NrStr::new("panic");
1820 assert_eq!(
1821 unsafe { plugin_handle_wrapper(panic_entry, 1, NrBytes::default()) },
1822 NrStatus::Panic
1823 );
1824
1825 let invalid_utf8 = [0xff];
1826 let invalid_entry = NrStr {
1827 ptr: invalid_utf8.as_ptr(),
1828 len: 1,
1829 _reserved: 0,
1830 };
1831 assert_eq!(
1832 unsafe { plugin_handle_wrapper(invalid_entry, 2, NrBytes::default()) },
1833 NrStatus::Invalid
1834 );
1835 }
1836}