[glib/ebassi/aligned-alloc: 1/3] Add aligned memory allocators




commit 475d57444070280a2deba108d86470228d81f2ca
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

 docs/reference/glib/glib-sections.txt |   5 ++
 glib/gmem.c                           | 149 ++++++++++++++++++++++++++++++++++
 glib/gmem.h                           |  11 +++
 meson.build                           |  15 +++-
 4 files changed, 178 insertions(+), 2 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index c8681fa2f..0d534c295 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -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
diff --git a/glib/gmem.c b/glib/gmem.c
index 605eac1ff..67e283575 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,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);
+}
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]