AlignedAlloc<>: Use posix_memalloc() on MacOS

While on MacOS aligned_alloc() is only available starting from 10.15,
the posix_memalign() is available on older releases and can be used
directly, instead of the current malloc()-based over-allocating
solution.

Bug: None
Change-Id: I726aedc4b917dffc091698dbf7832a8f740c10f1
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/13720
Reviewed-by: Sylvain Defresne <sdefresne@chromium.org>
Reviewed-by: Takuto Ikuta <tikuta@google.com>
Commit-Queue: David Turner <digit@google.com>
diff --git a/src/util/aligned_alloc.h b/src/util/aligned_alloc.h
index f5cf757..99ecd29 100644
--- a/src/util/aligned_alloc.h
+++ b/src/util/aligned_alloc.h
@@ -13,17 +13,15 @@
 
 #define IMPL_ALIGNED_ALLOC_CXX17 1
 #define IMPL_ALIGNED_ALLOC_WIN32 2
-#define IMPL_ALIGNED_ALLOC_MALLOC 3
+#define IMPL_ALIGNED_ALLOC_POSIX 3
 
 #ifndef IMPL_ALIGNED_ALLOC
 #ifdef _WIN32
 #define IMPL_ALIGNED_ALLOC IMPL_ALIGNED_ALLOC_WIN32
-#elif defined(__APPLE__) && defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \
-    __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
+#elif defined(__APPLE__)
 // Note that aligned_alloc() is only available at runtime starting from
-// OSX 10.15, so use malloc() when compiling binaries that must run on older
-// releases.
-#define IMPL_ALIGNED_ALLOC IMPL_ALIGNED_ALLOC_MALLOC
+// OSX 10.15, so use posix_memalign() instead which is more portable.
+#define IMPL_ALIGNED_ALLOC IMPL_ALIGNED_ALLOC_POSIX
 #else
 #define IMPL_ALIGNED_ALLOC IMPL_ALIGNED_ALLOC_CXX17
 #endif
@@ -33,8 +31,8 @@
 #include <malloc.h>  // for _aligned_malloc() and _aligned_free()
 #endif
 
-#if IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_MALLOC
-#include "base/logging.h"  // For DCHECK()
+#if IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_POSIX
+#include "base/logging.h"  // for CHECK()
 #endif
 
 // AlignedAlloc<N> provides Alloc() and Free() methods that can be
@@ -43,8 +41,8 @@
 //
 // The implementation uses std::aligned_alloc() when it is available,
 // or uses fallbacks otherwise. On Win32, _aligned_malloc() and
-// _aligned_free() are used, while for older MacOS releases, ::malloc() is
-// used directly with a small trick.
+// _aligned_free() are used, while for MacOS releases, ::posix_memaloc()
+// is used.
 template <size_t ALIGNMENT>
 struct AlignedAlloc {
   static void* Alloc(size_t size) {
@@ -52,35 +50,10 @@
                   "ALIGNMENT must be a power of 2");
 #if IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_WIN32
     return _aligned_malloc(size, ALIGNMENT);
-#elif IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_MALLOC
-    if (ALIGNMENT <= sizeof(void*)) {
-      return ::malloc(size);
-    } else if (size == 0) {
-      return nullptr;
-    } else {
-      // Allocation size must be a multiple of ALIGNMENT
-      DCHECK((size % ALIGNMENT) == 0);
-
-      // Allocate block and store its address just before just before the
-      // result's address, as in:
-      //    ________________________________________
-      //   |    |          |                        |
-      //   |    | real_ptr |                        |
-      //   |____|__________|________________________|
-      //
-      //   ^               ^
-      //   real_ptr         result
-      //
-      // Note that malloc() guarantees that results are aligned on sizeof(void*)
-      // if the allocation size if larger than sizeof(void*). Hence, only
-      // |ALIGNMENT - sizeof(void*)| extra bytes are required.
-      void* real_block = ::malloc(size + ALIGNMENT - sizeof(void*));
-      auto addr = reinterpret_cast<uintptr_t>(real_block) + sizeof(void*);
-      uintptr_t padding = (ALIGNMENT - addr) % ALIGNMENT;
-      addr += padding;
-      reinterpret_cast<void**>(addr - sizeof(void*))[0] = real_block;
-      return reinterpret_cast<void*>(addr);
-    }
+#elif IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_POSIX
+    void* ptr = nullptr;
+    CHECK(!posix_memalign(&ptr, ALIGNMENT, size));
+    return ptr;
 #else  // IMPL_ALIGNED_ALLOC_CXX17
     return std::aligned_alloc(ALIGNMENT, size);
 #endif
@@ -89,16 +62,8 @@
   static void Free(void* block) {
 #if IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_WIN32
     _aligned_free(block);
-#elif IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_MALLOC
-    if (ALIGNMENT <= sizeof(void*)) {
-      ::free(block);
-    } else if (block) {
-      if (ALIGNMENT > sizeof(void*)) {
-        // Read address of real block just before the aligned block.
-        block = *(reinterpret_cast<void**>(block) - 1);
-      }
-      ::free(block);
-    }
+#elif IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_POSIX
+    return ::free(block);
 #else
     // Allocation came from std::aligned_alloc()
     return std::free(block);