Custom allocator not showing allocated data

I've been playing around with the allocator_api. I have a custom allocator backed by a Vec<u8, Global>. When I print the contents of the data member, I'm expecting to see what I've allocated, but the data is empty:

#![feature(allocator_api)]

use std::{alloc::Allocator, cell::RefCell, sync::Mutex};

#[derive(Debug)]
struct MyAllocator {
    data: RefCell<Mutex<Vec<u8>>>,
    used: RefCell<Mutex<usize>>,
}

impl MyAllocator {
    fn new(capacity: usize) -> Self {
        Self {
            data: RefCell::new(Mutex::new(Vec::with_capacity(capacity))),
            used: RefCell::new(Mutex::new(0)),
        }
    }

    fn print(&self) {
        let mutex = self.data.borrow_mut();
        let data = mutex.lock().expect("could not lock allocator");

        println!("==== data contents ====");
        for d in data.chunks_exact(4) {
            let num = i32::from_ne_bytes([d[0], d[1], d[2], d[3]]);
            println!("{}", num);
        }
        println!("=======================");
    }
}

unsafe impl Allocator for &MyAllocator {
    fn allocate(
        &self,
        layout: std::alloc::Layout,
    ) -> Result<std::ptr::NonNull<[u8]>, std::alloc::AllocError> {
        unsafe {
            let used = *self
                .used
                .borrow()
                .lock()
                .map_err(|_| std::alloc::AllocError)?;
            let data = self
                .data
                .borrow_mut()
                .lock()
                .map_err(|_| std::alloc::AllocError)?
                .as_mut_ptr()
                .add(used);
            *self
                .used
                .borrow_mut()
                .lock()
                .map_err(|_| std::alloc::AllocError)? += layout.size();

            let ptr = std::ptr::slice_from_raw_parts_mut(data, layout.size());

            std::ptr::NonNull::new(ptr).ok_or(std::alloc::AllocError)
        }
    }

    unsafe fn deallocate(&self, _ptr: std::ptr::NonNull<u8>, _layout: std::alloc::Layout) {
        // noop
    }

    unsafe fn shrink(
        &self,
        ptr: std::ptr::NonNull<u8>,
        old_layout: std::alloc::Layout,
        _new_layout: std::alloc::Layout,
    ) -> Result<std::ptr::NonNull<[u8]>, std::alloc::AllocError> {
        // noop

        let ptr = std::ptr::slice_from_raw_parts_mut(ptr.as_ptr(), old_layout.size());

        std::ptr::NonNull::new(ptr).ok_or(std::alloc::AllocError)
    }
}

fn main() {
    let a = MyAllocator::new(40);
    let mut v = Vec::with_capacity_in(4, &a);
    v.push(7);
    v.push(4);
    v.push(1);
    v.push(10);
    v.sort_unstable();

    for i in v {
        println!("{}", i);
    }

    a.print();
}

Output:

1
4
7
10
==== data contents (0 items) ====
=================================

The allocations are clearly happening correctly, otherwise I'd not be able to get the data back out of the vector. Why is the data member empty and where are my allocations actually going?

A Vec has two distinct parameters - its capacity, which is the length of the buffer allocated and the length which is the number of elements stored in it (as is returned by .len()).
When you make a Vec via Vec::with_capacity(), it makes a vector of n capacity, which means the buffer is of length n, but the len is still 0. Hence the allocation works all fine, but when you call .chunks_exact(), then it sees the length is 0 and prints nothing.
What you want to do is to convert the buffer to a slice (with length set to capacity) and then print using that.

That makes sense. I forgot that I'm taking a pointer to the start of the Vec and offsetting by the used amount, which is not going to increase the length of the vector. Thanks for pointing that out.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.