aligned means: returned memory address starts with an address that are divisible by alignment
eg. alignment = 16, we want the allocated memory address starts at 0x10, 0x20, 0x1010 etc, instead of 0x1A, 0x100B etc.
void* aligned_malloc(size_t size, size_t alignment){
if (size == 0 || alignment == 0){
return NULL;
}
// if alignment is not power of 2
if ((alignment & (alignment - 1)) != 0){
return NULL;
}
size_t offset = sizeof(void*) + alignment - 1;
void* raw = malloc(size + offset);
if (!raw){
return NULL;
}
// round up to next aligned address
uintptr_t aligned_addr = ((uintptr_t)raw + offset) & ~ (alignment - 1)
// (unitptr_t)raw + offset has round up that definitely includes the next algnment
// we just need to clear the lower bits by & ~ (alignment - 1)
void aligned = (void*)aligned_addr;
// cast aligned to an array of pointers, and we stored pointer raw right before it
((void**)aligned)[-1] = raw;
return aligned;
}
void aligned_free(void* aligned){
if (!aligned){
return;
}
// free the raw pointer returned by malloc, just one pointer before aligned, where we stored.
free(((void**)aligned)[-1]);
}