aboutsummaryrefslogtreecommitdiff
path: root/lib/libc/mingw/misc/mingw-aligned-malloc.c
blob: 02872c921a1871da939c9d68352ff003c7168928 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/**
 * This file has no copyright assigned and is placed in the Public Domain.
 * This file is part of the mingw-w64 runtime package.
 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
 */
/*
  __mingw_aligned_malloc and friends, implemented using Microsoft's public
  interfaces and with the help of the algorithm description provided
  by Wu Yongwei: http://sourceforge.net/mailarchive/message.php?msg_id=3847075

  I hereby place this implementation in the public domain.
               -- Steven G. Johnson (stevenj@alum.mit.edu)
*/

#include <stdlib.h>
#include <errno.h>
#include <stddef.h>		/* ptrdiff_t */
#include <stdint.h>		/* uintptr_t */
#include <string.h>		/* memmove */

#define NOT_POWER_OF_TWO(n) (((n) & ((n) - 1)))
#define UI(p) ((uintptr_t) (p))
#define CP(p) ((char *) p)

#define GAP(offset) ((0 - offset) & (sizeof (void *) -1))
#define PTR_ALIGN(p0, alignment, offset)				\
            ((void *) (((UI(p0) + (alignment + GAP(offset) + sizeof(void*)) + offset)	\
			& (~UI(alignment)))				\
		       - offset))

/* Pointer must sometimes be aligned; assume sizeof(void*) is a power of two. */
#define ORIG_PTR(p) (*(((void **) (UI(p) & (~UI(sizeof(void*) - 1)))) - 1))

void *
__mingw_aligned_offset_malloc (size_t size, size_t alignment, size_t offset)
{
  void *p0, *p;

  if (NOT_POWER_OF_TWO (alignment))
    {
      errno = EINVAL;
      return ((void *) 0);
    }
  if (size == 0)
    return ((void *) 0);
  if (alignment < sizeof (void *))
    alignment = sizeof (void *);
  alignment--;

  /* Including the extra sizeof(void*) is overkill on a 32-bit
     machine, since malloc is already 8-byte aligned, as long
     as we enforce alignment >= 8 ...but oh well.  */

  p0 = malloc (size + (alignment + GAP (offset) + sizeof (void *)));
  if (!p0)
    return ((void *) 0);
  p = PTR_ALIGN (p0, alignment, offset);
  ORIG_PTR (p) = p0;
  return p;
}

void *
__mingw_aligned_malloc (size_t size, size_t alignment)
{
  return __mingw_aligned_offset_malloc (size, alignment, 0);
}

void
__mingw_aligned_free (void *memblock)
{
  if (memblock)
    free (ORIG_PTR (memblock));
}

void *
__mingw_aligned_offset_realloc (void *memblock, size_t size,
				size_t alignment, size_t offset)
{
  void *p0, *p;
  ptrdiff_t shift;

  if (!memblock)
    return __mingw_aligned_offset_malloc (size, alignment, offset);
  if (NOT_POWER_OF_TWO (alignment))
    goto bad;
  if (size == 0)
    {
      __mingw_aligned_free (memblock);
      return ((void *) 0);
    }
  if (alignment < sizeof (void *))
    alignment = sizeof (void *);
  alignment--;

  p0 = ORIG_PTR (memblock);
  /* It is an error for the alignment to change. */
  if (memblock != PTR_ALIGN (p0, alignment, offset))
    goto bad;
  shift = CP (memblock) - CP (p0);

  p0 = realloc (p0, size + (alignment + GAP (offset) + sizeof (void *)));
  if (!p0)
    return ((void *) 0);
  p = PTR_ALIGN (p0, alignment, offset);

  /* Relative shift of actual data may be different from before, ugh.  */
  if (shift != CP (p) - CP (p0))
    /* ugh, moves more than necessary if size is increased.  */
    memmove (CP (p), CP (p0) + shift, size);

  ORIG_PTR (p) = p0;
  return p;

bad:
  errno = EINVAL;
  return ((void *) 0);
}

void *
__mingw_aligned_realloc (void *memblock, size_t size, size_t alignment)
{
  return __mingw_aligned_offset_realloc (memblock, size, alignment, 0);
}

size_t
__mingw_aligned_msize (void *memblock, size_t alignment, size_t offset)
{
  void *p0;

  if (!memblock || NOT_POWER_OF_TWO (alignment))
    {
      errno = EINVAL;
      return (size_t)-1;
    }
  if (alignment < sizeof (void *))
    alignment = sizeof (void *);
  alignment--;

  p0 = ORIG_PTR (memblock);

  /* It is an error if the alignment or offset does not match. */
  if (memblock != PTR_ALIGN (p0, alignment, offset))
    {
      errno = EINVAL;
      return (size_t)-1;
    }

  return _msize (p0) - (alignment + GAP (offset) + sizeof (void *));
}