#![cfg_attr(feature = "cargo-clippy", allow(doc_markdown))]
extern crate alloc;
extern crate object_alloc;
use {PAGE_SIZE, PAGE_ALIGN_MASK, OBJECTS_PER_SLAB};
use stack;
use stack::{SlabHeader, Layout};
use init::InitSystem;
use util::ptrmap::*;
use self::alloc::allocator;
use self::object_alloc::UntypedObjectAlloc;
pub struct ConfigData {
pub map: Map<u8, SlabHeader>,
map_by_page_addr: bool,
}
impl stack::ConfigData for ConfigData {
fn post_alloc(&mut self, layout: &Layout, slab_size: usize, slab: *mut SlabHeader) {
if self.map_by_page_addr {
for i in 0..(slab_size / *PAGE_SIZE) {
let page_ptr = ((slab as usize) + (i * *PAGE_SIZE)) as *mut u8;
debug_assert_eq!(page_ptr as usize % *PAGE_SIZE, 0);
self.map.insert(page_ptr, slab);
}
} else {
for i in 0..layout.num_obj {
let ptr = layout.nth_obj(slab, unsafe { (*slab).get_color() }, i);
self.map.insert(ptr, slab);
}
}
}
fn pre_dealloc(&mut self, layout: &Layout, slab_size: usize, slab: *mut SlabHeader) {
if self.map_by_page_addr {
for i in 0..(slab_size / *PAGE_SIZE) {
let page_ptr = ((slab as usize) + (i * *PAGE_SIZE)) as *mut u8;
debug_assert_eq!(page_ptr as usize % *PAGE_SIZE, 0);
self.map.delete(page_ptr);
}
} else {
for i in 0..layout.num_obj {
let ptr = layout.nth_obj(slab, unsafe { (*slab).get_color() }, i);
self.map.delete(ptr);
}
}
}
fn ptr_to_slab(&self, _slab_size: usize, ptr: *mut u8) -> *mut SlabHeader {
if self.map_by_page_addr {
self.map
.get(((ptr as usize) & *PAGE_ALIGN_MASK) as *mut u8)
} else {
self.map.get(ptr)
}
}
}
pub type System<A> = stack::System<A, ConfigData>;
pub const DEFAULT_MAP_SIZE: usize = 256;
impl<A: UntypedObjectAlloc> System<A> {
pub fn new(layout: allocator::Layout, alloc: A) -> Option<System<A>> {
if let Some((slab_layout, _)) =
Layout::for_slab_size(layout.clone(), alloc.layout().size()) {
let map_by_page_addr = layout.size() < *PAGE_SIZE;
let map_key_align = if map_by_page_addr {
*PAGE_SIZE
} else {
layout.size().next_power_of_two()
};
Some(Self::from_config_data(ConfigData {
map: Map::new(DEFAULT_MAP_SIZE, map_key_align),
map_by_page_addr: map_by_page_addr,
},
slab_layout,
alloc))
} else {
None
}
}
}
pub fn backing_size_for<I: InitSystem>(layout: &allocator::Layout) -> usize {
struct PageIterator(usize);
impl Iterator for PageIterator {
type Item = usize;
fn next(&mut self) -> Option<usize> {
let (next, _) = self.0.overflowing_add(*PAGE_SIZE);
if next > self.0 {
self.0 = next;
Some(next)
} else {
None
}
}
}
let unused = |slab_size: usize| {
Layout::for_slab_size(layout.clone(), slab_size).map(|(layout, unused)| {
(layout.num_obj, unused)
})
};
let init_size = if layout.size() % *PAGE_SIZE == 0 {
layout.size()
} else {
(layout.size() / *PAGE_SIZE) * *PAGE_SIZE
};
::util::size::choose_size(PageIterator(init_size), unused, OBJECTS_PER_SLAB)
}
#[cfg(not(feature = "use-stdlib-hashmap"))]
#[cfg(test)]
mod tests {
extern crate alloc;
use {DefaultInitSystem, SizedSlabAlloc};
use init::DefaultInitializer;
use self::alloc::allocator::Layout;
fn test_hash_table_bucket_distribution<T: Default>() {
for i in 0..4 {
use backing::heap;
use sysconf::page::pagesize;
let layout = Layout::new::<T>();
let backing_layout = Layout::from_size_align(pagesize(), pagesize()).unwrap();
let mut alloc =
SizedSlabAlloc::new(DefaultInitSystem::<T>::new(DefaultInitializer::new()),
layout.clone(),
super::System::new(layout, heap::get_large(backing_layout))
.unwrap());
let mut ptrs = Vec::new();
let size = super::DEFAULT_MAP_SIZE << i;
for _ in 0..size {
ptrs.push(alloc.alloc().unwrap());
}
let buckets = alloc.slab_system.data.map.map.dump_by_bucket();
for p in ptrs {
alloc.dealloc(p);
}
eprintln!("Size: {}", size);
for (i, b) in buckets.iter().enumerate() {
eprintln!("{:>3}: {:?}", i, b);
}
eprintln!("");
}
}
macro_rules! make_test_hash_table_bucket_distribution {
($name:ident, $type:ty) => (
#[test]
#[ignore]
fn $name() {
test_hash_table_bucket_distribution::<$type>();
}
);
}
call_for_all_types_prefix!(make_test_hash_table_bucket_distribution,
test_hash_table_bucket_distribution);
}