(root)/
coreutils-9.4/
lib/
alignalloc.c
       1  /* aligned memory allocation
       2  
       3     Copyright 2022-2023 Free Software Foundation, Inc.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation, either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  /* Written by Paul Eggert.  */
      19  
      20  #include <config.h>
      21  
      22  #define ALIGNALLOC_INLINE _GL_EXTERN_INLINE
      23  #include "alignalloc.h"
      24  
      25  #include <limits.h>
      26  #include <stdckdint.h>
      27  #include <stdint.h>
      28  
      29  #if !ALIGNALLOC_VIA_ALIGNED_ALLOC
      30  # if HAVE_POSIX_MEMALIGN
      31  
      32  /* posix_memalign requires the alignment to be a power-of-two multiple of
      33     sizeof (void *), whereas alignalloc requires it to be a power of two.
      34     To make it OK for the latter to call the former, check that
      35     sizeof (void *) is a power of two, which is true on all known platforms.
      36     This check is here rather than in alignalloc.h to save the compiler
      37     the trouble of checking it each time alignalloc.h is included.  */
      38  static_assert (! (sizeof (void *) & (sizeof (void *) - 1)));
      39  
      40  # else /* !HAVE_POSIX_MEMALIGN */
      41  
      42  /* Return P aligned down to ALIGNMENT, which should be a power of two.  */
      43  
      44  static void *
      45  align_down (void *p, idx_t alignment)
      46  {
      47    char *c = p;
      48    return c - ((uintptr_t) p & (alignment - 1));
      49  }
      50  
      51  /* If alignalloc returned R and the base of the originally-allocated
      52     storage is less than R - UCHAR_MAX, return the address of a pointer
      53     holding the base of the originally-allocated storage.  */
      54  
      55  static void **
      56  address_of_pointer_to_malloced (unsigned char *r)
      57  {
      58    /* The pointer P is located at the highest address A such that A is
      59       aligned for pointers, and A + sizeof P < R so that there is room
      60       for a 0 byte at R - 1.  This approach assumes UCHAR_MAX is large
      61       enough so that there is room for P; although true on all
      62       plausible platforms, check the assumption to be safe.  */
      63    static_assert (sizeof (void *) + alignof (void *) - 1 <= UCHAR_MAX);
      64  
      65    return align_down (r - 1 - sizeof (void *), alignof (void *));
      66  }
      67  
      68  /* Return an ALIGNMENT-aligned pointer to new storage of size SIZE,
      69     or a null pointer (setting errno) if memory is exhausted.
      70     ALIGNMENT must be a power of two.
      71     If SIZE is zero, on success return a unique pointer each time.
      72     To free storage later, call alignfree.  */
      73  
      74  void *
      75  alignalloc (idx_t alignment, idx_t size)
      76  {
      77    /* malloc (ALIGNMENT + SIZE); if it succeeds, there must be at least
      78       one byte available before the returned pointer.  It's OK if
      79       ALIGNMENT + SIZE fits in size_t but not idx_t.  */
      80  
      81    size_t malloc_size;
      82    unsigned char *q;
      83    if (ckd_add (&malloc_size, size, alignment)
      84        || ! (q = malloc (malloc_size)))
      85      {
      86        errno = ENOMEM;
      87        return NULL;
      88      }
      89  
      90    unsigned char *r = align_down (q + alignment, alignment);
      91    idx_t offset = r - q;
      92  
      93    if (offset <= UCHAR_MAX)
      94      r[-1] = offset;
      95    else
      96      {
      97        r[-1] = 0;
      98        *address_of_pointer_to_malloced (r) = q;
      99      }
     100  
     101    return r;
     102  }
     103  
     104  /* Free storage allocated via alignalloc.  Do nothing if PTR is null.  */
     105  
     106  void
     107  alignfree (void *ptr)
     108  {
     109    if (ptr)
     110      {
     111        unsigned char *r = ptr;
     112        unsigned char offset = r[-1];
     113        void *q = offset ? r - offset : *address_of_pointer_to_malloced (r);
     114        free (q);
     115      }
     116  }
     117  
     118  # endif /* ! HAVE_POSIX_MEMALIGN */
     119  #endif /* ! ALIGNALLOC_VIA_ALIGNED_ALLOC */