xref: /relibc/ralloc/src/block.rs (revision 4b65baa341911909a129f29242ef66ba2cc4836e)
1 //! Memory blocks.
2 //!
3 //! Blocks are the main unit for the memory bookkeeping. A block is a simple construct with a
4 //! `Pointer` pointer and a size. Occupied (non-free) blocks are represented by a zero-sized block.
5 
6 use core::{ptr, cmp, mem, fmt};
7 
8 use ptr::Pointer;
9 use sys;
10 
11 /// A contiguous memory block.
12 ///
13 /// This provides a number of guarantees,
14 ///
15 /// 1. The inner pointer is never aliased. No byte in the block is contained in another block
16 ///    (aliased in this case is defined purely based on liveliness).
17 /// 2. The buffer is valid, but not necessarily initialized.
18 ///
19 /// All this is enforced through the type system.
20 pub struct Block {
21     /// The size of this block, in bytes.
22     size: usize,
23     /// The pointer to the start of this block.
24     ptr: Pointer<u8>,
25 }
26 
27 impl Block {
28     /// Create an empty block starting at `ptr`.
29     pub fn empty(ptr: &Pointer<u8>) -> Block {
30         Block {
31             size: 0,
32             // This won't alias `ptr`, since the block is empty.
33             ptr: unsafe { Pointer::new(**ptr) },
34         }
35     }
36 
37     /// Construct a block from its raw parts (pointer and size).
38     pub unsafe fn from_raw_parts(ptr: Pointer<u8>, size: usize) ->  Block {
39         Block {
40             size: size,
41             ptr: ptr,
42         }
43     }
44 
45     /// Get the size of the block.
46     pub fn size(&self) -> usize {
47         self.size
48     }
49 
50     /// BRK allocate a block.
51     ///
52     /// This is unsafe due to the allocator assuming that only it makes use of BRK.
53    pub unsafe fn brk(size: usize) -> Block {
54         Block {
55             size: size,
56             ptr: sys::inc_brk(size).unwrap_or_else(|x| x.handle()),
57         }
58     }
59 
60     /// Merge this block with a block to the right.
61     ///
62     /// This will simply extend the block, adding the size of the block, and then set the size to
63     /// zero. The return value is `Ok(())` on success, and `Err(())` on failure (e.g., the blocks
64     /// are not adjacent).
65     pub fn merge_right(&mut self, block: &mut Block) -> Result<(), ()> {
66         if self.left_to(&block) {
67             // Since the end of `block` is bounded by the address space, adding them cannot
68             // overflow.
69             self.size += block.pop().size;
70             // We pop it to make sure it isn't aliased.
71             Ok(())
72         } else { Err(()) }
73     }
74 
75     /// Is this block empty/free?
76     pub fn is_empty(&self) -> bool {
77         self.size != 0
78     }
79 
80     /// Is this block aligned to `align`?
81     pub fn aligned_to(&self, align: usize) -> bool {
82         *self.ptr as usize % align == 0
83     }
84 
85     /// Get the inner pointer.
86     pub fn into_ptr(self) -> Pointer<u8> {
87         self.ptr
88     }
89 
90     /// memcpy the block to another pointer.
91     ///
92     /// # Panics
93     ///
94     /// This will panic if the target block is smaller than the source.
95     pub fn copy_to(&self, block: &mut Block) {
96         // Bound check.
97         assert!(self.size <= block.size, "Block too small.");
98 
99         unsafe {
100             ptr::copy_nonoverlapping(*self.ptr, *block.ptr, self.size);
101         }
102     }
103 
104     /// "Pop" this block.
105     ///
106     /// This marks it as free, and returns the old value.
107     pub fn pop(&mut self) -> Block {
108         let empty = Block::empty(&self.ptr);
109         mem::replace(self, empty)
110     }
111 
112     /// Is this block placed left to the given other block?
113     pub fn left_to(&self, to: &Block) -> bool {
114         // This won't overflow due to the end being bounded by the address space.
115         self.size + *self.ptr as usize == *to.ptr as usize
116     }
117 
118     /// Split the block at some position.
119     ///
120     /// # Panics
121     ///
122     /// Panics if `pos` is out of bound.
123     pub fn split(self, pos: usize) -> (Block, Block) {
124         assert!(pos <= self.size, "Split {} out of bound (size is {})!", pos, self.size);
125 
126         (
127             Block {
128                 size: pos,
129                 ptr: self.ptr.duplicate(),
130             },
131             Block {
132                 size: self.size - pos,
133                 ptr: unsafe { self.ptr.offset(pos as isize) },
134             }
135         )
136     }
137 
138     /// Split this block, such that the second block is aligned to `align`.
139     ///
140     /// Returns an `None` holding the intact block if `align` is out of bounds.
141     pub fn align(&mut self, align: usize) -> Option<(Block, Block)> {
142         let aligner = align - *self.ptr as usize % align;
143 
144         // Bound check.
145         if aligner < self.size {
146             // Invalidate the old block.
147             let old = self.pop();
148 
149             Some((
150                 Block {
151                     size: aligner,
152                     ptr: old.ptr.duplicate(),
153                 },
154                 Block {
155                     size: old.size - aligner,
156                     ptr: unsafe { old.ptr.offset(aligner as isize) },
157                 }
158             ))
159         } else { None }
160     }
161 }
162 
163 impl PartialOrd for Block {
164     fn partial_cmp(&self, other: &Block) -> Option<cmp::Ordering> {
165         self.ptr.partial_cmp(&other.ptr)
166     }
167 }
168 
169 /// Compare the blocks address.
170 impl Ord for Block {
171     fn cmp(&self, other: &Block) -> cmp::Ordering {
172         self.ptr.cmp(&other.ptr)
173     }
174 }
175 
176 impl cmp::PartialEq for Block {
177     fn eq(&self, other: &Block) -> bool {
178         self.size == other.size && *self.ptr == *other.ptr
179     }
180 }
181 
182 impl cmp::Eq for Block {}
183 
184 impl fmt::Debug for Block {
185     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186         write!(f, "0x{:x}[0x{:x}]", *self.ptr as usize, self.size)
187     }
188 }
189 
190 #[cfg(test)]
191 mod test {
192     use super::*;
193 
194     use ptr::Pointer;
195 
196     #[test]
197     fn test_array() {
198         let arr = b"Lorem ipsum dolor sit amet";
199         let block = unsafe {
200             Block::from_raw_parts(Pointer::new(arr.as_ptr() as *mut u8), arr.len())
201         };
202 
203         // Test split.
204         let (mut lorem, mut rest) = block.split(5);
205         assert_eq!(lorem.size(), 5);
206         assert_eq!(lorem.size() + rest.size(), arr.len());
207         assert!(lorem < rest);
208 
209         /* TODO
210         assert_eq!(unsafe {
211             slice::from_raw_parts(*lorem.into_ptr() as *const _, lorem.size())
212         }, b"Lorem");
213         */
214 
215         assert_eq!(lorem, lorem);
216         assert!(rest.is_empty());
217         assert!(lorem.align(2).unwrap().1.aligned_to(2));
218         assert!(rest.align(16).unwrap().1.aligned_to(16));
219         assert_eq!(*lorem.into_ptr() as usize + 5, *rest.into_ptr() as usize);
220     }
221 
222     #[test]
223     fn test_merge() {
224         let arr = b"Lorem ipsum dolor sit amet";
225         let block = unsafe {
226             Block::from_raw_parts(Pointer::new(arr.as_ptr() as *mut u8), arr.len())
227         };
228 
229         let (mut lorem, mut rest) = block.split(5);
230         lorem.merge_right(&mut rest).unwrap();
231     }
232 
233     #[test]
234     #[should_panic]
235     fn test_oob() {
236         let arr = b"lorem";
237         let block = unsafe {
238             Block::from_raw_parts(Pointer::new(arr.as_ptr() as *mut u8), arr.len())
239         };
240 
241         // Test OOB.
242         block.split(6);
243     }
244 }
245