master
  1/**
  2 * This file has no copyright assigned and is placed in the Public Domain.
  3 * This file is part of the mingw-w64 runtime package.
  4 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
  5 */
  6/*
  7  __mingw_aligned_malloc and friends, implemented using Microsoft's public
  8  interfaces and with the help of the algorithm description provided
  9  by Wu Yongwei: http://sourceforge.net/mailarchive/message.php?msg_id=3847075
 10
 11  I hereby place this implementation in the public domain.
 12               -- Steven G. Johnson (stevenj@alum.mit.edu)
 13*/
 14
 15#include <stdlib.h>
 16#include <errno.h>
 17#include <stddef.h>		/* ptrdiff_t */
 18#include <stdint.h>		/* uintptr_t */
 19#include <string.h>		/* memmove */
 20
 21#define NOT_POWER_OF_TWO(n) (((n) & ((n) - 1)))
 22#define UI(p) ((uintptr_t) (p))
 23#define CP(p) ((char *) p)
 24
 25#define GAP(offset) ((0 - offset) & (sizeof (void *) -1))
 26#define PTR_ALIGN(p0, alignment, offset)				\
 27            ((void *) (((UI(p0) + (alignment + GAP(offset) + sizeof(void*)) + offset)	\
 28			& (~UI(alignment)))				\
 29		       - offset))
 30
 31/* Pointer must sometimes be aligned; assume sizeof(void*) is a power of two. */
 32#define ORIG_PTR(p) (*(((void **) (UI(p) & (~UI(sizeof(void*) - 1)))) - 1))
 33
 34void *
 35__mingw_aligned_offset_malloc (size_t size, size_t alignment, size_t offset)
 36{
 37  void *p0, *p;
 38
 39  if (NOT_POWER_OF_TWO (alignment))
 40    {
 41      errno = EINVAL;
 42      return ((void *) 0);
 43    }
 44  if (size == 0)
 45    return ((void *) 0);
 46  if (alignment < sizeof (void *))
 47    alignment = sizeof (void *);
 48  alignment--;
 49
 50  /* Including the extra sizeof(void*) is overkill on a 32-bit
 51     machine, since malloc is already 8-byte aligned, as long
 52     as we enforce alignment >= 8 ...but oh well.  */
 53
 54  p0 = malloc (size + (alignment + GAP (offset) + sizeof (void *)));
 55  if (!p0)
 56    return ((void *) 0);
 57  p = PTR_ALIGN (p0, alignment, offset);
 58  ORIG_PTR (p) = p0;
 59  return p;
 60}
 61
 62void *
 63__mingw_aligned_malloc (size_t size, size_t alignment)
 64{
 65  return __mingw_aligned_offset_malloc (size, alignment, 0);
 66}
 67
 68void
 69__mingw_aligned_free (void *memblock)
 70{
 71  if (memblock)
 72    free (ORIG_PTR (memblock));
 73}
 74
 75void *
 76__mingw_aligned_offset_realloc (void *memblock, size_t size,
 77				size_t alignment, size_t offset)
 78{
 79  void *p0, *p;
 80  ptrdiff_t shift;
 81
 82  if (!memblock)
 83    return __mingw_aligned_offset_malloc (size, alignment, offset);
 84  if (NOT_POWER_OF_TWO (alignment))
 85    goto bad;
 86  if (size == 0)
 87    {
 88      __mingw_aligned_free (memblock);
 89      return ((void *) 0);
 90    }
 91  if (alignment < sizeof (void *))
 92    alignment = sizeof (void *);
 93  alignment--;
 94
 95  p0 = ORIG_PTR (memblock);
 96  /* It is an error for the alignment to change. */
 97  if (memblock != PTR_ALIGN (p0, alignment, offset))
 98    goto bad;
 99  shift = CP (memblock) - CP (p0);
100
101  p0 = realloc (p0, size + (alignment + GAP (offset) + sizeof (void *)));
102  if (!p0)
103    return ((void *) 0);
104  p = PTR_ALIGN (p0, alignment, offset);
105
106  /* Relative shift of actual data may be different from before, ugh.  */
107  if (shift != CP (p) - CP (p0))
108    /* ugh, moves more than necessary if size is increased.  */
109    memmove (CP (p), CP (p0) + shift, size);
110
111  ORIG_PTR (p) = p0;
112  return p;
113
114bad:
115  errno = EINVAL;
116  return ((void *) 0);
117}
118
119void *
120__mingw_aligned_realloc (void *memblock, size_t size, size_t alignment)
121{
122  return __mingw_aligned_offset_realloc (memblock, size, alignment, 0);
123}
124
125size_t
126__mingw_aligned_msize (void *memblock, size_t alignment, size_t offset)
127{
128  void *p0;
129
130  if (!memblock || NOT_POWER_OF_TWO (alignment))
131    {
132      errno = EINVAL;
133      return (size_t)-1;
134    }
135  if (alignment < sizeof (void *))
136    alignment = sizeof (void *);
137  alignment--;
138
139  p0 = ORIG_PTR (memblock);
140
141  /* It is an error if the alignment or offset does not match. */
142  if (memblock != PTR_ALIGN (p0, alignment, offset))
143    {
144      errno = EINVAL;
145      return (size_t)-1;
146    }
147
148  return _msize (p0) - (alignment + GAP (offset) + sizeof (void *));
149}