use avr_oxide::deviceconsts::memory;
use core::alloc::{GlobalAlloc, Layout};
use core::cell::UnsafeCell;
#[cfg(target_arch="avr")]
extern crate alloc;
#[cfg(target_arch="avr")]
pub use alloc::vec;
#[cfg(target_arch="avr")]
pub use alloc::boxed;
#[cfg(target_arch="avr")]
pub use alloc::rc;
#[cfg(target_arch="avr")]
pub use alloc::string;
#[cfg(not(target_arch="avr"))]
pub use std::boxed;
#[cfg(not(target_arch="avr"))]
pub use std::vec;
#[cfg(not(target_arch="avr"))]
pub use std::rc;
#[repr(C,align(2))]
struct FirstFitHeap<const HEAPSIZE_WORDS: usize, const SEGS: usize, const S21: usize> {
v: [i16; HEAPSIZE_WORDS],
pa: [u16; SEGS],
st: [i16; S21],
s: usize,
words_per_seg: usize
}
struct GlobalAllocator<const H:usize, const S:usize, const S2:usize> {
heap: UnsafeCell<FirstFitHeap<H,S,S2>>
}
#[cfg(target_arch="avr")]
#[global_allocator]
static mut GLOBAL: GlobalAllocator::<{memory::alloc::HEAPSIZE/2}, {memory::alloc::SMAX}, {memory::alloc::SMAX * 2}> = GlobalAllocator {
heap: UnsafeCell::new(FirstFitHeap::new())
};
#[cfg(target_arch="avr")]
#[alloc_error_handler]
fn _alloc_error(_: core::alloc::Layout) -> ! {
panic!();
}
#[cfg(target_arch="avr")]
pub fn initialise() {
unsafe {
let heap = avr_oxide::panic_if_none!(GLOBAL.heap.get().as_mut());
heap.init();
}
}
unsafe impl<const H:usize, const S:usize, const S2:usize> GlobalAlloc for GlobalAllocator<H,S,S2> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let heap = avr_oxide::panic_if_none!(self.heap.get().as_mut());
let align_words = match layout.align() {
0 => 0,
1 => 0,
_other => (layout.align()/2)-1
};
let size_words= ((layout.size() + 1) / 2) + align_words;
let ptr = avr_oxide::hal::concurrency::interrupt::isolated(||{
match heap.bl_new(size_words){
0 => {
core::ptr::null_mut()
},
index => {
core::mem::transmute(&heap.v[index])
}
}
});
match (ptr as usize) % layout.align() {
0 => ptr, misalignment => {
let padding_needed = (layout.align() - misalignment) as isize;
let returned_ptr = ptr.offset(padding_needed);
let flag = returned_ptr.offset(-2) as *mut i16; *flag = (padding_needed as i16)/2;
returned_ptr
}
}
}
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
let heap = avr_oxide::panic_if_none!(self.heap.get().as_mut());
let index0:usize = core::mem::transmute(&heap.v[0]);
let mut index = ((ptr as usize) - index0)/2;
avr_oxide::hal::concurrency::interrupt::isolated(||{
if heap.v[index-1] > 0 {
index -= heap.v[index-1] as usize;
}
heap.bl_dispose(index);
});
}
}
impl<const HEAPSIZE_WORDS: usize, const SEGS: usize, const S21: usize> FirstFitHeap<HEAPSIZE_WORDS,SEGS,S21> {
const HEAPSIZE_MAX: usize = HEAPSIZE_WORDS-1;
const fn new() -> Self {
FirstFitHeap {
v: [0; HEAPSIZE_WORDS],
pa: [0; SEGS],
st: [0; S21],
s: 0,
words_per_seg: 0
}
}
fn init(&mut self) {
self.s = 1;
self.st[1] = HEAPSIZE_WORDS as i16;
self.pa[0] = 0;
self.v[0] = HEAPSIZE_WORDS as i16;
self.words_per_seg = HEAPSIZE_WORDS/SEGS;
self.bl_new(0);
}
fn bl_seg(&self, addr: usize) -> usize {
self.s + (addr/self.words_per_seg)
}
fn bl_double(&mut self) {
debug_assert!(self.s <= SEGS/2);
for i in self.s ..= 2*self.s-1 {
self.pa[i] = Self::HEAPSIZE_MAX as u16;
}
let mut k = self.s;
while k > 0 {
for i in 0 ..= k-1 {
self.st[2*k+i] = self.st[k+i];
self.st[3*k+i] = 0;
}
k /= 2;
}
self.st[1] = self.st[2];
self.s *= 2;
}
fn bl_fix_left(&mut self, addr: usize) {
let mut j = self.bl_seg(addr);
if (self.v[addr] <= 0) || (self.st[j] as i16 <= self.v[addr]) {
let mut pj = self.pa[j-self.s] as usize; let mut pn = (j+1-self.s) * self.words_per_seg; if pn > Self::HEAPSIZE_MAX { pn = Self::HEAPSIZE_MAX;
}
let mut mx = if pj < pn {
let mut mx = 1i16;
while pj < pn {
if mx < self.v[pj] {
mx = self.v[pj];
}
pj = pj + self.v[pj].abs() as usize;
}
mx
} else {
0
};
self.st[0] = 0; while self.st[j] > mx { self.st[j] = mx;
let sister = match j % 2 {
1 => self.st[j-1], _ => self.st[j+1] };
if mx < sister {
mx = sister;
}
j /= 2;
}
}
}
fn bl_fix_right(&mut self, addr: usize) {
let mut j = self.bl_seg(addr);
while j >= 2*self.s && self.s <= SEGS/2 {
self.bl_double();
j = self.bl_seg(addr);
}
if self.pa[j-self.s] > addr as u16 {
self.pa[j-self.s] = addr as u16;
}
let vp = self.v[addr];
self.st[0] = vp; while self.st[j] < vp { self.st[j] = vp;
j /= 2;
}
}
fn bl_pred(&self, addr: usize) -> usize {
let mut j = self.bl_seg(addr);
if self.pa[j-self.s] == addr as u16 {
while self.st[j-1] == 0 {
j /= 2;
}
j -= 1;
while j<self.s {
if self.st[2*j+1] > 0 {
j = 2*j+1;
} else {
j *= 2;
}
}
}
let mut qn = self.pa[j-self.s];
let mut q = qn;
while qn != addr as u16 {
q = qn;
qn = qn+self.v[qn as usize].abs() as u16;
}
q as usize
}
fn bl_new(&mut self, size: usize) -> usize {
if self.st[1] <= size as i16 { 0
} else {
let n = size +1; let mut j = 1;
while j < self.s {
if self.st[2*j] >= n as i16 {
j *= 2;
} else {
j = 2*j+1;
}
}
let mut p = self.pa[j-self.s] as usize; while self.v[p] < n as i16 {
p = p + self.v[p].abs() as usize;
}
let vp = self.v[p];
self.v[p] = -(n as i16); let fix_left = vp == self.st[j];
if vp > n as i16 {
self.v[p+n] = vp - (n as i16);
if self.bl_seg(p+n) > j {
self.bl_fix_right(p+n);
}
}
if fix_left {
self.bl_fix_left(p);
}
p+1 }
}
fn bl_dispose(&mut self, addr: usize) {
let p = addr-1; let vp = -self.v[p];
if vp > 0 {
self.v[p] = vp; let j = self.bl_seg(p);
let pn = p + vp as usize;
if pn < Self::HEAPSIZE_MAX && self.v[pn] >= 0 {
self.v[p] = vp + self.v[pn];
let jn = self.bl_seg(pn);
if jn > j {
self.pa[jn-self.s] = (p as i16 + self.v[p]) as u16;
self.bl_fix_left(pn);
}
}
let pr = self.bl_pred(p); if self.v[pr] >= 0 {
self.v[pr] += self.v[p];
if self.pa[j-self.s] as usize == p {
self.pa[j-self.s] = (pr as i16 + self.v[pr]) as u16; self.bl_fix_left(p);
}
self.bl_fix_right(pr);
} else if self.v[p] > self.st[j] {
self.bl_fix_right(p);
}
} else {
panic!(); }
}
#[allow(dead_code)]
fn bl_size(&self, addr: usize) -> usize {
let p = self.v[addr-1];
if p > 0 {
panic!();
} else {
-(p+1) as usize
}
}
#[allow(dead_code)]
fn bl_available(&self) -> usize {
self.st[1] as usize - 1
}
#[allow(dead_code)]
fn bl_segments(&self) -> usize {
self.s
}
}
#[cfg(test)]
mod test {
use std::vec::Vec;
use avr_oxide::alloc::*;
#[test]
fn test_create_heap() {
let mut heap = FirstFitHeap::<512,32,64>::new();
heap.init();
assert_eq!(heap.bl_seg(1), 1);
}
#[test]
fn test_alloc_free() {
let mut heap = FirstFitHeap::<512,32,64>::new();
heap.init();
let init_free = heap.bl_available();
println!("Heap with {} words available\n", init_free);
let alloc1 = heap.bl_new(128);
println!("First allocation, {} bytes @ {}", heap.bl_size(alloc1), alloc1);
println!("Free space: {}", heap.bl_available());
println!("Segments: {}", heap.bl_segments());
assert!(heap.bl_size(alloc1) >= 128);
assert_eq!(alloc1, 2);
let alloc2 = heap.bl_new(64);
println!("Second allocation, {} bytes @ {}", heap.bl_size(alloc2), alloc2);
println!("Free space: {}", heap.bl_available());
println!("Segments: {}", heap.bl_segments());
assert!(heap.bl_size(alloc2) >= 64);
assert_eq!(alloc2, 131);
let alloc3 = heap.bl_new(27);
println!("Third allocation, {} bytes @ {}", heap.bl_size(alloc3), alloc3);
println!("Free space: {}", heap.bl_available());
println!("Segments: {}", heap.bl_segments());
assert!(heap.bl_size(alloc3) >= 27);
assert_eq!(alloc3, 196);
let alloc4 = heap.bl_new(256);
println!("Fourth allocation, {} bytes @ {}", heap.bl_size(alloc4), alloc4);
println!("Free space: {}", heap.bl_available());
println!("Segments: {}", heap.bl_segments());
assert!(heap.bl_size(alloc4) >= 256);
assert_eq!(alloc4, 224);
let alloc5 = heap.bl_new(31);
println!("Fifth allocation, {} bytes @ {}", heap.bl_size(alloc5), alloc5);
println!("Free space: {}", heap.bl_available());
println!("Segments: {}", heap.bl_segments());
assert!(heap.bl_size(alloc5) >= 31);
assert_eq!(alloc5, 481);
println!("Freeing allocation @ {}", alloc3);
heap.bl_dispose(alloc3);
println!("Free space: {}", heap.bl_available());
println!("Segments: {}", heap.bl_segments());
assert_eq!(heap.bl_available(), 27);
let alloc6 = heap.bl_new(4);
println!("Sixth allocation, {} bytes @ {}", heap.bl_size(alloc6), alloc6);
println!("Free space: {}", heap.bl_available());
println!("Segments: {}", heap.bl_segments());
assert!(heap.bl_size(alloc6) >= 4);
assert_eq!(alloc6, 196);
let alloc7 = heap.bl_new(10);
println!("Seventh allocation, {} bytes @ {}", heap.bl_size(alloc7), alloc7);
println!("Free space: {}", heap.bl_available());
println!("Segments: {}", heap.bl_segments());
assert!(heap.bl_size(alloc7) >= 10);
assert_eq!(alloc7, 201);
heap.bl_dispose(alloc1);
println!("Free space: {}", heap.bl_available());
heap.bl_dispose(alloc2);
println!("Free space: {}", heap.bl_available());
heap.bl_dispose(alloc4);
println!("Free space: {}", heap.bl_available());
heap.bl_dispose(alloc5);
println!("Free space: {}", heap.bl_available());
heap.bl_dispose(alloc6);
println!("Free space: {}", heap.bl_available());
heap.bl_dispose(alloc7);
println!("Free space: {}", heap.bl_available());
assert_eq!(heap.bl_available(), init_free);
}
fn fillspace(addr: *mut u8, size: usize, value: u8) {
unsafe {
for i in 0..size as isize {
*(addr.offset(i)) = value;
}
}
}
fn checkspace(addr: *mut u8, size: usize, value: u8) -> bool {
unsafe {
for i in 0..size as isize {
if *(addr.offset(i)) != value {
return false;
}
}
}
true
}
#[test]
pub fn test_global_alloc() {
unsafe {
let global = GlobalAllocator {
heap: UnsafeCell::new(FirstFitHeap::<512,32,64>::new())
};
let mut heap = global.heap.get().as_mut().unwrap();
heap.init();
let addr = global.alloc(Layout::from_size_align(16,2).unwrap());
fillspace(addr, 16,0xde);
println!("Allocated a block @ address: {:?}", addr);
println!("Free space: {}b", heap.bl_available()*2);
assert_eq!(addr as usize % 2, 0);
global.dealloc(addr, Layout::from_size_align(16,2).unwrap());
println!("Freed block");
println!("Free space: {}b", heap.bl_available()*2);
println!("Alignment checks");
let discard = global.alloc(Layout::from_size_align(3, 2).unwrap());
let addr1 = global.alloc(Layout::from_size_align(175,4).unwrap());
fillspace(addr1, 175, 0xde);
println!("Allocated a block @ address: {:?}", addr1);
println!("Free space: {}b", heap.bl_available()*2);
assert_eq!(addr1 as usize % 4, 0);
let addr2 = global.alloc(Layout::from_size_align(230, 8).unwrap());
fillspace(addr2, 230, 0xde);
println!("Allocated a block @ address: {:?}", addr2);
println!("Free space: {}b", heap.bl_available()*2);
assert_eq!(addr2 as usize % 8, 0);
let addr3 = global.alloc(Layout::from_size_align(49, 16).unwrap());
fillspace(addr3, 49, 0xde);
println!("Allocated a block @ address: {:?}", addr3);
println!("Free space: {}b", heap.bl_available()*2);
assert_eq!(addr3 as usize % 16, 0);
println!("Checking for data corruption");
assert!(checkspace(addr1, 175,0xde));
assert!(checkspace(addr2, 230,0xde));
assert!(checkspace(addr3, 49,0xde));
println!("Freeing aligned allocations");
global.dealloc(addr1, Layout::from_size_align(175,4).unwrap());
assert!(checkspace(addr2, 230,0xde));
assert!(checkspace(addr3, 49,0xde));
global.dealloc(addr2, Layout::from_size_align(230, 8).unwrap());
assert!(checkspace(addr3, 49,0xde));
global.dealloc(addr3, Layout::from_size_align(49, 16).unwrap());
}
}
#[test]
pub fn test_soaktest_global_alloc() {
unsafe {
let global = GlobalAllocator {
heap: UnsafeCell::new(FirstFitHeap::<512,32,64>::new())
};
let mut heap = global.heap.get().as_mut().unwrap();
heap.init();
for blocksize in 1..1000 {
println!("Testing blocks of size {}", blocksize);
let mut blocks : Vec<*mut u8> = Vec::new();
let mut i:i32 = 0;
loop {
let block = global.alloc(Layout::from_size_align(blocksize,1).unwrap());
if block != core::ptr::null_mut() {
fillspace(block,blocksize,i as u8);
blocks.push(block);
} else {
break;
}
i += 1;
}
println!(" Allocated {} blocks", blocks.len());
let mut i:i32 = 0;
for block in blocks {
assert!(checkspace(block,blocksize,i as u8));
global.dealloc(block,Layout::from_size_align(blocksize,1).unwrap());
i += 1;
}
}
}
}
}