[glib/ebassi/aligned-alloc] Add aligned memory allocators




commit e7a378ab2a7c22d4b8f3c9fac5dd44cfb3454c8e
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Thu Jan 6 18:03:16 2022 +0000

    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

 glib/gmem.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 glib/gmem.h |  11 ++++++
 meson.build |  15 ++++++-
 3 files changed, 153 insertions(+), 2 deletions(-)
---
diff --git a/glib/gmem.c b/glib/gmem.c
index 605eac1ff..988b80ec7 100644
--- a/glib/gmem.c
+++ b/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,113 @@ 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, as a power of 2
+ *
+ * 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 (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;
+
+      real_size += (alignment - offset);
+    }
+
+  res = aligned_alloc (alignment, real_size);
+#elif defined(HAVE_MEMALIGN)
+  res = memalign (alignment, real_size);
+#else
+  res = malloc (real_size);
+#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, as a power of 2
+ *
+ * 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);
+}
diff --git a/glib/gmem.h b/glib/gmem.h
index 47c4735ac..d29907a67 100644
--- a/glib/gmem.h
+++ b/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                                           \
diff --git a/meson.build b/meson.build
index 6e566d483..d7b770ace 100644
--- a/meson.build
+++ b/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
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]