]> git.lizzy.rs Git - rust.git/blob - library/alloc/tests/boxed.rs
Link to the LLVM issue from a comment on `SpecOptionPartialEq`
[rust.git] / library / alloc / tests / boxed.rs
1 use core::alloc::{AllocError, Allocator, Layout};
2 use core::cell::Cell;
3 use core::mem::MaybeUninit;
4 use core::ptr::NonNull;
5
6 #[test]
7 fn uninitialized_zero_size_box() {
8     assert_eq!(
9         &*Box::<()>::new_uninit() as *const _,
10         NonNull::<MaybeUninit<()>>::dangling().as_ptr(),
11     );
12     assert_eq!(
13         Box::<[()]>::new_uninit_slice(4).as_ptr(),
14         NonNull::<MaybeUninit<()>>::dangling().as_ptr(),
15     );
16     assert_eq!(
17         Box::<[String]>::new_uninit_slice(0).as_ptr(),
18         NonNull::<MaybeUninit<String>>::dangling().as_ptr(),
19     );
20 }
21
22 #[derive(Clone, PartialEq, Eq, Debug)]
23 struct Dummy {
24     _data: u8,
25 }
26
27 #[test]
28 fn box_clone_and_clone_from_equivalence() {
29     for size in (0..8).map(|i| 2usize.pow(i)) {
30         let control = vec![Dummy { _data: 42 }; size].into_boxed_slice();
31         let clone = control.clone();
32         let mut copy = vec![Dummy { _data: 84 }; size].into_boxed_slice();
33         copy.clone_from(&control);
34         assert_eq!(control, clone);
35         assert_eq!(control, copy);
36     }
37 }
38
39 /// This test might give a false positive in case the box reallocates,
40 /// but the allocator keeps the original pointer.
41 ///
42 /// On the other hand, it won't give a false negative: If it fails, then the
43 /// memory was definitely not reused.
44 #[test]
45 fn box_clone_from_ptr_stability() {
46     for size in (0..8).map(|i| 2usize.pow(i)) {
47         let control = vec![Dummy { _data: 42 }; size].into_boxed_slice();
48         let mut copy = vec![Dummy { _data: 84 }; size].into_boxed_slice();
49         let copy_raw = copy.as_ptr() as usize;
50         copy.clone_from(&control);
51         assert_eq!(copy.as_ptr() as usize, copy_raw);
52     }
53 }
54
55 #[test]
56 fn box_deref_lval() {
57     let x = Box::new(Cell::new(5));
58     x.set(1000);
59     assert_eq!(x.get(), 1000);
60 }
61
62 pub struct ConstAllocator;
63
64 unsafe impl const Allocator for ConstAllocator {
65     fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
66         match layout.size() {
67             0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)),
68             _ => unsafe {
69                 let ptr = core::intrinsics::const_allocate(layout.size(), layout.align());
70                 Ok(NonNull::new_unchecked(ptr as *mut [u8; 0] as *mut [u8]))
71             },
72         }
73     }
74
75     unsafe fn deallocate(&self, _ptr: NonNull<u8>, layout: Layout) {
76         match layout.size() {
77             0 => { /* do nothing */ }
78             _ => { /* do nothing too */ }
79         }
80     }
81
82     fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
83         let ptr = self.allocate(layout)?;
84         if layout.size() > 0 {
85             unsafe {
86                 ptr.as_mut_ptr().write_bytes(0, layout.size());
87             }
88         }
89         Ok(ptr)
90     }
91
92     unsafe fn grow(
93         &self,
94         ptr: NonNull<u8>,
95         old_layout: Layout,
96         new_layout: Layout,
97     ) -> Result<NonNull<[u8]>, AllocError> {
98         debug_assert!(
99             new_layout.size() >= old_layout.size(),
100             "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
101         );
102
103         let new_ptr = self.allocate(new_layout)?;
104         if new_layout.size() > 0 {
105             // Safety: `new_ptr` is valid for writes and `ptr` for reads of
106             // `old_layout.size()`, because `new_layout.size() >=
107             // old_layout.size()` (which is an invariant that must be upheld by
108             // callers).
109             unsafe {
110                 new_ptr.as_mut_ptr().copy_from_nonoverlapping(ptr.as_ptr(), old_layout.size());
111             }
112             // Safety: `ptr` is never used again is also an invariant which must
113             // be upheld by callers.
114             unsafe {
115                 self.deallocate(ptr, old_layout);
116             }
117         }
118         Ok(new_ptr)
119     }
120
121     unsafe fn grow_zeroed(
122         &self,
123         ptr: NonNull<u8>,
124         old_layout: Layout,
125         new_layout: Layout,
126     ) -> Result<NonNull<[u8]>, AllocError> {
127         // Safety: Invariants of `grow_zeroed` and `grow` are the same, and must
128         // be enforced by callers.
129         let new_ptr = unsafe { self.grow(ptr, old_layout, new_layout)? };
130         if new_layout.size() > 0 {
131             let old_size = old_layout.size();
132             let new_size = new_layout.size();
133             let raw_ptr = new_ptr.as_mut_ptr();
134             // Safety:
135             // - `grow` returned Ok, so the returned pointer must be valid for
136             //   `new_size` bytes
137             // - `new_size` must be larger than `old_size`, which is an
138             //   invariant which must be upheld by callers.
139             unsafe {
140                 raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
141             }
142         }
143         Ok(new_ptr)
144     }
145
146     unsafe fn shrink(
147         &self,
148         ptr: NonNull<u8>,
149         old_layout: Layout,
150         new_layout: Layout,
151     ) -> Result<NonNull<[u8]>, AllocError> {
152         debug_assert!(
153             new_layout.size() <= old_layout.size(),
154             "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
155         );
156
157         let new_ptr = self.allocate(new_layout)?;
158         if new_layout.size() > 0 {
159             // Safety: `new_ptr` and `ptr` are valid for reads/writes of
160             // `new_layout.size()` because of the invariants of shrink, which
161             // include `new_layout.size()` being smaller than (or equal to)
162             // `old_layout.size()`.
163             unsafe {
164                 new_ptr.as_mut_ptr().copy_from_nonoverlapping(ptr.as_ptr(), new_layout.size());
165             }
166             // Safety: `ptr` is never used again is also an invariant which must
167             // be upheld by callers.
168             unsafe {
169                 self.deallocate(ptr, old_layout);
170             }
171         }
172         Ok(new_ptr)
173     }
174
175     fn by_ref(&self) -> &Self
176     where
177         Self: Sized,
178     {
179         self
180     }
181 }
182
183 #[test]
184 fn const_box() {
185     const VALUE: u32 = {
186         let mut boxed = Box::new_in(1u32, ConstAllocator);
187         assert!(*boxed == 1);
188
189         *boxed = 42;
190         assert!(*boxed == 42);
191
192         *Box::leak(boxed)
193     };
194
195     assert!(VALUE == 42);
196 }