Skip to main content

zerodds_corba_rt/
priority.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3
4//! `Priority` + `PriorityMapping` (RT-CORBA §5.3/§5.4).
5
6/// CORBA priority (RT-CORBA §5.3): a platform-independent `short` in the range
7/// `0..=32767` (negative values are reserved). Higher = more urgent.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct Priority(i16);
10
11/// Minimum valid CORBA priority.
12pub const MIN_PRIORITY: i16 = 0;
13/// Maximum valid CORBA priority.
14pub const MAX_PRIORITY: i16 = 32767;
15
16impl Priority {
17    /// Constructs a priority; `None` if outside `0..=32767`.
18    #[must_use]
19    pub fn new(value: i16) -> Option<Self> {
20        if (MIN_PRIORITY..=MAX_PRIORITY).contains(&value) {
21            Some(Self(value))
22        } else {
23            None
24        }
25    }
26
27    /// Constructs a priority, clamped to `0..=32767`.
28    #[must_use]
29    pub fn clamped(value: i32) -> Self {
30        Self(value.clamp(MIN_PRIORITY as i32, MAX_PRIORITY as i32) as i16)
31    }
32
33    /// The raw `short` value.
34    #[must_use]
35    pub fn value(self) -> i16 {
36        self.0
37    }
38}
39
40/// Maps CORBA priorities to native (OS) priorities and back
41/// (RT-CORBA §5.4 `PriorityMapping`).
42pub trait PriorityMapping {
43    /// CORBA priority → native priority. `None` if not mappable.
44    fn to_native(&self, corba: Priority) -> Option<i32>;
45    /// Native priority → CORBA priority. `None` if not mappable.
46    fn to_corba(&self, native: i32) -> Option<Priority>;
47}
48
49/// Default linear mapping: `native = floor / divisor`-scaled onto a native
50/// window `[native_min, native_max]` (e.g. POSIX `SCHED_FIFO` 1..99).
51#[derive(Debug, Clone, Copy)]
52pub struct LinearPriorityMapping {
53    native_min: i32,
54    native_max: i32,
55}
56
57impl LinearPriorityMapping {
58    /// New linear mapping onto `[native_min, native_max]` (e.g. `(1, 99)`).
59    #[must_use]
60    pub fn new(native_min: i32, native_max: i32) -> Self {
61        Self {
62            native_min,
63            native_max,
64        }
65    }
66}
67
68impl PriorityMapping for LinearPriorityMapping {
69    fn to_native(&self, corba: Priority) -> Option<i32> {
70        let span = i64::from(self.native_max - self.native_min);
71        let scaled = i64::from(corba.value()) * span / i64::from(MAX_PRIORITY);
72        Some(self.native_min + scaled as i32)
73    }
74
75    fn to_corba(&self, native: i32) -> Option<Priority> {
76        if native < self.native_min || native > self.native_max {
77            return None;
78        }
79        let span = i64::from(self.native_max - self.native_min);
80        if span == 0 {
81            return Priority::new(0);
82        }
83        let scaled = i64::from(native - self.native_min) * i64::from(MAX_PRIORITY) / span;
84        Priority::new(scaled as i16)
85    }
86}
87
88#[cfg(test)]
89#[allow(clippy::unwrap_used, clippy::panic)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn priority_range() {
95        assert!(Priority::new(0).is_some());
96        assert!(Priority::new(32767).is_some());
97        assert!(Priority::new(-1).is_none());
98        assert_eq!(Priority::clamped(99999).value(), 32767);
99        assert_eq!(Priority::clamped(-5).value(), 0);
100    }
101
102    #[test]
103    fn linear_mapping_endpoints() {
104        let m = LinearPriorityMapping::new(1, 99);
105        assert_eq!(m.to_native(Priority::new(0).unwrap()), Some(1));
106        assert_eq!(m.to_native(Priority::new(32767).unwrap()), Some(99)); // native_min + span = 1 + 98
107        // Middle ~50.
108        let mid = m.to_native(Priority::new(16383).unwrap()).unwrap();
109        assert!((49..=51).contains(&mid), "mid={mid}");
110    }
111
112    #[test]
113    fn native_out_of_window_unmapped() {
114        let m = LinearPriorityMapping::new(1, 99);
115        assert!(m.to_corba(0).is_none());
116        assert!(m.to_corba(100).is_none());
117        assert!(m.to_corba(50).is_some());
118    }
119}