When an allocation cannot grow in place, realloc falls back to moving it - allocate a new block, copy the bytes over, free the old one. Today you complete realloc with that relocate-and-copy path.
Relocate a growing allocation that cannot expand in place, preserving its contents.
If an allocation cannot grow where it sits, realloc does what a caller expects: it finds space elsewhere, copies the old contents into it, releases the old block, and returns the new offset. The copy length is the old payload’s capacity, so every byte the caller had written survives the move. This is the general fallback, and it is why a realloc’d pointer can change - the classic footgun in C, made explicit here as a changed offset.
With this, realloc is complete: it shrinks in place, grows in place when a free
neighbour allows, and relocates when it must. Notice how it is assembled entirely
from pieces you already built - Malloc, Free, and byte copying - which is the
mark of a clean allocator core. One convenience allocation remains before the demo:
calloc, which allocates and zeroes.
// fall-through when in-place growth is impossible:newPayload, err := a.Malloc(n) // find space elsewhereif err != nil { return -1, err }oldPayloadCap := size - overheadcopy(a.buf[newPayload:newPayload+oldPayloadCap], a.buf[payload:payload+oldPayloadCap])a.Free(payload) // release the old blockreturn newPayload, nil