212 5. Engine Support Systems
// Calculate the adjusted address, and return as a
// pointer.
U32 alignedAddress = rawAddress + adjustment;
return (void*)alignedAddress;
}
When this block is later freed, the code will pass us the adjusted address,
not the original address we allocated. How, then, do we actually free the mem-
ory? We need some way to convert an adjusted address back into the original,
possibly misaligned address.
To accomplish this, we simply store some meta-information in those
extra bytes we allocated in order to align the data in the fi rst place. The
smallest adjustment we might make is one byte. That’s enough room to
store the number of bytes by which the address was adjusted (since it will
never be more than 256). We always store this information in the byte im-
mediately preceding the adjusted address (no matt er how many bytes of
adjustment we actually added), so that it is trivial to fi nd it again, given the
adjusted address. Here’s how the modifi ed allocateAligned() function
would look.
// Aligned allocation function. IMPORTANT: ‘alignment’
// must be a power of 2 (typically 4 or 16).
void*allocateAligned(U32 size_bytes, U32 alignment)
{
// Clients must call allocateUnaligned() and
// freeUnaligned() if alignment == 1.
ASSERT(alignment > 1);
// Determine total amount of memory to allocate.
U32 expandedSize_bytes = size_bytes + alignment;
// Allocate an unaligned block & convert address to a
// U32.
U32 rawAddress
= (U32)allocateUnaligned(expandedSize_bytes);
// Calculate the adjustment by masking off the lower
// bits of the address, to determine how “misaligned”
// it is.
U32 mask = (alignment – 1);
U32 misalignment = (rawAddress & mask);
U32 adjustment = alignment – misalignment;
// Calculate the adjusted address, and return as a
// pointer.
U32 alignedAddress = rawAddress + adjustment;