/*------------------------------------------------------------------------- * * alignedalloc.c * Allocator functions to implement palloc_aligned * * This is not a fully-fledged MemoryContext type as there is no means to * create a MemoryContext of this type. The code here only serves to allow * operations such as pfree() and repalloc() to work correctly on a memory * chunk that was allocated by palloc_aligned(). * * Portions Copyright (c) 2022-2023, PostgreSQL Global Development Group * * IDENTIFICATION * src/backend/utils/mmgr/alignedalloc.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "utils/memdebug.h" #include "utils/memutils_memorychunk.h" /* * AlignedAllocFree * Frees allocated memory; memory is removed from its owning context. */ void AlignedAllocFree(void *pointer) { MemoryChunk *chunk = PointerGetMemoryChunk(pointer); void *unaligned; VALGRIND_MAKE_MEM_DEFINED(chunk, sizeof(MemoryChunk)); Assert(!MemoryChunkIsExternal(chunk)); /* obtain the original (unaligned) allocated pointer */ unaligned = MemoryChunkGetBlock(chunk); #ifdef MEMORY_CONTEXT_CHECKING /* Test for someone scribbling on unused space in chunk */ if (!sentinel_ok(pointer, chunk->requested_size)) elog(WARNING, "detected write past chunk end in %s %p", GetMemoryChunkContext(unaligned)->name, chunk); #endif pfree(unaligned); } /* * AlignedAllocRealloc * Change the allocated size of a chunk and return possibly a different * pointer to a memory address aligned to the same boundary as the * originally requested alignment. The contents of 'pointer' will be * copied into the returned pointer up until 'size'. Any additional * memory will be uninitialized. */ void * AlignedAllocRealloc(void *pointer, Size size) { MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer); Size alignto; void *unaligned; MemoryContext ctx; Size old_size; void *newptr; VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk)); alignto = MemoryChunkGetValue(redirchunk); unaligned = MemoryChunkGetBlock(redirchunk); /* sanity check this is a power of 2 value */ Assert((alignto & (alignto - 1)) == 0); /* * Determine the size of the original allocation. We can't determine this * exactly as GetMemoryChunkSpace() returns the total space used for the * allocation, which for contexts like aset includes rounding up to the * next power of 2. However, this value is just used to memcpy() the old * data into the new allocation, so we only need to concern ourselves with * not reading beyond the end of the original allocation's memory. The * drawback here is that we may copy more bytes than we need to, which * only amounts to wasted effort. We can safely subtract the extra bytes * that we requested to allow us to align the pointer. We must also * subtract the space for the unaligned pointer's MemoryChunk since * GetMemoryChunkSpace should have included that. This does assume that * all context types use MemoryChunk as a chunk header. */ old_size = GetMemoryChunkSpace(unaligned) - PallocAlignedExtraBytes(alignto) - sizeof(MemoryChunk); #ifdef MEMORY_CONTEXT_CHECKING /* check that GetMemoryChunkSpace returned something realistic */ Assert(old_size >= redirchunk->requested_size); #endif ctx = GetMemoryChunkContext(unaligned); newptr = MemoryContextAllocAligned(ctx, size, alignto, 0); /* * We may memcpy beyond the end of the original allocation request size, * so we must mark the entire allocation as defined. */ VALGRIND_MAKE_MEM_DEFINED(pointer, old_size); memcpy(newptr, pointer, Min(size, old_size)); pfree(unaligned); return newptr; } /* * AlignedAllocGetChunkContext * Return the MemoryContext that 'pointer' belongs to. */ MemoryContext AlignedAllocGetChunkContext(void *pointer) { MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer); MemoryContext cxt; VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk)); Assert(!MemoryChunkIsExternal(redirchunk)); cxt = GetMemoryChunkContext(MemoryChunkGetBlock(redirchunk)); VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk)); return cxt; } /* * AlignedAllocGetChunkSpace * Given a currently-allocated chunk, determine the total space * it occupies (including all memory-allocation overhead). */ Size AlignedAllocGetChunkSpace(void *pointer) { MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer); void *unaligned; Size space; VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk)); unaligned = MemoryChunkGetBlock(redirchunk); space = GetMemoryChunkSpace(unaligned); VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk)); return space; }