I spent like half an hour programming up a set of functions to make the allocation / deallocate of pointers easier within a bigger project. I felt that they were satisfactory, but was wondering if there's anywhere I can improve, especially in the area of naming, usability, and performance (are there any faster versions of the allocation functions I wrapped?). I also wanted to ask; are there any places where this program could benefit from restrict
? I understand the vague meaning of the keyword but have yet to come across a situation in which I can confidently say restrict
was necessary.
I've already run this program through Valgrind and ensured nothing leaks. Take a look.
// Allocate.h
#include <stdio.h>
#include <memory.h>
/**
* @brief A wrapped pointer. Includes the size of the pointer, alongside
* the pointer itself.
*/
typedef struct
{
/**
* @brief An ambiguous pointer to an ambiguous location at memory.
*/
void* inner;
/**
* @brief The size of the allocated pointer. Editing this will likely
* cause problems as the interface either reads data that doesn't
* exist, or cuts off data that @b does exist.
*/
size_t size;
} ptr_t;
/**
* @brief An empty wrapped pointer initializer.
*/
#define WRAPPED_POINTER_EMPTY \
(ptr_t) { NULL, 0 }
/**
* @brief Allocate a block of @param size. The block is initialized to
* nothing, and will be (probably) filled with garbage data.
* @param size Size of the allocated block.
* @return The pointer created, alongside its assigned size.
*/
ptr_t AllocateBlock(size_t size);
/**
* @brief Much the same as @ref AllocateBlock, allocate a block of @param
* size. This block, however, is fully initialized to the value 0.
* @param size The size of the allocated block.
* @return The pointer created, alongside its assigned size.
*/
ptr_t AllocateZeroedBlock(size_t size);
/**
* @brief Free the given pointer. If this is passed a NULL pointer, the
* fatal @enum memory_free_failure exception is raised. The pointer
* associated with the given @struct ptr_t object is set to NULL, and the
* size is set to 0.
* @param ptr The pointer to be freed.
*/
void FreeBlock(ptr_t* ptr);
/**
* @brief Copy the contents and size of a wrapped pointer to the shell of
* another. @param dest is overwritten during this operation, but no
* changes are made to @param src. If the two pointers are the same, if
* either are NULL, or if the contents of @param src is NULL, nothing is
* done.
* @param dest The destination pointer, where things should be copied to.
* @param src The source pointer, where things are to be copied from.
*/
void CopyBlock(ptr_t* dest, const ptr_t src);
/**
* @brief Shrink the given pointer's usable section of memory. This is not
* guaranteed to keep the data inside the pointer. For that behavior, check
* out @ref ShrinkBlockSafe. If the passed-in size is greater than or equal
* to the pointer's current size, nothing is done.
* @param ptr The pointer to shrink.
* @param new_size The new size of the pointer.
*/
void ShrinkBlock(ptr_t* ptr, size_t new_size);
/**
* @brief Grow the given pointer's usable section of memory. This is not
* guaranteed to keep the data inside the pointer. For that behavior, check
* out @ref ExpandBlockSafe. If the passed-in size is less than or equal
* to the pointer's current size, nothing is done.
* @param ptr The pointer to shrink.
* @param new_size The new size of the pointer.
*/
void ExpandBlock(ptr_t* ptr, size_t new_size);
/**
* @brief Resize the given pointer's usable section of memory. This,
* depending on the size passed to the function, either calls @ref
* ShrinkBlock or @ref ExpandBlock. If the size is equal to the given
* pointer's current size, nothing is done.
* @param ptr The pointer to be resized.
* @param new_size The requested new size of the pointer.
*/
void ReallocateBlock(ptr_t* ptr, size_t new_size);
/**
* @brief Shrink the given pointer's usable section of memory. This is
* guaranteed to keep the data inside the pointer. If the passed-in size is
* greater than or equal to the pointer's current size, nothing is done.
* @param ptr The pointer to shrink.
* @param new_size The new size of the pointer.
*/
void ShrinkBlockSafe(ptr_t* ptr, size_t new_size);
/**
* @brief Grow the given pointer's usable section of memory. This is
* guaranteed to keep the data inside the pointer. If the passed-in size is
* less than or equal to the pointer's current size, nothing is done.
* @param ptr The pointer to shrink.
* @param new_size The new size of the pointer.
*/
void ExpandBlockSafe(ptr_t* ptr, size_t new_size);
/**
* @brief Resize the given pointer's usable section of memory. This,
* depending on the size passed to the function, either calls @ref
* ShrinkBlockSafe or @ref ExpandBlockSafe. If the size is equal to the
* given pointer's current size, nothing is done.
* @param ptr The pointer to be resized.
* @param new_size The requested new size of the pointer.
*/
void ReallocateBlockSafe(ptr_t* ptr, size_t new_size);
/**
* @brief Set the contents of the given pointer to the given value.
* @param ptr The pointer to set.
* @param contents The new contents of the pointer.
*/
static inline void SetBlockContents(ptr_t* ptr, void* contents)
{
if (contents == NULL) return;
memcpy(ptr->inner, contents, ptr->size);
}
// Allocate.c
#include "Allocate.h"
// Error reporting, provides "ReportError".
// Ignore it, treat it as a call to exit().
#include <Output/Error.h>
ptr_t AllocateBlock(size_t size)
{
ptr_t created_ptr = {NULL, size};
created_ptr.inner = malloc(size);
if (created_ptr.inner == NULL)
ReportError(memory_allocation_failure, false);
return created_ptr;
}
ptr_t AllocateZeroedBlock(size_t size)
{
ptr_t created_ptr = {NULL, size};
created_ptr.inner = calloc(1, size);
if (created_ptr.inner == NULL)
ReportError(memory_allocation_failure, false);
return created_ptr;
}
void FreeBlock(ptr_t* ptr)
{
if (ptr == NULL || ptr->inner == NULL)
ReportError(memory_free_failure, false);
free(ptr->inner);
ptr->inner = NULL;
ptr->size = 0;
}
void CopyBlock(ptr_t* dest, const ptr_t src)
{
if (dest == NULL || dest->inner == src.inner) return;
if (src.inner == NULL) return;
if (dest->inner != NULL) FreeBlock(dest);
*dest = AllocateBlock(src.size);
SetBlockContents(dest, src.inner);
}
static void ChangeBlockSize(ptr_t* ptr, size_t new_size)
{
if (ptr->inner != NULL) FreeBlock(ptr);
*ptr = AllocateBlock(new_size);
}
void ShrinkBlock(ptr_t* ptr, size_t new_size)
{
if (new_size >= ptr->size) return;
ChangeBlockSize(ptr, new_size);
}
void ExpandBlock(ptr_t* ptr, size_t new_size)
{
if (new_size <= ptr->size) return;
ChangeBlockSize(ptr, new_size);
}
void ReallocateBlock(ptr_t* ptr, size_t new_size)
{
if (new_size == ptr->size) return;
if (new_size > ptr->size) ExpandBlock(ptr, new_size);
else ShrinkBlock(ptr, new_size);
}
static void ChangeBlockSizeSafe(ptr_t* ptr, size_t new_size)
{
ptr_t contents = AllocateBlock(new_size);
if (ptr->inner != NULL)
{
SetBlockContents(&contents, ptr->inner);
FreeBlock(ptr);
}
*ptr = AllocateBlock(new_size);
SetBlockContents(ptr, contents.inner);
}
void ShrinkBlockSafe(ptr_t* ptr, size_t new_size)
{
if (new_size >= ptr->size) return;
ChangeBlockSizeSafe(ptr, new_size);
}
void ExpandBlockSafe(ptr_t* ptr, size_t new_size)
{
if (new_size <= ptr->size) return;
ChangeBlockSizeSafe(ptr, new_size);
}
void ReallocateBlockSafe(ptr_t* ptr, size_t new_size)
{
if (new_size == ptr->size) return;
if (new_size > ptr->size) ExpandBlockSafe(ptr, new_size);
else ShrinkBlockSafe(ptr, new_size);
}