/rust/registry/src/index.crates.io-1949cf8c6b5b557f/portable-atomic-1.13.1/src/utils.rs
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 OR MIT |
2 | | |
3 | | #![cfg_attr(not(all(test, feature = "float")), allow(dead_code, unused_macros))] |
4 | | |
5 | | #[allow(unused_imports)] |
6 | | pub(crate) use self::generated::{RegISize, RegSize}; |
7 | | #[macro_use] |
8 | | #[path = "gen/utils.rs"] |
9 | | mod generated; |
10 | | |
11 | | use core::sync::atomic::Ordering; |
12 | | |
13 | | macro_rules! static_assert { |
14 | | ($cond:expr $(,)?) => {{ |
15 | | let [()] = [(); (true /* type check */ & $cond) as usize]; |
16 | | }}; |
17 | | } |
18 | | |
19 | | macro_rules! static_assert_layout { |
20 | | ($atomic_type:ty, $value_type:ty) => { |
21 | | static_assert!( |
22 | | core::mem::align_of::<$atomic_type>() == core::mem::size_of::<$atomic_type>() |
23 | | ); |
24 | | static_assert!(core::mem::size_of::<$atomic_type>() == core::mem::size_of::<$value_type>()); |
25 | | }; |
26 | | } |
27 | | |
28 | | // #[doc = concat!(...)] requires Rust 1.54 |
29 | | macro_rules! doc_comment { |
30 | | ($doc:expr, $($tt:tt)*) => { |
31 | | #[doc = $doc] |
32 | | $($tt)* |
33 | | }; |
34 | | } |
35 | | |
36 | | // Adapted from https://github.com/BurntSushi/memchr/blob/2.4.1/src/memchr/x86/mod.rs#L9-L71. |
37 | | /// # Safety |
38 | | /// |
39 | | /// - the caller must uphold the safety contract for the function returned by $detect_body. |
40 | | /// - the memory pointed by the function pointer returned by $detect_body must be visible from any threads. |
41 | | /// |
42 | | /// The second requirement is always met if the function pointer is to the function definition. |
43 | | /// (Currently, all uses of this macro in our code are in this case.) |
44 | | #[allow(unused_macros)] |
45 | | #[cfg(not(portable_atomic_no_outline_atomics))] |
46 | | #[cfg(any( |
47 | | target_arch = "aarch64", |
48 | | target_arch = "arm", |
49 | | target_arch = "arm64ec", |
50 | | target_arch = "powerpc64", |
51 | | target_arch = "riscv32", |
52 | | target_arch = "riscv64", |
53 | | all(target_arch = "x86_64", not(any(target_env = "sgx", miri))), |
54 | | ))] |
55 | | macro_rules! ifunc { |
56 | | (unsafe fn($($arg_pat:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)? { $($init_body:tt)* }) => {{ |
57 | | type FnTy = unsafe fn($($arg_ty),*) $(-> $ret_ty)?; |
58 | | static FUNC: core::sync::atomic::AtomicPtr<()> |
59 | | = core::sync::atomic::AtomicPtr::new(init as *mut ()); |
60 | | #[cold] |
61 | 0 | unsafe fn init($($arg_pat: $arg_ty),*) $(-> $ret_ty)? { |
62 | 0 | let func: FnTy = { $($init_body)* }; |
63 | 0 | FUNC.store(func as *mut (), core::sync::atomic::Ordering::Relaxed); |
64 | | // SAFETY: the caller must uphold the safety contract for the function returned by $init_body. |
65 | 0 | unsafe { func($($arg_pat),*) } |
66 | 0 | } Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_add::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_and::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_max::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_min::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_neg::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_not::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_sub::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_xor::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_load::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_nand::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_swap::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_umax::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_umin::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_store::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_store::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_compare_exchange::init Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_or::init |
67 | | // SAFETY: `FnTy` is a function pointer, which is always safe to transmute with a `*mut ()`. |
68 | | // (To force the caller to use unsafe block for this macro, do not use |
69 | | // unsafe block here.) |
70 | | let func = { |
71 | | core::mem::transmute::<*mut (), FnTy>(FUNC.load(core::sync::atomic::Ordering::Relaxed)) |
72 | | }; |
73 | | // SAFETY: the caller must uphold the safety contract for the function returned by $init_body. |
74 | | // (To force the caller to use unsafe block for this macro, do not use |
75 | | // unsafe block here.) |
76 | | func($($arg_pat),*) |
77 | | }}; |
78 | | } |
79 | | |
80 | | #[allow(unused_macros)] |
81 | | #[cfg(not(portable_atomic_no_outline_atomics))] |
82 | | #[cfg(any( |
83 | | target_arch = "aarch64", |
84 | | target_arch = "arm", |
85 | | target_arch = "arm64ec", |
86 | | target_arch = "powerpc64", |
87 | | target_arch = "riscv32", |
88 | | target_arch = "riscv64", |
89 | | all(target_arch = "x86_64", not(any(target_env = "sgx", miri))), |
90 | | ))] |
91 | | macro_rules! fn_alias { |
92 | | ( |
93 | | $(#[$($fn_attr:tt)*])* |
94 | | $vis:vis unsafe fn($($arg_pat:ident: $arg_ty:ty),*) $(-> $ret_ty:ty)?; |
95 | | $(#[$($alias_attr:tt)*])* |
96 | | $new:ident = $from:ident($($last_args:tt)*); |
97 | | $($rest:tt)* |
98 | | ) => { |
99 | | $(#[$($fn_attr)*])* |
100 | | $(#[$($alias_attr)*])* |
101 | 0 | $vis unsafe fn $new($($arg_pat: $arg_ty),*) $(-> $ret_ty)? { |
102 | | // SAFETY: the caller must uphold the safety contract. |
103 | 0 | unsafe { $from($($arg_pat,)* $($last_args)*) } |
104 | 0 | } Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_or_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_add_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_and_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_max_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_min_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_neg_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_not_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_sub_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_xor_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_load_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_nand_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_swap_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_umax_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_umin_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_store_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_store_non_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::fallback::atomic_compare_exchange_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_add::cmpxchg16b_seqcst_fn Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_and::cmpxchg16b_seqcst_fn Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_max::cmpxchg16b_seqcst_fn Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_min::cmpxchg16b_seqcst_fn Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_neg::cmpxchg16b_seqcst_fn Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_not::cmpxchg16b_seqcst_fn Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_sub::cmpxchg16b_seqcst_fn Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_xor::cmpxchg16b_seqcst_fn Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_nand::cmpxchg16b_seqcst_fn Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_swap::cmpxchg16b_seqcst_fn Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_umax::cmpxchg16b_seqcst_fn Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_umin::cmpxchg16b_seqcst_fn Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_store::_atomic_store_vmovdqa_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_store::_atomic_store_vmovdqa_non_seqcst Unexecuted instantiation: portable_atomic::imp::atomic128::x86_64::atomic_or::cmpxchg16b_seqcst_fn |
105 | | fn_alias! { |
106 | | $(#[$($fn_attr)*])* |
107 | | $vis unsafe fn($($arg_pat: $arg_ty),*) $(-> $ret_ty)?; |
108 | | $($rest)* |
109 | | } |
110 | | }; |
111 | | ( |
112 | | $(#[$($attr:tt)*])* |
113 | | $vis:vis unsafe fn($($arg_pat:ident: $arg_ty:ty),*) $(-> $ret_ty:ty)?; |
114 | | ) => {} |
115 | | } |
116 | | |
117 | | /// Make the given function const if the given condition is true. |
118 | | macro_rules! const_fn { |
119 | | ( |
120 | | const_if: #[cfg($($cfg:tt)+)]; |
121 | | $(#[$($attr:tt)*])* |
122 | | $vis:vis const $($rest:tt)* |
123 | | ) => { |
124 | | #[cfg($($cfg)+)] |
125 | | $(#[$($attr)*])* |
126 | | $vis const $($rest)* |
127 | | #[cfg(not($($cfg)+))] |
128 | | $(#[$($attr)*])* |
129 | | $vis $($rest)* |
130 | | }; |
131 | | } |
132 | | |
133 | | /// Implements `core::fmt::Debug` and `serde::{Serialize, Deserialize}` (when serde |
134 | | /// feature is enabled) for atomic bool, integer, or float. |
135 | | macro_rules! impl_debug_and_serde { |
136 | | // TODO(f16_and_f128): Implement serde traits for f16 & f128 once stabilized. |
137 | | (AtomicF16) => { |
138 | | impl_debug!(AtomicF16); |
139 | | }; |
140 | | (AtomicF128) => { |
141 | | impl_debug!(AtomicF128); |
142 | | }; |
143 | | ($atomic_type:ident) => { |
144 | | impl_debug!($atomic_type); |
145 | | #[cfg(feature = "serde")] |
146 | | #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] |
147 | | impl serde::ser::Serialize for $atomic_type { |
148 | | #[allow(clippy::missing_inline_in_public_items)] // serde doesn't use inline on std atomic's Serialize/Deserialize impl |
149 | | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
150 | | where |
151 | | S: serde::ser::Serializer, |
152 | | { |
153 | | // https://github.com/serde-rs/serde/blob/v1.0.152/serde/src/ser/impls.rs#L958-L959 |
154 | | self.load(Ordering::Relaxed).serialize(serializer) |
155 | | } |
156 | | } |
157 | | #[cfg(feature = "serde")] |
158 | | #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] |
159 | | impl<'de> serde::de::Deserialize<'de> for $atomic_type { |
160 | | #[allow(clippy::missing_inline_in_public_items)] // serde doesn't use inline on std atomic's Serialize/Deserialize impl |
161 | | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
162 | | where |
163 | | D: serde::de::Deserializer<'de>, |
164 | | { |
165 | | serde::de::Deserialize::deserialize(deserializer).map(Self::new) |
166 | | } |
167 | | } |
168 | | }; |
169 | | } |
170 | | macro_rules! impl_debug { |
171 | | ($atomic_type:ident) => { |
172 | | impl fmt::Debug for $atomic_type { |
173 | | #[inline] // fmt is not hot path, but #[inline] on fmt seems to still be useful: https://github.com/rust-lang/rust/pull/117727 |
174 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
175 | | // std atomic types use Relaxed in Debug::fmt: https://github.com/rust-lang/rust/blob/1.84.0/library/core/src/sync/atomic.rs#L2188 |
176 | 0 | fmt::Debug::fmt(&self.load(Ordering::Relaxed), f) |
177 | 0 | } Unexecuted instantiation: <portable_atomic::AtomicI128 as core::fmt::Debug>::fmt Unexecuted instantiation: <portable_atomic::AtomicU128 as core::fmt::Debug>::fmt Unexecuted instantiation: <portable_atomic::AtomicBool as core::fmt::Debug>::fmt Unexecuted instantiation: <portable_atomic::AtomicU16 as core::fmt::Debug>::fmt Unexecuted instantiation: <portable_atomic::AtomicI32 as core::fmt::Debug>::fmt Unexecuted instantiation: <portable_atomic::AtomicU32 as core::fmt::Debug>::fmt Unexecuted instantiation: <portable_atomic::AtomicI64 as core::fmt::Debug>::fmt Unexecuted instantiation: <portable_atomic::AtomicU64 as core::fmt::Debug>::fmt Unexecuted instantiation: <portable_atomic::AtomicIsize as core::fmt::Debug>::fmt Unexecuted instantiation: <portable_atomic::AtomicUsize as core::fmt::Debug>::fmt Unexecuted instantiation: <portable_atomic::AtomicI8 as core::fmt::Debug>::fmt Unexecuted instantiation: <portable_atomic::AtomicU8 as core::fmt::Debug>::fmt Unexecuted instantiation: <portable_atomic::AtomicI16 as core::fmt::Debug>::fmt |
178 | | } |
179 | | }; |
180 | | } |
181 | | |
182 | | // We do not provide `nand` because it cannot be optimized on neither x86 nor MSP430. |
183 | | // https://godbolt.org/z/ahWejchbT |
184 | | macro_rules! impl_default_no_fetch_ops { |
185 | | ($atomic_type:ident, bool) => { |
186 | | impl $atomic_type { |
187 | | #[inline] |
188 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
189 | | pub(crate) fn and(&self, val: bool, order: Ordering) { |
190 | | self.fetch_and(val, order); |
191 | | } |
192 | | #[inline] |
193 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
194 | | pub(crate) fn or(&self, val: bool, order: Ordering) { |
195 | | self.fetch_or(val, order); |
196 | | } |
197 | | #[inline] |
198 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
199 | | pub(crate) fn xor(&self, val: bool, order: Ordering) { |
200 | | self.fetch_xor(val, order); |
201 | | } |
202 | | } |
203 | | }; |
204 | | ($atomic_type:ident, $int_type:ty) => { |
205 | | impl $atomic_type { |
206 | | #[inline] |
207 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
208 | 0 | pub(crate) fn add(&self, val: $int_type, order: Ordering) { |
209 | 0 | self.fetch_add(val, order); |
210 | 0 | } Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicI128>::add Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicU128>::add Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicIsize>::add Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicUsize>::add Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI64>::add Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU64>::add Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI8>::add Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU8>::add Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI16>::add Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU16>::add Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI32>::add Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU32>::add |
211 | | #[inline] |
212 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
213 | 0 | pub(crate) fn sub(&self, val: $int_type, order: Ordering) { |
214 | 0 | self.fetch_sub(val, order); |
215 | 0 | } Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicI128>::sub Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicU128>::sub Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicIsize>::sub Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicUsize>::sub Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI64>::sub Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU64>::sub Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI8>::sub Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU8>::sub Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI16>::sub Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU16>::sub Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI32>::sub Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU32>::sub |
216 | | #[inline] |
217 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
218 | 0 | pub(crate) fn and(&self, val: $int_type, order: Ordering) { |
219 | 0 | self.fetch_and(val, order); |
220 | 0 | } Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicI128>::and Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicU128>::and Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicIsize>::and Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicUsize>::and Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI64>::and Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU64>::and Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI8>::and Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU8>::and Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI16>::and Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU16>::and Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI32>::and Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU32>::and |
221 | | #[inline] |
222 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
223 | 0 | pub(crate) fn or(&self, val: $int_type, order: Ordering) { |
224 | 0 | self.fetch_or(val, order); |
225 | 0 | } Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicI128>::or Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicU128>::or Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicIsize>::or Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicUsize>::or Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI64>::or Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU64>::or Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI8>::or Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU8>::or Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI16>::or Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU16>::or Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI32>::or Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU32>::or |
226 | | #[inline] |
227 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
228 | 0 | pub(crate) fn xor(&self, val: $int_type, order: Ordering) { |
229 | 0 | self.fetch_xor(val, order); |
230 | 0 | } Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicI128>::xor Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicU128>::xor Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicIsize>::xor Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicUsize>::xor Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI64>::xor Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU64>::xor Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI8>::xor Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU8>::xor Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI16>::xor Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU16>::xor Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI32>::xor Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU32>::xor |
231 | | } |
232 | | }; |
233 | | } |
234 | | macro_rules! impl_default_bit_opts { |
235 | | (AtomicPtr, $int_type:ty) => { |
236 | | impl<T> AtomicPtr<T> { |
237 | | #[inline] |
238 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
239 | 0 | pub(crate) fn bit_set(&self, bit: u32, order: Ordering) -> bool { |
240 | | #[cfg(portable_atomic_no_strict_provenance)] |
241 | | use crate::utils::ptr::PtrExt as _; |
242 | 0 | let mask = <$int_type>::wrapping_shl(1, bit); |
243 | 0 | self.fetch_or(mask, order).addr() & mask != 0 |
244 | 0 | } |
245 | | #[inline] |
246 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
247 | 0 | pub(crate) fn bit_clear(&self, bit: u32, order: Ordering) -> bool { |
248 | | #[cfg(portable_atomic_no_strict_provenance)] |
249 | | use crate::utils::ptr::PtrExt as _; |
250 | 0 | let mask = <$int_type>::wrapping_shl(1, bit); |
251 | 0 | self.fetch_and(!mask, order).addr() & mask != 0 |
252 | 0 | } |
253 | | #[inline] |
254 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
255 | 0 | pub(crate) fn bit_toggle(&self, bit: u32, order: Ordering) -> bool { |
256 | | #[cfg(portable_atomic_no_strict_provenance)] |
257 | | use crate::utils::ptr::PtrExt as _; |
258 | 0 | let mask = <$int_type>::wrapping_shl(1, bit); |
259 | 0 | self.fetch_xor(mask, order).addr() & mask != 0 |
260 | 0 | } |
261 | | } |
262 | | }; |
263 | | ($atomic_type:ident, $int_type:ty) => { |
264 | | impl $atomic_type { |
265 | | #[inline] |
266 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
267 | 0 | pub(crate) fn bit_set(&self, bit: u32, order: Ordering) -> bool { |
268 | 0 | let mask = <$int_type>::wrapping_shl(1, bit); |
269 | 0 | self.fetch_or(mask, order) & mask != 0 |
270 | 0 | } Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicI128>::bit_set Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicU128>::bit_set Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI8>::bit_set Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU8>::bit_set Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI16>::bit_set Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU16>::bit_set Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI32>::bit_set Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU32>::bit_set Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI64>::bit_set Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU64>::bit_set Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicIsize>::bit_set Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicUsize>::bit_set |
271 | | #[inline] |
272 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
273 | 0 | pub(crate) fn bit_clear(&self, bit: u32, order: Ordering) -> bool { |
274 | 0 | let mask = <$int_type>::wrapping_shl(1, bit); |
275 | 0 | self.fetch_and(!mask, order) & mask != 0 |
276 | 0 | } Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicI128>::bit_clear Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicU128>::bit_clear Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI8>::bit_clear Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU8>::bit_clear Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI16>::bit_clear Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU16>::bit_clear Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI32>::bit_clear Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU32>::bit_clear Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI64>::bit_clear Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU64>::bit_clear Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicIsize>::bit_clear Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicUsize>::bit_clear |
277 | | #[inline] |
278 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
279 | 0 | pub(crate) fn bit_toggle(&self, bit: u32, order: Ordering) -> bool { |
280 | 0 | let mask = <$int_type>::wrapping_shl(1, bit); |
281 | 0 | self.fetch_xor(mask, order) & mask != 0 |
282 | 0 | } Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicI128>::bit_toggle Unexecuted instantiation: <portable_atomic::imp::atomic128::x86_64::AtomicU128>::bit_toggle Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI8>::bit_toggle Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU8>::bit_toggle Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI16>::bit_toggle Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU16>::bit_toggle Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI32>::bit_toggle Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU32>::bit_toggle Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicI64>::bit_toggle Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicU64>::bit_toggle Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicIsize>::bit_toggle Unexecuted instantiation: <portable_atomic::imp::core_atomic::AtomicUsize>::bit_toggle |
283 | | } |
284 | | }; |
285 | | } |
286 | | |
287 | | // This just outputs the input as is, but can be used like an item-level block by using it with cfg. |
288 | | // Note: This macro is items!({ }), not items! { }. |
289 | | // An extra brace is used in input to make contents rustfmt-able. |
290 | | macro_rules! items { |
291 | | ({$($tt:tt)*}) => { |
292 | | $($tt)* |
293 | | }; |
294 | | } |
295 | | |
296 | | // rustfmt-compatible cfg_select/cfg_if alternative |
297 | | // Note: This macro is cfg_sel!({ }), not cfg_sel! { }. |
298 | | // An extra brace is used in input to make contents rustfmt-able. |
299 | | macro_rules! cfg_sel { |
300 | | ({#[cfg(else)] { $($output:tt)* }}) => { |
301 | | $($output)* |
302 | | }; |
303 | | ({ |
304 | | #[cfg($cfg:meta)] |
305 | | { $($output:tt)* } |
306 | | $($( $rest:tt )+)? |
307 | | }) => { |
308 | | #[cfg($cfg)] |
309 | | cfg_sel! {{#[cfg(else)] { $($output)* }}} |
310 | | $( |
311 | | #[cfg(not($cfg))] |
312 | | cfg_sel! {{ $($rest)+ }} |
313 | | )? |
314 | | }; |
315 | | ({ |
316 | | #[cfg_attr(portable_atomic_no_cfg_target_has_atomic, cfg($cfg1:meta))] |
317 | | #[cfg_attr(not(portable_atomic_no_cfg_target_has_atomic), cfg($cfg2:meta))] |
318 | | { $($output:tt)* } |
319 | | $($( $rest:tt )+)? |
320 | | }) => { |
321 | | #[cfg_attr(portable_atomic_no_cfg_target_has_atomic, cfg($cfg1))] |
322 | | #[cfg_attr(not(portable_atomic_no_cfg_target_has_atomic), cfg($cfg2))] |
323 | | cfg_sel! {{#[cfg(else)] { $($output)* }}} |
324 | | $( |
325 | | #[cfg_attr(portable_atomic_no_cfg_target_has_atomic, cfg(not($cfg1)))] |
326 | | #[cfg_attr(not(portable_atomic_no_cfg_target_has_atomic), cfg(not($cfg2)))] |
327 | | cfg_sel! {{ $($rest)+ }} |
328 | | )? |
329 | | }; |
330 | | } |
331 | | |
332 | | // Stable equivalent of core::hint::{likely, unlikely}. |
333 | | #[allow(dead_code)] |
334 | | #[inline(always)] |
335 | | #[cold] |
336 | 0 | fn cold_path() {} |
337 | | #[allow(dead_code)] |
338 | | #[inline(always)] |
339 | 0 | pub(crate) fn likely(b: bool) -> bool { |
340 | 0 | if b { |
341 | 0 | true |
342 | | } else { |
343 | 0 | cold_path(); |
344 | 0 | false |
345 | | } |
346 | 0 | } |
347 | | #[allow(dead_code)] |
348 | | #[inline(always)] |
349 | 0 | pub(crate) fn unlikely(b: bool) -> bool { |
350 | 0 | if b { |
351 | 0 | cold_path(); |
352 | 0 | true |
353 | | } else { |
354 | 0 | false |
355 | | } |
356 | 0 | } |
357 | | |
358 | | // Equivalent to core::hint::assert_unchecked, but compatible with pre-1.81 rustc. |
359 | | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
360 | | #[allow(dead_code)] |
361 | | #[inline(always)] |
362 | | #[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)] |
363 | 0 | pub(crate) unsafe fn assert_unchecked(cond: bool) { |
364 | 0 | if !cond { |
365 | | #[cfg(debug_assertions)] |
366 | | unreachable!(); |
367 | | #[cfg(not(debug_assertions))] |
368 | | // SAFETY: the caller promised `cond` is true. |
369 | | unsafe { |
370 | 0 | core::hint::unreachable_unchecked() |
371 | | } |
372 | 0 | } |
373 | 0 | } |
374 | | |
375 | | // https://github.com/rust-lang/rust/blob/1.84.0/library/core/src/sync/atomic.rs#L3338 |
376 | | #[inline] |
377 | | #[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)] |
378 | 0 | pub(crate) fn assert_load_ordering(order: Ordering) { |
379 | 0 | match order { |
380 | 0 | Ordering::Acquire | Ordering::Relaxed | Ordering::SeqCst => {} |
381 | 0 | Ordering::Release => panic!("there is no such thing as a release load"), |
382 | 0 | Ordering::AcqRel => panic!("there is no such thing as an acquire-release load"), |
383 | 0 | _ => unreachable!(), |
384 | | } |
385 | 0 | } Unexecuted instantiation: portable_atomic::utils::assert_load_ordering Unexecuted instantiation: portable_atomic::utils::assert_load_ordering Unexecuted instantiation: portable_atomic::utils::assert_load_ordering Unexecuted instantiation: portable_atomic::utils::assert_load_ordering |
386 | | // https://github.com/rust-lang/rust/blob/1.84.0/library/core/src/sync/atomic.rs#L3323 |
387 | | #[inline] |
388 | | #[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)] |
389 | 0 | pub(crate) fn assert_store_ordering(order: Ordering) { |
390 | 0 | match order { |
391 | 0 | Ordering::Release | Ordering::Relaxed | Ordering::SeqCst => {} |
392 | 0 | Ordering::Acquire => panic!("there is no such thing as an acquire store"), |
393 | 0 | Ordering::AcqRel => panic!("there is no such thing as an acquire-release store"), |
394 | 0 | _ => unreachable!(), |
395 | | } |
396 | 0 | } Unexecuted instantiation: portable_atomic::utils::assert_store_ordering Unexecuted instantiation: portable_atomic::utils::assert_store_ordering |
397 | | // https://github.com/rust-lang/rust/blob/1.84.0/library/core/src/sync/atomic.rs#L3404 |
398 | | #[inline] |
399 | | #[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)] |
400 | 0 | pub(crate) fn assert_compare_exchange_ordering(success: Ordering, failure: Ordering) { |
401 | 0 | match success { |
402 | | Ordering::AcqRel |
403 | | | Ordering::Acquire |
404 | | | Ordering::Relaxed |
405 | | | Ordering::Release |
406 | 0 | | Ordering::SeqCst => {} |
407 | 0 | _ => unreachable!(), |
408 | | } |
409 | 0 | match failure { |
410 | 0 | Ordering::Acquire | Ordering::Relaxed | Ordering::SeqCst => {} |
411 | 0 | Ordering::Release => panic!("there is no such thing as a release failure ordering"), |
412 | 0 | Ordering::AcqRel => panic!("there is no such thing as an acquire-release failure ordering"), |
413 | 0 | _ => unreachable!(), |
414 | | } |
415 | 0 | } Unexecuted instantiation: portable_atomic::utils::assert_compare_exchange_ordering Unexecuted instantiation: portable_atomic::utils::assert_compare_exchange_ordering Unexecuted instantiation: portable_atomic::utils::assert_compare_exchange_ordering |
416 | | |
417 | | // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0418r2.html |
418 | | // https://github.com/rust-lang/rust/pull/98383 |
419 | | #[allow(dead_code)] |
420 | | #[inline] |
421 | 0 | pub(crate) fn upgrade_success_ordering(success: Ordering, failure: Ordering) -> Ordering { |
422 | 0 | match (success, failure) { |
423 | 0 | (Ordering::Relaxed, Ordering::Acquire) => Ordering::Acquire, |
424 | 0 | (Ordering::Release, Ordering::Acquire) => Ordering::AcqRel, |
425 | 0 | (_, Ordering::SeqCst) => Ordering::SeqCst, |
426 | 0 | _ => success, |
427 | | } |
428 | 0 | } |
429 | | |
430 | | #[cfg(not(portable_atomic_no_asm_maybe_uninit))] |
431 | | #[cfg(target_pointer_width = "32")] |
432 | | // SAFETY: MaybeUninit returned by zero_extend64_ptr is always initialized. |
433 | | const _: () = assert!(unsafe { |
434 | | zero_extend64_ptr(ptr::without_provenance_mut(!0)).assume_init() == !0_u32 as u64 |
435 | | }); |
436 | | /// Zero-extends the given 32-bit pointer to `MaybeUninit<u64>`. |
437 | | /// This is used for 64-bit architecture's 32-bit ABI (e.g., AArch64 ILP32 ABI). |
438 | | /// See ptr_reg! macro in src/gen/utils.rs for details. |
439 | | #[cfg(not(portable_atomic_no_asm_maybe_uninit))] |
440 | | #[cfg(target_pointer_width = "32")] |
441 | | #[allow(dead_code)] |
442 | | #[inline] |
443 | | pub(crate) const fn zero_extend64_ptr(v: *mut ()) -> core::mem::MaybeUninit<u64> { |
444 | | #[repr(C)] |
445 | | struct ZeroExtended { |
446 | | #[cfg(target_endian = "big")] |
447 | | pad: *mut (), |
448 | | v: *mut (), |
449 | | #[cfg(target_endian = "little")] |
450 | | pad: *mut (), |
451 | | } |
452 | | // SAFETY: we can safely transmute any 64-bit value to MaybeUninit<u64>. |
453 | | unsafe { core::mem::transmute(ZeroExtended { v, pad: core::ptr::null_mut() }) } |
454 | | } |
455 | | |
456 | | #[allow(dead_code)] |
457 | | #[cfg(any( |
458 | | target_arch = "aarch64", |
459 | | target_arch = "arm64ec", |
460 | | target_arch = "powerpc64", |
461 | | target_arch = "riscv64", |
462 | | target_arch = "s390x", |
463 | | target_arch = "x86_64", |
464 | | ))] |
465 | | /// A 128-bit value represented as a pair of 64-bit values. |
466 | | /// |
467 | | /// This type is `#[repr(C)]`, both fields have the same in-memory representation |
468 | | /// and are plain old data types, so access to the fields is always safe. |
469 | | #[derive(Clone, Copy)] |
470 | | #[repr(C)] |
471 | | pub(crate) union U128 { |
472 | | pub(crate) whole: u128, |
473 | | pub(crate) pair: Pair<u64>, |
474 | | } |
475 | | #[allow(dead_code)] |
476 | | #[cfg(any(target_arch = "arm", target_arch = "riscv32"))] |
477 | | /// A 64-bit value represented as a pair of 32-bit values. |
478 | | /// |
479 | | /// This type is `#[repr(C)]`, both fields have the same in-memory representation |
480 | | /// and are plain old data types, so access to the fields is always safe. |
481 | | #[derive(Clone, Copy)] |
482 | | #[repr(C)] |
483 | | pub(crate) union U64 { |
484 | | pub(crate) whole: u64, |
485 | | pub(crate) pair: Pair<u32>, |
486 | | } |
487 | | #[allow(dead_code)] |
488 | | #[derive(Clone, Copy)] |
489 | | #[repr(C)] |
490 | | pub(crate) struct Pair<T: Copy> { |
491 | | // little endian order |
492 | | #[cfg(any( |
493 | | target_endian = "little", |
494 | | target_arch = "aarch64", |
495 | | target_arch = "arm", |
496 | | target_arch = "arm64ec", |
497 | | ))] |
498 | | pub(crate) lo: T, |
499 | | pub(crate) hi: T, |
500 | | // big endian order |
501 | | #[cfg(not(any( |
502 | | target_endian = "little", |
503 | | target_arch = "aarch64", |
504 | | target_arch = "arm", |
505 | | target_arch = "arm64ec", |
506 | | )))] |
507 | | pub(crate) lo: T, |
508 | | } |
509 | | |
510 | | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] |
511 | | type MinWord = u32; |
512 | | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] |
513 | | type RetInt = u32; |
514 | | // Adapted from https://github.com/taiki-e/atomic-maybe-uninit/blob/v0.3.6/src/utils.rs#L255. |
515 | | // Helper for implementing sub-word atomic operations using word-sized LL/SC loop or CAS loop. |
516 | | // |
517 | | // Refs: https://github.com/llvm/llvm-project/blob/llvmorg-22.1.0-rc1/llvm/lib/CodeGen/AtomicExpandPass.cpp#L811 |
518 | | // (aligned_ptr, shift, mask) |
519 | | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] |
520 | | #[allow(dead_code)] |
521 | | #[inline] |
522 | | pub(crate) fn create_sub_word_mask_values<T>(ptr: *mut T) -> (*mut MinWord, RetInt, RetInt) { |
523 | | use core::mem; |
524 | | |
525 | | #[cfg(portable_atomic_no_strict_provenance)] |
526 | | use self::ptr::PtrExt as _; |
527 | | |
528 | | // RISC-V, MIPS, SPARC, LoongArch, Xtensa, BPF: shift amount of 32-bit shift instructions is 5 bits unsigned (0-31). |
529 | | // PowerPC, C-SKY: shift amount of 32-bit shift instructions is 6 bits unsigned (0-63) and shift amount 32-63 means "clear". |
530 | | // Arm: shift amount of 32-bit shift instructions is 8 bits unsigned (0-255). |
531 | | // Hexagon: shift amount of 32-bit shift instructions is 7 bits signed (-64-63) and negative shift amount means "reverse the direction of the shift". |
532 | | // (On s390x, we don't use the mask returned from this function.) |
533 | | // (See also https://devblogs.microsoft.com/oldnewthing/20230904-00/?p=108704 for others) |
534 | | const SHIFT_MASK: bool = !cfg!(any( |
535 | | target_arch = "bpf", |
536 | | target_arch = "loongarch32", |
537 | | target_arch = "loongarch64", |
538 | | target_arch = "mips", |
539 | | target_arch = "mips32r6", |
540 | | target_arch = "mips64", |
541 | | target_arch = "mips64r6", |
542 | | target_arch = "riscv32", |
543 | | target_arch = "riscv64", |
544 | | target_arch = "s390x", |
545 | | target_arch = "sparc", |
546 | | target_arch = "sparc64", |
547 | | target_arch = "xtensa", |
548 | | )); |
549 | | let ptr_mask = mem::size_of::<MinWord>() - 1; |
550 | | let aligned_ptr = ptr.with_addr(ptr.addr() & !ptr_mask) as *mut MinWord; |
551 | | let ptr_lsb = if SHIFT_MASK { |
552 | | ptr.addr() & ptr_mask |
553 | | } else { |
554 | | // We use 32-bit wrapping shift instructions in asm on these platforms. |
555 | | ptr.addr() |
556 | | }; |
557 | | let shift = if cfg!(any(target_endian = "little", target_arch = "s390x")) { |
558 | | ptr_lsb.wrapping_mul(8) |
559 | | } else { |
560 | | (ptr_lsb ^ (mem::size_of::<MinWord>() - mem::size_of::<T>())).wrapping_mul(8) |
561 | | }; |
562 | | let mut mask: RetInt = (1 << (mem::size_of::<T>() * 8)) - 1; // !(0 as T) as RetInt |
563 | | if SHIFT_MASK { |
564 | | mask <<= shift; |
565 | | } |
566 | | #[allow(clippy::cast_possible_truncation)] |
567 | | { |
568 | | (aligned_ptr, shift as RetInt, mask) |
569 | | } |
570 | | } |
571 | | |
572 | | // This module provides core::ptr strict_provenance/exposed_provenance polyfill for pre-1.84 rustc. |
573 | | #[allow(dead_code)] |
574 | | pub(crate) mod ptr { |
575 | | cfg_sel!({ |
576 | | #[cfg(not(portable_atomic_no_strict_provenance))] |
577 | | { |
578 | | #[allow(unused_imports)] |
579 | | pub(crate) use core::ptr::{ |
580 | | with_exposed_provenance, with_exposed_provenance_mut, without_provenance_mut, |
581 | | }; |
582 | | } |
583 | | #[cfg(else)] |
584 | | { |
585 | | use core::mem; |
586 | | |
587 | | #[inline(always)] |
588 | | #[must_use] |
589 | | pub(crate) const fn without_provenance_mut<T>(addr: usize) -> *mut T { |
590 | | // An int-to-pointer transmute currently has exactly the intended semantics: it creates a |
591 | | // pointer without provenance. Note that this is *not* a stable guarantee about transmute |
592 | | // semantics, it relies on sysroot crates having special status. |
593 | | // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that |
594 | | // pointer). |
595 | | #[cfg(miri)] |
596 | | unsafe { |
597 | | mem::transmute(addr) |
598 | | } |
599 | | // const transmute requires Rust 1.56. |
600 | | #[cfg(not(miri))] |
601 | | { |
602 | | addr as *mut T |
603 | | } |
604 | | } |
605 | | #[inline(always)] |
606 | | #[must_use] |
607 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
608 | | pub(crate) fn with_exposed_provenance<T>(addr: usize) -> *const T { |
609 | | addr as *const T |
610 | | } |
611 | | #[inline(always)] |
612 | | #[must_use] |
613 | | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
614 | | pub(crate) fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T { |
615 | | addr as *mut T |
616 | | } |
617 | | |
618 | | pub(crate) trait PtrExt<T: ?Sized>: Copy { |
619 | | #[must_use] |
620 | | fn addr(self) -> usize; |
621 | | #[must_use] |
622 | | fn with_addr(self, addr: usize) -> Self |
623 | | where |
624 | | T: Sized; |
625 | | } |
626 | | impl<T: ?Sized> PtrExt<T> for *mut T { |
627 | | #[inline(always)] |
628 | | #[must_use] |
629 | | fn addr(self) -> usize { |
630 | | // A pointer-to-integer transmute currently has exactly the right semantics: it returns the |
631 | | // address without exposing the provenance. Note that this is *not* a stable guarantee about |
632 | | // transmute semantics, it relies on sysroot crates having special status. |
633 | | // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the |
634 | | // provenance). |
635 | | unsafe { mem::transmute(self as *mut ()) } |
636 | | } |
637 | | #[inline] |
638 | | #[must_use] |
639 | | fn with_addr(self, addr: usize) -> Self |
640 | | where |
641 | | T: Sized, |
642 | | { |
643 | | // This should probably be an intrinsic to avoid doing any sort of arithmetic, but |
644 | | // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's |
645 | | // provenance. |
646 | | let self_addr = self.addr() as isize; |
647 | | let dest_addr = addr as isize; |
648 | | let offset = dest_addr.wrapping_sub(self_addr); |
649 | | (self as *mut u8).wrapping_offset(offset) as *mut T |
650 | | } |
651 | | } |
652 | | } |
653 | | }); |
654 | | } |
655 | | |
656 | | // This module provides: |
657 | | // - core::ffi polyfill (c_* type aliases and CStr) for pre-1.64 rustc compatibility. |
658 | | // (core::ffi::* (except c_void) requires Rust 1.64) |
659 | | // - safe abstraction (c! macro) for creating static C strings without runtime checks. |
660 | | // (c"..." requires Rust 1.77) |
661 | | // - helper macros for defining FFI bindings. |
662 | | #[cfg(any( |
663 | | test, |
664 | | portable_atomic_test_no_std_static_assert_ffi, |
665 | | not(any(target_arch = "x86", target_arch = "x86_64")) |
666 | | ))] |
667 | | #[cfg(any(not(portable_atomic_no_asm), portable_atomic_unstable_asm))] |
668 | | #[allow(dead_code, non_camel_case_types, unused_macros)] |
669 | | #[macro_use] |
670 | | pub(crate) mod ffi { |
671 | | pub(crate) type c_void = core::ffi::c_void; |
672 | | // c_{,u}int is {i,u}16 on 16-bit targets, otherwise {i,u}32. |
673 | | // https://github.com/rust-lang/rust/blob/1.84.0/library/core/src/ffi/mod.rs#L156 |
674 | | #[cfg(target_pointer_width = "16")] |
675 | | pub(crate) type c_int = i16; |
676 | | #[cfg(target_pointer_width = "16")] |
677 | | pub(crate) type c_uint = u16; |
678 | | #[cfg(not(target_pointer_width = "16"))] |
679 | | pub(crate) type c_int = i32; |
680 | | #[cfg(not(target_pointer_width = "16"))] |
681 | | pub(crate) type c_uint = u32; |
682 | | // c_{,u}long is {i,u}64 on non-Windows 64-bit targets, otherwise {i,u}32. |
683 | | // https://github.com/rust-lang/rust/blob/1.84.0/library/core/src/ffi/mod.rs#L168 |
684 | | #[cfg(all(target_pointer_width = "64", not(windows)))] |
685 | | pub(crate) type c_long = i64; |
686 | | #[cfg(all(target_pointer_width = "64", not(windows)))] |
687 | | pub(crate) type c_ulong = u64; |
688 | | #[cfg(not(all(target_pointer_width = "64", not(windows))))] |
689 | | pub(crate) type c_long = i32; |
690 | | #[cfg(not(all(target_pointer_width = "64", not(windows))))] |
691 | | pub(crate) type c_ulong = u32; |
692 | | // c_size_t is currently always usize. |
693 | | // https://github.com/rust-lang/rust/blob/1.84.0/library/core/src/ffi/mod.rs#L76 |
694 | | pub(crate) type c_size_t = usize; |
695 | | // c_char is u8 by default on non-Apple/non-Windows/non-Vita Arm/C-SKY/Hexagon/MSP430/PowerPC/RISC-V/s390x/Xtensa targets, otherwise i8 by default. |
696 | | // See references in https://github.com/rust-lang/rust/issues/129945 for details. |
697 | | cfg_sel!({ |
698 | | #[cfg(all( |
699 | | not(any(target_vendor = "apple", windows, target_os = "vita")), |
700 | | any( |
701 | | target_arch = "aarch64", |
702 | | target_arch = "arm", |
703 | | target_arch = "csky", |
704 | | target_arch = "hexagon", |
705 | | target_arch = "msp430", |
706 | | target_arch = "powerpc", |
707 | | target_arch = "powerpc64", |
708 | | target_arch = "riscv32", |
709 | | target_arch = "riscv64", |
710 | | target_arch = "s390x", |
711 | | target_arch = "xtensa", |
712 | | ), |
713 | | ))] |
714 | | { |
715 | | pub(crate) type c_char = u8; |
716 | | } |
717 | | #[cfg(else)] |
718 | | { |
719 | | pub(crate) type c_char = i8; |
720 | | } |
721 | | }); |
722 | | |
723 | | // Static assertions for C type definitions. |
724 | | #[cfg(test)] |
725 | | const _: fn() = || { |
726 | | let _: c_int = 0 as std::os::raw::c_int; |
727 | | let _: c_uint = 0 as std::os::raw::c_uint; |
728 | | let _: c_long = 0 as std::os::raw::c_long; |
729 | | let _: c_ulong = 0 as std::os::raw::c_ulong; |
730 | | #[cfg(unix)] |
731 | | let _: c_size_t = 0 as libc::size_t; // std::os::raw::c_size_t is unstable |
732 | | let _: c_char = 0 as std::os::raw::c_char; |
733 | | }; |
734 | | |
735 | | #[repr(transparent)] |
736 | | pub(crate) struct CStr([c_char]); |
737 | | impl CStr { |
738 | | #[inline] |
739 | | #[must_use] |
740 | | pub(crate) const fn as_ptr(&self) -> *const c_char { |
741 | | self.0.as_ptr() |
742 | | } |
743 | | /// # Safety |
744 | | /// |
745 | | /// The provided slice **must** be nul-terminated and not contain any interior |
746 | | /// nul bytes. |
747 | | #[inline] |
748 | | #[must_use] |
749 | | pub(crate) unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { |
750 | | // SAFETY: Casting to CStr is safe because *our* CStr is #[repr(transparent)] |
751 | | // and its internal representation is a [u8] too. (Note that std's CStr |
752 | | // is not #[repr(transparent)].) |
753 | | // Dereferencing the obtained pointer is safe because it comes from a |
754 | | // reference. Making a reference is then safe because its lifetime |
755 | | // is bound by the lifetime of the given `bytes`. |
756 | | unsafe { &*(bytes as *const [u8] as *const CStr) } |
757 | | } |
758 | | #[cfg(test)] |
759 | | #[inline] |
760 | | #[must_use] |
761 | | pub(crate) fn to_bytes_with_nul(&self) -> &[u8] { |
762 | | #[allow(clippy::unnecessary_cast)] // triggered for targets that c_char is u8 |
763 | | // SAFETY: Transmuting a slice of `c_char`s to a slice of `u8`s |
764 | | // is safe on all supported targets. |
765 | | unsafe { |
766 | | &*(&self.0 as *const [c_char] as *const [u8]) |
767 | | } |
768 | | } |
769 | | } |
770 | | |
771 | | macro_rules! c { |
772 | | ($s:expr) => {{ |
773 | | const BYTES: &[u8] = concat!($s, "\0").as_bytes(); |
774 | | const _: () = static_assert!(crate::utils::ffi::_const_is_c_str(BYTES)); |
775 | | #[allow(unused_unsafe)] |
776 | | // SAFETY: we've checked `BYTES` is a valid C string |
777 | | unsafe { |
778 | | crate::utils::ffi::CStr::from_bytes_with_nul_unchecked(BYTES) |
779 | | } |
780 | | }}; |
781 | | } |
782 | | |
783 | | #[must_use] |
784 | | pub(crate) const fn _const_is_c_str(bytes: &[u8]) -> bool { |
785 | | #[cfg(portable_atomic_no_track_caller)] |
786 | | { |
787 | | // const_if_match/const_loop was stabilized (nightly-2020-06-30) 2 days before |
788 | | // track_caller was stabilized (nightly-2020-07-02), so we reuse the cfg for |
789 | | // track_caller here instead of emitting a cfg for const_if_match/const_loop. |
790 | | // https://github.com/rust-lang/rust/pull/72437 |
791 | | // track_caller was stabilized 11 days after the oldest nightly version |
792 | | // that uses this module, and is included in the same 1.46 stable release. |
793 | | // The check here is insufficient in this case, but this is fine because this function |
794 | | // is internal code that is not used to process input from the user and our CI checks |
795 | | // all builtin targets and some custom targets with some versions of newer compilers. |
796 | | !bytes.is_empty() |
797 | | } |
798 | | #[cfg(not(portable_atomic_no_track_caller))] |
799 | | { |
800 | | // Based on https://github.com/rust-lang/rust/blob/1.84.0/library/core/src/ffi/c_str.rs#L417 |
801 | | // - bytes must be nul-terminated. |
802 | | // - bytes must not contain any interior nul bytes. |
803 | | if bytes.is_empty() { |
804 | | return false; |
805 | | } |
806 | | let mut i = bytes.len() - 1; |
807 | | if bytes[i] != 0 { |
808 | | return false; |
809 | | } |
810 | | // Ending null byte exists, skip to the rest. |
811 | | while i != 0 { |
812 | | i -= 1; |
813 | | if bytes[i] == 0 { |
814 | | return false; |
815 | | } |
816 | | } |
817 | | true |
818 | | } |
819 | | } |
820 | | |
821 | | /// Defines types with #[cfg(test)] static assertions which checks |
822 | | /// types are the same as the platform's latest header files' ones. |
823 | | // Note: This macro is sys_ty!({ }), not sys_ty! { }. |
824 | | // An extra brace is used in input to make contents rustfmt-able. |
825 | | macro_rules! sys_type { |
826 | | ({$( |
827 | | $(#[$attr:meta])* |
828 | | $vis:vis type $([$($windows_path:ident)::+])? $name:ident = $ty:ty; |
829 | | )*}) => { |
830 | | $( |
831 | | $(#[$attr])* |
832 | | $vis type $name = $ty; |
833 | | )* |
834 | | #[cfg(any(test, portable_atomic_test_no_std_static_assert_ffi))] |
835 | | test_helper::static_assert_sys_type!($( |
836 | | $(#[$attr])* |
837 | | type $([$($windows_path)::+])? $name; |
838 | | )*); |
839 | | }; |
840 | | } |
841 | | /// Defines #[repr(C)] structs with #[cfg(test)] static assertions which checks |
842 | | /// fields are the same as the platform's latest header files' ones. |
843 | | // Note: This macro is sys_struct!({ }), not sys_struct! { }. |
844 | | // An extra brace is used in input to make contents rustfmt-able. |
845 | | macro_rules! sys_struct { |
846 | | ({$( |
847 | | $(#[$attr:meta])* |
848 | | $vis:vis struct $([$($windows_path:ident)::+])? $name:ident {$( |
849 | | $(#[$field_attr:meta])* |
850 | | $field_vis:vis $field_name:ident: $field_ty:ty, |
851 | | )*} |
852 | | )*}) => { |
853 | | $( |
854 | | $(#[$attr])* |
855 | | #[derive(Clone, Copy)] |
856 | | #[cfg_attr( |
857 | | any(test, portable_atomic_test_no_std_static_assert_ffi), |
858 | | derive(Debug, PartialEq) |
859 | | )] |
860 | | #[repr(C)] |
861 | | $vis struct $name {$( |
862 | | $(#[$field_attr])* |
863 | | $field_vis $field_name: $field_ty, |
864 | | )*} |
865 | | )* |
866 | | #[cfg(any(test, portable_atomic_test_no_std_static_assert_ffi))] |
867 | | test_helper::static_assert_sys_struct!($( |
868 | | $(#[$attr])* |
869 | | struct $([$($windows_path)::+])? $name {$( |
870 | | $(#[$field_attr])* |
871 | | $field_name: $field_ty, |
872 | | )*} |
873 | | )*); |
874 | | }; |
875 | | } |
876 | | /// Defines constants with #[cfg(test)] static assertions which checks |
877 | | /// values are the same as the platform's latest header files' ones. |
878 | | // Note: This macro is sys_const!({ }), not sys_const! { }. |
879 | | // An extra brace is used in input to make contents rustfmt-able. |
880 | | macro_rules! sys_const { |
881 | | ({$( |
882 | | $(#[$attr:meta])* |
883 | | $vis:vis const $([$($windows_path:ident)::+])? $name:ident: $ty:ty = $val:expr; |
884 | | )*}) => { |
885 | | $( |
886 | | $(#[$attr])* |
887 | | $vis const $name: $ty = $val; |
888 | | )* |
889 | | #[cfg(any(test, portable_atomic_test_no_std_static_assert_ffi))] |
890 | | test_helper::static_assert_sys_const!($( |
891 | | $(#[$attr])* |
892 | | const $([$($windows_path)::+])? $name: $ty; |
893 | | )*); |
894 | | }; |
895 | | } |
896 | | /// Defines functions with #[cfg(test)] static assertions which checks |
897 | | /// signatures are the same as the platform's latest header files' ones. |
898 | | // Note: This macro is sys_fn!({ }), not sys_fn! { }. |
899 | | // An extra brace is used in input to make contents rustfmt-able. |
900 | | macro_rules! sys_fn { |
901 | | ({ |
902 | | $(#[$extern_attr:meta])* |
903 | | extern $abi:literal {$( |
904 | | $(#[$fn_attr:meta])* |
905 | | $vis:vis fn $([$($windows_path:ident)::+])? $name:ident( |
906 | | $($args:tt)* |
907 | | ) $(-> $ret_ty:ty)?; |
908 | | )*} |
909 | | }) => { |
910 | | $(#[$extern_attr])* |
911 | | extern $abi {$( |
912 | | $(#[$fn_attr])* |
913 | | $vis fn $name($($args)*) $(-> $ret_ty)?; |
914 | | )*} |
915 | | #[cfg(any(test, portable_atomic_test_no_std_static_assert_ffi))] |
916 | | test_helper::static_assert_sys_fn!( |
917 | | $(#[$extern_attr])* |
918 | | extern $abi {$( |
919 | | $(#[$fn_attr])* |
920 | | fn $([$($windows_path)::+])? $name($($args)*) $(-> $ret_ty)?; |
921 | | )*} |
922 | | ); |
923 | | }; |
924 | | } |
925 | | |
926 | | #[allow( |
927 | | clippy::alloc_instead_of_core, |
928 | | clippy::std_instead_of_alloc, |
929 | | clippy::std_instead_of_core, |
930 | | clippy::undocumented_unsafe_blocks, |
931 | | clippy::wildcard_imports |
932 | | )] |
933 | | #[cfg(test)] |
934 | | mod tests { |
935 | | #[test] |
936 | | fn test_c_macro() { |
937 | | #[track_caller] |
938 | | fn t(s: &crate::utils::ffi::CStr, raw: &[u8]) { |
939 | | assert_eq!(s.to_bytes_with_nul(), raw); |
940 | | } |
941 | | t(c!(""), b"\0"); |
942 | | t(c!("a"), b"a\0"); |
943 | | t(c!("abc"), b"abc\0"); |
944 | | t(c!(concat!("abc", "d")), b"abcd\0"); |
945 | | } |
946 | | |
947 | | #[test] |
948 | | fn test_is_c_str() { |
949 | | #[track_caller] |
950 | | fn t(bytes: &[u8]) { |
951 | | assert_eq!( |
952 | | super::_const_is_c_str(bytes), |
953 | | std::ffi::CStr::from_bytes_with_nul(bytes).is_ok() |
954 | | ); |
955 | | } |
956 | | t(b"\0"); |
957 | | t(b"a\0"); |
958 | | t(b"abc\0"); |
959 | | t(b""); |
960 | | t(b"a"); |
961 | | t(b"abc"); |
962 | | t(b"\0a"); |
963 | | t(b"\0a\0"); |
964 | | t(b"ab\0c\0"); |
965 | | t(b"\0\0"); |
966 | | } |
967 | | } |
968 | | } |