1use core::fmt;
4use core::sync::atomic::{Ordering, compiler_fence};
5
6#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub enum NativePlatform {
9 Linux,
11 MacOs,
13 Windows,
15}
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum NativeArchitecture {
20 Arm64,
22 Amd64,
24}
25
26#[derive(Clone, Copy, Debug, Eq, PartialEq)]
28pub enum AuthorityMechanism {
29 DescriptorSeals,
31 MaximumPortRights,
33 ExactHandleRights,
35}
36
37#[derive(Clone, Copy, Debug, Eq, PartialEq)]
39pub struct NativeMemoryCapabilities {
40 platform: NativePlatform,
41 architecture: NativeArchitecture,
42 authority: AuthorityMechanism,
43}
44
45impl NativeMemoryCapabilities {
46 pub const fn platform(self) -> NativePlatform {
48 self.platform
49 }
50
51 pub const fn architecture(self) -> NativeArchitecture {
53 self.architecture
54 }
55
56 pub const fn authority_mechanism(self) -> AuthorityMechanism {
58 self.authority
59 }
60
61 pub const fn supports_replacement_growth(self) -> bool {
63 true
64 }
65
66 pub const fn supports_in_place_growth(self) -> bool {
68 false
69 }
70
71 pub const fn supports_post_share_permission_changes(self) -> bool {
73 false
74 }
75
76 pub const fn releases_on_drop(self) -> bool {
78 true
79 }
80}
81
82pub const fn native_memory_capabilities() -> NativeMemoryCapabilities {
84 NativeMemoryCapabilities {
85 platform: native_platform(),
86 architecture: native_architecture(),
87 authority: authority_mechanism(),
88 }
89}
90
91#[derive(Clone, Copy, Debug, Eq, PartialEq)]
93pub enum WriterOwner {
94 Creator,
96 Peer,
98}
99
100#[derive(Clone, Copy, Debug, Eq, PartialEq)]
102pub enum MemoryAccess {
103 ReadOnly,
105 ReadWrite,
107}
108
109#[derive(Clone, Copy, Debug, Eq, PartialEq)]
111pub struct PermissionPlan {
112 writer: WriterOwner,
113}
114
115impl PermissionPlan {
116 pub const fn new(writer: WriterOwner) -> Self {
118 Self { writer }
119 }
120
121 pub const fn writer(self) -> WriterOwner {
123 self.writer
124 }
125
126 pub const fn creator_access(self) -> MemoryAccess {
128 match self.writer {
129 WriterOwner::Creator => MemoryAccess::ReadWrite,
130 WriterOwner::Peer => MemoryAccess::ReadOnly,
131 }
132 }
133
134 pub const fn peer_access(self) -> MemoryAccess {
136 match self.writer {
137 WriterOwner::Creator => MemoryAccess::ReadOnly,
138 WriterOwner::Peer => MemoryAccess::ReadWrite,
139 }
140 }
141
142 pub const fn executable(self) -> bool {
144 false
145 }
146}
147
148#[derive(Clone, Copy, Debug, Eq, PartialEq)]
150pub enum GrowthPolicy {
151 Fixed,
153 ReplaceBeforeShare {
155 maximum_len: usize,
157 },
158}
159
160#[derive(Clone, Copy, Debug, Eq, PartialEq)]
162pub enum CleanupPolicy {
163 ReleaseOnDrop,
165 ClearThenRelease,
167}
168
169#[derive(Clone, Copy, Debug, Eq, PartialEq)]
171pub enum SealPolicy {
172 RequiredOnShare,
174}
175
176#[derive(Clone, Copy, Debug, Eq, PartialEq)]
178pub struct RegionOptions {
179 logical_len: usize,
180 growth: GrowthPolicy,
181 cleanup: CleanupPolicy,
182 permissions: PermissionPlan,
183 seal: SealPolicy,
184}
185
186impl RegionOptions {
187 pub const fn fixed(logical_len: usize, writer: WriterOwner) -> Self {
189 Self {
190 logical_len,
191 growth: GrowthPolicy::Fixed,
192 cleanup: CleanupPolicy::ClearThenRelease,
193 permissions: PermissionPlan::new(writer),
194 seal: SealPolicy::RequiredOnShare,
195 }
196 }
197
198 pub const fn growable(logical_len: usize, maximum_len: usize, writer: WriterOwner) -> Self {
203 Self {
204 logical_len,
205 growth: GrowthPolicy::ReplaceBeforeShare { maximum_len },
206 cleanup: CleanupPolicy::ClearThenRelease,
207 permissions: PermissionPlan::new(writer),
208 seal: SealPolicy::RequiredOnShare,
209 }
210 }
211
212 pub const fn with_cleanup(mut self, cleanup: CleanupPolicy) -> Self {
214 self.cleanup = cleanup;
215 self
216 }
217
218 pub const fn logical_len(self) -> usize {
220 self.logical_len
221 }
222
223 pub const fn growth(self) -> GrowthPolicy {
225 self.growth
226 }
227
228 pub const fn cleanup(self) -> CleanupPolicy {
230 self.cleanup
231 }
232
233 pub const fn permissions(self) -> PermissionPlan {
235 self.permissions
236 }
237
238 pub const fn seal(self) -> SealPolicy {
240 self.seal
241 }
242
243 pub const fn maximum_len(self) -> usize {
245 match self.growth {
246 GrowthPolicy::Fixed => self.logical_len,
247 GrowthPolicy::ReplaceBeforeShare { maximum_len } => maximum_len,
248 }
249 }
250}
251
252#[derive(Clone, Copy, Debug, Eq, PartialEq)]
254pub enum RegionState {
255 Quiescent,
257}
258
259#[derive(Clone, Copy, Debug, Eq, PartialEq)]
261pub struct RegionStatus {
262 pub state: RegionState,
264 pub logical_len: usize,
266 pub mapped_len: usize,
268 pub maximum_len: usize,
270 pub can_grow: bool,
272 pub permissions: PermissionPlan,
274 pub cleanup: CleanupPolicy,
276 pub seal: SealPolicy,
278}
279
280#[derive(Debug)]
282pub enum MemoryError {
283 ZeroLength,
285 MaximumBelowInitial {
287 initial: usize,
289 maximum: usize,
291 },
292 FixedSize,
294 ShrinkUnsupported {
296 current: usize,
298 requested: usize,
300 },
301 MaximumExceeded {
303 requested: usize,
305 maximum: usize,
307 },
308 Platform(NativeMemoryPlatformError),
310}
311
312impl fmt::Display for MemoryError {
313 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
314 write!(formatter, "native shared-memory operation failed: {self:?}")
315 }
316}
317
318impl std::error::Error for MemoryError {
319 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
320 match self {
321 Self::Platform(error) => Some(error),
322 _ => None,
323 }
324 }
325}
326
327impl From<NativeMemoryPlatformError> for MemoryError {
328 fn from(value: NativeMemoryPlatformError) -> Self {
329 Self::Platform(value)
330 }
331}
332
333pub struct NativeRegion {
341 inner: Option<PlatformQuiescentRegion>,
342 logical_len: usize,
343 options: RegionOptions,
344}
345
346impl NativeRegion {
347 pub fn allocate(options: RegionOptions) -> Result<Self, MemoryError> {
349 validate_options(options)?;
350 let inner = PlatformQuiescentRegion::new(options.logical_len())?;
351 Ok(Self {
352 inner: Some(inner),
353 logical_len: options.logical_len(),
354 options,
355 })
356 }
357
358 pub const fn options(&self) -> RegionOptions {
360 self.options
361 }
362
363 pub fn status(&self) -> RegionStatus {
365 let maximum_len = self.options.maximum_len();
366 RegionStatus {
367 state: RegionState::Quiescent,
368 logical_len: self.logical_len,
369 mapped_len: self.inner().len(),
370 maximum_len,
371 can_grow: matches!(
372 self.options.growth(),
373 GrowthPolicy::ReplaceBeforeShare { .. }
374 ) && self.logical_len < maximum_len,
375 permissions: self.options.permissions(),
376 cleanup: self.options.cleanup(),
377 seal: self.options.seal(),
378 }
379 }
380
381 pub fn initialize<R>(&mut self, operation: impl FnOnce(&mut [u8]) -> R) -> R {
386 let logical_len = self.logical_len;
387 operation(&mut self.inner_mut().as_bytes_mut()[..logical_len])
388 }
389
390 pub fn clear(&mut self) {
395 clear_mapping(self.inner_mut());
396 }
397
398 pub fn grow(&mut self, requested: usize) -> Result<(), MemoryError> {
400 if requested == 0 {
401 return Err(MemoryError::ZeroLength);
402 }
403 if requested < self.logical_len {
404 return Err(MemoryError::ShrinkUnsupported {
405 current: self.logical_len,
406 requested,
407 });
408 }
409 if requested == self.logical_len {
410 return Ok(());
411 }
412 let maximum = match self.options.growth() {
413 GrowthPolicy::Fixed => return Err(MemoryError::FixedSize),
414 GrowthPolicy::ReplaceBeforeShare { maximum_len } => maximum_len,
415 };
416 if requested > maximum {
417 return Err(MemoryError::MaximumExceeded { requested, maximum });
418 }
419
420 let mut replacement = PlatformQuiescentRegion::new(requested)?;
421 replacement.as_bytes_mut()[..self.logical_len]
422 .copy_from_slice(&self.inner().as_bytes()[..self.logical_len]);
423 let mut previous = self
424 .inner
425 .replace(replacement)
426 .expect("managed region always owns its mapping");
427 if self.options.cleanup() == CleanupPolicy::ClearThenRelease {
428 clear_mapping(&mut previous);
429 }
430 self.logical_len = requested;
431 Ok(())
432 }
433
434 pub fn close(mut self) {
436 self.release();
437 }
438
439 pub fn destroy(mut self) {
446 if let Some(inner) = self.inner.as_mut() {
447 clear_mapping(inner);
448 }
449 drop(self.inner.take());
450 }
451
452 pub fn prepare_for_sharing(mut self) -> NativeShareRequest {
458 NativeShareRequest {
459 inner: self.inner.take(),
460 permissions: self.options.permissions(),
461 seal: self.options.seal(),
462 cleanup: self.options.cleanup(),
463 }
464 }
465
466 fn inner(&self) -> &PlatformQuiescentRegion {
467 self.inner
468 .as_ref()
469 .expect("managed region always owns its mapping")
470 }
471
472 fn inner_mut(&mut self) -> &mut PlatformQuiescentRegion {
473 self.inner
474 .as_mut()
475 .expect("managed region always owns its mapping")
476 }
477
478 fn release(&mut self) {
479 if let Some(inner) = self.inner.as_mut()
480 && self.options.cleanup() == CleanupPolicy::ClearThenRelease
481 {
482 clear_mapping(inner);
483 }
484 drop(self.inner.take());
485 }
486}
487
488pub struct NativeShareRequest {
494 inner: Option<PlatformQuiescentRegion>,
495 permissions: PermissionPlan,
496 seal: SealPolicy,
497 cleanup: CleanupPolicy,
498}
499
500impl NativeShareRequest {
501 pub const fn permissions(&self) -> PermissionPlan {
503 self.permissions
504 }
505
506 pub const fn seal_policy(&self) -> SealPolicy {
508 self.seal
509 }
510
511 pub fn mapped_len(&self) -> usize {
513 self.inner
514 .as_ref()
515 .expect("share request always owns its mapping")
516 .len()
517 }
518
519 pub fn into_platform_parts(mut self) -> (PlatformQuiescentRegion, PermissionPlan) {
525 let region = self
526 .inner
527 .take()
528 .expect("share request always owns its mapping");
529 (region, self.permissions)
530 }
531
532 pub fn destroy(mut self) {
534 if let Some(inner) = self.inner.as_mut() {
535 clear_mapping(inner);
536 }
537 drop(self.inner.take());
538 }
539
540 fn release(&mut self) {
541 if let Some(inner) = self.inner.as_mut()
542 && self.cleanup == CleanupPolicy::ClearThenRelease
543 {
544 clear_mapping(inner);
545 }
546 drop(self.inner.take());
547 }
548}
549
550impl Drop for NativeShareRequest {
551 fn drop(&mut self) {
552 self.release();
553 }
554}
555
556fn clear_mapping(region: &mut PlatformQuiescentRegion) {
557 for byte in region.as_bytes_mut() {
558 unsafe { core::ptr::write_volatile(byte, 0) };
561 }
562 compiler_fence(Ordering::SeqCst);
563}
564
565impl Drop for NativeRegion {
566 fn drop(&mut self) {
567 self.release();
568 }
569}
570
571fn validate_options(options: RegionOptions) -> Result<(), MemoryError> {
572 if options.logical_len() == 0 {
573 return Err(MemoryError::ZeroLength);
574 }
575 let maximum = options.maximum_len();
576 if maximum < options.logical_len() {
577 return Err(MemoryError::MaximumBelowInitial {
578 initial: options.logical_len(),
579 maximum,
580 });
581 }
582 Ok(())
583}
584
585#[cfg(target_os = "linux")]
586pub type PlatformQuiescentRegion = native_ipc_platform::linux::QuiescentRegion;
588#[cfg(target_os = "linux")]
589pub type NativeMemoryPlatformError = native_ipc_platform::linux::LinuxError;
591
592#[cfg(target_os = "macos")]
593pub type PlatformQuiescentRegion = native_ipc_platform::macos::QuiescentRegion;
595#[cfg(target_os = "macos")]
596pub type NativeMemoryPlatformError = native_ipc_platform::macos::MachError;
598
599#[cfg(target_os = "windows")]
600pub type PlatformQuiescentRegion = native_ipc_platform::windows::QuiescentRegion;
602#[cfg(target_os = "windows")]
603pub type NativeMemoryPlatformError = native_ipc_platform::windows::WindowsError;
605
606#[cfg(target_os = "linux")]
607const fn native_platform() -> NativePlatform {
608 NativePlatform::Linux
609}
610#[cfg(target_os = "macos")]
611const fn native_platform() -> NativePlatform {
612 NativePlatform::MacOs
613}
614#[cfg(target_os = "windows")]
615const fn native_platform() -> NativePlatform {
616 NativePlatform::Windows
617}
618
619#[cfg(target_arch = "aarch64")]
620const fn native_architecture() -> NativeArchitecture {
621 NativeArchitecture::Arm64
622}
623
624#[cfg(target_arch = "x86_64")]
625const fn native_architecture() -> NativeArchitecture {
626 NativeArchitecture::Amd64
627}
628
629#[cfg(target_os = "linux")]
630const fn authority_mechanism() -> AuthorityMechanism {
631 AuthorityMechanism::DescriptorSeals
632}
633#[cfg(target_os = "macos")]
634const fn authority_mechanism() -> AuthorityMechanism {
635 AuthorityMechanism::MaximumPortRights
636}
637#[cfg(target_os = "windows")]
638const fn authority_mechanism() -> AuthorityMechanism {
639 AuthorityMechanism::ExactHandleRights
640}
641
642#[cfg(test)]
643mod tests {
644 use super::*;
645
646 #[test]
647 fn capabilities_report_the_compilation_architecture() {
648 let capabilities = native_memory_capabilities();
649
650 #[cfg(target_arch = "aarch64")]
651 assert_eq!(capabilities.architecture(), NativeArchitecture::Arm64);
652 #[cfg(target_arch = "x86_64")]
653 assert_eq!(capabilities.architecture(), NativeArchitecture::Amd64);
654 }
655
656 #[test]
657 fn growable_region_preserves_bytes_and_reports_policy() {
658 let options = RegionOptions::growable(32, 128, WriterOwner::Creator);
659 let mut region = NativeRegion::allocate(options).unwrap();
660 region.initialize(|bytes| bytes[..4].copy_from_slice(b"NIPC"));
661 region.grow(96).unwrap();
662
663 let status = region.status();
664 assert_eq!(status.logical_len, 96);
665 assert_eq!(status.maximum_len, 128);
666 assert!(status.can_grow);
667 assert_eq!(status.permissions.creator_access(), MemoryAccess::ReadWrite);
668 assert_eq!(status.permissions.peer_access(), MemoryAccess::ReadOnly);
669 region.initialize(|bytes| assert_eq!(&bytes[..4], b"NIPC"));
670 }
671
672 #[test]
673 fn fixed_and_bounded_growth_fail_closed() {
674 let mut fixed =
675 NativeRegion::allocate(RegionOptions::fixed(32, WriterOwner::Peer)).unwrap();
676 assert!(matches!(fixed.grow(64), Err(MemoryError::FixedSize)));
677
678 let mut bounded =
679 NativeRegion::allocate(RegionOptions::growable(32, 64, WriterOwner::Peer)).unwrap();
680 assert!(matches!(
681 bounded.grow(65),
682 Err(MemoryError::MaximumExceeded {
683 requested: 65,
684 maximum: 64
685 })
686 ));
687 assert!(matches!(
688 bounded.grow(16),
689 Err(MemoryError::ShrinkUnsupported {
690 current: 32,
691 requested: 16
692 })
693 ));
694 }
695
696 #[test]
697 fn clear_covers_logical_bytes() {
698 let mut region =
699 NativeRegion::allocate(RegionOptions::fixed(32, WriterOwner::Creator)).unwrap();
700 region.initialize(|bytes| bytes.fill(0xaa));
701 region.clear();
702 region.initialize(|bytes| assert!(bytes.iter().all(|byte| *byte == 0)));
703 region.initialize(|bytes| bytes[..4].copy_from_slice(b"REUS"));
704 region.initialize(|bytes| assert_eq!(&bytes[..4], b"REUS"));
705 region.destroy();
706 }
707
708 #[test]
709 fn share_request_preserves_permissions_and_removes_byte_access() {
710 let region = NativeRegion::allocate(RegionOptions::fixed(32, WriterOwner::Peer)).unwrap();
711 let request = region.prepare_for_sharing();
712 assert_eq!(request.seal_policy(), SealPolicy::RequiredOnShare);
713 assert_eq!(
714 request.permissions().creator_access(),
715 MemoryAccess::ReadOnly
716 );
717 assert_eq!(request.permissions().peer_access(), MemoryAccess::ReadWrite);
718 assert!(request.mapped_len() >= 32);
719 request.destroy();
720 }
721}