1use std::marker::PhantomData;
9
10use super::vtable::VirtualReturn;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
15#[repr(C)]
16pub struct Pair<A, B> {
17 pub first: A,
18 pub second: B,
19}
20
21impl<A, B> VirtualReturn for Pair<A, B> {
22 type Raw = Self;
23 fn from_raw(raw: Self) -> Self {
24 raw
25 }
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32#[repr(C)]
33pub struct Span<T> {
34 pub data: *mut T,
35 pub size: usize,
36}
37
38impl<T> Span<T> {
39 #[must_use]
41 pub fn of(items: &mut [T]) -> Self {
42 Self {
43 data: items.as_mut_ptr(),
44 size: items.len(),
45 }
46 }
47}
48
49#[derive(Debug, Clone, Copy)]
52#[repr(C)]
53pub struct HybridStringDynamicStorage {
54 pub ptr: *mut u8,
55 pub free: Option<unsafe extern "C" fn(*mut std::ffi::c_void)>,
56}
57
58#[derive(Clone, Copy)]
60#[repr(C)]
61pub union HybridStringStorage<const N: usize> {
62 pub dynamic: HybridStringDynamicStorage,
63 pub inline: [u8; N],
64}
65
66#[repr(C)]
73pub struct HybridString<const N: usize> {
74 pub len_dynamic: usize,
75 pub storage: HybridStringStorage<N>,
76}
77
78impl<const N: usize> HybridString<N> {
79 #[must_use]
82 pub fn new(text: &str) -> Option<Self> {
83 let bytes = text.as_bytes();
84 if bytes.len() >= N {
85 return None;
86 }
87 let mut inline = [0u8; N];
88 inline[..bytes.len()].copy_from_slice(bytes);
89 Some(Self {
90 len_dynamic: bytes.len() << 1,
91 storage: HybridStringStorage { inline },
92 })
93 }
94
95 #[must_use]
97 pub fn is_dynamic(&self) -> bool {
98 self.len_dynamic & 1 != 0
99 }
100
101 #[must_use]
103 pub fn len(&self) -> usize {
104 self.len_dynamic >> 1
105 }
106
107 #[must_use]
109 pub fn is_empty(&self) -> bool {
110 self.len() == 0
111 }
112
113 #[must_use]
119 pub unsafe fn as_bytes(&self) -> &[u8] {
120 let len = self.len();
121 if self.is_dynamic() {
122 let ptr = unsafe { self.storage.dynamic.ptr };
123 if ptr.is_null() {
124 return &[];
125 }
126 unsafe { std::slice::from_raw_parts(ptr, len) }
127 } else {
128 let inline = unsafe { &self.storage.inline };
129 &inline[..len.min(N)]
130 }
131 }
132
133 #[must_use]
138 pub unsafe fn to_string_lossy(&self) -> String {
139 String::from_utf8_lossy(unsafe { self.as_bytes() }).into_owned()
140 }
141}
142
143impl<const N: usize> Default for HybridString<N> {
144 fn default() -> Self {
145 Self {
146 len_dynamic: 0,
147 storage: HybridStringStorage { inline: [0; N] },
148 }
149 }
150}
151
152#[repr(C)]
156pub struct FlatSet<T> {
157 _opaque: [u8; 0],
158 _item: PhantomData<*mut T>,
159}
160
161#[cfg(not(target_env = "msvc"))]
165mod set_layout {
166 pub const KEY_VALS: usize = 8;
167 pub const INFO: usize = 12;
168 pub const NUM_ELEMENTS: usize = 16;
169 pub const MASK: usize = 20;
170}
171
172#[cfg(target_env = "msvc")]
173mod set_layout {
174 pub const KEY_VALS: usize = 16;
175 pub const INFO: usize = 20;
176 pub const NUM_ELEMENTS: usize = 24;
177 pub const MASK: usize = 28;
178}
179
180fn buckets(mask: usize) -> Option<usize> {
185 let slots = mask.checked_add(1)?;
186 let allowed = if slots <= usize::MAX / 100 {
187 slots * 80 / 100
188 } else {
189 (slots / 100) * 80
190 };
191 slots.checked_add(allowed.min(0xff))
192}
193
194#[must_use]
205pub unsafe fn flat_set_entries<T>(set: *const FlatSet<T>) -> Vec<*mut T> {
206 if set.is_null() {
207 return Vec::new();
208 }
209 let base = set.cast::<u8>();
210 let read_usize = |offset: usize| unsafe { base.add(offset).cast::<usize>().read_unaligned() };
211 let read_ptr = |offset: usize| unsafe { base.add(offset).cast::<*const u8>().read_unaligned() };
212
213 let count = read_usize(set_layout::NUM_ELEMENTS);
214 let mask = read_usize(set_layout::MASK);
215 let key_vals = read_ptr(set_layout::KEY_VALS).cast::<*mut T>();
216 let info = read_ptr(set_layout::INFO);
217 if count == 0 || key_vals.is_null() || info.is_null() {
218 return Vec::new();
219 }
220 let Some(bound) = buckets(mask) else {
221 return Vec::new();
222 };
223 if count > bound {
224 return Vec::new();
225 }
226
227 let mut found = Vec::with_capacity(count);
228 for i in 0..bound {
229 if found.len() == count {
230 break;
231 }
232 if unsafe { info.add(i).read() } != 0 {
233 found.push(unsafe { key_vals.add(i).read() });
234 }
235 }
236 if found.len() == count {
237 found
238 } else {
239 Vec::new()
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[cfg(target_arch = "x86")]
249 #[test]
250 fn layouts_match_clang() {
251 use std::mem::{offset_of, size_of};
252 assert_eq!(size_of::<Span<u8>>(), 8);
253 assert_eq!(offset_of!(Span<u8>, size), 4);
254 assert_eq!(size_of::<Pair<i32, i32>>(), 8);
255 assert_eq!(size_of::<Pair<bool, crate::omp::StringView>>(), 12);
256 assert_eq!(offset_of!(Pair<bool, crate::omp::StringView>, second), 4);
257 assert_eq!(size_of::<HybridString<16>>(), 20);
258 assert_eq!(size_of::<HybridString<46>>(), 52);
259 assert_eq!(offset_of!(HybridString<16>, storage), 4);
260 }
261
262 #[test]
263 fn hybrid_string_round_trips_inline() {
264 let s = HybridString::<16>::new("hello").unwrap();
265 assert!(!s.is_dynamic());
266 assert_eq!(s.len(), 5);
267 assert_eq!(unsafe { s.to_string_lossy() }, "hello");
268 assert!(HybridString::<16>::new("123456789012345").is_some());
270 assert!(HybridString::<16>::new("1234567890123456").is_none());
271 }
272
273 #[test]
274 fn hybrid_string_reads_a_heap_copy() {
275 let mut bytes = *b"far too long for sixteen";
276 let s = HybridString::<16> {
277 len_dynamic: (bytes.len() << 1) | 1,
278 storage: HybridStringStorage {
279 dynamic: HybridStringDynamicStorage {
280 ptr: bytes.as_mut_ptr(),
281 free: None,
282 },
283 },
284 };
285 assert_eq!(unsafe { s.to_string_lossy() }, "far too long for sixteen");
286 }
287
288 #[repr(C, align(8))]
292 struct FakeSet([*const u8; 16]);
293
294 fn fake(count: usize, mask: usize, keys: &[*mut i32], info: &[u8]) -> FakeSet {
295 let mut raw = FakeSet([std::ptr::null(); 16]);
296 let base = raw.0.as_mut_ptr().cast::<u8>();
297 unsafe {
299 base.add(set_layout::KEY_VALS)
300 .cast::<*const *mut i32>()
301 .write_unaligned(keys.as_ptr());
302 base.add(set_layout::INFO)
303 .cast::<*const u8>()
304 .write_unaligned(info.as_ptr());
305 base.add(set_layout::NUM_ELEMENTS)
306 .cast::<usize>()
307 .write_unaligned(count);
308 base.add(set_layout::MASK)
309 .cast::<usize>()
310 .write_unaligned(mask);
311 }
312 raw
313 }
314
315 #[cfg(target_arch = "x86")]
317 #[test]
318 fn walks_the_occupied_buckets() {
319 let (mut a, mut b, mut c) = (1, 2, 3);
320 let n = std::ptr::null_mut();
321 let keys = [n, &raw mut a, n, n, &raw mut b, n, &raw mut c, n, n];
322 let info = [0u8, 1, 0, 0, 1, 0, 2, 0, 1];
323 let raw = fake(3, 7, &keys, &info);
324 let got = unsafe { flat_set_entries((&raw const raw).cast::<FlatSet<i32>>()) };
325 assert_eq!(got, vec![&raw mut a, &raw mut b, &raw mut c]);
326 }
327
328 #[test]
329 fn fails_closed_when_the_count_disagrees() {
330 let mut a = 1;
331 let keys = [&raw mut a, std::ptr::null_mut()];
332 let info = [1u8, 0];
333 let raw = fake(5, 0, &keys, &info);
336 let got = unsafe { flat_set_entries((&raw const raw).cast::<FlatSet<i32>>()) };
337 assert!(got.is_empty());
338 }
339
340 #[test]
341 fn bucket_count_follows_robin_hood() {
342 assert_eq!(buckets(0), Some(1));
343 assert_eq!(buckets(7), Some(14));
344 assert_eq!(buckets(1023), Some(1024 + 255));
345 }
346
347 #[test]
348 fn a_null_set_is_empty() {
349 assert!(unsafe { flat_set_entries(std::ptr::null::<FlatSet<i32>>()) }.is_empty());
350 }
351}