Description
In my opinion the current Allocator interface is suboptimal. Mainly because it does more than just allocate memory, it will also copy data for you. I think it's important to get something like this right, as it will be used pretty much everywhere. I'm giving my opinion because I'd like to see it done right, especially in Zig, which seems to get so many things right already.
Currently the Allocator interface is as follows (I left out some documentation to keep it shorter):
/// Realloc is used to modify the size or alignment of an existing allocation,
/// as well as to provide the allocator with an opportunity to move an allocation
/// to a better location.
reallocFn: fn (
self: *Allocator,
old_mem: []u8,
old_alignment: u29,
new_byte_count: usize,
new_alignment: u29,
) Error![]u8,
/// This function deallocates memory. It must succeed.
shrinkFn: fn (
self: *Allocator,
old_mem: []u8,
old_alignment: u29,
new_byte_count: usize,
new_alignment: u29,
) []u8,
I have issues with the semantics of reallocFn
(shrinkFn
is fine). It is essentially the same as the C library realloc
function in that it can both modify (grow/shrink) the existing memory area, or allocate a totally new area and copy the data to there. The last part is where I think this is suboptimal. There are many cases where copying 'the data' is not what the caller wants.
- Consider inserting an element in the middle of an array. In this case copying 'the data' includes all elements while actually only the elements up until the insertion point need to be copied, the rest needs to be moved up one element. Using the current
reallocFn
semantics, all elements after the insertion point will end up being copied twice. - Similar to 1, but worse is when a matrix is stored in one memory block row-by-row and a column needs to be appended (or inserted). The current
reallocFn
will copy all elements, while actually the rows need to be copied separately to slightly different locations. In addition to having to move the rows, the resulting code will be more complicated, because now the new memory comes already filled with the rows and the old memory block is no longer available. This means that the elements in the rows have to be copied backwards (starting from the end). - Consider an array which has capacity for 100 elements, but only has a few in use. Appending 100 new elements will require reallocation and
reallocFn
will happily copy all the 100 elements to the new memory, while actually only a few need to be copied and the rest is undefined data. - Though probably less likely, but if there is a data structure which refers to other nodes but stored in one block of memory the copying/moving done by
reallocFn
is completely useless and will actually destroy the data-structure.
What I would like to see is in the basis an allocate
and free
, with on top a realloc
which only tries to modify the existing memory block and fails if that's not possible. In no case should it try to be smart and move data around, that's better left to the user, who has more knowledge and can move the data to it's correct position.
Please let me know what you think.