1/*
2 * Copyright (c) 2015, Intel Corporation
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * * Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of Intel Corporation nor the names of its contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** \file
30 * \brief Aligned memory alloc/free.
31 */
32#include "ue2common.h"
33#include "alloc.h"
34
35#include <cstdlib>
36#include <cstring>
37
38namespace ue2 {
39
40// This is one of the simplest ways to catch failure where we aren't using an
41// aligned_(zmalloc|_free) pair - it will force death if the wrong free is used.
42// We use this whenever assertions are switched on.
43#if !defined(NDEBUG)
44#define HACK_OFFSET 64
45#else
46#define HACK_OFFSET 0
47#endif
48
49/* get us a posix_memalign from somewhere */
50#if !defined(HAVE_POSIX_MEMALIGN)
51# if defined(HAVE_MEMALIGN)
52 #define posix_memalign(A, B, C) ((*A = (void *)memalign(B, C)) == nullptr)
53# elif defined(HAVE__ALIGNED_MALLOC)
54 /* on Windows */
55 #include <malloc.h>
56 #define posix_memalign(A, B, C) ((*A = (void *)_aligned_malloc(C, B)) == nullptr)
57# else
58 #error no posix_memalign or memalign aligned malloc
59# endif
60#endif
61
62void *aligned_malloc_internal(size_t size, size_t align) {
63 void *mem;
64#if !defined(_WIN32)
65 int rv = posix_memalign(&mem, align, size);
66 if (rv != 0) {
67 DEBUG_PRINTF("posix_memalign returned %d when asked for %zu bytes\n",
68 rv, size);
69 return nullptr;
70 }
71#else
72 if (nullptr == (mem = _aligned_malloc(size, align))) {
73 DEBUG_PRINTF("_aligned_malloc failed when asked for %zu bytes\n",
74 size);
75 return nullptr;
76 }
77#endif
78
79 assert(mem);
80 return mem;
81}
82
83void aligned_free_internal(void *ptr) {
84 if (!ptr) {
85 return;
86 }
87
88#if defined(_WIN32)
89 _aligned_free(ptr);
90#else
91 free(ptr);
92#endif
93}
94
95/** \brief 64-byte aligned, zeroed malloc.
96 *
97 * Pointers should be freed with \ref aligned_free. If we are unable to
98 * allocate the requested number of bytes, this function will throw
99 * std::bad_alloc. */
100void *aligned_zmalloc(size_t size) {
101 // Really huge allocations are probably an indication that we've
102 // done something wrong.
103 assert(size < 1024 * 1024 * 1024); // 1GB
104
105 const size_t alloc_size = size + HACK_OFFSET;
106
107 void *mem = aligned_malloc_internal(alloc_size, 64);
108 if (!mem) {
109 DEBUG_PRINTF("unable to allocate %zu bytes\n", alloc_size);
110 throw std::bad_alloc();
111 }
112
113 DEBUG_PRINTF("alloced %p reporting %p\n", mem, (char *)mem + HACK_OFFSET);
114 assert(ISALIGNED_N(mem, 64));
115
116 memset(mem, 0, alloc_size);
117 return (void *)((char *)mem + HACK_OFFSET);
118}
119
120/** \brief Free a pointer allocated with \ref aligned_zmalloc. */
121void aligned_free(void *ptr) {
122 if (!ptr) {
123 return;
124 }
125
126 void *addr = (void *)((char *)ptr - HACK_OFFSET);
127 DEBUG_PRINTF("asked to free %p freeing %p\n", ptr, addr);
128
129 assert(ISALIGNED_N(addr, 64));
130 aligned_free_internal(addr);
131}
132
133} // namespace ue2
134