1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
use core::mem; use core::ops::Deref; use core::slice; use crate::private::Slice; /// Collection of static elements that are gathered into a contiguous section of /// the binary by the linker. /// /// The implementation is based on `link_section` attributes and /// platform-specific linker support. It does not involve life-before-main or /// any other runtime initialization on any platform. This is a zero-cost safe /// abstraction that operates entirely during compilation and linking. /// /// ## Declaration /// /// A static distributed slice may be declared by writing `#[distributed_slice]` /// on a static item whose type is `[T]` for some type `T`. The initializer /// expression must be `[..]` to indicate that elements come from elsewhere. /// /// ``` /// # struct Bencher; /// # /// use linkme::distributed_slice; /// /// #[distributed_slice] /// pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; /// ``` /// /// The attribute rewrites the `[T]` type of the static into /// `DistributedSlice<[T]>`, so the static in the example technically has type /// `DistributedSlice<[fn(&mut Bencher)]>`. /// /// ## Elements /// /// Slice elements may be registered into a distributed slice by a /// `#[distributed_slice(...)]` attribute in which the path to the distributed /// slice is given in the parentheses. The initializer is required to be a const /// expression. /// /// Elements may be defined in the same crate that declares the distributed /// slice, or in any downstream crate. Elements across all crates linked into /// the final binary will be observed to be present in the slice at runtime. /// /// ``` /// # mod other_crate { /// # use linkme::distributed_slice; /// # /// # pub struct Bencher; /// # /// # #[distributed_slice] /// # pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; /// # } /// # /// # use other_crate::Bencher; /// # /// use linkme::distributed_slice; /// use other_crate::BENCHMARKS; /// /// #[distributed_slice(BENCHMARKS)] /// static BENCH_DESERIALIZE: fn(&mut Bencher) = bench_deserialize; /// /// fn bench_deserialize(b: &mut Bencher) { /// /* ... */ /// } /// ``` /// /// The compiler will require that the static element type matches with the /// element type of the distributed slice. If the two do not match, the program /// will not compile. /// /// ```compile_fail /// # mod other_crate { /// # use linkme::distributed_slice; /// # /// # pub struct Bencher; /// # /// # #[distributed_slice] /// # pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; /// # } /// # /// # use linkme::distributed_slice; /// # use other_crate::BENCHMARKS; /// # /// #[distributed_slice(BENCHMARKS)] /// static BENCH_WTF: usize = 999; /// ``` /// /// ```text /// error[E0308]: mismatched types /// --> src/distributed_slice.rs:65:19 /// | /// 17 | static BENCH_WTF: usize = 999; /// | ^^^^^ expected fn pointer, found `usize` /// | /// = note: expected fn pointer `fn(&mut other_crate::Bencher)` /// found type `usize` /// ``` /// /// ## Function elements /// /// As a shorthand for the common case of distributed slices containing function /// pointers, the distributed\_slice attribute may be applied directly to a /// function definition to place a pointer to that function into a distributed /// slice. /// /// ``` /// # pub struct Bencher; /// # /// use linkme::distributed_slice; /// /// #[distributed_slice] /// pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; /// /// // Equivalent to: /// // /// // #[distributed_slice(BENCHMARKS)] /// // static _: fn(&mut Bencher) = bench_deserialize; /// // /// #[distributed_slice(BENCHMARKS)] /// fn bench_deserialize(b: &mut Bencher) { /// /* ... */ /// } /// ``` pub struct DistributedSlice<T: ?Sized + Slice> { start: StaticPtr<T::Element>, stop: StaticPtr<T::Element>, } struct StaticPtr<T> { ptr: *const T, } unsafe impl<T> Send for StaticPtr<T> {} unsafe impl<T> Sync for StaticPtr<T> {} impl<T> Copy for StaticPtr<T> {} impl<T> Clone for StaticPtr<T> { fn clone(&self) -> Self { *self } } impl<T> DistributedSlice<[T]> { #[doc(hidden)] #[cfg(any( target_os = "none", target_os = "linux", target_os = "macos", target_os = "illumos" ))] pub const unsafe fn private_new(start: *const T, stop: *const T) -> Self { DistributedSlice { start: StaticPtr { ptr: start }, stop: StaticPtr { ptr: stop }, } } #[doc(hidden)] #[cfg(target_os = "windows")] pub const unsafe fn private_new(start: *const (), stop: *const ()) -> Self { DistributedSlice { start: StaticPtr { ptr: start as *const T, }, stop: StaticPtr { ptr: stop as *const T, }, } } #[doc(hidden)] #[inline] pub unsafe fn private_typecheck(self, element: T) { mem::forget(element); } } impl<T> DistributedSlice<[T]> { /// Retrieve a contiguous slice containing all the elements linked into this /// program. /// /// **Note**: Ordinarily this method should not need to be called because /// `DistributedSlice<[T]>` already behaves like `&'static [T]` in most ways /// through the power of `Deref`. In particular, iteration and indexing and /// method calls can all happen directly on the static without calling /// `static_slice()`. /// /// ```no_run /// # struct Bencher; /// # /// use linkme::distributed_slice; /// /// #[distributed_slice] /// static BENCHMARKS: [fn(&mut Bencher)] = [..]; /// /// fn main() { /// // Iterate the elements. /// for bench in BENCHMARKS { /// /* ... */ /// } /// /// // Index into the elements. /// let first = BENCHMARKS[0]; /// /// // Slice the elements. /// let except_first = &BENCHMARKS[1..]; /// /// // Invoke methods on the underlying slice. /// let len = BENCHMARKS.len(); /// } /// ``` pub fn static_slice(self) -> &'static [T] { let stride = mem::size_of::<T>(); if stride == 0 { // We could make this work by storing a 1-byte companion entry in a // different link_section and using that count as the len if anyone // requires this to work, but for now just: return &[]; } let start = self.start.ptr; let stop = self.stop.ptr; let byte_offset = stop as usize - start as usize; let len = byte_offset / stride; unsafe { slice::from_raw_parts(start, len) } } } impl<T> Copy for DistributedSlice<[T]> {} impl<T> Clone for DistributedSlice<[T]> { fn clone(&self) -> Self { *self } } impl<T: 'static> Deref for DistributedSlice<[T]> { type Target = [T]; fn deref(&self) -> &'static Self::Target { self.static_slice() } } impl<T: 'static> IntoIterator for DistributedSlice<[T]> { type Item = &'static T; type IntoIter = slice::Iter<'static, T>; fn into_iter(self) -> Self::IntoIter { self.static_slice().iter() } }