1/*
2 * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16#include <aws/common/assert.h>
17#include <aws/common/common.h>
18#include <aws/common/logging.h>
19#include <aws/common/math.h>
20
21#include <stdarg.h>
22#include <stdlib.h>
23
24#ifdef _WIN32
25# include <Windows.h>
26#endif
27
28#ifdef __MACH__
29# include <CoreFoundation/CoreFoundation.h>
30#endif
31
32/* turn off unused named parameter warning on msvc.*/
33#ifdef _MSC_VER
34# pragma warning(push)
35# pragma warning(disable : 4100)
36#endif
37
38bool aws_allocator_is_valid(const struct aws_allocator *alloc) {
39 /* An allocator must define mem_acquire and mem_release. All other fields are optional */
40 return alloc && AWS_OBJECT_PTR_IS_READABLE(alloc) && alloc->mem_acquire && alloc->mem_release;
41}
42
43static void *s_default_malloc(struct aws_allocator *allocator, size_t size) {
44 (void)allocator;
45 return malloc(size);
46}
47
48static void s_default_free(struct aws_allocator *allocator, void *ptr) {
49 (void)allocator;
50 free(ptr);
51}
52
53static void *s_default_realloc(struct aws_allocator *allocator, void *ptr, size_t oldsize, size_t newsize) {
54 (void)allocator;
55 (void)oldsize;
56 return realloc(ptr, newsize);
57}
58
59static void *s_default_calloc(struct aws_allocator *allocator, size_t num, size_t size) {
60 (void)allocator;
61 return calloc(num, size);
62}
63
64static struct aws_allocator default_allocator = {
65 .mem_acquire = s_default_malloc,
66 .mem_release = s_default_free,
67 .mem_realloc = s_default_realloc,
68 .mem_calloc = s_default_calloc,
69};
70
71struct aws_allocator *aws_default_allocator(void) {
72 return &default_allocator;
73}
74
75void *aws_mem_acquire(struct aws_allocator *allocator, size_t size) {
76 AWS_FATAL_PRECONDITION(allocator != NULL);
77 AWS_FATAL_PRECONDITION(allocator->mem_acquire != NULL);
78 /* Protect against https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations */
79 AWS_FATAL_PRECONDITION(size != 0);
80
81 void *mem = allocator->mem_acquire(allocator, size);
82 if (!mem) {
83 aws_raise_error(AWS_ERROR_OOM);
84 }
85 return mem;
86}
87
88void *aws_mem_calloc(struct aws_allocator *allocator, size_t num, size_t size) {
89 AWS_FATAL_PRECONDITION(allocator != NULL);
90 AWS_FATAL_PRECONDITION(allocator->mem_calloc || allocator->mem_acquire);
91 /* Protect against https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations */
92 AWS_FATAL_PRECONDITION(num != 0 && size != 0);
93
94 /* Defensive check: never use calloc with size * num that would overflow
95 * https://wiki.sei.cmu.edu/confluence/display/c/MEM07-C.+Ensure+that+the+arguments+to+calloc%28%29%2C+when+multiplied%2C+do+not+wrap
96 */
97 size_t required_bytes;
98 if (aws_mul_size_checked(num, size, &required_bytes)) {
99 return NULL;
100 }
101
102 /* If there is a defined calloc, use it */
103 if (allocator->mem_calloc) {
104 void *mem = allocator->mem_calloc(allocator, num, size);
105 if (!mem) {
106 aws_raise_error(AWS_ERROR_OOM);
107 }
108 return mem;
109 }
110
111 /* Otherwise, emulate calloc */
112 void *mem = allocator->mem_acquire(allocator, required_bytes);
113 if (!mem) {
114 aws_raise_error(AWS_ERROR_OOM);
115 return NULL;
116 }
117 memset(mem, 0, required_bytes);
118 AWS_POSTCONDITION(mem != NULL);
119 return mem;
120}
121
122#define AWS_ALIGN_ROUND_UP(value, alignment) (((value) + ((alignment)-1)) & ~((alignment)-1))
123
124void *aws_mem_acquire_many(struct aws_allocator *allocator, size_t count, ...) {
125
126 enum { S_ALIGNMENT = sizeof(intmax_t) };
127
128 va_list args_size;
129 va_start(args_size, count);
130 va_list args_allocs;
131 va_copy(args_allocs, args_size);
132
133 size_t total_size = 0;
134 for (size_t i = 0; i < count; ++i) {
135
136 /* Ignore the pointer argument for now */
137 va_arg(args_size, void **);
138
139 size_t alloc_size = va_arg(args_size, size_t);
140 total_size += AWS_ALIGN_ROUND_UP(alloc_size, S_ALIGNMENT);
141 }
142 va_end(args_size);
143
144 void *allocation = NULL;
145
146 if (total_size > 0) {
147
148 allocation = aws_mem_acquire(allocator, total_size);
149 if (!allocation) {
150 aws_raise_error(AWS_ERROR_OOM);
151 goto cleanup;
152 }
153
154 uint8_t *current_ptr = allocation;
155
156 for (size_t i = 0; i < count; ++i) {
157
158 void **out_ptr = va_arg(args_allocs, void **);
159
160 size_t alloc_size = va_arg(args_allocs, size_t);
161 alloc_size = AWS_ALIGN_ROUND_UP(alloc_size, S_ALIGNMENT);
162
163 *out_ptr = current_ptr;
164 current_ptr += alloc_size;
165 }
166 }
167
168cleanup:
169 va_end(args_allocs);
170 return allocation;
171}
172
173#undef AWS_ALIGN_ROUND_UP
174
175void aws_mem_release(struct aws_allocator *allocator, void *ptr) {
176 AWS_FATAL_PRECONDITION(allocator != NULL);
177 AWS_FATAL_PRECONDITION(allocator->mem_release != NULL);
178
179 if (ptr != NULL) {
180 allocator->mem_release(allocator, ptr);
181 }
182}
183
184int aws_mem_realloc(struct aws_allocator *allocator, void **ptr, size_t oldsize, size_t newsize) {
185 AWS_FATAL_PRECONDITION(allocator != NULL);
186 AWS_FATAL_PRECONDITION(allocator->mem_realloc || allocator->mem_acquire);
187 AWS_FATAL_PRECONDITION(allocator->mem_release);
188
189 /* Protect against https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations */
190 if (newsize == 0) {
191 aws_mem_release(allocator, *ptr);
192 *ptr = NULL;
193 return AWS_OP_SUCCESS;
194 }
195
196 if (allocator->mem_realloc) {
197 void *newptr = allocator->mem_realloc(allocator, *ptr, oldsize, newsize);
198 if (!newptr) {
199 return aws_raise_error(AWS_ERROR_OOM);
200 }
201 *ptr = newptr;
202 return AWS_OP_SUCCESS;
203 }
204
205 /* Since the allocator doesn't support realloc, we'll need to emulate it (inefficiently). */
206 if (oldsize >= newsize) {
207 return AWS_OP_SUCCESS;
208 }
209
210 void *newptr = allocator->mem_acquire(allocator, newsize);
211 if (!newptr) {
212 return aws_raise_error(AWS_ERROR_OOM);
213 }
214
215 memcpy(newptr, *ptr, oldsize);
216 memset((uint8_t *)newptr + oldsize, 0, newsize - oldsize);
217
218 aws_mem_release(allocator, *ptr);
219
220 *ptr = newptr;
221
222 return AWS_OP_SUCCESS;
223}
224
225/* Wraps a CFAllocator around aws_allocator. For Mac only. */
226#ifdef __MACH__
227
228static CFStringRef s_cf_allocator_description = CFSTR("CFAllocator wrapping aws_allocator.");
229
230/* note we don't have a standard specification stating sizeof(size_t) == sizeof(void *) so we have some extra casts */
231static void *s_cf_allocator_allocate(CFIndex alloc_size, CFOptionFlags hint, void *info) {
232 (void)hint;
233
234 struct aws_allocator *allocator = info;
235
236 void *mem = aws_mem_acquire(allocator, (size_t)alloc_size + sizeof(size_t));
237
238 if (!mem) {
239 return NULL;
240 }
241
242 size_t allocation_size = (size_t)alloc_size + sizeof(size_t);
243 memcpy(mem, &allocation_size, sizeof(size_t));
244 return (void *)((uint8_t *)mem + sizeof(size_t));
245}
246
247static void s_cf_allocator_deallocate(void *ptr, void *info) {
248 struct aws_allocator *allocator = info;
249
250 void *original_allocation = (uint8_t *)ptr - sizeof(size_t);
251
252 aws_mem_release(allocator, original_allocation);
253}
254
255static void *s_cf_allocator_reallocate(void *ptr, CFIndex new_size, CFOptionFlags hint, void *info) {
256 (void)hint;
257
258 struct aws_allocator *allocator = info;
259 AWS_ASSERT(allocator->mem_realloc);
260
261 void *original_allocation = (uint8_t *)ptr - sizeof(size_t);
262 size_t original_size = 0;
263 memcpy(&original_size, original_allocation, sizeof(size_t));
264
265 if (aws_mem_realloc(allocator, &original_allocation, original_size, (size_t)new_size)) {
266 return NULL;
267 }
268
269 size_t new_allocation_size = (size_t)new_size;
270 memcpy(original_allocation, &new_allocation_size, sizeof(size_t));
271
272 return (void *)((uint8_t *)original_allocation + sizeof(size_t));
273}
274
275static CFStringRef s_cf_allocator_copy_description(const void *info) {
276 (void)info;
277
278 return s_cf_allocator_description;
279}
280
281static CFIndex s_cf_allocator_preferred_size(CFIndex size, CFOptionFlags hint, void *info) {
282 (void)hint;
283 (void)info;
284
285 return size + sizeof(size_t);
286}
287
288CFAllocatorRef aws_wrapped_cf_allocator_new(struct aws_allocator *allocator) {
289 CFAllocatorRef cf_allocator = NULL;
290
291 CFAllocatorReallocateCallBack reallocate_callback = NULL;
292
293 if (allocator->mem_realloc) {
294 reallocate_callback = s_cf_allocator_reallocate;
295 }
296
297 CFAllocatorContext context = {
298 .allocate = s_cf_allocator_allocate,
299 .copyDescription = s_cf_allocator_copy_description,
300 .deallocate = s_cf_allocator_deallocate,
301 .reallocate = reallocate_callback,
302 .info = allocator,
303 .preferredSize = s_cf_allocator_preferred_size,
304 .release = NULL,
305 .retain = NULL,
306 .version = 0,
307 };
308
309 cf_allocator = CFAllocatorCreate(NULL, &context);
310
311 if (!cf_allocator) {
312 aws_raise_error(AWS_ERROR_OOM);
313 }
314
315 return cf_allocator;
316}
317
318void aws_wrapped_cf_allocator_destroy(CFAllocatorRef allocator) {
319 CFRelease(allocator);
320}
321
322#endif /*__MACH__ */
323