mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-09 19:06:15 +01:00
Add aligned memory allocators
When working with storage (especially GInputStream or GOutputStream) it is preferred to use page-aligned buffers so that the operating system can do page-mapping tricks as the operation passes through the kernel. Another use case is allocating memory used for vectorised operations, which must be aligned to specific boundaries. POSIX and Windows, as well as the C11 specification, provide this kind of allocator functions, and GLib already makes use of it inside GSlice. It would be convenient to have a public, portable wrapper that other projects can use. Fixes: #2574
This commit is contained in:
parent
ae0ec9b753
commit
475d574440
@ -1393,6 +1393,11 @@ g_alloca0
|
||||
g_newa
|
||||
g_newa0
|
||||
|
||||
<SUBSECTION>
|
||||
g_aligned_alloc
|
||||
g_aligned_alloc0
|
||||
g_aligned_free
|
||||
|
||||
<SUBSECTION>
|
||||
g_memmove
|
||||
g_memdup
|
||||
|
149
glib/gmem.c
149
glib/gmem.c
@ -30,6 +30,25 @@
|
||||
|
||||
#include "gmem.h"
|
||||
|
||||
#if defined(HAVE_POSIX_MEMALIGN) && !defined(_XOPEN_SOURCE)
|
||||
# define _XOPEN_SOURCE 600
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_MEMALIGN) || defined(HAVE__ALIGNED_MALLOC)
|
||||
/* Required for _aligned_malloc() and _aligned_free() on Windows */
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE__ALIGNED_MALLOC
|
||||
/* _aligned_malloc() takes parameters of aligned_malloc() in reverse order */
|
||||
# define aligned_alloc(alignment, size) _aligned_malloc (size, alignment)
|
||||
|
||||
/* _aligned_malloc()'ed memory must be freed by _align_free() on MSVC */
|
||||
# define aligned_free(x) _aligned_free (x)
|
||||
#else
|
||||
# define aligned_free(x) free (x)
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
@ -522,3 +541,133 @@ g_mem_profile (void)
|
||||
{
|
||||
g_warning (G_STRLOC ": memory profiling not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* g_aligned_alloc:
|
||||
* @n_blocks: the number of blocks to allocate
|
||||
* @n_block_bytes: the size of each block in bytes
|
||||
* @alignment: the alignment to be enforced, which must be a positive power of 2
|
||||
* and a multiple of `sizeof(void*)`
|
||||
*
|
||||
* This function is similar to g_malloc(), allocating (@n_blocks * @n_block_bytes)
|
||||
* bytes, but care is taken to align the allocated memory to with the given
|
||||
* alignment value. Additionally, it will detect possible overflow during
|
||||
* multiplication.
|
||||
*
|
||||
* Aligned memory allocations returned by this function can only be
|
||||
* freed using g_aligned_free().
|
||||
*
|
||||
* Returns: (transfer full): the allocated memory
|
||||
*
|
||||
* Since: 2.72
|
||||
*/
|
||||
gpointer
|
||||
g_aligned_alloc (gsize n_blocks,
|
||||
gsize n_block_bytes,
|
||||
gsize alignment)
|
||||
{
|
||||
gpointer res = NULL;
|
||||
gsize real_size;
|
||||
|
||||
if (G_UNLIKELY ((alignment == 0) || (alignment & (alignment - 1)) != 0))
|
||||
{
|
||||
g_error ("%s: alignment %"G_GSIZE_FORMAT" must be a positive power of two",
|
||||
G_STRLOC, alignment);
|
||||
}
|
||||
|
||||
if (G_UNLIKELY ((alignment % sizeof (void *)) != 0))
|
||||
{
|
||||
g_error ("%s: alignment %"G_GSIZE_FORMAT" must be a multiple of %"G_GSIZE_FORMAT,
|
||||
G_STRLOC, alignment, sizeof (void *));
|
||||
}
|
||||
|
||||
if (SIZE_OVERFLOWS (n_blocks, n_block_bytes))
|
||||
{
|
||||
g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes",
|
||||
G_STRLOC, n_blocks, n_block_bytes);
|
||||
}
|
||||
|
||||
real_size = n_blocks * n_block_bytes;
|
||||
|
||||
if (G_UNLIKELY (real_size == 0))
|
||||
{
|
||||
TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) real_size, 0, 0));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
#if defined(HAVE_POSIX_MEMALIGN)
|
||||
errno = posix_memalign (&res, alignment, real_size);
|
||||
#elif defined(HAVE_ALIGNED_ALLOC) || defined(HAVE__ALIGNED_MALLOC)
|
||||
/* real_size must be a multiple of alignment */
|
||||
if (real_size % alignment != 0)
|
||||
{
|
||||
gsize offset = real_size % alignment;
|
||||
|
||||
if (G_MAXSIZE - real_size < (alignment - offset))
|
||||
{
|
||||
g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"+%"G_GSIZE_FORMAT" bytes",
|
||||
G_STRLOC, real_size, (alignment - offset));
|
||||
}
|
||||
|
||||
real_size += (alignment - offset);
|
||||
}
|
||||
|
||||
res = aligned_alloc (alignment, real_size);
|
||||
#elif defined(HAVE_MEMALIGN)
|
||||
res = memalign (alignment, real_size);
|
||||
#else
|
||||
# error "This platform does not have an aligned memory allocator."
|
||||
#endif
|
||||
|
||||
TRACE (GLIB_MEM_ALLOC((void*) res, (unsigned int) real_size, 0, 0));
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes",
|
||||
G_STRLOC, real_size);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_aligned_alloc0:
|
||||
* @n_blocks: the number of blocks to allocate
|
||||
* @n_block_bytes: the size of each block in bytes
|
||||
* @alignment: the alignment to be enforced, which must be a positive power of 2
|
||||
* and a multiple of `sizeof(void*)`
|
||||
*
|
||||
* This function is similar to g_aligned_alloc(), but it will
|
||||
* also clear the allocated memory before returning it.
|
||||
*
|
||||
* Returns: (transfer full): the allocated, cleared memory
|
||||
*
|
||||
* Since: 2.72
|
||||
*/
|
||||
gpointer
|
||||
g_aligned_alloc0 (gsize n_blocks,
|
||||
gsize n_block_bytes,
|
||||
gsize alignment)
|
||||
{
|
||||
gpointer res = g_aligned_alloc (n_blocks, n_block_bytes, alignment);
|
||||
|
||||
if (G_LIKELY (res != NULL))
|
||||
memset (res, 0, n_blocks * n_block_bytes);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_aligned_free:
|
||||
* @mem: (nullable): the memory to deallocate
|
||||
*
|
||||
* Frees the memory allocated by g_aligned_alloc().
|
||||
*
|
||||
* Since: 2.72
|
||||
*/
|
||||
void
|
||||
g_aligned_free (gpointer mem)
|
||||
{
|
||||
aligned_free (mem);
|
||||
}
|
||||
|
11
glib/gmem.h
11
glib/gmem.h
@ -111,6 +111,17 @@ gpointer g_try_realloc_n (gpointer mem,
|
||||
gsize n_blocks,
|
||||
gsize n_block_bytes) G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
GLIB_AVAILABLE_IN_2_72
|
||||
gpointer g_aligned_alloc (gsize n_blocks,
|
||||
gsize n_block_bytes,
|
||||
gsize alignment) G_GNUC_WARN_UNUSED_RESULT G_GNUC_ALLOC_SIZE2(1,2);
|
||||
GLIB_AVAILABLE_IN_2_72
|
||||
gpointer g_aligned_alloc0 (gsize n_blocks,
|
||||
gsize n_block_bytes,
|
||||
gsize alignment) G_GNUC_WARN_UNUSED_RESULT G_GNUC_ALLOC_SIZE2(1,2);
|
||||
GLIB_AVAILABLE_IN_2_72
|
||||
void g_aligned_free (gpointer mem);
|
||||
|
||||
#if defined(glib_typeof) && GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_58
|
||||
#define g_clear_pointer(pp, destroy) \
|
||||
G_STMT_START \
|
||||
|
15
meson.build
15
meson.build
@ -648,8 +648,19 @@ if host_system == 'android'
|
||||
endif
|
||||
|
||||
|
||||
# Check that posix_memalign() is usable; must use header
|
||||
if host_system != 'windows' and cc.has_function('posix_memalign', prefix : '#include <stdlib.h>')
|
||||
if cc.has_function('memalign', prefix: '#include <stdlib.h>\n#include <malloc.h>')
|
||||
glib_conf.set('HAVE_MEMALIGN', 1)
|
||||
endif
|
||||
|
||||
if cc.has_function('_aligned_malloc', prefix: '#include <malloc.h>')
|
||||
glib_conf.set('HAVE__ALIGNED_MALLOC', 1)
|
||||
endif
|
||||
|
||||
if host_system != 'windows' and cc.has_function('aligned_alloc', prefix: '#include <stdlib.h>')
|
||||
glib_conf.set('HAVE_ALIGNED_ALLOC', 1)
|
||||
endif
|
||||
|
||||
if host_system != 'windows' and cc.has_function('posix_memalign', prefix: '#include <stdlib.h>')
|
||||
glib_conf.set('HAVE_POSIX_MEMALIGN', 1)
|
||||
endif
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user