zenoh_shm/api/provider/
types.rs

1//
2// Copyright (c) 2023 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13//
14
15use std::{convert::Infallible, fmt::Display, num::NonZeroUsize};
16
17use super::chunk::AllocatedChunk;
18
19/// Allocation error.
20#[zenoh_macros::unstable_doc]
21#[derive(Debug)]
22pub enum ZAllocError {
23    /// Defragmentation is needed.
24    NeedDefragment,
25    /// The provider is out of memory.
26    OutOfMemory,
27    /// Uncategorized allocation error.
28    Other,
29}
30
31impl Display for ZAllocError {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        match self {
34            ZAllocError::NeedDefragment => write!(f, "need defragmentation"),
35            ZAllocError::OutOfMemory => write!(f, "out of memory"),
36            ZAllocError::Other => write!(f, "other"),
37        }
38    }
39}
40
41impl std::error::Error for ZAllocError {}
42
43impl From<zenoh_result::Error> for ZAllocError {
44    fn from(_value: zenoh_result::Error) -> Self {
45        Self::Other
46    }
47}
48
49/// alignment in powers of 2: 0 == 1-byte alignment, 1 == 2byte, 2 == 4byte, 3 == 8byte etc
50#[zenoh_macros::unstable_doc]
51#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
52pub struct AllocAlignment {
53    pow: u8,
54}
55
56impl Display for AllocAlignment {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        f.write_fmt(format_args!("[{}]", self.get_alignment_value()))
59    }
60}
61
62impl Default for AllocAlignment {
63    fn default() -> Self {
64        Self::ALIGN_1_BYTE
65    }
66}
67
68impl AllocAlignment {
69    pub const ALIGN_1_BYTE: AllocAlignment = AllocAlignment { pow: 0 };
70    pub const ALIGN_2_BYTES: AllocAlignment = AllocAlignment { pow: 1 };
71    pub const ALIGN_4_BYTES: AllocAlignment = AllocAlignment { pow: 2 };
72    pub const ALIGN_8_BYTES: AllocAlignment = AllocAlignment { pow: 3 };
73
74    /// Try to create a new AllocAlignment from alignment representation in powers of 2.
75    ///
76    /// # Errors
77    ///
78    /// This function will return an error if provided alignment power cannot fit into usize.
79    #[zenoh_macros::unstable_doc]
80    pub const fn new(pow: u8) -> Result<Self, ZLayoutError> {
81        if pow < usize::BITS as u8 {
82            Ok(Self { pow })
83        } else {
84            Err(ZLayoutError::IncorrectLayoutArgs)
85        }
86    }
87
88    /// Create a new AllocAlignment for type
89    #[zenoh_macros::unstable_doc]
90    pub const fn for_type<T: Sized>() -> Self {
91        let align = std::mem::align_of::<T>();
92        let pow = align.trailing_zeros() as u8;
93        Self { pow }
94    }
95
96    /// Create a new AllocAlignment for value type
97    #[zenoh_macros::unstable_doc]
98    pub const fn for_value<T: Sized>(_: &T) -> Self {
99        Self::for_type::<T>()
100    }
101
102    /// Get alignment in normal units (bytes)
103    #[zenoh_macros::unstable_doc]
104    pub fn get_alignment_value(&self) -> NonZeroUsize {
105        // SAFETY: this is safe because we limit pow in new based on usize size
106        unsafe { NonZeroUsize::new_unchecked(1usize << self.pow) }
107    }
108
109    /// Align size according to inner alignment.
110    /// This call may extend the size (see the example)
111    /// # Examples
112    ///
113    /// ```
114    /// use zenoh_shm::api::provider::types::AllocAlignment;
115    ///
116    /// let alignment = AllocAlignment::new(2).unwrap(); // 4-byte alignment
117    /// let initial_size = 7.try_into().unwrap();
118    /// let aligned_size = alignment.align_size(initial_size);
119    /// assert_eq!(aligned_size.get(), 8);
120    /// ```
121    #[zenoh_macros::unstable_doc]
122    pub fn align_size(&self, size: NonZeroUsize) -> NonZeroUsize {
123        // Notations:
124        // - size to align S
125        // - usize::BITS B
126        // - pow P where 0 ≤ P < B
127        // - alignment value A = 2^P
128        // - return R = min{x | x ≥ S, x % A = 0}
129        //
130        // Example 1: A = 4 = (00100)₂, S = 4 = (00100)₂ ⇒ R = 4  = (00100)₂
131        // Example 2: A = 4 = (00100)₂, S = 7 = (00111)₂ ⇒ R = 8  = (01000)₂
132        // Example 3: A = 4 = (00100)₂, S = 8 = (01000)₂ ⇒ R = 8  = (01000)₂
133        // Example 4: A = 4 = (00100)₂, S = 9 = (01001)₂ ⇒ R = 12 = (01100)₂
134        //
135        // Algorithm: For any x = (bₙ, ⋯, b₂, b₁)₂ in binary representation,
136        // 1. x % A = 0 ⇔ ∀i < P, bᵢ = 0
137        // 2. f(x) ≜ x & !(A-1) leads to ∀i < P, bᵢ = 0, hence f(x) % A = 0
138        // (i.e. f zeros all bits before the P-th bit)
139        // 3. R = min{x | x ≥ S, x % A = 0} is equivalent to find the unique R where S ≤ R < S+A and R % A = 0
140        // 4. x-A < f(x) ≤ x ⇒ S-1 < f(S+A-1) ≤ S+A-1 ⇒ S ≤ f(S+A-1) < S+A
141        //
142        // Hence R = f(S+A-1) = (S+(A-1)) & !(A-1) is the desired value
143
144        // Compute A - 1 = 2^P - 1
145        let a_minus_1 = self.get_alignment_value().get() - 1;
146
147        // Overflow check: ensure S ≤ 2^B - 2^P = (2^B - 1) - (A - 1)
148        // so that R < S+A ≤ 2^B and hence it's a valid usize
149        let bound = usize::MAX - a_minus_1;
150        assert!(
151            size.get() <= bound,
152            "The given size {} exceeded the maximum {}",
153            size.get(),
154            bound
155        );
156
157        // Overflow never occurs due to the check above
158        let r = (size.get() + a_minus_1) & !a_minus_1;
159
160        // SAFETY: R ≥ 0 since R ≥ S ≥ 0
161        unsafe { NonZeroUsize::new_unchecked(r) }
162    }
163}
164
165/// Layout error.
166#[zenoh_macros::unstable_doc]
167#[derive(Debug)]
168pub enum ZLayoutError {
169    /// Incorrect layout arguments.
170    IncorrectLayoutArgs,
171    /// The layout is incompatible with the provider.
172    ProviderIncompatibleLayout,
173}
174
175impl Display for ZLayoutError {
176    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177        match self {
178            Self::IncorrectLayoutArgs => write!(f, "incorrect layout arguments"),
179            Self::ProviderIncompatibleLayout => {
180                write!(f, "layout is incompatible with the provider")
181            }
182        }
183    }
184}
185
186impl std::error::Error for ZLayoutError {}
187
188impl From<Infallible> for ZLayoutError {
189    fn from(_: Infallible) -> Self {
190        unreachable!()
191    }
192}
193
194/// SHM chunk allocation result
195#[zenoh_macros::unstable_doc]
196pub type ChunkAllocResult = Result<AllocatedChunk, ZAllocError>;
197
198/// Layout or allocation error.
199#[zenoh_macros::unstable_doc]
200#[derive(Debug)]
201pub enum ZLayoutAllocError {
202    /// Allocation error.
203    Alloc(ZAllocError),
204    /// Layout error.
205    Layout(ZLayoutError),
206}
207
208impl Display for ZLayoutAllocError {
209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210        match self {
211            ZLayoutAllocError::Alloc(err) => write!(f, "{}", err),
212            ZLayoutAllocError::Layout(err) => write!(f, "{}", err),
213        }
214    }
215}
216
217impl std::error::Error for ZLayoutAllocError {}
218
219impl From<ZLayoutError> for ZLayoutAllocError {
220    fn from(value: ZLayoutError) -> Self {
221        Self::Layout(value)
222    }
223}
224
225impl From<ZAllocError> for ZLayoutAllocError {
226    fn from(value: ZAllocError) -> Self {
227        Self::Alloc(value)
228    }
229}