Skip to main content

cgc_single_threaded/
mem.rs

1use std::cmp::*;
2use std::fmt;
3static mut PAGE_SIZE: usize = 0;
4static mut PAGE_SIZE_BITS: usize = 0;
5
6pub fn page_size() -> usize {
7    let result = unsafe { PAGE_SIZE };
8
9    if result != 0 {
10        return result;
11    }
12
13    init_page_size();
14
15    unsafe { PAGE_SIZE }
16}
17
18pub fn page_size_bits() -> usize {
19    let result = unsafe { PAGE_SIZE_BITS };
20
21    if result != 0 {
22        return result;
23    }
24
25    init_page_size();
26
27    unsafe { PAGE_SIZE_BITS }
28}
29
30fn init_page_size() {
31    unsafe {
32        PAGE_SIZE = determine_page_size();
33        assert!((PAGE_SIZE & (PAGE_SIZE - 1)) == 0);
34
35        PAGE_SIZE_BITS = log2(PAGE_SIZE);
36    }
37}
38
39pub fn map_gc_mem() -> Address {
40    commit(memory_limit(), false)
41}
42
43#[cfg(target_family = "unix")]
44pub fn memory_limit() -> usize {
45    unsafe {
46        use libc::*;
47        sysconf(_SC_PHYS_PAGES) as usize * sysconf(_SC_PAGESIZE) as usize
48    }
49}
50
51#[cfg(target_family = "windows")]
52pub(crate) fn memory_limit() -> usize {
53    unimplemented!()
54}
55
56#[cfg(target_family = "unix")]
57fn determine_page_size() -> usize {
58    let val = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
59
60    if val <= 0 {
61        panic!("could not determine page size.");
62    }
63
64    val as usize
65}
66
67#[cfg(target_family = "windows")]
68fn determine_page_size() -> usize {
69    use winapi::um::sysinfoapi::{GetSystemInfo, LPSYSTEM_INFO, SYSTEM_INFO};
70
71    unsafe {
72        let mut system_info: SYSTEM_INFO = std::mem::zeroed();
73        GetSystemInfo(&mut system_info as LPSYSTEM_INFO);
74
75        system_info.dwPageSize as usize
76    }
77}
78
79/// determine log_2 of given value
80fn log2(mut val: usize) -> usize {
81    let mut log = 0;
82    assert!(val <= u32::max_value() as usize);
83
84    if (val & 0xFFFF0000) != 0 {
85        val >>= 16;
86        log += 16;
87    }
88    if val >= 256 {
89        val >>= 8;
90        log += 8;
91    }
92    if val >= 16 {
93        val >>= 4;
94        log += 4;
95    }
96    if val >= 4 {
97        val >>= 2;
98        log += 2;
99    }
100
101    log + (val >> 1)
102}
103
104#[test]
105fn test_log2() {
106    for i in 0..32 {
107        assert_eq!(i, log2(1 << i));
108    }
109}
110use std::i32;
111use std::mem::size_of;
112/// return pointer width: either 4 or 8
113/// (although only 64bit architectures are supported right now)
114#[inline(always)]
115pub fn ptr_width() -> i32 {
116    size_of::<*const u8>() as i32
117}
118
119#[inline(always)]
120pub fn ptr_width_usize() -> usize {
121    size_of::<*const u8>() as usize
122}
123
124/// returns true if given value is a multiple of a page size.
125pub fn is_page_aligned(val: usize) -> bool {
126    let align = page_size_bits();
127
128    // we can use shifts here since we know that
129    // page size is power of 2
130    val == ((val >> align) << align)
131}
132
133#[test]
134fn test_is_page_aligned() {
135    let p = page_size();
136
137    assert_eq!(false, is_page_aligned(1));
138    assert_eq!(false, is_page_aligned(2));
139    assert_eq!(false, is_page_aligned(64));
140    assert_eq!(true, is_page_aligned(p));
141    assert_eq!(true, is_page_aligned(2 * p));
142    assert_eq!(true, is_page_aligned(3 * p));
143}
144
145/// round the given value up to the nearest multiple of a page
146pub fn page_align(val: usize) -> usize {
147    let align = page_size_bits();
148
149    // we know that page size is power of 2, hence
150    // we can use shifts instead of expensive division
151    ((val + (1 << align) - 1) >> align) << align
152}
153
154#[test]
155fn test_page_align() {
156    let p = page_size();
157
158    assert_eq!(p, page_align(1));
159    assert_eq!(p, page_align(p - 1));
160    assert_eq!(p, page_align(p));
161    assert_eq!(2 * p, page_align(p + 1));
162}
163
164/// rounds the given value `val` up to the nearest multiple
165/// of `align`
166pub fn align(value: u32, align: u32) -> u32 {
167    if align == 0 {
168        return value;
169    }
170
171    ((value + align - 1) / align) * align
172}
173
174/// rounds the given value `val` up to the nearest multiple
175/// of `align`
176pub fn align_i32(value: i32, align: i32) -> i32 {
177    if align == 0 {
178        return value;
179    }
180
181    ((value + align - 1) / align) * align
182}
183
184/// rounds the given value `val` up to the nearest multiple
185/// of `align`.
186pub fn align_usize(value: usize, align: usize) -> usize {
187    if align == 0 {
188        return value;
189    }
190
191    ((value + align - 1) / align) * align
192}
193
194/// returns 'true' if th given `value` is already aligned
195/// to `align`.
196pub fn is_aligned(value: usize, align: usize) -> bool {
197    align_usize(value, align) == value
198}
199
200/// returns true if value fits into u8 (unsigned 8bits).
201pub fn fits_u8(value: i64) -> bool {
202    0 <= value && value <= 255
203}
204
205/// returns true if value fits into i32 (signed 32bits).
206pub fn fits_i32(value: i64) -> bool {
207    i32::MIN as i64 <= value && value <= i32::MAX as i64
208}
209use super::*;
210use std::ptr;
211#[cfg(test)]
212mod tests {
213    use super::*;
214
215    #[test]
216    fn test_fits_u8() {
217        assert_eq!(true, fits_u8(0));
218        assert_eq!(true, fits_u8(255));
219        assert_eq!(false, fits_u8(256));
220        assert_eq!(false, fits_u8(-1));
221    }
222
223    #[test]
224    fn test_fits_i32() {
225        assert_eq!(true, fits_i32(0));
226        assert_eq!(true, fits_i32(i32::MAX as i64));
227        assert_eq!(true, fits_i32(i32::MIN as i64));
228        assert_eq!(false, fits_i32(i32::MAX as i64 + 1));
229        assert_eq!(false, fits_i32(i32::MIN as i64 - 1));
230    }
231}
232
233#[cfg(target_family = "unix")]
234pub fn reserve(size: usize) -> Address {
235    debug_assert!(mem::is_page_aligned(size));
236
237    let ptr = unsafe {
238        libc::mmap(
239            ptr::null_mut(),
240            size,
241            libc::PROT_NONE,
242            libc::MAP_PRIVATE | libc::MAP_ANON | libc::MAP_NORESERVE,
243            -1,
244            0,
245        ) as *mut libc::c_void
246    };
247
248    if ptr == libc::MAP_FAILED {
249        panic!("reserving memory with mmap() failed");
250    }
251
252    Address::from_ptr(ptr)
253}
254
255#[cfg(target_family = "windows")]
256pub fn reserve(size: usize) -> Address {
257    debug_assert!(mem::is_page_aligned(size));
258
259    use kernel32::VirtualAlloc;
260    use winapi::um::winnt::{MEM_RESERVE, PAGE_NOACCESS};
261
262    let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size as u64, MEM_RESERVE, PAGE_NOACCESS) };
263
264    if ptr.is_null() {
265        panic!("VirtualAlloc failed");
266    }
267
268    Address::from_ptr(ptr)
269}
270
271pub fn reserve_align(size: usize, align: usize) -> Address {
272    debug_assert!(mem::is_page_aligned(size));
273    debug_assert!(mem::is_page_aligned(align));
274
275    let align_minus_page = align - page_size();
276
277    let unaligned = reserve(size + align_minus_page);
278    let aligned: Address = mem::align_usize(unaligned.to_usize(), align).into();
279
280    let gap_start = aligned.offset_from(unaligned);
281    let gap_end = align_minus_page - gap_start;
282
283    if gap_start > 0 {
284        uncommit(unaligned, gap_start);
285    }
286
287    if gap_end > 0 {
288        uncommit(aligned.offset(size), gap_end);
289    }
290
291    aligned
292}
293
294#[cfg(target_family = "unix")]
295pub fn commit(size: usize, executable: bool) -> Address {
296    debug_assert!(mem::is_page_aligned(size));
297
298    let mut prot = libc::PROT_READ | libc::PROT_WRITE;
299
300    if executable {
301        prot |= libc::PROT_EXEC;
302    }
303
304    let ptr = unsafe {
305        libc::mmap(
306            ptr::null_mut(),
307            size,
308            prot,
309            libc::MAP_PRIVATE | libc::MAP_ANON,
310            -1,
311            0,
312        )
313    };
314
315    if ptr == libc::MAP_FAILED {
316        panic!("committing memory with mmap() failed");
317    }
318
319    Address::from_ptr(ptr)
320}
321
322#[cfg(target_family = "windows")]
323pub fn commit(size: usize, executable: bool) -> Address {
324    debug_assert!(mem::is_page_aligned(size));
325
326    use kernel32::VirtualAlloc;
327    use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE, PAGE_READWRITE};
328
329    let prot = if executable {
330        PAGE_EXECUTE_READWRITE
331    } else {
332        PAGE_READWRITE
333    };
334
335    let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size as u64, MEM_COMMIT | MEM_RESERVE, prot) };
336
337    if ptr.is_null() {
338        panic!("VirtualAlloc failed");
339    }
340
341    Address::from_ptr(ptr)
342}
343
344#[cfg(target_family = "unix")]
345pub fn commit_at(ptr: Address, size: usize, executable: bool) {
346    debug_assert!(ptr.is_page_aligned());
347    debug_assert!(mem::is_page_aligned(size));
348
349    let mut prot = libc::PROT_READ | libc::PROT_WRITE;
350
351    if executable {
352        prot |= libc::PROT_EXEC;
353    }
354
355    let val = unsafe {
356        libc::mmap(
357            ptr.to_mut_ptr(),
358            size,
359            prot,
360            libc::MAP_PRIVATE | libc::MAP_ANON | libc::MAP_FIXED,
361            -1,
362            0,
363        )
364    };
365
366    if val == libc::MAP_FAILED {
367        panic!("committing memory with mmap() failed");
368    }
369}
370
371#[cfg(target_family = "windows")]
372pub fn commit_at(ptr: Address, size: usize, executable: bool) {
373    debug_assert!(ptr.is_page_aligned());
374    debug_assert!(mem::is_page_aligned(size));
375
376    use kernel32::VirtualAlloc;
377    use winapi::um::winnt::{MEM_COMMIT, PAGE_EXECUTE_READWRITE, PAGE_READWRITE};
378
379    let prot = if executable {
380        PAGE_EXECUTE_READWRITE
381    } else {
382        PAGE_READWRITE
383    };
384
385    let result = unsafe { VirtualAlloc(ptr.to_mut_ptr(), size as u64, MEM_COMMIT, prot) };
386
387    if result != ptr.to_mut_ptr() {
388        panic!("VirtualAlloc failed");
389    }
390}
391
392#[cfg(target_family = "unix")]
393pub fn uncommit(ptr: Address, size: usize) {
394    debug_assert!(ptr.is_page_aligned());
395    debug_assert!(mem::is_page_aligned(size));
396
397    let val = unsafe {
398        libc::mmap(
399            ptr.to_mut_ptr(),
400            size,
401            libc::PROT_NONE,
402            libc::MAP_PRIVATE | libc::MAP_ANON | libc::MAP_NORESERVE,
403            -1,
404            0,
405        )
406    };
407
408    if val == libc::MAP_FAILED {
409        panic!("uncommitting memory with mmap() failed");
410    }
411}
412
413#[cfg(target_family = "windows")]
414pub fn uncommit(ptr: Address, size: usize) {
415    debug_assert!(ptr.is_page_aligned());
416    debug_assert!(mem::is_page_aligned(size));
417
418    use kernel32::VirtualFree;
419    use winapi::um::winnt::MEM_RELEASE;
420
421    let _ = unsafe { VirtualFree(ptr.to_mut_ptr(), size as _, MEM_RELEASE) };
422}
423
424#[cfg(target_family = "unix")]
425pub fn discard(ptr: Address, size: usize) {
426    debug_assert!(ptr.is_page_aligned());
427    debug_assert!(mem::is_page_aligned(size));
428
429    let res = unsafe { libc::madvise(ptr.to_mut_ptr(), size, libc::MADV_DONTNEED) };
430
431    if res != 0 {
432        panic!("discarding memory with madvise() failed");
433    }
434
435    let res = unsafe { libc::mprotect(ptr.to_mut_ptr(), size, libc::PROT_NONE) };
436
437    if res != 0 {
438        panic!("discarding memory with mprotect() failed");
439    }
440}
441
442#[cfg(target_family = "windows")]
443pub fn discard(ptr: Address, size: usize) {
444    debug_assert!(ptr.is_page_aligned());
445    debug_assert!(mem::is_page_aligned(size));
446
447    use kernel32::VirtualFree;
448    use winapi::um::winnt::MEM_DECOMMIT;
449
450    let _ = unsafe { VirtualFree(ptr.to_mut_ptr(), size as u64, MEM_DECOMMIT) };
451}
452
453#[cfg(target_family = "unix")]
454pub fn protect(start: Address, size: usize, access: Access) {
455    debug_assert!(start.is_page_aligned());
456    debug_assert!(mem::is_page_aligned(size));
457
458    if access.is_none() {
459        discard(start, size);
460        return;
461    }
462
463    let protection = match access {
464        Access::None => unreachable!(),
465        Access::Read => libc::PROT_READ,
466        Access::ReadWrite => libc::PROT_READ | libc::PROT_WRITE,
467        Access::ReadExecutable => libc::PROT_READ | libc::PROT_EXEC,
468        Access::ReadWriteExecutable => libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC,
469    };
470
471    let res = unsafe { libc::mprotect(start.to_mut_ptr(), size, protection) };
472
473    if res != 0 {
474        panic!("mprotect() failed");
475    }
476}
477
478#[cfg(target_family = "windows")]
479pub fn protect(start: Address, size: usize, access: Access) {
480    debug_assert!(start.is_page_aligned());
481    debug_assert!(mem::is_page_aligned(size));
482
483    use kernel32::VirtualAlloc;
484    use winapi::um::winnt::{
485        MEM_COMMIT, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_READONLY, PAGE_READWRITE,
486    };
487
488    if access.is_none() {
489        discard(start, size);
490        return;
491    }
492
493    let protection = match access {
494        Access::None => unreachable!(),
495        Access::Read => PAGE_READONLY,
496        Access::ReadWrite => PAGE_READWRITE,
497        Access::ReadExecutable => PAGE_EXECUTE_READ,
498        Access::ReadWriteExecutable => PAGE_EXECUTE_READWRITE,
499    };
500
501    let ptr = unsafe { VirtualAlloc(start.to_mut_ptr(), size as u64, MEM_COMMIT, protection) };
502
503    if ptr.is_null() {
504        panic!("VirtualAlloc failed");
505    }
506}
507
508pub enum Access {
509    None,
510    Read,
511    ReadWrite,
512    ReadExecutable,
513    ReadWriteExecutable,
514}
515
516impl Access {
517    fn is_none(&self) -> bool {
518        match self {
519            Access::None => true,
520            _ => false,
521        }
522    }
523}
524
525#[derive(Copy, Clone, PartialEq, Eq, Hash)]
526pub struct Address(usize);
527
528impl Address {
529    pub fn deref(self) -> Self {
530        unsafe { *(self.offset(0).to_mut_ptr::<Self>()) }
531    }
532
533    #[inline(always)]
534    pub fn from(val: usize) -> Address {
535        Address(val)
536    }
537
538    #[inline(always)]
539    pub fn region_start(self, size: usize) -> Region {
540        Region::new(self, self.offset(size))
541    }
542
543    #[inline(always)]
544    pub fn offset_from(self, base: Address) -> usize {
545        debug_assert!(self >= base);
546
547        self.to_usize() - base.to_usize()
548    }
549
550    #[inline(always)]
551    pub fn offset(self, offset: usize) -> Address {
552        Address(self.0 + offset)
553    }
554
555    #[inline(always)]
556    pub fn sub(self, offset: usize) -> Address {
557        Address(self.0 - offset)
558    }
559
560    #[inline(always)]
561    pub fn add_ptr(self, words: usize) -> Address {
562        Address(self.0 + words * mem::ptr_width_usize())
563    }
564
565    #[inline(always)]
566    pub fn sub_ptr(self, words: usize) -> Address {
567        Address(self.0 - words * mem::ptr_width_usize())
568    }
569
570    #[inline(always)]
571    pub fn to_usize(self) -> usize {
572        self.0
573    }
574
575    #[inline(always)]
576    pub fn from_ptr<T>(ptr: *const T) -> Address {
577        Address(ptr as usize)
578    }
579
580    #[inline(always)]
581    pub fn to_ptr<T>(&self) -> *const T {
582        self.0 as *const T
583    }
584
585    #[inline(always)]
586    pub fn to_mut_ptr<T>(&self) -> *mut T {
587        self.0 as *const T as *mut T
588    }
589
590    #[inline(always)]
591    pub fn null() -> Address {
592        Address(0)
593    }
594
595    #[inline(always)]
596    pub fn is_null(self) -> bool {
597        self.0 == 0
598    }
599
600    #[inline(always)]
601    pub fn is_non_null(self) -> bool {
602        self.0 != 0
603    }
604
605    #[inline(always)]
606    pub fn align_page(self) -> Address {
607        mem::page_align(self.to_usize()).into()
608    }
609
610    #[inline(always)]
611    pub fn align_page_down(self) -> Address {
612        Address(self.0 & !(mem::page_size() - 1))
613    }
614
615    #[inline(always)]
616    pub fn is_page_aligned(self) -> bool {
617        mem::is_page_aligned(self.to_usize())
618    }
619
620    #[inline(always)]
621    pub const fn and(self, x: Address) -> Self {
622        Self(self.0 & x.0)
623    }
624
625    #[inline(always)]
626    pub const fn or(self, x: Address) -> Self {
627        Self(self.0 | x.0)
628    }
629}
630
631impl fmt::Display for Address {
632    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
633        write!(f, "0x{:x}", self.to_usize())
634    }
635}
636
637impl fmt::Debug for Address {
638    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
639        write!(f, "0x{:x}", self.to_usize())
640    }
641}
642
643impl PartialOrd for Address {
644    fn partial_cmp(&self, other: &Address) -> Option<std::cmp::Ordering> {
645        Some(self.cmp(other))
646    }
647}
648
649impl Ord for Address {
650    fn cmp(&self, other: &Address) -> std::cmp::Ordering {
651        self.to_usize().cmp(&other.to_usize())
652    }
653}
654
655impl From<usize> for Address {
656    fn from(val: usize) -> Address {
657        Address(val)
658    }
659}
660
661#[derive(Copy, Clone)]
662pub struct Region {
663    pub start: Address,
664    pub end: Address,
665}
666
667impl Region {
668    pub fn new(start: Address, end: Address) -> Region {
669        debug_assert!(start <= end);
670
671        Region { start, end }
672    }
673
674    #[inline(always)]
675    pub fn contains(&self, addr: Address) -> bool {
676        self.start <= addr && addr < self.end
677    }
678
679    #[inline(always)]
680    pub fn valid_top(&self, addr: Address) -> bool {
681        self.start <= addr && addr <= self.end
682    }
683
684    #[inline(always)]
685    pub fn size(&self) -> usize {
686        self.end.to_usize() - self.start.to_usize()
687    }
688
689    #[inline(always)]
690    pub fn empty(&self) -> bool {
691        self.start == self.end
692    }
693
694    #[inline(always)]
695    pub fn disjunct(&self, other: &Region) -> bool {
696        self.end <= other.start || self.start >= other.end
697    }
698
699    #[inline(always)]
700    pub fn overlaps(&self, other: &Region) -> bool {
701        !self.disjunct(other)
702    }
703
704    #[inline(always)]
705    pub fn fully_contains(&self, other: &Region) -> bool {
706        self.contains(other.start) && self.valid_top(other.end)
707    }
708}
709
710impl Default for Region {
711    fn default() -> Region {
712        Region {
713            start: Address::null(),
714            end: Address::null(),
715        }
716    }
717}
718
719impl fmt::Display for Region {
720    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
721        write!(f, "{}-{}", self.start, self.end)
722    }
723}
724
725pub struct FormattedSize {
726    size: usize,
727}
728
729impl fmt::Display for FormattedSize {
730    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
731        let ksize = (self.size as f64) / 1024f64;
732
733        if ksize < 1f64 {
734            return write!(f, "{}B", self.size);
735        }
736
737        let msize = ksize / 1024f64;
738
739        if msize < 1f64 {
740            return write!(f, "{:.1}K", ksize);
741        }
742
743        let gsize = msize / 1024f64;
744
745        if gsize < 1f64 {
746            write!(f, "{:.1}M", msize)
747        } else {
748            write!(f, "{:.1}G", gsize)
749        }
750    }
751}
752
753pub fn formatted_size(size: usize) -> FormattedSize {
754    FormattedSize { size }
755}
756
757#[repr(transparent)]
758pub struct Ptr<T: ?Sized>(pub(crate) *mut T);
759
760impl<T: ?Sized> Ptr<T> {
761    pub fn get(&self) -> &mut T {
762        unsafe { &mut *self.0 }
763    }
764}
765
766impl<T> Ptr<T> {
767    pub fn new(x: T) -> Self {
768        Self(Box::into_raw(Box::new(x)))
769    }
770
771    pub fn from_box(b: Box<T>) -> Self {
772        Self(Box::into_raw(b))
773    }
774
775    pub fn set(&self, val: T) {
776        unsafe { self.0.write(val) };
777    }
778
779    pub fn replace(&self, val: T) -> T {
780        std::mem::replace(self.get(), val)
781    }
782
783    pub fn take(&self) -> T
784    where
785        T: Default,
786    {
787        self.replace(T::default())
788    }
789
790    pub fn is_null(&self) -> bool {
791        self.0.is_null()
792    }
793
794    pub fn null() -> Self {
795        Self(std::ptr::null_mut())
796    }
797}
798
799use std::hash::*;
800
801impl<T> Hash for Ptr<T> {
802    fn hash<H: Hasher>(&self, state: &mut H) {
803        self.0.hash(state);
804    }
805}
806
807impl<T> PartialEq for Ptr<T> {
808    fn eq(&self, other: &Self) -> bool {
809        self.0 == other.0
810    }
811}
812
813impl<T> Eq for Ptr<T> {}
814
815impl<T> Copy for Ptr<T> {}
816impl<T> Clone for Ptr<T> {
817    fn clone(&self) -> Self {
818        *self
819    }
820}
821
822impl<T> std::ops::Deref for Ptr<T> {
823    type Target = T;
824    fn deref(&self) -> &T {
825        self.get()
826    }
827}
828
829unsafe impl<T> Send for Ptr<T> {}
830unsafe impl<T> Sync for Ptr<T> {}
831
832use std::hash::{Hash, Hasher};
833use std::sync::atomic::{AtomicPtr, Ordering};
834
835/// The mask to use for untagging a pointer.
836const UNTAG_MASK: usize = (!0x7) as usize;
837
838/// Returns true if the pointer has the given bit set to 1.
839pub fn bit_is_set<T>(pointer: *mut T, bit: usize) -> bool {
840    let shifted = 1 << bit;
841
842    (pointer as usize & shifted) == shifted
843}
844
845/// Returns the pointer with the given bit set.
846pub fn with_bit<T>(pointer: *mut T, bit: usize) -> *mut T {
847    (pointer as usize | 1 << bit) as _
848}
849
850pub fn without_bit<T>(pointer: *mut T, bit: usize) -> *mut T {
851    (pointer as usize ^ 1 << bit) as _
852}
853
854/// Returns the given pointer without any tags set.
855pub fn untagged<T>(pointer: *mut T) -> *mut T {
856    (pointer as usize & UNTAG_MASK) as _
857}
858
859/// Structure wrapping a raw, tagged pointer.
860#[derive(Debug)]
861#[repr(transparent)]
862pub struct TaggedPointer<T: ?Sized> {
863    pub raw: *mut T,
864}
865
866impl<T> TaggedPointer<T> {
867    /// Returns a new TaggedPointer without setting any bits.
868    pub fn new(raw: *mut T) -> TaggedPointer<T> {
869        TaggedPointer { raw }
870    }
871
872    /// Returns a new TaggedPointer with the given bit set.
873    pub fn with_bit(raw: *mut T, bit: usize) -> TaggedPointer<T> {
874        let mut pointer = Self::new(raw);
875
876        pointer.set_bit(bit);
877
878        pointer
879    }
880
881    pub fn unset_bit(&mut self, bit: usize) {
882        if self.bit_is_set(bit) {
883            self.raw = without_bit(self.raw, bit);
884        }
885    }
886
887    /// Returns a null pointer.
888    pub const fn null() -> TaggedPointer<T> {
889        TaggedPointer {
890            raw: ptr::null::<T>() as *mut T,
891        }
892    }
893
894    /// Returns the wrapped pointer without any tags.
895    pub fn untagged(self) -> *mut T {
896        self::untagged(self.raw)
897    }
898
899    /// Returns a new TaggedPointer using the current pointer but without any
900    /// tags.
901    pub fn without_tags(self) -> Self {
902        Self::new(self.untagged())
903    }
904
905    /// Returns true if the given bit is set.
906    pub fn bit_is_set(self, bit: usize) -> bool {
907        self::bit_is_set(self.raw, bit)
908    }
909
910    /// Sets the given bit.
911    pub fn set_bit(&mut self, bit: usize) {
912        self.raw = with_bit(self.raw, bit);
913    }
914
915    /// Returns true if the current pointer is a null pointer.
916    pub fn is_null(self) -> bool {
917        self.untagged().is_null()
918    }
919
920    /// Returns an immutable to the pointer's value.
921    pub fn as_ref<'a>(self) -> Option<&'a T> {
922        unsafe { self.untagged().as_ref() }
923    }
924
925    /// Returns a mutable reference to the pointer's value.
926    pub fn as_mut<'a>(self) -> Option<&'a mut T> {
927        unsafe { self.untagged().as_mut() }
928    }
929
930    /// Atomically swaps the internal pointer with another one.
931    ///
932    /// This boolean returns true if the pointer was swapped, false otherwise.
933    #[cfg_attr(feature = "cargo-clippy", allow(clippy::trivially_copy_pass_by_ref))]
934    pub fn compare_and_swap(&self, current: *mut T, other: *mut T) -> bool {
935        self.as_atomic()
936            .compare_and_swap(current, other, Ordering::AcqRel)
937            == current
938    }
939
940    /// Atomically replaces the current pointer with the given one.
941    #[cfg_attr(feature = "cargo-clippy", allow(clippy::trivially_copy_pass_by_ref))]
942    pub fn atomic_store(&self, other: *mut T) {
943        self.as_atomic().store(other, Ordering::Release);
944    }
945
946    /// Atomically loads the pointer.
947    #[cfg_attr(feature = "cargo-clippy", allow(clippy::trivially_copy_pass_by_ref))]
948    pub fn atomic_load(&self) -> *mut T {
949        self.as_atomic().load(Ordering::Acquire)
950    }
951
952    /// Checks if a bit is set using an atomic load.
953    #[cfg_attr(feature = "cargo-clippy", allow(clippy::trivially_copy_pass_by_ref))]
954    pub fn atomic_bit_is_set(&self, bit: usize) -> bool {
955        Self::new(self.atomic_load()).bit_is_set(bit)
956    }
957
958    fn as_atomic(&self) -> &AtomicPtr<T> {
959        unsafe { &*(self as *const TaggedPointer<T> as *const AtomicPtr<T>) }
960    }
961}
962
963impl<T> PartialEq for TaggedPointer<T> {
964    fn eq(&self, other: &TaggedPointer<T>) -> bool {
965        self.raw == other.raw
966    }
967}
968
969impl<T> Eq for TaggedPointer<T> {}
970
971// These traits are implemented manually as "derive" doesn't handle the generic
972// "T" argument very well.
973impl<T> Clone for TaggedPointer<T> {
974    fn clone(&self) -> TaggedPointer<T> {
975        TaggedPointer::new(self.raw)
976    }
977}
978
979impl<T> Copy for TaggedPointer<T> {}
980
981impl<T> Hash for TaggedPointer<T> {
982    fn hash<H: Hasher>(&self, state: &mut H) {
983        self.raw.hash(state);
984    }
985}