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
// Copyright 2017-2018 Maskerad Developers // // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. use alloc::raw_vec::RawVec; use std::cell::Cell; use core::mem; use utils; /// The type of the memory chunk. /// /// Only used by the double-ended allocators, to choose which memory chunk to operate on. pub enum ChunkType { TempData, ResidentData, } /// The MemoryChunk is just a chunk of memory. /// It uses a [RawVec](https://doc.rust-lang.org/alloc/raw_vec/struct.RawVec.html) to allocate bytes /// in a vector-like fashion. /// /// This structure allows you allocate data of different type in the same storage, since : /// /// - The chunk knows the location of the first unused byte in its memory storage, and update it when allocation occurs or /// when objects in the memory chunk are dropped. /// /// - The chunk extracts, for the types implementing the Drop trait, /// some info about the type (its virtual table) and place it next to the object. The chunk is able to call the drop method of the object /// with the virtual table. /// /// /// You should not use the MemoryChunk directly. The allocators manage memory chunks, use them. pub struct MemoryChunk { storage: RawVec<u8>, /// Index of the first unused byte. fill: Cell<usize>, } impl MemoryChunk { /// Creates a new memory chunk, allocating the given number of bytes. pub fn new(size: usize) -> Self { MemoryChunk { storage: RawVec::with_capacity(size), fill: Cell::new(0), } } /// Returns the index of the first unused byte in the memory storage of the chunk. pub fn fill(&self) -> usize { self.fill.get() } /// Set the index of the first unused byte in the memory storage of the chunk. pub fn set_fill(&self, first_unused_byte: usize) { self.fill.set(first_unused_byte) } /// Returns the maximal number of bytes the chunk can store. pub fn capacity(&self) -> usize { self.storage.cap() } /// Returns a pointer to the start of the memory storage used by the chunk. pub fn as_ptr(&self) -> *const u8 { self.storage.ptr() } /// Drop all the data contained in the chunk. pub unsafe fn destroy(&self) { self.destroy_to_marker(0); } /// Drop the data contained in the chunk, starting from the given marker. pub unsafe fn destroy_to_marker(&self, marker: usize) { //Get the index of the marker. //We'll start dropping the content from this location. let mut index = marker; //Get a raw pointer to the bottom of the memory storage. let storage_start = self.as_ptr(); //Get the index of the first unused memory address. //We'll stop dropping the content when we are at this location. let fill = self.fill.get(); //While the starting index is inferior to the ending one... while index < fill { //Get a raw pointer on the TypeDescription of the object. let type_description_data = storage_start.offset(index as isize) as *const usize; //Decode this raw pointer to obtain the vtable of the object, and a boolean to know if //the object has been initialized. let (type_description, is_done) = utils::un_bitpack_type_description_ptr(*type_description_data); //Get the size and the alignment of the object, with its type description. let (size, alignment) = ((*type_description).size, (*type_description).alignment); //Get the index of the memory address just after the type description. //It's the unaligned memory address of the object. let after_type_description = index + mem::size_of::<*const utils::TypeDescription>(); //Get the aligned memory address, with the unaligned one and the alignment of the object. //This is where the object *really* lives. let start = utils::round_up(after_type_description, alignment); //If the object has been successfully initialized, we can call its drop function. //We call the function pointer of the object's vtable, here drop_glue, and give him the pointer //to the location of the object. if is_done { ((*type_description).drop_glue)(storage_start.offset(start as isize) as *const i8); } //Find where the next type description lives. index = utils::round_up(start + size, mem::align_of::<*const utils::TypeDescription>()); } } }