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